Shader Extra Set Support + Cleanup (#36)

Separate samplers are now supported and arrays in constant sets are bound
This commit is contained in:
Isaac Marovitz 2024-07-31 23:32:37 +01:00
parent d9025904a7
commit 4cd15cb1a6
20 changed files with 412 additions and 181 deletions

View file

@ -2,7 +2,6 @@ namespace Ryujinx.Graphics.Metal
{ {
static class Constants static class Constants
{ {
// TODO: Check these values, these were largely copied from Vulkan
public const int MaxShaderStages = 5; public const int MaxShaderStages = 5;
public const int MaxVertexBuffers = 16; public const int MaxVertexBuffers = 16;
public const int MaxUniformBuffersPerStage = 18; public const int MaxUniformBuffersPerStage = 18;
@ -15,17 +14,25 @@ namespace Ryujinx.Graphics.Metal
public const int MaxViewports = 16; public const int MaxViewports = 16;
// TODO: Check this value // TODO: Check this value
public const int MaxVertexAttributes = 31; public const int MaxVertexAttributes = 31;
// TODO: Check this value
public const int MaxVertexLayouts = 31;
public const int MinResourceAlignment = 16; public const int MinResourceAlignment = 16;
// Must match constants set in shader generation // Must match constants set in shader generation
public const uint ZeroBufferIndex = 18; public const uint ZeroBufferIndex = MaxVertexBuffers;
public const uint BaseSetIndex = MaxVertexBuffers + 1;
public const uint ConstantBuffersIndex = 20; public const uint ConstantBuffersIndex = BaseSetIndex;
public const uint StorageBuffersIndex = 21; public const uint StorageBuffersIndex = BaseSetIndex + 1;
public const uint TexturesIndex = 22; public const uint TexturesIndex = BaseSetIndex + 2;
public const uint ImagesIndex = 23; public const uint ImagesIndex = BaseSetIndex + 3;
public const uint ConstantBuffersSetIndex = 0;
public const uint StorageBuffersSetIndex = 1;
public const uint TexturesSetIndex = 2;
public const uint ImagesSetIndex = 3;
public const uint MaximumBufferArgumentTableEntries = 31;
public const uint MaximumExtraSets = MaximumBufferArgumentTableEntries - ImagesIndex;
} }
} }

View file

@ -57,11 +57,16 @@ namespace Ryujinx.Graphics.Metal
_depthStencilCache.Dispose(); _depthStencilCache.Dispose();
} }
private readonly void SignalDirty(DirtyFlags flags)
{
_currentState.Dirty |= flags;
}
public EncoderState SwapState(EncoderState state, DirtyFlags flags = DirtyFlags.All) public EncoderState SwapState(EncoderState state, DirtyFlags flags = DirtyFlags.All)
{ {
_currentState = state ?? _mainState; _currentState = state ?? _mainState;
_currentState.Dirty |= flags; SignalDirty(flags);
return _mainState; return _mainState;
} }
@ -84,7 +89,7 @@ namespace Ryujinx.Graphics.Metal
_currentState.Topology = state.Topology; _currentState.Topology = state.Topology;
_currentState.Viewports = state.Viewports; _currentState.Viewports = state.Viewports;
_currentState.Dirty |= DirtyFlags.CullMode | DirtyFlags.DepthStencil | DirtyFlags.Viewports; SignalDirty(DirtyFlags.CullMode | DirtyFlags.DepthStencil | DirtyFlags.Viewports);
} }
public readonly void SetClearLoadAction(bool clear) public readonly void SetClearLoadAction(bool clear)
@ -94,12 +99,12 @@ namespace Ryujinx.Graphics.Metal
public void DirtyTextures() public void DirtyTextures()
{ {
_currentState.Dirty |= DirtyFlags.Textures; SignalDirty(DirtyFlags.Textures);
} }
public void DirtyImages() public void DirtyImages()
{ {
_currentState.Dirty |= DirtyFlags.Images; SignalDirty(DirtyFlags.Images);
} }
public readonly MTLRenderCommandEncoder CreateRenderCommandEncoder() public readonly MTLRenderCommandEncoder CreateRenderCommandEncoder()
@ -161,7 +166,7 @@ namespace Ryujinx.Graphics.Metal
var renderCommandEncoder = _pipeline.CommandBuffer.RenderCommandEncoder(renderPassDescriptor); var renderCommandEncoder = _pipeline.CommandBuffer.RenderCommandEncoder(renderPassDescriptor);
// Mark all state as dirty to ensure it is set on the encoder // Mark all state as dirty to ensure it is set on the encoder
_currentState.Dirty |= DirtyFlags.RenderAll; SignalDirty(DirtyFlags.RenderAll);
// Cleanup // Cleanup
renderPassDescriptor.Dispose(); renderPassDescriptor.Dispose();
@ -175,7 +180,7 @@ namespace Ryujinx.Graphics.Metal
var computeCommandEncoder = _pipeline.CommandBuffer.ComputeCommandEncoder(descriptor); var computeCommandEncoder = _pipeline.CommandBuffer.ComputeCommandEncoder(descriptor);
// Mark all state as dirty to ensure it is set on the encoder // Mark all state as dirty to ensure it is set on the encoder
_currentState.Dirty |= DirtyFlags.ComputeAll; SignalDirty(DirtyFlags.ComputeAll);
// Cleanup // Cleanup
descriptor.Dispose(); descriptor.Dispose();
@ -233,22 +238,22 @@ namespace Ryujinx.Graphics.Metal
if ((_currentState.Dirty & DirtyFlags.Uniforms) != 0) if ((_currentState.Dirty & DirtyFlags.Uniforms) != 0)
{ {
UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, MetalRenderer.UniformSetIndex); UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, Constants.ConstantBuffersSetIndex);
} }
if ((_currentState.Dirty & DirtyFlags.Storages) != 0) if ((_currentState.Dirty & DirtyFlags.Storages) != 0)
{ {
UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, MetalRenderer.StorageSetIndex); UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, Constants.StorageBuffersSetIndex);
} }
if ((_currentState.Dirty & DirtyFlags.Textures) != 0) if ((_currentState.Dirty & DirtyFlags.Textures) != 0)
{ {
UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, MetalRenderer.TextureSetIndex); UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, Constants.TexturesSetIndex);
} }
if (_currentState.Dirty.HasFlag(DirtyFlags.Images)) if ((_currentState.Dirty & DirtyFlags.Images) != 0)
{ {
UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, MetalRenderer.ImageSetIndex); UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, Constants.ImagesSetIndex);
} }
_currentState.Dirty &= ~DirtyFlags.RenderAll; _currentState.Dirty &= ~DirtyFlags.RenderAll;
@ -256,29 +261,29 @@ namespace Ryujinx.Graphics.Metal
public readonly void RebindComputeState(MTLComputeCommandEncoder computeCommandEncoder) public readonly void RebindComputeState(MTLComputeCommandEncoder computeCommandEncoder)
{ {
if (_currentState.Dirty.HasFlag(DirtyFlags.ComputePipeline)) if ((_currentState.Dirty & DirtyFlags.ComputePipeline) != 0)
{ {
SetComputePipelineState(computeCommandEncoder); SetComputePipelineState(computeCommandEncoder);
} }
if (_currentState.Dirty.HasFlag(DirtyFlags.Uniforms)) if ((_currentState.Dirty & DirtyFlags.Uniforms) != 0)
{ {
UpdateAndBind(computeCommandEncoder, _currentState.ComputeProgram, MetalRenderer.UniformSetIndex); UpdateAndBind(computeCommandEncoder, _currentState.ComputeProgram, Constants.ConstantBuffersSetIndex);
} }
if (_currentState.Dirty.HasFlag(DirtyFlags.Storages)) if ((_currentState.Dirty & DirtyFlags.Storages) != 0)
{ {
UpdateAndBind(computeCommandEncoder, _currentState.ComputeProgram, MetalRenderer.StorageSetIndex); UpdateAndBind(computeCommandEncoder, _currentState.ComputeProgram, Constants.StorageBuffersSetIndex);
} }
if (_currentState.Dirty.HasFlag(DirtyFlags.Textures)) if ((_currentState.Dirty & DirtyFlags.Textures) != 0)
{ {
UpdateAndBind(computeCommandEncoder, _currentState.ComputeProgram, MetalRenderer.TextureSetIndex); UpdateAndBind(computeCommandEncoder, _currentState.ComputeProgram, Constants.TexturesSetIndex);
} }
if (_currentState.Dirty.HasFlag(DirtyFlags.Images)) if ((_currentState.Dirty & DirtyFlags.Images) != 0)
{ {
UpdateAndBind(computeCommandEncoder, _currentState.ComputeProgram, MetalRenderer.ImageSetIndex); UpdateAndBind(computeCommandEncoder, _currentState.ComputeProgram, Constants.ImagesSetIndex);
} }
_currentState.Dirty &= ~DirtyFlags.ComputeAll; _currentState.Dirty &= ~DirtyFlags.ComputeAll;
@ -347,13 +352,13 @@ namespace Ryujinx.Graphics.Metal
{ {
_currentState.RenderProgram = prg; _currentState.RenderProgram = prg;
_currentState.Dirty |= DirtyFlags.RenderPipeline | DirtyFlags.ArgBuffers; SignalDirty(DirtyFlags.RenderPipeline | DirtyFlags.ArgBuffers);
} }
else if (prg.ComputeFunction != IntPtr.Zero) else if (prg.ComputeFunction != IntPtr.Zero)
{ {
_currentState.ComputeProgram = prg; _currentState.ComputeProgram = prg;
_currentState.Dirty |= DirtyFlags.ComputePipeline | DirtyFlags.ArgBuffers; SignalDirty(DirtyFlags.ComputePipeline | DirtyFlags.ArgBuffers);
} }
} }
@ -516,8 +521,7 @@ namespace Ryujinx.Graphics.Metal
// Update the buffers on the pipeline // Update the buffers on the pipeline
UpdatePipelineVertexState(_currentState.VertexBuffers, _currentState.VertexAttribs); UpdatePipelineVertexState(_currentState.VertexBuffers, _currentState.VertexAttribs);
// Mark dirty SignalDirty(DirtyFlags.RenderPipeline);
_currentState.Dirty |= DirtyFlags.RenderPipeline;
} }
public readonly void UpdateBlendDescriptors(int index, BlendDescriptor blend) public readonly void UpdateBlendDescriptors(int index, BlendDescriptor blend)
@ -541,11 +545,9 @@ namespace Ryujinx.Graphics.Metal
_currentState.BlendColor = blend.BlendConstant; _currentState.BlendColor = blend.BlendConstant;
// Mark dirty SignalDirty(DirtyFlags.RenderPipeline);
_currentState.Dirty |= DirtyFlags.RenderPipeline;
} }
// Inlineable
public void UpdateStencilState(StencilTestDescriptor stencilTest) public void UpdateStencilState(StencilTestDescriptor stencilTest)
{ {
ref DepthStencilUid uid = ref _currentState.DepthStencilUid; ref DepthStencilUid uid = ref _currentState.DepthStencilUid;
@ -574,8 +576,7 @@ namespace Ryujinx.Graphics.Metal
UpdateStencilRefValue(stencilTest.FrontFuncRef, stencilTest.BackFuncRef); UpdateStencilRefValue(stencilTest.FrontFuncRef, stencilTest.BackFuncRef);
// Mark dirty SignalDirty(DirtyFlags.DepthStencil);
_currentState.Dirty |= DirtyFlags.DepthStencil;
} }
public readonly void UpdateDepthState(DepthTestDescriptor depthTest) public readonly void UpdateDepthState(DepthTestDescriptor depthTest)
@ -585,11 +586,9 @@ namespace Ryujinx.Graphics.Metal
uid.DepthCompareFunction = depthTest.TestEnable ? depthTest.Func.Convert() : MTLCompareFunction.Always; uid.DepthCompareFunction = depthTest.TestEnable ? depthTest.Func.Convert() : MTLCompareFunction.Always;
uid.DepthWriteEnabled = depthTest.TestEnable && depthTest.WriteEnable; uid.DepthWriteEnabled = depthTest.TestEnable && depthTest.WriteEnable;
// Mark dirty SignalDirty(DirtyFlags.DepthStencil);
_currentState.Dirty |= DirtyFlags.DepthStencil;
} }
// Inlineable
public readonly void UpdateDepthClamp(bool clamp) public readonly void UpdateDepthClamp(bool clamp)
{ {
_currentState.DepthClipMode = clamp ? MTLDepthClipMode.Clamp : MTLDepthClipMode.Clip; _currentState.DepthClipMode = clamp ? MTLDepthClipMode.Clamp : MTLDepthClipMode.Clip;
@ -601,11 +600,9 @@ namespace Ryujinx.Graphics.Metal
return; return;
} }
// Mark dirty SignalDirty(DirtyFlags.DepthClamp);
_currentState.Dirty |= DirtyFlags.DepthClamp;
} }
// Inlineable
public readonly void UpdateDepthBias(float depthBias, float slopeScale, float clamp) public readonly void UpdateDepthBias(float depthBias, float slopeScale, float clamp)
{ {
_currentState.DepthBias = depthBias; _currentState.DepthBias = depthBias;
@ -619,11 +616,9 @@ namespace Ryujinx.Graphics.Metal
return; return;
} }
// Mark dirty SignalDirty(DirtyFlags.DepthBias);
_currentState.Dirty |= DirtyFlags.DepthBias;
} }
// Inlineable
public void UpdateScissors(ReadOnlySpan<Rectangle<int>> regions) public void UpdateScissors(ReadOnlySpan<Rectangle<int>> regions)
{ {
for (int i = 0; i < regions.Length; i++) for (int i = 0; i < regions.Length; i++)
@ -646,11 +641,9 @@ namespace Ryujinx.Graphics.Metal
return; return;
} }
// Mark dirty SignalDirty(DirtyFlags.Scissors);
_currentState.Dirty |= DirtyFlags.Scissors;
} }
// Inlineable
public void UpdateViewports(ReadOnlySpan<Viewport> viewports) public void UpdateViewports(ReadOnlySpan<Viewport> viewports)
{ {
static float Clamp(float value) static float Clamp(float value)
@ -680,8 +673,7 @@ namespace Ryujinx.Graphics.Metal
return; return;
} }
// Mark dirty SignalDirty(DirtyFlags.Viewports);
_currentState.Dirty |= DirtyFlags.Viewports;
} }
public readonly void UpdateVertexBuffers(ReadOnlySpan<VertexBufferDescriptor> vertexBuffers) public readonly void UpdateVertexBuffers(ReadOnlySpan<VertexBufferDescriptor> vertexBuffers)
@ -708,8 +700,7 @@ namespace Ryujinx.Graphics.Metal
// Update the buffers on the pipeline // Update the buffers on the pipeline
UpdatePipelineVertexState(_currentState.VertexBuffers, _currentState.VertexAttribs); UpdatePipelineVertexState(_currentState.VertexBuffers, _currentState.VertexAttribs);
// Mark dirty SignalDirty(DirtyFlags.RenderPipeline);
_currentState.Dirty |= DirtyFlags.RenderPipeline;
} }
public readonly void UpdateUniformBuffers(ReadOnlySpan<BufferAssignment> buffers) public readonly void UpdateUniformBuffers(ReadOnlySpan<BufferAssignment> buffers)
@ -726,7 +717,7 @@ namespace Ryujinx.Graphics.Metal
_currentState.UniformBufferRefs[index] = new BufferRef(mtlBuffer, ref buffer); _currentState.UniformBufferRefs[index] = new BufferRef(mtlBuffer, ref buffer);
} }
_currentState.Dirty |= DirtyFlags.Uniforms; SignalDirty(DirtyFlags.Uniforms);
} }
public readonly void UpdateStorageBuffers(ReadOnlySpan<BufferAssignment> buffers) public readonly void UpdateStorageBuffers(ReadOnlySpan<BufferAssignment> buffers)
@ -743,7 +734,7 @@ namespace Ryujinx.Graphics.Metal
_currentState.StorageBufferRefs[index] = new BufferRef(mtlBuffer, ref buffer); _currentState.StorageBufferRefs[index] = new BufferRef(mtlBuffer, ref buffer);
} }
_currentState.Dirty |= DirtyFlags.Storages; SignalDirty(DirtyFlags.Storages);
} }
public readonly void UpdateStorageBuffers(int first, ReadOnlySpan<Auto<DisposableBuffer>> buffers) public readonly void UpdateStorageBuffers(int first, ReadOnlySpan<Auto<DisposableBuffer>> buffers)
@ -756,10 +747,9 @@ namespace Ryujinx.Graphics.Metal
_currentState.StorageBufferRefs[index] = new BufferRef(mtlBuffer); _currentState.StorageBufferRefs[index] = new BufferRef(mtlBuffer);
} }
_currentState.Dirty |= DirtyFlags.Storages; SignalDirty(DirtyFlags.Storages);
} }
// Inlineable
public void UpdateCullMode(bool enable, Face face) public void UpdateCullMode(bool enable, Face face)
{ {
var dirtyScissor = (face == Face.FrontAndBack) != _currentState.CullBoth; var dirtyScissor = (face == Face.FrontAndBack) != _currentState.CullBoth;
@ -776,15 +766,14 @@ namespace Ryujinx.Graphics.Metal
} }
// Mark dirty // Mark dirty
_currentState.Dirty |= DirtyFlags.CullMode; SignalDirty(DirtyFlags.CullMode);
if (dirtyScissor) if (dirtyScissor)
{ {
_currentState.Dirty |= DirtyFlags.Scissors; SignalDirty(DirtyFlags.Scissors);
} }
} }
// Inlineable
public readonly void UpdateFrontFace(FrontFace frontFace) public readonly void UpdateFrontFace(FrontFace frontFace)
{ {
_currentState.Winding = frontFace.Convert(); _currentState.Winding = frontFace.Convert();
@ -796,8 +785,7 @@ namespace Ryujinx.Graphics.Metal
return; return;
} }
// Mark dirty SignalDirty(DirtyFlags.FrontFace);
_currentState.Dirty |= DirtyFlags.FrontFace;
} }
private readonly void UpdateStencilRefValue(int frontRef, int backRef) private readonly void UpdateStencilRefValue(int frontRef, int backRef)
@ -811,8 +799,7 @@ namespace Ryujinx.Graphics.Metal
SetStencilRefValue(renderCommandEncoder); SetStencilRefValue(renderCommandEncoder);
} }
// Mark dirty SignalDirty(DirtyFlags.StencilRef);
_currentState.Dirty |= DirtyFlags.StencilRef;
} }
public readonly void UpdateTextureAndSampler(ShaderStage stage, ulong binding, TextureBase texture, Sampler sampler) public readonly void UpdateTextureAndSampler(ShaderStage stage, ulong binding, TextureBase texture, Sampler sampler)
@ -826,7 +813,7 @@ namespace Ryujinx.Graphics.Metal
_currentState.TextureRefs[binding] = default; _currentState.TextureRefs[binding] = default;
} }
_currentState.Dirty |= DirtyFlags.Textures; SignalDirty(DirtyFlags.Textures);
} }
public readonly void UpdateImage(ShaderStage stage, ulong binding, TextureBase texture) public readonly void UpdateImage(ShaderStage stage, ulong binding, TextureBase texture)
@ -840,7 +827,7 @@ namespace Ryujinx.Graphics.Metal
_currentState.ImageRefs[binding] = default; _currentState.ImageRefs[binding] = default;
} }
_currentState.Dirty |= DirtyFlags.Images; SignalDirty(DirtyFlags.Images);
} }
public void UpdateTextureArray(ShaderStage stage, ulong binding, TextureArray array) public void UpdateTextureArray(ShaderStage stage, ulong binding, TextureArray array)
@ -851,19 +838,19 @@ namespace Ryujinx.Graphics.Metal
{ {
arrayRef = new EncoderState.ArrayRef<TextureArray>(stage, array); arrayRef = new EncoderState.ArrayRef<TextureArray>(stage, array);
_currentState.Dirty |= DirtyFlags.Textures; SignalDirty(DirtyFlags.Textures);
} }
} }
public void UpdateTextureArraySeparate(ShaderStage stage, int setIndex, TextureArray array) public void UpdateTextureArraySeparate(ShaderStage stage, int setIndex, TextureArray array)
{ {
ref EncoderState.ArrayRef<TextureArray> arrayRef = ref GetArrayRef(ref _currentState.TextureArrayRefs, setIndex); ref EncoderState.ArrayRef<TextureArray> arrayRef = ref GetArrayRef(ref _currentState.TextureArrayExtraRefs, setIndex - MetalRenderer.TotalSets);
if (arrayRef.Stage != stage || arrayRef.Array != array) if (arrayRef.Stage != stage || arrayRef.Array != array)
{ {
arrayRef = new EncoderState.ArrayRef<TextureArray>(stage, array); arrayRef = new EncoderState.ArrayRef<TextureArray>(stage, array);
_currentState.Dirty |= DirtyFlags.Textures; SignalDirty(DirtyFlags.Textures);
} }
} }
@ -875,19 +862,19 @@ namespace Ryujinx.Graphics.Metal
{ {
arrayRef = new EncoderState.ArrayRef<ImageArray>(stage, array); arrayRef = new EncoderState.ArrayRef<ImageArray>(stage, array);
_currentState.Dirty |= DirtyFlags.Images; SignalDirty(DirtyFlags.Images);
} }
} }
public void UpdateImageArraySeparate(ShaderStage stage, int setIndex, ImageArray array) public void UpdateImageArraySeparate(ShaderStage stage, int setIndex, ImageArray array)
{ {
ref EncoderState.ArrayRef<ImageArray> arrayRef = ref GetArrayRef(ref _currentState.ImageArrayExtraRefs, setIndex); ref EncoderState.ArrayRef<ImageArray> arrayRef = ref GetArrayRef(ref _currentState.ImageArrayExtraRefs, setIndex - MetalRenderer.TotalSets);
if (arrayRef.Stage != stage || arrayRef.Array != array) if (arrayRef.Stage != stage || arrayRef.Array != array)
{ {
arrayRef = new EncoderState.ArrayRef<ImageArray>(stage, array); arrayRef = new EncoderState.ArrayRef<ImageArray>(stage, array);
_currentState.Dirty |= DirtyFlags.Images; SignalDirty(DirtyFlags.Images);
} }
} }
@ -1054,7 +1041,7 @@ namespace Ryujinx.Graphics.Metal
renderCommandEncoder.SetVertexBuffer(zeroMtlBuffer, 0, Constants.ZeroBufferIndex); renderCommandEncoder.SetVertexBuffer(zeroMtlBuffer, 0, Constants.ZeroBufferIndex);
} }
private readonly void UpdateAndBind(MTLRenderCommandEncoder renderCommandEncoder, Program program, int setIndex) private readonly void UpdateAndBind(MTLRenderCommandEncoder renderCommandEncoder, Program program, uint setIndex)
{ {
var bindingSegments = program.BindingSegments[setIndex]; var bindingSegments = program.BindingSegments[setIndex];
@ -1089,7 +1076,7 @@ namespace Ryujinx.Graphics.Metal
switch (setIndex) switch (setIndex)
{ {
case MetalRenderer.UniformSetIndex: case Constants.ConstantBuffersSetIndex:
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
int index = binding + i; int index = binding + i;
@ -1139,7 +1126,7 @@ namespace Ryujinx.Graphics.Metal
renderCommandEncoder.UseResource(new MTLResource(mtlBuffer.NativePtr), MTLResourceUsage.Read, renderStages); renderCommandEncoder.UseResource(new MTLResource(mtlBuffer.NativePtr), MTLResourceUsage.Read, renderStages);
} }
break; break;
case MetalRenderer.StorageSetIndex: case Constants.StorageBuffersSetIndex:
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
int index = binding + i; int index = binding + i;
@ -1170,7 +1157,7 @@ namespace Ryujinx.Graphics.Metal
MTLRenderStages renderStages = 0; MTLRenderStages renderStages = 0;
if (segment.Stages.HasFlag(ResourceStages.Vertex)) if ((segment.Stages & ResourceStages.Vertex) != 0)
{ {
vertResourceIds[vertResourceIdIndex] = mtlBuffer.GpuAddress + (ulong)offset; vertResourceIds[vertResourceIdIndex] = mtlBuffer.GpuAddress + (ulong)offset;
vertResourceIdIndex++; vertResourceIdIndex++;
@ -1178,7 +1165,7 @@ namespace Ryujinx.Graphics.Metal
renderStages |= MTLRenderStages.RenderStageVertex; renderStages |= MTLRenderStages.RenderStageVertex;
} }
if (segment.Stages.HasFlag(ResourceStages.Fragment)) if ((segment.Stages & ResourceStages.Fragment) != 0)
{ {
fragResourceIds[fragResourceIdIndex] = mtlBuffer.GpuAddress + (ulong)offset; fragResourceIds[fragResourceIdIndex] = mtlBuffer.GpuAddress + (ulong)offset;
fragResourceIdIndex++; fragResourceIdIndex++;
@ -1189,7 +1176,7 @@ namespace Ryujinx.Graphics.Metal
renderCommandEncoder.UseResource(new MTLResource(mtlBuffer.NativePtr), MTLResourceUsage.Read, renderStages); renderCommandEncoder.UseResource(new MTLResource(mtlBuffer.NativePtr), MTLResourceUsage.Read, renderStages);
} }
break; break;
case MetalRenderer.TextureSetIndex: case Constants.TexturesSetIndex:
if (!segment.IsArray) if (!segment.IsArray)
{ {
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
@ -1247,10 +1234,106 @@ namespace Ryujinx.Graphics.Metal
} }
else else
{ {
// TODO: Texture arrays var textureArray = _currentState.TextureArrayRefs[binding].Array;
if (segment.Type != ResourceType.BufferTexture)
{
var textures = textureArray.GetTextureRefs();
var samplers = new Sampler[textures.Length];
for (int i = 0; i < textures.Length; i++)
{
TextureRef texture = textures[i];
if (texture.Storage == null)
{
continue;
}
var mtlTexture = texture.Storage.GetHandle();
samplers[i] = texture.Sampler;
MTLRenderStages renderStages = 0;
if ((segment.Stages & ResourceStages.Vertex) != 0)
{
vertResourceIds[vertResourceIdIndex] = mtlTexture.GpuResourceID._impl;
vertResourceIdIndex++;
renderStages |= MTLRenderStages.RenderStageVertex;
}
if ((segment.Stages & ResourceStages.Fragment) != 0)
{
fragResourceIds[fragResourceIdIndex] = mtlTexture.GpuResourceID._impl;
fragResourceIdIndex++;
renderStages |= MTLRenderStages.RenderStageFragment;
}
renderCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr),
MTLResourceUsage.Read, renderStages);
}
foreach (var sampler in samplers)
{
if (sampler == null)
{
continue;
}
if ((segment.Stages & ResourceStages.Vertex) != 0)
{
vertResourceIds[vertResourceIdIndex] = sampler.GetSampler().GpuResourceID._impl;
vertResourceIdIndex++;
}
if ((segment.Stages & ResourceStages.Fragment) != 0)
{
fragResourceIds[fragResourceIdIndex] = sampler.GetSampler().GpuResourceID._impl;
fragResourceIdIndex++;
}
}
}
else
{
var bufferTextures = textureArray.GetBufferTextureRefs();
foreach (TextureBuffer bufferTexture in bufferTextures)
{
if (bufferTexture == null)
{
continue;
}
bufferTexture.RebuildStorage(false);
var mtlTexture = bufferTexture.GetHandle();
MTLRenderStages renderStages = 0;
if ((segment.Stages & ResourceStages.Vertex) != 0)
{
vertResourceIds[vertResourceIdIndex] = mtlTexture.GpuResourceID._impl;
vertResourceIdIndex++;
renderStages |= MTLRenderStages.RenderStageVertex;
}
if ((segment.Stages & ResourceStages.Fragment) != 0)
{
fragResourceIds[fragResourceIdIndex] = mtlTexture.GpuResourceID._impl;
fragResourceIdIndex++;
renderStages |= MTLRenderStages.RenderStageFragment;
}
renderCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read, renderStages);
}
}
} }
break; break;
case MetalRenderer.ImageSetIndex: case Constants.ImagesSetIndex:
if (!segment.IsArray) if (!segment.IsArray)
{ {
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
@ -1306,7 +1389,7 @@ namespace Ryujinx.Graphics.Metal
} }
} }
private readonly void UpdateAndBind(MTLComputeCommandEncoder computeCommandEncoder, Program program, int setIndex) private readonly void UpdateAndBind(MTLComputeCommandEncoder computeCommandEncoder, Program program, uint setIndex)
{ {
var bindingSegments = program.BindingSegments[setIndex]; var bindingSegments = program.BindingSegments[setIndex];
@ -1332,7 +1415,7 @@ namespace Ryujinx.Graphics.Metal
switch (setIndex) switch (setIndex)
{ {
case MetalRenderer.UniformSetIndex: case Constants.ConstantBuffersSetIndex:
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
int index = binding + i; int index = binding + i;
@ -1369,7 +1452,7 @@ namespace Ryujinx.Graphics.Metal
} }
} }
break; break;
case MetalRenderer.StorageSetIndex: case Constants.StorageBuffersSetIndex:
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
int index = binding + i; int index = binding + i;
@ -1406,7 +1489,7 @@ namespace Ryujinx.Graphics.Metal
} }
} }
break; break;
case MetalRenderer.TextureSetIndex: case Constants.TexturesSetIndex:
if (!segment.IsArray) if (!segment.IsArray)
{ {
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
@ -1429,7 +1512,7 @@ namespace Ryujinx.Graphics.Metal
var mtlTexture = storage.GetHandle(); var mtlTexture = storage.GetHandle();
if (segment.Stages.HasFlag(ResourceStages.Compute)) if ((segment.Stages & ResourceStages.Compute) != 0)
{ {
computeCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read); computeCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read);
resourceIds[resourceIdIndex] = mtlTexture.GpuResourceID._impl; resourceIds[resourceIdIndex] = mtlTexture.GpuResourceID._impl;
@ -1445,10 +1528,70 @@ namespace Ryujinx.Graphics.Metal
} }
else else
{ {
// TODO: Texture arrays var textureArray = _currentState.TextureArrayRefs[binding].Array;
if (segment.Type != ResourceType.BufferTexture)
{
var textures = textureArray.GetTextureRefs();
var samplers = new Sampler[textures.Length];
for (int i = 0; i < textures.Length; i++)
{
TextureRef texture = textures[i];
if (texture.Storage == null)
{
continue;
}
var mtlTexture = texture.Storage.GetHandle();
if ((segment.Stages & ResourceStages.Compute) != 0)
{
computeCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr),
MTLResourceUsage.Read);
resourceIds[resourceIdIndex] = mtlTexture.GpuResourceID._impl;
resourceIdIndex++;
samplers[i] = texture.Sampler;
}
}
foreach (var sampler in samplers)
{
if (sampler != null)
{
resourceIds[resourceIdIndex] = sampler.GetSampler().GpuResourceID._impl;
resourceIdIndex++;
}
}
}
else
{
var bufferTextures = textureArray.GetBufferTextureRefs();
foreach (TextureBuffer bufferTexture in bufferTextures)
{
if (bufferTexture == null)
{
continue;
}
bufferTexture.RebuildStorage(false);
var mtlTexture = bufferTexture.GetHandle();
if ((segment.Stages & ResourceStages.Compute) != 0)
{
computeCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read);
resourceIds[resourceIdIndex] = mtlTexture.GpuResourceID._impl;
resourceIdIndex++;
}
}
}
} }
break; break;
case MetalRenderer.ImageSetIndex: case Constants.ImagesSetIndex:
if (!segment.IsArray) if (!segment.IsArray)
{ {
if (segment.Type != ResourceType.BufferTexture) if (segment.Type != ResourceType.BufferTexture)
@ -1468,7 +1611,7 @@ namespace Ryujinx.Graphics.Metal
var mtlTexture = storage.GetHandle(); var mtlTexture = storage.GetHandle();
if (segment.Stages.HasFlag(ResourceStages.Compute)) if ((segment.Stages & ResourceStages.Compute) != 0)
{ {
computeCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read | MTLResourceUsage.Write); computeCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read | MTLResourceUsage.Write);
resourceIds[resourceIdIndex] = mtlTexture.GpuResourceID._impl; resourceIds[resourceIdIndex] = mtlTexture.GpuResourceID._impl;
@ -1489,14 +1632,14 @@ namespace Ryujinx.Graphics.Metal
} }
} }
private static uint SetIndexToBindingIndex(int setIndex) private static uint SetIndexToBindingIndex(uint setIndex)
{ {
return setIndex switch return setIndex switch
{ {
MetalRenderer.UniformSetIndex => Constants.ConstantBuffersIndex, Constants.ConstantBuffersSetIndex => Constants.ConstantBuffersIndex,
MetalRenderer.StorageSetIndex => Constants.StorageBuffersIndex, Constants.StorageBuffersSetIndex => Constants.StorageBuffersIndex,
MetalRenderer.TextureSetIndex => Constants.TexturesIndex, Constants.TexturesSetIndex => Constants.TexturesIndex,
MetalRenderer.ImageSetIndex => Constants.ImagesIndex, Constants.ImagesSetIndex => Constants.ImagesIndex,
}; };
} }

