| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530 |
- using Ryujinx.Common;
- using Ryujinx.Graphics.GAL;
- using Silk.NET.Vulkan;
- using System;
- using System.Collections.Generic;
- using System.Numerics;
- using VkBuffer = Silk.NET.Vulkan.Buffer;
- using VkFormat = Silk.NET.Vulkan.Format;
- namespace Ryujinx.Graphics.Vulkan
- {
- class TextureStorage : IDisposable
- {
- private const MemoryPropertyFlags DefaultImageMemoryFlags =
- MemoryPropertyFlags.DeviceLocalBit;
- private const ImageUsageFlags DefaultUsageFlags =
- ImageUsageFlags.SampledBit |
- ImageUsageFlags.TransferSrcBit |
- ImageUsageFlags.TransferDstBit;
- public const AccessFlags DefaultAccessMask =
- AccessFlags.ShaderReadBit |
- AccessFlags.ShaderWriteBit |
- AccessFlags.ColorAttachmentReadBit |
- AccessFlags.ColorAttachmentWriteBit |
- AccessFlags.DepthStencilAttachmentReadBit |
- AccessFlags.DepthStencilAttachmentWriteBit |
- AccessFlags.TransferReadBit |
- AccessFlags.TransferWriteBit;
- private readonly VulkanRenderer _gd;
- private readonly Device _device;
- private TextureCreateInfo _info;
- public TextureCreateInfo Info => _info;
- private readonly Image _image;
- private readonly Auto<DisposableImage> _imageAuto;
- private readonly Auto<MemoryAllocation> _allocationAuto;
- private Auto<MemoryAllocation> _foreignAllocationAuto;
- private Dictionary<GAL.Format, TextureStorage> _aliasedStorages;
- private AccessFlags _lastModificationAccess;
- private PipelineStageFlags _lastModificationStage;
- private AccessFlags _lastReadAccess;
- private PipelineStageFlags _lastReadStage;
- private int _viewsCount;
- private ulong _size;
- public VkFormat VkFormat { get; }
- public float ScaleFactor { get; }
- public unsafe TextureStorage(
- VulkanRenderer gd,
- Device device,
- TextureCreateInfo info,
- float scaleFactor,
- Auto<MemoryAllocation> foreignAllocation = null)
- {
- _gd = gd;
- _device = device;
- _info = info;
- ScaleFactor = scaleFactor;
- var format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format);
- var levels = (uint)info.Levels;
- var layers = (uint)info.GetLayers();
- var depth = (uint)(info.Target == Target.Texture3D ? info.Depth : 1);
- VkFormat = format;
- var type = info.Target.Convert();
- var extent = new Extent3D((uint)info.Width, (uint)info.Height, depth);
- var sampleCountFlags = ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)info.Samples);
- var usage = GetImageUsageFromFormat(info.Format);
- var flags = ImageCreateFlags.CreateMutableFormatBit;
- // This flag causes mipmapped texture arrays to break on AMD GCN, so for that copy dependencies are forced for aliasing as cube.
- bool isCube = info.Target == Target.Cubemap || info.Target == Target.CubemapArray;
- bool cubeCompatible = gd.IsAmdGcn ? isCube : (info.Width == info.Height && layers >= 6);
- if (type == ImageType.Type2D && cubeCompatible)
- {
- flags |= ImageCreateFlags.CreateCubeCompatibleBit;
- }
- if (type == ImageType.Type3D && !gd.Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.No3DImageView))
- {
- flags |= ImageCreateFlags.Create2DArrayCompatibleBit;
- }
- var imageCreateInfo = new ImageCreateInfo()
- {
- SType = StructureType.ImageCreateInfo,
- ImageType = type,
- Format = format,
- Extent = extent,
- MipLevels = levels,
- ArrayLayers = layers,
- Samples = sampleCountFlags,
- Tiling = ImageTiling.Optimal,
- Usage = usage,
- SharingMode = SharingMode.Exclusive,
- InitialLayout = ImageLayout.Undefined,
- Flags = flags
- };
- gd.Api.CreateImage(device, imageCreateInfo, null, out _image).ThrowOnError();
- if (foreignAllocation == null)
- {
- gd.Api.GetImageMemoryRequirements(device, _image, out var requirements);
- var allocation = gd.MemoryAllocator.AllocateDeviceMemory(requirements, DefaultImageMemoryFlags);
- if (allocation.Memory.Handle == 0UL)
- {
- gd.Api.DestroyImage(device, _image, null);
- throw new Exception("Image initialization failed.");
- }
- _size = requirements.Size;
- gd.Api.BindImageMemory(device, _image, allocation.Memory, allocation.Offset).ThrowOnError();
- _allocationAuto = new Auto<MemoryAllocation>(allocation);
- _imageAuto = new Auto<DisposableImage>(new DisposableImage(_gd.Api, device, _image), null, _allocationAuto);
- InitialTransition(ImageLayout.Undefined, ImageLayout.General);
- }
- else
- {
- _foreignAllocationAuto = foreignAllocation;
- foreignAllocation.IncrementReferenceCount();
- var allocation = foreignAllocation.GetUnsafe();
- gd.Api.BindImageMemory(device, _image, allocation.Memory, allocation.Offset).ThrowOnError();
- _imageAuto = new Auto<DisposableImage>(new DisposableImage(_gd.Api, device, _image));
- InitialTransition(ImageLayout.Preinitialized, ImageLayout.General);
- }
- }
- public TextureStorage CreateAliasedColorForDepthStorageUnsafe(GAL.Format format)
- {
- var colorFormat = format switch
- {
- GAL.Format.S8Uint => GAL.Format.R8Unorm,
- GAL.Format.D16Unorm => GAL.Format.R16Unorm,
- GAL.Format.S8UintD24Unorm => GAL.Format.R8G8B8A8Unorm,
- GAL.Format.D32Float => GAL.Format.R32Float,
- GAL.Format.D24UnormS8Uint => GAL.Format.R8G8B8A8Unorm,
- GAL.Format.D32FloatS8Uint => GAL.Format.R32G32Float,
- _ => throw new ArgumentException($"\"{format}\" is not a supported depth or stencil format.")
- };
- return CreateAliasedStorageUnsafe(colorFormat);
- }
- public TextureStorage CreateAliasedStorageUnsafe(GAL.Format format)
- {
- if (_aliasedStorages == null || !_aliasedStorages.TryGetValue(format, out var storage))
- {
- _aliasedStorages ??= new Dictionary<GAL.Format, TextureStorage>();
- var info = NewCreateInfoWith(ref _info, format, _info.BytesPerPixel);
- storage = new TextureStorage(_gd, _device, info, ScaleFactor, _allocationAuto);
- _aliasedStorages.Add(format, storage);
- }
- return storage;
- }
- public static TextureCreateInfo NewCreateInfoWith(ref TextureCreateInfo info, GAL.Format format, int bytesPerPixel)
- {
- return NewCreateInfoWith(ref info, format, bytesPerPixel, info.Width, info.Height);
- }
- public static TextureCreateInfo NewCreateInfoWith(
- ref TextureCreateInfo info,
- GAL.Format format,
- int bytesPerPixel,
- int width,
- int height)
- {
- return new TextureCreateInfo(
- width,
- height,
- info.Depth,
- info.Levels,
- info.Samples,
- info.BlockWidth,
- info.BlockHeight,
- bytesPerPixel,
- format,
- info.DepthStencilMode,
- info.Target,
- info.SwizzleR,
- info.SwizzleG,
- info.SwizzleB,
- info.SwizzleA);
- }
- public Auto<DisposableImage> GetImage()
- {
- return _imageAuto;
- }
- public Image GetImageForViewCreation()
- {
- return _image;
- }
- public bool HasCommandBufferDependency(CommandBufferScoped cbs)
- {
- if (_foreignAllocationAuto != null)
- {
- return _foreignAllocationAuto.HasCommandBufferDependency(cbs);
- }
- else if (_allocationAuto != null)
- {
- return _allocationAuto.HasCommandBufferDependency(cbs);
- }
- return false;
- }
- private unsafe void InitialTransition(ImageLayout srcLayout, ImageLayout dstLayout)
- {
- CommandBufferScoped cbs;
- bool useTempCbs = !_gd.CommandBufferPool.OwnedByCurrentThread;
- if (useTempCbs)
- {
- cbs = _gd.BackgroundResources.Get().GetPool().Rent();
- }
- else
- {
- if (_gd.PipelineInternal != null)
- {
- cbs = _gd.PipelineInternal.GetPreloadCommandBuffer();
- }
- else
- {
- cbs = _gd.CommandBufferPool.Rent();
- useTempCbs = true;
- }
- }
- var aspectFlags = _info.Format.ConvertAspectFlags();
- var subresourceRange = new ImageSubresourceRange(aspectFlags, 0, (uint)_info.Levels, 0, (uint)_info.GetLayers());
- var barrier = new ImageMemoryBarrier()
- {
- SType = StructureType.ImageMemoryBarrier,
- SrcAccessMask = 0,
- DstAccessMask = DefaultAccessMask,
- OldLayout = srcLayout,
- NewLayout = dstLayout,
- SrcQueueFamilyIndex = Vk.QueueFamilyIgnored,
- DstQueueFamilyIndex = Vk.QueueFamilyIgnored,
- Image = _imageAuto.Get(cbs).Value,
- SubresourceRange = subresourceRange
- };
- _gd.Api.CmdPipelineBarrier(
- cbs.CommandBuffer,
- PipelineStageFlags.TopOfPipeBit,
- PipelineStageFlags.AllCommandsBit,
- 0,
- 0,
- null,
- 0,
- null,
- 1,
- barrier);
- if (useTempCbs)
- {
- cbs.Dispose();
- }
- }
- public static ImageUsageFlags GetImageUsageFromFormat(GAL.Format format)
- {
- var usage = DefaultUsageFlags;
- if (format.IsDepthOrStencil())
- {
- usage |= ImageUsageFlags.DepthStencilAttachmentBit;
- }
- else if (format.IsRtColorCompatible())
- {
- usage |= ImageUsageFlags.ColorAttachmentBit;
- }
- if (format.IsImageCompatible())
- {
- usage |= ImageUsageFlags.StorageBit;
- }
- return usage;
- }
- public static SampleCountFlags ConvertToSampleCountFlags(SampleCountFlags supportedSampleCounts, uint samples)
- {
- if (samples == 0 || samples > (uint)SampleCountFlags.Count64Bit)
- {
- return SampleCountFlags.Count1Bit;
- }
- // Round up to the nearest power of two.
- SampleCountFlags converted = (SampleCountFlags)(1u << (31 - BitOperations.LeadingZeroCount(samples)));
- // Pick nearest sample count that the host actually supports.
- while (converted != SampleCountFlags.Count1Bit && (converted & supportedSampleCounts) == 0)
- {
- converted = (SampleCountFlags)((uint)converted >> 1);
- }
- return converted;
- }
- public TextureView CreateView(TextureCreateInfo info, int firstLayer, int firstLevel)
- {
- return new TextureView(_gd, _device, info, this, firstLayer, firstLevel);
- }
- public void CopyFromOrToBuffer(
- CommandBuffer commandBuffer,
- VkBuffer buffer,
- Image image,
- int size,
- bool to,
- int x,
- int y,
- int dstLayer,
- int dstLevel,
- int dstLayers,
- int dstLevels,
- bool singleSlice,
- ImageAspectFlags aspectFlags,
- bool forFlush)
- {
- bool is3D = Info.Target == Target.Texture3D;
- int width = Info.Width;
- int height = Info.Height;
- int depth = is3D && !singleSlice ? Info.Depth : 1;
- int layer = is3D ? 0 : dstLayer;
- int layers = dstLayers;
- int levels = dstLevels;
- int offset = 0;
- for (int level = 0; level < levels; level++)
- {
- int mipSize = Info.GetMipSize(level);
- if (forFlush)
- {
- mipSize = GetBufferDataLength(mipSize);
- }
- int endOffset = offset + mipSize;
- if ((uint)endOffset > (uint)size)
- {
- break;
- }
- int rowLength = (Info.GetMipStride(level) / Info.BytesPerPixel) * Info.BlockWidth;
- var sl = new ImageSubresourceLayers(
- aspectFlags,
- (uint)(dstLevel + level),
- (uint)layer,
- (uint)layers);
- var extent = new Extent3D((uint)width, (uint)height, (uint)depth);
- int z = is3D ? dstLayer : 0;
- var region = new BufferImageCopy(
- (ulong)offset,
- (uint)BitUtils.AlignUp(rowLength, Info.BlockWidth),
- (uint)BitUtils.AlignUp(height, Info.BlockHeight),
- sl,
- new Offset3D(x, y, z),
- extent);
- if (to)
- {
- _gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, region);
- }
- else
- {
- _gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, region);
- }
- 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 int GetBufferDataLength(int length)
- {
- if (NeedsD24S8Conversion())
- {
- return length * 2;
- }
- return length;
- }
- private bool NeedsD24S8Conversion()
- {
- return FormatCapabilities.IsD24S8(Info.Format) && VkFormat == VkFormat.D32SfloatS8Uint;
- }
- public void SetModification(AccessFlags accessFlags, PipelineStageFlags stage)
- {
- _lastModificationAccess = accessFlags;
- _lastModificationStage = stage;
- }
- public void InsertReadToWriteBarrier(CommandBufferScoped cbs, AccessFlags dstAccessFlags, PipelineStageFlags dstStageFlags)
- {
- if (_lastReadAccess != AccessFlags.NoneKhr)
- {
- ImageAspectFlags aspectFlags = Info.Format.ConvertAspectFlags();
- TextureView.InsertImageBarrier(
- _gd.Api,
- cbs.CommandBuffer,
- _imageAuto.Get(cbs).Value,
- _lastReadAccess,
- dstAccessFlags,
- _lastReadStage,
- dstStageFlags,
- aspectFlags,
- 0,
- 0,
- _info.GetLayers(),
- _info.Levels);
- _lastReadAccess = AccessFlags.NoneKhr;
- _lastReadStage = PipelineStageFlags.None;
- }
- }
- public void InsertWriteToReadBarrier(CommandBufferScoped cbs, AccessFlags dstAccessFlags, PipelineStageFlags dstStageFlags)
- {
- _lastReadAccess |= dstAccessFlags;
- _lastReadStage |= dstStageFlags;
- if (_lastModificationAccess != AccessFlags.NoneKhr)
- {
- ImageAspectFlags aspectFlags = Info.Format.ConvertAspectFlags();
- TextureView.InsertImageBarrier(
- _gd.Api,
- cbs.CommandBuffer,
- _imageAuto.Get(cbs).Value,
- _lastModificationAccess,
- dstAccessFlags,
- _lastModificationStage,
- dstStageFlags,
- aspectFlags,
- 0,
- 0,
- _info.GetLayers(),
- _info.Levels);
- _lastModificationAccess = AccessFlags.NoneKhr;
- }
- }
- public void IncrementViewsCount()
- {
- _viewsCount++;
- }
- public void DecrementViewsCount()
- {
- if (--_viewsCount == 0)
- {
- _gd.PipelineInternal?.FlushCommandsIfWeightExceeding(_imageAuto, _size);
- Dispose();
- }
- }
- public void Dispose()
- {
- if (_aliasedStorages != null)
- {
- foreach (var storage in _aliasedStorages.Values)
- {
- storage.Dispose();
- }
- _aliasedStorages.Clear();
- }
- _imageAuto.Dispose();
- _allocationAuto?.Dispose();
- _foreignAllocationAuto?.DecrementReferenceCount();
- _foreignAllocationAuto = null;
- }
- }
- }
|