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;
|
int binding;
|
||||||
|
|
||||||
if (_context.Capabilities.Api == TargetApi.Vulkan)
|
if (_context.Capabilities.Api != TargetApi.OpenGL)
|
||||||
{
|
{
|
||||||
binding = GetBindingFromIndex(index, _context.Capabilities.MaximumUniformBuffersPerStage, "Uniform buffer");
|
binding = GetBindingFromIndex(index, _context.Capabilities.MaximumUniformBuffersPerStage, "Uniform buffer");
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
{
|
{
|
||||||
int binding;
|
int binding;
|
||||||
|
|
||||||
if (_context.Capabilities.Api == TargetApi.Vulkan)
|
if (_context.Capabilities.Api != TargetApi.OpenGL)
|
||||||
{
|
{
|
||||||
if (count == 1)
|
if (count == 1)
|
||||||
{
|
{
|
||||||
|
@ -103,7 +103,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
{
|
{
|
||||||
int binding;
|
int binding;
|
||||||
|
|
||||||
if (_context.Capabilities.Api == TargetApi.Vulkan)
|
if (_context.Capabilities.Api != TargetApi.OpenGL)
|
||||||
{
|
{
|
||||||
binding = GetBindingFromIndex(index, _context.Capabilities.MaximumStorageBuffersPerStage, "Storage buffer");
|
binding = GetBindingFromIndex(index, _context.Capabilities.MaximumStorageBuffersPerStage, "Storage buffer");
|
||||||
}
|
}
|
||||||
|
@ -119,7 +119,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
{
|
{
|
||||||
int binding;
|
int binding;
|
||||||
|
|
||||||
if (_context.Capabilities.Api == TargetApi.Vulkan)
|
if (_context.Capabilities.Api != TargetApi.OpenGL)
|
||||||
{
|
{
|
||||||
if (count == 1)
|
if (count == 1)
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,6 +8,8 @@ namespace Ryujinx.Graphics.Metal
|
||||||
public const int MaxUniformBuffersPerStage = 18;
|
public const int MaxUniformBuffersPerStage = 18;
|
||||||
public const int MaxStorageBuffersPerStage = 16;
|
public const int MaxStorageBuffersPerStage = 16;
|
||||||
public const int MaxTexturesPerStage = 64;
|
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 MaxTextureBindings = MaxTexturesPerStage * MaxShaderStages;
|
||||||
public const int MaxColorAttachments = 8;
|
public const int MaxColorAttachments = 8;
|
||||||
// TODO: Check this value
|
// TODO: Check this value
|
||||||
|
@ -18,9 +20,11 @@ namespace Ryujinx.Graphics.Metal
|
||||||
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 ConstantBuffersIndex = 20;
|
public const uint ConstantBuffersIndex = 20;
|
||||||
public const uint StorageBuffersIndex = 21;
|
public const uint StorageBuffersIndex = 21;
|
||||||
public const uint ZeroBufferIndex = 18;
|
|
||||||
public const uint TexturesIndex = 22;
|
public const uint TexturesIndex = 22;
|
||||||
|
public const uint ImagessIndex = 23;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Metal.State;
|
using Ryujinx.Graphics.Metal.State;
|
||||||
|
using Ryujinx.Graphics.Shader;
|
||||||
using SharpMetal.Metal;
|
using SharpMetal.Metal;
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -22,13 +23,13 @@ namespace Ryujinx.Graphics.Metal
|
||||||
StencilRef = 1 << 7,
|
StencilRef = 1 << 7,
|
||||||
Viewports = 1 << 8,
|
Viewports = 1 << 8,
|
||||||
Scissors = 1 << 9,
|
Scissors = 1 << 9,
|
||||||
Buffers = 1 << 10,
|
Uniforms = 1 << 10,
|
||||||
VertexTextures = 1 << 11,
|
Storages = 1 << 11,
|
||||||
FragmentTextures = 1 << 12,
|
Textures = 1 << 12,
|
||||||
ComputeTextures = 1 << 13,
|
Images = 1 << 13,
|
||||||
|
|
||||||
RenderAll = RenderPipeline | DepthStencil | DepthClamp | DepthBias | CullMode | FrontFace | StencilRef | Viewports | Scissors | Buffers | VertexTextures | FragmentTextures,
|
RenderAll = RenderPipeline | DepthStencil | DepthClamp | DepthBias | CullMode | FrontFace | StencilRef | Viewports | Scissors | Uniforms | Storages | Textures | Images,
|
||||||
ComputeAll = ComputePipeline | Buffers | ComputeTextures,
|
ComputeAll = ComputePipeline | Uniforms | Storages | Textures | Images,
|
||||||
All = RenderAll | ComputeAll,
|
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
|
struct PredrawState
|
||||||
{
|
{
|
||||||
public MTLCullMode CullMode;
|
public MTLCullMode CullMode;
|
||||||
|
@ -73,17 +88,9 @@ namespace Ryujinx.Graphics.Metal
|
||||||
public PipelineState Pipeline;
|
public PipelineState Pipeline;
|
||||||
public DepthStencilUid DepthStencilUid;
|
public DepthStencilUid DepthStencilUid;
|
||||||
|
|
||||||
public TextureBase[] FragmentTextures = new TextureBase[Constants.MaxTexturesPerStage];
|
public readonly BufferRef[] UniformBufferRefs = new BufferRef[Constants.MaxUniformBufferBindings];
|
||||||
public MTLSamplerState[] FragmentSamplers = new MTLSamplerState[Constants.MaxTexturesPerStage];
|
public readonly BufferRef[] StorageBufferRefs = new BufferRef[Constants.MaxStorageBufferBindings];
|
||||||
|
public readonly TextureRef[] TextureRefs = new TextureRef[Constants.MaxTextureBindings];
|
||||||
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 Auto<DisposableBuffer> IndexBuffer = default;
|
public Auto<DisposableBuffer> IndexBuffer = default;
|
||||||
public MTLIndexType IndexType = MTLIndexType.UInt16;
|
public MTLIndexType IndexType = MTLIndexType.UInt16;
|
||||||
|
|
|
@ -179,8 +179,8 @@ namespace Ryujinx.Graphics.Metal
|
||||||
{
|
{
|
||||||
if (_currentState.Dirty.HasFlag(DirtyFlags.RenderPipeline))
|
if (_currentState.Dirty.HasFlag(DirtyFlags.RenderPipeline))
|
||||||
{
|
{
|
||||||
SetVertexBuffers(renderCommandEncoder, _currentState.VertexBuffers);
|
|
||||||
SetRenderPipelineState(renderCommandEncoder);
|
SetRenderPipelineState(renderCommandEncoder);
|
||||||
|
SetVertexBuffers(renderCommandEncoder, _currentState.VertexBuffers);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_currentState.Dirty.HasFlag(DirtyFlags.DepthStencil))
|
if (_currentState.Dirty.HasFlag(DirtyFlags.DepthStencil))
|
||||||
|
@ -223,21 +223,26 @@ namespace Ryujinx.Graphics.Metal
|
||||||
SetScissors(renderCommandEncoder);
|
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;
|
_currentState.Dirty &= ~DirtyFlags.RenderAll;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,15 +253,27 @@ namespace Ryujinx.Graphics.Metal
|
||||||
SetComputePipelineState(computeCommandEncoder);
|
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)
|
private void SetRenderPipelineState(MTLRenderCommandEncoder renderCommandEncoder)
|
||||||
|
@ -694,10 +711,10 @@ namespace Ryujinx.Graphics.Metal
|
||||||
? null
|
? null
|
||||||
: _bufferManager.GetBuffer(buffer.Handle, buffer.Write);
|
: _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)
|
public void UpdateStorageBuffers(ReadOnlySpan<BufferAssignment> buffers)
|
||||||
|
@ -711,10 +728,10 @@ namespace Ryujinx.Graphics.Metal
|
||||||
? null
|
? null
|
||||||
: _bufferManager.GetBuffer(buffer.Handle, buffer.Write);
|
: _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)
|
public void UpdateStorageBuffers(int first, ReadOnlySpan<Auto<DisposableBuffer>> buffers)
|
||||||
|
@ -724,10 +741,10 @@ namespace Ryujinx.Graphics.Metal
|
||||||
var mtlBuffer = buffers[i];
|
var mtlBuffer = buffers[i];
|
||||||
int index = first + 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
|
// Inlineable
|
||||||
|
@ -786,63 +803,22 @@ namespace Ryujinx.Graphics.Metal
|
||||||
_currentState.Dirty |= DirtyFlags.StencilRef;
|
_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)
|
public void UpdateTextureAndSampler(ShaderStage stage, ulong binding, TextureBase texture, Sampler sampler)
|
||||||
{
|
{
|
||||||
UpdateTexture(stage, binding, texture);
|
if (texture is TextureBuffer textureBuffer)
|
||||||
|
|
||||||
if (sampler != null)
|
|
||||||
{
|
{
|
||||||
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)
|
private readonly void SetDepthStencilState(MTLRenderCommandEncoder renderCommandEncoder)
|
||||||
|
@ -999,119 +975,373 @@ namespace Ryujinx.Graphics.Metal
|
||||||
renderCommandEncoder.SetVertexBuffer(zeroMtlBuffer, 0, Constants.ZeroBufferIndex);
|
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 bindingSegments = program.BindingSegments[setIndex];
|
||||||
var uniformArgBuffer = _bufferManager.GetBuffer(uniformArgBufferRange.Handle, false).Get(_pipeline.Cbs).Value;
|
|
||||||
|
|
||||||
renderCommandEncoder.SetVertexBuffer(uniformArgBuffer, (ulong)uniformArgBufferRange.Offset, Constants.ConstantBuffersIndex);
|
if (bindingSegments.Length == 0)
|
||||||
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++)
|
|
||||||
{
|
{
|
||||||
var range = buffers[i].Range;
|
return;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
Span<ulong> vertResourceIds = stackalloc ulong[program.ArgumentBufferSizes[setIndex]];
|
||||||
argBuffer.Holder.SetDataUnchecked(argBuffer.Offset, MemoryMarshal.AsBytes(resourceIds));
|
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)
|
foreach (ResourceBindingSegment segment in bindingSegments)
|
||||||
{
|
|
||||||
var usage = constant ? MTLResourceUsage.Read : MTLResourceUsage.Write;
|
|
||||||
|
|
||||||
Span<ulong> resourceIds = stackalloc ulong[buffers.Length];
|
|
||||||
|
|
||||||
for (int i = 0; i < buffers.Length; i++)
|
|
||||||
{
|
{
|
||||||
var range = buffers[i].Range;
|
int binding = segment.Binding;
|
||||||
var autoBuffer = buffers[i].Buffer;
|
int count = segment.Count;
|
||||||
var offset = 0;
|
|
||||||
|
|
||||||
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));
|
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)
|
private readonly void SetCullMode(MTLRenderCommandEncoder renderCommandEncoder)
|
||||||
{
|
{
|
||||||
renderCommandEncoder.SetCullMode(_currentState.CullMode);
|
renderCommandEncoder.SetCullMode(_currentState.CullMode);
|
||||||
|
@ -1126,105 +1356,5 @@ namespace Ryujinx.Graphics.Metal
|
||||||
{
|
{
|
||||||
renderCommandEncoder.SetStencilReferenceValues((uint)_currentState.FrontRefValue, (uint)_currentState.BackRefValue);
|
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));
|
_samplerNearest = new Sampler(_device, SamplerCreateInfo.Create(MinFilter.Nearest, MagFilter.Nearest));
|
||||||
_samplerLinear = new Sampler(_device, SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
|
_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");
|
var blitSource = ReadMsl("Blit.metal");
|
||||||
_programColorBlit = new Program(
|
_programColorBlit = new Program(
|
||||||
[
|
[
|
||||||
new ShaderSource(blitSource, ShaderStage.Fragment, TargetLanguage.Msl),
|
new ShaderSource(blitSource, ShaderStage.Fragment, TargetLanguage.Msl),
|
||||||
new ShaderSource(blitSource, ShaderStage.Vertex, 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");
|
var colorClearSource = ReadMsl("ColorClear.metal");
|
||||||
for (int i = 0; i < Constants.MaxColorAttachments; i++)
|
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.Fragment, TargetLanguage.Msl),
|
||||||
new ShaderSource(crntSource, ShaderStage.Vertex, TargetLanguage.Msl)
|
new ShaderSource(crntSource, ShaderStage.Vertex, TargetLanguage.Msl)
|
||||||
], device));
|
], colorClearResourceLayout, device));
|
||||||
}
|
}
|
||||||
|
|
||||||
var depthStencilClearSource = ReadMsl("DepthStencilClear.metal");
|
var depthStencilClearSource = ReadMsl("DepthStencilClear.metal");
|
||||||
|
@ -59,13 +66,18 @@ namespace Ryujinx.Graphics.Metal
|
||||||
[
|
[
|
||||||
new ShaderSource(depthStencilClearSource, ShaderStage.Fragment, TargetLanguage.Msl),
|
new ShaderSource(depthStencilClearSource, ShaderStage.Fragment, TargetLanguage.Msl),
|
||||||
new ShaderSource(depthStencilClearSource, ShaderStage.Vertex, 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");
|
var strideChangeSource = ReadMsl("ChangeBufferStride.metal");
|
||||||
_programStrideChange = new Program(
|
_programStrideChange = new Program(
|
||||||
[
|
[
|
||||||
new ShaderSource(strideChangeSource, ShaderStage.Compute, TargetLanguage.Msl)
|
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)
|
private static string ReadMsl(string fileName)
|
||||||
|
|
|
@ -12,6 +12,13 @@ namespace Ryujinx.Graphics.Metal
|
||||||
[SupportedOSPlatform("macos")]
|
[SupportedOSPlatform("macos")]
|
||||||
public sealed class MetalRenderer : IRenderer
|
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 MTLDevice _device;
|
||||||
private readonly MTLCommandQueue _queue;
|
private readonly MTLCommandQueue _queue;
|
||||||
private readonly Func<CAMetalLayer> _getMetalLayer;
|
private readonly Func<CAMetalLayer> _getMetalLayer;
|
||||||
|
@ -95,7 +102,7 @@ namespace Ryujinx.Graphics.Metal
|
||||||
|
|
||||||
public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info)
|
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)
|
public ISampler CreateSampler(SamplerCreateInfo info)
|
||||||
|
@ -188,10 +195,10 @@ namespace Ryujinx.Graphics.Metal
|
||||||
supportsViewportSwizzle: false,
|
supportsViewportSwizzle: false,
|
||||||
supportsIndirectParameters: true,
|
supportsIndirectParameters: true,
|
||||||
supportsDepthClipControl: false,
|
supportsDepthClipControl: false,
|
||||||
uniformBufferSetIndex: 0,
|
uniformBufferSetIndex: UniformSetIndex,
|
||||||
storageBufferSetIndex: 1,
|
storageBufferSetIndex: StorageSetIndex,
|
||||||
textureSetIndex: 2,
|
textureSetIndex: TextureSetIndex,
|
||||||
imageSetIndex: 3,
|
imageSetIndex: ImageSetIndex,
|
||||||
extraSetBaseIndex: 0,
|
extraSetBaseIndex: 0,
|
||||||
maximumExtraSets: 0,
|
maximumExtraSets: 0,
|
||||||
maximumUniformBuffersPerStage: Constants.MaxUniformBuffersPerStage,
|
maximumUniformBuffersPerStage: Constants.MaxUniformBuffersPerStage,
|
||||||
|
|
|
@ -4,6 +4,8 @@ using Ryujinx.Graphics.Shader;
|
||||||
using SharpMetal.Foundation;
|
using SharpMetal.Foundation;
|
||||||
using SharpMetal.Metal;
|
using SharpMetal.Metal;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Metal
|
namespace Ryujinx.Graphics.Metal
|
||||||
|
@ -21,7 +23,14 @@ namespace Ryujinx.Graphics.Metal
|
||||||
private MTLComputePipelineState? _computePipelineCache;
|
private MTLComputePipelineState? _computePipelineCache;
|
||||||
private bool _firstBackgroundUse;
|
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;
|
ComputeLocalSize = computeLocalSize;
|
||||||
|
|
||||||
|
@ -56,9 +65,155 @@ namespace Ryujinx.Graphics.Metal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ClearSegments = BuildClearSegments(resourceLayout.Sets);
|
||||||
|
(BindingSegments, ArgumentBufferSizes, FragArgumentBufferSizes) = BuildBindingSegments(resourceLayout.SetUsages);
|
||||||
|
|
||||||
_status = ProgramLinkStatus.Success;
|
_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)
|
public ProgramLinkStatus CheckProgramLink(bool blocking)
|
||||||
{
|
{
|
||||||
return _status;
|
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
|
struct Textures
|
||||||
{
|
{
|
||||||
texture2d<float, access::sample> texture;
|
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;
|
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]],
|
vertex CopyVertexOut vertexMain(uint vid [[vertex_id]],
|
||||||
|
|
|
@ -19,7 +19,6 @@ struct ConstantBuffers {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct StorageBuffers {
|
struct StorageBuffers {
|
||||||
ulong padding;
|
|
||||||
device InData* in_data;
|
device InData* in_data;
|
||||||
device OutData* out_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)
|
private static void DeclareBufferStructures(CodeGenContext context, IEnumerable<BufferDefinition> buffers, bool constant)
|
||||||
{
|
{
|
||||||
var name = constant ? "ConstantBuffers" : "StorageBuffers";
|
var name = constant ? "ConstantBuffers" : "StorageBuffers";
|
||||||
var count = constant ? Defaults.MaxUniformBuffersPerStage : Defaults.MaxStorageBuffersPerStage;
|
|
||||||
var addressSpace = constant ? "constant" : "device";
|
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;
|
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.AppendLine($"struct {Defaults.StructPrefix}_{buffer.Name}");
|
||||||
context.EnterScope();
|
context.EnterScope();
|
||||||
|
@ -211,18 +213,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl
|
||||||
context.AppendLine($"struct {name}");
|
context.AppendLine($"struct {name}");
|
||||||
context.EnterScope();
|
context.EnterScope();
|
||||||
|
|
||||||
for (int i = 0; i < argBufferPointers.Length; i++)
|
foreach (var pointer in argBufferPointers)
|
||||||
{
|
{
|
||||||
if (argBufferPointers[i] == null)
|
context.AppendLine(pointer);
|
||||||
{
|
|
||||||
// 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.LeaveScope(";");
|
context.LeaveScope(";");
|
||||||
|
@ -234,31 +227,25 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl
|
||||||
context.AppendLine("struct Textures");
|
context.AppendLine("struct Textures");
|
||||||
context.EnterScope();
|
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();
|
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)
|
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)
|
context.AppendLine(pointer);
|
||||||
{
|
|
||||||
// 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.LeaveScope(";");
|
context.LeaveScope(";");
|
||||||
|
|
Loading…
Reference in a new issue