1 module deferred; 2 3 import buffer; 4 import example; 5 import pipeline; 6 import scene; 7 8 import gfx.core; 9 import gfx.graal; 10 import gfx.math; 11 import gfx.window; 12 13 import std.datetime : Duration; 14 import std.stdio; 15 16 class DeferredExample : Example 17 { 18 Rc!RenderPass deferredRenderPass; 19 Rc!RenderPass bloomRenderPass; 20 Rc!RenderPass blendRenderPass; 21 22 Rc!DescriptorPool descriptorPool; 23 24 Rc!DeferredBuffers buffers; 25 Rc!DeferredPipelines pipelines; 26 27 DescriptorSet geomDescriptorSet; 28 DescriptorSet lightBufDescriptorSet; 29 30 Rc!Sampler bloomSampler; 31 32 Duration lastTimeElapsed; 33 34 DeferredScene scene; 35 FMat4 viewProj; 36 FVec3 viewerPos; 37 38 Timer bufUpdateTimer; 39 Timer recordTimer; 40 41 this(string[] args) { 42 super("Deferred", args ~ "--no-gl3"); 43 } 44 45 override void dispose() 46 { 47 if (device) 48 device.waitIdle(); 49 bloomSampler.unload(); 50 deferredRenderPass.unload(); 51 bloomRenderPass.unload(); 52 blendRenderPass.unload(); 53 descriptorPool.reset(); 54 descriptorPool.unload(); 55 pipelines.unload(); 56 buffers.unload(); 57 58 super.dispose(); 59 } 60 61 override void prepare() 62 { 63 super.prepare(); 64 const rad = scene.prepare(); 65 buffers = new DeferredBuffers(this, scene.saucerCount); 66 pipelines = new DeferredPipelines(device, deferredRenderPass, 67 bloomRenderPass, blendRenderPass); 68 69 bloomSampler = device.createSampler( 70 SamplerInfo.bilinear().withWrapMode(WrapMode.clamp) 71 ); 72 73 prepareDescriptors(); 74 75 viewerPos = 1.2 * fvec(rad, rad, rad); 76 const view = lookAt(viewerPos, fvec(0, 0, 0), fvec(0, 0, 1)); 77 const proj = perspective!float(this.ndc, 45, 4f/3f, 1f, rad * 3f); 78 viewProj = proj * view; 79 } 80 81 override void prepareRenderPass() 82 { 83 // Deferred render pass 84 // - subpass 1: geom 85 // renders geometry into 4 images (position, normal, color, shininess) 86 // - subpass 2: light 87 // render lighted scene into HDR image 88 // the brightest areas are extracted into the bloomBase image at the same time 89 prepareDeferredRenderPass(); 90 91 // Bloom render pass 92 // - done successively with flipping attachments 93 // in order to perform horizontal and vertical 94 // blur ping pong passes 95 // - the first pass blurs the bloomBase image horizontally into 96 // the blurH image 97 // - the second pass blurs the blurH image vertically into 98 // the blurV image 99 // - the third pass blurs the blurV image horizontally into 100 // the blurH image 101 // - and so on... 102 // a descriptor set is prepared for each case 103 prepareBloomRenderPass(); 104 105 // Blend render pass 106 // - blend the lighted 107 prepareBlendRenderPass(); 108 } 109 110 final void prepareDeferredRenderPass() 111 { 112 enum Attachment { 113 worldPos, 114 normal, 115 color, 116 depth, 117 118 hdrScene, 119 bloomBase, 120 121 count, 122 } 123 enum Subpass { 124 geom, 125 light, 126 127 count, 128 } 129 130 auto attachments = new AttachmentDescription[Attachment.count]; 131 auto subpasses = new SubpassDescription[Subpass.count]; 132 133 attachments[Attachment.worldPos] = AttachmentDescription.color( 134 Format.rgba32_sFloat, AttachmentOps(LoadOp.clear, StoreOp.dontCare), 135 trans(ImageLayout.undefined, ImageLayout.colorAttachmentOptimal) 136 ); 137 attachments[Attachment.normal] = AttachmentDescription.color( 138 Format.rgba16_sFloat, AttachmentOps(LoadOp.clear, StoreOp.dontCare), 139 trans(ImageLayout.undefined, ImageLayout.colorAttachmentOptimal) 140 ); 141 attachments[Attachment.color] = AttachmentDescription.color( 142 Format.rgba8_uNorm, AttachmentOps(LoadOp.clear, StoreOp.dontCare), 143 trans(ImageLayout.undefined, ImageLayout.colorAttachmentOptimal) 144 ); 145 attachments[Attachment.depth] = AttachmentDescription.depth( 146 Format.d16_uNorm, AttachmentOps(LoadOp.clear, StoreOp.dontCare), 147 trans(ImageLayout.undefined, ImageLayout.depthStencilAttachmentOptimal) 148 ); 149 attachments[Attachment.hdrScene] = AttachmentDescription.color( 150 Format.rgba16_sFloat, AttachmentOps(LoadOp.clear, StoreOp.store), 151 trans(ImageLayout.undefined, ImageLayout.shaderReadOnlyOptimal) 152 ); 153 attachments[Attachment.bloomBase] = AttachmentDescription.color( 154 Format.rgba16_sFloat, AttachmentOps(LoadOp.clear, StoreOp.store), 155 trans(ImageLayout.undefined, ImageLayout.shaderReadOnlyOptimal) 156 ); 157 158 subpasses[Subpass.geom] = SubpassDescription( 159 // inputs 160 [], 161 // outputs 162 [ 163 AttachmentRef(Attachment.worldPos, ImageLayout.colorAttachmentOptimal), 164 AttachmentRef(Attachment.normal, ImageLayout.colorAttachmentOptimal), 165 AttachmentRef(Attachment.color, ImageLayout.colorAttachmentOptimal), 166 ], 167 // depth 168 some(AttachmentRef(Attachment.depth, ImageLayout.depthStencilAttachmentOptimal)) 169 ); 170 subpasses[Subpass.light] = SubpassDescription( 171 // inputs 172 [ 173 AttachmentRef(Attachment.worldPos, ImageLayout.shaderReadOnlyOptimal), 174 AttachmentRef(Attachment.normal, ImageLayout.shaderReadOnlyOptimal), 175 AttachmentRef(Attachment.color, ImageLayout.shaderReadOnlyOptimal), 176 ], 177 // outputs 178 [ 179 AttachmentRef(Attachment.hdrScene, ImageLayout.colorAttachmentOptimal), 180 AttachmentRef(Attachment.bloomBase, ImageLayout.colorAttachmentOptimal), 181 ], 182 // depth 183 none!AttachmentRef 184 ); 185 const dependencies = [ 186 SubpassDependency( 187 trans(subpassExternal, Subpass.geom), 188 trans(PipelineStage.bottomOfPipe, PipelineStage.vertexShader), 189 trans(Access.memoryWrite, Access.uniformRead) 190 ), 191 SubpassDependency( 192 trans!uint(Subpass.geom, Subpass.light), // from geometry to lighting pass 193 trans(PipelineStage.colorAttachmentOutput, PipelineStage.fragmentShader), 194 trans(Access.colorAttachmentWrite, Access.inputAttachmentRead) 195 ), 196 SubpassDependency( 197 trans(Subpass.light, subpassExternal), 198 trans(PipelineStage.colorAttachmentOutput, PipelineStage.fragmentShader), 199 trans(Access.colorAttachmentWrite, Access.shaderRead) 200 ), 201 ]; 202 203 deferredRenderPass = device.createRenderPass(attachments, subpasses, dependencies); 204 } 205 206 final void prepareBloomRenderPass() 207 { 208 enum Attachment { 209 blurOutput, 210 211 count, 212 } 213 enum Subpass { 214 blur, 215 216 count, 217 } 218 219 auto attachments = new AttachmentDescription[Attachment.count]; 220 auto subpasses = new SubpassDescription[Subpass.count]; 221 222 attachments[Attachment.blurOutput] = AttachmentDescription.color( 223 Format.rgba16_sFloat, AttachmentOps(LoadOp.clear, StoreOp.store), 224 trans(ImageLayout.undefined, ImageLayout.shaderReadOnlyOptimal) 225 ); 226 227 subpasses[Subpass.blur] = SubpassDescription( 228 // inputs 229 [], 230 // outputs 231 [ 232 AttachmentRef(Attachment.blurOutput, ImageLayout.colorAttachmentOptimal), 233 ], 234 // depth 235 none!AttachmentRef 236 ); 237 const dependencies = [ 238 SubpassDependency( 239 trans(subpassExternal, Subpass.blur), 240 trans(PipelineStage.bottomOfPipe, PipelineStage.fragmentShader), 241 trans(Access.memoryRead, Access.shaderRead) 242 ), 243 SubpassDependency( 244 trans(Subpass.blur, subpassExternal), 245 trans(PipelineStage.colorAttachmentOutput, PipelineStage.topOfPipe), 246 trans(Access.colorAttachmentWrite, Access.memoryRead) 247 ), 248 ]; 249 250 bloomRenderPass = device.createRenderPass(attachments, subpasses, dependencies); 251 } 252 253 final void prepareBlendRenderPass() 254 { 255 enum Attachment { 256 hdrScene, 257 bloom, 258 259 swcColor, 260 261 count, 262 } 263 enum Subpass { 264 blend, 265 266 count, 267 } 268 269 auto attachments = new AttachmentDescription[Attachment.count]; 270 auto subpasses = new SubpassDescription[Subpass.count]; 271 272 attachments[Attachment.hdrScene] = AttachmentDescription.color( 273 Format.rgba16_sFloat, AttachmentOps(LoadOp.load, StoreOp.dontCare), 274 trans(ImageLayout.shaderReadOnlyOptimal, ImageLayout.colorAttachmentOptimal) 275 ); 276 attachments[Attachment.bloom] = AttachmentDescription.color( 277 Format.rgba16_sFloat, AttachmentOps(LoadOp.load, StoreOp.dontCare), 278 trans(ImageLayout.shaderReadOnlyOptimal, ImageLayout.colorAttachmentOptimal) 279 ); 280 attachments[Attachment.swcColor] = AttachmentDescription.color( 281 swapchain.format, AttachmentOps(LoadOp.clear, StoreOp.store), 282 trans(ImageLayout.undefined, ImageLayout.presentSrc) 283 ); 284 285 subpasses[Subpass.blend] = SubpassDescription( 286 // inputs 287 [ 288 AttachmentRef(Attachment.hdrScene, ImageLayout.shaderReadOnlyOptimal), 289 AttachmentRef(Attachment.bloom, ImageLayout.shaderReadOnlyOptimal), 290 ], 291 // outputs 292 [ 293 AttachmentRef(Attachment.swcColor, ImageLayout.colorAttachmentOptimal), 294 ], 295 // depth 296 none!AttachmentRef 297 ); 298 const dependencies = [ 299 SubpassDependency( 300 trans(subpassExternal, Subpass.blend), 301 trans(PipelineStage.bottomOfPipe, PipelineStage.colorAttachmentOutput), 302 trans(Access.memoryRead, Access.colorAttachmentWrite) 303 ), 304 SubpassDependency( 305 trans(Subpass.blend, subpassExternal), 306 trans(PipelineStage.colorAttachmentOutput, PipelineStage.topOfPipe), 307 trans(Access.colorAttachmentWrite, Access.memoryRead) 308 ), 309 ]; 310 311 blendRenderPass = device.createRenderPass(attachments, subpasses, dependencies); 312 } 313 314 class DeferredFrameData : FrameData 315 { 316 struct FbImage 317 { 318 Image img; 319 ImageView view; 320 321 this(Device device, DeferredExample ex, ImageInfo info) { 322 img = retainObj(device.createImage(info)); 323 ex.bindImageMemory(img); 324 const aspect = info.usage & ImageUsage.depthStencilAttachment ? 325 ImageAspect.depth : 326 ImageAspect.color; 327 view = retainObj(img.createView( 328 ImageType.d2, ImageSubresourceRange(aspect), Swizzle.identity 329 )); 330 } 331 332 this(this) { 333 if (img) { 334 retainObj(img); 335 retainObj(view); 336 } 337 } 338 339 ~this() { 340 if (img) { 341 releaseObj(view); 342 releaseObj(img); 343 } 344 } 345 346 ImageDescriptor attachmentDescriptor() 347 { 348 return view.descriptor(ImageLayout.shaderReadOnlyOptimal); 349 } 350 351 ImageSamplerDescriptor samplerDescriptor(Sampler sampler) 352 { 353 return view.descriptorWithSampler(ImageLayout.shaderReadOnlyOptimal, sampler); 354 } 355 } 356 // G-buffer 357 FbImage worldPos; 358 FbImage normal; 359 FbImage color; 360 361 /// depth buffer 362 FbImage depth; 363 364 // HDR image the scene is rendered to 365 FbImage hdrScene; 366 FbImage bloomBase; 367 368 // Framebuffer for deferred pass 369 Rc!Framebuffer deferredFramebuffer; 370 371 // ping pong blur framebuffers for blooming 372 FbImage blurH; 373 FbImage blurV; 374 Rc!Framebuffer blurHFramebuffer; 375 Rc!Framebuffer blurVFramebuffer; 376 377 // final blend framebuffer 378 Rc!Framebuffer blendFramebuffer; 379 380 /// Command buffer 381 PrimaryCommandBuffer cmdBuf; 382 383 /// Descriptors 384 Rc!DescriptorPool descriptorPool; 385 DescriptorSet lightAttachDescriptorSet; 386 DescriptorSet[] bloomDescriptorSets; 387 DescriptorSet blendDescriptorSet; 388 389 this(ImageBase swcColor, CommandBuffer tempBuf) 390 { 391 import std.exception : enforce; 392 393 super(swcColor); 394 395 cmdBuf = cmdPool.allocatePrimary(1)[0]; 396 397 worldPos = FbImage(device, this.outer, ImageInfo.d2(size[0], size[1]) 398 .withFormat(Format.rgba32_sFloat) 399 .withUsage(ImageUsage.colorAttachment | ImageUsage.inputAttachment) 400 ); 401 normal = FbImage(device, this.outer, ImageInfo.d2(size[0], size[1]) 402 .withFormat(Format.rgba16_sFloat) 403 .withUsage(ImageUsage.colorAttachment | ImageUsage.inputAttachment) 404 ); 405 color = FbImage(device, this.outer, ImageInfo.d2(size[0], size[1]) 406 .withFormat(Format.rgba8_uNorm) 407 .withUsage(ImageUsage.colorAttachment | ImageUsage.inputAttachment) 408 ); 409 depth = FbImage(device, this.outer, ImageInfo.d2(size[0], size[1]) 410 .withFormat(Format.d16_uNorm) 411 .withUsage(ImageUsage.depthStencilAttachment) 412 ); 413 hdrScene = FbImage(device, this.outer, ImageInfo.d2(size[0], size[1]) 414 .withFormat(Format.rgba16_sFloat) 415 .withUsage(ImageUsage.colorAttachment | ImageUsage.inputAttachment) 416 ); 417 bloomBase = FbImage(device, this.outer, ImageInfo.d2(size[0], size[1]) 418 .withFormat(Format.rgba16_sFloat) 419 .withUsage(ImageUsage.colorAttachment | ImageUsage.sampled ) 420 ); 421 blurH = FbImage(device, this.outer, ImageInfo.d2(size[0], size[1]) 422 .withFormat(Format.rgba16_sFloat) 423 .withUsage(ImageUsage.colorAttachment | ImageUsage.sampled) 424 ); 425 blurV = FbImage(device, this.outer, ImageInfo.d2(size[0], size[1]) 426 .withFormat(Format.rgba16_sFloat) 427 .withUsage(ImageUsage.colorAttachment | ImageUsage.inputAttachment | ImageUsage.sampled) 428 ); 429 430 auto swcColorView = swcColor.createView( 431 ImageType.d2, ImageSubresourceRange(ImageAspect.color), Swizzle.identity 432 ).rc; 433 434 deferredFramebuffer = this.outer.device.createFramebuffer( 435 this.outer.deferredRenderPass, [ 436 worldPos.view, normal.view, color.view, 437 depth.view, hdrScene.view, bloomBase.view 438 ], size[0], size[1], 1 439 ); 440 441 blurHFramebuffer = this.outer.device.createFramebuffer( 442 this.outer.bloomRenderPass, [ 443 blurH.view, 444 ], size[0], size[1], 1 445 ); 446 blurVFramebuffer = this.outer.device.createFramebuffer( 447 this.outer.bloomRenderPass, [ 448 blurV.view, 449 ], size[0], size[1], 1 450 ); 451 452 blendFramebuffer = this.outer.device.createFramebuffer( 453 this.outer.blendRenderPass, [ 454 hdrScene.view, blurV.view, swcColorView.obj, 455 ], size[0], size[1], 1 456 ); 457 } 458 459 final void prepareDescriptors() 460 { 461 if (descriptorPool) return; // already initialized 462 463 auto pipelines = this.outer.pipelines.obj; 464 auto bloomSampler = this.outer.bloomSampler.obj; 465 466 const poolSizes = [ 467 DescriptorPoolSize(DescriptorType.inputAttachment, 5), 468 DescriptorPoolSize(DescriptorType.combinedImageSampler, 3), 469 ]; 470 descriptorPool = device.createDescriptorPool(5, poolSizes); 471 auto sets = descriptorPool.allocate([ 472 pipelines.light.descriptorLayouts[1], 473 pipelines.bloom.descriptorLayouts[0], 474 pipelines.bloom.descriptorLayouts[0], 475 pipelines.bloom.descriptorLayouts[0], 476 pipelines.blend.descriptorLayouts[0], 477 ]); 478 lightAttachDescriptorSet = sets[0]; 479 bloomDescriptorSets = sets[1 .. 4]; 480 blendDescriptorSet = sets[4]; 481 482 auto writes = [ 483 WriteDescriptorSet(lightAttachDescriptorSet, 0, 0, DescriptorWrite.make( 484 DescriptorType.inputAttachment, 485 [ 486 worldPos.attachmentDescriptor, 487 normal.attachmentDescriptor, 488 color.attachmentDescriptor, 489 ] 490 )), 491 492 WriteDescriptorSet(bloomDescriptorSets[0], 0, 0, DescriptorWrite.make( 493 DescriptorType.combinedImageSampler, 494 blurH.samplerDescriptor(bloomSampler), 495 )), 496 497 WriteDescriptorSet(bloomDescriptorSets[1], 0, 0, DescriptorWrite.make( 498 DescriptorType.combinedImageSampler, 499 blurV.samplerDescriptor(bloomSampler), 500 )), 501 502 WriteDescriptorSet(bloomDescriptorSets[2], 0, 0, DescriptorWrite.make( 503 DescriptorType.combinedImageSampler, 504 bloomBase.samplerDescriptor(bloomSampler), 505 )), 506 507 WriteDescriptorSet(blendDescriptorSet, 0, 0, DescriptorWrite.make( 508 DescriptorType.inputAttachment, 509 [ 510 hdrScene.attachmentDescriptor, 511 blurV.attachmentDescriptor, 512 ] 513 )), 514 ]; 515 device.updateDescriptorSets(writes, []); 516 } 517 518 override void dispose() 519 { 520 if (descriptorPool) descriptorPool.reset(); 521 descriptorPool.unload(); 522 blendFramebuffer.unload(); 523 blurVFramebuffer.unload(); 524 blurHFramebuffer.unload(); 525 deferredFramebuffer.unload(); 526 blurH = FbImage.init; 527 blurV = FbImage.init; 528 depth = FbImage.init; 529 bloomBase = FbImage.init; 530 hdrScene = FbImage.init; 531 color = FbImage.init; 532 normal = FbImage.init; 533 worldPos = FbImage.init; 534 cmdPool.free([ cast(CommandBuffer)cmdBuf ]); 535 super.dispose(); 536 } 537 } 538 539 override FrameData makeFrameData(ImageBase swcColor, CommandBuffer tempBuf) 540 { 541 return new DeferredFrameData(swcColor, tempBuf); 542 } 543 544 final void prepareDescriptors() 545 { 546 const poolSizes = [ 547 DescriptorPoolSize(DescriptorType.uniformBuffer, 2), 548 DescriptorPoolSize(DescriptorType.uniformBufferDynamic, 2), 549 ]; 550 551 descriptorPool = device.createDescriptorPool(7, poolSizes); 552 auto sets = descriptorPool.allocate([ 553 pipelines.geom.descriptorLayouts[0], 554 pipelines.light.descriptorLayouts[0], 555 ]); 556 geomDescriptorSet = sets[0]; 557 lightBufDescriptorSet = sets[1]; 558 559 auto writes = [ 560 WriteDescriptorSet(geomDescriptorSet, 0, 0, DescriptorWrite.make( 561 DescriptorType.uniformBuffer, 562 buffers.geomFrameUbo.descriptor(), 563 )), 564 WriteDescriptorSet(geomDescriptorSet, 1, 0, DescriptorWrite.make( 565 DescriptorType.uniformBufferDynamic, 566 buffers.geomModelUbo.descriptor(0, 1), 567 )), 568 WriteDescriptorSet(lightBufDescriptorSet, 0, 0, DescriptorWrite.make( 569 DescriptorType.uniformBuffer, 570 buffers.lightFrameUbo.descriptor(), 571 )), 572 WriteDescriptorSet(lightBufDescriptorSet, 1, 0, DescriptorWrite.make( 573 DescriptorType.uniformBufferDynamic, 574 buffers.lightModelUbo.descriptor(0, 1), 575 )), 576 ]; 577 device.updateDescriptorSets(writes, []); 578 } 579 580 void updateSceneAndBuffers(in float dt) 581 { 582 import std.math : sqrt; 583 584 auto ft = bufUpdateTimer.frame(); 585 586 scene.mov.rotate(dt); 587 const M1 = scene.mov.transform(); 588 589 foreach (ref ss; scene.subStructs) { 590 ss.mov.rotate(dt); 591 const M2 = M1 * ss.mov.transform(); 592 593 foreach (ref s; ss.saucers) { 594 s.anim(dt); 595 const M3 = M2 * s.mov.transform(); 596 597 foreach (bi; 0 .. 3) { 598 buffers.geomModelUbo.data[s.saucerIdx].data[bi] = GeomModelData( 599 (M3 * s.bodies[bi].transform).transpose(), 600 fvec( 601 s.bodies[bi].color, 602 s.bodies[bi].shininess, 603 ), 604 ); 605 } 606 607 const lightPosMat = M3 * translation(s.lightPos); 608 // set the light sphere volume as a function of brightness on the edge 609 // brightness = luminosity / (distance ^ 2 + 1) 610 enum edgeBrightness = 0.02; 611 const lightRadius = sqrt(s.lightLuminosity / edgeBrightness - 1.0); 612 buffers.lightModelUbo.data[s.saucerIdx] = LightModelUbo( 613 transpose(viewProj * lightPosMat * scale(FVec3(lightRadius))), 614 lightPosMat * fvec(0, 0, 0, 1), 615 fvec( 616 s.lightAnim.color, 617 s.lightLuminosity, 618 ), 619 ); 620 } 621 } 622 623 buffers.geomFrameUbo.data[0].viewProj = viewProj.transpose(); 624 buffers.lightFrameUbo.data[0].viewerPos = fvec(viewerPos, 1.0); 625 626 buffers.flush(device.obj); 627 } 628 629 override Submission[] recordCmds(FrameData frameData) 630 { 631 import std.datetime : dur; 632 633 auto dfd = cast(DeferredFrameData)frameData; 634 dfd.prepareDescriptors(); 635 636 // update scene 637 const time = timeElapsed(); 638 if (time > lastTimeElapsed) { 639 const dt = (time - lastTimeElapsed).total!"hnsecs" / 10_000_000.0; 640 updateSceneAndBuffers(dt); 641 lastTimeElapsed = time; 642 } 643 644 auto ft = recordTimer.frame(); 645 646 PrimaryCommandBuffer buf = dfd.cmdBuf; 647 648 buf.begin(CommandBufferUsage.oneTimeSubmit); 649 650 buf.setViewport(0, [ Viewport(0f, 0f, cast(float)surfaceSize[0], cast(float)surfaceSize[1]) ]); 651 buf.setScissor(0, [ Rect(0, 0, surfaceSize[0], surfaceSize[1]) ]); 652 653 deferredPass(buf, dfd); 654 655 bloomPasses(buf, dfd); 656 657 blendPass(buf, dfd); 658 659 buf.end(); 660 661 return simpleSubmission([ buf ]); 662 } 663 664 void deferredPass(PrimaryCommandBuffer buf, DeferredFrameData dfd) 665 { 666 const(ClearValues[6]) cvs = [ 667 // clearing all attachments in the framebuffer 668 ClearValues(ClearColorValues( 0f, 0f, 0f, 0f )), // world-pos 669 ClearValues(ClearColorValues( 0f, 0f, 0f, 0f )), // normal 670 ClearValues(ClearColorValues( 0f, 0f, 0f, 0f )), // color 671 ClearValues(ClearDepthStencilValues( 1f, 0 )), // depth 672 ClearValues(ClearColorValues( 0f, 0f, 0f, 0f )), // hdrScene 673 ClearValues(ClearColorValues( 0f, 0f, 0f, 1f )), // bloom base 674 ]; 675 676 buf.beginRenderPass( 677 deferredRenderPass, 678 dfd.deferredFramebuffer, 679 Rect(0, 0, surfaceSize[0], surfaceSize[1]), 680 cvs[], 681 ); 682 683 // geometry pass 684 685 buf.bindPipeline(pipelines.geom.pipeline); 686 buffers.hiResSphere.bindIndex(buf); 687 buf.bindVertexBuffers(0, [ buffers.hiResSphere.vertexBinding() ]); 688 689 foreach (ref ss; scene.subStructs) { 690 foreach (ref s; ss.saucers) { 691 buf.bindDescriptorSets( 692 PipelineBindPoint.graphics, 693 pipelines.geom.layout, 0, [ geomDescriptorSet ], 694 [ s.saucerIdx * GeomModelUbo.sizeof ] 695 ); 696 // only draw the bulbs that are off 697 const numInstances = s.lightAnim.on ? 2 : 3; 698 buf.drawIndexed( 699 cast(uint)buffers.hiResSphere.indicesCount, numInstances, 0, 0, 0 700 ); 701 } 702 } 703 704 // now draw the "on" bulbs with inverted normals as the light is from within 705 buffers.invertedSphere.bindIndex(buf); 706 buf.bindVertexBuffers(0, [ buffers.invertedSphere.vertexBinding() ]); 707 708 foreach (ref ss; scene.subStructs) { 709 foreach (ref s; ss.saucers) { 710 if (!s.lightAnim.on) continue; 711 712 buf.bindDescriptorSets( 713 PipelineBindPoint.graphics, 714 pipelines.geom.layout, 0, [ geomDescriptorSet ], 715 [ s.saucerIdx * GeomModelUbo.sizeof ] 716 ); 717 buf.drawIndexed( 718 cast(uint)buffers.hiResSphere.indicesCount, 1, 0, 0, 2 719 ); 720 } 721 } 722 723 buf.nextSubpass(); 724 725 // light pass 726 727 buf.bindPipeline(pipelines.light.pipeline); 728 buffers.loResSphere.bindIndex(buf); 729 buf.bindVertexBuffers(0, [ buffers.loResSphere.vertexBinding() ]); 730 buf.bindDescriptorSets( 731 PipelineBindPoint.graphics, 732 pipelines.light.layout, 1, [ dfd.lightAttachDescriptorSet ], [] 733 ); 734 735 foreach (ref ss; scene.subStructs) { 736 foreach (ref s; ss.saucers) { 737 738 if (!s.lightAnim.on) continue; 739 740 buf.bindDescriptorSets( 741 PipelineBindPoint.graphics, 742 pipelines.light.layout, 0, [ lightBufDescriptorSet ], 743 [ s.saucerIdx * LightModelUbo.sizeof ] 744 ); 745 buf.drawIndexed( 746 cast(uint)buffers.loResSphere.indicesCount, 1, 0, 0, 0 747 ); 748 } 749 } 750 751 buf.endRenderPass(); 752 753 } 754 755 void bloomPasses(PrimaryCommandBuffer buf, DeferredFrameData dfd) 756 { 757 buf.bindPipeline(pipelines.bloom.pipeline); 758 buffers.square.bindIndex(buf); 759 buf.bindVertexBuffers(0, [ buffers.square.vertexBinding() ]); 760 761 const cv = ClearValues(ClearColorValues( 0f, 0f, 0f, 1f )); 762 763 enum numBlurPasses = 4; 764 765 foreach(i; 0 .. numBlurPasses) { 766 foreach(v; 0 .. 2) { 767 const h = 1 - v; 768 // input descriptor sets: 769 // 0: blurH, 1: blurV, 2: bloomBase (result of previous pass) 770 auto dsInd = (i+v == 0) ? 2 : h; 771 // output 772 auto fb = v == 0 ? dfd.blurHFramebuffer.obj : dfd.blurVFramebuffer.obj; 773 774 buf.bindDescriptorSets( 775 PipelineBindPoint.graphics, 776 pipelines.bloom.layout, 0, dfd.bloomDescriptorSets[dsInd .. dsInd+1], 777 [] 778 ); 779 780 buf.beginRenderPass(bloomRenderPass, fb, 781 Rect(0, 0, surfaceSize[0], surfaceSize[1]), 782 (&cv)[0 .. 1]); 783 784 buf.pushConstants(pipelines.bloom.layout, ShaderStage.fragment, 785 0, int.sizeof, &h 786 ); 787 788 buf.drawIndexed(buffers.square.indicesCount, 1, 0, 0, 0); 789 790 buf.endRenderPass(); 791 } 792 } 793 } 794 795 796 void blendPass(PrimaryCommandBuffer buf, DeferredFrameData dfd) 797 { 798 buf.bindPipeline(pipelines.blend.pipeline); 799 // square mesh already bound in bloom pass 800 buf.bindDescriptorSets( 801 PipelineBindPoint.graphics, 802 pipelines.blend.layout, 0, [ dfd.blendDescriptorSet ], 803 [] 804 ); 805 806 const cvs = [ 807 ClearValues(ClearColorValues( 0f, 0f, 0f, 1f )), 808 ClearValues(ClearColorValues( 0f, 0f, 0f, 1f )), 809 ClearValues(ClearColorValues( 0f, 0f, 0f, 1f )), 810 ]; 811 812 buf.beginRenderPass(blendRenderPass, dfd.blendFramebuffer.obj, 813 Rect(0, 0, surfaceSize[0], surfaceSize[1]), cvs); 814 815 buf.drawIndexed(buffers.square.indicesCount, 1, 0, 0, 0); 816 817 buf.endRenderPass(); 818 } 819 } 820 821 int main(string[] args) 822 { 823 try { 824 auto example = new DeferredExample(args); 825 example.prepare(); 826 scope(exit) example.dispose(); 827 828 example.window.onKeyOn = (KeyEvent ev) 829 { 830 if (ev.sym == KeySym.escape) { 831 example.window.closeFlag = true; 832 } 833 }; 834 835 while (!example.window.closeFlag) { 836 example.render(); 837 example.frameTick(); 838 839 enum timerReportFreq = 300; 840 if (example.recordTimer.framecount % timerReportFreq == 0) { 841 gfxExLog.infof("buf update = %s µs", example.bufUpdateTimer.avgDur.total!"usecs"); 842 gfxExLog.infof("record cmds = %s µs", example.recordTimer.avgDur.total!"usecs"); 843 } 844 845 example.display.pollAndDispatch(); 846 } 847 return 0; 848 } 849 catch(Exception ex) 850 { 851 import std.stdio : stderr; 852 853 stderr.writeln("error occured: ", ex.msg); 854 return 1; 855 } 856 }