using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
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, state.TransformFeedbackDescriptors != null)
{
_isVulkan = context.Capabilities.Api == TargetApi.Vulkan;
_channel = channel;
_state = state;
_stageIndex = stageIndex;
}
///
/// 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, false)
{
_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 bool QueryAlphaToCoverageDitherEnable()
{
return _state.GraphicsState.AlphaToCoverageEnable && _state.GraphicsState.AlphaToCoverageDitherEnable;
}
///
public AlphaTestOp QueryAlphaTestCompare()
{
if (!_isVulkan || !_state.GraphicsState.AlphaTestEnable)
{
return AlphaTestOp.Always;
}
return _state.GraphicsState.AlphaTestCompare switch
{
CompareOp.Never or CompareOp.NeverGl => AlphaTestOp.Never,
CompareOp.Less or CompareOp.LessGl => AlphaTestOp.Less,
CompareOp.Equal or CompareOp.EqualGl => AlphaTestOp.Equal,
CompareOp.LessOrEqual or CompareOp.LessOrEqualGl => AlphaTestOp.LessOrEqual,
CompareOp.Greater or CompareOp.GreaterGl => AlphaTestOp.Greater,
CompareOp.NotEqual or CompareOp.NotEqualGl => AlphaTestOp.NotEqual,
CompareOp.GreaterOrEqual or CompareOp.GreaterOrEqualGl => AlphaTestOp.GreaterOrEqual,
_ => AlphaTestOp.Always,
};
}
///
public float QueryAlphaTestReference()
{
return _state.GraphicsState.AlphaTestReference;
}
///
public AttributeType QueryAttributeType(int location)
{
return _state.GraphicsState.AttributeTypes[location];
}
///
public bool QueryEarlyZForce()
{
_state.SpecializationState?.RecordEarlyZForce();
return _state.GraphicsState.EarlyZForce;
}
///
public AttributeType QueryFragmentOutputType(int location)
{
return _state.GraphicsState.FragmentOutputTypes[location];
}
///
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 bool QueryHasConstantBufferDrawParameters()
{
return _state.GraphicsState.HasConstantBufferDrawParameters;
}
///
public bool QueryHasUnalignedStorageBuffer()
{
return _state.GraphicsState.HasUnalignedStorageBuffer || _state.ComputeState.HasUnalignedStorageBuffer;
}
///
public bool QueryDualSourceBlendEnable()
{
return _state.GraphicsState.DualSourceBlendEnable;
}
///
public InputTopology QueryPrimitiveTopology()
{
_state.SpecializationState?.RecordPrimitiveTopology();
return ConvertToInputTopology(_state.GraphicsState.Topology, _state.GraphicsState.TessellationMode);
}
///
public bool QueryProgramPointSize()
{
return _state.GraphicsState.ProgramPointSizeEnable;
}
///
public float QueryPointSize()
{
return _state.GraphicsState.PointSize;
}
///
public bool QueryTessCw()
{
return _state.GraphicsState.TessellationMode.UnpackCw();
}
///
public TessPatchType QueryTessPatchType()
{
return _state.GraphicsState.TessellationMode.UnpackPatchType();
}
///
public TessSpacing QueryTessSpacing()
{
return _state.GraphicsState.TessellationMode.UnpackSpacing();
}
////
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 SamplerType QuerySamplerType(int handle, int cbufSlot)
{
_state.SpecializationState?.RecordTextureSamplerType(_stageIndex, handle, cbufSlot);
return GetTextureDescriptor(handle, cbufSlot).UnpackTextureTarget().ConvertSamplerType();
}
///
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 QueryTransformDepthMinusOneToOne()
{
return _state.GraphicsState.DepthMode;
}
///
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 bool QueryViewportTransformDisable()
{
return _state.GraphicsState.ViewportTransformDisable;
}
///
public bool QueryYNegateEnabled()
{
return _state.GraphicsState.YNegateEnabled;
}
///
public void RegisterTexture(int handle, int cbufSlot)
{
_state.SpecializationState?.RegisterTexture(_stageIndex, handle, cbufSlot, GetTextureDescriptor(handle, cbufSlot));
}
}
}