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