Metal: Better Bindings (#29)
* Tell GAL to use Vk model (and break everything) * ResourceBindingSegments * Set information on backend caps * Get ready to break everything * Refactor EncoderStateManager * Remove padding from helper shaders * Fix ref array sizes * Seperate vert & frag buffers * Shader-side changes * Fixes * Fix some helper shader resource layouts * Sort by binding id * Fix helper shader layouts * Don’t do inline vertex buffer updates * Check for null storage
This commit is contained in:
parent
971c270bcf
commit
daee63c451
12 changed files with 709 additions and 453 deletions
|
@ -55,7 +55,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
{
|
||||
int binding;
|
||||
|
||||
if (_context.Capabilities.Api == TargetApi.Vulkan)
|
||||
if (_context.Capabilities.Api != TargetApi.OpenGL)
|
||||
{
|
||||
binding = GetBindingFromIndex(index, _context.Capabilities.MaximumUniformBuffersPerStage, "Uniform buffer");
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
{
|
||||
int binding;
|
||||
|
||||
if (_context.Capabilities.Api == TargetApi.Vulkan)
|
||||
if (_context.Capabilities.Api != TargetApi.OpenGL)
|
||||
{
|
||||
if (count == 1)
|
||||
{
|
||||
|
@ -103,7 +103,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
{
|
||||
int binding;
|
||||
|
||||
if (_context.Capabilities.Api == TargetApi.Vulkan)
|
||||
if (_context.Capabilities.Api != TargetApi.OpenGL)
|
||||
{
|
||||
binding = GetBindingFromIndex(index, _context.Capabilities.MaximumStorageBuffersPerStage, "Storage buffer");
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
{
|
||||
int binding;
|
||||
|
||||
if (_context.Capabilities.Api == TargetApi.Vulkan)
|
||||
if (_context.Capabilities.Api != TargetApi.OpenGL)
|
||||
{
|
||||
if (count == 1)
|
||||
{
|
||||
|
|
|
@ -8,6 +8,8 @@ namespace Ryujinx.Graphics.Metal
|
|||
public const int MaxUniformBuffersPerStage = 18;
|
||||
public const int MaxStorageBuffersPerStage = 16;
|
||||
public const int MaxTexturesPerStage = 64;
|
||||
public const int MaxUniformBufferBindings = MaxUniformBuffersPerStage * MaxShaderStages;
|
||||
public const int MaxStorageBufferBindings = MaxStorageBuffersPerStage * MaxShaderStages;
|
||||
public const int MaxTextureBindings = MaxTexturesPerStage * MaxShaderStages;
|
||||
public const int MaxColorAttachments = 8;
|
||||
// TODO: Check this value
|
||||
|
@ -18,9 +20,11 @@ namespace Ryujinx.Graphics.Metal
|
|||
public const int MinResourceAlignment = 16;
|
||||
|
||||
// Must match constants set in shader generation
|
||||
public const uint ZeroBufferIndex = 18;
|
||||
|
||||
public const uint ConstantBuffersIndex = 20;
|
||||
public const uint StorageBuffersIndex = 21;
|
||||
public const uint ZeroBufferIndex = 18;
|
||||
public const uint TexturesIndex = 22;
|
||||
public const uint ImagessIndex = 23;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Metal.State;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
using SharpMetal.Metal;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
@ -22,13 +23,13 @@ namespace Ryujinx.Graphics.Metal
|
|||
StencilRef = 1 << 7,
|
||||
Viewports = 1 << 8,
|
||||
Scissors = 1 << 9,
|
||||
Buffers = 1 << 10,
|
||||
VertexTextures = 1 << 11,
|
||||
FragmentTextures = 1 << 12,
|
||||
ComputeTextures = 1 << 13,
|
||||
Uniforms = 1 << 10,
|
||||
Storages = 1 << 11,
|
||||
Textures = 1 << 12,
|
||||
Images = 1 << 13,
|
||||
|
||||
RenderAll = RenderPipeline | DepthStencil | DepthClamp | DepthBias | CullMode | FrontFace | StencilRef | Viewports | Scissors | Buffers | VertexTextures | FragmentTextures,
|
||||
ComputeAll = ComputePipeline | Buffers | ComputeTextures,
|
||||
RenderAll = RenderPipeline | DepthStencil | DepthClamp | DepthBias | CullMode | FrontFace | StencilRef | Viewports | Scissors | Uniforms | Storages | Textures | Images,
|
||||
ComputeAll = ComputePipeline | Uniforms | Storages | Textures | Images,
|
||||
All = RenderAll | ComputeAll,
|
||||
}
|
||||
|
||||
|
@ -49,6 +50,20 @@ namespace Ryujinx.Graphics.Metal
|
|||
}
|
||||
}
|
||||
|
||||
record struct TextureRef
|
||||
{
|
||||
public ShaderStage Stage;
|
||||
public Texture Storage;
|
||||
public Sampler Sampler;
|
||||
|
||||
public TextureRef(ShaderStage stage, Texture storage, Sampler sampler)
|
||||
{
|
||||
Stage = stage;
|
||||
Storage = storage;
|
||||
Sampler = sampler;
|
||||
}
|
||||
}
|
||||
|
||||
struct PredrawState
|
||||
{
|
||||
public MTLCullMode CullMode;
|
||||
|
@ -73,17 +88,9 @@ namespace Ryujinx.Graphics.Metal
|
|||
public PipelineState Pipeline;
|
||||
public DepthStencilUid DepthStencilUid;
|
||||
|
||||
public TextureBase[] FragmentTextures = new TextureBase[Constants.MaxTexturesPerStage];
|
||||
public MTLSamplerState[] FragmentSamplers = new MTLSamplerState[Constants.MaxTexturesPerStage];
|
||||
|
||||
public TextureBase[] VertexTextures = new TextureBase[Constants.MaxTexturesPerStage];
|
||||
public MTLSamplerState[] VertexSamplers = new MTLSamplerState[Constants.MaxTexturesPerStage];
|
||||
|
||||
public TextureBase[] ComputeTextures = new TextureBase[Constants.MaxTexturesPerStage];
|
||||
public MTLSamplerState[] ComputeSamplers = new MTLSamplerState[Constants.MaxTexturesPerStage];
|
||||
|
||||
public BufferRef[] UniformBuffers = new BufferRef[Constants.MaxUniformBuffersPerStage];
|
||||
public BufferRef[] StorageBuffers = new BufferRef[Constants.MaxStorageBuffersPerStage];
|
||||
public readonly BufferRef[] UniformBufferRefs = new BufferRef[Constants.MaxUniformBufferBindings];
|
||||
public readonly BufferRef[] StorageBufferRefs = new BufferRef[Constants.MaxStorageBufferBindings];
|
||||
public readonly TextureRef[] TextureRefs = new TextureRef[Constants.MaxTextureBindings];
|
||||
|
||||
public Auto<DisposableBuffer> IndexBuffer = default;
|
||||
public MTLIndexType IndexType = MTLIndexType.UInt16;
|
||||
|
|
|
@ -179,8 +179,8 @@ namespace Ryujinx.Graphics.Metal
|
|||
{
|
||||
if (_currentState.Dirty.HasFlag(DirtyFlags.RenderPipeline))
|
||||
{
|
||||
SetVertexBuffers(renderCommandEncoder, _currentState.VertexBuffers);
|
||||
SetRenderPipelineState(renderCommandEncoder);
|
||||
SetVertexBuffers(renderCommandEncoder, _currentState.VertexBuffers);
|
||||
}
|
||||
|
||||
if (_currentState.Dirty.HasFlag(DirtyFlags.DepthStencil))
|
||||
|
@ -223,21 +223,26 @@ namespace Ryujinx.Graphics.Metal
|
|||
SetScissors(renderCommandEncoder);
|
||||
}
|
||||
|
||||
if (_currentState.Dirty.HasFlag(DirtyFlags.Buffers))
|
||||
if (_currentState.Dirty.HasFlag(DirtyFlags.Uniforms))
|
||||
{
|
||||
SetRenderBuffers(renderCommandEncoder, _currentState.UniformBuffers, _currentState.StorageBuffers);
|
||||
UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, MetalRenderer.UniformSetIndex);
|
||||
}
|
||||
|
||||
if (_currentState.Dirty.HasFlag(DirtyFlags.VertexTextures))
|
||||
if (_currentState.Dirty.HasFlag(DirtyFlags.Storages))
|
||||
{
|
||||
SetRenderTextures(renderCommandEncoder, ShaderStage.Vertex, _currentState.VertexTextures, _currentState.VertexSamplers);
|
||||
UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, MetalRenderer.StorageSetIndex);
|
||||
}
|
||||
|
||||
if (_currentState.Dirty.HasFlag(DirtyFlags.FragmentTextures))
|
||||
if (_currentState.Dirty.HasFlag(DirtyFlags.Textures))
|
||||
{
|
||||
SetRenderTextures(renderCommandEncoder, ShaderStage.Fragment, _currentState.FragmentTextures, _currentState.FragmentSamplers);
|
||||
UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, MetalRenderer.TextureSetIndex);
|
||||
}
|
||||
|
||||
// if (_currentState.Dirty.HasFlag(DirtyFlags.Images))
|
||||
// {
|
||||
// UpdateAndBind(renderCommandEncoder, _currentState.RenderProgram, MetalRenderer.ImageSetIndex);
|
||||
// }
|
||||
|
||||
_currentState.Dirty &= ~DirtyFlags.RenderAll;
|
||||
}
|
||||
|
||||
|
@ -248,15 +253,27 @@ namespace Ryujinx.Graphics.Metal
|
|||
SetComputePipelineState(computeCommandEncoder);
|
||||
}
|
||||
|
||||
if (_currentState.Dirty.HasFlag(DirtyFlags.Buffers))
|
||||
if (_currentState.Dirty.HasFlag(DirtyFlags.Uniforms))
|
||||
{
|
||||
SetComputeBuffers(computeCommandEncoder, _currentState.UniformBuffers, _currentState.StorageBuffers);
|
||||
UpdateAndBind(computeCommandEncoder, _currentState.ComputeProgram, MetalRenderer.UniformSetIndex);
|
||||
}
|
||||
|
||||
if (_currentState.Dirty.HasFlag(DirtyFlags.ComputeTextures))
|
||||
if (_currentState.Dirty.HasFlag(DirtyFlags.Storages))
|
||||
{
|
||||
SetComputeTextures(computeCommandEncoder, _currentState.ComputeTextures, _currentState.ComputeSamplers);
|
||||
UpdateAndBind(computeCommandEncoder, _currentState.ComputeProgram, MetalRenderer.StorageSetIndex);
|
||||
}
|
||||
|
||||
if (_currentState.Dirty.HasFlag(DirtyFlags.Textures))
|
||||
{
|
||||
UpdateAndBind(computeCommandEncoder, _currentState.ComputeProgram, MetalRenderer.TextureSetIndex);
|
||||
}
|
||||
|
||||
// if (_currentState.Dirty.HasFlag(DirtyFlags.Images))
|
||||
// {
|
||||
// UpdateAndBind(computeCommandEncoder, _currentState.ComputeProgram, MetalRenderer.ImageSetIndex);
|
||||
// }
|
||||
|
||||
_currentState.Dirty &= ~DirtyFlags.ComputeAll;
|
||||
}
|
||||
|
||||
private void SetRenderPipelineState(MTLRenderCommandEncoder renderCommandEncoder)
|
||||
|
@ -694,10 +711,10 @@ namespace Ryujinx.Graphics.Metal
|
|||
? null
|
||||
: _bufferManager.GetBuffer(buffer.Handle, buffer.Write);
|
||||
|
||||
_currentState.UniformBuffers[index] = new BufferRef(mtlBuffer, ref buffer);
|
||||
_currentState.UniformBufferRefs[index] = new BufferRef(mtlBuffer, ref buffer);
|
||||
}
|
||||
|
||||
_currentState.Dirty |= DirtyFlags.Buffers;
|
||||
_currentState.Dirty |= DirtyFlags.Uniforms;
|
||||
}
|
||||
|
||||
public void UpdateStorageBuffers(ReadOnlySpan<BufferAssignment> buffers)
|
||||
|
@ -711,10 +728,10 @@ namespace Ryujinx.Graphics.Metal
|
|||
? null
|
||||
: _bufferManager.GetBuffer(buffer.Handle, buffer.Write);
|
||||
|
||||
_currentState.StorageBuffers[index] = new BufferRef(mtlBuffer, ref buffer);
|
||||
_currentState.StorageBufferRefs[index] = new BufferRef(mtlBuffer, ref buffer);
|
||||
}
|
||||
|
||||
_currentState.Dirty |= DirtyFlags.Buffers;
|
||||
_currentState.Dirty |= DirtyFlags.Storages;
|
||||
}
|
||||
|
||||
public void UpdateStorageBuffers(int first, ReadOnlySpan<Auto<DisposableBuffer>> buffers)
|
||||
|
@ -724,10 +741,10 @@ namespace Ryujinx.Graphics.Metal
|
|||
var mtlBuffer = buffers[i];
|
||||
int index = first + i;
|
||||
|
||||
_currentState.StorageBuffers[index] = new BufferRef(mtlBuffer);
|
||||
_currentState.StorageBufferRefs[index] = new BufferRef(mtlBuffer);
|
||||
}
|
||||
|
||||
_currentState.Dirty |= DirtyFlags.Buffers;
|
||||
_currentState.Dirty |= DirtyFlags.Storages;
|
||||
}
|
||||
|
||||
// Inlineable
|
||||
|
@ -786,63 +803,22 @@ namespace Ryujinx.Graphics.Metal
|
|||
_currentState.Dirty |= DirtyFlags.StencilRef;
|
||||
}
|
||||
|
||||
public void UpdateTexture(ShaderStage stage, ulong binding, TextureBase texture)
|
||||
{
|
||||
if (binding > Constants.MaxTexturesPerStage)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Gpu, $"Texture binding ({binding}) must be <= {Constants.MaxTexturesPerStage}");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (stage)
|
||||
{
|
||||
case ShaderStage.Fragment:
|
||||
_currentState.FragmentTextures[binding] = texture;
|
||||
_currentState.Dirty |= DirtyFlags.FragmentTextures;
|
||||
break;
|
||||
case ShaderStage.Vertex:
|
||||
_currentState.VertexTextures[binding] = texture;
|
||||
_currentState.Dirty |= DirtyFlags.VertexTextures;
|
||||
break;
|
||||
case ShaderStage.Compute:
|
||||
_currentState.ComputeTextures[binding] = texture;
|
||||
_currentState.Dirty |= DirtyFlags.ComputeTextures;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateSampler(ShaderStage stage, ulong binding, MTLSamplerState sampler)
|
||||
{
|
||||
if (binding > Constants.MaxTexturesPerStage)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Gpu, $"Sampler binding ({binding}) must be <= {Constants.MaxTexturesPerStage}");
|
||||
return;
|
||||
}
|
||||
switch (stage)
|
||||
{
|
||||
case ShaderStage.Fragment:
|
||||
_currentState.FragmentSamplers[binding] = sampler;
|
||||
_currentState.Dirty |= DirtyFlags.FragmentTextures;
|
||||
break;
|
||||
case ShaderStage.Vertex:
|
||||
_currentState.VertexSamplers[binding] = sampler;
|
||||
_currentState.Dirty |= DirtyFlags.VertexTextures;
|
||||
break;
|
||||
case ShaderStage.Compute:
|
||||
_currentState.ComputeSamplers[binding] = sampler;
|
||||
_currentState.Dirty |= DirtyFlags.ComputeTextures;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateTextureAndSampler(ShaderStage stage, ulong binding, TextureBase texture, Sampler sampler)
|
||||
{
|
||||
UpdateTexture(stage, binding, texture);
|
||||
|
||||
if (sampler != null)
|
||||
if (texture is TextureBuffer textureBuffer)
|
||||
{
|
||||
UpdateSampler(stage, binding, sampler.GetSampler());
|
||||
// TODO: Texture buffers
|
||||
}
|
||||
else if (texture is Texture view)
|
||||
{
|
||||
_currentState.TextureRefs[binding] = new(stage, view, sampler);
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentState.TextureRefs[binding] = default;
|
||||
}
|
||||
|
||||
_currentState.Dirty |= DirtyFlags.Textures;
|
||||
}
|
||||
|
||||
private readonly void SetDepthStencilState(MTLRenderCommandEncoder renderCommandEncoder)
|
||||
|
@ -999,119 +975,373 @@ namespace Ryujinx.Graphics.Metal
|
|||
renderCommandEncoder.SetVertexBuffer(zeroMtlBuffer, 0, Constants.ZeroBufferIndex);
|
||||
}
|
||||
|
||||
private readonly void SetRenderBuffers(MTLRenderCommandEncoder renderCommandEncoder, BufferRef[] uniformBuffers, BufferRef[] storageBuffers)
|
||||
private void UpdateAndBind(MTLRenderCommandEncoder renderCommandEncoder, Program program, int setIndex)
|
||||
{
|
||||
var uniformArgBufferRange = CreateArgumentBufferForRenderEncoder(renderCommandEncoder, uniformBuffers, true);
|
||||
var uniformArgBuffer = _bufferManager.GetBuffer(uniformArgBufferRange.Handle, false).Get(_pipeline.Cbs).Value;
|
||||
var bindingSegments = program.BindingSegments[setIndex];
|
||||
|
||||
renderCommandEncoder.SetVertexBuffer(uniformArgBuffer, (ulong)uniformArgBufferRange.Offset, Constants.ConstantBuffersIndex);
|
||||
renderCommandEncoder.SetFragmentBuffer(uniformArgBuffer, (ulong)uniformArgBufferRange.Offset, Constants.ConstantBuffersIndex);
|
||||
|
||||
var storageArgBufferRange = CreateArgumentBufferForRenderEncoder(renderCommandEncoder, storageBuffers, false);
|
||||
var storageArgBuffer = _bufferManager.GetBuffer(storageArgBufferRange.Handle, true).Get(_pipeline.Cbs).Value;
|
||||
|
||||
renderCommandEncoder.SetVertexBuffer(storageArgBuffer, (ulong)storageArgBufferRange.Offset, Constants.StorageBuffersIndex);
|
||||
renderCommandEncoder.SetFragmentBuffer(storageArgBuffer, (ulong)storageArgBufferRange.Offset, Constants.StorageBuffersIndex);
|
||||
}
|
||||
|
||||
private readonly void SetComputeBuffers(MTLComputeCommandEncoder computeCommandEncoder, BufferRef[] uniformBuffers, BufferRef[] storageBuffers)
|
||||
{
|
||||
var uniformArgBufferRange = CreateArgumentBufferForComputeEncoder(computeCommandEncoder, uniformBuffers, true);
|
||||
var uniformArgBuffer = _bufferManager.GetBuffer(uniformArgBufferRange.Handle, false).Get(_pipeline.Cbs).Value;
|
||||
|
||||
computeCommandEncoder.SetBuffer(uniformArgBuffer, (ulong)uniformArgBufferRange.Offset, Constants.ConstantBuffersIndex);
|
||||
|
||||
|
||||
var storageArgBufferRange = CreateArgumentBufferForComputeEncoder(computeCommandEncoder, storageBuffers, false);
|
||||
var storageArgBuffer = _bufferManager.GetBuffer(storageArgBufferRange.Handle, true).Get(_pipeline.Cbs).Value;
|
||||
|
||||
computeCommandEncoder.SetBuffer(storageArgBuffer, (ulong)storageArgBufferRange.Offset, Constants.StorageBuffersIndex);
|
||||
}
|
||||
|
||||
private readonly BufferRange CreateArgumentBufferForRenderEncoder(MTLRenderCommandEncoder renderCommandEncoder, BufferRef[] buffers, bool constant)
|
||||
{
|
||||
var usage = constant ? MTLResourceUsage.Read : MTLResourceUsage.Write;
|
||||
|
||||
Span<ulong> resourceIds = stackalloc ulong[buffers.Length];
|
||||
|
||||
for (int i = 0; i < buffers.Length; i++)
|
||||
if (bindingSegments.Length == 0)
|
||||
{
|
||||
var range = buffers[i].Range;
|
||||
var autoBuffer = buffers[i].Buffer;
|
||||
var offset = 0;
|
||||
|
||||
if (autoBuffer == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
MTLBuffer mtlBuffer;
|
||||
|
||||
if (range.HasValue)
|
||||
{
|
||||
offset = range.Value.Offset;
|
||||
mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Value.Size, range.Value.Write).Value;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value;
|
||||
}
|
||||
|
||||
renderCommandEncoder.UseResource(new MTLResource(mtlBuffer.NativePtr), usage, MTLRenderStages.RenderStageFragment | MTLRenderStages.RenderStageVertex);
|
||||
resourceIds[i] = mtlBuffer.GpuAddress + (ulong)offset;
|
||||
return;
|
||||
}
|
||||
|
||||
var sizeOfArgumentBuffer = sizeof(ulong) * buffers.Length;
|
||||
var vertArgBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, program.ArgumentBufferSizes[setIndex] * sizeof(ulong));
|
||||
var fragArgBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, program.FragArgumentBufferSizes[setIndex] * sizeof(ulong));
|
||||
|
||||
var argBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, sizeOfArgumentBuffer);
|
||||
argBuffer.Holder.SetDataUnchecked(argBuffer.Offset, MemoryMarshal.AsBytes(resourceIds));
|
||||
Span<ulong> vertResourceIds = stackalloc ulong[program.ArgumentBufferSizes[setIndex]];
|
||||
Span<ulong> fragResourceIds = stackalloc ulong[program.FragArgumentBufferSizes[setIndex]];
|
||||
|
||||
return argBuffer.Range;
|
||||
}
|
||||
var vertResourceIdIndex = 0;
|
||||
var fragResourceIdIndex = 0;
|
||||
|
||||
private readonly BufferRange CreateArgumentBufferForComputeEncoder(MTLComputeCommandEncoder computeCommandEncoder, BufferRef[] buffers, bool constant)
|
||||
{
|
||||
var usage = constant ? MTLResourceUsage.Read : MTLResourceUsage.Write;
|
||||
|
||||
Span<ulong> resourceIds = stackalloc ulong[buffers.Length];
|
||||
|
||||
for (int i = 0; i < buffers.Length; i++)
|
||||
foreach (ResourceBindingSegment segment in bindingSegments)
|
||||
{
|
||||
var range = buffers[i].Range;
|
||||
var autoBuffer = buffers[i].Buffer;
|
||||
var offset = 0;
|
||||
int binding = segment.Binding;
|
||||
int count = segment.Count;
|
||||
|
||||
if (autoBuffer == null)
|
||||
switch (setIndex)
|
||||
{
|
||||
continue;
|
||||
case MetalRenderer.UniformSetIndex:
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
int index = binding + i;
|
||||
|
||||
ref BufferRef buffer = ref _currentState.UniformBufferRefs[index];
|
||||
|
||||
var range = buffer.Range;
|
||||
var autoBuffer = buffer.Buffer;
|
||||
var offset = 0;
|
||||
|
||||
if (autoBuffer == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
MTLBuffer mtlBuffer;
|
||||
|
||||
if (range.HasValue)
|
||||
{
|
||||
offset = range.Value.Offset;
|
||||
mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Value.Size, range.Value.Write).Value;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value;
|
||||
}
|
||||
|
||||
MTLRenderStages renderStages = 0;
|
||||
|
||||
if (segment.Stages.HasFlag(ResourceStages.Vertex))
|
||||
{
|
||||
vertResourceIds[vertResourceIdIndex] = mtlBuffer.GpuAddress + (ulong)offset;
|
||||
vertResourceIdIndex++;
|
||||
|
||||
renderStages |= MTLRenderStages.RenderStageVertex;
|
||||
}
|
||||
|
||||
if (segment.Stages.HasFlag(ResourceStages.Fragment))
|
||||
{
|
||||
fragResourceIds[fragResourceIdIndex] = mtlBuffer.GpuAddress + (ulong)offset;
|
||||
fragResourceIdIndex++;
|
||||
|
||||
renderStages |= MTLRenderStages.RenderStageFragment;
|
||||
}
|
||||
|
||||
renderCommandEncoder.UseResource(new MTLResource(mtlBuffer.NativePtr), MTLResourceUsage.Read, renderStages);
|
||||
}
|
||||
break;
|
||||
case MetalRenderer.StorageSetIndex:
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
int index = binding + i;
|
||||
|
||||
ref BufferRef buffer = ref _currentState.StorageBufferRefs[index];
|
||||
|
||||
var range = buffer.Range;
|
||||
var autoBuffer = buffer.Buffer;
|
||||
var offset = 0;
|
||||
|
||||
if (autoBuffer == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
MTLBuffer mtlBuffer;
|
||||
|
||||
if (range.HasValue)
|
||||
{
|
||||
offset = range.Value.Offset;
|
||||
mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Value.Size, range.Value.Write).Value;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value;
|
||||
}
|
||||
|
||||
MTLRenderStages renderStages = 0;
|
||||
|
||||
if (segment.Stages.HasFlag(ResourceStages.Vertex))
|
||||
{
|
||||
vertResourceIds[vertResourceIdIndex] = mtlBuffer.GpuAddress + (ulong)offset;
|
||||
vertResourceIdIndex++;
|
||||
|
||||
renderStages |= MTLRenderStages.RenderStageVertex;
|
||||
}
|
||||
|
||||
if (segment.Stages.HasFlag(ResourceStages.Fragment))
|
||||
{
|
||||
fragResourceIds[fragResourceIdIndex] = mtlBuffer.GpuAddress + (ulong)offset;
|
||||
fragResourceIdIndex++;
|
||||
|
||||
renderStages |= MTLRenderStages.RenderStageFragment;
|
||||
}
|
||||
|
||||
renderCommandEncoder.UseResource(new MTLResource(mtlBuffer.NativePtr), MTLResourceUsage.Read, renderStages);
|
||||
}
|
||||
break;
|
||||
case MetalRenderer.TextureSetIndex:
|
||||
if (!segment.IsArray)
|
||||
{
|
||||
if (segment.Type != ResourceType.BufferTexture)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
int index = binding + i;
|
||||
|
||||
ref var texture = ref _currentState.TextureRefs[index];
|
||||
|
||||
var storage = texture.Storage;
|
||||
|
||||
if (storage == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var mtlTexture = storage.GetHandle();
|
||||
|
||||
MTLRenderStages renderStages = 0;
|
||||
|
||||
if (segment.Stages.HasFlag(ResourceStages.Vertex))
|
||||
{
|
||||
vertResourceIds[vertResourceIdIndex] = mtlTexture.GpuResourceID._impl;
|
||||
vertResourceIdIndex++;
|
||||
|
||||
if (texture.Sampler != null)
|
||||
{
|
||||
vertResourceIds[vertResourceIdIndex] = texture.Sampler.GetSampler().GpuResourceID._impl;
|
||||
vertResourceIdIndex++;
|
||||
}
|
||||
|
||||
renderStages |= MTLRenderStages.RenderStageVertex;
|
||||
}
|
||||
|
||||
if (segment.Stages.HasFlag(ResourceStages.Fragment))
|
||||
{
|
||||
fragResourceIds[fragResourceIdIndex] = mtlTexture.GpuResourceID._impl;
|
||||
fragResourceIdIndex++;
|
||||
|
||||
if (texture.Sampler != null)
|
||||
{
|
||||
fragResourceIds[fragResourceIdIndex] = texture.Sampler.GetSampler().GpuResourceID._impl;
|
||||
fragResourceIdIndex++;
|
||||
}
|
||||
|
||||
renderStages |= MTLRenderStages.RenderStageFragment;
|
||||
}
|
||||
|
||||
renderCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read, renderStages);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Buffer textures
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Texture arrays
|
||||
}
|
||||
break;
|
||||
case MetalRenderer.ImageSetIndex:
|
||||
// TODO: Images
|
||||
break;
|
||||
}
|
||||
|
||||
MTLBuffer mtlBuffer;
|
||||
|
||||
if (range.HasValue)
|
||||
{
|
||||
offset = range.Value.Offset;
|
||||
mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Value.Size, range.Value.Write).Value;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value;
|
||||
}
|
||||
|
||||
computeCommandEncoder.UseResource(new MTLResource(mtlBuffer.NativePtr), usage);
|
||||
resourceIds[i] = mtlBuffer.GpuAddress + (ulong)offset;
|
||||
}
|
||||
|
||||
var sizeOfArgumentBuffer = sizeof(ulong) * buffers.Length;
|
||||
vertArgBuffer.Holder.SetDataUnchecked(vertArgBuffer.Offset, MemoryMarshal.AsBytes(vertResourceIds));
|
||||
fragArgBuffer.Holder.SetDataUnchecked(fragArgBuffer.Offset, MemoryMarshal.AsBytes(fragResourceIds));
|
||||
|
||||
var mtlVertArgBuffer = _bufferManager.GetBuffer(vertArgBuffer.Handle, false).Get(_pipeline.Cbs).Value;
|
||||
var mtlFragArgBuffer = _bufferManager.GetBuffer(fragArgBuffer.Handle, false).Get(_pipeline.Cbs).Value;
|
||||
|
||||
renderCommandEncoder.SetVertexBuffer(mtlVertArgBuffer, (uint)vertArgBuffer.Range.Offset, SetIndexToBindingIndex(setIndex));
|
||||
renderCommandEncoder.SetFragmentBuffer(mtlFragArgBuffer, (uint)fragArgBuffer.Range.Offset, SetIndexToBindingIndex(setIndex));
|
||||
}
|
||||
|
||||
private void UpdateAndBind(MTLComputeCommandEncoder computeCommandEncoder, Program program, int setIndex)
|
||||
{
|
||||
var bindingSegments = program.BindingSegments[setIndex];
|
||||
|
||||
if (bindingSegments.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var argBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, program.ArgumentBufferSizes[setIndex] * sizeof(ulong));
|
||||
Span<ulong> resourceIds = stackalloc ulong[program.ArgumentBufferSizes[setIndex]];
|
||||
var resourceIdIndex = 0;
|
||||
|
||||
foreach (ResourceBindingSegment segment in bindingSegments)
|
||||
{
|
||||
int binding = segment.Binding;
|
||||
int count = segment.Count;
|
||||
|
||||
switch (setIndex)
|
||||
{
|
||||
case MetalRenderer.UniformSetIndex:
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
int index = binding + i;
|
||||
|
||||
ref BufferRef buffer = ref _currentState.UniformBufferRefs[index];
|
||||
|
||||
var range = buffer.Range;
|
||||
var autoBuffer = buffer.Buffer;
|
||||
var offset = 0;
|
||||
|
||||
if (autoBuffer == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
MTLBuffer mtlBuffer;
|
||||
|
||||
if (range.HasValue)
|
||||
{
|
||||
offset = range.Value.Offset;
|
||||
mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Value.Size, range.Value.Write).Value;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value;
|
||||
}
|
||||
|
||||
if (segment.Stages.HasFlag(ResourceStages.Compute))
|
||||
{
|
||||
computeCommandEncoder.UseResource(new MTLResource(mtlBuffer.NativePtr), MTLResourceUsage.Read);
|
||||
resourceIds[resourceIdIndex] = mtlBuffer.GpuAddress + (ulong)offset;
|
||||
resourceIdIndex++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MetalRenderer.StorageSetIndex:
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
int index = binding + i;
|
||||
|
||||
ref BufferRef buffer = ref _currentState.StorageBufferRefs[index];
|
||||
|
||||
var range = buffer.Range;
|
||||
var autoBuffer = buffer.Buffer;
|
||||
var offset = 0;
|
||||
|
||||
if (autoBuffer == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
MTLBuffer mtlBuffer;
|
||||
|
||||
if (range.HasValue)
|
||||
{
|
||||
offset = range.Value.Offset;
|
||||
mtlBuffer = autoBuffer.Get(_pipeline.Cbs, offset, range.Value.Size, range.Value.Write).Value;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
mtlBuffer = autoBuffer.Get(_pipeline.Cbs).Value;
|
||||
}
|
||||
|
||||
if (segment.Stages.HasFlag(ResourceStages.Compute))
|
||||
{
|
||||
computeCommandEncoder.UseResource(new MTLResource(mtlBuffer.NativePtr), MTLResourceUsage.Read | MTLResourceUsage.Write);
|
||||
resourceIds[resourceIdIndex] = mtlBuffer.GpuAddress + (ulong)offset;
|
||||
resourceIdIndex++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MetalRenderer.TextureSetIndex:
|
||||
if (!segment.IsArray)
|
||||
{
|
||||
if (segment.Type != ResourceType.BufferTexture)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
int index = binding + i;
|
||||
|
||||
ref var texture = ref _currentState.TextureRefs[index];
|
||||
|
||||
var storage = texture.Storage;
|
||||
|
||||
if (storage == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var mtlTexture = storage.GetHandle();
|
||||
|
||||
if (segment.Stages.HasFlag(ResourceStages.Compute))
|
||||
{
|
||||
computeCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read);
|
||||
resourceIds[resourceIdIndex] = mtlTexture.GpuResourceID._impl;
|
||||
resourceIdIndex++;
|
||||
|
||||
if (texture.Sampler != null)
|
||||
{
|
||||
resourceIds[resourceIdIndex] = texture.Sampler.GetSampler().GpuResourceID._impl;
|
||||
resourceIdIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Buffer textures
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Texture arrays
|
||||
}
|
||||
break;
|
||||
case MetalRenderer.ImageSetIndex:
|
||||
// TODO: Images
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var argBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, sizeOfArgumentBuffer);
|
||||
argBuffer.Holder.SetDataUnchecked(argBuffer.Offset, MemoryMarshal.AsBytes(resourceIds));
|
||||
|
||||
return argBuffer.Range;
|
||||
var mtlArgBuffer = _bufferManager.GetBuffer(argBuffer.Handle, false).Get(_pipeline.Cbs).Value;
|
||||
|
||||
computeCommandEncoder.SetBuffer(mtlArgBuffer, (uint)argBuffer.Range.Offset, SetIndexToBindingIndex(setIndex));
|
||||
}
|
||||
|
||||
private uint SetIndexToBindingIndex(int setIndex)
|
||||
{
|
||||
return setIndex switch
|
||||
{
|
||||
MetalRenderer.UniformSetIndex => Constants.ConstantBuffersIndex,
|
||||
MetalRenderer.StorageSetIndex => Constants.StorageBuffersIndex,
|
||||
MetalRenderer.TextureSetIndex => Constants.TexturesIndex,
|
||||
MetalRenderer.ImageSetIndex => Constants.ImagessIndex,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private readonly void SetCullMode(MTLRenderCommandEncoder renderCommandEncoder)
|
||||
{
|
||||
renderCommandEncoder.SetCullMode(_currentState.CullMode);
|
||||
|
@ -1126,105 +1356,5 @@ namespace Ryujinx.Graphics.Metal
|
|||
{
|
||||
renderCommandEncoder.SetStencilReferenceValues((uint)_currentState.FrontRefValue, (uint)_currentState.BackRefValue);
|
||||
}
|
||||
|
||||
private readonly void SetRenderTextures(MTLRenderCommandEncoder renderCommandEncoder, ShaderStage stage, TextureBase[] textures, MTLSamplerState[] samplers)
|
||||
{
|
||||
var argBufferRange = CreateArgumentBufferForRenderEncoder(renderCommandEncoder, stage, textures, samplers);
|
||||
var argBuffer = _bufferManager.GetBuffer(argBufferRange.Handle, false).Get(_pipeline.Cbs).Value;
|
||||
|
||||
switch (stage)
|
||||
{
|
||||
case ShaderStage.Vertex:
|
||||
renderCommandEncoder.SetVertexBuffer(argBuffer, (ulong)argBufferRange.Offset, Constants.TexturesIndex);
|
||||
break;
|
||||
case ShaderStage.Fragment:
|
||||
renderCommandEncoder.SetFragmentBuffer(argBuffer, (ulong)argBufferRange.Offset, Constants.TexturesIndex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly void SetComputeTextures(MTLComputeCommandEncoder computeCommandEncoder, TextureBase[] textures, MTLSamplerState[] samplers)
|
||||
{
|
||||
var argBufferRange = CreateArgumentBufferForComputeEncoder(computeCommandEncoder, textures, samplers);
|
||||
var argBuffer = _bufferManager.GetBuffer(argBufferRange.Handle, false).Get(_pipeline.Cbs).Value;
|
||||
|
||||
computeCommandEncoder.SetBuffer(argBuffer, (ulong)argBufferRange.Offset, Constants.TexturesIndex);
|
||||
}
|
||||
|
||||
private readonly BufferRange CreateArgumentBufferForRenderEncoder(MTLRenderCommandEncoder renderCommandEncoder, ShaderStage stage, TextureBase[] textures, MTLSamplerState[] samplers)
|
||||
{
|
||||
var renderStage = stage == ShaderStage.Vertex ? MTLRenderStages.RenderStageVertex : MTLRenderStages.RenderStageFragment;
|
||||
|
||||
Span<ulong> resourceIds = stackalloc ulong[textures.Length + samplers.Length];
|
||||
|
||||
for (int i = 0; i < textures.Length; i++)
|
||||
{
|
||||
if (textures[i] == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var mtlTexture = textures[i].GetHandle();
|
||||
|
||||
renderCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read, renderStage);
|
||||
resourceIds[i] = mtlTexture.GpuResourceID._impl;
|
||||
}
|
||||
|
||||
for (int i = 0; i < samplers.Length; i++)
|
||||
{
|
||||
if (samplers[i].NativePtr == IntPtr.Zero)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var sampler = samplers[i];
|
||||
|
||||
resourceIds[i + textures.Length] = sampler.GpuResourceID._impl;
|
||||
}
|
||||
|
||||
var sizeOfArgumentBuffer = sizeof(ulong) * (textures.Length + samplers.Length);
|
||||
|
||||
var argBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, sizeOfArgumentBuffer);
|
||||
argBuffer.Holder.SetDataUnchecked(argBuffer.Offset, MemoryMarshal.AsBytes(resourceIds));
|
||||
|
||||
return argBuffer.Range;
|
||||
}
|
||||
|
||||
private readonly BufferRange CreateArgumentBufferForComputeEncoder(MTLComputeCommandEncoder computeCommandEncoder, TextureBase[] textures, MTLSamplerState[] samplers)
|
||||
{
|
||||
Span<ulong> resourceIds = stackalloc ulong[textures.Length + samplers.Length];
|
||||
|
||||
for (int i = 0; i < textures.Length; i++)
|
||||
{
|
||||
if (textures[i] == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var mtlTexture = textures[i].GetHandle();
|
||||
|
||||
computeCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read);
|
||||
resourceIds[i] = mtlTexture.GpuResourceID._impl;
|
||||
}
|
||||
|
||||
for (int i = 0; i < samplers.Length; i++)
|
||||
{
|
||||
if (samplers[i].NativePtr == IntPtr.Zero)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var sampler = samplers[i];
|
||||
|
||||
resourceIds[i + textures.Length] = sampler.GpuResourceID._impl;
|
||||
}
|
||||
|
||||
var sizeOfArgumentBuffer = sizeof(ulong) * (textures.Length + samplers.Length);
|
||||
|
||||
var argBuffer = _bufferManager.ReserveOrCreate(_pipeline.Cbs, sizeOfArgumentBuffer);
|
||||
argBuffer.Holder.SetDataUnchecked(argBuffer.Offset, MemoryMarshal.AsBytes(resourceIds));
|
||||
|
||||
return argBuffer.Range;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,12 +36,19 @@ namespace Ryujinx.Graphics.Metal
|
|||
_samplerNearest = new Sampler(_device, SamplerCreateInfo.Create(MinFilter.Nearest, MagFilter.Nearest));
|
||||
_samplerLinear = new Sampler(_device, SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
|
||||
|
||||
var blitResourceLayout = new ResourceLayoutBuilder()
|
||||
.Add(ResourceStages.Vertex, ResourceType.UniformBuffer, 0)
|
||||
.Add(ResourceStages.Fragment, ResourceType.TextureAndSampler, 0).Build();
|
||||
|
||||
var blitSource = ReadMsl("Blit.metal");
|
||||
_programColorBlit = new Program(
|
||||
[
|
||||
new ShaderSource(blitSource, ShaderStage.Fragment, TargetLanguage.Msl),
|
||||
new ShaderSource(blitSource, ShaderStage.Vertex, TargetLanguage.Msl)
|
||||
], device);
|
||||
], blitResourceLayout, device);
|
||||
|
||||
var colorClearResourceLayout = new ResourceLayoutBuilder()
|
||||
.Add(ResourceStages.Fragment, ResourceType.UniformBuffer, 0).Build();
|
||||
|
||||
var colorClearSource = ReadMsl("ColorClear.metal");
|
||||
for (int i = 0; i < Constants.MaxColorAttachments; i++)
|
||||
|
@ -51,7 +58,7 @@ namespace Ryujinx.Graphics.Metal
|
|||
[
|
||||
new ShaderSource(crntSource, ShaderStage.Fragment, TargetLanguage.Msl),
|
||||
new ShaderSource(crntSource, ShaderStage.Vertex, TargetLanguage.Msl)
|
||||
], device));
|
||||
], colorClearResourceLayout, device));
|
||||
}
|
||||
|
||||
var depthStencilClearSource = ReadMsl("DepthStencilClear.metal");
|
||||
|
@ -59,13 +66,18 @@ namespace Ryujinx.Graphics.Metal
|
|||
[
|
||||
new ShaderSource(depthStencilClearSource, ShaderStage.Fragment, TargetLanguage.Msl),
|
||||
new ShaderSource(depthStencilClearSource, ShaderStage.Vertex, TargetLanguage.Msl)
|
||||
], device);
|
||||
], colorClearResourceLayout, device);
|
||||
|
||||
var strideChangeResourceLayout = new ResourceLayoutBuilder()
|
||||
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
|
||||
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
|
||||
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build();
|
||||
|
||||
var strideChangeSource = ReadMsl("ChangeBufferStride.metal");
|
||||
_programStrideChange = new Program(
|
||||
[
|
||||
new ShaderSource(strideChangeSource, ShaderStage.Compute, TargetLanguage.Msl)
|
||||
], device, new ComputeSize(64, 1, 1));
|
||||
], strideChangeResourceLayout, device, new ComputeSize(64, 1, 1));
|
||||
}
|
||||
|
||||
private static string ReadMsl(string fileName)
|
||||
|
|
|
@ -12,6 +12,13 @@ namespace Ryujinx.Graphics.Metal
|
|||
[SupportedOSPlatform("macos")]
|
||||
public sealed class MetalRenderer : IRenderer
|
||||
{
|
||||
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;
|
||||
|
@ -95,7 +102,7 @@ namespace Ryujinx.Graphics.Metal
|
|||
|
||||
public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info)
|
||||
{
|
||||
return new Program(shaders, _device, info.ComputeLocalSize);
|
||||
return new Program(shaders, info.ResourceLayout, _device, info.ComputeLocalSize);
|
||||
}
|
||||
|
||||
public ISampler CreateSampler(SamplerCreateInfo info)
|
||||
|
@ -188,10 +195,10 @@ namespace Ryujinx.Graphics.Metal
|
|||
supportsViewportSwizzle: false,
|
||||
supportsIndirectParameters: true,
|
||||
supportsDepthClipControl: false,
|
||||
uniformBufferSetIndex: 0,
|
||||
storageBufferSetIndex: 1,
|
||||
textureSetIndex: 2,
|
||||
imageSetIndex: 3,
|
||||
uniformBufferSetIndex: UniformSetIndex,
|
||||
storageBufferSetIndex: StorageSetIndex,
|
||||
textureSetIndex: TextureSetIndex,
|
||||
imageSetIndex: ImageSetIndex,
|
||||
extraSetBaseIndex: 0,
|
||||
maximumExtraSets: 0,
|
||||
maximumUniformBuffersPerStage: Constants.MaxUniformBuffersPerStage,
|
||||
|
|
|
@ -4,6 +4,8 @@ using Ryujinx.Graphics.Shader;
|
|||
using SharpMetal.Foundation;
|
||||
using SharpMetal.Metal;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Graphics.Metal
|
||||
|
@ -21,7 +23,14 @@ namespace Ryujinx.Graphics.Metal
|
|||
private MTLComputePipelineState? _computePipelineCache;
|
||||
private bool _firstBackgroundUse;
|
||||
|
||||
public Program(ShaderSource[] shaders, MTLDevice device, ComputeSize computeLocalSize = default)
|
||||
public ResourceBindingSegment[][] ClearSegments { get; }
|
||||
public ResourceBindingSegment[][] BindingSegments { get; }
|
||||
// Argument buffer sizes for Vertex or Compute stages
|
||||
public int[] ArgumentBufferSizes { get; }
|
||||
// Argument buffer sizes for Fragment stage
|
||||
public int[] FragArgumentBufferSizes { get; }
|
||||
|
||||
public Program(ShaderSource[] shaders, ResourceLayout resourceLayout, MTLDevice device, ComputeSize computeLocalSize = default)
|
||||
{
|
||||
ComputeLocalSize = computeLocalSize;
|
||||
|
||||
|
@ -56,9 +65,155 @@ namespace Ryujinx.Graphics.Metal
|
|||
}
|
||||
}
|
||||
|
||||
ClearSegments = BuildClearSegments(resourceLayout.Sets);
|
||||
(BindingSegments, ArgumentBufferSizes, FragArgumentBufferSizes) = BuildBindingSegments(resourceLayout.SetUsages);
|
||||
|
||||
_status = ProgramLinkStatus.Success;
|
||||
}
|
||||
|
||||
private static ResourceBindingSegment[][] BuildClearSegments(ReadOnlyCollection<ResourceDescriptorCollection> sets)
|
||||
{
|
||||
ResourceBindingSegment[][] segments = new ResourceBindingSegment[sets.Count][];
|
||||
|
||||
for (int setIndex = 0; setIndex < sets.Count; setIndex++)
|
||||
{
|
||||
List<ResourceBindingSegment> currentSegments = new();
|
||||
|
||||
ResourceDescriptor currentDescriptor = default;
|
||||
int currentCount = 0;
|
||||
|
||||
for (int index = 0; index < sets[setIndex].Descriptors.Count; index++)
|
||||
{
|
||||
ResourceDescriptor descriptor = sets[setIndex].Descriptors[index];
|
||||
|
||||
if (currentDescriptor.Binding + currentCount != descriptor.Binding ||
|
||||
currentDescriptor.Type != descriptor.Type ||
|
||||
currentDescriptor.Stages != descriptor.Stages ||
|
||||
currentDescriptor.Count > 1 ||
|
||||
descriptor.Count > 1)
|
||||
{
|
||||
if (currentCount != 0)
|
||||
{
|
||||
currentSegments.Add(new ResourceBindingSegment(
|
||||
currentDescriptor.Binding,
|
||||
currentCount,
|
||||
currentDescriptor.Type,
|
||||
currentDescriptor.Stages,
|
||||
currentDescriptor.Count > 1));
|
||||
}
|
||||
|
||||
currentDescriptor = descriptor;
|
||||
currentCount = descriptor.Count;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentCount += descriptor.Count;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentCount != 0)
|
||||
{
|
||||
currentSegments.Add(new ResourceBindingSegment(
|
||||
currentDescriptor.Binding,
|
||||
currentCount,
|
||||
currentDescriptor.Type,
|
||||
currentDescriptor.Stages,
|
||||
currentDescriptor.Count > 1));
|
||||
}
|
||||
|
||||
segments[setIndex] = currentSegments.ToArray();
|
||||
}
|
||||
|
||||
return segments;
|
||||
}
|
||||
|
||||
private static (ResourceBindingSegment[][], int[], int[]) BuildBindingSegments(ReadOnlyCollection<ResourceUsageCollection> setUsages)
|
||||
{
|
||||
ResourceBindingSegment[][] segments = new ResourceBindingSegment[setUsages.Count][];
|
||||
int[] argBufferSizes = new int[setUsages.Count];
|
||||
int[] fragArgBufferSizes = new int[setUsages.Count];
|
||||
|
||||
for (int setIndex = 0; setIndex < setUsages.Count; setIndex++)
|
||||
{
|
||||
List<ResourceBindingSegment> currentSegments = new();
|
||||
|
||||
ResourceUsage currentUsage = default;
|
||||
int currentCount = 0;
|
||||
|
||||
for (int index = 0; index < setUsages[setIndex].Usages.Count; index++)
|
||||
{
|
||||
ResourceUsage usage = setUsages[setIndex].Usages[index];
|
||||
|
||||
if (currentUsage.Binding + currentCount != usage.Binding ||
|
||||
currentUsage.Type != usage.Type ||
|
||||
currentUsage.Stages != usage.Stages ||
|
||||
currentUsage.ArrayLength > 1 ||
|
||||
usage.ArrayLength > 1)
|
||||
{
|
||||
if (currentCount != 0)
|
||||
{
|
||||
currentSegments.Add(new ResourceBindingSegment(
|
||||
currentUsage.Binding,
|
||||
currentCount,
|
||||
currentUsage.Type,
|
||||
currentUsage.Stages,
|
||||
currentUsage.ArrayLength > 1));
|
||||
|
||||
var size = currentCount * ResourcePointerSize(currentUsage.Type);
|
||||
if (currentUsage.Stages.HasFlag(ResourceStages.Fragment))
|
||||
{
|
||||
fragArgBufferSizes[setIndex] += size;
|
||||
}
|
||||
|
||||
if (currentUsage.Stages.HasFlag(ResourceStages.Vertex) ||
|
||||
currentUsage.Stages.HasFlag(ResourceStages.Compute))
|
||||
{
|
||||
argBufferSizes[setIndex] += size;
|
||||
}
|
||||
}
|
||||
|
||||
currentUsage = usage;
|
||||
currentCount = usage.ArrayLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentCount != 0)
|
||||
{
|
||||
currentSegments.Add(new ResourceBindingSegment(
|
||||
currentUsage.Binding,
|
||||
currentCount,
|
||||
currentUsage.Type,
|
||||
currentUsage.Stages,
|
||||
currentUsage.ArrayLength > 1));
|
||||
|
||||
var size = currentCount * ResourcePointerSize(currentUsage.Type);
|
||||
if (currentUsage.Stages.HasFlag(ResourceStages.Fragment))
|
||||
{
|
||||
fragArgBufferSizes[setIndex] += size;
|
||||
}
|
||||
|
||||
if (currentUsage.Stages.HasFlag(ResourceStages.Vertex) ||
|
||||
currentUsage.Stages.HasFlag(ResourceStages.Compute))
|
||||
{
|
||||
argBufferSizes[setIndex] += size;
|
||||
}
|
||||
}
|
||||
|
||||
segments[setIndex] = currentSegments.ToArray();
|
||||
}
|
||||
|
||||
return (segments, argBufferSizes, fragArgBufferSizes);
|
||||
}
|
||||
|
||||
private static int ResourcePointerSize(ResourceType type)
|
||||
{
|
||||
return (type == ResourceType.TextureAndSampler ? 2 : 1);
|
||||
}
|
||||
|
||||
public ProgramLinkStatus CheckProgramLink(bool blocking)
|
||||
{
|
||||
return _status;
|
||||
|
|
22
src/Ryujinx.Graphics.Metal/ResourceBindingSegment.cs
Normal file
22
src/Ryujinx.Graphics.Metal/ResourceBindingSegment.cs
Normal file
|
@ -0,0 +1,22 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
|
||||
namespace Ryujinx.Graphics.Metal
|
||||
{
|
||||
readonly struct ResourceBindingSegment
|
||||
{
|
||||
public readonly int Binding;
|
||||
public readonly int Count;
|
||||
public readonly ResourceType Type;
|
||||
public readonly ResourceStages Stages;
|
||||
public readonly bool IsArray;
|
||||
|
||||
public ResourceBindingSegment(int binding, int count, ResourceType type, ResourceStages stages, bool isArray)
|
||||
{
|
||||
Binding = binding;
|
||||
Count = count;
|
||||
Type = type;
|
||||
Stages = stages;
|
||||
IsArray = isArray;
|
||||
}
|
||||
}
|
||||
}
|
59
src/Ryujinx.Graphics.Metal/ResourceLayoutBuilder.cs
Normal file
59
src/Ryujinx.Graphics.Metal/ResourceLayoutBuilder.cs
Normal file
|
@ -0,0 +1,59 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Graphics.Metal
|
||||
{
|
||||
[SupportedOSPlatform("macos")]
|
||||
class ResourceLayoutBuilder
|
||||
{
|
||||
private const int TotalSets = MetalRenderer.TotalSets;
|
||||
|
||||
private readonly List<ResourceDescriptor>[] _resourceDescriptors;
|
||||
private readonly List<ResourceUsage>[] _resourceUsages;
|
||||
|
||||
public ResourceLayoutBuilder()
|
||||
{
|
||||
_resourceDescriptors = new List<ResourceDescriptor>[TotalSets];
|
||||
_resourceUsages = new List<ResourceUsage>[TotalSets];
|
||||
|
||||
for (int index = 0; index < TotalSets; index++)
|
||||
{
|
||||
_resourceDescriptors[index] = new();
|
||||
_resourceUsages[index] = new();
|
||||
}
|
||||
}
|
||||
|
||||
public ResourceLayoutBuilder Add(ResourceStages stages, ResourceType type, int binding)
|
||||
{
|
||||
int 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,
|
||||
_ => throw new ArgumentException($"Invalid resource type \"{type}\"."),
|
||||
};
|
||||
|
||||
_resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding, 1, type, stages));
|
||||
_resourceUsages[setIndex].Add(new ResourceUsage(binding, 1, type, stages));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public ResourceLayout Build()
|
||||
{
|
||||
var descriptors = new ResourceDescriptorCollection[TotalSets];
|
||||
var usages = new ResourceUsageCollection[TotalSets];
|
||||
|
||||
for (int index = 0; index < TotalSets; index++)
|
||||
{
|
||||
descriptors[index] = new ResourceDescriptorCollection(_resourceDescriptors[index].ToArray().AsReadOnly());
|
||||
usages[index] = new ResourceUsageCollection(_resourceUsages[index].ToArray().AsReadOnly());
|
||||
}
|
||||
|
||||
return new ResourceLayout(descriptors.AsReadOnly(), usages.AsReadOnly());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,133 +18,7 @@ struct ConstantBuffers {
|
|||
struct Textures
|
||||
{
|
||||
texture2d<float, access::sample> texture;
|
||||
ulong padding_1;
|
||||
ulong padding_2;
|
||||
ulong padding_3;
|
||||
ulong padding_4;
|
||||
ulong padding_5;
|
||||
ulong padding_6;
|
||||
ulong padding_7;
|
||||
ulong padding_8;
|
||||
ulong padding_9;
|
||||
ulong padding_10;
|
||||
ulong padding_11;
|
||||
ulong padding_12;
|
||||
ulong padding_13;
|
||||
ulong padding_14;
|
||||
ulong padding_15;
|
||||
ulong padding_16;
|
||||
ulong padding_17;
|
||||
ulong padding_18;
|
||||
ulong padding_19;
|
||||
ulong padding_20;
|
||||
ulong padding_21;
|
||||
ulong padding_22;
|
||||
ulong padding_23;
|
||||
ulong padding_24;
|
||||
ulong padding_25;
|
||||
ulong padding_26;
|
||||
ulong padding_27;
|
||||
ulong padding_28;
|
||||
ulong padding_29;
|
||||
ulong padding_30;
|
||||
ulong padding_31;
|
||||
ulong padding_32;
|
||||
ulong padding_33;
|
||||
ulong padding_34;
|
||||
ulong padding_35;
|
||||
ulong padding_36;
|
||||
ulong padding_37;
|
||||
ulong padding_38;
|
||||
ulong padding_39;
|
||||
ulong padding_40;
|
||||
ulong padding_41;
|
||||
ulong padding_42;
|
||||
ulong padding_43;
|
||||
ulong padding_44;
|
||||
ulong padding_45;
|
||||
ulong padding_46;
|
||||
ulong padding_47;
|
||||
ulong padding_48;
|
||||
ulong padding_49;
|
||||
ulong padding_50;
|
||||
ulong padding_51;
|
||||
ulong padding_52;
|
||||
ulong padding_53;
|
||||
ulong padding_54;
|
||||
ulong padding_55;
|
||||
ulong padding_56;
|
||||
ulong padding_57;
|
||||
ulong padding_58;
|
||||
ulong padding_59;
|
||||
ulong padding_60;
|
||||
ulong padding_61;
|
||||
ulong padding_62;
|
||||
ulong padding_63;
|
||||
sampler sampler;
|
||||
ulong padding_65;
|
||||
ulong padding_66;
|
||||
ulong padding_67;
|
||||
ulong padding_68;
|
||||
ulong padding_69;
|
||||
ulong padding_70;
|
||||
ulong padding_71;
|
||||
ulong padding_72;
|
||||
ulong padding_73;
|
||||
ulong padding_74;
|
||||
ulong padding_75;
|
||||
ulong padding_76;
|
||||
ulong padding_77;
|
||||
ulong padding_78;
|
||||
ulong padding_79;
|
||||
ulong padding_80;
|
||||
ulong padding_81;
|
||||
ulong padding_82;
|
||||
ulong padding_83;
|
||||
ulong padding_84;
|
||||
ulong padding_85;
|
||||
ulong padding_86;
|
||||
ulong padding_87;
|
||||
ulong padding_88;
|
||||
ulong padding_89;
|
||||
ulong padding_90;
|
||||
ulong padding_91;
|
||||
ulong padding_92;
|
||||
ulong padding_93;
|
||||
ulong padding_94;
|
||||
ulong padding_95;
|
||||
ulong padding_96;
|
||||
ulong padding_97;
|
||||
ulong padding_98;
|
||||
ulong padding_99;
|
||||
ulong padding_100;
|
||||
ulong padding_101;
|
||||
ulong padding_102;
|
||||
ulong padding_103;
|
||||
ulong padding_104;
|
||||
ulong padding_105;
|
||||
ulong padding_106;
|
||||
ulong padding_107;
|
||||
ulong padding_108;
|
||||
ulong padding_109;
|
||||
ulong padding_110;
|
||||
ulong padding_111;
|
||||
ulong padding_112;
|
||||
ulong padding_113;
|
||||
ulong padding_114;
|
||||
ulong padding_115;
|
||||
ulong padding_116;
|
||||
ulong padding_117;
|
||||
ulong padding_118;
|
||||
ulong padding_119;
|
||||
ulong padding_120;
|
||||
ulong padding_121;
|
||||
ulong padding_122;
|
||||
ulong padding_123;
|
||||
ulong padding_124;
|
||||
ulong padding_125;
|
||||
ulong padding_126;
|
||||
ulong padding_127;
|
||||
};
|
||||
|
||||
vertex CopyVertexOut vertexMain(uint vid [[vertex_id]],
|
||||
|
|
|
@ -19,7 +19,6 @@ struct ConstantBuffers {
|
|||
};
|
||||
|
||||
struct StorageBuffers {
|
||||
ulong padding;
|
||||
device InData* in_data;
|
||||
device OutData* out_data;
|
||||
};
|
||||
|
|
|
@ -164,16 +164,18 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl
|
|||
private static void DeclareBufferStructures(CodeGenContext context, IEnumerable<BufferDefinition> buffers, bool constant)
|
||||
{
|
||||
var name = constant ? "ConstantBuffers" : "StorageBuffers";
|
||||
var count = constant ? Defaults.MaxUniformBuffersPerStage : Defaults.MaxStorageBuffersPerStage;
|
||||
var addressSpace = constant ? "constant" : "device";
|
||||
|
||||
var argBufferPointers = new string[count];
|
||||
List<string> argBufferPointers = [];
|
||||
|
||||
foreach (BufferDefinition buffer in buffers)
|
||||
// TODO: Avoid Linq if we can
|
||||
var sortedBuffers = buffers.OrderBy(x => x.Binding).ToArray();
|
||||
|
||||
foreach (BufferDefinition buffer in sortedBuffers)
|
||||
{
|
||||
var needsPadding = buffer.Layout == BufferLayout.Std140;
|
||||
|
||||
argBufferPointers[buffer.Binding] = $"{addressSpace} {Defaults.StructPrefix}_{buffer.Name}* {buffer.Name};";
|
||||
argBufferPointers.Add($"{addressSpace} {Defaults.StructPrefix}_{buffer.Name}* {buffer.Name};");
|
||||
|
||||
context.AppendLine($"struct {Defaults.StructPrefix}_{buffer.Name}");
|
||||
context.EnterScope();
|
||||
|
@ -211,18 +213,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl
|
|||
context.AppendLine($"struct {name}");
|
||||
context.EnterScope();
|
||||
|
||||
for (int i = 0; i < argBufferPointers.Length; i++)
|
||||
foreach (var pointer in argBufferPointers)
|
||||
{
|
||||
if (argBufferPointers[i] == null)
|
||||
{
|
||||
// We need to pad the struct definition in order to read
|
||||
// non-contiguous resources correctly.
|
||||
context.AppendLine($"ulong padding_{i};");
|
||||
}
|
||||
else
|
||||
{
|
||||
context.AppendLine(argBufferPointers[i]);
|
||||
}
|
||||
context.AppendLine(pointer);
|
||||
}
|
||||
|
||||
context.LeaveScope(";");
|
||||
|
@ -234,31 +227,25 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl
|
|||
context.AppendLine("struct Textures");
|
||||
context.EnterScope();
|
||||
|
||||
var argBufferPointers = new string[Defaults.MaxTexturesPerStage * 2];
|
||||
List<string> argBufferPointers = [];
|
||||
|
||||
foreach (TextureDefinition texture in textures)
|
||||
// TODO: Avoid Linq if we can
|
||||
var sortedTextures = textures.OrderBy(x => x.Binding).ToArray();
|
||||
|
||||
foreach (TextureDefinition texture in sortedTextures)
|
||||
{
|
||||
var textureTypeName = texture.Type.ToMslTextureType();
|
||||
argBufferPointers[texture.Binding] = $"{textureTypeName} tex_{texture.Name};";
|
||||
argBufferPointers.Add($"{textureTypeName} tex_{texture.Name};");
|
||||
|
||||
if (!texture.Separate && texture.Type != SamplerType.TextureBuffer)
|
||||
{
|
||||
argBufferPointers[Defaults.MaxTexturesPerStage + texture.Binding] = $"sampler samp_{texture.Name};";
|
||||
argBufferPointers.Add($"sampler samp_{texture.Name};");
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < argBufferPointers.Length; i++)
|
||||
foreach (var pointer in argBufferPointers)
|
||||
{
|
||||
if (argBufferPointers[i] == null)
|
||||
{
|
||||
// We need to pad the struct definition in order to read
|
||||
// non-contiguous resources correctly.
|
||||
context.AppendLine($"ulong padding_{i};");
|
||||
}
|
||||
else
|
||||
{
|
||||
context.AppendLine(argBufferPointers[i]);
|
||||
}
|
||||
context.AppendLine(pointer);
|
||||
}
|
||||
|
||||
context.LeaveScope(";");
|
||||
|
|
Loading…
Reference in a new issue