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;
private readonly bool _hasGeometryShader;
private readonly bool _supportsQuads;
///
/// 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)
/// Indicates if a geometry shader is present
public GpuAccessor(
GpuContext context,
GpuChannel channel,
GpuAccessorState state,
int stageIndex,
bool hasGeometryShader) : base(context, state.ResourceCounts, stageIndex)
{
_channel = channel;
_state = state;
_stageIndex = stageIndex;
_isVulkan = context.Capabilities.Api == TargetApi.Vulkan;
_hasGeometryShader = hasGeometryShader;
_supportsQuads = context.Capabilities.SupportsQuads;
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,
_supportsQuads,
_hasGeometryShader,
_isVulkan || _state.GraphicsState.YNegateEnabled);
}
///
public bool QueryHasConstantBufferDrawParameters()
{
return _state.GraphicsState.HasConstantBufferDrawParameters;
}
///
public bool QueryHasUnalignedStorageBuffer()
{
return _state.GraphicsState.HasUnalignedStorageBuffer || _state.ComputeState.HasUnalignedStorageBuffer;
}
///
public int QuerySamplerArrayLengthFromPool()
{
int length = _state.SamplerPoolMaximumId + 1;
_state.SpecializationState?.RegisterTextureArrayLengthFromPool(isSampler: true, length);
return length;
}
///
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 int QueryTextureArrayLengthFromPool()
{
int length = _state.PoolState.TexturePoolMaximumId + 1;
_state.SpecializationState?.RegisterTextureArrayLengthFromPool(isSampler: false, length);
return length;
}
////
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));
}
}
}