1 module shadow; 2 3 import example; 4 5 import gfx.core.rc; 6 import gfx.core.typecons; 7 import gfx.graal.buffer; 8 import gfx.graal.cmd; 9 import gfx.graal.format; 10 import gfx.graal.image; 11 import gfx.graal.pipeline; 12 import gfx.graal.renderpass; 13 import gfx.graal.sync; 14 import gfx.graal.types; 15 16 import gfx.math; 17 18 import std.math : PI; 19 import std.stdio; 20 import std.typecons; 21 22 23 final class ShadowExample : Example 24 { 25 // generic resources 26 Rc!Buffer vertBuf; 27 Rc!Buffer indBuf; 28 29 Rc!Buffer meshUniformBuf; // per mesh and per light 30 Rc!Buffer ligUniformBuf; // per light 31 32 Rc!DescriptorPool descPool; 33 34 // shadow pass 35 Rc!Semaphore shadowFinishedSem; 36 Rc!RenderPass shadowRenderPass; 37 Rc!Pipeline shadowPipeline; 38 Rc!Image shadowTex; 39 Rc!Sampler shadowSampler; 40 Rc!Framebuffer shadowFramebuffer; 41 Rc!DescriptorSetLayout shadowDSLayout; 42 Rc!PipelineLayout shadowLayout; 43 DescriptorSet shadowDS; 44 45 // mesh pass 46 Rc!RenderPass meshRenderPass; 47 Rc!Pipeline meshPipeline; 48 PerImage[] framebuffers; 49 Rc!ImageView meshShadowView; 50 Rc!DescriptorSetLayout meshDSLayout; 51 Rc!PipelineLayout meshLayout; 52 DescriptorSet meshDS; 53 54 // scene 55 Mesh[] meshes; 56 Light[] lights; 57 58 // constants 59 enum shadowSize = 2048; 60 enum maxLights = 5; 61 62 final class PerImage : Disposable 63 { 64 ImageBase color; 65 Rc!ImageView colorView; 66 Rc!Image depth; 67 Rc!ImageView depthView; 68 Rc!Framebuffer meshFramebuffer; 69 70 override void dispose() { 71 meshFramebuffer.unload(); 72 colorView.unload(); 73 depth.unload(); 74 depthView.unload(); 75 } 76 } 77 78 // supporting structs 79 80 static struct Vertex { 81 FVec3 position; 82 FVec3 normal; 83 } 84 85 // uniform types 86 static struct ShadowVsLocals { // meshDynamicBuf (per mesh and per light) 87 FMat4 proj; 88 } 89 90 static struct MeshVsLocals { // meshDynamicBuf (per mesh) 91 FMat4 mvp; 92 FMat4 model; 93 } 94 95 static struct MeshFsMaterial { // meshDynamicBuf (per mesh) 96 FVec4 color; 97 FVec4 padding; 98 } 99 100 static struct LightBlk { // within MeshFsLights 101 FVec4 position; 102 FVec4 color; 103 FMat4 proj; 104 } 105 106 static struct MeshFsLights { // ligUniformBuf (static, single instance) 107 int numLights; 108 int[3] padding; 109 LightBlk[maxLights] lights; 110 } 111 112 // scene types 113 static struct Mesh { 114 uint vertOffset; 115 uint indOffset; 116 uint numVertices; 117 float pulse; 118 FVec4 color; 119 FMat4 model; 120 } 121 122 static struct Light { 123 FVec4 position; 124 FVec4 color; 125 FMat4 view; 126 FMat4 proj; 127 CommandBuffer cmdBuf; 128 Rc!ImageView shadowPlane; 129 Rc!Framebuffer shadowFb; 130 } 131 132 this(string[] args=[]) { 133 super("Shadow", args); 134 } 135 136 override void dispose() { 137 device.waitIdle(); 138 139 vertBuf.unload(); 140 indBuf.unload(); 141 meshUniformBuf.unload(); 142 ligUniformBuf.unload(); 143 144 descPool.unload(); 145 146 shadowFinishedSem.unload(); 147 shadowRenderPass.unload(); 148 shadowPipeline.unload(); 149 shadowTex.unload(); 150 shadowSampler.unload(); 151 shadowFramebuffer.unload(); 152 shadowDSLayout.unload(); 153 shadowLayout.unload(); 154 155 meshRenderPass.unload(); 156 meshPipeline.unload(); 157 disposeArr(framebuffers); 158 meshShadowView.unload(); 159 meshDSLayout.unload(); 160 meshLayout.unload(); 161 162 reinitArr(lights); 163 164 super.dispose(); 165 } 166 167 override void prepare() { 168 super.prepare(); 169 prepareSceneAndResources(); 170 prepareShadowRenderPass(); 171 prepareMeshRenderPass(); 172 prepareFramebuffers(); 173 prepareDescriptors(); 174 preparePipelines(); 175 } 176 177 void prepareSceneAndResources() { 178 179 import std.exception : enforce; 180 181 // setting up lights 182 183 enum numLights = 3; 184 185 shadowTex = device.createImage( 186 ImageInfo.d2Array(shadowSize, shadowSize, numLights) 187 .withFormat(Format.d32_sFloat) 188 .withUsage(ImageUsage.sampled | ImageUsage.depthStencilAttachment) 189 ); 190 enforce(bindImageMemory(shadowTex)); 191 meshShadowView = shadowTex.createView( 192 ImageType.d2Array, 193 ImageSubresourceRange(ImageAspect.depth, 0, 1, 0, numLights), 194 Swizzle.identity 195 ); 196 { 197 auto b = autoCmdBuf().rc; 198 b.cmdBuf.pipelineBarrier( 199 trans(PipelineStage.topOfPipe, PipelineStage.earlyFragmentTests), [], [ 200 ImageMemoryBarrier( 201 trans(Access.none, Access.depthStencilAttachmentRead | Access.depthStencilAttachmentWrite), 202 trans(ImageLayout.undefined, ImageLayout.depthStencilAttachmentOptimal), 203 trans(queueFamilyIgnored, queueFamilyIgnored), 204 shadowTex, ImageSubresourceRange(ImageAspect.depth, 0, 1, 0, numLights) 205 ) 206 ] 207 ); 208 } 209 210 shadowSampler = device.createSampler(SamplerInfo( 211 Filter.linear, Filter.linear, Filter.nearest, 212 [WrapMode.repeat, WrapMode.repeat, WrapMode.repeat], 213 none!float, 0f, [0f, 0f], some(CompareOp.lessOrEqual) 214 )); 215 216 auto ligCmdBufs = cmdPool.allocate(numLights); 217 218 auto makeLight(uint layer, FVec3 pos, FVec4 color, float fov) 219 { 220 enum near = 5f; 221 enum far = 20f; 222 223 Light l; 224 l.position = fvec(pos, 1); 225 l.color = color; 226 l.view = lookAt(pos, fvec(0, 0, 0), fvec(0, 0, 1)); 227 l.proj = perspective(fov, 1f, near, far); 228 l.shadowPlane = shadowTex.createView( 229 ImageType.d2, 230 ImageSubresourceRange( 231 ImageAspect.depth, 0, 1, layer, 1 232 ), 233 Swizzle.identity 234 ); 235 l.cmdBuf = ligCmdBufs[layer]; 236 237 return l; 238 } 239 lights = [ 240 makeLight(0, fvec(7, -5, 10), fvec(0.5, 0.7, 0.5, 1), 60), 241 makeLight(1, fvec(-5, 7, 10), fvec(0.7, 0.5, 0.5, 1), 45), 242 makeLight(2, fvec(10, 7, 5), fvec(0.5, 0.5, 0.7, 1), 90), 243 ]; 244 245 { 246 MeshFsLights mfl; 247 mfl.numLights = numLights; 248 foreach (ind, l; lights) { 249 mfl.lights[ind] = LightBlk( 250 l.position, l.color, transpose(l.proj*l.view) 251 ); 252 } 253 auto data = cast(void[])((&mfl)[0 .. 1]); 254 ligUniformBuf = createStaticBuffer(data, BufferUsage.uniform); 255 } 256 257 // setup meshes 258 259 // buffers layout: 260 // vertBuf: cube vertices | plane vertices 261 // indBuf: cube indices | plane indices 262 263 import gfx.genmesh.cube : genCube; 264 import gfx.genmesh.algorithm : indexCollectMesh, triangulate, vertices; 265 import gfx.genmesh.poly : quad; 266 import std.algorithm : map; 267 268 const cube = genCube() 269 .map!(f => quad( 270 Vertex( fvec(f[0].p), fvec(f[0].n) ), 271 Vertex( fvec(f[1].p), fvec(f[1].n) ), 272 Vertex( fvec(f[2].p), fvec(f[2].n) ), 273 Vertex( fvec(f[3].p), fvec(f[3].n) ), 274 )) 275 .triangulate() 276 .vertices() 277 .indexCollectMesh(); 278 279 const planeVertices = [ 280 Vertex(fvec(-7, -7, 0), fvec(0, 0, 1)), 281 Vertex(fvec( 7, -7, 0), fvec(0, 0, 1)), 282 Vertex(fvec( 7, 7, 0), fvec(0, 0, 1)), 283 Vertex(fvec(-7, 7, 0), fvec(0, 0, 1)), 284 ]; 285 286 const ushort[] planeIndices = [ 0, 1, 2, 0, 2, 3 ]; 287 288 const cubeVertBytes = cast(uint)(cube.vertices.length * Vertex.sizeof); 289 const cubeIndBytes = cast(uint)(cube.indices.length * ushort.sizeof); 290 291 const cubeVertOffset = 0; 292 const cubeIndOffset = 0; 293 const cubeNumIndices = cast(uint)cube.indices.length; 294 const planeVertOffset = cubeVertBytes; 295 const planeIndOffset = cubeIndBytes; 296 const planeNumIndices = cast(uint)planeIndices.length; 297 298 auto makeMesh(in uint vertOffset, in uint indOffset, in uint numVertices, in float rpm, 299 in FMat4 model, in FVec4 color) { 300 return Mesh(vertOffset, indOffset, numVertices, rpm*2*PI/3600f, color, model); 301 } 302 303 auto makeCube(in float rpm, in FVec3 pos, in float scale, in float angle, in FVec4 color) { 304 const r = rotation(angle*PI/180f, normalize(pos)); 305 const t = translation(pos); 306 const s = gfx.math.scale(scale, scale, scale); 307 const model = t * s * r; 308 return makeMesh(cubeVertOffset, cubeIndOffset, cubeNumIndices, rpm, model, color); 309 } 310 311 auto makePlane(in FMat4 model, in FVec4 color) { 312 return makeMesh(planeVertOffset, planeIndOffset, planeNumIndices, 0, model, color); 313 } 314 315 meshes = [ 316 makeCube(3, fvec(-2, -2, 2), 0.7, 10, fvec(0.8, 0.2, 0.2, 1)), 317 makeCube(7, fvec(2, -2, 2), 1.3, 50, fvec(0.2, 0.8, 0.2, 1)), 318 makeCube(10, fvec(-2, 2, 2), 1.1, 140, fvec(0.2, 0.2, 0.8, 1)), 319 makeCube(5, fvec(2, 2, 2), 0.9, 210, fvec(0.8, 0.8, 0.2, 1)), 320 makePlane(FMat4.identity, fvec(1, 1, 1, 1)), 321 ]; 322 323 { 324 auto verts = cube.vertices ~ planeVertices; 325 vertBuf = createStaticBuffer(verts, BufferUsage.vertex); 326 } 327 { 328 auto inds = cube.indices ~ planeIndices; 329 indBuf = createStaticBuffer(inds, BufferUsage.index); 330 } 331 meshUniformBuf = createDynamicBuffer( 332 ShadowVsLocals.sizeof * meshes.length * lights.length + 333 MeshVsLocals.sizeof * meshes.length + 334 MeshFsMaterial.sizeof * meshes.length, 335 BufferUsage.uniform 336 ); 337 } 338 339 void prepareShadowRenderPass() { 340 const attachments = [ 341 AttachmentDescription( 342 Format.d32_sFloat, 1, 343 AttachmentOps(LoadOp.clear, StoreOp.store), 344 AttachmentOps(LoadOp.dontCare, StoreOp.dontCare), 345 trans(ImageLayout.depthStencilAttachmentOptimal, ImageLayout.depthStencilAttachmentOptimal), 346 No.mayAlias 347 ), 348 ]; 349 const subpasses = [ 350 SubpassDescription( 351 [], [], some(AttachmentRef(0, ImageLayout.depthStencilAttachmentOptimal)), [] 352 ), 353 ]; 354 shadowRenderPass = device.createRenderPass(attachments, subpasses, []); 355 shadowFinishedSem = device.createSemaphore(); 356 } 357 358 void prepareMeshRenderPass() { 359 const attachments = [ 360 AttachmentDescription(swapchain.format, 1, 361 AttachmentOps(LoadOp.clear, StoreOp.store), 362 AttachmentOps(LoadOp.dontCare, StoreOp.dontCare), 363 trans(ImageLayout.presentSrc, ImageLayout.presentSrc), 364 No.mayAlias 365 ), 366 AttachmentDescription(findDepthFormat(), 1, 367 AttachmentOps(LoadOp.clear, StoreOp.dontCare), 368 AttachmentOps(LoadOp.dontCare, StoreOp.dontCare), 369 trans(ImageLayout.undefined, ImageLayout.depthStencilAttachmentOptimal), 370 No.mayAlias 371 ) 372 ]; 373 const subpasses = [ 374 SubpassDescription( 375 [], [ AttachmentRef(0, ImageLayout.colorAttachmentOptimal) ], 376 some(AttachmentRef(1, ImageLayout.depthStencilAttachmentOptimal)), 377 [] 378 ) 379 ]; 380 meshRenderPass = device.createRenderPass(attachments, subpasses, []); 381 } 382 383 void prepareFramebuffers() { 384 foreach (ref l; lights) { 385 l.shadowFb = device.createFramebuffer( 386 shadowRenderPass, [ l.shadowPlane.obj ], shadowSize, shadowSize, 1 387 ); 388 } 389 390 auto b = autoCmdBuf().rc; 391 392 foreach (img; scImages) { 393 394 auto pi = new PerImage; 395 pi.color = img; 396 pi.colorView = img.createView( 397 ImageType.d2, ImageSubresourceRange(ImageAspect.color), Swizzle.identity 398 ); 399 pi.depth = createDepthImage(surfaceSize[0], surfaceSize[1]); 400 pi.depthView = pi.depth.createView( 401 ImageType.d2, ImageSubresourceRange(ImageAspect.depth), Swizzle.identity 402 ); 403 pi.meshFramebuffer = device.createFramebuffer(meshRenderPass, [ 404 pi.colorView.obj, pi.depthView.obj 405 ], surfaceSize[0], surfaceSize[1], 1); 406 407 b.cmdBuf.pipelineBarrier( 408 trans(PipelineStage.colorAttachmentOutput, PipelineStage.colorAttachmentOutput), [], [ 409 ImageMemoryBarrier( 410 trans(Access.none, Access.colorAttachmentWrite), 411 trans(ImageLayout.undefined, ImageLayout.presentSrc), 412 trans(queueFamilyIgnored, queueFamilyIgnored), 413 img, ImageSubresourceRange(ImageAspect.color) 414 ) 415 ] 416 ); 417 418 b.cmdBuf.pipelineBarrier( 419 trans(PipelineStage.topOfPipe, PipelineStage.earlyFragmentTests), [], [ 420 ImageMemoryBarrier( 421 trans(Access.none, Access.depthStencilAttachmentRead | Access.depthStencilAttachmentWrite), 422 trans(ImageLayout.undefined, ImageLayout.depthStencilAttachmentOptimal), 423 trans(queueFamilyIgnored, queueFamilyIgnored), 424 pi.depth, ImageSubresourceRange(ImageAspect.depth) 425 ) 426 ] 427 ); 428 429 framebuffers ~= pi; 430 } 431 } 432 433 void prepareDescriptors() { 434 435 shadowDSLayout = device.createDescriptorSetLayout( 436 [ 437 PipelineLayoutBinding(0, DescriptorType.uniformBufferDynamic, 1, ShaderStage.vertex), 438 ] 439 ); 440 shadowLayout = device.createPipelineLayout( 441 [ shadowDSLayout ], [] 442 ); 443 444 meshDSLayout = device.createDescriptorSetLayout( 445 [ 446 PipelineLayoutBinding(0, DescriptorType.uniformBufferDynamic, 1, ShaderStage.vertex), 447 PipelineLayoutBinding(1, DescriptorType.uniformBufferDynamic, 1, ShaderStage.fragment), 448 PipelineLayoutBinding(2, DescriptorType.uniformBuffer, 1, ShaderStage.fragment), 449 PipelineLayoutBinding(3, DescriptorType.combinedImageSampler, 1, ShaderStage.fragment), 450 ] 451 ); 452 meshLayout = device.createPipelineLayout( 453 [ meshDSLayout ], [] 454 ); 455 456 descPool = device.createDescriptorPool( 2, 457 [ 458 DescriptorPoolSize(DescriptorType.uniformBufferDynamic, 3), 459 DescriptorPoolSize(DescriptorType.uniformBuffer, 1), 460 DescriptorPoolSize(DescriptorType.combinedImageSampler, 1) 461 ] 462 ); 463 464 auto dss = descPool.allocate( [ shadowDSLayout, meshDSLayout ] ); 465 shadowDS = dss[0]; 466 meshDS = dss[1]; 467 468 const shadowVsLen = cast(uint)(ShadowVsLocals.sizeof * lights.length * meshes.length); 469 const meshVsLen = cast(uint)(MeshVsLocals.sizeof * meshes.length); 470 const ligFsLen = cast(uint)MeshFsLights.sizeof; 471 472 import std.algorithm : map; 473 import std.array : array; 474 475 auto writes = [ 476 WriteDescriptorSet(shadowDS, 0, 0, new UniformBufferDynamicDescWrites( 477 [ BufferRange(meshUniformBuf, 0, ShadowVsLocals.sizeof) ] 478 )), 479 WriteDescriptorSet(meshDS, 0, 0, new UniformBufferDynamicDescWrites( 480 [ BufferRange(meshUniformBuf, shadowVsLen, MeshVsLocals.sizeof) ] 481 )), 482 WriteDescriptorSet(meshDS, 1, 0, new UniformBufferDynamicDescWrites( 483 [ BufferRange(meshUniformBuf, shadowVsLen+meshVsLen, MeshFsMaterial.sizeof) ] 484 )), 485 WriteDescriptorSet(meshDS, 2, 0, new UniformBufferDescWrites( 486 [ BufferRange(ligUniformBuf, 0, ligFsLen) ] 487 )), 488 WriteDescriptorSet(meshDS, 3, 0, new CombinedImageSamplerDescWrites( 489 [ CombinedImageSampler(shadowSampler, meshShadowView, ImageLayout.depthStencilReadOnlyOptimal) ] 490 )) 491 ]; 492 device.updateDescriptorSets(writes, []); 493 } 494 495 PipelineInfo prepareShadowPipeline() 496 { 497 PipelineInfo info; 498 info.shaders.vertex = device.createShaderModule( 499 cast(immutable(uint)[])import("shadow.vert.spv"), "main" 500 ); 501 info.shaders.fragment = device.createShaderModule( 502 cast(immutable(uint)[])import("shadow.frag.spv"), "main" 503 ); 504 info.shaders.vertex.retain(); 505 info.shaders.fragment.retain(); 506 507 info.inputBindings = [ 508 VertexInputBinding(0, Vertex.sizeof, No.instanced) 509 ]; 510 info.inputAttribs = [ 511 VertexInputAttrib(0, 0, Format.rgb32_sFloat, 0), 512 VertexInputAttrib(1, 0, Format.rgb32_sFloat, Vertex.normal.offsetof), 513 ]; 514 info.assembly = InputAssembly(Primitive.triangleList, No.primitiveRestart); 515 info.rasterizer = Rasterizer( 516 PolygonMode.fill, Cull.back, FrontFace.ccw, No.depthClamp, 517 some(DepthBias(1f, 0f, 2f)), 1f 518 ); 519 info.viewports = [ 520 ViewportConfig( 521 Viewport(0, 0, cast(float)shadowSize, cast(float)shadowSize), 522 Rect(0, 0, shadowSize, shadowSize) 523 ) 524 ]; 525 info.depthInfo = DepthInfo( 526 Yes.enabled, Yes.write, CompareOp.lessOrEqual, No.boundsTest, 0f, 1f 527 ); 528 info.blendInfo = ColorBlendInfo( 529 none!LogicOp, [], [ 0f, 0f, 0f, 0f ] 530 ); 531 info.layout = shadowLayout; 532 info.renderPass = shadowRenderPass; 533 info.subpassIndex = 0; 534 535 return info; 536 } 537 538 PipelineInfo prepareMeshPipeline() 539 { 540 PipelineInfo info; 541 info.shaders.vertex = device.createShaderModule( 542 cast(immutable(uint)[])import("mesh.vert.spv"), "main" 543 ); 544 info.shaders.fragment = device.createShaderModule( 545 cast(immutable(uint)[])import("mesh.frag.spv"), "main" 546 ); 547 info.shaders.vertex.retain(); 548 info.shaders.fragment.retain(); 549 550 info.inputBindings = [ 551 VertexInputBinding(0, Vertex.sizeof, No.instanced) 552 ]; 553 info.inputAttribs = [ 554 VertexInputAttrib(0, 0, Format.rgb32_sFloat, 0), 555 VertexInputAttrib(1, 0, Format.rgb32_sFloat, Vertex.normal.offsetof), 556 ]; 557 info.assembly = InputAssembly(Primitive.triangleList, No.primitiveRestart); 558 info.rasterizer = Rasterizer( 559 PolygonMode.fill, Cull.back, FrontFace.ccw, No.depthClamp, 560 none!DepthBias, 1f 561 ); 562 info.viewports = [ 563 ViewportConfig( 564 Viewport(0, 0, cast(float)surfaceSize[0], cast(float)surfaceSize[1]), 565 Rect(0, 0, surfaceSize[0], surfaceSize[1]) 566 ) 567 ]; 568 info.depthInfo = DepthInfo( 569 Yes.enabled, Yes.write, CompareOp.lessOrEqual, No.boundsTest, 0f, 1f 570 ); 571 info.blendInfo = ColorBlendInfo( 572 none!LogicOp, [ ColorBlendAttachment.solid() ], [ 0f, 0f, 0f, 0f ] 573 ); 574 info.layout = meshLayout; 575 info.renderPass = meshRenderPass; 576 info.subpassIndex = 0; 577 578 return info; 579 } 580 581 void preparePipelines() { 582 auto infos = [ 583 prepareShadowPipeline(), prepareMeshPipeline() 584 ]; 585 auto pls = device.createPipelines(infos); 586 shadowPipeline = pls[0]; 587 meshPipeline = pls[1]; 588 589 foreach (ref i; infos) { 590 i.shaders.vertex.release(); 591 i.shaders.fragment.release(); 592 } 593 } 594 595 void updateBuffers(in FMat4 viewProj) { 596 597 const axis = fvec(0, 0, 1); 598 foreach (ref m; meshes) { 599 const r = rotation(m.pulse, axis); 600 m.model *= r; 601 } 602 603 const shadowVsLen = cast(uint)(ShadowVsLocals.sizeof * lights.length * meshes.length); 604 const meshVsLen = cast(uint)(MeshVsLocals.sizeof * meshes.length); 605 606 import gfx.graal.device : MappedMemorySet; 607 608 auto mm = meshUniformBuf.boundMemory.map(); 609 610 { 611 auto v = mm.view!(ShadowVsLocals[])(0, lights.length*meshes.length); 612 foreach (il, ref l; lights) { 613 foreach (im, ref m; meshes) { 614 const mat = ShadowVsLocals(transpose(l.proj * l.view * m.model)); 615 v[il*meshes.length + im] = mat; 616 } 617 } 618 } 619 { 620 auto v = mm.view!(MeshVsLocals[])(shadowVsLen, meshes.length); 621 foreach (im, ref m; meshes) { 622 v[im] = MeshVsLocals( 623 transpose(viewProj * m.model), 624 transpose(m.model), 625 ); 626 } 627 } 628 { 629 auto v = mm.view!(MeshFsMaterial[])(shadowVsLen+meshVsLen, meshes.length); 630 foreach (im, ref m; meshes) { 631 v[im] = MeshFsMaterial( m.color ); 632 } 633 } 634 MappedMemorySet mms; 635 mm.addToSet(mms); 636 device.flushMappedMemory(mms); 637 } 638 639 override void render() 640 { 641 import core.time : dur; 642 import gfx.graal.queue : PresentRequest, StageWait, Submission; 643 import std.algorithm : map; 644 import std.array : array; 645 646 bool needReconstruction; 647 const imgInd = swapchain.acquireNextImage(dur!"seconds"(-1), imageAvailableSem, needReconstruction); 648 const cmdBufInd = nextCmdBuf(); 649 650 // we have cmdbufs for each light that must be used on every frame 651 // therefore, we must sync with the same fence on every frame 652 fences[0].wait(); 653 fences[0].reset(); 654 655 recordCmds(cmdBufInd, imgInd); 656 657 auto shadowSubmission = Submission( 658 [], 659 [ shadowFinishedSem.obj ], 660 lights.map!((ref Light l) { return l.cmdBuf; }).array 661 ); 662 auto meshSubmission = Submission( 663 [ 664 StageWait(shadowFinishedSem, PipelineStage.fragmentShader), 665 StageWait(imageAvailableSem, PipelineStage.transfer) 666 ], 667 [ renderingFinishSem.obj ], [ cmdBufs[cmdBufInd] ] 668 ); 669 670 graphicsQueue.submit( 671 [ 672 shadowSubmission, 673 meshSubmission 674 ], 675 fences[0] 676 ); 677 678 presentQueue.present( 679 [ renderingFinishSem.obj ], 680 [ PresentRequest(swapchain, imgInd) ] 681 ); 682 683 // if (needReconstruction) { 684 // prepareSwapchain(swapchain); 685 // presentPool.reset(); 686 // } 687 } 688 689 690 override void recordCmds(ulong cmdBufInd, ulong imgInd) 691 { 692 void recordLight(uint il, ref Light l) { 693 auto buf = l.cmdBuf; 694 buf.begin(No.persistent); 695 696 buf.beginRenderPass( 697 shadowRenderPass, l.shadowFb, Rect(0, 0, shadowSize, shadowSize), 698 [ ClearValues.depthStencil(1f, 0) ] 699 ); 700 701 buf.bindPipeline(shadowPipeline); 702 foreach (c, m; meshes) { 703 buf.bindIndexBuffer(indBuf, m.indOffset, IndexType.u16); 704 buf.bindVertexBuffers(0, [ VertexBinding(vertBuf, m.vertOffset) ]); 705 buf.bindDescriptorSets( 706 PipelineBindPoint.graphics, shadowLayout, 0, [ shadowDS ], 707 [ 708 (il*meshes.length + c) * ShadowVsLocals.sizeof 709 ] 710 ); 711 buf.drawIndexed(m.numVertices, 1, 0, 0, 0); 712 } 713 714 buf.endRenderPass(); 715 716 buf.end(); 717 } 718 719 void recordMeshes() { 720 auto buf = cmdBufs[cmdBufInd]; 721 auto fb = framebuffers[imgInd]; 722 723 buf.begin(No.persistent); 724 buf.beginRenderPass( 725 meshRenderPass, fb.meshFramebuffer, Rect(0, 0, surfaceSize[0], surfaceSize[1]), 726 [ 727 ClearValues.color(0.6f, 0.6f, 0.6f, hasAlpha ? 0.5f : 1f), 728 ClearValues.depthStencil(1f, 0) 729 ] 730 ); 731 732 buf.bindPipeline(meshPipeline); 733 foreach (c, m; meshes) { 734 buf.bindIndexBuffer(indBuf, m.indOffset, IndexType.u16); 735 buf.bindVertexBuffers(0, [ VertexBinding(vertBuf, m.vertOffset) ]); 736 buf.bindDescriptorSets( 737 PipelineBindPoint.graphics, meshLayout, 0, [ meshDS ], 738 [ 739 c * MeshVsLocals.sizeof, c * MeshFsMaterial.sizeof 740 ] 741 ); 742 buf.drawIndexed(m.numVertices, 1, 0, 0, 0); 743 } 744 745 buf.endRenderPass(); 746 buf.end(); 747 } 748 749 foreach (uint il, ref l; lights) { 750 recordLight(il, l); 751 } 752 recordMeshes(); 753 } 754 755 } 756 757 int main(string[] args) { 758 759 try { 760 auto example = new ShadowExample(args); 761 example.prepare(); 762 scope(exit) example.dispose(); 763 764 example.window.onMouseOn = (uint, uint) { 765 example.window.closeFlag = true; 766 }; 767 768 const winSize = example.surfaceSize; 769 const proj = perspective(45, winSize[0]/(cast(float)winSize[1]), 1f, 20f); 770 const viewProj = proj * lookAt(fvec(3, -10, 6), fvec(0, 0, 0), fvec(0, 0, 1)); 771 772 FPSProbe fpsProbe; 773 fpsProbe.start(); 774 775 enum reportFreq = 60; 776 777 while (!example.window.closeFlag) { 778 779 example.updateBuffers(viewProj); 780 example.render(); 781 example.display.pollAndDispatch(); 782 783 fpsProbe.tick(); 784 if ((fpsProbe.frameCount % reportFreq) == 0) { 785 writeln("FPS: ", fpsProbe.fps); 786 } 787 } 788 789 return 0; 790 } 791 catch(Exception ex) { 792 stderr.writeln("error occured: ", ex.msg); 793 return 1; 794 } 795 }