/ src / Ryujinx.Graphics.Vulkan / TextureView.cs
TextureView.cs
   1  using Ryujinx.Common.Memory;
   2  using Ryujinx.Graphics.GAL;
   3  using Silk.NET.Vulkan;
   4  using System;
   5  using System.Collections.Generic;
   6  using System.Linq;
   7  using System.Threading;
   8  using Format = Ryujinx.Graphics.GAL.Format;
   9  using VkBuffer = Silk.NET.Vulkan.Buffer;
  10  using VkFormat = Silk.NET.Vulkan.Format;
  11  
  12  namespace Ryujinx.Graphics.Vulkan
  13  {
  14      class TextureView : ITexture, IDisposable
  15      {
  16          private readonly VulkanRenderer _gd;
  17  
  18          private readonly Device _device;
  19  
  20          private readonly Auto<DisposableImageView> _imageView;
  21          private readonly Auto<DisposableImageView> _imageViewDraw;
  22          private readonly Auto<DisposableImageView> _imageViewIdentity;
  23          private readonly Auto<DisposableImageView> _imageView2dArray;
  24          private Dictionary<Format, TextureView> _selfManagedViews;
  25  
  26          private int _hazardUses;
  27  
  28          private readonly TextureCreateInfo _info;
  29  
  30          private HashTableSlim<RenderPassCacheKey, RenderPassHolder> _renderPasses;
  31  
  32          public TextureCreateInfo Info => _info;
  33  
  34          public TextureStorage Storage { get; }
  35  
  36          public int Width => Info.Width;
  37          public int Height => Info.Height;
  38          public int Layers => Info.GetDepthOrLayers();
  39          public int FirstLayer { get; }
  40          public int FirstLevel { get; }
  41          public VkFormat VkFormat { get; }
  42          private int _isValid;
  43          public bool Valid => Volatile.Read(ref _isValid) != 0;
  44  
  45          public TextureView(
  46              VulkanRenderer gd,
  47              Device device,
  48              TextureCreateInfo info,
  49              TextureStorage storage,
  50              int firstLayer,
  51              int firstLevel)
  52          {
  53              _gd = gd;
  54              _device = device;
  55              _info = info;
  56              Storage = storage;
  57              FirstLayer = firstLayer;
  58              FirstLevel = firstLevel;
  59  
  60              storage.IncrementViewsCount();
  61  
  62              gd.Textures.Add(this);
  63  
  64              var format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format);
  65              var usage = TextureStorage.GetImageUsage(info.Format, info.Target, gd.Capabilities);
  66              var levels = (uint)info.Levels;
  67              var layers = (uint)info.GetLayers();
  68  
  69              VkFormat = format;
  70  
  71              var type = info.Target.ConvertView();
  72  
  73              var swizzleR = info.SwizzleR.Convert();
  74              var swizzleG = info.SwizzleG.Convert();
  75              var swizzleB = info.SwizzleB.Convert();
  76              var swizzleA = info.SwizzleA.Convert();
  77  
  78              if (info.Format == Format.R5G5B5A1Unorm ||
  79                  info.Format == Format.R5G5B5X1Unorm ||
  80                  info.Format == Format.R5G6B5Unorm)
  81              {
  82                  (swizzleB, swizzleR) = (swizzleR, swizzleB);
  83              }
  84              else if (VkFormat == VkFormat.R4G4B4A4UnormPack16 || info.Format == Format.A1B5G5R5Unorm)
  85              {
  86                  var tempB = swizzleB;
  87                  var tempA = swizzleA;
  88  
  89                  swizzleB = swizzleG;
  90                  swizzleA = swizzleR;
  91                  swizzleR = tempA;
  92                  swizzleG = tempB;
  93              }
  94  
  95              var componentMapping = new ComponentMapping(swizzleR, swizzleG, swizzleB, swizzleA);
  96  
  97              var aspectFlags = info.Format.ConvertAspectFlags(info.DepthStencilMode);
  98              var aspectFlagsDepth = info.Format.ConvertAspectFlags();
  99  
 100              var subresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, levels, (uint)firstLayer, layers);
 101              var subresourceRangeDepth = new ImageSubresourceRange(aspectFlagsDepth, (uint)firstLevel, levels, (uint)firstLayer, layers);
 102  
 103              unsafe Auto<DisposableImageView> CreateImageView(ComponentMapping cm, ImageSubresourceRange sr, ImageViewType viewType, ImageUsageFlags usageFlags)
 104              {
 105                  var imageViewUsage = new ImageViewUsageCreateInfo
 106                  {
 107                      SType = StructureType.ImageViewUsageCreateInfo,
 108                      Usage = usageFlags,
 109                  };
 110  
 111                  var imageCreateInfo = new ImageViewCreateInfo
 112                  {
 113                      SType = StructureType.ImageViewCreateInfo,
 114                      Image = storage.GetImageForViewCreation(),
 115                      ViewType = viewType,
 116                      Format = format,
 117                      Components = cm,
 118                      SubresourceRange = sr,
 119                      PNext = &imageViewUsage,
 120                  };
 121  
 122                  gd.Api.CreateImageView(device, in imageCreateInfo, null, out var imageView).ThrowOnError();
 123                  return new Auto<DisposableImageView>(new DisposableImageView(gd.Api, device, imageView), null, storage.GetImage());
 124              }
 125  
 126              ImageUsageFlags shaderUsage = ImageUsageFlags.SampledBit;
 127  
 128              if (info.Format.IsImageCompatible() && (_gd.Capabilities.SupportsShaderStorageImageMultisample || !info.Target.IsMultisample()))
 129              {
 130                  shaderUsage |= ImageUsageFlags.StorageBit;
 131              }
 132  
 133              _imageView = CreateImageView(componentMapping, subresourceRange, type, shaderUsage);
 134  
 135              // Framebuffer attachments and storage images requires a identity component mapping.
 136              var identityComponentMapping = new ComponentMapping(
 137                  ComponentSwizzle.R,
 138                  ComponentSwizzle.G,
 139                  ComponentSwizzle.B,
 140                  ComponentSwizzle.A);
 141  
 142              _imageViewDraw = CreateImageView(identityComponentMapping, subresourceRangeDepth, type, usage);
 143              _imageViewIdentity = aspectFlagsDepth == aspectFlags ? _imageViewDraw : CreateImageView(identityComponentMapping, subresourceRange, type, usage);
 144  
 145              // Framebuffer attachments also require 3D textures to be bound as 2D array.
 146              if (info.Target == Target.Texture3D)
 147              {
 148                  if (gd.Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.No3DImageView))
 149                  {
 150                      if (levels == 1 && (info.Format.IsRtColorCompatible() || info.Format.IsDepthOrStencil()))
 151                      {
 152                          subresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, levels, (uint)firstLayer, 1);
 153  
 154                          _imageView2dArray = CreateImageView(identityComponentMapping, subresourceRange, ImageViewType.Type2D, ImageUsageFlags.ColorAttachmentBit);
 155                      }
 156                  }
 157                  else
 158                  {
 159                      subresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, 1, (uint)firstLayer, (uint)info.Depth);
 160  
 161                      _imageView2dArray = CreateImageView(identityComponentMapping, subresourceRange, ImageViewType.Type2DArray, usage);
 162                  }
 163              }
 164  
 165              _isValid = 1;
 166          }
 167  
 168          /// <summary>
 169          /// Create a texture view for an existing swapchain image view.
 170          /// Does not set storage, so only appropriate for swapchain use.
 171          /// </summary>
 172          /// <remarks>Do not use this for normal textures, and make sure uses do not try to read storage.</remarks>
 173          public TextureView(VulkanRenderer gd, Device device, DisposableImageView view, TextureCreateInfo info, VkFormat format)
 174          {
 175              _gd = gd;
 176              _device = device;
 177  
 178              _imageView = new Auto<DisposableImageView>(view);
 179              _imageViewDraw = _imageView;
 180              _imageViewIdentity = _imageView;
 181              _info = info;
 182  
 183              VkFormat = format;
 184  
 185              _isValid = 1;
 186          }
 187  
 188          public Auto<DisposableImage> GetImage()
 189          {
 190              return Storage.GetImage();
 191          }
 192  
 193          public Auto<DisposableImageView> GetImageView()
 194          {
 195              return _imageView;
 196          }
 197  
 198          public Auto<DisposableImageView> GetIdentityImageView()
 199          {
 200              return _imageViewIdentity;
 201          }
 202  
 203          public Auto<DisposableImageView> GetImageViewForAttachment()
 204          {
 205              return _imageView2dArray ?? _imageViewDraw;
 206          }
 207  
 208          public void CopyTo(ITexture destination, int firstLayer, int firstLevel)
 209          {
 210              var src = this;
 211              var dst = (TextureView)destination;
 212  
 213              if (!Valid || !dst.Valid)
 214              {
 215                  return;
 216              }
 217  
 218              _gd.PipelineInternal.EndRenderPass();
 219  
 220              var cbs = _gd.PipelineInternal.CurrentCommandBuffer;
 221  
 222              var srcImage = src.GetImage().Get(cbs).Value;
 223              var dstImage = dst.GetImage().Get(cbs).Value;
 224  
 225              if (!dst.Info.Target.IsMultisample() && Info.Target.IsMultisample())
 226              {
 227                  int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer);
 228                  _gd.HelperShader.CopyMSToNonMS(_gd, cbs, src, dst, 0, firstLayer, layers);
 229              }
 230              else if (dst.Info.Target.IsMultisample() && !Info.Target.IsMultisample())
 231              {
 232                  int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer);
 233                  _gd.HelperShader.CopyNonMSToMS(_gd, cbs, src, dst, 0, firstLayer, layers);
 234              }
 235              else if (dst.Info.BytesPerPixel != Info.BytesPerPixel)
 236              {
 237                  int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer);
 238                  int levels = Math.Min(Info.Levels, dst.Info.Levels - firstLevel);
 239                  _gd.HelperShader.CopyIncompatibleFormats(_gd, cbs, src, dst, 0, firstLayer, 0, firstLevel, layers, levels);
 240              }
 241              else if (src.Info.Format.IsDepthOrStencil() != dst.Info.Format.IsDepthOrStencil())
 242              {
 243                  int layers = Math.Min(Info.GetLayers(), dst.Info.GetLayers() - firstLayer);
 244                  int levels = Math.Min(Info.Levels, dst.Info.Levels - firstLevel);
 245  
 246                  _gd.HelperShader.CopyColor(_gd, cbs, src, dst, 0, firstLayer, 0, FirstLevel, layers, levels);
 247              }
 248              else
 249              {
 250                  TextureCopy.Copy(
 251                      _gd.Api,
 252                      cbs.CommandBuffer,
 253                      srcImage,
 254                      dstImage,
 255                      src.Info,
 256                      dst.Info,
 257                      src.FirstLayer,
 258                      dst.FirstLayer,
 259                      src.FirstLevel,
 260                      dst.FirstLevel,
 261                      0,
 262                      firstLayer,
 263                      0,
 264                      firstLevel);
 265              }
 266          }
 267  
 268          public void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel)
 269          {
 270              var src = this;
 271              var dst = (TextureView)destination;
 272  
 273              if (!Valid || !dst.Valid)
 274              {
 275                  return;
 276              }
 277  
 278              _gd.PipelineInternal.EndRenderPass();
 279  
 280              var cbs = _gd.PipelineInternal.CurrentCommandBuffer;
 281  
 282              var srcImage = src.GetImage().Get(cbs).Value;
 283              var dstImage = dst.GetImage().Get(cbs).Value;
 284  
 285              if (!dst.Info.Target.IsMultisample() && Info.Target.IsMultisample())
 286              {
 287                  _gd.HelperShader.CopyMSToNonMS(_gd, cbs, src, dst, srcLayer, dstLayer, 1);
 288              }
 289              else if (dst.Info.Target.IsMultisample() && !Info.Target.IsMultisample())
 290              {
 291                  _gd.HelperShader.CopyNonMSToMS(_gd, cbs, src, dst, srcLayer, dstLayer, 1);
 292              }
 293              else if (dst.Info.BytesPerPixel != Info.BytesPerPixel)
 294              {
 295                  _gd.HelperShader.CopyIncompatibleFormats(_gd, cbs, src, dst, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1);
 296              }
 297              else if (src.Info.Format.IsDepthOrStencil() != dst.Info.Format.IsDepthOrStencil())
 298              {
 299                  _gd.HelperShader.CopyColor(_gd, cbs, src, dst, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1);
 300              }
 301              else
 302              {
 303                  TextureCopy.Copy(
 304                      _gd.Api,
 305                      cbs.CommandBuffer,
 306                      srcImage,
 307                      dstImage,
 308                      src.Info,
 309                      dst.Info,
 310                      src.FirstLayer,
 311                      dst.FirstLayer,
 312                      src.FirstLevel,
 313                      dst.FirstLevel,
 314                      srcLayer,
 315                      dstLayer,
 316                      srcLevel,
 317                      dstLevel,
 318                      1,
 319                      1);
 320              }
 321          }
 322  
 323          public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter)
 324          {
 325              var dst = (TextureView)destination;
 326  
 327              if (_gd.CommandBufferPool.OwnedByCurrentThread)
 328              {
 329                  _gd.PipelineInternal.EndRenderPass();
 330  
 331                  var cbs = _gd.PipelineInternal.CurrentCommandBuffer;
 332  
 333                  CopyToImpl(cbs, dst, srcRegion, dstRegion, linearFilter);
 334              }
 335              else
 336              {
 337                  var cbp = _gd.BackgroundResources.Get().GetPool();
 338  
 339                  using var cbs = cbp.Rent();
 340  
 341                  CopyToImpl(cbs, dst, srcRegion, dstRegion, linearFilter);
 342              }
 343          }
 344  
 345          private void CopyToImpl(CommandBufferScoped cbs, TextureView dst, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter)
 346          {
 347              var src = this;
 348  
 349              var srcFormat = GetCompatibleGalFormat(src.Info.Format);
 350              var dstFormat = GetCompatibleGalFormat(dst.Info.Format);
 351  
 352              bool srcUsesStorageFormat = src.VkFormat == src.Storage.VkFormat;
 353              bool dstUsesStorageFormat = dst.VkFormat == dst.Storage.VkFormat;
 354  
 355              int layers = Math.Min(dst.Info.GetDepthOrLayers(), src.Info.GetDepthOrLayers());
 356              int levels = Math.Min(dst.Info.Levels, src.Info.Levels);
 357  
 358              if (srcUsesStorageFormat && dstUsesStorageFormat)
 359              {
 360                  if ((srcRegion.X1 | dstRegion.X1) == 0 &&
 361                      (srcRegion.Y1 | dstRegion.Y1) == 0 &&
 362                      srcRegion.X2 == src.Width &&
 363                      srcRegion.Y2 == src.Height &&
 364                      dstRegion.X2 == dst.Width &&
 365                      dstRegion.Y2 == dst.Height &&
 366                      src.Width == dst.Width &&
 367                      src.Height == dst.Height &&
 368                      src.VkFormat == dst.VkFormat)
 369                  {
 370                      if (src.Info.Samples > 1 && src.Info.Samples != dst.Info.Samples && src.Info.Format.IsDepthOrStencil())
 371                      {
 372                          // CmdResolveImage does not support depth-stencil resolve, so we need to use an alternative path
 373                          // for those textures.
 374                          TextureCopy.ResolveDepthStencil(_gd, _device, cbs, src, dst);
 375                      }
 376                      else
 377                      {
 378                          TextureCopy.Copy(
 379                              _gd.Api,
 380                              cbs.CommandBuffer,
 381                              src.GetImage().Get(cbs).Value,
 382                              dst.GetImage().Get(cbs).Value,
 383                              src.Info,
 384                              dst.Info,
 385                              src.FirstLayer,
 386                              dst.FirstLayer,
 387                              src.FirstLevel,
 388                              dst.FirstLevel,
 389                              0,
 390                              0,
 391                              0,
 392                              0,
 393                              layers,
 394                              levels);
 395                      }
 396  
 397                      return;
 398                  }
 399  
 400                  if (_gd.FormatCapabilities.OptimalFormatSupports(FormatFeatureFlags.BlitSrcBit, srcFormat) &&
 401                      _gd.FormatCapabilities.OptimalFormatSupports(FormatFeatureFlags.BlitDstBit, dstFormat))
 402                  {
 403                      TextureCopy.Blit(
 404                          _gd.Api,
 405                          cbs.CommandBuffer,
 406                          src.GetImage().Get(cbs).Value,
 407                          dst.GetImage().Get(cbs).Value,
 408                          src.Info,
 409                          dst.Info,
 410                          srcRegion,
 411                          dstRegion,
 412                          src.FirstLayer,
 413                          dst.FirstLayer,
 414                          src.FirstLevel,
 415                          dst.FirstLevel,
 416                          layers,
 417                          levels,
 418                          linearFilter);
 419  
 420                      return;
 421                  }
 422              }
 423  
 424              bool isDepthOrStencil = dst.Info.Format.IsDepthOrStencil();
 425  
 426              if (!VulkanConfiguration.UseUnsafeBlit || (_gd.Vendor != Vendor.Nvidia && _gd.Vendor != Vendor.Intel))
 427              {
 428                  _gd.HelperShader.Blit(
 429                      _gd,
 430                      src,
 431                      dst,
 432                      srcRegion,
 433                      dstRegion,
 434                      layers,
 435                      levels,
 436                      isDepthOrStencil,
 437                      linearFilter);
 438  
 439                  return;
 440              }
 441  
 442              Auto<DisposableImage> srcImage;
 443              Auto<DisposableImage> dstImage;
 444  
 445              if (isDepthOrStencil)
 446              {
 447                  srcImage = src.Storage.CreateAliasedColorForDepthStorageUnsafe(srcFormat).GetImage();
 448                  dstImage = dst.Storage.CreateAliasedColorForDepthStorageUnsafe(dstFormat).GetImage();
 449              }
 450              else
 451              {
 452                  srcImage = src.Storage.CreateAliasedStorageUnsafe(srcFormat).GetImage();
 453                  dstImage = dst.Storage.CreateAliasedStorageUnsafe(dstFormat).GetImage();
 454              }
 455  
 456              TextureCopy.Blit(
 457                  _gd.Api,
 458                  cbs.CommandBuffer,
 459                  srcImage.Get(cbs).Value,
 460                  dstImage.Get(cbs).Value,
 461                  src.Info,
 462                  dst.Info,
 463                  srcRegion,
 464                  dstRegion,
 465                  src.FirstLayer,
 466                  dst.FirstLayer,
 467                  src.FirstLevel,
 468                  dst.FirstLevel,
 469                  layers,
 470                  levels,
 471                  linearFilter,
 472                  ImageAspectFlags.ColorBit,
 473                  ImageAspectFlags.ColorBit);
 474          }
 475  
 476          public static unsafe void InsertMemoryBarrier(
 477              Vk api,
 478              CommandBuffer commandBuffer,
 479              AccessFlags srcAccessMask,
 480              AccessFlags dstAccessMask,
 481              PipelineStageFlags srcStageMask,
 482              PipelineStageFlags dstStageMask)
 483          {
 484              MemoryBarrier memoryBarrier = new()
 485              {
 486                  SType = StructureType.MemoryBarrier,
 487                  SrcAccessMask = srcAccessMask,
 488                  DstAccessMask = dstAccessMask,
 489              };
 490  
 491              api.CmdPipelineBarrier(
 492                  commandBuffer,
 493                  srcStageMask,
 494                  dstStageMask,
 495                  DependencyFlags.None,
 496                  1,
 497                  in memoryBarrier,
 498                  0,
 499                  null,
 500                  0,
 501                  null);
 502          }
 503  
 504          public static ImageMemoryBarrier GetImageBarrier(
 505              Image image,
 506              AccessFlags srcAccessMask,
 507              AccessFlags dstAccessMask,
 508              ImageAspectFlags aspectFlags,
 509              int firstLayer,
 510              int firstLevel,
 511              int layers,
 512              int levels)
 513          {
 514              return new()
 515              {
 516                  SType = StructureType.ImageMemoryBarrier,
 517                  SrcAccessMask = srcAccessMask,
 518                  DstAccessMask = dstAccessMask,
 519                  SrcQueueFamilyIndex = Vk.QueueFamilyIgnored,
 520                  DstQueueFamilyIndex = Vk.QueueFamilyIgnored,
 521                  Image = image,
 522                  OldLayout = ImageLayout.General,
 523                  NewLayout = ImageLayout.General,
 524                  SubresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, (uint)levels, (uint)firstLayer, (uint)layers),
 525              };
 526          }
 527  
 528          public static unsafe void InsertImageBarrier(
 529              Vk api,
 530              CommandBuffer commandBuffer,
 531              Image image,
 532              AccessFlags srcAccessMask,
 533              AccessFlags dstAccessMask,
 534              PipelineStageFlags srcStageMask,
 535              PipelineStageFlags dstStageMask,
 536              ImageAspectFlags aspectFlags,
 537              int firstLayer,
 538              int firstLevel,
 539              int layers,
 540              int levels)
 541          {
 542              ImageMemoryBarrier memoryBarrier = GetImageBarrier(
 543                  image,
 544                  srcAccessMask,
 545                  dstAccessMask,
 546                  aspectFlags,
 547                  firstLayer,
 548                  firstLevel,
 549                  layers,
 550                  levels);
 551  
 552              api.CmdPipelineBarrier(
 553                  commandBuffer,
 554                  srcStageMask,
 555                  dstStageMask,
 556                  0,
 557                  0,
 558                  null,
 559                  0,
 560                  null,
 561                  1,
 562                  in memoryBarrier);
 563          }
 564  
 565          public TextureView GetView(Format format)
 566          {
 567              if (format == Info.Format)
 568              {
 569                  return this;
 570              }
 571  
 572              if (_selfManagedViews != null && _selfManagedViews.TryGetValue(format, out var view))
 573              {
 574                  return view;
 575              }
 576  
 577              view = CreateViewImpl(new TextureCreateInfo(
 578                  Info.Width,
 579                  Info.Height,
 580                  Info.Depth,
 581                  Info.Levels,
 582                  Info.Samples,
 583                  Info.BlockWidth,
 584                  Info.BlockHeight,
 585                  Info.BytesPerPixel,
 586                  format,
 587                  Info.DepthStencilMode,
 588                  Info.Target,
 589                  Info.SwizzleR,
 590                  Info.SwizzleG,
 591                  Info.SwizzleB,
 592                  Info.SwizzleA), 0, 0);
 593  
 594              (_selfManagedViews ??= new Dictionary<Format, TextureView>()).Add(format, view);
 595  
 596              return view;
 597          }
 598  
 599          public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel)
 600          {
 601              return CreateViewImpl(info, firstLayer, firstLevel);
 602          }
 603  
 604          public TextureView CreateViewImpl(TextureCreateInfo info, int firstLayer, int firstLevel)
 605          {
 606              return new TextureView(_gd, _device, info, Storage, FirstLayer + firstLayer, FirstLevel + firstLevel);
 607          }
 608  
 609          public byte[] GetData(int x, int y, int width, int height)
 610          {
 611              int size = width * height * Info.BytesPerPixel;
 612              using var bufferHolder = _gd.BufferManager.Create(_gd, size);
 613  
 614              using (var cbs = _gd.CommandBufferPool.Rent())
 615              {
 616                  var buffer = bufferHolder.GetBuffer(cbs.CommandBuffer).Get(cbs).Value;
 617                  var image = GetImage().Get(cbs).Value;
 618  
 619                  CopyFromOrToBuffer(cbs.CommandBuffer, buffer, image, size, true, 0, 0, x, y, width, height);
 620              }
 621  
 622              bufferHolder.WaitForFences();
 623              byte[] bitmap = new byte[size];
 624              GetDataFromBuffer(bufferHolder.GetDataStorage(0, size), size, Span<byte>.Empty).CopyTo(bitmap);
 625              return bitmap;
 626          }
 627  
 628          public PinnedSpan<byte> GetData()
 629          {
 630              BackgroundResource resources = _gd.BackgroundResources.Get();
 631  
 632              if (_gd.CommandBufferPool.OwnedByCurrentThread)
 633              {
 634                  _gd.FlushAllCommands();
 635  
 636                  return PinnedSpan<byte>.UnsafeFromSpan(GetData(_gd.CommandBufferPool, resources.GetFlushBuffer()));
 637              }
 638  
 639              return PinnedSpan<byte>.UnsafeFromSpan(GetData(resources.GetPool(), resources.GetFlushBuffer()));
 640          }
 641  
 642          public PinnedSpan<byte> GetData(int layer, int level)
 643          {
 644              BackgroundResource resources = _gd.BackgroundResources.Get();
 645  
 646              if (_gd.CommandBufferPool.OwnedByCurrentThread)
 647              {
 648                  _gd.FlushAllCommands();
 649  
 650                  return PinnedSpan<byte>.UnsafeFromSpan(GetData(_gd.CommandBufferPool, resources.GetFlushBuffer(), layer, level));
 651              }
 652  
 653              return PinnedSpan<byte>.UnsafeFromSpan(GetData(resources.GetPool(), resources.GetFlushBuffer(), layer, level));
 654          }
 655  
 656          public void CopyTo(BufferRange range, int layer, int level, int stride)
 657          {
 658              _gd.PipelineInternal.EndRenderPass();
 659              var cbs = _gd.PipelineInternal.CurrentCommandBuffer;
 660  
 661              int outSize = Info.GetMipSize(level);
 662              int hostSize = GetBufferDataLength(outSize);
 663  
 664              var image = GetImage().Get(cbs).Value;
 665              int offset = range.Offset;
 666  
 667              Auto<DisposableBuffer> autoBuffer = _gd.BufferManager.GetBuffer(cbs.CommandBuffer, range.Handle, true);
 668              VkBuffer buffer = autoBuffer.Get(cbs, range.Offset, outSize).Value;
 669  
 670              if (PrepareOutputBuffer(cbs, hostSize, buffer, out VkBuffer copyToBuffer, out BufferHolder tempCopyHolder))
 671              {
 672                  // No barrier necessary, as this is a temporary copy buffer.
 673                  offset = 0;
 674              }
 675              else
 676              {
 677                  BufferHolder.InsertBufferBarrier(
 678                      _gd,
 679                      cbs.CommandBuffer,
 680                      copyToBuffer,
 681                      BufferHolder.DefaultAccessFlags,
 682                      AccessFlags.TransferWriteBit,
 683                      PipelineStageFlags.AllCommandsBit,
 684                      PipelineStageFlags.TransferBit,
 685                      offset,
 686                      outSize);
 687              }
 688  
 689              InsertImageBarrier(
 690                  _gd.Api,
 691                  cbs.CommandBuffer,
 692                  image,
 693                  TextureStorage.DefaultAccessMask,
 694                  AccessFlags.TransferReadBit,
 695                  PipelineStageFlags.AllCommandsBit,
 696                  PipelineStageFlags.TransferBit,
 697                  Info.Format.ConvertAspectFlags(),
 698                  FirstLayer + layer,
 699                  FirstLevel + level,
 700                  1,
 701                  1);
 702  
 703              CopyFromOrToBuffer(cbs.CommandBuffer, copyToBuffer, image, hostSize, true, layer, level, 1, 1, singleSlice: true, offset, stride);
 704  
 705              if (tempCopyHolder != null)
 706              {
 707                  CopyDataToOutputBuffer(cbs, tempCopyHolder, autoBuffer, hostSize, range.Offset);
 708                  tempCopyHolder.Dispose();
 709              }
 710              else
 711              {
 712                  BufferHolder.InsertBufferBarrier(
 713                      _gd,
 714                      cbs.CommandBuffer,
 715                      copyToBuffer,
 716                      AccessFlags.TransferWriteBit,
 717                      BufferHolder.DefaultAccessFlags,
 718                      PipelineStageFlags.TransferBit,
 719                      PipelineStageFlags.AllCommandsBit,
 720                      offset,
 721                      outSize);
 722              }
 723          }
 724  
 725          private ReadOnlySpan<byte> GetData(CommandBufferPool cbp, PersistentFlushBuffer flushBuffer)
 726          {
 727              int size = 0;
 728  
 729              for (int level = 0; level < Info.Levels; level++)
 730              {
 731                  size += Info.GetMipSize(level);
 732              }
 733  
 734              size = GetBufferDataLength(size);
 735  
 736              Span<byte> result = flushBuffer.GetTextureData(cbp, this, size);
 737              return GetDataFromBuffer(result, size, result);
 738          }
 739  
 740          private ReadOnlySpan<byte> GetData(CommandBufferPool cbp, PersistentFlushBuffer flushBuffer, int layer, int level)
 741          {
 742              int size = GetBufferDataLength(Info.GetMipSize(level));
 743  
 744              Span<byte> result = flushBuffer.GetTextureData(cbp, this, size, layer, level);
 745              return GetDataFromBuffer(result, size, result);
 746          }
 747  
 748          /// <inheritdoc/>
 749          public void SetData(MemoryOwner<byte> data)
 750          {
 751              SetData(data.Span, 0, 0, Info.GetLayers(), Info.Levels, singleSlice: false);
 752              data.Dispose();
 753          }
 754  
 755          /// <inheritdoc/>
 756          public void SetData(MemoryOwner<byte> data, int layer, int level)
 757          {
 758              SetData(data.Span, layer, level, 1, 1, singleSlice: true);
 759              data.Dispose();
 760          }
 761  
 762          /// <inheritdoc/>
 763          public void SetData(MemoryOwner<byte> data, int layer, int level, Rectangle<int> region)
 764          {
 765              SetData(data.Span, layer, level, 1, 1, singleSlice: true, region);
 766              data.Dispose();
 767          }
 768  
 769          private void SetData(ReadOnlySpan<byte> data, int layer, int level, int layers, int levels, bool singleSlice, Rectangle<int>? region = null)
 770          {
 771              int bufferDataLength = GetBufferDataLength(data.Length);
 772  
 773              using var bufferHolder = _gd.BufferManager.Create(_gd, bufferDataLength);
 774  
 775              Auto<DisposableImage> imageAuto = GetImage();
 776  
 777              // Load texture data inline if the texture has been used on the current command buffer.
 778  
 779              bool loadInline = Storage.HasCommandBufferDependency(_gd.PipelineInternal.CurrentCommandBuffer);
 780  
 781              var cbs = loadInline ? _gd.PipelineInternal.CurrentCommandBuffer : _gd.PipelineInternal.GetPreloadCommandBuffer();
 782  
 783              if (loadInline)
 784              {
 785                  _gd.PipelineInternal.EndRenderPass();
 786              }
 787  
 788              CopyDataToBuffer(bufferHolder.GetDataStorage(0, bufferDataLength), data);
 789  
 790              var buffer = bufferHolder.GetBuffer(cbs.CommandBuffer).Get(cbs).Value;
 791              var image = imageAuto.Get(cbs).Value;
 792  
 793              if (region.HasValue)
 794              {
 795                  CopyFromOrToBuffer(
 796                      cbs.CommandBuffer,
 797                      buffer,
 798                      image,
 799                      bufferDataLength,
 800                      false,
 801                      layer,
 802                      level,
 803                      region.Value.X,
 804                      region.Value.Y,
 805                      region.Value.Width,
 806                      region.Value.Height);
 807              }
 808              else
 809              {
 810                  CopyFromOrToBuffer(cbs.CommandBuffer, buffer, image, bufferDataLength, false, layer, level, layers, levels, singleSlice);
 811              }
 812          }
 813  
 814          private int GetBufferDataLength(int length)
 815          {
 816              if (NeedsD24S8Conversion())
 817              {
 818                  return length * 2;
 819              }
 820  
 821              return length;
 822          }
 823  
 824          private Format GetCompatibleGalFormat(Format format)
 825          {
 826              if (NeedsD24S8Conversion())
 827              {
 828                  return Format.D32FloatS8Uint;
 829              }
 830  
 831              return format;
 832          }
 833  
 834          private void CopyDataToBuffer(Span<byte> storage, ReadOnlySpan<byte> input)
 835          {
 836              if (NeedsD24S8Conversion())
 837              {
 838                  FormatConverter.ConvertD24S8ToD32FS8(storage, input);
 839                  return;
 840              }
 841  
 842              input.CopyTo(storage);
 843          }
 844  
 845          private ReadOnlySpan<byte> GetDataFromBuffer(ReadOnlySpan<byte> storage, int size, Span<byte> output)
 846          {
 847              if (NeedsD24S8Conversion())
 848              {
 849                  if (output.IsEmpty)
 850                  {
 851                      output = new byte[GetBufferDataLength(size)];
 852                  }
 853  
 854                  FormatConverter.ConvertD32FS8ToD24S8(output, storage);
 855                  return output;
 856              }
 857  
 858              return storage;
 859          }
 860  
 861          private bool PrepareOutputBuffer(CommandBufferScoped cbs, int hostSize, VkBuffer target, out VkBuffer copyTarget, out BufferHolder copyTargetHolder)
 862          {
 863              if (NeedsD24S8Conversion())
 864              {
 865                  copyTargetHolder = _gd.BufferManager.Create(_gd, hostSize);
 866                  copyTarget = copyTargetHolder.GetBuffer().Get(cbs, 0, hostSize).Value;
 867  
 868                  return true;
 869              }
 870  
 871              copyTarget = target;
 872              copyTargetHolder = null;
 873  
 874              return false;
 875          }
 876  
 877          private void CopyDataToOutputBuffer(CommandBufferScoped cbs, BufferHolder hostData, Auto<DisposableBuffer> copyTarget, int hostSize, int dstOffset)
 878          {
 879              if (NeedsD24S8Conversion())
 880              {
 881                  _gd.HelperShader.ConvertD32S8ToD24S8(_gd, cbs, hostData, copyTarget, hostSize / (2 * sizeof(int)), dstOffset);
 882              }
 883          }
 884  
 885          private bool NeedsD24S8Conversion()
 886          {
 887              return FormatCapabilities.IsD24S8(Info.Format) && VkFormat == VkFormat.D32SfloatS8Uint;
 888          }
 889  
 890          public void CopyFromOrToBuffer(
 891              CommandBuffer commandBuffer,
 892              VkBuffer buffer,
 893              Image image,
 894              int size,
 895              bool to,
 896              int dstLayer,
 897              int dstLevel,
 898              int dstLayers,
 899              int dstLevels,
 900              bool singleSlice,
 901              int offset = 0,
 902              int stride = 0)
 903          {
 904              bool is3D = Info.Target == Target.Texture3D;
 905              int width = Math.Max(1, Info.Width >> dstLevel);
 906              int height = Math.Max(1, Info.Height >> dstLevel);
 907              int depth = is3D && !singleSlice ? Math.Max(1, Info.Depth >> dstLevel) : 1;
 908              int layer = is3D ? 0 : dstLayer;
 909              int layers = dstLayers;
 910              int levels = dstLevels;
 911  
 912              for (int level = 0; level < levels; level++)
 913              {
 914                  int mipSize = GetBufferDataLength(is3D && !singleSlice
 915                      ? Info.GetMipSize(dstLevel + level)
 916                      : Info.GetMipSize2D(dstLevel + level) * dstLayers);
 917  
 918                  int endOffset = offset + mipSize;
 919  
 920                  if ((uint)endOffset > (uint)size)
 921                  {
 922                      break;
 923                  }
 924  
 925                  int rowLength = ((stride == 0 ? Info.GetMipStride(dstLevel + level) : stride) / Info.BytesPerPixel) * Info.BlockWidth;
 926  
 927                  var aspectFlags = Info.Format.ConvertAspectFlags();
 928  
 929                  if (aspectFlags == (ImageAspectFlags.DepthBit | ImageAspectFlags.StencilBit))
 930                  {
 931                      aspectFlags = ImageAspectFlags.DepthBit;
 932                  }
 933  
 934                  var sl = new ImageSubresourceLayers(
 935                      aspectFlags,
 936                      (uint)(FirstLevel + dstLevel + level),
 937                      (uint)(FirstLayer + layer),
 938                      (uint)layers);
 939  
 940                  var extent = new Extent3D((uint)width, (uint)height, (uint)depth);
 941  
 942                  int z = is3D ? dstLayer : 0;
 943  
 944                  var region = new BufferImageCopy(
 945                      (ulong)offset,
 946                      (uint)AlignUpNpot(rowLength, Info.BlockWidth),
 947                      (uint)AlignUpNpot(height, Info.BlockHeight),
 948                      sl,
 949                      new Offset3D(0, 0, z),
 950                      extent);
 951  
 952                  if (to)
 953                  {
 954                      _gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, in region);
 955                  }
 956                  else
 957                  {
 958                      _gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, in region);
 959                  }
 960  
 961                  offset += mipSize;
 962  
 963                  width = Math.Max(1, width >> 1);
 964                  height = Math.Max(1, height >> 1);
 965  
 966                  if (Info.Target == Target.Texture3D)
 967                  {
 968                      depth = Math.Max(1, depth >> 1);
 969                  }
 970              }
 971          }
 972  
 973          private void CopyFromOrToBuffer(
 974              CommandBuffer commandBuffer,
 975              VkBuffer buffer,
 976              Image image,
 977              int size,
 978              bool to,
 979              int dstLayer,
 980              int dstLevel,
 981              int x,
 982              int y,
 983              int width,
 984              int height)
 985          {
 986              var aspectFlags = Info.Format.ConvertAspectFlags();
 987  
 988              if (aspectFlags == (ImageAspectFlags.DepthBit | ImageAspectFlags.StencilBit))
 989              {
 990                  aspectFlags = ImageAspectFlags.DepthBit;
 991              }
 992  
 993              var sl = new ImageSubresourceLayers(aspectFlags, (uint)(FirstLevel + dstLevel), (uint)(FirstLayer + dstLayer), 1);
 994  
 995              var extent = new Extent3D((uint)width, (uint)height, 1);
 996  
 997              int rowLengthAlignment = Info.BlockWidth;
 998  
 999              // We expect all data being written into the texture to have a stride aligned by 4.
1000              if (!to && Info.BytesPerPixel < 4)
1001              {
1002                  rowLengthAlignment = 4 / Info.BytesPerPixel;
1003              }
1004  
1005              var region = new BufferImageCopy(
1006                  0,
1007                  (uint)AlignUpNpot(width, rowLengthAlignment),
1008                  (uint)AlignUpNpot(height, Info.BlockHeight),
1009                  sl,
1010                  new Offset3D(x, y, 0),
1011                  extent);
1012  
1013              if (to)
1014              {
1015                  _gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, in region);
1016              }
1017              else
1018              {
1019                  _gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, in region);
1020              }
1021          }
1022  
1023          private static int AlignUpNpot(int size, int alignment)
1024          {
1025              int remainder = size % alignment;
1026              if (remainder == 0)
1027              {
1028                  return size;
1029              }
1030  
1031              return size + (alignment - remainder);
1032          }
1033  
1034          public void SetStorage(BufferRange buffer)
1035          {
1036              throw new NotImplementedException();
1037          }
1038  
1039          public void PrepareForUsage(CommandBufferScoped cbs, PipelineStageFlags flags, List<TextureView> feedbackLoopHazards)
1040          {
1041              Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, flags);
1042  
1043              if (feedbackLoopHazards != null && Storage.IsBound(this))
1044              {
1045                  feedbackLoopHazards.Add(this);
1046                  _hazardUses++;
1047              }
1048          }
1049  
1050          public void ClearUsage(List<TextureView> feedbackLoopHazards)
1051          {
1052              if (_hazardUses != 0 && feedbackLoopHazards != null)
1053              {
1054                  feedbackLoopHazards.Remove(this);
1055                  _hazardUses--;
1056              }
1057          }
1058  
1059          public void DecrementHazardUses()
1060          {
1061              if (_hazardUses != 0)
1062              {
1063                  _hazardUses--;
1064              }
1065          }
1066  
1067          public (RenderPassHolder rpHolder, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
1068              VulkanRenderer gd,
1069              Device device,
1070              CommandBufferScoped cbs,
1071              FramebufferParams fb)
1072          {
1073              var key = fb.GetRenderPassCacheKey();
1074  
1075              if (_renderPasses == null || !_renderPasses.TryGetValue(ref key, out RenderPassHolder rpHolder))
1076              {
1077                  rpHolder = new RenderPassHolder(gd, device, key, fb);
1078              }
1079  
1080              return (rpHolder, rpHolder.GetFramebuffer(gd, cbs, fb));
1081          }
1082  
1083          public void AddRenderPass(RenderPassCacheKey key, RenderPassHolder renderPass)
1084          {
1085              _renderPasses ??= new HashTableSlim<RenderPassCacheKey, RenderPassHolder>();
1086  
1087              _renderPasses.Add(ref key, renderPass);
1088          }
1089  
1090          public void RemoveRenderPass(RenderPassCacheKey key)
1091          {
1092              _renderPasses.Remove(ref key);
1093          }
1094  
1095          protected virtual void Dispose(bool disposing)
1096          {
1097              if (disposing)
1098              {
1099                  bool wasValid = Interlocked.Exchange(ref _isValid, 0) != 0;
1100                  if (wasValid)
1101                  {
1102                      _gd.Textures.Remove(this);
1103  
1104                      _imageView.Dispose();
1105                      _imageView2dArray?.Dispose();
1106  
1107                      if (_imageViewIdentity != _imageView)
1108                      {
1109                          _imageViewIdentity.Dispose();
1110                      }
1111  
1112                      if (_imageViewDraw != _imageViewIdentity)
1113                      {
1114                          _imageViewDraw.Dispose();
1115                      }
1116  
1117                      Storage?.DecrementViewsCount();
1118  
1119                      if (_renderPasses != null)
1120                      {
1121                          var renderPasses = _renderPasses.Values.ToArray();
1122  
1123                          foreach (var pass in renderPasses)
1124                          {
1125                              pass.Dispose();
1126                          }
1127                      }
1128  
1129                      if (_selfManagedViews != null)
1130                      {
1131                          foreach (var view in _selfManagedViews.Values)
1132                          {
1133                              view.Dispose();
1134                          }
1135  
1136                          _selfManagedViews = null;
1137                      }
1138                  }
1139              }
1140          }
1141  
1142          public void Dispose()
1143          {
1144              Dispose(true);
1145          }
1146  
1147          public void Release()
1148          {
1149              Dispose();
1150          }
1151      }
1152  }