1053 lines
		
	
	
	
		
			36 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			1053 lines
		
	
	
	
		
			36 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using Ryujinx.Common.Memory;
 | |
| using Ryujinx.Graphics.GAL;
 | |
| using Silk.NET.Vulkan;
 | |
| using System;
 | |
| using System.Collections.Generic;
 | |
| using VkBuffer = Silk.NET.Vulkan.Buffer;
 | |
| using VkFormat = Silk.NET.Vulkan.Format;
 | |
| 
 | |
| namespace Ryujinx.Graphics.Vulkan
 | |
| {
 | |
|     class TextureView : ITexture, IDisposable
 | |
|     {
 | |
|         private readonly VulkanRenderer _gd;
 | |
| 
 | |
|         private readonly Device _device;
 | |
| 
 | |
|         private readonly Auto<DisposableImageView> _imageView;
 | |
|         private readonly Auto<DisposableImageView> _imageViewIdentity;
 | |
|         private readonly Auto<DisposableImageView> _imageView2dArray;
 | |
|         private Dictionary<GAL.Format, TextureView> _selfManagedViews;
 | |
| 
 | |
|         private TextureCreateInfo _info;
 | |
| 
 | |
|         public TextureCreateInfo Info => _info;
 | |
| 
 | |
|         public TextureStorage Storage { get; }
 | |
| 
 | |
|         public int Width => Info.Width;
 | |
|         public int Height => Info.Height;
 | |
|         public int Layers => Info.GetDepthOrLayers();
 | |
|         public int FirstLayer { get; }
 | |
|         public int FirstLevel { get; }
 | |
|         public float ScaleFactor => Storage.ScaleFactor;
 | |
|         public VkFormat VkFormat { get; }
 | |
|         public bool Valid { get; private set; }
 | |
| 
 | |
|         public TextureView(
 | |
|             VulkanRenderer gd,
 | |
|             Device device,
 | |
|             TextureCreateInfo info,
 | |
|             TextureStorage storage,
 | |
|             int firstLayer,
 | |
|             int firstLevel)
 | |
|         {
 | |
|             _gd = gd;
 | |
|             _device = device;
 | |
|             _info = info;
 | |
|             Storage = storage;
 | |
|             FirstLayer = firstLayer;
 | |
|             FirstLevel = firstLevel;
 | |
| 
 | |
|             storage.IncrementViewsCount();
 | |
| 
 | |
|             gd.Textures.Add(this);
 | |
| 
 | |
|             var format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format);
 | |
|             var levels = (uint)info.Levels;
 | |
|             var layers = (uint)info.GetLayers();
 | |
| 
 | |
|             VkFormat = format;
 | |
| 
 | |
|             var type = info.Target.ConvertView();
 | |
| 
 | |
|             var swizzleR = info.SwizzleR.Convert();
 | |
|             var swizzleG = info.SwizzleG.Convert();
 | |
|             var swizzleB = info.SwizzleB.Convert();
 | |
|             var swizzleA = info.SwizzleA.Convert();
 | |
| 
 | |
|             if (info.Format == GAL.Format.R5G5B5A1Unorm ||
 | |
|                 info.Format == GAL.Format.R5G5B5X1Unorm ||
 | |
|                 info.Format == GAL.Format.R5G6B5Unorm)
 | |
|             {
 | |
|                 var temp = swizzleR;
 | |
| 
 | |
|                 swizzleR = swizzleB;
 | |
|                 swizzleB = temp;
 | |
|             }
 | |
|             else if (VkFormat == VkFormat.R4G4B4A4UnormPack16 || info.Format == GAL.Format.A1B5G5R5Unorm)
 | |
|             {
 | |
|                 var tempB = swizzleB;
 | |
|                 var tempA = swizzleA;
 | |
| 
 | |
|                 swizzleB = swizzleG;
 | |
|                 swizzleA = swizzleR;
 | |
|                 swizzleR = tempA;
 | |
|                 swizzleG = tempB;
 | |
|             }
 | |
| 
 | |
|             var componentMapping = new ComponentMapping(swizzleR, swizzleG, swizzleB, swizzleA);
 | |
| 
 | |
|             var aspectFlags = info.Format.ConvertAspectFlags(info.DepthStencilMode);
 | |
|             var aspectFlagsDepth = info.Format.ConvertAspectFlags(DepthStencilMode.Depth);
 | |
| 
 | |
|             var subresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, levels, (uint)firstLayer, layers);
 | |
|             var subresourceRangeDepth = new ImageSubresourceRange(aspectFlagsDepth, (uint)firstLevel, levels, (uint)firstLayer, layers);
 | |
| 
 | |
|             unsafe Auto<DisposableImageView> CreateImageView(ComponentMapping cm, ImageSubresourceRange sr, ImageViewType viewType)
 | |
|             {
 | |
|                 var imageCreateInfo = new ImageViewCreateInfo()
 | |
|                 {
 | |
|                     SType = StructureType.ImageViewCreateInfo,
 | |
|                     Image = storage.GetImageForViewCreation(),
 | |
|                     ViewType = viewType,
 | |
|                     Format = format,
 | |
|                     Components = cm,
 | |
|                     SubresourceRange = sr
 | |
|                 };
 | |
| 
 | |
|                 gd.Api.CreateImageView(device, imageCreateInfo, null, out var imageView).ThrowOnError();
 | |
|                 return new Auto<DisposableImageView>(new DisposableImageView(gd.Api, device, imageView), null, storage.GetImage());
 | |
|             }
 | |
| 
 | |
|             _imageView = CreateImageView(componentMapping, subresourceRange, type);
 | |
| 
 | |
|             // Framebuffer attachments and storage images requires a identity component mapping.
 | |
|             var identityComponentMapping = new ComponentMapping(
 | |
|                 ComponentSwizzle.R,
 | |
|                 ComponentSwizzle.G,
 | |
|                 ComponentSwizzle.B,
 | |
|                 ComponentSwizzle.A);
 | |
| 
 | |
|             _imageViewIdentity = CreateImageView(identityComponentMapping, subresourceRangeDepth, type);
 | |
| 
 | |
|             // Framebuffer attachments also require 3D textures to be bound as 2D array.
 | |
|             if (info.Target == Target.Texture3D)
 | |
|             {
 | |
|                 subresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, levels, (uint)firstLayer, (uint)info.Depth);
 | |
| 
 | |
|                 _imageView2dArray = CreateImageView(identityComponentMapping, subresourceRange, ImageViewType.Type2DArray);
 | |
|             }
 | |
