/ src / Ryujinx.Graphics.Gpu / Shader / DiskCache / DiskCacheGpuAccessor.cs
DiskCacheGpuAccessor.cs
  1  using Ryujinx.Common.Logging;
  2  using Ryujinx.Graphics.Gpu.Image;
  3  using Ryujinx.Graphics.Shader;
  4  using Ryujinx.Graphics.Shader.Translation;
  5  using System;
  6  using System.Runtime.InteropServices;
  7  
  8  namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
  9  {
 10      /// <summary>
 11      /// Represents a GPU state and memory accessor.
 12      /// </summary>
 13      class DiskCacheGpuAccessor : GpuAccessorBase, IGpuAccessor
 14      {
 15          private readonly ReadOnlyMemory<byte> _data;
 16          private readonly ReadOnlyMemory<byte> _cb1Data;
 17          private readonly ShaderSpecializationState _oldSpecState;
 18          private readonly ShaderSpecializationState _newSpecState;
 19          private readonly int _stageIndex;
 20          private readonly bool _isVulkan;
 21          private readonly bool _hasGeometryShader;
 22          private readonly bool _supportsQuads;
 23  
 24          /// <summary>
 25          /// Creates a new instance of the cached GPU state accessor for shader translation.
 26          /// </summary>
 27          /// <param name="context">GPU context</param>
 28          /// <param name="data">The data of the shader</param>
 29          /// <param name="cb1Data">The constant buffer 1 data of the shader</param>
 30          /// <param name="oldSpecState">Shader specialization state of the cached shader</param>
 31          /// <param name="newSpecState">Shader specialization state of the recompiled shader</param>
 32          /// <param name="counts">Resource counts shared across all shader stages</param>
 33          /// <param name="stageIndex">Shader stage index</param>
 34          /// <param name="hasGeometryShader">Indicates if a geometry shader is present</param>
 35          public DiskCacheGpuAccessor(
 36              GpuContext context,
 37              ReadOnlyMemory<byte> data,
 38              ReadOnlyMemory<byte> cb1Data,
 39              ShaderSpecializationState oldSpecState,
 40              ShaderSpecializationState newSpecState,
 41              ResourceCounts counts,
 42              int stageIndex,
 43              bool hasGeometryShader) : base(context, counts, stageIndex)
 44          {
 45              _data = data;
 46              _cb1Data = cb1Data;
 47              _oldSpecState = oldSpecState;
 48              _newSpecState = newSpecState;
 49              _stageIndex = stageIndex;
 50              _isVulkan = context.Capabilities.Api == TargetApi.Vulkan;
 51              _hasGeometryShader = hasGeometryShader;
 52              _supportsQuads = context.Capabilities.SupportsQuads;
 53  
 54              if (stageIndex == (int)ShaderStage.Geometry - 1)
 55              {
 56                  // Only geometry shaders require the primitive topology.
 57                  newSpecState.RecordPrimitiveTopology();
 58              }
 59          }
 60  
 61          /// <inheritdoc/>
 62          public uint ConstantBuffer1Read(int offset)
 63          {
 64              if (offset + sizeof(uint) > _cb1Data.Length)
 65              {
 66                  throw new DiskCacheLoadException(DiskCacheLoadResult.InvalidCb1DataLength);
 67              }
 68  
 69              return MemoryMarshal.Cast<byte, uint>(_cb1Data.Span[offset..])[0];
 70          }
 71  
 72          /// <inheritdoc/>
 73          public void Log(string message)
 74          {
 75              Logger.Warning?.Print(LogClass.Gpu, $"Shader translator: {message}");
 76          }
 77  
 78          /// <inheritdoc/>
 79          public ReadOnlySpan<ulong> GetCode(ulong address, int minimumSize)
 80          {
 81              return MemoryMarshal.Cast<byte, ulong>(_data.Span[(int)address..]);
 82          }
 83  
 84          /// <inheritdoc/>
 85          public int QueryComputeLocalSizeX() => _oldSpecState.ComputeState.LocalSizeX;
 86  
 87          /// <inheritdoc/>
 88          public int QueryComputeLocalSizeY() => _oldSpecState.ComputeState.LocalSizeY;
 89  
 90          /// <inheritdoc/>
 91          public int QueryComputeLocalSizeZ() => _oldSpecState.ComputeState.LocalSizeZ;
 92  
 93          /// <inheritdoc/>
 94          public int QueryComputeLocalMemorySize() => _oldSpecState.ComputeState.LocalMemorySize;
 95  
 96          /// <inheritdoc/>
 97          public int QueryComputeSharedMemorySize() => _oldSpecState.ComputeState.SharedMemorySize;
 98  
 99          /// <inheritdoc/>
100          public uint QueryConstantBufferUse()
101          {
102              _newSpecState.RecordConstantBufferUse(_stageIndex, _oldSpecState.ConstantBufferUse[_stageIndex]);
103              return _oldSpecState.ConstantBufferUse[_stageIndex];
104          }
105  
106          /// <inheritdoc/>
107          public GpuGraphicsState QueryGraphicsState()
108          {
109              return _oldSpecState.GraphicsState.CreateShaderGraphicsState(
110                  !_isVulkan,
111                  _supportsQuads,
112                  _hasGeometryShader,
113                  _isVulkan || _oldSpecState.GraphicsState.YNegateEnabled);
114          }
115  
116          /// <inheritdoc/>
117          public bool QueryHasConstantBufferDrawParameters()
118          {
119              return _oldSpecState.GraphicsState.HasConstantBufferDrawParameters;
120          }
121  
122          /// <inheritdoc/>
123          /// <exception cref="DiskCacheLoadException">Pool length is not available on the cache</exception>
124          public int QuerySamplerArrayLengthFromPool()
125          {
126              return QueryArrayLengthFromPool(isSampler: true);
127          }
128  
129          /// <inheritdoc/>
130          public SamplerType QuerySamplerType(int handle, int cbufSlot)
131          {
132              _newSpecState.RecordTextureSamplerType(_stageIndex, handle, cbufSlot);
133              return _oldSpecState.GetTextureTarget(_stageIndex, handle, cbufSlot).ConvertSamplerType();
134          }
135  
136          /// <inheritdoc/>
137          /// <exception cref="DiskCacheLoadException">Constant buffer derived length is not available on the cache</exception>
138          public int QueryTextureArrayLengthFromBuffer(int slot)
139          {
140              if (!_oldSpecState.TextureArrayFromBufferRegistered(_stageIndex, 0, slot))
141              {
142                  throw new DiskCacheLoadException(DiskCacheLoadResult.MissingTextureArrayLength);
143              }
144  
145              int arrayLength = _oldSpecState.GetTextureArrayFromBufferLength(_stageIndex, 0, slot);
146              _newSpecState.RegisterTextureArrayLengthFromBuffer(_stageIndex, 0, slot, arrayLength);
147  
148              return arrayLength;
149          }
150  
151          /// <inheritdoc/>
152          /// <exception cref="DiskCacheLoadException">Pool length is not available on the cache</exception>
153          public int QueryTextureArrayLengthFromPool()
154          {
155              return QueryArrayLengthFromPool(isSampler: false);
156          }
157  
158          /// <inheritdoc/>
159          public TextureFormat QueryTextureFormat(int handle, int cbufSlot)
160          {
161              _newSpecState.RecordTextureFormat(_stageIndex, handle, cbufSlot);
162              (uint format, bool formatSrgb) = _oldSpecState.GetFormat(_stageIndex, handle, cbufSlot);
163              return ConvertToTextureFormat(format, formatSrgb);
164          }
165  
166          /// <inheritdoc/>
167          public bool QueryTextureCoordNormalized(int handle, int cbufSlot)
168          {
169              _newSpecState.RecordTextureCoordNormalized(_stageIndex, handle, cbufSlot);
170              return _oldSpecState.GetCoordNormalized(_stageIndex, handle, cbufSlot);
171          }
172  
173          /// <inheritdoc/>
174          public bool QueryTransformFeedbackEnabled()
175          {
176              return _oldSpecState.TransformFeedbackDescriptors != null;
177          }
178  
179          /// <inheritdoc/>
180          public ReadOnlySpan<byte> QueryTransformFeedbackVaryingLocations(int bufferIndex)
181          {
182              return _oldSpecState.TransformFeedbackDescriptors[bufferIndex].AsSpan();
183          }
184  
185          /// <inheritdoc/>
186          public int QueryTransformFeedbackStride(int bufferIndex)
187          {
188              return _oldSpecState.TransformFeedbackDescriptors[bufferIndex].Stride;
189          }
190  
191          /// <inheritdoc/>
192          public bool QueryHasUnalignedStorageBuffer()
193          {
194              return _oldSpecState.GraphicsState.HasUnalignedStorageBuffer || _oldSpecState.ComputeState.HasUnalignedStorageBuffer;
195          }
196  
197          /// <inheritdoc/>
198          /// <exception cref="DiskCacheLoadException">Texture information is not available on the cache</exception>
199          public void RegisterTexture(int handle, int cbufSlot)
200          {
201              if (!_oldSpecState.TextureRegistered(_stageIndex, handle, cbufSlot))
202              {
203                  throw new DiskCacheLoadException(DiskCacheLoadResult.MissingTextureDescriptor);
204              }
205  
206              (uint format, bool formatSrgb) = _oldSpecState.GetFormat(_stageIndex, handle, cbufSlot);
207              TextureTarget target = _oldSpecState.GetTextureTarget(_stageIndex, handle, cbufSlot);
208              bool coordNormalized = _oldSpecState.GetCoordNormalized(_stageIndex, handle, cbufSlot);
209              _newSpecState.RegisterTexture(_stageIndex, handle, cbufSlot, format, formatSrgb, target, coordNormalized);
210          }
211  
212          /// <summary>
213          /// Gets the cached texture or sampler pool capacity.
214          /// </summary>
215          /// <param name="isSampler">True to get sampler pool length, false for texture pool length</param>
216          /// <returns>Pool length</returns>
217          /// <exception cref="DiskCacheLoadException">Pool length is not available on the cache</exception>
218          private int QueryArrayLengthFromPool(bool isSampler)
219          {
220              if (!_oldSpecState.TextureArrayFromPoolRegistered(isSampler))
221              {
222                  throw new DiskCacheLoadException(DiskCacheLoadResult.MissingTextureArrayLength);
223              }
224  
225              int arrayLength = _oldSpecState.GetTextureArrayFromPoolLength(isSampler);
226              _newSpecState.RegisterTextureArrayLengthFromPool(isSampler, arrayLength);
227  
228              return arrayLength;
229          }
230      }
231  }