| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654 |
- using Ryujinx.Common.Logging;
- using Ryujinx.Common.Memory;
- using Ryujinx.Graphics.GAL;
- using SharpMetal.Foundation;
- using SharpMetal.Metal;
- using System;
- using System.Runtime.Versioning;
- namespace Ryujinx.Graphics.Metal
- {
- [SupportedOSPlatform("macos")]
- class Texture : TextureBase, ITexture
- {
- private MTLTexture _identitySwizzleHandle;
- private readonly bool _identityIsDifferent;
- public Texture(MTLDevice device, MetalRenderer renderer, Pipeline pipeline, TextureCreateInfo info) : base(device, renderer, pipeline, info)
- {
- MTLPixelFormat pixelFormat = FormatTable.GetFormat(Info.Format);
- MTLTextureDescriptor descriptor = new()
- {
- PixelFormat = pixelFormat,
- Usage = MTLTextureUsage.Unknown,
- SampleCount = (ulong)Info.Samples,
- TextureType = Info.Target.Convert(),
- Width = (ulong)Info.Width,
- Height = (ulong)Info.Height,
- MipmapLevelCount = (ulong)Info.Levels
- };
- if (info.Target == Target.Texture3D)
- {
- descriptor.Depth = (ulong)Info.Depth;
- }
- else if (info.Target != Target.Cubemap)
- {
- if (info.Target == Target.CubemapArray)
- {
- descriptor.ArrayLength = (ulong)(Info.Depth / 6);
- }
- else
- {
- descriptor.ArrayLength = (ulong)Info.Depth;
- }
- }
- MTLTextureSwizzleChannels swizzle = GetSwizzle(info, descriptor.PixelFormat);
- _identitySwizzleHandle = Device.NewTexture(descriptor);
- if (SwizzleIsIdentity(swizzle))
- {
- MtlTexture = _identitySwizzleHandle;
- }
- else
- {
- MtlTexture = CreateDefaultView(_identitySwizzleHandle, swizzle, descriptor);
- _identityIsDifferent = true;
- }
- 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)
- {
- MTLPixelFormat pixelFormat = FormatTable.GetFormat(Info.Format);
- if (info.DepthStencilMode == DepthStencilMode.Stencil)
- {
- pixelFormat = pixelFormat switch
- {
- MTLPixelFormat.Depth32FloatStencil8 => MTLPixelFormat.X32Stencil8,
- MTLPixelFormat.Depth24UnormStencil8 => MTLPixelFormat.X24Stencil8,
- _ => pixelFormat
- };
- }
- MTLTextureType textureType = Info.Target.Convert();
- NSRange levels;
- levels.location = (ulong)firstLevel;
- levels.length = (ulong)Info.Levels;
- NSRange slices;
- slices.location = (ulong)firstLayer;
- slices.length = textureType == MTLTextureType.Type3D ? 1 : (ulong)info.GetDepthOrLayers();
- MTLTextureSwizzleChannels swizzle = GetSwizzle(info, pixelFormat);
- _identitySwizzleHandle = sourceTexture.NewTextureView(pixelFormat, textureType, levels, slices);
- if (SwizzleIsIdentity(swizzle))
- {
- MtlTexture = _identitySwizzleHandle;
- }
- else
- {
- MtlTexture = sourceTexture.NewTextureView(pixelFormat, textureType, levels, slices, swizzle);
- _identityIsDifferent = true;
- }
- MtlFormat = pixelFormat;
- FirstLayer = firstLayer;
- FirstLevel = firstLevel;
- }
- public void PopulateRenderPassAttachment(MTLRenderPassColorAttachmentDescriptor descriptor)
- {
- descriptor.Texture = _identitySwizzleHandle;
- }
- private MTLTexture CreateDefaultView(MTLTexture texture, MTLTextureSwizzleChannels swizzle, MTLTextureDescriptor descriptor)
- {
- NSRange levels;
- levels.location = 0;
- levels.length = (ulong)Info.Levels;
- NSRange slices;
- slices.location = 0;
- slices.length = Info.Target == Target.Texture3D ? 1 : (ulong)Info.GetDepthOrLayers();
- return texture.NewTextureView(descriptor.PixelFormat, descriptor.TextureType, levels, slices, swizzle);
- }
- private bool SwizzleIsIdentity(MTLTextureSwizzleChannels swizzle)
- {
- return swizzle.red == MTLTextureSwizzle.Red &&
- swizzle.green == MTLTextureSwizzle.Green &&
- swizzle.blue == MTLTextureSwizzle.Blue &&
- swizzle.alpha == MTLTextureSwizzle.Alpha;
- }
- private MTLTextureSwizzleChannels GetSwizzle(TextureCreateInfo info, MTLPixelFormat pixelFormat)
- {
- MTLTextureSwizzle swizzleR = Info.SwizzleR.Convert();
- MTLTextureSwizzle swizzleG = Info.SwizzleG.Convert();
- MTLTextureSwizzle swizzleB = Info.SwizzleB.Convert();
- MTLTextureSwizzle swizzleA = Info.SwizzleA.Convert();
- if (info.Format == Format.R5G5B5A1Unorm ||
- info.Format == Format.R5G5B5X1Unorm ||
- info.Format == Format.R5G6B5Unorm)
- {
- (swizzleB, swizzleR) = (swizzleR, swizzleB);
- }
- else if (pixelFormat == MTLPixelFormat.ABGR4Unorm || info.Format == Format.A1B5G5R5Unorm)
- {
- MTLTextureSwizzle tempB = swizzleB;
- MTLTextureSwizzle tempA = swizzleA;
- swizzleB = swizzleG;
- swizzleA = swizzleR;
- swizzleR = tempA;
- swizzleG = tempB;
- }
- return new MTLTextureSwizzleChannels
- {
- red = swizzleR,
- green = swizzleG,
- blue = swizzleB,
- alpha = swizzleA
- };
- }
- public void CopyTo(ITexture destination, int firstLayer, int firstLevel)
- {
- CommandBufferScoped cbs = Pipeline.Cbs;
- TextureBase src = this;
- TextureBase dst = (TextureBase)destination;
- if (!Valid || !dst.Valid)
- {
- return;
- }
- MTLTexture srcImage = GetHandle();
- MTLTexture dstImage = dst.GetHandle();
- if (!dst.Info.Target.IsMultisample() && Info.Target.IsMultisample())
- {
- // int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer);
- // _gd.HelperShader.CopyMSToNonMS(_gd, cbs, src, dst, 0, firstLayer, layers);
- }
- else if (dst.Info.Target.IsMultisample() && !Info.Target.IsMultisample())
- {
- // int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer);
- // _gd.HelperShader.CopyNonMSToMS(_gd, cbs, src, dst, 0, firstLayer, layers);
- }
- else if (dst.Info.BytesPerPixel != Info.BytesPerPixel)
- {
- // int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer);
- // int levels = Math.Min(Info.Levels, dst.Info.Levels - firstLevel);
- // _gd.HelperShader.CopyIncompatibleFormats(_gd, cbs, src, dst, 0, firstLayer, 0, firstLevel, layers, levels);
- }
- else if (src.Info.Format.IsDepthOrStencil() != dst.Info.Format.IsDepthOrStencil())
- {
- // int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer);
- // int levels = Math.Min(Info.Levels, dst.Info.Levels - firstLevel);
- // TODO: depth copy?
- // _gd.HelperShader.CopyColor(_gd, cbs, src, dst, 0, firstLayer, 0, FirstLevel, layers, levels);
- }
- else
- {
- TextureCopy.Copy(
- cbs,
- srcImage,
- dstImage,
- src.Info,
- dst.Info,
- 0,
- firstLayer,
- 0,
- firstLevel);
- }
- }
- public void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel)
- {
- CommandBufferScoped cbs = Pipeline.Cbs;
- TextureBase src = this;
- TextureBase dst = (TextureBase)destination;
- if (!Valid || !dst.Valid)
- {
- return;
- }
- MTLTexture srcImage = GetHandle();
- MTLTexture dstImage = dst.GetHandle();
- if (!dst.Info.Target.IsMultisample() && Info.Target.IsMultisample())
- {
- // _gd.HelperShader.CopyMSToNonMS(_gd, cbs, src, dst, srcLayer, dstLayer, 1);
- }
- else if (dst.Info.Target.IsMultisample() && !Info.Target.IsMultisample())
- {
- // _gd.HelperShader.CopyNonMSToMS(_gd, cbs, src, dst, srcLayer, dstLayer, 1);
- }
- else if (dst.Info.BytesPerPixel != Info.BytesPerPixel)
- {
- // _gd.HelperShader.CopyIncompatibleFormats(_gd, cbs, src, dst, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1);
- }
- else if (src.Info.Format.IsDepthOrStencil() != dst.Info.Format.IsDepthOrStencil())
- {
- // _gd.HelperShader.CopyColor(_gd, cbs, src, dst, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1);
- }
- else
- {
- TextureCopy.Copy(
- cbs,
- srcImage,
- dstImage,
- src.Info,
- dst.Info,
- srcLayer,
- dstLayer,
- srcLevel,
- dstLevel,
- 1,
- 1);
- }
- }
- public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter)
- {
- if (!Renderer.CommandBufferPool.OwnedByCurrentThread)
- {
- Logger.Warning?.PrintMsg(LogClass.Gpu, "Metal doesn't currently support scaled blit on background thread.");
- return;
- }
- Texture dst = (Texture)destination;
- bool isDepthOrStencil = dst.Info.Format.IsDepthOrStencil();
- Pipeline.Blit(this, dst, srcRegion, dstRegion, isDepthOrStencil, linearFilter);
- }
- public void CopyTo(BufferRange range, int layer, int level, int stride)
- {
- CommandBufferScoped cbs = Pipeline.Cbs;
- int outSize = Info.GetMipSize(level);
- int hostSize = GetBufferDataLength(outSize);
- int offset = range.Offset;
- Auto<DisposableBuffer> autoBuffer = Renderer.BufferManager.GetBuffer(range.Handle, true);
- MTLBuffer mtlBuffer = autoBuffer.Get(cbs, range.Offset, outSize).Value;
- if (PrepareOutputBuffer(cbs, hostSize, mtlBuffer, out MTLBuffer copyToBuffer, out BufferHolder tempCopyHolder))
- {
- offset = 0;
- }
- CopyFromOrToBuffer(cbs, copyToBuffer, MtlTexture, hostSize, true, layer, level, 1, 1, singleSlice: true, offset, stride);
- if (tempCopyHolder != null)
- {
- CopyDataToOutputBuffer(cbs, tempCopyHolder, autoBuffer, hostSize, range.Offset);
- tempCopyHolder.Dispose();
- }
- }
- public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel)
- {
- return new Texture(Device, Renderer, Pipeline, info, _identitySwizzleHandle, firstLayer, firstLevel);
- }
- private void CopyDataToBuffer(Span<byte> storage, ReadOnlySpan<byte> input)
- {
- if (NeedsD24S8Conversion())
- {
- FormatConverter.ConvertD24S8ToD32FS8(storage, input);
- return;
- }
- input.CopyTo(storage);
- }
- private ReadOnlySpan<byte> GetDataFromBuffer(ReadOnlySpan<byte> storage, int size, Span<byte> output)
- {
- if (NeedsD24S8Conversion())
- {
- if (output.IsEmpty)
- {
- output = new byte[GetBufferDataLength(size)];
- }
- FormatConverter.ConvertD32FS8ToD24S8(output, storage);
- return output;
- }
- return storage;
- }
- private bool PrepareOutputBuffer(CommandBufferScoped cbs, int hostSize, MTLBuffer target, out MTLBuffer copyTarget, out BufferHolder copyTargetHolder)
- {
- if (NeedsD24S8Conversion())
- {
- copyTargetHolder = Renderer.BufferManager.Create(hostSize);
- copyTarget = copyTargetHolder.GetBuffer().Get(cbs, 0, hostSize).Value;
- return true;
- }
- copyTarget = target;
- copyTargetHolder = null;
- return false;
- }
- private void CopyDataToOutputBuffer(CommandBufferScoped cbs, BufferHolder hostData, Auto<DisposableBuffer> copyTarget, int hostSize, int dstOffset)
- {
- if (NeedsD24S8Conversion())
- {
- Renderer.HelperShader.ConvertD32S8ToD24S8(cbs, hostData, copyTarget, hostSize / (2 * sizeof(int)), dstOffset);
- }
- }
- private bool NeedsD24S8Conversion()
- {
- return FormatTable.IsD24S8(Info.Format) && MtlFormat == MTLPixelFormat.Depth32FloatStencil8;
- }
- public void CopyFromOrToBuffer(
- CommandBufferScoped cbs,
- MTLBuffer buffer,
- MTLTexture image,
- int size,
- bool to,
- int dstLayer,
- int dstLevel,
- int dstLayers,
- int dstLevels,
- bool singleSlice,
- int offset = 0,
- int stride = 0)
- {
- MTLBlitCommandEncoder blitCommandEncoder = cbs.Encoders.EnsureBlitEncoder();
- bool is3D = Info.Target == Target.Texture3D;
- int width = Math.Max(1, Info.Width >> dstLevel);
- int height = Math.Max(1, Info.Height >> dstLevel);
- int depth = is3D && !singleSlice ? Math.Max(1, Info.Depth >> dstLevel) : 1;
- int layers = dstLayers;
- int levels = dstLevels;
- for (int oLevel = 0; oLevel < levels; oLevel++)
- {
- int level = oLevel + dstLevel;
- int mipSize = Info.GetMipSize2D(level);
- int mipSizeLevel = GetBufferDataLength(is3D && !singleSlice
- ? Info.GetMipSize(level)
- : mipSize * dstLayers);
- int endOffset = offset + mipSizeLevel;
- if ((uint)endOffset > (uint)size)
- {
- break;
- }
- for (int oLayer = 0; oLayer < layers; oLayer++)
- {
- int layer = !is3D ? dstLayer + oLayer : 0;
- int z = is3D ? dstLayer + oLayer : 0;
- if (to)
- {
- blitCommandEncoder.CopyFromTexture(
- image,
- (ulong)layer,
- (ulong)level,
- new MTLOrigin { z = (ulong)z },
- new MTLSize { width = (ulong)width, height = (ulong)height, depth = 1 },
- buffer,
- (ulong)offset,
- (ulong)Info.GetMipStride(level),
- (ulong)mipSize
- );
- }
- else
- {
- blitCommandEncoder.CopyFromBuffer(
- buffer,
- (ulong)offset,
- (ulong)Info.GetMipStride(level),
- (ulong)mipSize,
- new MTLSize { width = (ulong)width, height = (ulong)height, depth = 1 },
- image,
- (ulong)(layer + oLayer),
- (ulong)level,
- new MTLOrigin { z = (ulong)z }
- );
- }
- offset += mipSize;
- }
- width = Math.Max(1, width >> 1);
- height = Math.Max(1, height >> 1);
- if (Info.Target == Target.Texture3D)
- {
- depth = Math.Max(1, depth >> 1);
- }
- }
- }
- private ReadOnlySpan<byte> GetData(CommandBufferPool cbp, PersistentFlushBuffer flushBuffer)
- {
- int size = 0;
- for (int level = 0; level < Info.Levels; level++)
- {
- size += Info.GetMipSize(level);
- }
- size = GetBufferDataLength(size);
- Span<byte> result = flushBuffer.GetTextureData(cbp, this, size);
- return GetDataFromBuffer(result, size, result);
- }
- private ReadOnlySpan<byte> GetData(CommandBufferPool cbp, PersistentFlushBuffer flushBuffer, int layer, int level)
- {
- int size = GetBufferDataLength(Info.GetMipSize(level));
- Span<byte> result = flushBuffer.GetTextureData(cbp, this, size, layer, level);
- return GetDataFromBuffer(result, size, result);
- }
- public PinnedSpan<byte> GetData()
- {
- BackgroundResource resources = Renderer.BackgroundResources.Get();
- if (Renderer.CommandBufferPool.OwnedByCurrentThread)
- {
- Renderer.FlushAllCommands();
- return PinnedSpan<byte>.UnsafeFromSpan(GetData(Renderer.CommandBufferPool, resources.GetFlushBuffer()));
- }
- return PinnedSpan<byte>.UnsafeFromSpan(GetData(resources.GetPool(), resources.GetFlushBuffer()));
- }
- public PinnedSpan<byte> GetData(int layer, int level)
- {
- BackgroundResource resources = Renderer.BackgroundResources.Get();
- if (Renderer.CommandBufferPool.OwnedByCurrentThread)
- {
- Renderer.FlushAllCommands();
- return PinnedSpan<byte>.UnsafeFromSpan(GetData(Renderer.CommandBufferPool, resources.GetFlushBuffer(), layer, level));
- }
- return PinnedSpan<byte>.UnsafeFromSpan(GetData(resources.GetPool(), resources.GetFlushBuffer(), layer, level));
- }
- public void SetData(MemoryOwner<byte> data)
- {
- MTLBlitCommandEncoder blitCommandEncoder = Pipeline.GetOrCreateBlitEncoder();
- Span<byte> dataSpan = data.Memory.Span;
- BufferHolder buffer = Renderer.BufferManager.Create(dataSpan.Length);
- buffer.SetDataUnchecked(0, dataSpan);
- MTLBuffer mtlBuffer = buffer.GetBuffer(false).Get(Pipeline.Cbs).Value;
- int width = Info.Width;
- int height = Info.Height;
- int depth = Info.Depth;
- int levels = Info.Levels;
- int layers = Info.GetLayers();
- bool is3D = Info.Target == Target.Texture3D;
- int offset = 0;
- for (int level = 0; level < levels; level++)
- {
- int mipSize = Info.GetMipSize2D(level);
- int endOffset = offset + mipSize;
- if ((uint)endOffset > (uint)dataSpan.Length)
- {
- return;
- }
- for (int layer = 0; layer < layers; layer++)
- {
- blitCommandEncoder.CopyFromBuffer(
- mtlBuffer,
- (ulong)offset,
- (ulong)Info.GetMipStride(level),
- (ulong)mipSize,
- new MTLSize { width = (ulong)width, height = (ulong)height, depth = is3D ? (ulong)depth : 1 },
- MtlTexture,
- (ulong)layer,
- (ulong)level,
- new MTLOrigin()
- );
- offset += mipSize;
- }
- width = Math.Max(1, width >> 1);
- height = Math.Max(1, height >> 1);
- if (is3D)
- {
- depth = Math.Max(1, depth >> 1);
- }
- }
- // Cleanup
- buffer.Dispose();
- }
- private void SetData(ReadOnlySpan<byte> data, int layer, int level, int layers, int levels, bool singleSlice)
- {
- int bufferDataLength = GetBufferDataLength(data.Length);
- using BufferHolder bufferHolder = Renderer.BufferManager.Create(bufferDataLength);
- // TODO: loadInline logic
- CommandBufferScoped cbs = Pipeline.Cbs;
- CopyDataToBuffer(bufferHolder.GetDataStorage(0, bufferDataLength), data);
- MTLBuffer buffer = bufferHolder.GetBuffer().Get(cbs).Value;
- MTLTexture image = GetHandle();
- CopyFromOrToBuffer(cbs, buffer, image, bufferDataLength, false, layer, level, layers, levels, singleSlice);
- }
- public void SetData(MemoryOwner<byte> data, int layer, int level)
- {
- SetData(data.Memory.Span, layer, level, 1, 1, singleSlice: true);
- data.Dispose();
- }
- public void SetData(MemoryOwner<byte> data, int layer, int level, Rectangle<int> region)
- {
- MTLBlitCommandEncoder blitCommandEncoder = Pipeline.GetOrCreateBlitEncoder();
- ulong bytesPerRow = (ulong)Info.GetMipStride(level);
- ulong bytesPerImage = 0;
- if (MtlTexture.TextureType == MTLTextureType.Type3D)
- {
- bytesPerImage = bytesPerRow * (ulong)Info.Height;
- }
- Span<byte> dataSpan = data.Memory.Span;
- BufferHolder buffer = Renderer.BufferManager.Create(dataSpan.Length);
- buffer.SetDataUnchecked(0, dataSpan);
- MTLBuffer mtlBuffer = buffer.GetBuffer(false).Get(Pipeline.Cbs).Value;
- blitCommandEncoder.CopyFromBuffer(
- mtlBuffer,
- 0,
- bytesPerRow,
- bytesPerImage,
- new MTLSize { width = (ulong)region.Width, height = (ulong)region.Height, depth = 1 },
- MtlTexture,
- (ulong)layer,
- (ulong)level,
- new MTLOrigin { x = (ulong)region.X, y = (ulong)region.Y }
- );
- // Cleanup
- buffer.Dispose();
- }
- private int GetBufferDataLength(int length)
- {
- if (NeedsD24S8Conversion())
- {
- return length * 2;
- }
- return length;
- }
- public void SetStorage(BufferRange buffer)
- {
- throw new NotImplementedException();
- }
- public override void Release()
- {
- if (_identityIsDifferent)
- {
- _identitySwizzleHandle.Dispose();
- }
- base.Release();
- }
- }
- }
|