diff --git a/src/Ryujinx.Graphics.GAL/ComputeSize.cs b/src/Ryujinx.Graphics.GAL/ComputeSize.cs new file mode 100644 index 000000000..ce9c2531c --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/ComputeSize.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.GAL +{ + public readonly struct ComputeSize + { + public readonly static ComputeSize VtgAsCompute = new ComputeSize(32, 32, 1); + + public readonly int X; + public readonly int Y; + public readonly int Z; + + public ComputeSize(int x, int y, int z) + { + X = x; + Y = y; + Z = z; + } + } +} diff --git a/src/Ryujinx.Graphics.GAL/Format.cs b/src/Ryujinx.Graphics.GAL/Format.cs index 17c42d2d4..0eeae8e26 100644 --- a/src/Ryujinx.Graphics.GAL/Format.cs +++ b/src/Ryujinx.Graphics.GAL/Format.cs @@ -339,6 +339,84 @@ namespace Ryujinx.Graphics.GAL return 1; } + /// + /// Get bytes per element for this format. + /// + /// Texture format + /// Byte size for an element of this format (pixel, vertex attribute, etc) + public static int GetBytesPerElement(this Format format) + { + int scalarSize = format.GetScalarSize(); + + switch (format) + { + case Format.R8G8Unorm: + case Format.R8G8Snorm: + case Format.R8G8Uint: + case Format.R8G8Sint: + case Format.R8G8Uscaled: + case Format.R8G8Sscaled: + case Format.R16G16Float: + case Format.R16G16Unorm: + case Format.R16G16Snorm: + case Format.R16G16Uint: + case Format.R16G16Sint: + case Format.R16G16Uscaled: + case Format.R16G16Sscaled: + case Format.R32G32Float: + case Format.R32G32Uint: + case Format.R32G32Sint: + case Format.R32G32Uscaled: + case Format.R32G32Sscaled: + return 2 * scalarSize; + + case Format.R8G8B8Unorm: + case Format.R8G8B8Snorm: + case Format.R8G8B8Uint: + case Format.R8G8B8Sint: + case Format.R8G8B8Uscaled: + case Format.R8G8B8Sscaled: + case Format.R16G16B16Float: + case Format.R16G16B16Unorm: + case Format.R16G16B16Snorm: + case Format.R16G16B16Uint: + case Format.R16G16B16Sint: + case Format.R16G16B16Uscaled: + case Format.R16G16B16Sscaled: + case Format.R32G32B32Float: + case Format.R32G32B32Uint: + case Format.R32G32B32Sint: + case Format.R32G32B32Uscaled: + case Format.R32G32B32Sscaled: + return 3 * scalarSize; + + case Format.R8G8B8A8Unorm: + case Format.R8G8B8A8Snorm: + case Format.R8G8B8A8Uint: + case Format.R8G8B8A8Sint: + case Format.R8G8B8A8Srgb: + case Format.R8G8B8A8Uscaled: + case Format.R8G8B8A8Sscaled: + case Format.B8G8R8A8Unorm: + case Format.B8G8R8A8Srgb: + case Format.R16G16B16A16Float: + case Format.R16G16B16A16Unorm: + case Format.R16G16B16A16Snorm: + case Format.R16G16B16A16Uint: + case Format.R16G16B16A16Sint: + case Format.R16G16B16A16Uscaled: + case Format.R16G16B16A16Sscaled: + case Format.R32G32B32A32Float: + case Format.R32G32B32A32Uint: + case Format.R32G32B32A32Sint: + case Format.R32G32B32A32Uscaled: + case Format.R32G32B32A32Sscaled: + return 4 * scalarSize; + } + + return scalarSize; + } + /// /// Checks if the texture format is a depth or depth-stencil format. /// diff --git a/src/Ryujinx.Graphics.GAL/IPipeline.cs b/src/Ryujinx.Graphics.GAL/IPipeline.cs index 08533ceaa..b8409a573 100644 --- a/src/Ryujinx.Graphics.GAL/IPipeline.cs +++ b/src/Ryujinx.Graphics.GAL/IPipeline.cs @@ -25,7 +25,7 @@ namespace Ryujinx.Graphics.GAL void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size); - void DispatchCompute(int groupsX, int groupsY, int groupsZ, int groupSizeX, int groupSizeY, int groupSizeZ); + void DispatchCompute(int groupsX, int groupsY, int groupsZ); void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance); void DrawIndexed( diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DispatchComputeCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DispatchComputeCommand.cs index 36e0d836a..65028378f 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DispatchComputeCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/DispatchComputeCommand.cs @@ -6,23 +6,17 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands private int _groupsX; private int _groupsY; private int _groupsZ; - private int _groupSizeX; - private int _groupSizeY; - private int _groupSizeZ; - public void Set(int groupsX, int groupsY, int groupsZ, int groupSizeX, int groupSizeY, int groupSizeZ) + public void Set(int groupsX, int groupsY, int groupsZ) { _groupsX = groupsX; _groupsY = groupsY; _groupsZ = groupsZ; - _groupSizeX = groupSizeX; - _groupSizeY = groupSizeY; - _groupSizeZ = groupSizeZ; } public static void Run(ref DispatchComputeCommand command, ThreadedRenderer threaded, IRenderer renderer) { - renderer.Pipeline.DispatchCompute(command._groupsX, command._groupsY, command._groupsZ, command._groupSizeX, command._groupSizeY, command._groupSizeZ); + renderer.Pipeline.DispatchCompute(command._groupsX, command._groupsY, command._groupsZ); } } } diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs index 509954faf..deec36648 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs @@ -63,9 +63,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading _renderer.QueueCommand(); } - public void DispatchCompute(int groupsX, int groupsY, int groupsZ, int groupSizeX, int groupSizeY, int groupSizeZ) + public void DispatchCompute(int groupsX, int groupsY, int groupsZ) { - _renderer.New().Set(groupsX, groupsY, groupsZ, groupSizeX, groupSizeY, groupSizeZ); + _renderer.New().Set(groupsX, groupsY, groupsZ); _renderer.QueueCommand(); } diff --git a/src/Ryujinx.Graphics.GAL/ShaderInfo.cs b/src/Ryujinx.Graphics.GAL/ShaderInfo.cs index 2fd3227dc..c7965a03d 100644 --- a/src/Ryujinx.Graphics.GAL/ShaderInfo.cs +++ b/src/Ryujinx.Graphics.GAL/ShaderInfo.cs @@ -4,23 +4,22 @@ namespace Ryujinx.Graphics.GAL { public int FragmentOutputMap { get; } public ResourceLayout ResourceLayout { get; } + public ComputeSize ComputeLocalSize { get; } public ProgramPipelineState? State { get; } public bool FromCache { get; set; } - public ShaderInfo(int fragmentOutputMap, ResourceLayout resourceLayout, ProgramPipelineState state, bool fromCache = false) + public ShaderInfo( + int fragmentOutputMap, + ResourceLayout resourceLayout, + ComputeSize computeLocalSize, + ProgramPipelineState? state, + bool fromCache = false) { FragmentOutputMap = fragmentOutputMap; ResourceLayout = resourceLayout; + ComputeLocalSize = computeLocalSize; State = state; FromCache = fromCache; } - - public ShaderInfo(int fragmentOutputMap, ResourceLayout resourceLayout, bool fromCache = false) - { - FragmentOutputMap = fragmentOutputMap; - ResourceLayout = resourceLayout; - State = null; - FromCache = fromCache; - } } } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs index 98c0ffa20..cd8144724 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs @@ -200,7 +200,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute _channel.BufferManager.CommitComputeBindings(); - _context.Renderer.Pipeline.DispatchCompute(qmd.CtaRasterWidth, qmd.CtaRasterHeight, qmd.CtaRasterDepth, qmd.CtaThreadDimension0, qmd.CtaThreadDimension1, qmd.CtaThreadDimension2); + _context.Renderer.Pipeline.DispatchCompute(qmd.CtaRasterWidth, qmd.CtaRasterHeight, qmd.CtaRasterDepth); _3dEngine.ForceShaderUpdate(); } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeContext.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeContext.cs index 6de50fb2e..6dba27a7d 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeContext.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeContext.cs @@ -48,7 +48,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw 1, 1, 1, - 1, + format.GetBytesPerElement(), format, DepthStencilMode.Depth, Target.TextureBuffer, diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs index 16ae83e6f..73682866b 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs @@ -211,10 +211,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw _context.Renderer.Pipeline.DispatchCompute( BitUtils.DivRoundUp(_count, ComputeLocalSize), BitUtils.DivRoundUp(_instanceCount, ComputeLocalSize), - 1, - ComputeLocalSize, - ComputeLocalSize, - ComputeLocalSize); + 1); } /// @@ -263,10 +260,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw _context.Renderer.Pipeline.DispatchCompute( BitUtils.DivRoundUp(primitivesCount, ComputeLocalSize), BitUtils.DivRoundUp(_instanceCount, ComputeLocalSize), - _geometryAsCompute.Info.ThreadsPerInputPrimitive, - ComputeLocalSize, - ComputeLocalSize, - ComputeLocalSize); + _geometryAsCompute.Info.ThreadsPerInputPrimitive); } /// diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index c36fc0ada..e621177d6 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -392,7 +392,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache context, shaders, specState.PipelineState, - specState.TransformFeedbackDescriptors != null); + specState.TransformFeedbackDescriptors != null, + specState.ComputeState.GetLocalSize()); IProgram hostProgram; diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs index 20f96462e..74922d1e3 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs @@ -490,7 +490,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache { ShaderSource[] shaderSources = new ShaderSource[compilation.TranslatedStages.Length]; - ShaderInfoBuilder shaderInfoBuilder = new(_context, compilation.SpecializationState.TransformFeedbackDescriptors != null); + ref GpuChannelComputeState computeState = ref compilation.SpecializationState.ComputeState; + + ShaderInfoBuilder shaderInfoBuilder = new( + _context, + compilation.SpecializationState.TransformFeedbackDescriptors != null, + computeLocalSize: computeState.GetLocalSize()); for (int index = 0; index < compilation.TranslatedStages.Length; index++) { diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelComputeState.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelComputeState.cs index d8cdbc348..720f7e796 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelComputeState.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuChannelComputeState.cs @@ -1,3 +1,5 @@ +using Ryujinx.Graphics.GAL; + namespace Ryujinx.Graphics.Gpu.Shader { /// @@ -61,5 +63,14 @@ namespace Ryujinx.Graphics.Gpu.Shader SharedMemorySize = sharedMemorySize; HasUnalignedStorageBuffer = hasUnalignedStorageBuffer; } + + /// + /// Gets the local group size of the shader in a GAL compatible struct. + /// + /// Local group size + public ComputeSize GetLocalSize() + { + return new ComputeSize(LocalSizeX, LocalSizeY, LocalSizeZ); + } } } diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index c67c6a2d6..64ea7c979 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -224,7 +224,10 @@ namespace Ryujinx.Graphics.Gpu.Shader TranslatedShader translatedShader = TranslateShader(_dumper, channel, translatorContext, cachedGuestCode, asCompute: false); ShaderSource[] shaderSourcesArray = new ShaderSource[] { CreateShaderSource(translatedShader.Program) }; - ShaderInfo info = ShaderInfoBuilder.BuildForCompute(_context, translatedShader.Program.Info); + ShaderInfo info = ShaderInfoBuilder.BuildForCompute( + _context, + translatedShader.Program.Info, + computeState.GetLocalSize()); IProgram hostProgram = _context.Renderer.CreateProgram(shaderSourcesArray, info); cpShader = new CachedShaderProgram(hostProgram, specState, translatedShader.Shader); diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs index 49823562f..54a03f43b 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderInfoBuilder.cs @@ -22,6 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader ResourceStages.Geometry; private readonly GpuContext _context; + private readonly ComputeSize _computeLocalSize; private int _fragmentOutputMap; @@ -39,9 +40,11 @@ namespace Ryujinx.Graphics.Gpu.Shader /// GPU context that owns the shaders that will be added to the builder /// Indicates if the graphics shader is used with transform feedback enabled /// Indicates that the vertex shader will be emulated on a compute shader - public ShaderInfoBuilder(GpuContext context, bool tfEnabled, bool vertexAsCompute = false) + /// Indicates the local thread size for a compute shader + public ShaderInfoBuilder(GpuContext context, bool tfEnabled, bool vertexAsCompute = false, ComputeSize computeLocalSize = default) { _context = context; + _computeLocalSize = computeLocalSize; _fragmentOutputMap = -1; @@ -361,14 +364,7 @@ namespace Ryujinx.Graphics.Gpu.Shader ResourceLayout resourceLayout = new(descriptors.AsReadOnly(), usages.AsReadOnly()); - if (pipeline.HasValue) - { - return new ShaderInfo(_fragmentOutputMap, resourceLayout, pipeline.Value, fromCache); - } - else - { - return new ShaderInfo(_fragmentOutputMap, resourceLayout, fromCache); - } + return new ShaderInfo(_fragmentOutputMap, resourceLayout, _computeLocalSize, pipeline, fromCache); } /// @@ -378,14 +374,16 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Shaders from the disk cache /// Optional pipeline for background compilation /// Indicates if the graphics shader is used with transform feedback enabled + /// Compute local thread size /// Shader information public static ShaderInfo BuildForCache( GpuContext context, IEnumerable programs, ProgramPipelineState? pipeline, - bool tfEnabled) + bool tfEnabled, + ComputeSize computeLocalSize) { - ShaderInfoBuilder builder = new(context, tfEnabled); + ShaderInfoBuilder builder = new(context, tfEnabled, computeLocalSize: computeLocalSize); foreach (CachedShaderStage program in programs) { @@ -403,11 +401,12 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// GPU context that owns the shader /// Compute shader information + /// Compute local thread size /// True if the compute shader comes from a disk cache, false otherwise /// Shader information - public static ShaderInfo BuildForCompute(GpuContext context, ShaderProgramInfo info, bool fromCache = false) + public static ShaderInfo BuildForCompute(GpuContext context, ShaderProgramInfo info, ComputeSize computeLocalSize, bool fromCache = false) { - ShaderInfoBuilder builder = new(context, tfEnabled: false, vertexAsCompute: false); + ShaderInfoBuilder builder = new(context, tfEnabled: false, vertexAsCompute: false, computeLocalSize: computeLocalSize); builder.AddStageInfo(info); @@ -424,7 +423,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Shader information public static ShaderInfo BuildForVertexAsCompute(GpuContext context, ShaderProgramInfo info, bool tfEnabled, bool fromCache = false) { - ShaderInfoBuilder builder = new(context, tfEnabled, vertexAsCompute: true); + ShaderInfoBuilder builder = new(context, tfEnabled, vertexAsCompute: true, computeLocalSize: ComputeSize.VtgAsCompute); builder.AddStageInfo(info, vertexAsCompute: true); diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index 62c965697..db0e8ffa7 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -29,6 +29,7 @@ namespace Ryujinx.Graphics.Metal public readonly PrimitiveTopology Topology => _currentState.Topology; public readonly Texture[] RenderTargets => _currentState.RenderTargets; public readonly Texture DepthStencil => _currentState.DepthStencil; + public readonly ComputeSize ComputeLocalSize => _currentState.ComputeProgram.ComputeLocalSize; // RGBA32F is the biggest format private const int ZeroBufferSize = 4 * 4; @@ -811,6 +812,7 @@ namespace Ryujinx.Graphics.Metal Logger.Warning?.Print(LogClass.Gpu, $"Texture binding ({binding}) must be <= {Constants.MaxTexturesPerStage}"); return; } + switch (stage) { case ShaderStage.Fragment: @@ -852,10 +854,14 @@ namespace Ryujinx.Graphics.Metal } } - public void UpdateTextureAndSampler(ShaderStage stage, ulong binding, TextureBase texture, MTLSamplerState sampler) + public void UpdateTextureAndSampler(ShaderStage stage, ulong binding, TextureBase texture, Sampler sampler) { UpdateTexture(stage, binding, texture); - UpdateSampler(stage, binding, sampler); + + if (sampler != null) + { + UpdateSampler(stage, binding, sampler.GetSampler()); + } } private readonly void SetDepthStencilState(MTLRenderCommandEncoder renderCommandEncoder) diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs index ec944b0f8..5525186f6 100644 --- a/src/Ryujinx.Graphics.Metal/HelperShader.cs +++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs @@ -65,7 +65,7 @@ namespace Ryujinx.Graphics.Metal _programStrideChange = new Program( [ new ShaderSource(strideChangeSource, ShaderStage.Compute, TargetLanguage.Msl) - ], device); + ], device, new ComputeSize(64, 1, 1)); } private static string ReadMsl(string fileName) @@ -260,7 +260,7 @@ namespace Ryujinx.Graphics.Metal _pipeline.SetStorageBuffers(1, sbRanges); _pipeline.SetProgram(_programStrideChange); - _pipeline.DispatchCompute(1 + elems / ConvertElementsPerWorkgroup, 1, 1, 64, 1, 1); + _pipeline.DispatchCompute(1 + elems / ConvertElementsPerWorkgroup, 1, 1); // Restore previous state _pipeline.SwapState(null); diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index c68da5a4a..a0d6faced 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -92,7 +92,7 @@ namespace Ryujinx.Graphics.Metal public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info) { - return new Program(shaders, _device); + return new Program(shaders, _device, info.ComputeLocalSize); } public ISampler CreateSampler(SamplerCreateInfo info) @@ -104,7 +104,7 @@ namespace Ryujinx.Graphics.Metal { if (info.Target == Target.TextureBuffer) { - return new TextureBuffer(this, info); + return new TextureBuffer(_device, this, _pipeline, info); } return new Texture(_device, this, _pipeline, info); diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 6363eb5d8..f410c789c 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -347,13 +347,15 @@ namespace Ryujinx.Graphics.Metal BufferHolder.Copy(this, Cbs, srcBuffer, dstBuffer, srcOffset, dstOffset, size); } - public void DispatchCompute(int groupsX, int groupsY, int groupsZ, int groupSizeX, int groupSizeY, int groupSizeZ) + public void DispatchCompute(int groupsX, int groupsY, int groupsZ) { var computeCommandEncoder = GetOrCreateComputeEncoder(true); + ComputeSize localSize = _encoderStateManager.ComputeLocalSize; + computeCommandEncoder.DispatchThreadgroups( new MTLSize { width = (ulong)groupsX, height = (ulong)groupsY, depth = (ulong)groupsZ }, - new MTLSize { width = (ulong)groupSizeX, height = (ulong)groupSizeY, depth = (ulong)groupSizeZ }); + new MTLSize { width = (ulong)localSize.X, height = (ulong)localSize.Y, depth = (ulong)localSize.Z }); } public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance) @@ -658,12 +660,11 @@ namespace Ryujinx.Graphics.Metal { if (texture is TextureBase tex) { - if (sampler is Sampler samp) + if (sampler == null || sampler is Sampler) { - var mtlSampler = samp.GetSampler(); var index = (ulong)binding; - _encoderStateManager.UpdateTextureAndSampler(stage, index, tex, mtlSampler); + _encoderStateManager.UpdateTextureAndSampler(stage, index, tex, (Sampler)sampler); } } } diff --git a/src/Ryujinx.Graphics.Metal/Program.cs b/src/Ryujinx.Graphics.Metal/Program.cs index 40cb6df77..5635b711c 100644 --- a/src/Ryujinx.Graphics.Metal/Program.cs +++ b/src/Ryujinx.Graphics.Metal/Program.cs @@ -15,13 +15,16 @@ namespace Ryujinx.Graphics.Metal public MTLFunction VertexFunction; public MTLFunction FragmentFunction; public MTLFunction ComputeFunction; + public ComputeSize ComputeLocalSize { get; } private HashTableSlim _graphicsPipelineCache; private MTLComputePipelineState? _computePipelineCache; private bool _firstBackgroundUse; - public Program(ShaderSource[] shaders, MTLDevice device) + public Program(ShaderSource[] shaders, MTLDevice device, ComputeSize computeLocalSize = default) { + ComputeLocalSize = computeLocalSize; + for (int index = 0; index < shaders.Length; index++) { ShaderSource shader = shaders[index]; diff --git a/src/Ryujinx.Graphics.Metal/State/PipelineState.cs b/src/Ryujinx.Graphics.Metal/State/PipelineState.cs index c6e548c95..fa6d5410b 100644 --- a/src/Ryujinx.Graphics.Metal/State/PipelineState.cs +++ b/src/Ryujinx.Graphics.Metal/State/PipelineState.cs @@ -1,4 +1,5 @@ using Ryujinx.Common.Logging; +using Ryujinx.Graphics.GAL; using SharpMetal.Foundation; using SharpMetal.Metal; using System; @@ -249,6 +250,27 @@ namespace Ryujinx.Graphics.Metal return pipelineState; } + public static MTLComputePipelineDescriptor CreateComputeDescriptor(Program program) + { + ComputeSize localSize = program.ComputeLocalSize; + + uint maxThreads = (uint)(localSize.X * localSize.Y * localSize.Z); + + if (maxThreads == 0) + { + throw new InvalidOperationException($"Local thread size for compute cannot be 0 in any dimension."); + } + + var descriptor = new MTLComputePipelineDescriptor + { + ComputeFunction = program.ComputeFunction, + MaxTotalThreadsPerThreadgroup = maxThreads, + ThreadGroupSizeIsMultipleOfThreadExecutionWidth = true, + }; + + return descriptor; + } + public static MTLComputePipelineState CreateComputePipeline(MTLDevice device, Program program) { if (program.TryGetComputePipeline(out var pipelineState)) @@ -256,8 +278,10 @@ namespace Ryujinx.Graphics.Metal return pipelineState; } + using MTLComputePipelineDescriptor descriptor = CreateComputeDescriptor(program); + var error = new NSError(IntPtr.Zero); - pipelineState = device.NewComputePipelineState(program.ComputeFunction, ref error); + pipelineState = device.NewComputePipelineState(descriptor, MTLPipelineOption.None, 0, ref error); if (error != IntPtr.Zero) { Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Compute Pipeline State: {StringHelper.String(error.LocalizedDescription)}"); diff --git a/src/Ryujinx.Graphics.Metal/Texture.cs b/src/Ryujinx.Graphics.Metal/Texture.cs index 668ddd8be..fdff81f0d 100644 --- a/src/Ryujinx.Graphics.Metal/Texture.cs +++ b/src/Ryujinx.Graphics.Metal/Texture.cs @@ -37,7 +37,9 @@ namespace Ryujinx.Graphics.Metal descriptor.Swizzle = GetSwizzle(info, descriptor.PixelFormat); _mtlTexture = _device.NewTexture(descriptor); + MtlFormat = pixelFormat; + descriptor.Dispose(); } public Texture(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info, MTLTexture sourceTexture, int firstLayer, int firstLevel) : base(device, renderer, pipeline, info) diff --git a/src/Ryujinx.Graphics.Metal/TextureBase.cs b/src/Ryujinx.Graphics.Metal/TextureBase.cs index 96daf8d3b..fcd07a66a 100644 --- a/src/Ryujinx.Graphics.Metal/TextureBase.cs +++ b/src/Ryujinx.Graphics.Metal/TextureBase.cs @@ -41,7 +41,7 @@ namespace Ryujinx.Graphics.Metal return _mtlTexture; } - public void Release() + public virtual void Release() { Dispose(); } diff --git a/src/Ryujinx.Graphics.Metal/TextureBuffer.cs b/src/Ryujinx.Graphics.Metal/TextureBuffer.cs index 3db1e7c4a..033e12105 100644 --- a/src/Ryujinx.Graphics.Metal/TextureBuffer.cs +++ b/src/Ryujinx.Graphics.Metal/TextureBuffer.cs @@ -7,27 +7,54 @@ using System.Runtime.Versioning; namespace Ryujinx.Graphics.Metal { [SupportedOSPlatform("macos")] - class TextureBuffer : ITexture + class TextureBuffer : TextureBase, ITexture { - private readonly MetalRenderer _renderer; - + private MTLTextureDescriptor _descriptor; private BufferHandle _bufferHandle; private int _offset; private int _size; private int _bufferCount; - public int Width { get; } - public int Height { get; } - - public MTLPixelFormat MtlFormat { get; } - - public TextureBuffer(MetalRenderer renderer, TextureCreateInfo info) + public TextureBuffer(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info) : base(device, renderer, pipeline, info) { - _renderer = renderer; - Width = info.Width; - Height = info.Height; - MtlFormat = FormatTable.GetFormat(info.Format); + MTLPixelFormat pixelFormat = FormatTable.GetFormat(Info.Format); + + _descriptor = new MTLTextureDescriptor + { + PixelFormat = pixelFormat, + Usage = MTLTextureUsage.Unknown, + TextureType = MTLTextureType.TextureBuffer, + Width = (ulong)Info.Width, + Height = (ulong)Info.Height, + }; + + MtlFormat = pixelFormat; + } + + private void RebuildStorage() + { + // Find the parent buffer, and try to build a texture from it. + + // TODO: texture uses should register read/write usage on the assigned buffer. + Auto bufferAuto = _renderer.BufferManager.GetBuffer(_bufferHandle, false); + + if (_mtlTexture.NativePtr != 0) + { + _mtlTexture.Dispose(); + } + + if (bufferAuto == null) + { + _mtlTexture = default; + } + else + { + DisposableBuffer buffer = bufferAuto.Get(_pipeline.Cbs, _offset, _size); + + _descriptor.Width = (uint)(_size / Info.BytesPerPixel); + _mtlTexture = buffer.Value.NewTexture(_descriptor, (ulong)_offset, (ulong)_size); + } } public void CopyTo(ITexture destination, int firstLayer, int firstLevel) @@ -65,11 +92,6 @@ namespace Ryujinx.Graphics.Metal throw new NotImplementedException(); } - public void Release() - { - - } - public void SetData(IMemoryOwner data) { _renderer.SetBufferData(_bufferHandle, _offset, data.Memory.Span); @@ -101,7 +123,14 @@ namespace Ryujinx.Graphics.Metal _size = buffer.Size; _bufferCount = _renderer.BufferManager.BufferCount; - Release(); + RebuildStorage(); + } + + public override void Release() + { + _descriptor.Dispose(); + + base.Release(); } } } diff --git a/src/Ryujinx.Graphics.Metal/Window.cs b/src/Ryujinx.Graphics.Metal/Window.cs index 38ee6459b..6489b591d 100644 --- a/src/Ryujinx.Graphics.Metal/Window.cs +++ b/src/Ryujinx.Graphics.Metal/Window.cs @@ -18,6 +18,10 @@ namespace Ryujinx.Graphics.Metal private int _width; private int _height; + + private int _requestedWidth; + private int _requestedHeight; + // private bool _vsyncEnabled; private AntiAliasing _currentAntiAliasing; private bool _updateEffect; @@ -35,10 +39,26 @@ namespace Ryujinx.Graphics.Metal _metalLayer = metalLayer; } - public void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback) + private unsafe void ResizeIfNeeded() + { + if (_requestedWidth != 0 && _requestedHeight != 0) + { + // TODO: This is actually a CGSize, but there is no overload for that, so fill the first two fields of rect with the size. + var rect = new NSRect(_requestedWidth, _requestedHeight, 0, 0); + + ObjectiveC.objc_msgSend(_metalLayer, "setDrawableSize:", rect); + + _requestedWidth = 0; + _requestedHeight = 0; + } + } + + public unsafe void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback) { if (_renderer.Pipeline is Pipeline pipeline && texture is Texture tex) { + ResizeIfNeeded(); + var drawable = new CAMetalDrawable(ObjectiveC.IntPtr_objc_msgSend(_metalLayer, "nextDrawable")); _width = (int)drawable.Texture.Width; @@ -114,7 +134,8 @@ namespace Ryujinx.Graphics.Metal public void SetSize(int width, int height) { - // Ignore + _requestedWidth = width; + _requestedHeight = height; } public void ChangeVSyncMode(bool vsyncEnabled) diff --git a/src/Ryujinx.Graphics.OpenGL/Pipeline.cs b/src/Ryujinx.Graphics.OpenGL/Pipeline.cs index f3599cf67..27aacac15 100644 --- a/src/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/src/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -205,7 +205,7 @@ namespace Ryujinx.Graphics.OpenGL Buffer.Copy(source, destination, srcOffset, dstOffset, size); } - public void DispatchCompute(int groupsX, int groupsY, int groupsZ, int groupSizeX, int groupSizeY, int groupSizeZ) + public void DispatchCompute(int groupsX, int groupsY, int groupsZ) { if (!_program.IsLinked) { diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index 3179c80a2..5fac994b3 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -241,7 +241,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl var textureTypeName = texture.Type.ToMslTextureType(); argBufferPointers[texture.Binding] = $"{textureTypeName} tex_{texture.Name};"; - if (!texture.Separate) + if (!texture.Separate && texture.Type != SamplerType.TextureBuffer) { argBufferPointers[Defaults.MaxTexturesPerStage + texture.Binding] = $"sampler samp_{texture.Name};"; } diff --git a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs index 0243dda40..a5599dbe7 100644 --- a/src/Ryujinx.Graphics.Vulkan/HelperShader.cs +++ b/src/Ryujinx.Graphics.Vulkan/HelperShader.cs @@ -1,4 +1,4 @@ -using Ryujinx.Common; +using Ryujinx.Common; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader.Translation; @@ -861,7 +861,7 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetStorageBuffers(1, sbRanges); _pipeline.SetProgram(_programStrideChange); - _pipeline.DispatchCompute(1 + elems / ConvertElementsPerWorkgroup, 1, 1, 0, 0, 0); + _pipeline.DispatchCompute(1 + elems / ConvertElementsPerWorkgroup, 1, 1); _pipeline.Finish(gd, cbs); } @@ -1044,7 +1044,7 @@ namespace Ryujinx.Graphics.Vulkan int dispatchX = (Math.Min(srcView.Info.Width, dstView.Info.Width) + 31) / 32; int dispatchY = (Math.Min(srcView.Info.Height, dstView.Info.Height) + 31) / 32; - _pipeline.DispatchCompute(dispatchX, dispatchY, 1, 0, 0, 0); + _pipeline.DispatchCompute(dispatchX, dispatchY, 1); if (srcView != src) { @@ -1170,7 +1170,7 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Compute, 0, srcView, null); _pipeline.SetImage(ShaderStage.Compute, 0, dstView.GetView(format)); - _pipeline.DispatchCompute(dispatchX, dispatchY, 1, 0, 0, 0); + _pipeline.DispatchCompute(dispatchX, dispatchY, 1); if (srcView != src) { @@ -1582,7 +1582,7 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetStorageBuffers(stackalloc[] { new BufferAssignment(3, patternScoped.Range) }); _pipeline.SetProgram(_programConvertIndirectData); - _pipeline.DispatchCompute(1, 1, 1, 0, 0, 0); + _pipeline.DispatchCompute(1, 1, 1); BufferHolder.InsertBufferBarrier( gd, @@ -1684,7 +1684,7 @@ namespace Ryujinx.Graphics.Vulkan _pipeline.SetStorageBuffers(1, sbRanges); _pipeline.SetProgram(_programConvertD32S8ToD24S8); - _pipeline.DispatchCompute(1 + inSize / ConvertElementsPerWorkgroup, 1, 1, 0, 0, 0); + _pipeline.DispatchCompute(1 + inSize / ConvertElementsPerWorkgroup, 1, 1); _pipeline.Finish(gd, cbs); diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs index c74c87e8d..5af0c3539 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -1,4 +1,4 @@ -using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader; using Silk.NET.Vulkan; using System; @@ -295,7 +295,7 @@ namespace Ryujinx.Graphics.Vulkan } } - public void DispatchCompute(int groupsX, int groupsY, int groupsZ, int groupSizeX, int groupSizeY, int groupSizeZ) + public void DispatchCompute(int groupsX, int groupsY, int groupsZ) { if (!_program.IsLinked) {