1 module depth; 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.device; 10 import gfx.graal.format; 11 import gfx.graal.image; 12 import gfx.graal.memory; 13 import gfx.graal.pipeline; 14 import gfx.graal.presentation; 15 import gfx.graal.queue; 16 import gfx.graal.renderpass; 17 import gfx.graal.types; 18 19 import gfx.math; 20 21 import std.exception; 22 import std.stdio; 23 import std.typecons; 24 import std.math; 25 26 class DepthExample : Example 27 { 28 Rc!RenderPass renderPass; 29 Rc!Pipeline pipeline; 30 Rc!PipelineLayout layout; 31 PerImage[] framebuffers; 32 size_t cubeLen; 33 enum cubeCount = 3; 34 const(ushort)[] indices; 35 Rc!Buffer vertBuf; 36 Rc!Buffer indBuf; 37 Rc!Buffer matBuf; 38 Rc!Buffer ligBuf; 39 Rc!DescriptorPool descPool; 40 Rc!DescriptorSetLayout setLayout; 41 DescriptorSet set; 42 43 class PerImage : Disposable { 44 ImageBase color; 45 Rc!ImageView colorView; 46 Rc!Image depth; 47 Rc!ImageView depthView; 48 Rc!Framebuffer framebuffer; 49 50 override void dispose() { 51 colorView.unload(); 52 depth.unload(); 53 depthView.unload(); 54 framebuffer.unload(); 55 } 56 } 57 58 struct Vertex { 59 FVec3 position; 60 FVec3 normal; 61 FVec4 color; 62 } 63 64 struct Matrices { 65 FMat4 mvp; 66 FMat4 normal; 67 } 68 69 enum maxLights = 5; 70 71 struct Light { 72 FVec4 direction; 73 FVec4 color; 74 } 75 76 struct Lights { 77 Light[maxLights] lights; 78 uint num; 79 } 80 81 this(string[] args) { 82 super("Depth", args); 83 } 84 85 override void dispose() { 86 if (device) { 87 device.waitIdle(); 88 } 89 vertBuf.unload(); 90 indBuf.unload(); 91 matBuf.unload(); 92 ligBuf.unload(); 93 disposeArr(framebuffers); 94 setLayout.unload(); 95 descPool.unload(); 96 layout.unload(); 97 pipeline.unload(); 98 renderPass.unload(); 99 super.dispose(); 100 } 101 102 override void prepare() { 103 super.prepare(); 104 prepareBuffers(); 105 prepareRenderPass(); 106 prepareFramebuffers(); 107 preparePipeline(); 108 prepareDescriptorSet(); 109 } 110 111 void prepareBuffers() { 112 113 import gfx.genmesh.cube : genCube; 114 import gfx.genmesh.algorithm : indexCollectMesh, triangulate, vertices; 115 import gfx.genmesh.poly : quad; 116 import std.algorithm : map; 117 118 const cube = genCube() 119 .map!(f => quad( 120 Vertex( fvec(f[0].p), fvec(f[0].n) ), 121 Vertex( fvec(f[1].p), fvec(f[1].n) ), 122 Vertex( fvec(f[2].p), fvec(f[2].n) ), 123 Vertex( fvec(f[3].p), fvec(f[3].n) ), 124 )) 125 .triangulate() 126 .vertices() 127 .indexCollectMesh(); 128 129 cubeLen = cube.vertices.length; 130 131 const red = fvec( 1f, 0f, 0f, 1f ); 132 const green = fvec( 0f, 1f, 0f, 1f ); 133 const blue = fvec( 0f, 0f, 1f, 1f ); 134 const colors = [ red, green, blue ]; 135 136 Vertex[] verts; 137 foreach (c; 0 .. cubeCount) { 138 verts ~= cube.vertices; 139 const color = colors[c % 3]; 140 foreach (v; 0 .. cubeLen) { 141 verts[$ - cubeLen + v].color = color; 142 } 143 } 144 145 indices = cube.indices; 146 vertBuf = createStaticBuffer(verts, BufferUsage.vertex); 147 indBuf = createStaticBuffer(indices, BufferUsage.index); 148 149 const lights = Lights( [ 150 Light(normalize(fvec(1.0, 1.0, -1.0, 0.0)), fvec(0.8, 0.5, 0.2, 1.0)), 151 Light(normalize(fvec(-1.0, 1.0, -1.0, 0.0)), fvec(0.2, 0.5, 0.8, 1.0)), 152 Light.init, Light.init, Light.init 153 ], 2); 154 155 matBuf = createDynamicBuffer(cubeCount * Matrices.sizeof, BufferUsage.uniform); 156 ligBuf = createStaticBuffer(lights, BufferUsage.uniform); 157 } 158 159 void prepareRenderPass() { 160 const attachments = [ 161 AttachmentDescription(swapchain.format, 1, 162 AttachmentOps(LoadOp.clear, StoreOp.store), 163 AttachmentOps(LoadOp.dontCare, StoreOp.dontCare), 164 trans(ImageLayout.presentSrc, ImageLayout.presentSrc), 165 No.mayAlias 166 ), 167 AttachmentDescription(findDepthFormat(), 1, 168 AttachmentOps(LoadOp.clear, StoreOp.dontCare), 169 AttachmentOps(LoadOp.dontCare, StoreOp.dontCare), 170 trans(ImageLayout.undefined, ImageLayout.depthStencilAttachmentOptimal), 171 No.mayAlias 172 ) 173 ]; 174 const subpasses = [ 175 SubpassDescription( 176 [], [ AttachmentRef(0, ImageLayout.colorAttachmentOptimal) ], 177 some(AttachmentRef(1, ImageLayout.depthStencilAttachmentOptimal)), 178 [] 179 ) 180 ]; 181 182 renderPass = device.createRenderPass(attachments, subpasses, []); 183 } 184 185 void prepareFramebuffers() 186 { 187 auto b = autoCmdBuf().rc; 188 189 foreach (img; scImages) { 190 auto pi = new PerImage; 191 pi.color = img; 192 pi.colorView = img.createView( 193 ImageType.d2, ImageSubresourceRange(ImageAspect.color), Swizzle.identity 194 ); 195 pi.depth = createDepthImage(surfaceSize[0], surfaceSize[1]); 196 pi.depthView = pi.depth.createView( 197 ImageType.d2, ImageSubresourceRange(ImageAspect.depth), Swizzle.identity 198 ); 199 pi.framebuffer = device.createFramebuffer(renderPass, [ 200 pi.colorView.obj, pi.depthView.obj 201 ], surfaceSize[0], surfaceSize[1], 1); 202 203 b.cmdBuf.pipelineBarrier( 204 trans(PipelineStage.colorAttachmentOutput, PipelineStage.colorAttachmentOutput), [], 205 [ ImageMemoryBarrier( 206 trans(Access.none, Access.colorAttachmentWrite), 207 trans(ImageLayout.undefined, ImageLayout.presentSrc), 208 trans(queueFamilyIgnored, queueFamilyIgnored), 209 img, ImageSubresourceRange(ImageAspect.color) 210 ) ] 211 ); 212 213 const hasStencil = formatDesc(pi.depth.info.format).surfaceType.stencilBits > 0; 214 const aspect = hasStencil ? ImageAspect.depthStencil : ImageAspect.depth; 215 b.cmdBuf.pipelineBarrier( 216 trans(PipelineStage.topOfPipe, PipelineStage.earlyFragmentTests), [], [ 217 ImageMemoryBarrier( 218 trans(Access.none, Access.depthStencilAttachmentRead | Access.depthStencilAttachmentWrite), 219 trans(ImageLayout.undefined, ImageLayout.depthStencilAttachmentOptimal), 220 trans(queueFamilyIgnored, queueFamilyIgnored), 221 pi.depth, ImageSubresourceRange(aspect) 222 ) 223 ] 224 ); 225 226 framebuffers ~= pi; 227 } 228 } 229 230 void preparePipeline() 231 { 232 Rc!ShaderModule vtxShader; 233 Rc!ShaderModule fragShader; 234 235 vtxShader = device.createShaderModule( 236 cast(immutable(uint)[])import("shader.vert.spv"), "main" 237 ); 238 fragShader = device.createShaderModule( 239 cast(immutable(uint)[])import("shader.frag.spv"), "main" 240 ); 241 242 const layoutBindings = [ 243 PipelineLayoutBinding(0, DescriptorType.uniformBufferDynamic, 1, ShaderStage.vertex), 244 PipelineLayoutBinding(1, DescriptorType.uniformBuffer, 1, ShaderStage.fragment), 245 ]; 246 247 setLayout = device.createDescriptorSetLayout(layoutBindings); 248 layout = device.createPipelineLayout([setLayout], []); 249 250 PipelineInfo info; 251 info.shaders.vertex = vtxShader; 252 info.shaders.fragment = fragShader; 253 info.inputBindings = [ 254 VertexInputBinding(0, Vertex.sizeof, No.instanced) 255 ]; 256 info.inputAttribs = [ 257 VertexInputAttrib(0, 0, Format.rgb32_sFloat, 0), 258 VertexInputAttrib(1, 0, Format.rgb32_sFloat, Vertex.normal.offsetof), 259 VertexInputAttrib(2, 0, Format.rgba32_sFloat, Vertex.color.offsetof), 260 ]; 261 info.assembly = InputAssembly(Primitive.triangleList, No.primitiveRestart); 262 info.rasterizer = Rasterizer( 263 PolygonMode.fill, Cull.back, FrontFace.ccw, No.depthClamp, 264 none!DepthBias, 1f 265 ); 266 // info.viewports = [ 267 // ViewportConfig( 268 // Viewport(0, 0, cast(float)surfaceSize[0], cast(float)surfaceSize[1]), 269 // Rect(0, 0, surfaceSize[0], surfaceSize[1]) 270 // ) 271 // ]; 272 info.depthInfo = DepthInfo( 273 Yes.enabled, Yes.write, CompareOp.less, No.boundsTest, 0f, 1f 274 ); 275 info.blendInfo = ColorBlendInfo( 276 none!LogicOp, [ 277 ColorBlendAttachment(No.enabled, 278 BlendState(trans(BlendFactor.one, BlendFactor.zero), BlendOp.add), 279 BlendState(trans(BlendFactor.one, BlendFactor.zero), BlendOp.add), 280 ColorMask.all 281 ) 282 ], 283 [ 0f, 0f, 0f, 0f ] 284 ); 285 info.dynamicStates = [ DynamicState.viewport, DynamicState.scissor ]; 286 info.layout = layout; 287 info.renderPass = renderPass; 288 info.subpassIndex = 0; 289 290 auto pls = device.createPipelines( [info] ); 291 pipeline = pls[0]; 292 } 293 294 void prepareDescriptorSet() { 295 const poolSizes = [ 296 DescriptorPoolSize(DescriptorType.uniformBufferDynamic, 1), 297 DescriptorPoolSize(DescriptorType.uniformBuffer, 1), 298 ]; 299 descPool = device.createDescriptorPool(1, poolSizes); 300 set = descPool.allocate([ setLayout ])[0]; 301 302 auto writes = [ 303 WriteDescriptorSet(set, 0, 0, new UniformBufferDynamicDescWrites([ 304 BufferRange(matBuf, 0, Matrices.sizeof), 305 ])), 306 WriteDescriptorSet(set, 1, 0, new UniformBufferDescWrites([ 307 BufferRange(ligBuf, 0, Lights.sizeof) 308 ])), 309 ]; 310 device.updateDescriptorSets(writes, []); 311 } 312 313 void updateMatrices(in Matrices[] mat) { 314 auto mm = matBuf.boundMemory.map(); 315 auto v = mm.view!(Matrices[])(0, mat.length); 316 v[] = mat; 317 MappedMemorySet mms; 318 mm.addToSet(mms); 319 device.flushMappedMemory(mms); 320 } 321 322 override void recordCmds(size_t cmdBufInd, size_t imgInd) { 323 import gfx.core.typecons : trans; 324 325 const ccv = ClearColorValues(0.6f, 0.6f, 0.6f, hasAlpha ? 0.5f : 1f); 326 const dcv = ClearDepthStencilValues(1f, 0); 327 328 auto buf = cmdBufs[cmdBufInd]; 329 auto fb = framebuffers[imgInd]; 330 331 buf.begin(No.persistent); 332 333 buf.setViewport(0, [ Viewport(0f, 0f, cast(float)surfaceSize[0], cast(float)surfaceSize[1]) ]); 334 buf.setScissor(0, [ Rect(0, 0, surfaceSize[0], surfaceSize[1]) ]); 335 336 buf.beginRenderPass( 337 renderPass, fb.framebuffer, 338 Rect(0, 0, surfaceSize[0], surfaceSize[1]), 339 [ ClearValues(ccv), ClearValues(dcv) ] 340 ); 341 342 buf.bindPipeline(pipeline); 343 buf.bindIndexBuffer(indBuf, 0, IndexType.u16); 344 foreach(c; 0 .. cubeCount) { 345 buf.bindVertexBuffers(0, [ VertexBinding(vertBuf, Vertex.sizeof*c*cubeLen) ]); 346 buf.bindDescriptorSets(PipelineBindPoint.graphics, layout, 0, [ set ], [c * Matrices.sizeof]); 347 buf.drawIndexed(cast(uint)indices.length, 1, 0, 0, 0); 348 } 349 350 buf.endRenderPass(); 351 352 buf.end(); 353 } 354 355 } 356 357 int main(string[] args) { 358 359 try { 360 auto example = new DepthExample(args); 361 example.prepare(); 362 scope(exit) example.dispose(); 363 364 example.window.onMouseOn = (uint, uint) { 365 example.window.closeFlag = true; 366 }; 367 import std.datetime.stopwatch : StopWatch; 368 369 ulong frameCount; 370 ulong lastUs; 371 StopWatch sw; 372 sw.start(); 373 374 enum reportFreq = 100; 375 376 // 6 RPM at 60 FPS 377 const puls = 6 * 2*PI / 3600f; 378 auto angle = 0f; 379 const view = lookAt(fvec(0, -7, 2), fvec(0, 0, 0), fvec(0, 0, 1)); 380 const proj = perspective!float(45, 4f/3f, 1f, 10f); 381 const viewProj = proj*view; 382 383 DepthExample.Matrices[3] matrices; 384 385 while (!example.window.closeFlag) { 386 387 import gfx.math.inverse : affineInverse; 388 389 foreach (m; 0 .. 3) { 390 const posAngle = cast(float)(m * 2f * PI / 3f); 391 const model = rotation(posAngle + angle, fvec(0, 0, 1)) 392 * translation(2f, 0f, 0f) 393 * rotation(-angle, fvec(0, 0, 1)); 394 const mvp = viewProj*model; 395 matrices[m] = DepthExample.Matrices( 396 mvp.transpose(), 397 model.affineInverse(), // need the transpose of model inverse 398 ); 399 } 400 401 example.updateMatrices(matrices); 402 403 angle += puls; 404 405 example.render(); 406 ++ frameCount; 407 if ((frameCount % reportFreq) == 0) { 408 const us = sw.peek().total!"usecs"; 409 writeln("FPS: ", 1000_000.0 * reportFreq / (us - lastUs)); 410 lastUs = us; 411 } 412 example.display.pollAndDispatch(); 413 } 414 415 return 0; 416 } 417 catch(Exception ex) { 418 stderr.writeln("error occured: ", ex.msg); 419 return 1; 420 } 421 }