diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index 77e711fbb..8d408d847 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -56,6 +56,7 @@ namespace Ryujinx.Graphics.Metal public ShaderStage Stage; public TextureBase Storage; public Sampler Sampler; + public Format ImageFormat; public TextureRef(ShaderStage stage, TextureBase storage, Sampler sampler) { @@ -101,11 +102,19 @@ namespace Ryujinx.Graphics.Metal public PipelineState Pipeline; public DepthStencilUid DepthStencilUid; + public readonly record struct ArrayRef(ShaderStage Stage, T Array); + public readonly BufferRef[] UniformBufferRefs = new BufferRef[Constants.MaxUniformBufferBindings]; public readonly BufferRef[] StorageBufferRefs = new BufferRef[Constants.MaxStorageBufferBindings]; public readonly TextureRef[] TextureRefs = new TextureRef[Constants.MaxTextureBindings]; public readonly ImageRef[] ImageRefs = new ImageRef[Constants.MaxTextureBindings]; + public ArrayRef[] TextureArrayRefs = []; + public ArrayRef[] ImageArrayRefs = []; + + public ArrayRef[] TextureArrayExtraRefs = []; + public ArrayRef[] ImageArrayExtraRefs = []; + public IndexBufferState IndexBuffer = default; public MTLDepthClipMode DepthClipMode = MTLDepthClipMode.Clip; diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index f515293b8..0f98496bc 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -14,6 +14,8 @@ namespace Ryujinx.Graphics.Metal [SupportedOSPlatform("macos")] struct EncoderStateManager : IDisposable { + private const int ArrayGrowthSize = 16; + private readonly MTLDevice _device; private readonly Pipeline _pipeline; private readonly BufferManager _bufferManager; @@ -90,6 +92,16 @@ namespace Ryujinx.Graphics.Metal _currentState.ClearLoadAction = clear; } + public void DirtyTextures() + { + _currentState.Dirty |= DirtyFlags.Textures; + } + + public void DirtyImages() + { + _currentState.Dirty |= DirtyFlags.Images; + } + public readonly MTLRenderCommandEncoder CreateRenderCommandEncoder() { // Initialise Pass & State @@ -831,6 +843,66 @@ namespace Ryujinx.Graphics.Metal _currentState.Dirty |= DirtyFlags.Images; } + public void UpdateTextureArray(ShaderStage stage, ulong binding, TextureArray array) + { + ref EncoderState.ArrayRef arrayRef = ref GetArrayRef(ref _currentState.TextureArrayRefs, (int)binding, ArrayGrowthSize); + + if (arrayRef.Stage != stage || arrayRef.Array != array) + { + arrayRef = new EncoderState.ArrayRef(stage, array); + + _currentState.Dirty |= DirtyFlags.Textures; + } + } + + public void UpdateTextureArraySeparate(ShaderStage stage, int setIndex, TextureArray array) + { + ref EncoderState.ArrayRef arrayRef = ref GetArrayRef(ref _currentState.TextureArrayRefs, setIndex); + + if (arrayRef.Stage != stage || arrayRef.Array != array) + { + arrayRef = new EncoderState.ArrayRef(stage, array); + + _currentState.Dirty |= DirtyFlags.Textures; + } + } + + public void UpdateImageArray(ShaderStage stage, ulong binding, ImageArray array) + { + ref EncoderState.ArrayRef arrayRef = ref GetArrayRef(ref _currentState.ImageArrayRefs, (int)binding, ArrayGrowthSize); + + if (arrayRef.Stage != stage || arrayRef.Array != array) + { + arrayRef = new EncoderState.ArrayRef(stage, array); + + _currentState.Dirty |= DirtyFlags.Images; + } + } + + public void UpdateImageArraySeparate(ShaderStage stage, int setIndex, ImageArray array) + { + ref EncoderState.ArrayRef arrayRef = ref GetArrayRef(ref _currentState.ImageArrayExtraRefs, setIndex); + + if (arrayRef.Stage != stage || arrayRef.Array != array) + { + arrayRef = new EncoderState.ArrayRef(stage, array); + + _currentState.Dirty |= DirtyFlags.Images; + } + } + + private static ref EncoderState.ArrayRef GetArrayRef(ref EncoderState.ArrayRef[] array, int index, int growthSize = 1) + { + ArgumentOutOfRangeException.ThrowIfNegative(index); + + if (array.Length <= index) + { + Array.Resize(ref array, index + growthSize); + } + + return ref array[index]; + } + private readonly void SetDepthStencilState(MTLRenderCommandEncoder renderCommandEncoder) { MTLDepthStencilState state = _depthStencilCache.GetOrCreate(_currentState.DepthStencilUid); diff --git a/src/Ryujinx.Graphics.Metal/ImageArray.cs b/src/Ryujinx.Graphics.Metal/ImageArray.cs new file mode 100644 index 000000000..67b8186b1 --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/ImageArray.cs @@ -0,0 +1,74 @@ +using Ryujinx.Graphics.GAL; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + internal class ImageArray : IImageArray + { + private readonly TextureRef[] _textureRefs; + private readonly TextureBuffer[] _bufferTextureRefs; + + private readonly bool _isBuffer; + private readonly Pipeline _pipeline; + + public ImageArray(int size, bool isBuffer, Pipeline pipeline) + { + if (isBuffer) + { + _bufferTextureRefs = new TextureBuffer[size]; + } + else + { + _textureRefs = new TextureRef[size]; + } + + _isBuffer = isBuffer; + _pipeline = pipeline; + } + + public void SetFormats(int index, Format[] imageFormats) + { + for (int i = 0; i < imageFormats.Length; i++) + { + _textureRefs[index + i].ImageFormat = imageFormats[i]; + } + + SetDirty(); + } + + public void SetImages(int index, ITexture[] images) + { + for (int i = 0; i < images.Length; i++) + { + ITexture image = images[i]; + + if (image is TextureBuffer textureBuffer) + { + _bufferTextureRefs[index + i] = textureBuffer; + } + else if (image is Texture texture) + { + _textureRefs[index + i].Storage = texture; + } + else if (!_isBuffer) + { + _textureRefs[index + i].Storage = null; + } + else + { + _bufferTextureRefs[index + i] = null; + } + } + + SetDirty(); + } + + private void SetDirty() + { + _pipeline.DirtyImages(); + } + + public void Dispose() { } + } +} diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs index fa24e70a0..cd2a83bff 100644 --- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs +++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs @@ -1,5 +1,4 @@ using Ryujinx.Common.Configuration; -using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader.Translation; using SharpMetal.Metal; @@ -97,7 +96,7 @@ namespace Ryujinx.Graphics.Metal public IImageArray CreateImageArray(int size, bool isBuffer) { - throw new NotImplementedException(); + return new ImageArray(size, isBuffer, _pipeline); } public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info) @@ -122,7 +121,7 @@ namespace Ryujinx.Graphics.Metal public ITextureArray CreateTextureArray(int size, bool isBuffer) { - throw new NotImplementedException(); + return new TextureArray(size, isBuffer, _pipeline); } public bool PrepareHostMapping(IntPtr address, ulong size) diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index 617034cf8..39f85d205 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -192,6 +192,16 @@ namespace Ryujinx.Graphics.Metal _renderer.RegisterFlush(); } + public void DirtyTextures() + { + _encoderStateManager.DirtyTextures(); + } + + public void DirtyImages() + { + _encoderStateManager.DirtyImages(); + } + public void Blit( Texture src, Texture dst, @@ -542,12 +552,20 @@ namespace Ryujinx.Graphics.Metal public void SetImageArray(ShaderStage stage, int binding, IImageArray array) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + if (array is ImageArray imageArray) + { + var index = (ulong)binding; + + _encoderStateManager.UpdateImageArray(stage, index, imageArray); + } } public void SetImageArraySeparate(ShaderStage stage, int setIndex, IImageArray array) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + if (array is ImageArray imageArray) + { + _encoderStateManager.UpdateImageArraySeparate(stage, setIndex, imageArray); + } } public void SetLineParameters(float width, bool smooth) @@ -656,12 +674,20 @@ namespace Ryujinx.Graphics.Metal public void SetTextureArray(ShaderStage stage, int binding, ITextureArray array) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + if (array is TextureArray textureArray) + { + var index = (ulong)binding; + + _encoderStateManager.UpdateTextureArray(stage, index, textureArray); + } } public void SetTextureArraySeparate(ShaderStage stage, int setIndex, ITextureArray array) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + if (array is TextureArray textureArray) + { + _encoderStateManager.UpdateTextureArraySeparate(stage, setIndex, textureArray); + } } public void SetUserClipDistance(int index, bool enableClip) diff --git a/src/Ryujinx.Graphics.Metal/TextureArray.cs b/src/Ryujinx.Graphics.Metal/TextureArray.cs new file mode 100644 index 000000000..762e6a5fd --- /dev/null +++ b/src/Ryujinx.Graphics.Metal/TextureArray.cs @@ -0,0 +1,83 @@ +using Ryujinx.Graphics.GAL; +using System.Runtime.Versioning; + +namespace Ryujinx.Graphics.Metal +{ + [SupportedOSPlatform("macos")] + internal class TextureArray : ITextureArray + { + private readonly TextureRef[] _textureRefs; + private readonly TextureBuffer[] _bufferTextureRefs; + + private readonly bool _isBuffer; + private readonly Pipeline _pipeline; + + public TextureArray(int size, bool isBuffer, Pipeline pipeline) + { + if (isBuffer) + { + _bufferTextureRefs = new TextureBuffer[size]; + } + else + { + _textureRefs = new TextureRef[size]; + } + + _isBuffer = isBuffer; + _pipeline = pipeline; + } + + public void SetSamplers(int index, ISampler[] samplers) + { + for (int i = 0; i < samplers.Length; i++) + { + ISampler sampler = samplers[i]; + + if (sampler is Sampler samp) + { + _textureRefs[index + i].Sampler = samp; + } + else + { + _textureRefs[index + i].Sampler = default; + } + } + + SetDirty(); + } + + public void SetTextures(int index, ITexture[] textures) + { + for (int i = 0; i < textures.Length; i++) + { + ITexture texture = textures[i]; + + if (texture is TextureBuffer textureBuffer) + { + _bufferTextureRefs[index + i] = textureBuffer; + } + else if (texture is Texture tex) + { + _textureRefs[index + i].Storage = tex; + } + else if (!_isBuffer) + { + _textureRefs[index + i].Storage = null; + } + else + { + _bufferTextureRefs[index + i] = null; + } + } + + SetDirty(); + } + + private void SetDirty() + { + _pipeline.DirtyTextures(); + } + + public void Dispose() { } + } +}