TextureArray & ImageArray Creation + State

This commit is contained in:
Isaac Marovitz 2024-07-31 16:29:48 +01:00 committed by Isaac Marovitz
parent d07f6ed38e
commit 47b99e6bc3
6 changed files with 270 additions and 7 deletions

View file

@ -56,6 +56,7 @@ namespace Ryujinx.Graphics.Metal
public ShaderStage Stage; public ShaderStage Stage;
public TextureBase Storage; public TextureBase Storage;
public Sampler Sampler; public Sampler Sampler;
public Format ImageFormat;
public TextureRef(ShaderStage stage, TextureBase storage, Sampler sampler) public TextureRef(ShaderStage stage, TextureBase storage, Sampler sampler)
{ {
@ -101,11 +102,19 @@ namespace Ryujinx.Graphics.Metal
public PipelineState Pipeline; public PipelineState Pipeline;
public DepthStencilUid DepthStencilUid; public DepthStencilUid DepthStencilUid;
public readonly record struct ArrayRef<T>(ShaderStage Stage, T Array);
public readonly BufferRef[] UniformBufferRefs = new BufferRef[Constants.MaxUniformBufferBindings]; public readonly BufferRef[] UniformBufferRefs = new BufferRef[Constants.MaxUniformBufferBindings];
public readonly BufferRef[] StorageBufferRefs = new BufferRef[Constants.MaxStorageBufferBindings]; public readonly BufferRef[] StorageBufferRefs = new BufferRef[Constants.MaxStorageBufferBindings];
public readonly TextureRef[] TextureRefs = new TextureRef[Constants.MaxTextureBindings]; public readonly TextureRef[] TextureRefs = new TextureRef[Constants.MaxTextureBindings];
public readonly ImageRef[] ImageRefs = new ImageRef[Constants.MaxTextureBindings]; public readonly ImageRef[] ImageRefs = new ImageRef[Constants.MaxTextureBindings];
public ArrayRef<TextureArray>[] TextureArrayRefs = [];
public ArrayRef<ImageArray>[] ImageArrayRefs = [];
public ArrayRef<TextureArray>[] TextureArrayExtraRefs = [];
public ArrayRef<ImageArray>[] ImageArrayExtraRefs = [];
public IndexBufferState IndexBuffer = default; public IndexBufferState IndexBuffer = default;
public MTLDepthClipMode DepthClipMode = MTLDepthClipMode.Clip; public MTLDepthClipMode DepthClipMode = MTLDepthClipMode.Clip;

View file

@ -14,6 +14,8 @@ namespace Ryujinx.Graphics.Metal
[SupportedOSPlatform("macos")] [SupportedOSPlatform("macos")]
struct EncoderStateManager : IDisposable struct EncoderStateManager : IDisposable
{ {
private const int ArrayGrowthSize = 16;
private readonly MTLDevice _device; private readonly MTLDevice _device;
private readonly Pipeline _pipeline; private readonly Pipeline _pipeline;
private readonly BufferManager _bufferManager; private readonly BufferManager _bufferManager;
@ -90,6 +92,16 @@ namespace Ryujinx.Graphics.Metal
_currentState.ClearLoadAction = clear; _currentState.ClearLoadAction = clear;
} }
public void DirtyTextures()
{
_currentState.Dirty |= DirtyFlags.Textures;
}
public void DirtyImages()
{
_currentState.Dirty |= DirtyFlags.Images;
}
public readonly MTLRenderCommandEncoder CreateRenderCommandEncoder() public readonly MTLRenderCommandEncoder CreateRenderCommandEncoder()
{ {
// Initialise Pass & State // Initialise Pass & State
@ -831,6 +843,66 @@ namespace Ryujinx.Graphics.Metal
_currentState.Dirty |= DirtyFlags.Images; _currentState.Dirty |= DirtyFlags.Images;
} }
public void UpdateTextureArray(ShaderStage stage, ulong binding, TextureArray array)
{
ref EncoderState.ArrayRef<TextureArray> arrayRef = ref GetArrayRef(ref _currentState.TextureArrayRefs, (int)binding, ArrayGrowthSize);
if (arrayRef.Stage != stage || arrayRef.Array != array)
{
arrayRef = new EncoderState.ArrayRef<TextureArray>(stage, array);
_currentState.Dirty |= DirtyFlags.Textures;
}
}
public void UpdateTextureArraySeparate(ShaderStage stage, int setIndex, TextureArray array)
{
ref EncoderState.ArrayRef<TextureArray> arrayRef = ref GetArrayRef(ref _currentState.TextureArrayRefs, setIndex);
if (arrayRef.Stage != stage || arrayRef.Array != array)
{
arrayRef = new EncoderState.ArrayRef<TextureArray>(stage, array);
_currentState.Dirty |= DirtyFlags.Textures;
}
}
public void UpdateImageArray(ShaderStage stage, ulong binding, ImageArray array)
{
ref EncoderState.ArrayRef<ImageArray> arrayRef = ref GetArrayRef(ref _currentState.ImageArrayRefs, (int)binding, ArrayGrowthSize);
if (arrayRef.Stage != stage || arrayRef.Array != array)
{
arrayRef = new EncoderState.ArrayRef<ImageArray>(stage, array);
_currentState.Dirty |= DirtyFlags.Images;
}
}
public void UpdateImageArraySeparate(ShaderStage stage, int setIndex, ImageArray array)
{
ref EncoderState.ArrayRef<ImageArray> arrayRef = ref GetArrayRef(ref _currentState.ImageArrayExtraRefs, setIndex);
if (arrayRef.Stage != stage || arrayRef.Array != array)
{
arrayRef = new EncoderState.ArrayRef<ImageArray>(stage, array);
_currentState.Dirty |= DirtyFlags.Images;
}
}
private static ref EncoderState.ArrayRef<T> GetArrayRef<T>(ref EncoderState.ArrayRef<T>[] 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) private readonly void SetDepthStencilState(MTLRenderCommandEncoder renderCommandEncoder)
{ {
MTLDepthStencilState state = _depthStencilCache.GetOrCreate(_currentState.DepthStencilUid); MTLDepthStencilState state = _depthStencilCache.GetOrCreate(_currentState.DepthStencilUid);

View file

@ -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() { }
}
}

View file

@ -1,5 +1,4 @@
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader.Translation; using Ryujinx.Graphics.Shader.Translation;
using SharpMetal.Metal; using SharpMetal.Metal;
@ -97,7 +96,7 @@ namespace Ryujinx.Graphics.Metal
public IImageArray CreateImageArray(int size, bool isBuffer) public IImageArray CreateImageArray(int size, bool isBuffer)
{ {
throw new NotImplementedException(); return new ImageArray(size, isBuffer, _pipeline);
} }
public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info) public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info)
@ -122,7 +121,7 @@ namespace Ryujinx.Graphics.Metal
public ITextureArray CreateTextureArray(int size, bool isBuffer) public ITextureArray CreateTextureArray(int size, bool isBuffer)
{ {
throw new NotImplementedException(); return new TextureArray(size, isBuffer, _pipeline);
} }
public bool PrepareHostMapping(IntPtr address, ulong size) public bool PrepareHostMapping(IntPtr address, ulong size)

View file

@ -192,6 +192,16 @@ namespace Ryujinx.Graphics.Metal
_renderer.RegisterFlush(); _renderer.RegisterFlush();
} }
public void DirtyTextures()
{
_encoderStateManager.DirtyTextures();
}
public void DirtyImages()
{
_encoderStateManager.DirtyImages();
}
public void Blit( public void Blit(
Texture src, Texture src,
Texture dst, Texture dst,
@ -542,12 +552,20 @@ namespace Ryujinx.Graphics.Metal
public void SetImageArray(ShaderStage stage, int binding, IImageArray array) 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) 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) public void SetLineParameters(float width, bool smooth)
@ -656,12 +674,20 @@ namespace Ryujinx.Graphics.Metal
public void SetTextureArray(ShaderStage stage, int binding, ITextureArray array) 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) 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) public void SetUserClipDistance(int index, bool enableClip)

View file

@ -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() { }
}
}