using Ryujinx.Common.Logging; using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader.Translation; using System; using System.Runtime.InteropServices; namespace Ryujinx.Graphics.Gpu.Shader.DiskCache { /// /// Represents a GPU state and memory accessor. /// class DiskCacheGpuAccessor : GpuAccessorBase, IGpuAccessor { private readonly ReadOnlyMemory _data; private readonly ReadOnlyMemory _cb1Data; private readonly ShaderSpecializationState _oldSpecState; private readonly ShaderSpecializationState _newSpecState; private readonly int _stageIndex; private readonly bool _isVulkan; /// /// Creates a new instance of the cached GPU state accessor for shader translation. /// /// GPU context /// The data of the shader /// The constant buffer 1 data of the shader /// Shader specialization state of the cached shader /// Shader specialization state of the recompiled shader /// Resource counts shared across all shader stages /// Shader stage index public DiskCacheGpuAccessor( GpuContext context, ReadOnlyMemory data, ReadOnlyMemory cb1Data, ShaderSpecializationState oldSpecState, ShaderSpecializationState newSpecState, ResourceCounts counts, int stageIndex) : base(context, counts, stageIndex) { _data = data; _cb1Data = cb1Data; _oldSpecState = oldSpecState; _newSpecState = newSpecState; _stageIndex = stageIndex; _isVulkan = context.Capabilities.Api == TargetApi.Vulkan; if (stageIndex == (int)ShaderStage.Geometry - 1) { // Only geometry shaders require the primitive topology. newSpecState.RecordPrimitiveTopology(); } } /// public uint ConstantBuffer1Read(int offset) { if (offset + sizeof(uint) > _cb1Data.Length) { throw new DiskCacheLoadException(DiskCacheLoadResult.InvalidCb1DataLength); } return MemoryMarshal.Cast(_cb1Data.Span[offset..])[0]; } /// public void Log(string message) { Logger.Warning?.Print(LogClass.Gpu, $"Shader translator: {message}"); } /// public ReadOnlySpan GetCode(ulong address, int minimumSize) { return MemoryMarshal.Cast(_data.Span[(int)address..]); } /// public int QueryComputeLocalSizeX() => _oldSpecState.ComputeState.LocalSizeX; /// public int QueryComputeLocalSizeY() => _oldSpecState.ComputeState.LocalSizeY; /// public int QueryComputeLocalSizeZ() => _oldSpecState.ComputeState.LocalSizeZ; /// public int QueryComputeLocalMemorySize() => _oldSpecState.ComputeState.LocalMemorySize; /// public int QueryComputeSharedMemorySize() => _oldSpecState.ComputeState.SharedMemorySize; /// public uint QueryConstantBufferUse() { _newSpecState.RecordConstantBufferUse(_stageIndex, _oldSpecState.ConstantBufferUse[_stageIndex]); return _oldSpecState.ConstantBufferUse[_stageIndex]; } /// public GpuGraphicsState QueryGraphicsState() { return _oldSpecState.GraphicsState.CreateShaderGraphicsState(!_isVulkan, _isVulkan || _oldSpecState.GraphicsState.YNegateEnabled); } /// public bool QueryHasConstantBufferDrawParameters() { return _oldSpecState.GraphicsState.HasConstantBufferDrawParameters; } /// /// Pool length is not available on the cache public int QuerySamplerArrayLengthFromPool() { return QueryArrayLengthFromPool(isSampler: true); } /// public SamplerType QuerySamplerType(int handle, int cbufSlot) { _newSpecState.RecordTextureSamplerType(_stageIndex, handle, cbufSlot); return _oldSpecState.GetTextureTarget(_stageIndex, handle, cbufSlot).ConvertSamplerType(); } /// /// Constant buffer derived length is not available on the cache public int QueryTextureArrayLengthFromBuffer(int slot) { if (!_oldSpecState.TextureArrayFromBufferRegistered(_stageIndex, 0, slot)) { throw new DiskCacheLoadException(DiskCacheLoadResult.MissingTextureArrayLength); } int arrayLength = _oldSpecState.GetTextureArrayFromBufferLength(_stageIndex, 0, slot); _newSpecState.RegisterTextureArrayLengthFromBuffer(_stageIndex, 0, slot, arrayLength); return arrayLength; } /// /// Pool length is not available on the cache public int QueryTextureArrayLengthFromPool() { return QueryArrayLengthFromPool(isSampler: false); } /// public TextureFormat QueryTextureFormat(int handle, int cbufSlot) { _newSpecState.RecordTextureFormat(_stageIndex, handle, cbufSlot); (uint format, bool formatSrgb) = _oldSpecState.GetFormat(_stageIndex, handle, cbufSlot); return ConvertToTextureFormat(format, formatSrgb); } /// public bool QueryTextureCoordNormalized(int handle, int cbufSlot) { _newSpecState.RecordTextureCoordNormalized(_stageIndex, handle, cbufSlot); return _oldSpecState.GetCoordNormalized(_stageIndex, handle, cbufSlot); } /// public bool QueryTransformFeedbackEnabled() { return _oldSpecState.TransformFeedbackDescriptors != null; } /// public ReadOnlySpan QueryTransformFeedbackVaryingLocations(int bufferIndex) { return _oldSpecState.TransformFeedbackDescriptors[bufferIndex].AsSpan(); } /// public int QueryTransformFeedbackStride(int bufferIndex) { return _oldSpecState.TransformFeedbackDescriptors[bufferIndex].Stride; } /// public bool QueryHasUnalignedStorageBuffer() { return _oldSpecState.GraphicsState.HasUnalignedStorageBuffer || _oldSpecState.ComputeState.HasUnalignedStorageBuffer; } /// /// Texture information is not available on the cache public void RegisterTexture(int handle, int cbufSlot) { if (!_oldSpecState.TextureRegistered(_stageIndex, handle, cbufSlot)) { throw new DiskCacheLoadException(DiskCacheLoadResult.MissingTextureDescriptor); } (uint format, bool formatSrgb) = _oldSpecState.GetFormat(_stageIndex, handle, cbufSlot); TextureTarget target = _oldSpecState.GetTextureTarget(_stageIndex, handle, cbufSlot); bool coordNormalized = _oldSpecState.GetCoordNormalized(_stageIndex, handle, cbufSlot); _newSpecState.RegisterTexture(_stageIndex, handle, cbufSlot, format, formatSrgb, target, coordNormalized); } /// /// Gets the cached texture or sampler pool capacity. /// /// True to get sampler pool length, false for texture pool length /// Pool length /// Pool length is not available on the cache private int QueryArrayLengthFromPool(bool isSampler) { if (!_oldSpecState.TextureArrayFromPoolRegistered(isSampler)) { throw new DiskCacheLoadException(DiskCacheLoadResult.MissingTextureArrayLength); } int arrayLength = _oldSpecState.GetTextureArrayFromPoolLength(isSampler); _newSpecState.RegisterTextureArrayLengthFromPool(isSampler, arrayLength); return arrayLength; } } }