diff --git a/src/Ryujinx.Graphics.Metal/Constants.cs b/src/Ryujinx.Graphics.Metal/Constants.cs index 76f4e0f87..58735824d 100644 --- a/src/Ryujinx.Graphics.Metal/Constants.cs +++ b/src/Ryujinx.Graphics.Metal/Constants.cs @@ -26,6 +26,6 @@ namespace Ryujinx.Graphics.Metal public const uint ConstantBuffersIndex = 20; public const uint StorageBuffersIndex = 21; public const uint TexturesIndex = 22; - public const uint ImagessIndex = 23; + public const uint ImagesIndex = 23; } } diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs index d7b9bb8c8..5ee8bd3d8 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderState.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs @@ -65,6 +65,18 @@ namespace Ryujinx.Graphics.Metal } } + record struct ImageRef + { + public ShaderStage Stage; + public Texture Storage; + + public ImageRef(ShaderStage stage, Texture storage) + { + Stage = stage; + Storage = storage; + } + } + struct PredrawState { public MTLCullMode CullMode; @@ -92,6 +104,7 @@ namespace Ryujinx.Graphics.Metal 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 IndexBufferState IndexBuffer = default; diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs index e3ace6e9a..463979dca 100644 --- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs +++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs @@ -821,6 +821,20 @@ namespace Ryujinx.Graphics.Metal _currentState.Dirty |= DirtyFlags.Textures; } + public readonly void UpdateImage(ShaderStage stage, ulong binding, TextureBase texture) + { + if (texture is Texture view) + { + _currentState.ImageRefs[binding] = new(stage, view); + } + else + { + _currentState.ImageRefs[binding] = default; + } + + _currentState.Dirty |= DirtyFlags.Images; + } + private readonly void SetDepthStencilState(MTLRenderCommandEncoder renderCommandEncoder) { MTLDepthStencilState state = _depthStencilCache.GetOrCreate(_currentState.DepthStencilUid); @@ -1171,7 +1185,42 @@ namespace Ryujinx.Graphics.Metal } break; case MetalRenderer.ImageSetIndex: - // TODO: Images + if (!segment.IsArray) + { + for (int i = 0; i < count; i++) + { + int index = binding + i; + + ref var image = ref _currentState.ImageRefs[index]; + + var storage = image.Storage; + + if (storage == null) + { + continue; + } + + var mtlTexture = storage.GetHandle(); + + MTLRenderStages renderStages = 0; + + if ((segment.Stages & ResourceStages.Vertex) != 0) + { + vertResourceIds[vertResourceIdIndex] = mtlTexture.GpuResourceID._impl; + vertResourceIdIndex++; + renderStages |= MTLRenderStages.RenderStageVertex; + } + + if ((segment.Stages & ResourceStages.Fragment) != 0) + { + fragResourceIds[fragResourceIdIndex] = mtlTexture.GpuResourceID._impl; + fragResourceIdIndex++; + renderStages |= MTLRenderStages.RenderStageFragment; + } + + renderCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read | MTLResourceUsage.Write, renderStages); + } + } break; } } @@ -1336,7 +1385,34 @@ namespace Ryujinx.Graphics.Metal } break; case MetalRenderer.ImageSetIndex: - // TODO: Images + if (!segment.IsArray) + { + if (segment.Type != ResourceType.BufferTexture) + { + for (int i = 0; i < count; i++) + { + int index = binding + i; + + ref var image = ref _currentState.ImageRefs[index]; + + var storage = image.Storage; + + if (storage == null) + { + continue; + } + + var mtlTexture = storage.GetHandle(); + + if (segment.Stages.HasFlag(ResourceStages.Compute)) + { + computeCommandEncoder.UseResource(new MTLResource(mtlTexture.NativePtr), MTLResourceUsage.Read | MTLResourceUsage.Write); + resourceIds[resourceIdIndex] = mtlTexture.GpuResourceID._impl; + resourceIdIndex++; + } + } + } + } break; } } @@ -1356,7 +1432,7 @@ namespace Ryujinx.Graphics.Metal MetalRenderer.UniformSetIndex => Constants.ConstantBuffersIndex, MetalRenderer.StorageSetIndex => Constants.StorageBuffersIndex, MetalRenderer.TextureSetIndex => Constants.TexturesIndex, - MetalRenderer.ImageSetIndex => Constants.ImagessIndex, + MetalRenderer.ImageSetIndex => Constants.ImagesIndex, }; } diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs index d5c04884a..00ca85b56 100644 --- a/src/Ryujinx.Graphics.Metal/Pipeline.cs +++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs @@ -497,7 +497,12 @@ namespace Ryujinx.Graphics.Metal public void SetImage(ShaderStage stage, int binding, ITexture texture, Format imageFormat) { - Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!"); + if (texture is TextureBase tex) + { + var index = (ulong)binding; + + _encoderStateManager.UpdateImage(stage, index, tex); + } } public void SetImageArray(ShaderStage stage, int binding, IImageArray array)