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 { /// /// Represents a GPU state and memory accessor. /// class GpuAccessor : GpuAccessorBase, IGpuAccessor { private readonly GpuChannel _channel; private readonly GpuAccessorState _state; private readonly int _stageIndex; private readonly bool _compute; private readonly bool _isVulkan; /// /// Creates a new instance of the GPU state accessor for graphics shader translation. /// /// GPU context /// GPU channel /// Current GPU state /// Graphics shader stage index (0 = Vertex, 4 = Fragment) public GpuAccessor(GpuContext context, GpuChannel channel, GpuAccessorState state, int stageIndex) : base(context, state.ResourceCounts, stageIndex) { _isVulkan = context.Capabilities.Api == TargetApi.Vulkan; _channel = channel; _state = state; _stageIndex = stageIndex; if (stageIndex == (int)ShaderStage.Geometry - 1) { // Only geometry shaders require the primitive topology. _state.SpecializationState.RecordPrimitiveTopology(); } } /// /// Creates a new instance of the GPU state accessor for compute shader translation. /// /// GPU context /// GPU channel /// Current GPU state public GpuAccessor(GpuContext context, GpuChannel channel, GpuAccessorState state) : base(context, state.ResourceCounts, 0) { _channel = channel; _state = state; _compute = true; } /// public uint ConstantBuffer1Read(int offset) { ulong baseAddress = _compute ? _channel.BufferManager.GetComputeUniformBufferAddress(1) : _channel.BufferManager.GetGraphicsUniformBufferAddress(_stageIndex, 1); return _channel.MemoryManager.Physical.Read(baseAddress + (ulong)offset); } /// public void Log(string message) { Logger.Warning?.Print(LogClass.Gpu, $"Shader translator: {message}"); } /// public ReadOnlySpan GetCode(ulong address, int minimumSize) { int size = Math.Max(minimumSize, 0x1000 - (int)(address & 0xfff)); return MemoryMarshal.Cast(_channel.MemoryManager.GetSpan(address, size)); } /// public int QueryComputeLocalSizeX() => _state.ComputeState.LocalSizeX; /// public int QueryComputeLocalSizeY() => _state.ComputeState.LocalSizeY; /// public int QueryComputeLocalSizeZ() => _state.ComputeState.LocalSizeZ; /// public int QueryComputeLocalMemorySize() => _state.ComputeState.LocalMemorySize; /// public int QueryComputeSharedMemorySize() => _state.ComputeState.SharedMemorySize; /// public uint QueryConstantBufferUse() { uint useMask = _compute ? _channel.BufferManager.GetComputeUniformBufferUseMask() : _channel.BufferManager.GetGraphicsUniformBufferUseMask(_stageIndex); _state.SpecializationState?.RecordConstantBufferUse(_stageIndex, useMask); return useMask; } /// public GpuGraphicsState QueryGraphicsState() { return _state.GraphicsState.CreateShaderGraphicsState(!_isVulkan, _isVulkan || _state.GraphicsState.YNegateEnabled); } /// public bool QueryHasConstantBufferDrawParameters() { return _state.GraphicsState.HasConstantBufferDrawParameters; } /// public bool QueryHasUnalignedStorageBuffer() { return _state.GraphicsState.HasUnalignedStorageBuffer || _state.ComputeState.HasUnalignedStorageBuffer; } /// public SamplerType QuerySamplerType(int handle, int cbufSlot) { _state.SpecializationState?.RecordTextureSamplerType(_stageIndex, handle, cbufSlot); return GetTextureDescriptor(handle, cbufSlot).UnpackTextureTarget().ConvertSamplerType(); } /// public int QueryTextureArrayLengthFromBuffer(int slot) { int size = _compute ? _channel.BufferManager.GetComputeUniformBufferSize(slot) : _channel.BufferManager.GetGraphicsUniformBufferSize(_stageIndex, slot); int arrayLength = size / Constants.TextureHandleSizeInBytes; _state.SpecializationState?.RegisterTextureArrayLengthFromBuffer(_stageIndex, 0, slot, arrayLength); return arrayLength; } //// public TextureFormat QueryTextureFormat(int handle, int cbufSlot) { _state.SpecializationState?.RecordTextureFormat(_stageIndex, handle, cbufSlot); var descriptor = GetTextureDescriptor(handle, cbufSlot); return ConvertToTextureFormat(descriptor.UnpackFormat(), descriptor.UnpackSrgb()); } /// public bool QueryTextureCoordNormalized(int handle, int cbufSlot) { _state.SpecializationState?.RecordTextureCoordNormalized(_stageIndex, handle, cbufSlot); return GetTextureDescriptor(handle, cbufSlot).UnpackTextureCoordNormalized(); } /// /// Gets the texture descriptor for a given texture on the pool. /// /// Index of the texture (this is the word offset of the handle in the constant buffer) /// Constant buffer slot for the texture handle /// Texture descriptor private Image.TextureDescriptor GetTextureDescriptor(int handle, int cbufSlot) { if (_compute) { return _channel.TextureManager.GetComputeTextureDescriptor( _state.PoolState.TexturePoolGpuVa, _state.PoolState.TextureBufferIndex, _state.PoolState.TexturePoolMaximumId, handle, cbufSlot); } else { return _channel.TextureManager.GetGraphicsTextureDescriptor( _state.PoolState.TexturePoolGpuVa, _state.PoolState.TextureBufferIndex, _state.PoolState.TexturePoolMaximumId, _stageIndex, handle, cbufSlot); } } /// public bool QueryTransformFeedbackEnabled() { return _state.TransformFeedbackDescriptors != null; } /// public ReadOnlySpan QueryTransformFeedbackVaryingLocations(int bufferIndex) { return _state.TransformFeedbackDescriptors[bufferIndex].AsSpan(); } /// public int QueryTransformFeedbackStride(int bufferIndex) { return _state.TransformFeedbackDescriptors[bufferIndex].Stride; } /// public void RegisterTexture(int handle, int cbufSlot) { _state.SpecializationState?.RegisterTexture(_stageIndex, handle, cbufSlot, GetTextureDescriptor(handle, cbufSlot)); } } }