using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader;
using System;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Gpu.Shader
{
///
/// Represents a GPU state and memory accessor.
///
class GpuAccessor : TextureDescriptorCapableGpuAccessor, IGpuAccessor
{
private readonly GpuChannel _channel;
private readonly GpuAccessorState _state;
private readonly int _stageIndex;
private readonly bool _compute;
private readonly int _localSizeX;
private readonly int _localSizeY;
private readonly int _localSizeZ;
private readonly int _localMemorySize;
private readonly int _sharedMemorySize;
public int Cb1DataSize { get; private set; }
///
/// 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)
{
_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
/// Local group size X of the compute shader
/// Local group size Y of the compute shader
/// Local group size Z of the compute shader
/// Local memory size of the compute shader
/// Shared memory size of the compute shader
public GpuAccessor(
GpuContext context,
GpuChannel channel,
GpuAccessorState state,
int localSizeX,
int localSizeY,
int localSizeZ,
int localMemorySize,
int sharedMemorySize) : base(context)
{
_channel = channel;
_state = state;
_compute = true;
_localSizeX = localSizeX;
_localSizeY = localSizeY;
_localSizeZ = localSizeZ;
_localMemorySize = localMemorySize;
_sharedMemorySize = sharedMemorySize;
}
///
/// Reads data from the constant buffer 1.
///
/// Offset in bytes to read from
/// Value at the given offset
public uint ConstantBuffer1Read(int offset)
{
if (Cb1DataSize < offset + 4)
{
Cb1DataSize = offset + 4;
}
ulong baseAddress = _compute
? _channel.BufferManager.GetComputeUniformBufferAddress(1)
: _channel.BufferManager.GetGraphicsUniformBufferAddress(_stageIndex, 1);
return _channel.MemoryManager.Physical.Read(baseAddress + (ulong)offset);
}
///
/// Prints a log message.
///
/// Message to print
public void Log(string message)
{
Logger.Warning?.Print(LogClass.Gpu, $"Shader translator: {message}");
}
///
/// Gets a span of the specified memory location, containing shader code.
///
/// GPU virtual address of the data
/// Minimum size that the returned span may have
/// Span of the memory location
public override ReadOnlySpan GetCode(ulong address, int minimumSize)
{
int size = Math.Max(minimumSize, 0x1000 - (int)(address & 0xfff));
return MemoryMarshal.Cast(_channel.MemoryManager.GetSpan(address, size));
}
///
/// Queries Local Size X for compute shaders.
///
/// Local Size X
public int QueryComputeLocalSizeX() => _localSizeX;
///
/// Queries Local Size Y for compute shaders.
///
/// Local Size Y
public int QueryComputeLocalSizeY() => _localSizeY;
///
/// Queries Local Size Z for compute shaders.
///
/// Local Size Z
public int QueryComputeLocalSizeZ() => _localSizeZ;
///
/// Queries Local Memory size in bytes for compute shaders.
///
/// Local Memory size in bytes
public int QueryComputeLocalMemorySize() => _localMemorySize;
///
/// Queries Shared Memory size in bytes for compute shaders.
///
/// Shared Memory size in bytes
public int QueryComputeSharedMemorySize() => _sharedMemorySize;
///
/// Queries Constant Buffer usage information.
///
/// A mask where each bit set indicates a bound constant buffer
public uint QueryConstantBufferUse()
{
return _compute
? _channel.BufferManager.GetComputeUniformBufferUseMask()
: _channel.BufferManager.GetGraphicsUniformBufferUseMask(_stageIndex);
}
///
/// Queries current primitive topology for geometry shaders.
///
/// Current primitive topology
public InputTopology QueryPrimitiveTopology()
{
return _state.Topology switch
{
PrimitiveTopology.Points => InputTopology.Points,
PrimitiveTopology.Lines or
PrimitiveTopology.LineLoop or
PrimitiveTopology.LineStrip => InputTopology.Lines,
PrimitiveTopology.LinesAdjacency or
PrimitiveTopology.LineStripAdjacency => InputTopology.LinesAdjacency,
PrimitiveTopology.Triangles or
PrimitiveTopology.TriangleStrip or
PrimitiveTopology.TriangleFan => InputTopology.Triangles,
PrimitiveTopology.TrianglesAdjacency or
PrimitiveTopology.TriangleStripAdjacency => InputTopology.TrianglesAdjacency,
PrimitiveTopology.Patches => _state.TessellationMode.UnpackPatchType() == TessPatchType.Isolines
? InputTopology.Lines
: InputTopology.Triangles,
_ => InputTopology.Points
};
}
///
/// Queries the tessellation evaluation shader primitive winding order.
///
/// True if the primitive winding order is clockwise, false if counter-clockwise
public bool QueryTessCw() => _state.TessellationMode.UnpackCw();
///
/// Queries the tessellation evaluation shader abstract patch type.
///
/// Abstract patch type
public TessPatchType QueryTessPatchType() => _state.TessellationMode.UnpackPatchType();
///
/// Queries the tessellation evaluation shader spacing between tessellated vertices of the patch.
///
/// Spacing between tessellated vertices of the patch
public TessSpacing QueryTessSpacing() => _state.TessellationMode.UnpackSpacing();
///
/// 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
public override Image.ITextureDescriptor GetTextureDescriptor(int handle, int cbufSlot)
{
if (_compute)
{
return _channel.TextureManager.GetComputeTextureDescriptor(
_state.TexturePoolGpuVa,
_state.TextureBufferIndex,
_state.TexturePoolMaximumId,
handle,
cbufSlot);
}
else
{
return _channel.TextureManager.GetGraphicsTextureDescriptor(
_state.TexturePoolGpuVa,
_state.TextureBufferIndex,
_state.TexturePoolMaximumId,
_stageIndex,
handle,
cbufSlot);
}
}
///
/// Queries if host state forces early depth testing.
///
/// True if early depth testing is forced
public bool QueryEarlyZForce()
{
return _state.EarlyZForce;
}
}
}