/ src / Ryujinx.Graphics.Vulkan / HelperShader.cs
HelperShader.cs
   1  using Ryujinx.Common;
   2  using Ryujinx.Graphics.GAL;
   3  using Ryujinx.Graphics.Shader;
   4  using Ryujinx.Graphics.Shader.Translation;
   5  using Silk.NET.Vulkan;
   6  using System;
   7  using System.Collections.Generic;
   8  using System.Numerics;
   9  using CompareOp = Ryujinx.Graphics.GAL.CompareOp;
  10  using Format = Ryujinx.Graphics.GAL.Format;
  11  using PrimitiveTopology = Ryujinx.Graphics.GAL.PrimitiveTopology;
  12  using SamplerCreateInfo = Ryujinx.Graphics.GAL.SamplerCreateInfo;
  13  using StencilOp = Ryujinx.Graphics.GAL.StencilOp;
  14  using Viewport = Ryujinx.Graphics.GAL.Viewport;
  15  using VkFormat = Silk.NET.Vulkan.Format;
  16  
  17  namespace Ryujinx.Graphics.Vulkan
  18  {
  19      enum ComponentType
  20      {
  21          Float,
  22          SignedInteger,
  23          UnsignedInteger,
  24      }
  25  
  26      class HelperShader : IDisposable
  27      {
  28          private const int UniformBufferAlignment = 256;
  29          private const int ConvertElementsPerWorkgroup = 32 * 100; // Work group size of 32 times 100 elements.
  30          private const string ShaderBinariesPath = "Ryujinx.Graphics.Vulkan/Shaders/SpirvBinaries";
  31  
  32          private readonly PipelineHelperShader _pipeline;
  33          private readonly ISampler _samplerLinear;
  34          private readonly ISampler _samplerNearest;
  35          private readonly IProgram _programColorBlit;
  36          private readonly IProgram _programColorBlitMs;
  37          private readonly IProgram _programColorBlitClearAlpha;
  38          private readonly IProgram _programColorClearF;
  39          private readonly IProgram _programColorClearSI;
  40          private readonly IProgram _programColorClearUI;
  41          private readonly IProgram _programDepthStencilClear;
  42          private readonly IProgram _programStrideChange;
  43          private readonly IProgram _programConvertD32S8ToD24S8;
  44          private readonly IProgram _programConvertIndexBuffer;
  45          private readonly IProgram _programConvertIndirectData;
  46          private readonly IProgram _programColorCopyShortening;
  47          private readonly IProgram _programColorCopyToNonMs;
  48          private readonly IProgram _programColorCopyWidening;
  49          private readonly IProgram _programColorDrawToMs;
  50          private readonly IProgram _programDepthBlit;
  51          private readonly IProgram _programDepthBlitMs;
  52          private readonly IProgram _programDepthDrawToMs;
  53          private readonly IProgram _programDepthDrawToNonMs;
  54          private readonly IProgram _programStencilBlit;
  55          private readonly IProgram _programStencilBlitMs;
  56          private readonly IProgram _programStencilDrawToMs;
  57          private readonly IProgram _programStencilDrawToNonMs;
  58  
  59          public HelperShader(VulkanRenderer gd, Device device)
  60          {
  61              _pipeline = new PipelineHelperShader(gd, device);
  62              _pipeline.Initialize();
  63  
  64              _samplerLinear = gd.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
  65              _samplerNearest = gd.CreateSampler(SamplerCreateInfo.Create(MinFilter.Nearest, MagFilter.Nearest));
  66  
  67              var blitResourceLayout = new ResourceLayoutBuilder()
  68                  .Add(ResourceStages.Vertex, ResourceType.UniformBuffer, 1)
  69                  .Add(ResourceStages.Fragment, ResourceType.TextureAndSampler, 0).Build();
  70  
  71              _programColorBlit = gd.CreateProgramWithMinimalLayout(new[]
  72              {
  73                  new ShaderSource(ReadSpirv("ColorBlitVertex.spv"), ShaderStage.Vertex, TargetLanguage.Spirv),
  74                  new ShaderSource(ReadSpirv("ColorBlitFragment.spv"), ShaderStage.Fragment, TargetLanguage.Spirv),
  75              }, blitResourceLayout);
  76  
  77              _programColorBlitMs = gd.CreateProgramWithMinimalLayout(new[]
  78              {
  79                  new ShaderSource(ReadSpirv("ColorBlitVertex.spv"), ShaderStage.Vertex, TargetLanguage.Spirv),
  80                  new ShaderSource(ReadSpirv("ColorBlitMsFragment.spv"), ShaderStage.Fragment, TargetLanguage.Spirv),
  81              }, blitResourceLayout);
  82  
  83              _programColorBlitClearAlpha = gd.CreateProgramWithMinimalLayout(new[]
  84              {
  85                  new ShaderSource(ReadSpirv("ColorBlitVertex.spv"), ShaderStage.Vertex, TargetLanguage.Spirv),
  86                  new ShaderSource(ReadSpirv("ColorBlitClearAlphaFragment.spv"), ShaderStage.Fragment, TargetLanguage.Spirv),
  87              }, blitResourceLayout);
  88  
  89              var colorClearResourceLayout = new ResourceLayoutBuilder().Add(ResourceStages.Vertex, ResourceType.UniformBuffer, 1).Build();
  90  
  91              _programColorClearF = gd.CreateProgramWithMinimalLayout(new[]
  92              {
  93                  new ShaderSource(ReadSpirv("ColorClearVertex.spv"), ShaderStage.Vertex, TargetLanguage.Spirv),
  94                  new ShaderSource(ReadSpirv("ColorClearFFragment.spv"), ShaderStage.Fragment, TargetLanguage.Spirv),
  95              }, colorClearResourceLayout);
  96  
  97              _programColorClearSI = gd.CreateProgramWithMinimalLayout(new[]
  98              {
  99                  new ShaderSource(ReadSpirv("ColorClearVertex.spv"), ShaderStage.Vertex, TargetLanguage.Spirv),
 100                  new ShaderSource(ReadSpirv("ColorClearSIFragment.spv"), ShaderStage.Fragment, TargetLanguage.Spirv),
 101              }, colorClearResourceLayout);
 102  
 103              _programColorClearUI = gd.CreateProgramWithMinimalLayout(new[]
 104              {
 105                  new ShaderSource(ReadSpirv("ColorClearVertex.spv"), ShaderStage.Vertex, TargetLanguage.Spirv),
 106                  new ShaderSource(ReadSpirv("ColorClearUIFragment.spv"), ShaderStage.Fragment, TargetLanguage.Spirv),
 107              }, colorClearResourceLayout);
 108  
 109              _programDepthStencilClear = gd.CreateProgramWithMinimalLayout(new[]
 110              {
 111                  new ShaderSource(ReadSpirv("ColorClearVertex.spv"), ShaderStage.Vertex, TargetLanguage.Spirv),
 112                  new ShaderSource(ReadSpirv("DepthStencilClearFragment.spv"), ShaderStage.Fragment, TargetLanguage.Spirv),
 113              }, colorClearResourceLayout);
 114  
 115              var strideChangeResourceLayout = new ResourceLayoutBuilder()
 116                  .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
 117                  .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
 118                  .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true).Build();
 119  
 120              _programStrideChange = gd.CreateProgramWithMinimalLayout(new[]
 121              {
 122                  new ShaderSource(ReadSpirv("ChangeBufferStride.spv"), ShaderStage.Compute, TargetLanguage.Spirv),
 123              }, strideChangeResourceLayout);
 124  
 125              var colorCopyResourceLayout = new ResourceLayoutBuilder()
 126                  .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
 127                  .Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 0)
 128                  .Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
 129  
 130              _programColorCopyShortening = gd.CreateProgramWithMinimalLayout(new[]
 131              {
 132                  new ShaderSource(ReadSpirv("ColorCopyShorteningCompute.spv"), ShaderStage.Compute, TargetLanguage.Spirv),
 133              }, colorCopyResourceLayout);
 134  
 135              _programColorCopyToNonMs = gd.CreateProgramWithMinimalLayout(new[]
 136              {
 137                  new ShaderSource(ReadSpirv("ColorCopyToNonMsCompute.spv"), ShaderStage.Compute, TargetLanguage.Spirv),
 138              }, colorCopyResourceLayout);
 139  
 140              _programColorCopyWidening = gd.CreateProgramWithMinimalLayout(new[]
 141              {
 142                  new ShaderSource(ReadSpirv("ColorCopyWideningCompute.spv"), ShaderStage.Compute, TargetLanguage.Spirv),
 143              }, colorCopyResourceLayout);
 144  
 145              var colorDrawToMsResourceLayout = new ResourceLayoutBuilder()
 146                  .Add(ResourceStages.Fragment, ResourceType.UniformBuffer, 0)
 147                  .Add(ResourceStages.Fragment, ResourceType.TextureAndSampler, 0).Build();
 148  
 149              _programColorDrawToMs = gd.CreateProgramWithMinimalLayout(new[]
 150              {
 151                  new ShaderSource(ReadSpirv("ColorDrawToMsVertex.spv"), ShaderStage.Vertex, TargetLanguage.Spirv),
 152                  new ShaderSource(ReadSpirv("ColorDrawToMsFragment.spv"), ShaderStage.Fragment, TargetLanguage.Spirv),
 153              }, colorDrawToMsResourceLayout);
 154  
 155              var convertD32S8ToD24S8ResourceLayout = new ResourceLayoutBuilder()
 156                  .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
 157                  .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
 158                  .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true).Build();
 159  
 160              _programConvertD32S8ToD24S8 = gd.CreateProgramWithMinimalLayout(new[]
 161              {
 162                  new ShaderSource(ReadSpirv("ConvertD32S8ToD24S8.spv"), ShaderStage.Compute, TargetLanguage.Spirv),
 163              }, convertD32S8ToD24S8ResourceLayout);
 164  
 165              var convertIndexBufferResourceLayout = new ResourceLayoutBuilder()
 166                  .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
 167                  .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
 168                  .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true).Build();
 169  
 170              _programConvertIndexBuffer = gd.CreateProgramWithMinimalLayout(new[]
 171              {
 172                  new ShaderSource(ReadSpirv("ConvertIndexBuffer.spv"), ShaderStage.Compute, TargetLanguage.Spirv),
 173              }, convertIndexBufferResourceLayout);
 174  
 175              var convertIndirectDataResourceLayout = new ResourceLayoutBuilder()
 176                  .Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
 177                  .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
 178                  .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true)
 179                  .Add(ResourceStages.Compute, ResourceType.StorageBuffer, 3).Build();
 180  
 181              _programConvertIndirectData = gd.CreateProgramWithMinimalLayout(new[]
 182              {
 183                  new ShaderSource(ReadSpirv("ConvertIndirectData.spv"), ShaderStage.Compute, TargetLanguage.Spirv),
 184              }, convertIndirectDataResourceLayout);
 185  
 186              _programDepthBlit = gd.CreateProgramWithMinimalLayout(new[]
 187              {
 188                  new ShaderSource(ReadSpirv("ColorBlitVertex.spv"), ShaderStage.Vertex, TargetLanguage.Spirv),
 189                  new ShaderSource(ReadSpirv("DepthBlitFragment.spv"), ShaderStage.Fragment, TargetLanguage.Spirv),
 190              }, blitResourceLayout);
 191  
 192              _programDepthBlitMs = gd.CreateProgramWithMinimalLayout(new[]
 193              {
 194                  new ShaderSource(ReadSpirv("ColorBlitVertex.spv"), ShaderStage.Vertex, TargetLanguage.Spirv),
 195                  new ShaderSource(ReadSpirv("DepthBlitMsFragment.spv"), ShaderStage.Fragment, TargetLanguage.Spirv),
 196              }, blitResourceLayout);
 197  
 198              _programDepthDrawToMs = gd.CreateProgramWithMinimalLayout(new[]
 199              {
 200                  new ShaderSource(ReadSpirv("ColorDrawToMsVertex.spv"), ShaderStage.Vertex, TargetLanguage.Spirv),
 201                  new ShaderSource(ReadSpirv("DepthDrawToMsFragment.spv"), ShaderStage.Fragment, TargetLanguage.Spirv),
 202              }, colorDrawToMsResourceLayout);
 203  
 204              _programDepthDrawToNonMs = gd.CreateProgramWithMinimalLayout(new[]
 205              {
 206                  new ShaderSource(ReadSpirv("ColorDrawToMsVertex.spv"), ShaderStage.Vertex, TargetLanguage.Spirv),
 207                  new ShaderSource(ReadSpirv("DepthDrawToNonMsFragment.spv"), ShaderStage.Fragment, TargetLanguage.Spirv),
 208              }, colorDrawToMsResourceLayout);
 209  
 210              if (gd.Capabilities.SupportsShaderStencilExport)
 211              {
 212                  _programStencilBlit = gd.CreateProgramWithMinimalLayout(new[]
 213                  {
 214                      new ShaderSource(ReadSpirv("ColorBlitVertex.spv"), ShaderStage.Vertex, TargetLanguage.Spirv),
 215                      new ShaderSource(ReadSpirv("StencilBlitFragment.spv"), ShaderStage.Fragment, TargetLanguage.Spirv),
 216                  }, blitResourceLayout);
 217  
 218                  _programStencilBlitMs = gd.CreateProgramWithMinimalLayout(new[]
 219                  {
 220                      new ShaderSource(ReadSpirv("ColorBlitVertex.spv"), ShaderStage.Vertex, TargetLanguage.Spirv),
 221                      new ShaderSource(ReadSpirv("StencilBlitMsFragment.spv"), ShaderStage.Fragment, TargetLanguage.Spirv),
 222                  }, blitResourceLayout);
 223  
 224                  _programStencilDrawToMs = gd.CreateProgramWithMinimalLayout(new[]
 225                  {
 226                      new ShaderSource(ReadSpirv("ColorDrawToMsVertex.spv"), ShaderStage.Vertex, TargetLanguage.Spirv),
 227                      new ShaderSource(ReadSpirv("StencilDrawToMsFragment.spv"), ShaderStage.Fragment, TargetLanguage.Spirv),
 228                  }, colorDrawToMsResourceLayout);
 229  
 230                  _programStencilDrawToNonMs = gd.CreateProgramWithMinimalLayout(new[]
 231                  {
 232                      new ShaderSource(ReadSpirv("ColorDrawToMsVertex.spv"), ShaderStage.Vertex, TargetLanguage.Spirv),
 233                      new ShaderSource(ReadSpirv("StencilDrawToNonMsFragment.spv"), ShaderStage.Fragment, TargetLanguage.Spirv),
 234                  }, colorDrawToMsResourceLayout);
 235              }
 236          }
 237  
 238          private static byte[] ReadSpirv(string fileName)
 239          {
 240              return EmbeddedResources.Read(string.Join('/', ShaderBinariesPath, fileName));
 241          }
 242  
 243          public void Blit(
 244              VulkanRenderer gd,
 245              TextureView src,
 246              TextureView dst,
 247              Extents2D srcRegion,
 248              Extents2D dstRegion,
 249              int layers,
 250              int levels,
 251              bool isDepthOrStencil,
 252              bool linearFilter,
 253              bool clearAlpha = false)
 254          {
 255              gd.FlushAllCommands();
 256  
 257              using var cbs = gd.CommandBufferPool.Rent();
 258  
 259              for (int l = 0; l < levels; l++)
 260              {
 261                  var mipSrcRegion = new Extents2D(
 262                      srcRegion.X1 >> l,
 263                      srcRegion.Y1 >> l,
 264                      srcRegion.X2 >> l,
 265                      srcRegion.Y2 >> l);
 266  
 267                  var mipDstRegion = new Extents2D(
 268                      dstRegion.X1 >> l,
 269                      dstRegion.Y1 >> l,
 270                      dstRegion.X2 >> l,
 271                      dstRegion.Y2 >> l);
 272  
 273                  for (int z = 0; z < layers; z++)
 274                  {
 275                      var srcView = Create2DLayerView(src, z, l);
 276                      var dstView = Create2DLayerView(dst, z, l);
 277  
 278                      if (isDepthOrStencil)
 279                      {
 280                          BlitDepthStencil(
 281                              gd,
 282                              cbs,
 283                              srcView,
 284                              dstView,
 285                              mipSrcRegion,
 286                              mipDstRegion);
 287                      }
 288                      else
 289                      {
 290                          BlitColor(
 291                              gd,
 292                              cbs,
 293                              srcView,
 294                              dstView,
 295                              mipSrcRegion,
 296                              mipDstRegion,
 297                              linearFilter,
 298                              clearAlpha);
 299                      }
 300  
 301                      if (srcView != src)
 302                      {
 303                          srcView.Release();
 304                      }
 305  
 306                      if (dstView != dst)
 307                      {
 308                          dstView.Release();
 309                      }
 310                  }
 311              }
 312          }
 313  
 314          public void CopyColor(
 315              VulkanRenderer gd,
 316              CommandBufferScoped cbs,
 317              TextureView src,
 318              TextureView dst,
 319              int srcLayer,
 320              int dstLayer,
 321              int srcLevel,
 322              int dstLevel,
 323              int depth,
 324              int levels)
 325          {
 326              for (int l = 0; l < levels; l++)
 327              {
 328                  int mipSrcLevel = srcLevel + l;
 329                  int mipDstLevel = dstLevel + l;
 330  
 331                  int srcWidth = Math.Max(1, src.Width >> mipSrcLevel);
 332                  int srcHeight = Math.Max(1, src.Height >> mipSrcLevel);
 333  
 334                  int dstWidth = Math.Max(1, dst.Width >> mipDstLevel);
 335                  int dstHeight = Math.Max(1, dst.Height >> mipDstLevel);
 336  
 337                  var extents = new Extents2D(
 338                      0,
 339                      0,
 340                      Math.Min(srcWidth, dstWidth),
 341                      Math.Min(srcHeight, dstHeight));
 342  
 343                  for (int z = 0; z < depth; z++)
 344                  {
 345                      var srcView = Create2DLayerView(src, srcLayer + z, mipSrcLevel);
 346                      var dstView = Create2DLayerView(dst, dstLayer + z, mipDstLevel);
 347  
 348                      BlitColor(
 349                          gd,
 350                          cbs,
 351                          srcView,
 352                          dstView,
 353                          extents,
 354                          extents,
 355                          false);
 356  
 357                      if (srcView != src)
 358                      {
 359                          srcView.Release();
 360                      }
 361  
 362                      if (dstView != dst)
 363                      {
 364                          dstView.Release();
 365                      }
 366                  }
 367              }
 368          }
 369  
 370          public void BlitColor(
 371              VulkanRenderer gd,
 372              CommandBufferScoped cbs,
 373              TextureView src,
 374              TextureView dst,
 375              Extents2D srcRegion,
 376              Extents2D dstRegion,
 377              bool linearFilter,
 378              bool clearAlpha = false)
 379          {
 380              _pipeline.SetCommandBuffer(cbs);
 381  
 382              const int RegionBufferSize = 16;
 383  
 384              var sampler = linearFilter ? _samplerLinear : _samplerNearest;
 385  
 386              _pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Fragment, 0, src, sampler);
 387  
 388              Span<float> region = stackalloc float[RegionBufferSize / sizeof(float)];
 389  
 390              region[0] = (float)srcRegion.X1 / src.Width;
 391              region[1] = (float)srcRegion.X2 / src.Width;
 392              region[2] = (float)srcRegion.Y1 / src.Height;
 393              region[3] = (float)srcRegion.Y2 / src.Height;
 394  
 395              if (dstRegion.X1 > dstRegion.X2)
 396              {
 397                  (region[0], region[1]) = (region[1], region[0]);
 398              }
 399  
 400              if (dstRegion.Y1 > dstRegion.Y2)
 401              {
 402                  (region[2], region[3]) = (region[3], region[2]);
 403              }
 404  
 405              using var buffer = gd.BufferManager.ReserveOrCreate(gd, cbs, RegionBufferSize);
 406  
 407              buffer.Holder.SetDataUnchecked<float>(buffer.Offset, region);
 408  
 409              _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, buffer.Range) });
 410  
 411              Span<Viewport> viewports = stackalloc Viewport[1];
 412  
 413              var rect = new Rectangle<float>(
 414                  MathF.Min(dstRegion.X1, dstRegion.X2),
 415                  MathF.Min(dstRegion.Y1, dstRegion.Y2),
 416                  MathF.Abs(dstRegion.X2 - dstRegion.X1),
 417                  MathF.Abs(dstRegion.Y2 - dstRegion.Y1));
 418  
 419              viewports[0] = new Viewport(
 420                  rect,
 421                  ViewportSwizzle.PositiveX,
 422                  ViewportSwizzle.PositiveY,
 423                  ViewportSwizzle.PositiveZ,
 424                  ViewportSwizzle.PositiveW,
 425                  0f,
 426                  1f);
 427  
 428              bool dstIsDepthOrStencil = dst.Info.Format.IsDepthOrStencil();
 429  
 430              if (dstIsDepthOrStencil)
 431              {
 432                  _pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programDepthBlitMs : _programDepthBlit);
 433                  _pipeline.SetDepthTest(new DepthTestDescriptor(true, true, CompareOp.Always));
 434              }
 435              else if (src.Info.Target.IsMultisample())
 436              {
 437                  _pipeline.SetProgram(_programColorBlitMs);
 438              }
 439              else if (clearAlpha)
 440              {
 441                  _pipeline.SetProgram(_programColorBlitClearAlpha);
 442              }
 443              else
 444              {
 445                  _pipeline.SetProgram(_programColorBlit);
 446              }
 447  
 448              int dstWidth = dst.Width;
 449              int dstHeight = dst.Height;
 450  
 451              _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight);
 452              _pipeline.SetRenderTargetColorMasks(new uint[] { 0xf });
 453              _pipeline.SetScissors(stackalloc Rectangle<int>[] { new Rectangle<int>(0, 0, dstWidth, dstHeight) });
 454  
 455              if (clearAlpha)
 456              {
 457                  _pipeline.ClearRenderTargetColor(0, 0, 1, new ColorF(0f, 0f, 0f, 1f));
 458              }
 459  
 460              _pipeline.SetViewports(viewports);
 461              _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
 462              _pipeline.Draw(4, 1, 0, 0);
 463  
 464              if (dstIsDepthOrStencil)
 465              {
 466                  _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always));
 467              }
 468  
 469              _pipeline.Finish(gd, cbs);
 470          }
 471  
 472          private void BlitDepthStencil(
 473              VulkanRenderer gd,
 474              CommandBufferScoped cbs,
 475              TextureView src,
 476              TextureView dst,
 477              Extents2D srcRegion,
 478              Extents2D dstRegion)
 479          {
 480              _pipeline.SetCommandBuffer(cbs);
 481  
 482              const int RegionBufferSize = 16;
 483  
 484              Span<float> region = stackalloc float[RegionBufferSize / sizeof(float)];
 485  
 486              region[0] = (float)srcRegion.X1 / src.Width;
 487              region[1] = (float)srcRegion.X2 / src.Width;
 488              region[2] = (float)srcRegion.Y1 / src.Height;
 489              region[3] = (float)srcRegion.Y2 / src.Height;
 490  
 491              if (dstRegion.X1 > dstRegion.X2)
 492              {
 493                  (region[0], region[1]) = (region[1], region[0]);
 494              }
 495  
 496              if (dstRegion.Y1 > dstRegion.Y2)
 497              {
 498                  (region[2], region[3]) = (region[3], region[2]);
 499              }
 500  
 501              using var buffer = gd.BufferManager.ReserveOrCreate(gd, cbs, RegionBufferSize);
 502  
 503              buffer.Holder.SetDataUnchecked<float>(buffer.Offset, region);
 504  
 505              _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, buffer.Range) });
 506  
 507              Span<Viewport> viewports = stackalloc Viewport[1];
 508  
 509              var rect = new Rectangle<float>(
 510                  MathF.Min(dstRegion.X1, dstRegion.X2),
 511                  MathF.Min(dstRegion.Y1, dstRegion.Y2),
 512                  MathF.Abs(dstRegion.X2 - dstRegion.X1),
 513                  MathF.Abs(dstRegion.Y2 - dstRegion.Y1));
 514  
 515              viewports[0] = new Viewport(
 516                  rect,
 517                  ViewportSwizzle.PositiveX,
 518                  ViewportSwizzle.PositiveY,
 519                  ViewportSwizzle.PositiveZ,
 520                  ViewportSwizzle.PositiveW,
 521                  0f,
 522                  1f);
 523  
 524              int dstWidth = dst.Width;
 525              int dstHeight = dst.Height;
 526  
 527              _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight);
 528              _pipeline.SetScissors(stackalloc Rectangle<int>[] { new Rectangle<int>(0, 0, dstWidth, dstHeight) });
 529              _pipeline.SetViewports(viewports);
 530              _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
 531  
 532              var aspectFlags = src.Info.Format.ConvertAspectFlags();
 533  
 534              if (aspectFlags.HasFlag(ImageAspectFlags.DepthBit))
 535              {
 536                  var depthTexture = CreateDepthOrStencilView(src, DepthStencilMode.Depth);
 537  
 538                  BlitDepthStencilDraw(depthTexture, isDepth: true);
 539  
 540                  if (depthTexture != src)
 541                  {
 542                      depthTexture.Release();
 543                  }
 544              }
 545  
 546              if (aspectFlags.HasFlag(ImageAspectFlags.StencilBit) && _programStencilBlit != null)
 547              {
 548                  var stencilTexture = CreateDepthOrStencilView(src, DepthStencilMode.Stencil);
 549  
 550                  BlitDepthStencilDraw(stencilTexture, isDepth: false);
 551  
 552                  if (stencilTexture != src)
 553                  {
 554                      stencilTexture.Release();
 555                  }
 556              }
 557  
 558              _pipeline.Finish(gd, cbs);
 559          }
 560  
 561          private static TextureView CreateDepthOrStencilView(TextureView depthStencilTexture, DepthStencilMode depthStencilMode)
 562          {
 563              if (depthStencilTexture.Info.DepthStencilMode == depthStencilMode)
 564              {
 565                  return depthStencilTexture;
 566              }
 567  
 568              return (TextureView)depthStencilTexture.CreateView(new TextureCreateInfo(
 569                  depthStencilTexture.Info.Width,
 570                  depthStencilTexture.Info.Height,
 571                  depthStencilTexture.Info.Depth,
 572                  depthStencilTexture.Info.Levels,
 573                  depthStencilTexture.Info.Samples,
 574                  depthStencilTexture.Info.BlockWidth,
 575                  depthStencilTexture.Info.BlockHeight,
 576                  depthStencilTexture.Info.BytesPerPixel,
 577                  depthStencilTexture.Info.Format,
 578                  depthStencilMode,
 579                  depthStencilTexture.Info.Target,
 580                  SwizzleComponent.Red,
 581                  SwizzleComponent.Green,
 582                  SwizzleComponent.Blue,
 583                  SwizzleComponent.Alpha), 0, 0);
 584          }
 585  
 586          private void BlitDepthStencilDraw(TextureView src, bool isDepth)
 587          {
 588              _pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Fragment, 0, src, _samplerNearest);
 589  
 590              if (isDepth)
 591              {
 592                  _pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programDepthBlitMs : _programDepthBlit);
 593                  _pipeline.SetDepthTest(new DepthTestDescriptor(true, true, CompareOp.Always));
 594              }
 595              else
 596              {
 597                  _pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programStencilBlitMs : _programStencilBlit);
 598                  _pipeline.SetStencilTest(CreateStencilTestDescriptor(true));
 599              }
 600  
 601              _pipeline.Draw(4, 1, 0, 0);
 602  
 603              if (isDepth)
 604              {
 605                  _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always));
 606              }
 607              else
 608              {
 609                  _pipeline.SetStencilTest(CreateStencilTestDescriptor(false));
 610              }
 611          }
 612  
 613          private static StencilTestDescriptor CreateStencilTestDescriptor(
 614              bool enabled,
 615              int refValue = 0,
 616              int compareMask = 0xff,
 617              int writeMask = 0xff)
 618          {
 619              return new StencilTestDescriptor(
 620                  enabled,
 621                  CompareOp.Always,
 622                  StencilOp.Replace,
 623                  StencilOp.Replace,
 624                  StencilOp.Replace,
 625                  refValue,
 626                  compareMask,
 627                  writeMask,
 628                  CompareOp.Always,
 629                  StencilOp.Replace,
 630                  StencilOp.Replace,
 631                  StencilOp.Replace,
 632                  refValue,
 633                  compareMask,
 634                  writeMask);
 635          }
 636  
 637          public void Clear(
 638              VulkanRenderer gd,
 639              TextureView dst,
 640              ReadOnlySpan<float> clearColor,
 641              uint componentMask,
 642              int dstWidth,
 643              int dstHeight,
 644              ComponentType type,
 645              Rectangle<int> scissor)
 646          {
 647              const int ClearColorBufferSize = 16;
 648  
 649              gd.FlushAllCommands();
 650  
 651              using var cbs = gd.CommandBufferPool.Rent();
 652  
 653              _pipeline.SetCommandBuffer(cbs);
 654  
 655              using var buffer = gd.BufferManager.ReserveOrCreate(gd, cbs, ClearColorBufferSize);
 656  
 657              buffer.Holder.SetDataUnchecked(buffer.Offset, clearColor);
 658  
 659              _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, buffer.Range) });
 660  
 661              Span<Viewport> viewports = stackalloc Viewport[1];
 662  
 663              viewports[0] = new Viewport(
 664                  new Rectangle<float>(0, 0, dstWidth, dstHeight),
 665                  ViewportSwizzle.PositiveX,
 666                  ViewportSwizzle.PositiveY,
 667                  ViewportSwizzle.PositiveZ,
 668                  ViewportSwizzle.PositiveW,
 669                  0f,
 670                  1f);
 671  
 672              IProgram program;
 673  
 674              if (type == ComponentType.SignedInteger)
 675              {
 676                  program = _programColorClearSI;
 677              }
 678              else if (type == ComponentType.UnsignedInteger)
 679              {
 680                  program = _programColorClearUI;
 681              }
 682              else
 683              {
 684                  program = _programColorClearF;
 685              }
 686  
 687              _pipeline.SetProgram(program);
 688              _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight);
 689              _pipeline.SetRenderTargetColorMasks(new[] { componentMask });
 690              _pipeline.SetViewports(viewports);
 691              _pipeline.SetScissors(stackalloc Rectangle<int>[] { scissor });
 692              _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
 693              _pipeline.Draw(4, 1, 0, 0);
 694              _pipeline.Finish();
 695          }
 696  
 697          public void Clear(
 698              VulkanRenderer gd,
 699              TextureView dst,
 700              float depthValue,
 701              bool depthMask,
 702              int stencilValue,
 703              int stencilMask,
 704              int dstWidth,
 705              int dstHeight,
 706              VkFormat dstFormat,
 707              Rectangle<int> scissor)
 708          {
 709              const int ClearColorBufferSize = 16;
 710  
 711              gd.FlushAllCommands();
 712  
 713              using var cbs = gd.CommandBufferPool.Rent();
 714  
 715              _pipeline.SetCommandBuffer(cbs);
 716  
 717              using var buffer = gd.BufferManager.ReserveOrCreate(gd, cbs, ClearColorBufferSize);
 718  
 719              buffer.Holder.SetDataUnchecked<float>(buffer.Offset, stackalloc float[] { depthValue });
 720  
 721              _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, buffer.Range) });
 722  
 723              Span<Viewport> viewports = stackalloc Viewport[1];
 724  
 725              viewports[0] = new Viewport(
 726                  new Rectangle<float>(0, 0, dstWidth, dstHeight),
 727                  ViewportSwizzle.PositiveX,
 728                  ViewportSwizzle.PositiveY,
 729                  ViewportSwizzle.PositiveZ,
 730                  ViewportSwizzle.PositiveW,
 731                  0f,
 732                  1f);
 733  
 734              _pipeline.SetProgram(_programDepthStencilClear);
 735              _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight);
 736              _pipeline.SetViewports(viewports);
 737              _pipeline.SetScissors(stackalloc Rectangle<int>[] { scissor });
 738              _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
 739              _pipeline.SetDepthTest(new DepthTestDescriptor(true, depthMask, CompareOp.Always));
 740              _pipeline.SetStencilTest(CreateStencilTestDescriptor(stencilMask != 0, stencilValue, 0xff, stencilMask));
 741              _pipeline.Draw(4, 1, 0, 0);
 742              _pipeline.Finish();
 743          }
 744  
 745          public void DrawTexture(
 746              VulkanRenderer gd,
 747              PipelineBase pipeline,
 748              TextureView src,
 749              ISampler srcSampler,
 750              Extents2DF srcRegion,
 751              Extents2DF dstRegion)
 752          {
 753              const int RegionBufferSize = 16;
 754  
 755              pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, srcSampler);
 756  
 757              Span<float> region = stackalloc float[RegionBufferSize / sizeof(float)];
 758  
 759              region[0] = srcRegion.X1 / src.Width;
 760              region[1] = srcRegion.X2 / src.Width;
 761              region[2] = srcRegion.Y1 / src.Height;
 762              region[3] = srcRegion.Y2 / src.Height;
 763  
 764              if (dstRegion.X1 > dstRegion.X2)
 765              {
 766                  (region[0], region[1]) = (region[1], region[0]);
 767              }
 768  
 769              if (dstRegion.Y1 > dstRegion.Y2)
 770              {
 771                  (region[2], region[3]) = (region[3], region[2]);
 772              }
 773  
 774              var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize);
 775  
 776              gd.BufferManager.SetData<float>(bufferHandle, 0, region);
 777  
 778              pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, new BufferRange(bufferHandle, 0, RegionBufferSize)) });
 779  
 780              Span<Viewport> viewports = stackalloc Viewport[1];
 781  
 782              var rect = new Rectangle<float>(
 783                  MathF.Min(dstRegion.X1, dstRegion.X2),
 784                  MathF.Min(dstRegion.Y1, dstRegion.Y2),
 785                  MathF.Abs(dstRegion.X2 - dstRegion.X1),
 786                  MathF.Abs(dstRegion.Y2 - dstRegion.Y1));
 787  
 788              viewports[0] = new Viewport(
 789                  rect,
 790                  ViewportSwizzle.PositiveX,
 791                  ViewportSwizzle.PositiveY,
 792                  ViewportSwizzle.PositiveZ,
 793                  ViewportSwizzle.PositiveW,
 794                  0f,
 795                  1f);
 796  
 797              pipeline.SetProgram(_programColorBlit);
 798              pipeline.SetViewports(viewports);
 799              pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
 800              pipeline.Draw(4, 1, 0, 0);
 801  
 802              gd.BufferManager.Delete(bufferHandle);
 803          }
 804  
 805          public void ConvertI8ToI16(VulkanRenderer gd, CommandBufferScoped cbs, BufferHolder src, BufferHolder dst, int srcOffset, int size)
 806          {
 807              ChangeStride(gd, cbs, src, dst, srcOffset, size, 1, 2);
 808          }
 809  
 810          public unsafe void ChangeStride(VulkanRenderer gd, CommandBufferScoped cbs, BufferHolder src, BufferHolder dst, int srcOffset, int size, int stride, int newStride)
 811          {
 812              bool supportsUint8 = gd.Capabilities.SupportsShaderInt8;
 813  
 814              int elems = size / stride;
 815              int newSize = elems * newStride;
 816  
 817              var srcBufferAuto = src.GetBuffer();
 818              var dstBufferAuto = dst.GetBuffer();
 819  
 820              var srcBuffer = srcBufferAuto.Get(cbs, srcOffset, size).Value;
 821              var dstBuffer = dstBufferAuto.Get(cbs, 0, newSize).Value;
 822  
 823              var access = supportsUint8 ? AccessFlags.ShaderWriteBit : AccessFlags.TransferWriteBit;
 824              var stage = supportsUint8 ? PipelineStageFlags.ComputeShaderBit : PipelineStageFlags.TransferBit;
 825  
 826              BufferHolder.InsertBufferBarrier(
 827                  gd,
 828                  cbs.CommandBuffer,
 829                  dstBuffer,
 830                  BufferHolder.DefaultAccessFlags,
 831                  access,
 832                  PipelineStageFlags.AllCommandsBit,
 833                  stage,
 834                  0,
 835                  newSize);
 836  
 837              if (supportsUint8)
 838              {
 839                  const int ParamsBufferSize = 16;
 840  
 841                  Span<int> shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)];
 842  
 843                  shaderParams[0] = stride;
 844                  shaderParams[1] = newStride;
 845                  shaderParams[2] = size;
 846                  shaderParams[3] = srcOffset;
 847  
 848                  using var buffer = gd.BufferManager.ReserveOrCreate(gd, cbs, ParamsBufferSize);
 849  
 850                  buffer.Holder.SetDataUnchecked<int>(buffer.Offset, shaderParams);
 851  
 852                  _pipeline.SetCommandBuffer(cbs);
 853  
 854                  _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, buffer.Range) });
 855  
 856                  Span<Auto<DisposableBuffer>> sbRanges = new Auto<DisposableBuffer>[2];
 857  
 858                  sbRanges[0] = srcBufferAuto;
 859                  sbRanges[1] = dstBufferAuto;
 860  
 861                  _pipeline.SetStorageBuffers(1, sbRanges);
 862  
 863                  _pipeline.SetProgram(_programStrideChange);
 864                  _pipeline.DispatchCompute(1 + elems / ConvertElementsPerWorkgroup, 1, 1);
 865  
 866                  _pipeline.Finish(gd, cbs);
 867              }
 868              else
 869              {
 870                  gd.Api.CmdFillBuffer(cbs.CommandBuffer, dstBuffer, 0, Vk.WholeSize, 0);
 871  
 872                  var bufferCopy = new BufferCopy[elems];
 873  
 874                  for (ulong i = 0; i < (ulong)elems; i++)
 875                  {
 876                      bufferCopy[i] = new BufferCopy((ulong)srcOffset + i * (ulong)stride, i * (ulong)newStride, (ulong)stride);
 877                  }
 878  
 879                  fixed (BufferCopy* pBufferCopy = bufferCopy)
 880                  {
 881                      gd.Api.CmdCopyBuffer(cbs.CommandBuffer, srcBuffer, dstBuffer, (uint)elems, pBufferCopy);
 882                  }
 883              }
 884  
 885              BufferHolder.InsertBufferBarrier(
 886                  gd,
 887                  cbs.CommandBuffer,
 888                  dstBuffer,
 889                  access,
 890                  BufferHolder.DefaultAccessFlags,
 891                  stage,
 892                  PipelineStageFlags.AllCommandsBit,
 893                  0,
 894                  newSize);
 895          }
 896  
 897          public unsafe void ConvertIndexBuffer(VulkanRenderer gd,
 898              CommandBufferScoped cbs,
 899              BufferHolder src,
 900              BufferHolder dst,
 901              IndexBufferPattern pattern,
 902              int indexSize,
 903              int srcOffset,
 904              int indexCount)
 905          {
 906              // TODO: Support conversion with primitive restart enabled.
 907              // TODO: Convert with a compute shader?
 908  
 909              int convertedCount = pattern.GetConvertedCount(indexCount);
 910              int outputIndexSize = 4;
 911  
 912              var srcBuffer = src.GetBuffer().Get(cbs, srcOffset, indexCount * indexSize).Value;
 913              var dstBuffer = dst.GetBuffer().Get(cbs, 0, convertedCount * outputIndexSize).Value;
 914  
 915              gd.Api.CmdFillBuffer(cbs.CommandBuffer, dstBuffer, 0, Vk.WholeSize, 0);
 916  
 917              var bufferCopy = new List<BufferCopy>();
 918              int outputOffset = 0;
 919  
 920              // Try to merge copies of adjacent indices to reduce copy count.
 921              int sequenceStart = 0;
 922              int sequenceLength = 0;
 923  
 924              foreach (var index in pattern.GetIndexMapping(indexCount))
 925              {
 926                  if (sequenceLength > 0)
 927                  {
 928                      if (index == sequenceStart + sequenceLength && indexSize == outputIndexSize)
 929                      {
 930                          sequenceLength++;
 931                          continue;
 932                      }
 933  
 934                      // Commit the copy so far.
 935                      bufferCopy.Add(new BufferCopy((ulong)(srcOffset + sequenceStart * indexSize), (ulong)outputOffset, (ulong)(indexSize * sequenceLength)));
 936                      outputOffset += outputIndexSize * sequenceLength;
 937                  }
 938  
 939                  sequenceStart = index;
 940                  sequenceLength = 1;
 941              }
 942  
 943              if (sequenceLength > 0)
 944              {
 945                  // Commit final pending copy.
 946                  bufferCopy.Add(new BufferCopy((ulong)(srcOffset + sequenceStart * indexSize), (ulong)outputOffset, (ulong)(indexSize * sequenceLength)));
 947              }
 948  
 949              var bufferCopyArray = bufferCopy.ToArray();
 950  
 951              BufferHolder.InsertBufferBarrier(
 952                  gd,
 953                  cbs.CommandBuffer,
 954                  dstBuffer,
 955                  BufferHolder.DefaultAccessFlags,
 956                  AccessFlags.TransferWriteBit,
 957                  PipelineStageFlags.AllCommandsBit,
 958                  PipelineStageFlags.TransferBit,
 959                  0,
 960                  convertedCount * outputIndexSize);
 961  
 962              fixed (BufferCopy* pBufferCopy = bufferCopyArray)
 963              {
 964                  gd.Api.CmdCopyBuffer(cbs.CommandBuffer, srcBuffer, dstBuffer, (uint)bufferCopyArray.Length, pBufferCopy);
 965              }
 966  
 967              BufferHolder.InsertBufferBarrier(
 968                  gd,
 969                  cbs.CommandBuffer,
 970                  dstBuffer,
 971                  AccessFlags.TransferWriteBit,
 972                  BufferHolder.DefaultAccessFlags,
 973                  PipelineStageFlags.TransferBit,
 974                  PipelineStageFlags.AllCommandsBit,
 975                  0,
 976                  convertedCount * outputIndexSize);
 977          }
 978  
 979          public void CopyIncompatibleFormats(
 980              VulkanRenderer gd,
 981              CommandBufferScoped cbs,
 982              TextureView src,
 983              TextureView dst,
 984              int srcLayer,
 985              int dstLayer,
 986              int srcLevel,
 987              int dstLevel,
 988              int depth,
 989              int levels)
 990          {
 991              const int ParamsBufferSize = 4;
 992  
 993              Span<int> shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)];
 994  
 995              int srcBpp = src.Info.BytesPerPixel;
 996              int dstBpp = dst.Info.BytesPerPixel;
 997  
 998              int ratio = srcBpp < dstBpp ? dstBpp / srcBpp : srcBpp / dstBpp;
 999  
