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