View file

@ -123,7 +123,16 @@ namespace Ryujinx.Graphics.Metal
private static string ReadMsl(string fileName) private static string ReadMsl(string fileName)
{ {
return EmbeddedResources.ReadAllText(string.Join('/', ShadersSourcePath, fileName)); var msl = EmbeddedResources.ReadAllText(string.Join('/', ShadersSourcePath, fileName));
#pragma warning disable IDE0055 // Disable formatting
msl = msl.Replace("CONSTANT_BUFFERS_INDEX", $"{Constants.ConstantBuffersIndex}")
.Replace("STORAGE_BUFFERS_INDEX", $"{Constants.StorageBuffersIndex}")
.Replace("TEXTURES_INDEX", $"{Constants.TexturesIndex}")
.Replace("IMAGES_INDEX", $"{Constants.ImagesIndex}");
#pragma warning restore IDE0055
return msl;
} }
public unsafe void BlitColor( public unsafe void BlitColor(

View file

@ -13,11 +13,6 @@ namespace Ryujinx.Graphics.Metal
{ {
public const int TotalSets = 4; public const int TotalSets = 4;
public const int UniformSetIndex = 0;
public const int StorageSetIndex = 1;
public const int TextureSetIndex = 2;
public const int ImageSetIndex = 3;
private readonly MTLDevice _device; private readonly MTLDevice _device;
private readonly MTLCommandQueue _queue; private readonly MTLCommandQueue _queue;
private readonly Func<CAMetalLayer> _getMetalLayer; private readonly Func<CAMetalLayer> _getMetalLayer;
@ -181,8 +176,7 @@ namespace Ryujinx.Graphics.Metal
supportsCubemapView: true, supportsCubemapView: true,
supportsNonConstantTextureOffset: false, supportsNonConstantTextureOffset: false,
supportsQuads: false, supportsQuads: false,
// TODO: Metal Bindless Support supportsSeparateSampler: true,
supportsSeparateSampler: false,
supportsShaderBallot: false, supportsShaderBallot: false,
supportsShaderBarrierDivergence: false, supportsShaderBarrierDivergence: false,
supportsShaderFloat64: false, supportsShaderFloat64: false,
@ -194,12 +188,12 @@ namespace Ryujinx.Graphics.Metal
supportsViewportSwizzle: false, supportsViewportSwizzle: false,
supportsIndirectParameters: true, supportsIndirectParameters: true,
supportsDepthClipControl: false, supportsDepthClipControl: false,
uniformBufferSetIndex: UniformSetIndex, uniformBufferSetIndex: (int)Constants.ConstantBuffersSetIndex,
storageBufferSetIndex: StorageSetIndex, storageBufferSetIndex: (int)Constants.StorageBuffersSetIndex,
textureSetIndex: TextureSetIndex, textureSetIndex: (int)Constants.TexturesSetIndex,
imageSetIndex: ImageSetIndex, imageSetIndex: (int)Constants.ImagesSetIndex,
extraSetBaseIndex: 0, extraSetBaseIndex: TotalSets,
maximumExtraSets: 0, maximumExtraSets: (int)Constants.MaximumExtraSets,
maximumUniformBuffersPerStage: Constants.MaxUniformBuffersPerStage, maximumUniformBuffersPerStage: Constants.MaxUniformBuffersPerStage,
maximumStorageBuffersPerStage: Constants.MaxStorageBuffersPerStage, maximumStorageBuffersPerStage: Constants.MaxStorageBuffersPerStage,
maximumTexturesPerStage: Constants.MaxTexturesPerStage, maximumTexturesPerStage: Constants.MaxTexturesPerStage,

View file

@ -27,12 +27,12 @@ namespace Ryujinx.Graphics.Metal
public ResourceLayoutBuilder Add(ResourceStages stages, ResourceType type, int binding, bool write = false) public ResourceLayoutBuilder Add(ResourceStages stages, ResourceType type, int binding, bool write = false)
{ {
int setIndex = type switch uint setIndex = type switch
{ {
ResourceType.UniformBuffer => MetalRenderer.UniformSetIndex, ResourceType.UniformBuffer => Constants.ConstantBuffersSetIndex,
ResourceType.StorageBuffer => MetalRenderer.StorageSetIndex, ResourceType.StorageBuffer => Constants.StorageBuffersSetIndex,
ResourceType.TextureAndSampler or ResourceType.BufferTexture => MetalRenderer.TextureSetIndex, ResourceType.TextureAndSampler or ResourceType.BufferTexture => Constants.TexturesSetIndex,
ResourceType.Image or ResourceType.BufferImage => MetalRenderer.ImageSetIndex, ResourceType.Image or ResourceType.BufferImage => Constants.ImagesSetIndex,
_ => throw new ArgumentException($"Invalid resource type \"{type}\"."), _ => throw new ArgumentException($"Invalid resource type \"{type}\"."),
}; };

View file

@ -22,7 +22,7 @@ struct Textures
}; };
vertex CopyVertexOut vertexMain(uint vid [[vertex_id]], vertex CopyVertexOut vertexMain(uint vid [[vertex_id]],
constant ConstantBuffers &constant_buffers [[buffer(20)]]) { constant ConstantBuffers &constant_buffers [[buffer(CONSTANT_BUFFERS_INDEX)]]) {
CopyVertexOut out; CopyVertexOut out;
int low = vid & 1; int low = vid & 1;
@ -38,6 +38,6 @@ vertex CopyVertexOut vertexMain(uint vid [[vertex_id]],
} }
fragment float4 fragmentMain(CopyVertexOut in [[stage_in]], fragment float4 fragmentMain(CopyVertexOut in [[stage_in]],
constant Textures &textures [[buffer(22)]]) { constant Textures &textures [[buffer(TEXTURES_INDEX)]]) {
return textures.texture.sample(textures.sampler, in.uv); return textures.texture.sample(textures.sampler, in.uv);
} }

View file

@ -13,7 +13,7 @@ struct Textures
}; };
fragment float4 fragmentMain(CopyVertexOut in [[stage_in]], fragment float4 fragmentMain(CopyVertexOut in [[stage_in]],
constant Textures &textures [[buffer(22)]], constant Textures &textures [[buffer(TEXTURES_INDEX)]],
uint sample_id [[sample_id]]) { uint sample_id [[sample_id]]) {
uint2 tex_size = uint2(textures.texture.get_width(), textures.texture.get_height()); uint2 tex_size = uint2(textures.texture.get_width(), textures.texture.get_height());
uint2 tex_coord = uint2(in.uv * float2(tex_size)); uint2 tex_coord = uint2(in.uv * float2(tex_size));

View file

@ -23,8 +23,8 @@ struct StorageBuffers {
device OutData* out_data; device OutData* out_data;
}; };
kernel void kernelMain(constant ConstantBuffers &constant_buffers [[buffer(20)]], kernel void kernelMain(constant ConstantBuffers &constant_buffers [[buffer(CONSTANT_BUFFERS_INDEX)]],
device StorageBuffers &storage_buffers [[buffer(21)]], device StorageBuffers &storage_buffers [[buffer(STORAGE_BUFFERS_INDEX)]],
uint3 thread_position_in_grid [[thread_position_in_grid]], uint3 thread_position_in_grid [[thread_position_in_grid]],
uint3 threads_per_threadgroup [[threads_per_threadgroup]], uint3 threads_per_threadgroup [[threads_per_threadgroup]],
uint3 threadgroups_per_grid [[threads_per_grid]]) uint3 threadgroups_per_grid [[threads_per_grid]])

View file

@ -33,6 +33,6 @@ struct FragmentOut {
}; };
fragment FragmentOut fragmentMain(VertexOut in [[stage_in]], fragment FragmentOut fragmentMain(VertexOut in [[stage_in]],
constant ConstantBuffers &constant_buffers [[buffer(20)]]) { constant ConstantBuffers &constant_buffers [[buffer(CONSTANT_BUFFERS_INDEX)]]) {
return {constant_buffers.clear_color->data}; return {constant_buffers.clear_color->data};
} }

View file

@ -18,7 +18,7 @@ struct FragmentOut {
}; };
fragment FragmentOut fragmentMain(CopyVertexOut in [[stage_in]], fragment FragmentOut fragmentMain(CopyVertexOut in [[stage_in]],
constant Textures &textures [[buffer(22)]]) { constant Textures &textures [[buffer(TEXTURES_INDEX)]]) {
FragmentOut out; FragmentOut out;
out.depth = textures.texture.sample(textures.sampler, in.uv).r; out.depth = textures.texture.sample(textures.sampler, in.uv).r;

View file

@ -17,7 +17,7 @@ struct FragmentOut {
}; };
fragment FragmentOut fragmentMain(CopyVertexOut in [[stage_in]], fragment FragmentOut fragmentMain(CopyVertexOut in [[stage_in]],
constant Textures &textures [[buffer(22)]], constant Textures &textures [[buffer(TEXTURES_INDEX)]],
uint sample_id [[sample_id]]) { uint sample_id [[sample_id]]) {
FragmentOut out; FragmentOut out;

View file

@ -33,7 +33,7 @@ vertex VertexOut vertexMain(ushort vid [[vertex_id]]) {
} }
fragment FragmentOut fragmentMain(VertexOut in [[stage_in]], fragment FragmentOut fragmentMain(VertexOut in [[stage_in]],
constant ConstantBuffers &constant_buffers [[buffer(20)]]) { constant ConstantBuffers &constant_buffers [[buffer(CONSTANT_BUFFERS_INDEX)]]) {
FragmentOut out; FragmentOut out;
out.depth = constant_buffers.clear_depth->data; out.depth = constant_buffers.clear_depth->data;

View file

@ -18,7 +18,7 @@ struct FragmentOut {
}; };
fragment FragmentOut fragmentMain(CopyVertexOut in [[stage_in]], fragment FragmentOut fragmentMain(CopyVertexOut in [[stage_in]],
constant Textures &textures [[buffer(22)]]) { constant Textures &textures [[buffer(TEXTURES_INDEX)]]) {
FragmentOut out; FragmentOut out;
out.stencil = textures.texture.sample(textures.sampler, in.uv).r; out.stencil = textures.texture.sample(textures.sampler, in.uv).r;

View file

@ -17,7 +17,7 @@ struct FragmentOut {
}; };
fragment FragmentOut fragmentMain(CopyVertexOut in [[stage_in]], fragment FragmentOut fragmentMain(CopyVertexOut in [[stage_in]],
constant Textures &textures [[buffer(22)]], constant Textures &textures [[buffer(TEXTURES_INDEX)]],
uint sample_id [[sample_id]]) { uint sample_id [[sample_id]]) {
FragmentOut out; FragmentOut out;

View file

@ -73,6 +73,16 @@ namespace Ryujinx.Graphics.Metal
SetDirty(); SetDirty();
} }
public TextureRef[] GetTextureRefs()
{
return _textureRefs;
}
public TextureBuffer[] GetBufferTextureRefs()
{
return _bufferTextureRefs;
}
private void SetDirty() private void SetDirty()
{ {
_pipeline.DirtyTextures(); _pipeline.DirtyTextures();

View file

@ -58,7 +58,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl
* *
*/ */
public static void Declare(CodeGenContext context, StructuredProgramInfo info) public static int[] Declare(CodeGenContext context, StructuredProgramInfo info)
{ {
// TODO: Re-enable this warning // TODO: Re-enable this warning
context.AppendLine("#pragma clang diagnostic ignored \"-Wunused-variable\""); context.AppendLine("#pragma clang diagnostic ignored \"-Wunused-variable\"");
@ -75,10 +75,32 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl
context.AppendLine(); context.AppendLine();
DeclareOutputAttributes(context, info.IoDefinitions.Where(x => x.StorageKind == StorageKind.Output)); DeclareOutputAttributes(context, info.IoDefinitions.Where(x => x.StorageKind == StorageKind.Output));
context.AppendLine(); context.AppendLine();
DeclareBufferStructures(context, context.Properties.ConstantBuffers.Values, true, fsi); DeclareBufferStructures(context, context.Properties.ConstantBuffers.Values.OrderBy(x => x.Binding).ToArray(), true, fsi);
DeclareBufferStructures(context, context.Properties.StorageBuffers.Values, false, fsi); DeclareBufferStructures(context, context.Properties.StorageBuffers.Values.OrderBy(x => x.Binding).ToArray(), false, fsi);
DeclareTextures(context, context.Properties.Textures.Values);
DeclareImages(context, context.Properties.Images.Values, fsi); // We need to declare each set as a new struct
var textureDefinitions = context.Properties.Textures.Values
.GroupBy(x => x.Set)
.ToDictionary(x => x.Key, x => x.OrderBy(y => y.Binding).ToArray());
var imageDefinitions = context.Properties.Images.Values
.GroupBy(x => x.Set)
.ToDictionary(x => x.Key, x => x.OrderBy(y => y.Binding).ToArray());
var textureSets = textureDefinitions.Keys.ToArray();
var imageSets = imageDefinitions.Keys.ToArray();
var sets = textureSets.Union(imageSets).ToArray();
foreach (var set in textureDefinitions)
{
DeclareTextures(context, set.Value, set.Key);
}
foreach (var set in imageDefinitions)
{
DeclareImages(context, set.Value, set.Key, fsi);
}
if ((info.HelperFunctionsMask & HelperFunctionsMask.FindLSB) != 0) if ((info.HelperFunctionsMask & HelperFunctionsMask.FindLSB) != 0)
{ {
@ -99,6 +121,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl
{ {
AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/SwizzleAdd.metal"); AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Msl/HelperFunctions/SwizzleAdd.metal");
} }
return sets;
} }
static bool IsUserDefined(IoDefinition ioDefinition, StorageKind storageKind) static bool IsUserDefined(IoDefinition ioDefinition, StorageKind storageKind)
@ -186,22 +210,21 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl
} }
} }
private static void DeclareBufferStructures(CodeGenContext context, IEnumerable<BufferDefinition> buffers, bool constant, bool fsi) private static void DeclareBufferStructures(CodeGenContext context, BufferDefinition[] buffers, bool constant, bool fsi)
{ {
var name = constant ? "ConstantBuffers" : "StorageBuffers"; var name = constant ? "ConstantBuffers" : "StorageBuffers";
var addressSpace = constant ? "constant" : "device"; var addressSpace = constant ? "constant" : "device";
List<string> argBufferPointers = []; string[] bufferDec = new string[buffers.Length];
// TODO: Avoid Linq if we can for (int i = 0; i < buffers.Length; i++)
var sortedBuffers = buffers.OrderBy(x => x.Binding).ToArray();
foreach (BufferDefinition buffer in sortedBuffers)
{ {
BufferDefinition buffer = buffers[i];
var needsPadding = buffer.Layout == BufferLayout.Std140; var needsPadding = buffer.Layout == BufferLayout.Std140;
string fsiSuffix = constant && fsi ? " [[raster_order_group(0)]]" : ""; string fsiSuffix = constant && fsi ? " [[raster_order_group(0)]]" : "";
argBufferPointers.Add($"{addressSpace} {Defaults.StructPrefix}_{buffer.Name}* {buffer.Name}{fsiSuffix};"); bufferDec[i] = $"{addressSpace} {Defaults.StructPrefix}_{buffer.Name}* {buffer.Name}{fsiSuffix};";
context.AppendLine($"struct {Defaults.StructPrefix}_{buffer.Name}"); context.AppendLine($"struct {Defaults.StructPrefix}_{buffer.Name}");
context.EnterScope(); context.EnterScope();
@ -209,7 +232,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl
foreach (StructureField field in buffer.Type.Fields) foreach (StructureField field in buffer.Type.Fields)
{ {
var type = field.Type; var type = field.Type;
type |= (needsPadding && (field.Type & AggregateType.Array) != 0) ? AggregateType.Vector4 : AggregateType.Invalid; type |= (needsPadding && (field.Type & AggregateType.Array) != 0)
? AggregateType.Vector4
: AggregateType.Invalid;
type &= ~AggregateType.Array; type &= ~AggregateType.Array;
@ -239,66 +264,85 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl
context.AppendLine($"struct {name}"); context.AppendLine($"struct {name}");
context.EnterScope(); context.EnterScope();
foreach (var pointer in argBufferPointers) foreach (var declaration in bufferDec)
{ {
context.AppendLine(pointer); context.AppendLine(declaration);
} }
context.LeaveScope(";"); context.LeaveScope(";");
context.AppendLine(); context.AppendLine();
} }
private static void DeclareTextures(CodeGenContext context, IEnumerable<TextureDefinition> textures) private static void DeclareTextures(CodeGenContext context, TextureDefinition[] textures, int set)
{ {
context.AppendLine("struct Textures"); var setName = GetNameForSet(set);
context.AppendLine($"struct {setName}");
context.EnterScope(); context.EnterScope();
List<string> argBufferPointers = []; List<string> textureDec = [];
// TODO: Avoid Linq if we can foreach (TextureDefinition texture in textures)
var sortedTextures = textures.OrderBy(x => x.Binding).ToArray(); {
if (texture.Type != SamplerType.None)
foreach (TextureDefinition texture in sortedTextures)
{ {
var textureTypeName = texture.Type.ToMslTextureType(); var textureTypeName = texture.Type.ToMslTextureType();
argBufferPointers.Add($"{textureTypeName} tex_{texture.Name};");
if (texture.ArrayLength > 1)
{
textureTypeName = $"array<{textureTypeName}, {texture.ArrayLength}>";
}
textureDec.Add($"{textureTypeName} tex_{texture.Name};");
}
if (!texture.Separate && texture.Type != SamplerType.TextureBuffer) if (!texture.Separate && texture.Type != SamplerType.TextureBuffer)
{ {
argBufferPointers.Add($"sampler samp_{texture.Name};"); var samplerType = "sampler";
if (texture.ArrayLength > 1)
{
samplerType = $"array<{samplerType}, {texture.ArrayLength}>";
}
textureDec.Add($"{samplerType} samp_{texture.Name};");
} }
} }
foreach (var pointer in argBufferPointers) foreach (var declaration in textureDec)
{ {
context.AppendLine(pointer); context.AppendLine(declaration);
} }
context.LeaveScope(";"); context.LeaveScope(";");
context.AppendLine(); context.AppendLine();
} }
private static void DeclareImages(CodeGenContext context, IEnumerable<TextureDefinition> images, bool fsi) private static void DeclareImages(CodeGenContext context, TextureDefinition[] images, int set, bool fsi)
{ {
context.AppendLine("struct Images"); var setName = GetNameForSet(set);
context.AppendLine($"struct {setName}");
context.EnterScope(); context.EnterScope();
List<string> argBufferPointers = []; string[] imageDec = new string[images.Length];
// TODO: Avoid Linq if we can for (int i = 0; i < images.Length; i++)
var sortedImages = images.OrderBy(x => x.Binding).ToArray();
foreach (TextureDefinition image in sortedImages)
{ {
var imageTypeName = image.Type.ToMslTextureType(true); TextureDefinition image = images[i];
string fsiSuffix = fsi ? " [[raster_order_group(0)]]" : "";
argBufferPointers.Add($"{imageTypeName} {image.Name}{fsiSuffix};"); var imageTypeName = image.Type.ToMslTextureType(true);
if (image.ArrayLength > 1)
{
imageTypeName = $"array<{imageTypeName}, {image.ArrayLength}>";
} }
foreach (var pointer in argBufferPointers) string fsiSuffix = fsi ? " [[raster_order_group(0)]]" : "";
imageDec[i] = $"{imageTypeName} {image.Name}{fsiSuffix};";
}
foreach (var declaration in imageDec)
{ {
context.AppendLine(pointer); context.AppendLine(declaration);
} }
context.LeaveScope(";"); context.LeaveScope(";");
@ -483,5 +527,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl
context.AppendLine(code); context.AppendLine(code);
context.AppendLine(); context.AppendLine();
} }
public static string GetNameForSet(int set, bool forVar = false)
{
return (uint)set switch
{
Defaults.TexturesSetIndex => forVar ? "textures" : "Textures",
Defaults.ImagesSetIndex => forVar ? "images" : "Images",
_ => $"{(forVar ? "set" : "Set")}{set}"
};
}
} }
} }

View file

@ -14,14 +14,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl
public const string UndefinedName = "0"; public const string UndefinedName = "0";
public const int MaxUniformBuffersPerStage = 18; public const int MaxVertexBuffers = 16;
public const int MaxStorageBuffersPerStage = 16;
public const int MaxTexturesPerStage = 64;
public const uint ConstantBuffersIndex = 20; public const uint ZeroBufferIndex = MaxVertexBuffers;
public const uint StorageBuffersIndex = 21; public const uint BaseSetIndex = MaxVertexBuffers + 1;
public const uint TexturesIndex = 22;
public const uint ImagesIndex = 23; public const uint ConstantBuffersIndex = BaseSetIndex;
public const uint StorageBuffersIndex = BaseSetIndex + 1;
public const uint TexturesIndex = BaseSetIndex + 2;
public const uint ImagesIndex = BaseSetIndex + 3;
public const uint ConstantBuffersSetIndex = 0;
public const uint StorageBuffersSetIndex = 1;
public const uint TexturesSetIndex = 2;
public const uint ImagesSetIndex = 3;
public const int TotalClipDistances = 8; public const int TotalClipDistances = 8;
} }

View file

@ -494,13 +494,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions
{ {
TextureDefinition textureDefinition = context.Properties.Textures[texOp.GetTextureSetAndBinding()]; TextureDefinition textureDefinition = context.Properties.Textures[texOp.GetTextureSetAndBinding()];
string name = textureDefinition.Name; string name = textureDefinition.Name;
string setName = Declarations.GetNameForSet(textureDefinition.Set, true);
if (textureDefinition.ArrayLength != 1) if (textureDefinition.ArrayLength != 1)
{ {
name = $"{name}[{GetSourceExpr(context, texOp.GetSource(srcIndex++), AggregateType.S32)}]"; name = $"{name}[{GetSourceExpr(context, texOp.GetSource(srcIndex++), AggregateType.S32)}]";
} }
return $"textures.tex_{name}"; return $"{setName}.tex_{name}";
} }
private static string GetSamplerName(CodeGenContext context, AstTextureOperation texOp, ref int srcIndex) private static string GetSamplerName(CodeGenContext context, AstTextureOperation texOp, ref int srcIndex)
@ -510,26 +511,28 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions
TextureDefinition samplerDefinition = context.Properties.Textures[index]; TextureDefinition samplerDefinition = context.Properties.Textures[index];
string name = samplerDefinition.Name; string name = samplerDefinition.Name;
string setName = Declarations.GetNameForSet(samplerDefinition.Set, true);
if (samplerDefinition.ArrayLength != 1) if (samplerDefinition.ArrayLength != 1)
{ {
name = $"{name}[{GetSourceExpr(context, texOp.GetSource(sourceIndex), AggregateType.S32)}]"; name = $"{name}[{GetSourceExpr(context, texOp.GetSource(sourceIndex), AggregateType.S32)}]";
} }
return $"textures.samp_{name}"; return $"{setName}.samp_{name}";
} }
private static string GetImageName(CodeGenContext context, AstTextureOperation texOp, ref int srcIndex) private static string GetImageName(CodeGenContext context, AstTextureOperation texOp, ref int srcIndex)
{ {
TextureDefinition definition = context.Properties.Images[texOp.GetTextureSetAndBinding()]; TextureDefinition imageDefinition = context.Properties.Images[texOp.GetTextureSetAndBinding()];
string name = definition.Name; string name = imageDefinition.Name;
string setName = Declarations.GetNameForSet(imageDefinition.Set, true);
if (definition.ArrayLength != 1) if (imageDefinition.ArrayLength != 1)
{ {
name = $"{name}[{GetSourceExpr(context, texOp.GetSource(srcIndex++), AggregateType.S32)}]"; name = $"{name}[{GetSourceExpr(context, texOp.GetSource(srcIndex++), AggregateType.S32)}]";
} }
return $"images.{name}"; return $"{setName}.{name}";
} }
private static string GetMaskMultiDest(int mask) private static string GetMaskMultiDest(int mask)

View file

@ -20,28 +20,28 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl
CodeGenContext context = new(info, parameters); CodeGenContext context = new(info, parameters);
Declarations.Declare(context, info); var sets = Declarations.Declare(context, info);
if (info.Functions.Count != 0) if (info.Functions.Count != 0)
{ {
for (int i = 1; i < info.Functions.Count; i++) for (int i = 1; i < info.Functions.Count; i++)
{ {
PrintFunction(context, info.Functions[i], parameters.Definitions.Stage); PrintFunction(context, info.Functions[i], parameters.Definitions.Stage, sets);
context.AppendLine(); context.AppendLine();
} }
} }
PrintFunction(context, info.Functions[0], parameters.Definitions.Stage, true); PrintFunction(context, info.Functions[0], parameters.Definitions.Stage, sets, true);
return context.GetCode(); return context.GetCode();
} }
private static void PrintFunction(CodeGenContext context, StructuredFunction function, ShaderStage stage, bool isMainFunc = false) private static void PrintFunction(CodeGenContext context, StructuredFunction function, ShaderStage stage, int[] sets, bool isMainFunc = false)
{ {
context.CurrentFunction = function; context.CurrentFunction = function;
context.AppendLine(GetFunctionSignature(context, function, stage, isMainFunc)); context.AppendLine(GetFunctionSignature(context, function, stage, sets, isMainFunc));
context.EnterScope(); context.EnterScope();
Declarations.DeclareLocals(context, function, stage, isMainFunc); Declarations.DeclareLocals(context, function, stage, isMainFunc);
@ -61,6 +61,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl
CodeGenContext context, CodeGenContext context,
StructuredFunction function, StructuredFunction function,
ShaderStage stage, ShaderStage stage,
int[] sets,
bool isMainFunc = false) bool isMainFunc = false)
{ {
int additionalArgCount = isMainFunc ? 0 : CodeGenContext.AdditionalArgCount + (context.Definitions.Stage != ShaderStage.Compute ? 1 : 0); int additionalArgCount = isMainFunc ? 0 : CodeGenContext.AdditionalArgCount + (context.Definitions.Stage != ShaderStage.Compute ? 1 : 0);
@ -166,8 +167,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl
args = args.Append($"constant ConstantBuffers &constant_buffers [[buffer({Defaults.ConstantBuffersIndex})]]").ToArray(); args = args.Append($"constant ConstantBuffers &constant_buffers [[buffer({Defaults.ConstantBuffersIndex})]]").ToArray();
args = args.Append($"device StorageBuffers &storage_buffers [[buffer({Defaults.StorageBuffersIndex})]]").ToArray(); args = args.Append($"device StorageBuffers &storage_buffers [[buffer({Defaults.StorageBuffersIndex})]]").ToArray();
args = args.Append($"constant Textures &textures [[buffer({Defaults.TexturesIndex})]]").ToArray();
args = args.Append($"constant Images &images [[buffer({Defaults.ImagesIndex})]]").ToArray(); foreach (var set in sets)
{
var bindingIndex = set + Defaults.BaseSetIndex;
args = args.Append($"constant {Declarations.GetNameForSet(set)} &{Declarations.GetNameForSet(set, true)} [[buffer({bindingIndex})]]").ToArray();
}
} }
var funcPrefix = $"{funcKeyword} {returnType} {funcName ?? function.Name}("; var funcPrefix = $"{funcKeyword} {returnType} {funcName ?? function.Name}(";

View file

@ -192,7 +192,7 @@ namespace Ryujinx.Graphics.Shader
typeName += "_array"; typeName += "_array";
} }
return $"{typeName} <float{(image ? ", access::read_write" : "")}>"; return $"{typeName}<float{(image ? ", access::read_write" : "")}>";
} }
} }
} }