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 }