| 
 | |
|             Valid = true;
 | |
|         }
 | |
| 
 | |
|         public Auto<DisposableImage> GetImage()
 | |
|         {
 | |
|             return Storage.GetImage();
 | |
|         }
 | |
| 
 | |
|         public Auto<DisposableImageView> GetImageView()
 | |
|         {
 | |
|             return _imageView;
 | |
|         }
 | |
| 
 | |
|         public Auto<DisposableImageView> GetIdentityImageView()
 | |
|         {
 | |
|             return _imageViewIdentity;
 | |
|         }
 | |
| 
 | |
|         public Auto<DisposableImageView> GetImageViewForAttachment()
 | |
|         {
 | |
|             return _imageView2dArray ?? _imageViewIdentity;
 | |
|         }
 | |
| 
 | |
|         public void CopyTo(ITexture destination, int firstLayer, int firstLevel)
 | |
|         {
 | |
|             var src = this;
 | |
|             var dst = (TextureView)destination;
 | |
| 
 | |
|             if (!Valid || !dst.Valid)
 | |
|             {
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             _gd.PipelineInternal.EndRenderPass();
 | |
| 
 | |
|             var cbs = _gd.PipelineInternal.CurrentCommandBuffer;
 | |
| 
 | |
|             var srcImage = src.GetImage().Get(cbs).Value;
 | |
|             var dstImage = dst.GetImage().Get(cbs).Value;
 | |
| 
 | |
|             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
 | |
|             {
 | |
|                 TextureCopy.Copy(
 | |
|                     _gd.Api,
 | |
|                     cbs.CommandBuffer,
 | |
|                     srcImage,
 | |
|                     dstImage,
 | |
|                     src.Info,
 | |
|                     dst.Info,
 | |
|                     src.FirstLayer,
 | |
|                     dst.FirstLayer,
 | |
|                     src.FirstLevel,
 | |
|                     dst.FirstLevel,
 | |
|                     0,
 | |
|                     firstLayer,
 | |
|                     0,
 | |
|                     firstLevel);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel)
 | |
|         {
 | |
|             var src = this;
 | |
|             var dst = (TextureView)destination;
 | |
| 
 | |
|             if (!Valid || !dst.Valid)
 | |
|             {
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             _gd.PipelineInternal.EndRenderPass();
 | |
| 
 | |
|             var cbs = _gd.PipelineInternal.CurrentCommandBuffer;
 | |
| 
 | |
|             var srcImage = src.GetImage().Get(cbs).Value;
 | |
|             var dstImage = dst.GetImage().Get(cbs).Value;
 | |
| 
 | |
|             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
 | |
|             {
 | |
|                 TextureCopy.Copy(
 | |
|                     _gd.Api,
 | |
|                     cbs.CommandBuffer,
 | |
|                     srcImage,
 | |
|                     dstImage,
 | |
|                     src.Info,
 | |
|                     dst.Info,
 | |
|                     src.FirstLayer,
 | |
|                     dst.FirstLayer,
 | |
|                     src.FirstLevel,
 | |
|                     dst.FirstLevel,
 | |
|                     srcLayer,
 | |
|                     dstLayer,
 | |
|                     srcLevel,
 | |
|                     dstLevel,
 | |
|                     1,
 | |
|                     1);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter)
 | |
|         {
 | |
|             var dst = (TextureView)destination;
 | |
| 
 | |
|             if (_gd.CommandBufferPool.OwnedByCurrentThread)
 | |
|             {
 | |
|                 _gd.PipelineInternal.EndRenderPass();
 | |
| 
 | |
|                 var cbs = _gd.PipelineInternal.CurrentCommandBuffer;
 | |
| 
 | |
|                 CopyToImpl(cbs, dst, srcRegion, dstRegion, linearFilter);
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 var cbp = _gd.BackgroundResources.Get().GetPool();
 | |
| 
 | |
|                 using var cbs = cbp.Rent();
 | |
| 
 | |
|                 CopyToImpl(cbs, dst, srcRegion, dstRegion, linearFilter);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private void CopyToImpl(CommandBufferScoped cbs, TextureView dst, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter)
 | |
|         {
 | |
|             var src = this;
 | |
| 
 | |
|             var srcFormat = GetCompatibleGalFormat(src.Info.Format);
 | |
|             var dstFormat = GetCompatibleGalFormat(dst.Info.Format);
 | |
| 
 | |
|             bool srcUsesStorageFormat = src.VkFormat == src.Storage.VkFormat;
 | |
|             bool dstUsesStorageFormat = dst.VkFormat == dst.Storage.VkFormat;
 | |
| 
 | |
|             int layers = Math.Min(dst.Info.GetDepthOrLayers(), src.Info.GetDepthOrLayers());
 | |
|             int levels = Math.Min(dst.Info.Levels, src.Info.Levels);
 | |
| 
 | |
|             if (srcUsesStorageFormat && dstUsesStorageFormat)
 | |
|             {
 | |
|                 if ((srcRegion.X1 | dstRegion.X1) == 0 &&
 | |
|                     (srcRegion.Y1 | dstRegion.Y1) == 0 &&
 | |
|                     srcRegion.X2 == src.Width &&
 | |
|                     srcRegion.Y2 == src.Height &&
 | |
|                     dstRegion.X2 == dst.Width &&
 | |
|                     dstRegion.Y2 == dst.Height &&
 | |
|                     src.Width == dst.Width &&
 | |
|                     src.Height == dst.Height &&
 | |
|                     src.VkFormat == dst.VkFormat)
 | |
|                 {
 | |
|                     if (src.Info.Samples > 1 && src.Info.Samples != dst.Info.Samples && src.Info.Format.IsDepthOrStencil())
 | |
|                     {
 | |
|                         // CmdResolveImage does not support depth-stencil resolve, so we need to use an alternative path
 | |
|                         // for those textures.
 | |
|                         TextureCopy.ResolveDepthStencil(_gd, _device, cbs, src, dst);
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|                         TextureCopy.Copy(
 | |
|                             _gd.Api,
 | |
|                             cbs.CommandBuffer,
 | |
|                             src.GetImage().Get(cbs).Value,
 | |
|                             dst.GetImage().Get(cbs).Value,
 | |
|                             src.Info,
 | |
|                             dst.Info,
 | |
|                             src.FirstLayer,
 | |
|                             dst.FirstLayer,
 | |
|                             src.FirstLevel,
 | |
|                             dst.FirstLevel,
 | |
|                             0,
 | |
|                             0,
 | |
|                             0,
 | |
|                             0,
 | |
|                             layers,
 | |
|                             levels);
 | |
|                     }
 | |
| 
 | |
|                     return;
 | |
|                 }
 | |
|                 else if (_gd.FormatCapabilities.OptimalFormatSupports(FormatFeatureFlags.BlitSrcBit, srcFormat) &&
 | |
|                          _gd.FormatCapabilities.OptimalFormatSupports(FormatFeatureFlags.BlitDstBit, dstFormat))
 | |
|                 {
 | |
|                     TextureCopy.Blit(
 | |
|                         _gd.Api,
 | |
|                         cbs.CommandBuffer,
 | |
|                         src.GetImage().Get(cbs).Value,
 | |
|                         dst.GetImage().Get(cbs).Value,
 | |
|                         src.Info,
 | |
|                         dst.Info,
 | |
|                         srcRegion,
 | |
|                         dstRegion,
 | |
|                         src.FirstLayer,
 | |
|                         dst.FirstLayer,
 | |
|                         src.FirstLevel,
 | |
|                         dst.FirstLevel,
 | |
|                         layers,
 | |
|                         levels,
 | |
|                         linearFilter);
 | |
| 
 | |
|                     return;
 | |
|                 }
 | |
|                 else if (srcFormat == GAL.Format.D32FloatS8Uint && srcFormat == dstFormat && SupportsBlitFromD32FS8ToD32FAndS8())
 | |
|                 {
 | |
|                     BlitDepthStencilWithBuffer(_gd, cbs, src, dst, srcRegion, dstRegion);
 | |
| 
 | |
|                     return;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             if (VulkanConfiguration.UseSlowSafeBlitOnAmd &&
 | |
|                 _gd.Vendor == Vendor.Amd &&
 | |
|                 src.Info.Target == Target.Texture2D &&
 | |
|                 dst.Info.Target == Target.Texture2D &&
 | |
|                 !dst.Info.Format.IsDepthOrStencil())
 | |
|             {
 | |
|                 _gd.HelperShader.Blit(
 | |
|                     _gd,
 | |
|                     src,
 | |
|                     dst.GetIdentityImageView(),
 | |
|                     dst.Width,
 | |
|                     dst.Height,
 | |
|                     dst.VkFormat,
 | |
|                     srcRegion,
 | |
|                     dstRegion,
 | |
|                     linearFilter);
 | |
| 
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             Auto<DisposableImage> srcImage;
 | |
|             Auto<DisposableImage> dstImage;
 | |
| 
 | |
|             if (dst.Info.Format.IsDepthOrStencil())
 | |
|             {
 | |
|                 srcImage = src.Storage.CreateAliasedColorForDepthStorageUnsafe(srcFormat).GetImage();
 | |
|                 dstImage = dst.Storage.CreateAliasedColorForDepthStorageUnsafe(dstFormat).GetImage();
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 srcImage = src.Storage.CreateAliasedStorageUnsafe(srcFormat).GetImage();
 | |
|                 dstImage = dst.Storage.CreateAliasedStorageUnsafe(dstFormat).GetImage();
 | |
|             }
 | |
| 
 | |
|             TextureCopy.Blit(
 | |
|                 _gd.Api,
 | |
|                 cbs.CommandBuffer,
 | |
|                 srcImage.Get(cbs).Value,
 | |
|                 dstImage.Get(cbs).Value,
 | |
|                 src.Info,
 | |
|                 dst.Info,
 | |
|                 srcRegion,
 | |
|                 dstRegion,
 | |
|                 src.FirstLayer,
 | |
|                 dst.FirstLayer,
 | |
|                 src.FirstLevel,
 | |
|                 dst.FirstLevel,
 | |
|                 layers,
 | |
|                 levels,
 | |
|                 linearFilter,
 | |
|                 ImageAspectFlags.ColorBit,
 | |
|                 ImageAspectFlags.ColorBit);
 | |
|         }
 | |
| 
 | |
|         private static void BlitDepthStencilWithBuffer(
 | |
|             VulkanRenderer gd,
 | |
|             CommandBufferScoped cbs,
 | |
|             TextureView src,
 | |
|             TextureView dst,
 | |
|             Extents2D srcRegion,
 | |
|             Extents2D dstRegion)
 | |
|         {
 | |
|             int drBaseX = Math.Min(dstRegion.X1, dstRegion.X2);
 | |
|             int drBaseY = Math.Min(dstRegion.Y1, dstRegion.Y2);
 | |
|             int drWidth = Math.Abs(dstRegion.X2 - dstRegion.X1);
 | |
|             int drHeight = Math.Abs(dstRegion.Y2 - dstRegion.Y1);
 | |
| 
 | |
|             var drOriginZero = new Extents2D(
 | |
|                 dstRegion.X1 - drBaseX,
 | |
|                 dstRegion.Y1 - drBaseY,
 | |
|                 dstRegion.X2 - drBaseX,
 | |
|                 dstRegion.Y2 - drBaseY);
 | |
| 
 | |
|             var d32SrcStorageInfo = TextureStorage.NewCreateInfoWith(ref src._info, GAL.Format.D32Float, 4);
 | |
|             var d32DstStorageInfo = TextureStorage.NewCreateInfoWith(ref dst._info, GAL.Format.D32Float, 4, drWidth, drHeight);
 | |
|             var s8SrcStorageInfo = TextureStorage.NewCreateInfoWith(ref src._info, GAL.Format.S8Uint, 1);
 | |
|             var s8DstStorageInfo = TextureStorage.NewCreateInfoWith(ref dst._info, GAL.Format.S8Uint, 1, drWidth, drHeight);
 | |
| 
 | |
|             using var d32SrcStorage = gd.CreateTextureStorage(d32SrcStorageInfo, src.Storage.ScaleFactor);
 | |
|             using var d32DstStorage = gd.CreateTextureStorage(d32DstStorageInfo, dst.Storage.ScaleFactor);
 | |
|             using var s8SrcStorage = gd.CreateTextureStorage(s8SrcStorageInfo, src.Storage.ScaleFactor);
 | |
|             using var s8DstStorage = gd.CreateTextureStorage(s8DstStorageInfo, dst.Storage.ScaleFactor);
 | |
| 
 | |
|             void SlowBlit(TextureStorage srcTemp, TextureStorage dstTemp, ImageAspectFlags aspectFlags)
 | |
|             {
 | |
|                 int levels = Math.Min(src.Info.Levels, dst.Info.Levels);
 | |
| 
 | |
|                 int srcSize = 0;
 | |
|                 int dstSize = 0;
 | |
| 
 | |
|                 for (int l = 0; l < levels; l++)
 | |
|                 {
 | |
|                     srcSize += srcTemp.Info.GetMipSize2D(l);
 | |
|                     dstSize += dstTemp.Info.GetMipSize2D(l);
 | |
|                 }
 | |
| 
 | |
|                 using var srcTempBuffer = gd.BufferManager.Create(gd, srcSize, deviceLocal: true);
 | |
|                 using var dstTempBuffer = gd.BufferManager.Create(gd, dstSize, deviceLocal: true);
 | |
| 
 | |
|                 src.Storage.CopyFromOrToBuffer(
 | |
|                     cbs.CommandBuffer,
 | |
|                     srcTempBuffer.GetBuffer().Get(cbs, 0, srcSize).Value,
 | |
|                     src.GetImage().Get(cbs).Value,
 | |
|                     srcSize,
 | |
|                     to: true,
 | |
|                     0,
 | |
|                     0,
 | |
|                     src.FirstLayer,
 | |
|                     src.FirstLevel,
 | |
|                     1,
 | |
|                     levels,
 | |
|                     true,
 | |
|                     aspectFlags,
 | |
|                     false);
 | |
| 
 | |
|                 BufferHolder.InsertBufferBarrier(
 | |
|                     gd,
 | |
|                     cbs.CommandBuffer,
 | |
|                     srcTempBuffer.GetBuffer().Get(cbs, 0, srcSize).Value,
 | |
|                     AccessFlags.TransferWriteBit,
 | |
|                     AccessFlags.TransferReadBit,
 | |
|                     PipelineStageFlags.TransferBit,
 | |
|                     PipelineStageFlags.TransferBit,
 | |
|                     0,
 | |
|                     srcSize);
 | |
| 
 | |
|                 srcTemp.CopyFromOrToBuffer(
 | |
|                     cbs.CommandBuffer,
 | |
|                     srcTempBuffer.GetBuffer().Get(cbs, 0, srcSize).Value,
 | |
|                     srcTemp.GetImage().Get(cbs).Value,
 | |
|                     srcSize,
 | |
|                     to: false,
 | |
|                     0,
 | |
|                     0,
 | |
|                     0,
 | |
|                     0,
 | |
|                     1,
 | |
|                     levels,
 | |
|                     true,
 | |
|                     aspectFlags,
 | |
|                     false);
 | |
| 
 | |
|                 InsertImageBarrier(
 | |
|                     gd.Api,
 | |
|                     cbs.CommandBuffer,
 | |
|                     srcTemp.GetImage().Get(cbs).Value,
 | |
|                     AccessFlags.TransferWriteBit,
 | |
|                     AccessFlags.TransferReadBit,
 | |
|                     PipelineStageFlags.TransferBit,
 | |
|                     PipelineStageFlags.TransferBit,
 | |
|                     aspectFlags,
 | |
|                     0,
 | |
|                     0,
 | |
|                     1,
 | |
|                     levels);
 | |
| 
 | |
|                 TextureCopy.Blit(
 | |
|                     gd.Api,
 | |
|                     cbs.CommandBuffer,
 | |
|                     srcTemp.GetImage().Get(cbs).Value,
 | |
|                     dstTemp.GetImage().Get(cbs).Value,
 | |
|                     srcTemp.Info,
 | |
|                     dstTemp.Info,
 | |
|                     srcRegion,
 | |
|                     drOriginZero,
 | |
|                     0,
 | |
|                     0,
 | |
|                     0,
 | |
|                     0,
 | |
|                     1,
 | |
|                     levels,
 | |
|                     false,
 | |
|                     aspectFlags,
 | |
|                     aspectFlags);
 | |
| 
 | |
|                 InsertImageBarrier(
 | |
|                     gd.Api,
 | |
|                     cbs.CommandBuffer,
 | |
|                     dstTemp.GetImage().Get(cbs).Value,
 | |
|                     AccessFlags.TransferWriteBit,
 | |
|                     AccessFlags.TransferReadBit,
 | |
|                     PipelineStageFlags.TransferBit,
 | |
|                     PipelineStageFlags.TransferBit,
 | |
|                     aspectFlags,
 | |
|                     0,
 | |
|                     0,
 | |
|                     1,
 | |
|                     levels);
 | |
| 
 | |
|                 dstTemp.CopyFromOrToBuffer(
 | |
|                     cbs.CommandBuffer,
 | |
|                     dstTempBuffer.GetBuffer().Get(cbs, 0, dstSize).Value,
 | |
|                     dstTemp.GetImage().Get(cbs).Value,
 | |
|                     dstSize,
 | |
|                     to: true,
 | |
|                     0,
 | |
|                     0,
 | |
|                     0,
 | |
|                     0,
 | |
|                     1,
 | |
|                     levels,
 | |
|                     true,
 | |
|                     aspectFlags,
 | |
|                     false);
 | |
| 
 | |
|                 BufferHolder.InsertBufferBarrier(
 | |
|                     gd,
 | |
|                     cbs.CommandBuffer,
 | |
|                     dstTempBuffer.GetBuffer().Get(cbs, 0, dstSize).Value,
 | |
|                     AccessFlags.TransferWriteBit,
 | |
|                     AccessFlags.TransferReadBit,
 | |
|                     PipelineStageFlags.TransferBit,
 | |
|                     PipelineStageFlags.TransferBit,
 | |
|                     0,
 | |
|                     dstSize);
 | |
| 
 | |
|                 dst.Storage.CopyFromOrToBuffer(
 | |
|                     cbs.CommandBuffer,
 | |
|                     dstTempBuffer.GetBuffer().Get(cbs, 0, dstSize).Value,
 | |
|                     dst.GetImage().Get(cbs).Value,
 | |
|                     dstSize,
 | |
|                     to: false,
 | |
|                     drBaseX,
 | |
|                     drBaseY,
 | |
|                     dst.FirstLayer,
 | |
|                     dst.FirstLevel,
 | |
|                     1,
 | |
|                     levels,
 | |
|                     true,
 | |
|                     aspectFlags,
 | |
|                     false);
 | |
|             }
 | |
| 
 | |
|             SlowBlit(d32SrcStorage, d32DstStorage, ImageAspectFlags.DepthBit);
 | |
|             SlowBlit(s8SrcStorage, s8DstStorage, ImageAspectFlags.StencilBit);
 | |
|         }
 | |
| 
 | |
|         public static unsafe void InsertImageBarrier(
 | |
|             Vk api,
 | |
|             CommandBuffer commandBuffer,
 | |
|             Image image,
 | |
|             AccessFlags srcAccessMask,
 | |
|             AccessFlags dstAccessMask,
 | |
|             PipelineStageFlags srcStageMask,
 | |
|             PipelineStageFlags dstStageMask,
 | |
|             ImageAspectFlags aspectFlags,
 | |
|             int firstLayer,
 | |
|             int firstLevel,
 | |
|             int layers,
 | |
|             int levels)
 | |
|         {
 | |
|             ImageMemoryBarrier memoryBarrier = new ImageMemoryBarrier()
 | |
|             {
 | |
|                 SType = StructureType.ImageMemoryBarrier,
 | |
|                 SrcAccessMask = srcAccessMask,
 | |
|                 DstAccessMask = dstAccessMask,
 | |
|                 SrcQueueFamilyIndex = Vk.QueueFamilyIgnored,
 | |
|                 DstQueueFamilyIndex = Vk.QueueFamilyIgnored,
 | |
|                 Image = image,
 | |
|                 OldLayout = ImageLayout.General,
 | |
|                 NewLayout = ImageLayout.General,
 | |
|                 SubresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, (uint)levels, (uint)firstLayer, (uint)layers)
 | |
|             };
 | |
| 
 | |
|             api.CmdPipelineBarrier(
 | |
|                 commandBuffer,
 | |
|                 srcStageMask,
 | |
|                 dstStageMask,
 | |
|                 0,
 | |
|                 0,
 | |
|                 null,
 | |
|                 0,
 | |
|                 null,
 | |
|                 1,
 | |
|                 memoryBarrier);
 | |
|         }
 | |
| 
 | |
|         private bool SupportsBlitFromD32FS8ToD32FAndS8()
 | |
|         {
 | |
|             var formatFeatureFlags = FormatFeatureFlags.BlitSrcBit | FormatFeatureFlags.BlitDstBit;
 | |
|             return _gd.FormatCapabilities.OptimalFormatSupports(formatFeatureFlags, GAL.Format.D32Float)  &&
 | |
|                    _gd.FormatCapabilities.OptimalFormatSupports(formatFeatureFlags, GAL.Format.S8Uint);
 | |
|         }
 | |
| 
 | |
|         public TextureView GetView(GAL.Format format)
 | |
|         {
 | |
|             if (format == Info.Format)
 | |
|             {
 | |
|                 return this;
 | |
|             }
 | |
| 
 | |
|             if (_selfManagedViews != null && _selfManagedViews.TryGetValue(format, out var view))
 | |
|             {
 | |
|                 return view;
 | |
|             }
 | |
| 
 | |
|             view = CreateViewImpl(new TextureCreateInfo(
 | |
|                 Info.Width,
 | |
|                 Info.Height,
 | |
|                 Info.Depth,
 | |
|                 Info.Levels,
 | |
|                 Info.Samples,
 | |
|                 Info.BlockWidth,
 | |
|                 Info.BlockHeight,
 | |
|                 Info.BytesPerPixel,
 | |
|                 format,
 | |
|                 Info.DepthStencilMode,
 | |
|                 Info.Target,
 | |
|                 Info.SwizzleR,
 | |
|                 Info.SwizzleG,
 | |
|                 Info.SwizzleB,
 | |
|                 Info.SwizzleA), 0, 0);
 | |
| 
 | |
|             (_selfManagedViews ??= new Dictionary<GAL.Format, TextureView>()).Add(format, view);
 | |
| 
 | |
|             return view;
 | |
|         }
 | |
| 
 | |
|         public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel)
 | |
|         {
 | |
|             return CreateViewImpl(info, firstLayer, firstLevel);
 | |
|         }
 | |
| 
 | |
|         private TextureView CreateViewImpl(TextureCreateInfo info, int firstLayer, int firstLevel)
 | |
|         {
 | |
|             return new TextureView(_gd, _device, info, Storage, FirstLayer + firstLayer, FirstLevel + firstLevel);
 | |
|         }
 | |
| 
 | |
|         public byte[] GetData(int x, int y, int width, int height)
 | |
|         {
 | |
|             int size = width * height * Info.BytesPerPixel;
 | |
|             using var bufferHolder = _gd.BufferManager.Create(_gd, size);
 | |
| 
 | |
|             using (var cbs = _gd.CommandBufferPool.Rent())
 | |
|             {
 | |
|                 var buffer = bufferHolder.GetBuffer(cbs.CommandBuffer).Get(cbs).Value;
 | |
|                 var image = GetImage().Get(cbs).Value;
 | |
| 
 | |
|                 CopyFromOrToBuffer(cbs.CommandBuffer, buffer, image, size, true, 0, 0, x, y, width, height);
 | |
|             }
 | |
| 
 | |
|             bufferHolder.WaitForFences();
 | |
|             byte[] bitmap = new byte[size];
 | |
|             GetDataFromBuffer(bufferHolder.GetDataStorage(0, size), size, Span<byte>.Empty).CopyTo(bitmap);
 | |
|             return bitmap;
 | |
|         }
 | |
| 
 | |
|         public ReadOnlySpan<byte> GetData()
 | |
|         {
 | |
|             BackgroundResource resources = _gd.BackgroundResources.Get();
 | |
| 
 | |
|             if (_gd.CommandBufferPool.OwnedByCurrentThread)
 | |
|             {
 | |
|                 _gd.FlushAllCommands();
 | |
| 
 | |
|                 return GetData(_gd.CommandBufferPool, resources.GetFlushBuffer());
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 return GetData(resources.GetPool(), resources.GetFlushBuffer());
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public ReadOnlySpan<byte> GetData(int layer, int level)
 | |
|         {
 | |
|             BackgroundResource resources = _gd.BackgroundResources.Get();
 | |
| 
 | |
|             if (_gd.CommandBufferPool.OwnedByCurrentThread)
 | |
|             {
 | |
|                 _gd.FlushAllCommands();
 | |
| 
 | |
|                 return GetData(_gd.CommandBufferPool, resources.GetFlushBuffer(), layer, level);
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 return GetData(resources.GetPool(), resources.GetFlushBuffer(), layer, level);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         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 void SetData(SpanOrArray<byte> data)
 | |
|         {
 | |
|             SetData(data, 0, 0, Info.GetLayers(), Info.Levels, singleSlice: false);
 | |
|         }
 | |
| 
 | |
|         public void SetData(SpanOrArray<byte> data, int layer, int level)
 | |
|         {
 | |
|             SetData(data, layer, level, 1, 1, singleSlice: true);
 | |
|         }
 | |
| 
 | |
|         public void SetData(SpanOrArray<byte> data, int layer, int level, Rectangle<int> region)
 | |
|         {
 | |
|             SetData(data, layer, level, 1, 1, singleSlice: true, region);
 | |
|         }
 | |
| 
 | |
|         private void SetData(ReadOnlySpan<byte> data, int layer, int level, int layers, int levels, bool singleSlice, Rectangle<int>? region = null)
 | |
|         {
 | |
|             int bufferDataLength = GetBufferDataLength(data.Length);
 | |
| 
 | |
|             using var bufferHolder = _gd.BufferManager.Create(_gd, bufferDataLength);
 | |
| 
 | |
|             Auto<DisposableImage> imageAuto = GetImage();
 | |
| 
 | |
|             // Load texture data inline if the texture has been used on the current command buffer.
 | |
| 
 | |
|             bool loadInline = Storage.HasCommandBufferDependency(_gd.PipelineInternal.CurrentCommandBuffer);
 | |
| 
 | |
|             var cbs = loadInline ? _gd.PipelineInternal.CurrentCommandBuffer : _gd.PipelineInternal.GetPreloadCommandBuffer();
 | |
| 
 | |
|             if (loadInline)
 | |
|             {
 | |
|                 _gd.PipelineInternal.EndRenderPass();
 | |
|             }
 | |
| 
 | |
|             CopyDataToBuffer(bufferHolder.GetDataStorage(0, bufferDataLength), data);
 | |
| 
 | |
|             var buffer = bufferHolder.GetBuffer(cbs.CommandBuffer).Get(cbs).Value;
 | |
|             var image = imageAuto.Get(cbs).Value;
 | |
| 
 | |
|             if (region.HasValue)
 | |
|             {
 | |
|                 CopyFromOrToBuffer(
 | |
|                     cbs.CommandBuffer,
 | |
|                     buffer,
 | |
|                     image,
 | |
|                     bufferDataLength,
 | |
|                     false,
 | |
|                     layer,
 | |
|                     level,
 | |
|                     region.Value.X,
 | |
|                     region.Value.Y,
 | |
|                     region.Value.Width,
 | |
|                     region.Value.Height);
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 CopyFromOrToBuffer(cbs.CommandBuffer, buffer, image, bufferDataLength, false, layer, level, layers, levels, singleSlice);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private int GetBufferDataLength(int length)
 | |
|         {
 | |
|             if (NeedsD24S8Conversion())
 | |
|             {
 | |
|                 return length * 2;
 | |
|             }
 | |
| 
 | |
|             return length;
 | |
|         }
 | |
| 
 | |
|         private GAL.Format GetCompatibleGalFormat(GAL.Format format)
 | |
|         {
 | |
|             if (NeedsD24S8Conversion())
 | |
|             {
 | |
|                 return GAL.Format.D32FloatS8Uint;
 | |
|             }
 | |
| 
 | |
|             return format;
 | |
|         }
 | |
| 
 | |
|         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 NeedsD24S8Conversion()
 | |
|         {
 | |
|             return FormatCapabilities.IsD24S8(Info.Format) && VkFormat == VkFormat.D32SfloatS8Uint;
 | |
|         }
 | |
| 
 | |
|         public void CopyFromOrToBuffer(
 | |
|             CommandBuffer commandBuffer,
 | |
|             VkBuffer buffer,
 | |
|             Image image,
 | |
|             int size,
 | |
|             bool to,
 | |
|             int dstLayer,
 | |
|             int dstLevel,
 | |
|             int dstLayers,
 | |
|             int dstLevels,
 | |
|             bool singleSlice)
 | |
|         {
 | |
|             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 layer = is3D ? 0 : dstLayer;
 | |
|             int layers = dstLayers;
 | |
|             int levels = dstLevels;
 | |
| 
 | |
|             int offset = 0;
 | |
| 
 | |
|             for (int level = 0; level < levels; level++)
 | |
|             {
 | |
|                 int mipSize = GetBufferDataLength(Info.GetMipSize(dstLevel + level));
 | |
| 
 | |
|                 int endOffset = offset + mipSize;
 | |
| 
 | |
|                 if ((uint)endOffset > (uint)size)
 | |
|                 {
 | |
|                     break;
 | |
|                 }
 | |
| 
 | |
|                 int rowLength = (Info.GetMipStride(dstLevel + level) / Info.BytesPerPixel) * Info.BlockWidth;
 | |
| 
 | |
|                 var aspectFlags = Info.Format.ConvertAspectFlags();
 | |
| 
 | |
|                 if (aspectFlags == (ImageAspectFlags.DepthBit | ImageAspectFlags.StencilBit))
 | |
|                 {
 | |
|                     aspectFlags = ImageAspectFlags.DepthBit;
 | |
|                 }
 | |
| 
 | |
|                 var sl = new ImageSubresourceLayers(
 | |
|                     aspectFlags,
 | |
|                     (uint)(FirstLevel + dstLevel + level),
 | |
|                     (uint)(FirstLayer + 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)AlignUpNpot(rowLength, Info.BlockWidth),
 | |
|                     (uint)AlignUpNpot(height, Info.BlockHeight),
 | |
|                     sl,
 | |
|                     new Offset3D(0, 0, 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 void CopyFromOrToBuffer(
 | |
|             CommandBuffer commandBuffer,
 | |
|             VkBuffer buffer,
 | |
|             Image image,
 | |
|             int size,
 | |
|             bool to,
 | |
|             int dstLayer,
 | |
|             int dstLevel,
 | |
|             int x,
 | |
|             int y,
 | |
|             int width,
 | |
|             int height)
 | |
|         {
 | |
|             var aspectFlags = Info.Format.ConvertAspectFlags();
 | |
| 
 | |
|             if (aspectFlags == (ImageAspectFlags.DepthBit | ImageAspectFlags.StencilBit))
 | |
|             {
 | |
|                 aspectFlags = ImageAspectFlags.DepthBit;
 | |
|             }
 | |
| 
 | |
|             var sl = new ImageSubresourceLayers(aspectFlags, (uint)(FirstLevel + dstLevel), (uint)(FirstLayer + dstLayer), 1);
 | |
| 
 | |
|             var extent = new Extent3D((uint)width, (uint)height, 1);
 | |
| 
 | |
|             int rowLengthAlignment = Info.BlockWidth;
 | |
| 
 | |
|             // We expect all data being written into the texture to have a stride aligned by 4.
 | |
|             if (!to && Info.BytesPerPixel < 4)
 | |
|             {
 | |
|                 rowLengthAlignment = 4 / Info.BytesPerPixel;
 | |
|             }
 | |
| 
 | |
|             var region = new BufferImageCopy(
 | |
|                 0,
 | |
|                 (uint)AlignUpNpot(width, rowLengthAlignment),
 | |
|                 (uint)AlignUpNpot(height, Info.BlockHeight),
 | |
|                 sl,
 | |
|                 new Offset3D(x, y, 0),
 | |
|                 extent);
 | |
| 
 | |
|             if (to)
 | |
|             {
 | |
|                 _gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, region);
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 _gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, region);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private static int AlignUpNpot(int size, int alignment)
 | |
|         {
 | |
|             int remainder = size % alignment;
 | |
|             if (remainder == 0)
 | |
|             {
 | |
|                 return size;
 | |
|             }
 | |
| 
 | |
|             return size + (alignment - remainder);
 | |
|         }
 | |
| 
 | |
|         public void SetStorage(BufferRange buffer)
 | |
|         {
 | |
|             throw new NotImplementedException();
 | |
|         }
 | |
| 
 | |
|         protected virtual void Dispose(bool disposing)
 | |
|         {
 | |
|             if (disposing)
 | |
|             {
 | |
|                 Valid = false;
 | |
| 
 | |
|                 if (_gd.Textures.Remove(this))
 | |
|                 {
 | |
|                     _imageView.Dispose();
 | |
|                     _imageViewIdentity.Dispose();
 | |
|                     _imageView2dArray?.Dispose();
 | |
| 
 | |
|                     Storage.DecrementViewsCount();
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public void Dispose()
 | |
|         {
 | |
|             if (_selfManagedViews != null)
 | |
|             {
 | |
|                 foreach (var view in _selfManagedViews.Values)
 | |
|                 {
 | |
|                     view.Dispose();
 | |
|                 }
 | |
| 
 | |
|                 _selfManagedViews = null;
 | |
|             }
 | |
| 
 | |
|             Dispose(true);
 | |
|         }
 | |
| 
 | |
|         public void Release()
 | |
|         {
 | |
|             Dispose();
 | |
|         }
 | |
|     }
 | |
| }
 | 
