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
committed by Evan Husted
parent 5b88ea66ba
commit 80bb95dfb9
20 changed files with 412 additions and 181 deletions

View File

@@ -2,7 +2,6 @@ namespace Ryujinx.Graphics.Metal
{
static class Constants
{
// TODO: Check these values, these were largely copied from Vulkan
public const int MaxShaderStages = 5;
public const int MaxVertexBuffers = 16;
public const int MaxUniformBuffersPerStage = 18;
@@ -15,17 +14,25 @@ namespace Ryujinx.Graphics.Metal
public const int MaxViewports = 16;
// TODO: Check this value
public const int MaxVertexAttributes = 31;
// TODO: Check this value
public const int MaxVertexLayouts = 31;
public const int MinResourceAlignment = 16;
// 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 StorageBuffersIndex = 21;
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 uint MaximumBufferArgumentTableEntries = 31;
public const uint MaximumExtraSets = MaximumBufferArgumentTableEntries - ImagesIndex;
}
}

View File

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

View File

@@ -123,7 +123,16 @@ namespace Ryujinx.Graphics.Metal
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(

View File

@@ -13,11 +13,6 @@ namespace Ryujinx.Graphics.Metal
{
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 MTLCommandQueue _queue;
private readonly Func<CAMetalLayer> _getMetalLayer;
@@ -181,8 +176,7 @@ namespace Ryujinx.Graphics.Metal
supportsCubemapView: true,
supportsNonConstantTextureOffset: false,
supportsQuads: false,
// TODO: Metal Bindless Support
supportsSeparateSampler: false,
supportsSeparateSampler: true,
supportsShaderBallot: false,
supportsShaderBarrierDivergence: false,
supportsShaderFloat64: false,
@@ -194,12 +188,12 @@ namespace Ryujinx.Graphics.Metal
supportsViewportSwizzle: false,
supportsIndirectParameters: true,
supportsDepthClipControl: false,
uniformBufferSetIndex: UniformSetIndex,
storageBufferSetIndex: StorageSetIndex,
textureSetIndex: TextureSetIndex,
imageSetIndex: ImageSetIndex,
extraSetBaseIndex: 0,
maximumExtraSets: 0,
uniformBufferSetIndex: (int)Constants.ConstantBuffersSetIndex,
storageBufferSetIndex: (int)Constants.StorageBuffersSetIndex,
textureSetIndex: (int)Constants.TexturesSetIndex,
imageSetIndex: (int)Constants.ImagesSetIndex,
extraSetBaseIndex: TotalSets,
maximumExtraSets: (int)Constants.MaximumExtraSets,
maximumUniformBuffersPerStage: Constants.MaxUniformBuffersPerStage,
maximumStorageBuffersPerStage: Constants.MaxStorageBuffersPerStage,
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)
{
int setIndex = type switch
uint setIndex = type switch
{
ResourceType.UniformBuffer => MetalRenderer.UniformSetIndex,
ResourceType.StorageBuffer => MetalRenderer.StorageSetIndex,
ResourceType.TextureAndSampler or ResourceType.BufferTexture => MetalRenderer.TextureSetIndex,
ResourceType.Image or ResourceType.BufferImage => MetalRenderer.ImageSetIndex,
ResourceType.UniformBuffer => Constants.ConstantBuffersSetIndex,
ResourceType.StorageBuffer => Constants.StorageBuffersSetIndex,
ResourceType.TextureAndSampler or ResourceType.BufferTexture => Constants.TexturesSetIndex,
ResourceType.Image or ResourceType.BufferImage => Constants.ImagesSetIndex,
_ => throw new ArgumentException($"Invalid resource type \"{type}\"."),
};

View File

@@ -22,7 +22,7 @@ struct Textures
};
vertex CopyVertexOut vertexMain(uint vid [[vertex_id]],
constant ConstantBuffers &constant_buffers [[buffer(20)]]) {
constant ConstantBuffers &constant_buffers [[buffer(CONSTANT_BUFFERS_INDEX)]]) {
CopyVertexOut out;
int low = vid & 1;
@@ -38,6 +38,6 @@ vertex CopyVertexOut vertexMain(uint vid [[vertex_id]],
}
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);
}

View File

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

View File

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

View File

@@ -33,6 +33,6 @@ struct FragmentOut {
};
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};
}

View File

@@ -18,7 +18,7 @@ struct FragmentOut {
};
fragment FragmentOut fragmentMain(CopyVertexOut in [[stage_in]],
constant Textures &textures [[buffer(22)]]) {
constant Textures &textures [[buffer(TEXTURES_INDEX)]]) {
FragmentOut out;
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]],
constant Textures &textures [[buffer(22)]],
constant Textures &textures [[buffer(TEXTURES_INDEX)]],
uint sample_id [[sample_id]]) {
FragmentOut out;

View File

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

View File

@@ -18,7 +18,7 @@ struct FragmentOut {
};
fragment FragmentOut fragmentMain(CopyVertexOut in [[stage_in]],
constant Textures &textures [[buffer(22)]]) {
constant Textures &textures [[buffer(TEXTURES_INDEX)]]) {
FragmentOut out;
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]],
constant Textures &textures [[buffer(22)]],
constant Textures &textures [[buffer(TEXTURES_INDEX)]],
uint sample_id [[sample_id]]) {
FragmentOut out;

View File

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