1 module declapi; 2 3 import example; 4 5 import gfx.core.rc; 6 import gfx.core.typecons; 7 import gfx.decl.engine; 8 import gfx.graal.buffer; 9 import gfx.graal.cmd; 10 import gfx.graal.device; 11 import gfx.graal.format; 12 import gfx.graal.image; 13 import gfx.graal.memory; 14 import gfx.graal.pipeline; 15 import gfx.graal.presentation; 16 import gfx.graal.queue; 17 import gfx.graal.renderpass; 18 import gfx.graal.types; 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 disposeArr(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 declEng = new DeclarativeEngine(device); 116 declEng.declareStruct!Vertex(); 117 declEng.addView!"shader.vert.spv"(); 118 declEng.addView!"shader.frag.spv"(); 119 declEng.store.store("sc_format", swapchain.format); 120 declEng.store.store("depth_format", findDepthFormat()); 121 //declEng.store.store("rp", renderPass); 122 declEng.parseSDLView!"pipeline.sdl"(); 123 124 prepareBuffers(); 125 prepareRenderPass(); 126 prepareFramebuffers(); 127 preparePipeline(); 128 prepareDescriptorSet(); 129 } 130 131 void prepareBuffers() { 132 133 import gfx.genmesh.cube : genCube; 134 import gfx.genmesh.algorithm : indexCollectMesh, triangulate, vertices; 135 import gfx.genmesh.poly : quad; 136 import std.algorithm : map; 137 138 const cube = genCube() 139 .map!(f => quad( 140 Vertex( fvec(f[0].p), fvec(f[0].n) ), 141 Vertex( fvec(f[1].p), fvec(f[1].n) ), 142 Vertex( fvec(f[2].p), fvec(f[2].n) ), 143 Vertex( fvec(f[3].p), fvec(f[3].n) ), 144 )) 145 .triangulate() 146 .vertices() 147 .indexCollectMesh(); 148 149 cubeLen = cube.vertices.length; 150 151 const red = fvec( 1f, 0f, 0f, 1f ); 152 const green = fvec( 0f, 1f, 0f, 1f ); 153 const blue = fvec( 0f, 0f, 1f, 1f ); 154 const colors = [ red, green, blue ]; 155 156 Vertex[] verts; 157 foreach (c; 0 .. cubeCount) { 158 verts ~= cube.vertices; 159 const color = colors[c % 3]; 160 foreach (v; 0 .. cubeLen) { 161 verts[$ - cubeLen + v].color = color; 162 } 163 } 164 165 indices = cube.indices; 166 vertBuf = createStaticBuffer(verts, BufferUsage.vertex); 167 indBuf = createStaticBuffer(indices, BufferUsage.index); 168 169 const lights = Lights( [ 170 Light(normalize(fvec(1.0, 1.0, -1.0, 0.0)), fvec(0.8, 0.5, 0.2, 1.0)), 171 Light(normalize(fvec(-1.0, 1.0, -1.0, 0.0)), fvec(0.2, 0.5, 0.8, 1.0)), 172 Light.init, Light.init, Light.init 173 ], 2); 174 175 matBuf = createDynamicBuffer(cubeCount * Matrices.sizeof, BufferUsage.uniform); 176 ligBuf = createStaticBuffer(lights, BufferUsage.uniform); 177 } 178 179 void prepareRenderPass() { 180 renderPass = declEng.store.expect!RenderPass("rp"); 181 } 182 183 void prepareFramebuffers() 184 { 185 auto b = autoCmdBuf().rc; 186 187 foreach (img; scImages) { 188 auto pi = new PerImage; 189 pi.color = img; 190 pi.colorView = img.createView( 191 ImageType.d2, ImageSubresourceRange(ImageAspect.color), Swizzle.identity 192 ); 193 pi.depth = createDepthImage(surfaceSize[0], surfaceSize[1]); 194 pi.depthView = pi.depth.createView( 195 ImageType.d2, ImageSubresourceRange(ImageAspect.depth), Swizzle.identity 196 ); 197 pi.framebuffer = device.createFramebuffer(renderPass, [ 198 pi.colorView.obj, pi.depthView.obj 199 ], surfaceSize[0], surfaceSize[1], 1); 200 201 b.cmdBuf.pipelineBarrier( 202 trans(PipelineStage.colorAttachmentOutput, PipelineStage.colorAttachmentOutput), [], 203 [ ImageMemoryBarrier( 204 trans(Access.none, Access.colorAttachmentWrite), 205 trans(ImageLayout.undefined, ImageLayout.presentSrc), 206 trans(queueFamilyIgnored, queueFamilyIgnored), 207 img, ImageSubresourceRange(ImageAspect.color) 208 ) ] 209 ); 210 211 const hasStencil = formatDesc(pi.depth.info.format).surfaceType.stencilBits > 0; 212 const aspect = hasStencil ? ImageAspect.depthStencil : ImageAspect.depth; 213 b.cmdBuf.pipelineBarrier( 214 trans(PipelineStage.topOfPipe, PipelineStage.earlyFragmentTests), [], [ 215 ImageMemoryBarrier( 216 trans(Access.none, Access.depthStencilAttachmentRead | Access.depthStencilAttachmentWrite), 217 trans(ImageLayout.undefined, ImageLayout.depthStencilAttachmentOptimal), 218 trans(queueFamilyIgnored, queueFamilyIgnored), 219 pi.depth, ImageSubresourceRange(aspect) 220 ) 221 ] 222 ); 223 224 framebuffers ~= pi; 225 } 226 } 227 228 void preparePipeline() 229 { 230 setLayout = declEng.store.expect!DescriptorSetLayout("dsl"); 231 layout = declEng.store.expect!PipelineLayout("layout"); 232 pipeline = declEng.store.expect!Pipeline("pl"); 233 } 234 235 void prepareDescriptorSet() { 236 const poolSizes = [ 237 DescriptorPoolSize(DescriptorType.uniformBufferDynamic, 1), 238 DescriptorPoolSize(DescriptorType.uniformBuffer, 1), 239 ]; 240 descPool = device.createDescriptorPool(1, poolSizes); 241 set = descPool.allocate([ setLayout ])[0]; 242 243 auto writes = [ 244 WriteDescriptorSet(set, 0, 0, new UniformBufferDynamicDescWrites([ 245 BufferRange(matBuf, 0, Matrices.sizeof), 246 ])), 247 WriteDescriptorSet(set, 1, 0, new UniformBufferDescWrites([ 248 BufferRange(ligBuf, 0, Lights.sizeof) 249 ])), 250 ]; 251 device.updateDescriptorSets(writes, []); 252 } 253 254 void updateMatrices(in Matrices[] mat) { 255 auto mm = matBuf.boundMemory.map(); 256 auto v = mm.view!(Matrices[])(0, mat.length); 257 v[] = mat; 258 MappedMemorySet mms; 259 mm.addToSet(mms); 260 device.flushMappedMemory(mms); 261 } 262 263 override void recordCmds(size_t cmdBufInd, size_t imgInd) { 264 import gfx.core.typecons : trans; 265 266 const ccv = ClearColorValues(0.6f, 0.6f, 0.6f, hasAlpha ? 0.5f : 1f); 267 const dcv = ClearDepthStencilValues(1f, 0); 268 269 auto buf = cmdBufs[cmdBufInd]; 270 auto fb = framebuffers[imgInd]; 271 272 buf.begin(No.persistent); 273 274 buf.setViewport(0, [ Viewport(0f, 0f, cast(float)surfaceSize[0], cast(float)surfaceSize[1]) ]); 275 buf.setScissor(0, [ Rect(0, 0, surfaceSize[0], surfaceSize[1]) ]); 276 277 buf.beginRenderPass( 278 renderPass, fb.framebuffer, 279 Rect(0, 0, surfaceSize[0], surfaceSize[1]), 280 [ ClearValues(ccv), ClearValues(dcv) ] 281 ); 282 283 buf.bindPipeline(pipeline); 284 buf.bindIndexBuffer(indBuf, 0, IndexType.u16); 285 foreach(c; 0 .. cubeCount) { 286 buf.bindVertexBuffers(0, [ VertexBinding(vertBuf, Vertex.sizeof*c*cubeLen) ]); 287 buf.bindDescriptorSets(PipelineBindPoint.graphics, layout, 0, [ set ], [c * Matrices.sizeof]); 288 buf.drawIndexed(cast(uint)indices.length, 1, 0, 0, 0); 289 } 290 291 buf.endRenderPass(); 292 293 buf.end(); 294 } 295 296 } 297 298 int main(string[] args) { 299 300 try { 301 auto example = new DeclAPIExample(args); 302 example.prepare(); 303 scope(exit) example.dispose(); 304 305 example.window.onMouseOn = (uint, uint) { 306 example.window.closeFlag = true; 307 }; 308 import std.datetime.stopwatch : StopWatch; 309 310 ulong frameCount; 311 ulong lastUs; 312 StopWatch sw; 313 sw.start(); 314 315 enum reportFreq = 100; 316 317 // 6 RPM at 60 FPS 318 const puls = 6 * 2*PI / 3600f; 319 auto angle = 0f; 320 const view = lookAt(fvec(0, -7, 2), fvec(0, 0, 0), fvec(0, 0, 1)); 321 const proj = perspective!float(45, 4f/3f, 1f, 10f); 322 const viewProj = proj*view; 323 324 DeclAPIExample.Matrices[3] matrices; 325 326 while (!example.window.closeFlag) { 327 328 import gfx.math.inverse : affineInverse; 329 330 foreach (m; 0 .. 3) { 331 const posAngle = cast(float)(m * 2f * PI / 3f); 332 const model = rotation(posAngle + angle, fvec(0, 0, 1)) 333 * translation(2f, 0f, 0f) 334 * rotation(-angle, fvec(0, 0, 1)); 335 const mvp = viewProj*model; 336 matrices[m] = DeclAPIExample.Matrices( 337 mvp.transpose(), 338 model.affineInverse(), // need the transpose of model inverse 339 ); 340 } 341 342 example.updateMatrices(matrices); 343 344 angle += puls; 345 346 example.render(); 347 ++ frameCount; 348 if ((frameCount % reportFreq) == 0) { 349 const us = sw.peek().total!"usecs"; 350 writeln("FPS: ", 1000_000.0 * reportFreq / (us - lastUs)); 351 lastUs = us; 352 } 353 example.display.pollAndDispatch(); 354 } 355 356 return 0; 357 } 358 catch(Exception ex) { 359 stderr.writeln("error occured: ", ex.msg); 360 return 1; 361 } 362 }