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 }