1000              shaderParams[0] = BitOperations.Log2((uint)ratio);
1001  
1002              using var buffer = gd.BufferManager.ReserveOrCreate(gd, cbs, ParamsBufferSize);
1003  
1004              buffer.Holder.SetDataUnchecked<int>(buffer.Offset, shaderParams);
1005  
1006              TextureView.InsertImageBarrier(
1007                  gd.Api,
1008                  cbs.CommandBuffer,
1009                  src.GetImage().Get(cbs).Value,
1010                  TextureStorage.DefaultAccessMask,
1011                  AccessFlags.ShaderReadBit,
1012                  PipelineStageFlags.AllCommandsBit,
1013                  PipelineStageFlags.ComputeShaderBit,
1014                  ImageAspectFlags.ColorBit,
1015                  src.FirstLayer + srcLayer,
1016                  src.FirstLevel + srcLevel,
1017                  depth,
1018                  levels);
1019  
1020              _pipeline.SetCommandBuffer(cbs);
1021  
1022              _pipeline.SetProgram(srcBpp < dstBpp ? _programColorCopyWidening : _programColorCopyShortening);
1023  
1024              // Calculate ideal component size, given our constraints:
1025              // - Component size must not exceed bytes per pixel of source and destination image formats.
1026              // - Maximum component size is 4 (R32).
1027              int componentSize = Math.Min(Math.Min(srcBpp, dstBpp), 4);
1028  
1029              var srcFormat = GetFormat(componentSize, srcBpp / componentSize);
1030              var dstFormat = GetFormat(componentSize, dstBpp / componentSize);
1031  
1032              _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, buffer.Range) });
1033  
1034              for (int l = 0; l < levels; l++)
1035              {
1036                  for (int z = 0; z < depth; z++)
1037                  {
1038                      var srcView = Create2DLayerView(src, srcLayer + z, srcLevel + l, srcFormat);
1039                      var dstView = Create2DLayerView(dst, dstLayer + z, dstLevel + l);
1040  
1041                      _pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Compute, 0, srcView, null);
1042                      _pipeline.SetImage(ShaderStage.Compute, 0, dstView.GetView(dstFormat));
1043  
1044                      int dispatchX = (Math.Min(srcView.Info.Width, dstView.Info.Width) + 31) / 32;
1045                      int dispatchY = (Math.Min(srcView.Info.Height, dstView.Info.Height) + 31) / 32;
1046  
1047                      _pipeline.DispatchCompute(dispatchX, dispatchY, 1);
1048  
1049                      if (srcView != src)
1050                      {
1051                          srcView.Release();
1052                      }
1053  
1054                      if (dstView != dst)
1055                      {
1056                          dstView.Release();
1057                      }
1058                  }
1059              }
1060  
1061              _pipeline.Finish(gd, cbs);
1062  
1063              TextureView.InsertImageBarrier(
1064                  gd.Api,
1065                  cbs.CommandBuffer,
1066                  dst.GetImage().Get(cbs).Value,
1067                  AccessFlags.ShaderWriteBit,
1068                  TextureStorage.DefaultAccessMask,
1069                  PipelineStageFlags.ComputeShaderBit,
1070                  PipelineStageFlags.AllCommandsBit,
1071                  ImageAspectFlags.ColorBit,
1072                  dst.FirstLayer + dstLayer,
1073                  dst.FirstLevel + dstLevel,
1074                  depth,
1075                  levels);
1076          }
1077  
1078          public void CopyMSToNonMS(VulkanRenderer gd, CommandBufferScoped cbs, TextureView src, TextureView dst, int srcLayer, int dstLayer, int depth)
1079          {
1080              const int ParamsBufferSize = 16;
1081  
1082              Span<int> shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)];
1083  
1084              int samples = src.Info.Samples;
1085              bool isDepthOrStencil = src.Info.Format.IsDepthOrStencil();
1086              var aspectFlags = src.Info.Format.ConvertAspectFlags();
1087  
1088              // X and Y are the expected texture samples.
1089              // Z and W are the actual texture samples used.
1090              // They may differ if the GPU does not support the samples count requested and we had to use a lower amount.
1091              (shaderParams[0], shaderParams[1]) = GetSampleCountXYLog2(samples);
1092              (shaderParams[2], shaderParams[3]) = GetSampleCountXYLog2((int)TextureStorage.ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)samples));
1093  
1094              using var buffer = gd.BufferManager.ReserveOrCreate(gd, cbs, ParamsBufferSize);
1095  
1096              buffer.Holder.SetDataUnchecked<int>(buffer.Offset, shaderParams);
1097  
1098              TextureView.InsertImageBarrier(
1099                  gd.Api,
1100                  cbs.CommandBuffer,
1101                  src.GetImage().Get(cbs).Value,
1102                  TextureStorage.DefaultAccessMask,
1103                  AccessFlags.ShaderReadBit,
1104                  PipelineStageFlags.AllCommandsBit,
1105                  isDepthOrStencil ? PipelineStageFlags.FragmentShaderBit : PipelineStageFlags.ComputeShaderBit,
1106                  aspectFlags,
1107                  src.FirstLayer + srcLayer,
1108                  src.FirstLevel,
1109                  depth,
1110                  1);
1111  
1112              _pipeline.SetCommandBuffer(cbs);
1113              _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, buffer.Range) });
1114  
1115              if (isDepthOrStencil)
1116              {
1117                  // We can't use compute for this case because compute can't modify depth textures.
1118  
1119                  Span<Viewport> viewports = stackalloc Viewport[1];
1120  
1121                  var rect = new Rectangle<float>(0, 0, dst.Width, dst.Height);
1122  
1123                  viewports[0] = new Viewport(
1124                      rect,
1125                      ViewportSwizzle.PositiveX,
1126                      ViewportSwizzle.PositiveY,
1127                      ViewportSwizzle.PositiveZ,
1128                      ViewportSwizzle.PositiveW,
1129                      0f,
1130                      1f);
1131  
1132                  _pipeline.SetScissors(stackalloc Rectangle<int>[] { new Rectangle<int>(0, 0, dst.Width, dst.Height) });
1133                  _pipeline.SetViewports(viewports);
1134                  _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
1135  
1136                  for (int z = 0; z < depth; z++)
1137                  {
1138                      var srcView = Create2DLayerView(src, srcLayer + z, 0);
1139                      var dstView = Create2DLayerView(dst, dstLayer + z, 0);
1140  
1141                      _pipeline.SetRenderTarget(dstView, (uint)dst.Width, (uint)dst.Height);
1142  
1143                      CopyMSDraw(srcView, aspectFlags, fromMS: true);
1144  
1145                      if (srcView != src)
1146                      {
1147                          srcView.Release();
1148                      }
1149  
1150                      if (dstView != dst)
1151                      {
1152                          dstView.Release();
1153                      }
1154                  }
1155              }
1156              else
1157              {
1158                  var format = GetFormat(src.Info.BytesPerPixel);
1159  
1160                  int dispatchX = (dst.Info.Width + 31) / 32;
1161                  int dispatchY = (dst.Info.Height + 31) / 32;
1162  
1163                  _pipeline.SetProgram(_programColorCopyToNonMs);
1164  
1165                  for (int z = 0; z < depth; z++)
1166                  {
1167                      var srcView = Create2DLayerView(src, srcLayer + z, 0, format);
1168                      var dstView = Create2DLayerView(dst, dstLayer + z, 0);
1169  
1170                      _pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Compute, 0, srcView, null);
1171                      _pipeline.SetImage(ShaderStage.Compute, 0, dstView.GetView(format));
1172  
1173                      _pipeline.DispatchCompute(dispatchX, dispatchY, 1);
1174  
1175                      if (srcView != src)
1176                      {
1177                          srcView.Release();
1178                      }
1179  
1180                      if (dstView != dst)
1181                      {
1182                          dstView.Release();
1183                      }
1184                  }
1185              }
1186  
1187              _pipeline.Finish(gd, cbs);
1188  
1189              TextureView.InsertImageBarrier(
1190                  gd.Api,
1191                  cbs.CommandBuffer,
1192                  dst.GetImage().Get(cbs).Value,
1193                  isDepthOrStencil ? AccessFlags.DepthStencilAttachmentWriteBit : AccessFlags.ShaderWriteBit,
1194                  TextureStorage.DefaultAccessMask,
1195                  isDepthOrStencil ? PipelineStageFlags.LateFragmentTestsBit : PipelineStageFlags.ComputeShaderBit,
1196                  PipelineStageFlags.AllCommandsBit,
1197                  aspectFlags,
1198                  dst.FirstLayer + dstLayer,
1199                  dst.FirstLevel,
1200                  depth,
1201                  1);
1202          }
1203  
1204          public void CopyNonMSToMS(VulkanRenderer gd, CommandBufferScoped cbs, TextureView src, TextureView dst, int srcLayer, int dstLayer, int depth)
1205          {
1206              const int ParamsBufferSize = 16;
1207  
1208              Span<int> shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)];
1209  
1210              int samples = dst.Info.Samples;
1211              bool isDepthOrStencil = src.Info.Format.IsDepthOrStencil();
1212              var aspectFlags = src.Info.Format.ConvertAspectFlags();
1213  
1214              // X and Y are the expected texture samples.
1215              // Z and W are the actual texture samples used.
1216              // They may differ if the GPU does not support the samples count requested and we had to use a lower amount.
1217              (shaderParams[0], shaderParams[1]) = GetSampleCountXYLog2(samples);
1218              (shaderParams[2], shaderParams[3]) = GetSampleCountXYLog2((int)TextureStorage.ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)samples));
1219  
1220              using var buffer = gd.BufferManager.ReserveOrCreate(gd, cbs, ParamsBufferSize);
1221  
1222              buffer.Holder.SetDataUnchecked<int>(buffer.Offset, shaderParams);
1223  
1224              TextureView.InsertImageBarrier(
1225                  gd.Api,
1226                  cbs.CommandBuffer,
1227                  src.GetImage().Get(cbs).Value,
1228                  TextureStorage.DefaultAccessMask,
1229                  AccessFlags.ShaderReadBit,
1230                  PipelineStageFlags.AllCommandsBit,
1231                  PipelineStageFlags.FragmentShaderBit,
1232                  aspectFlags,
1233                  src.FirstLayer + srcLayer,
1234                  src.FirstLevel,
1235                  depth,
1236                  1);
1237  
1238              _pipeline.SetCommandBuffer(cbs);
1239  
1240              Span<Viewport> viewports = stackalloc Viewport[1];
1241  
1242              var rect = new Rectangle<float>(0, 0, dst.Width, dst.Height);
1243  
1244              viewports[0] = new Viewport(
1245                  rect,
1246                  ViewportSwizzle.PositiveX,
1247                  ViewportSwizzle.PositiveY,
1248                  ViewportSwizzle.PositiveZ,
1249                  ViewportSwizzle.PositiveW,
1250                  0f,
1251                  1f);
1252  
1253              _pipeline.SetRenderTargetColorMasks(new uint[] { 0xf });
1254              _pipeline.SetScissors(stackalloc Rectangle<int>[] { new Rectangle<int>(0, 0, dst.Width, dst.Height) });
1255              _pipeline.SetViewports(viewports);
1256              _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
1257  
1258              _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, buffer.Range) });
1259  
1260              if (isDepthOrStencil)
1261              {
1262                  for (int z = 0; z < depth; z++)
1263                  {
1264                      var srcView = Create2DLayerView(src, srcLayer + z, 0);
1265                      var dstView = Create2DLayerView(dst, dstLayer + z, 0);
1266  
1267                      _pipeline.SetRenderTarget(dstView, (uint)dst.Width, (uint)dst.Height);
1268  
1269                      CopyMSDraw(srcView, aspectFlags, fromMS: false);
1270  
1271                      if (srcView != src)
1272                      {
1273                          srcView.Release();
1274                      }
1275  
1276                      if (dstView != dst)
1277                      {
1278                          dstView.Release();
1279                      }
1280                  }
1281              }
1282              else
1283              {
1284                  _pipeline.SetProgram(_programColorDrawToMs);
1285  
1286                  var format = GetFormat(src.Info.BytesPerPixel);
1287                  var vkFormat = FormatTable.GetFormat(format);
1288  
1289                  for (int z = 0; z < depth; z++)
1290                  {
1291                      var srcView = Create2DLayerView(src, srcLayer + z, 0, format);
1292                      var dstView = Create2DLayerView(dst, dstLayer + z, 0);
1293  
1294                      _pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Fragment, 0, srcView, null);
1295                      _pipeline.SetRenderTarget(dstView.GetView(format), (uint)dst.Width, (uint)dst.Height);
1296  
1297                      _pipeline.Draw(4, 1, 0, 0);
1298  
1299                      if (srcView != src)
1300                      {
1301                          srcView.Release();
1302                      }
1303  
1304                      if (dstView != dst)
1305                      {
1306                          dstView.Release();
1307                      }
1308                  }
1309              }
1310  
1311              _pipeline.Finish(gd, cbs);
1312  
1313              TextureView.InsertImageBarrier(
1314                  gd.Api,
1315                  cbs.CommandBuffer,
1316                  dst.GetImage().Get(cbs).Value,
1317                  isDepthOrStencil ? AccessFlags.DepthStencilAttachmentWriteBit : AccessFlags.ColorAttachmentWriteBit,
1318                  TextureStorage.DefaultAccessMask,
1319                  isDepthOrStencil ? PipelineStageFlags.LateFragmentTestsBit : PipelineStageFlags.ColorAttachmentOutputBit,
1320                  PipelineStageFlags.AllCommandsBit,
1321                  aspectFlags,
1322                  dst.FirstLayer + dstLayer,
1323                  dst.FirstLevel,
1324                  depth,
1325                  1);
1326          }
1327  
1328          private void CopyMSDraw(TextureView src, ImageAspectFlags aspectFlags, bool fromMS)
1329          {
1330              if (aspectFlags.HasFlag(ImageAspectFlags.DepthBit))
1331              {
1332                  var depthTexture = CreateDepthOrStencilView(src, DepthStencilMode.Depth);
1333  
1334                  CopyMSAspectDraw(depthTexture, fromMS, isDepth: true);
1335  
1336                  if (depthTexture != src)
1337                  {
1338                      depthTexture.Release();
1339                  }
1340              }
1341  
1342              if (aspectFlags.HasFlag(ImageAspectFlags.StencilBit) && _programStencilDrawToMs != null)
1343              {
1344                  var stencilTexture = CreateDepthOrStencilView(src, DepthStencilMode.Stencil);
1345  
1346                  CopyMSAspectDraw(stencilTexture, fromMS, isDepth: false);
1347  
1348                  if (stencilTexture != src)
1349                  {
1350                      stencilTexture.Release();
1351                  }
1352              }
1353          }
1354  
1355          private void CopyMSAspectDraw(TextureView src, bool fromMS, bool isDepth)
1356          {
1357              _pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Fragment, 0, src, _samplerNearest);
1358  
1359              if (isDepth)
1360              {
1361                  _pipeline.SetProgram(fromMS ? _programDepthDrawToNonMs : _programDepthDrawToMs);
1362                  _pipeline.SetDepthTest(new DepthTestDescriptor(true, true, CompareOp.Always));
1363              }
1364              else
1365              {
1366                  _pipeline.SetProgram(fromMS ? _programStencilDrawToNonMs : _programStencilDrawToMs);
1367                  _pipeline.SetStencilTest(CreateStencilTestDescriptor(true));
1368              }
1369  
1370              _pipeline.Draw(4, 1, 0, 0);
1371  
1372              if (isDepth)
1373              {
1374                  _pipeline.SetDepthTest(new DepthTestDescriptor(false, false, CompareOp.Always));
1375              }
1376              else
1377              {
1378                  _pipeline.SetStencilTest(CreateStencilTestDescriptor(false));
1379              }
1380          }
1381  
1382          private static (int, int) GetSampleCountXYLog2(int samples)
1383          {
1384              int samplesInXLog2 = 0;
1385              int samplesInYLog2 = 0;
1386  
1387              switch (samples)
1388              {
1389                  case 2: // 2x1
1390                      samplesInXLog2 = 1;
1391                      break;
1392                  case 4: // 2x2
1393                      samplesInXLog2 = 1;
1394                      samplesInYLog2 = 1;
1395                      break;
1396                  case 8: // 4x2
1397                      samplesInXLog2 = 2;
1398                      samplesInYLog2 = 1;
1399                      break;
1400                  case 16: // 4x4
1401                      samplesInXLog2 = 2;
1402                      samplesInYLog2 = 2;
1403                      break;
1404                  case 32: // 8x4
1405                      samplesInXLog2 = 3;
1406                      samplesInYLog2 = 2;
1407                      break;
1408                  case 64: // 8x8
1409                      samplesInXLog2 = 3;
1410                      samplesInYLog2 = 3;
1411                      break;
1412              }
1413  
1414              return (samplesInXLog2, samplesInYLog2);
1415          }
1416  
1417          private static TextureView Create2DLayerView(TextureView from, int layer, int level, Format? format = null)
1418          {
1419              if (from.Info.Target == Target.Texture2D && level == 0 && (format == null || format.Value == from.Info.Format))
1420              {
1421                  return from;
1422              }
1423  
1424              var target = from.Info.Target switch
1425              {
1426                  Target.Texture1DArray => Target.Texture1D,
1427                  Target.Texture2DMultisampleArray => Target.Texture2DMultisample,
1428                  _ => Target.Texture2D,
1429              };
1430  
1431              var info = new TextureCreateInfo(
1432                  Math.Max(1, from.Info.Width >> level),
1433                  Math.Max(1, from.Info.Height >> level),
1434                  1,
1435                  1,
1436                  from.Info.Samples,
1437                  from.Info.BlockWidth,
1438                  from.Info.BlockHeight,
1439                  from.Info.BytesPerPixel,
1440                  format ?? from.Info.Format,
1441                  from.Info.DepthStencilMode,
1442                  target,
1443                  from.Info.SwizzleR,
1444                  from.Info.SwizzleG,
1445                  from.Info.SwizzleB,
1446                  from.Info.SwizzleA);
1447  
1448              return from.CreateViewImpl(info, layer, level);
1449          }
1450  
1451          private static Format GetFormat(int bytesPerPixel)
1452          {
1453              return bytesPerPixel switch
1454              {
1455                  1 => Format.R8Uint,
1456                  2 => Format.R16Uint,
1457                  4 => Format.R32Uint,
1458                  8 => Format.R32G32Uint,
1459                  16 => Format.R32G32B32A32Uint,
1460                  _ => throw new ArgumentException($"Invalid bytes per pixel {bytesPerPixel}."),
1461              };
1462          }
1463  
1464          private static Format GetFormat(int componentSize, int componentsCount)
1465          {
1466              if (componentSize == 1)
1467              {
1468                  return componentsCount switch
1469                  {
1470                      1 => Format.R8Uint,
1471                      2 => Format.R8G8Uint,
1472                      4 => Format.R8G8B8A8Uint,
1473                      _ => throw new ArgumentException($"Invalid components count {componentsCount}."),
1474                  };
1475              }
1476  
1477              if (componentSize == 2)
1478              {
1479                  return componentsCount switch
1480                  {
1481                      1 => Format.R16Uint,
1482                      2 => Format.R16G16Uint,
1483                      4 => Format.R16G16B16A16Uint,
1484                      _ => throw new ArgumentException($"Invalid components count {componentsCount}."),
1485                  };
1486              }
1487  
1488              if (componentSize == 4)
1489              {
1490                  return componentsCount switch
1491                  {
1492                      1 => Format.R32Uint,
1493                      2 => Format.R32G32Uint,
1494                      4 => Format.R32G32B32A32Uint,
1495                      _ => throw new ArgumentException($"Invalid components count {componentsCount}."),
1496                  };
1497              }
1498  
1499              throw new ArgumentException($"Invalid component size {componentSize}.");
1500          }
1501  
1502          public void ConvertIndexBufferIndirect(
1503              VulkanRenderer gd,
1504              CommandBufferScoped cbs,
1505              BufferHolder srcIndirectBuffer,
1506              BufferHolder dstIndirectBuffer,
1507              BufferRange drawCountBuffer,
1508              BufferHolder srcIndexBuffer,
1509              BufferHolder dstIndexBuffer,
1510              IndexBufferPattern pattern,
1511              int indexSize,
1512              int srcIndexBufferOffset,
1513              int srcIndexBufferSize,
1514              int srcIndirectBufferOffset,
1515              bool hasDrawCount,
1516              int maxDrawCount,
1517              int indirectDataStride)
1518          {
1519              // TODO: Support conversion with primitive restart enabled.
1520  
1521              BufferRange drawCountBufferAligned = new(
1522                  drawCountBuffer.Handle,
1523                  drawCountBuffer.Offset & ~(UniformBufferAlignment - 1),
1524                  UniformBufferAlignment);
1525  
1526              int indirectDataSize = maxDrawCount * indirectDataStride;
1527  
1528              int indexCount = srcIndexBufferSize / indexSize;
1529              int primitivesCount = pattern.GetPrimitiveCount(indexCount);
1530              int convertedCount = pattern.GetConvertedCount(indexCount);
1531              int outputIndexSize = 4;
1532  
1533              var srcBuffer = srcIndexBuffer.GetBuffer().Get(cbs, srcIndexBufferOffset, indexCount * indexSize).Value;
1534              var dstBuffer = dstIndexBuffer.GetBuffer().Get(cbs, 0, convertedCount * outputIndexSize).Value;
1535  
1536              const int ParamsBufferSize = 24 * sizeof(int);
1537              const int ParamsIndirectDispatchOffset = 16 * sizeof(int);
1538              const int ParamsIndirectDispatchSize = 3 * sizeof(int);
1539  
1540              Span<int> shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)];
1541  
1542              shaderParams[8] = pattern.PrimitiveVertices;
1543              shaderParams[9] = pattern.PrimitiveVerticesOut;
1544              shaderParams[10] = indexSize;
1545              shaderParams[11] = outputIndexSize;
1546              shaderParams[12] = pattern.BaseIndex;
1547              shaderParams[13] = pattern.IndexStride;
1548              shaderParams[14] = srcIndexBufferOffset;
1549              shaderParams[15] = primitivesCount;
1550              shaderParams[16] = 1;
1551              shaderParams[17] = 1;
1552              shaderParams[18] = 1;
1553              shaderParams[19] = hasDrawCount ? 1 : 0;
1554              shaderParams[20] = maxDrawCount;
1555              shaderParams[21] = (drawCountBuffer.Offset & (UniformBufferAlignment - 1)) / 4;
1556              shaderParams[22] = indirectDataStride / 4;
1557              shaderParams[23] = srcIndirectBufferOffset / 4;
1558  
1559              pattern.OffsetIndex.CopyTo(shaderParams[..pattern.OffsetIndex.Length]);
1560  
1561              using var patternScoped = gd.BufferManager.ReserveOrCreate(gd, cbs, ParamsBufferSize);
1562              var patternBuffer = patternScoped.Holder;
1563              var patternBufferAuto = patternBuffer.GetBuffer();
1564  
1565              patternBuffer.SetDataUnchecked<int>(patternScoped.Offset, shaderParams);
1566  
1567              _pipeline.SetCommandBuffer(cbs);
1568  
1569              BufferHolder.InsertBufferBarrier(
1570                  gd,
1571                  cbs.CommandBuffer,
1572                  srcIndirectBuffer.GetBuffer().Get(cbs, srcIndirectBufferOffset, indirectDataSize).Value,
1573                  BufferHolder.DefaultAccessFlags,
1574                  AccessFlags.ShaderReadBit,
1575                  PipelineStageFlags.AllCommandsBit,
1576                  PipelineStageFlags.ComputeShaderBit,
1577                  srcIndirectBufferOffset,
1578                  indirectDataSize);
1579  
1580              _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, drawCountBufferAligned) });
1581              _pipeline.SetStorageBuffers(1, new[] { srcIndirectBuffer.GetBuffer(), dstIndirectBuffer.GetBuffer() });
1582              _pipeline.SetStorageBuffers(stackalloc[] { new BufferAssignment(3, patternScoped.Range) });
1583  
1584              _pipeline.SetProgram(_programConvertIndirectData);
1585              _pipeline.DispatchCompute(1, 1, 1);
1586  
1587              BufferHolder.InsertBufferBarrier(
1588                  gd,
1589                  cbs.CommandBuffer,
1590                  patternBufferAuto.Get(cbs, patternScoped.Offset + ParamsIndirectDispatchOffset, ParamsIndirectDispatchSize).Value,
1591                  AccessFlags.ShaderWriteBit,
1592                  AccessFlags.IndirectCommandReadBit,
1593                  PipelineStageFlags.ComputeShaderBit,
1594                  PipelineStageFlags.DrawIndirectBit,
1595                  patternScoped.Offset + ParamsIndirectDispatchOffset,
1596                  ParamsIndirectDispatchSize);
1597  
1598              BufferHolder.InsertBufferBarrier(
1599                  gd,
1600                  cbs.CommandBuffer,
1601                  dstBuffer,
1602                  BufferHolder.DefaultAccessFlags,
1603                  AccessFlags.TransferWriteBit,
1604                  PipelineStageFlags.AllCommandsBit,
1605                  PipelineStageFlags.TransferBit,
1606                  0,
1607                  convertedCount * outputIndexSize);
1608  
1609              _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(patternScoped.Handle, patternScoped.Offset, ParamsBufferSize)) });
1610              _pipeline.SetStorageBuffers(1, new[] { srcIndexBuffer.GetBuffer(), dstIndexBuffer.GetBuffer() });
1611  
1612              _pipeline.SetProgram(_programConvertIndexBuffer);
1613              _pipeline.DispatchComputeIndirect(patternBufferAuto, patternScoped.Offset + ParamsIndirectDispatchOffset);
1614  
1615              BufferHolder.InsertBufferBarrier(
1616                  gd,
1617                  cbs.CommandBuffer,
1618                  dstBuffer,
1619                  AccessFlags.TransferWriteBit,
1620                  BufferHolder.DefaultAccessFlags,
1621                  PipelineStageFlags.TransferBit,
1622                  PipelineStageFlags.AllCommandsBit,
1623                  0,
1624                  convertedCount * outputIndexSize);
1625  
1626              _pipeline.Finish(gd, cbs);
1627          }
1628  
1629          public unsafe void ConvertD32S8ToD24S8(VulkanRenderer gd, CommandBufferScoped cbs, BufferHolder src, Auto<DisposableBuffer> dstBufferAuto, int pixelCount, int dstOffset)
1630          {
1631              int inSize = pixelCount * 2 * sizeof(int);
1632              int outSize = pixelCount * sizeof(int);
1633  
1634              var srcBufferAuto = src.GetBuffer();
1635  
1636              var srcBuffer = srcBufferAuto.Get(cbs, 0, inSize).Value;
1637              var dstBuffer = dstBufferAuto.Get(cbs, dstOffset, outSize).Value;
1638  
1639              var access = AccessFlags.ShaderWriteBit;
1640              var stage = PipelineStageFlags.ComputeShaderBit;
1641  
1642              BufferHolder.InsertBufferBarrier(
1643                  gd,
1644                  cbs.CommandBuffer,
1645                  srcBuffer,
1646                  BufferHolder.DefaultAccessFlags,
1647                  AccessFlags.ShaderReadBit,
1648                  PipelineStageFlags.AllCommandsBit,
1649                  stage,
1650                  0,
1651                  outSize);
1652  
1653              BufferHolder.InsertBufferBarrier(
1654                  gd,
1655                  cbs.CommandBuffer,
1656                  dstBuffer,
1657                  BufferHolder.DefaultAccessFlags,
1658                  access,
1659                  PipelineStageFlags.AllCommandsBit,
1660                  stage,
1661                  0,
1662                  outSize);
1663  
1664              const int ParamsBufferSize = sizeof(int) * 2;
1665  
1666              Span<int> shaderParams = stackalloc int[2];
1667  
1668              shaderParams[0] = pixelCount;
1669              shaderParams[1] = dstOffset;
1670  
1671              using var buffer = gd.BufferManager.ReserveOrCreate(gd, cbs, ParamsBufferSize);
1672  
1673              buffer.Holder.SetDataUnchecked<int>(buffer.Offset, shaderParams);
1674  
1675              _pipeline.SetCommandBuffer(cbs);
1676  
1677              _pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, buffer.Range) });
1678  
1679              Span<Auto<DisposableBuffer>> sbRanges = new Auto<DisposableBuffer>[2];
1680  
1681              sbRanges[0] = srcBufferAuto;
1682              sbRanges[1] = dstBufferAuto;
1683  
1684              _pipeline.SetStorageBuffers(1, sbRanges);
1685  
1686              _pipeline.SetProgram(_programConvertD32S8ToD24S8);
1687              _pipeline.DispatchCompute(1 + inSize / ConvertElementsPerWorkgroup, 1, 1);
1688  
1689              _pipeline.Finish(gd, cbs);
1690  
1691              BufferHolder.InsertBufferBarrier(
1692                  gd,
1693                  cbs.CommandBuffer,
1694                  dstBuffer,
1695                  access,
1696                  BufferHolder.DefaultAccessFlags,
1697                  stage,
1698                  PipelineStageFlags.AllCommandsBit,
1699                  0,
1700                  outSize);
1701          }
1702  
1703          protected virtual void Dispose(bool disposing)
1704          {
1705              if (disposing)
1706              {
1707                  _programColorBlitClearAlpha.Dispose();
1708                  _programColorBlit.Dispose();
1709                  _programColorBlitMs.Dispose();
1710                  _programColorClearF.Dispose();
1711                  _programColorClearSI.Dispose();
1712                  _programColorClearUI.Dispose();
1713                  _programDepthStencilClear.Dispose();
1714                  _programStrideChange.Dispose();
1715                  _programConvertIndexBuffer.Dispose();
1716                  _programConvertIndirectData.Dispose();
1717                  _programColorCopyShortening.Dispose();
1718                  _programColorCopyToNonMs.Dispose();
1719                  _programColorCopyWidening.Dispose();
1720                  _programColorDrawToMs.Dispose();
1721                  _programDepthBlit.Dispose();
1722                  _programDepthBlitMs.Dispose();
1723                  _programDepthDrawToMs.Dispose();
1724                  _programDepthDrawToNonMs.Dispose();
1725                  _programStencilBlit?.Dispose();
1726                  _programStencilBlitMs?.Dispose();
1727                  _programStencilDrawToMs?.Dispose();
1728                  _programStencilDrawToNonMs?.Dispose();
1729                  _samplerNearest.Dispose();
1730                  _samplerLinear.Dispose();
1731                  _pipeline.Dispose();
1732              }
1733          }
1734  
1735          public void Dispose()
1736          {
1737              Dispose(true);
1738          }
1739      }
1740  }