1 module declapi; 2 3 import example; 4 5 import gfx.core; 6 import gfx.decl.engine; 7 import gfx.graal; 8 import gfx.window; 9 10 import gfx.math; 11 12 import std.exception; 13 import std.stdio; 14 import std.typecons; 15 import std.math; 16 17 class DeclApiExample : Example 18 { 19 Rc!RenderPass renderPass; 20 Rc!Pipeline pipeline; 21 Rc!PipelineLayout layout; 22 size_t cubeLen; 23 enum cubeCount = 3; 24 const(ushort)[] indices; 25 Rc!Buffer vertBuf; 26 Rc!Buffer indBuf; 27 Rc!Buffer matBuf; 28 Rc!Buffer ligBuf; 29 Rc!DescriptorPool descPool; 30 Rc!DescriptorSetLayout setLayout; 31 DescriptorSet set; 32 33 DeclarativeEngine declEng; 34 35 struct Vertex 36 { 37 @(Format.rgb32_sFloat) 38 FVec3 position; 39 40 @(Format.rgb32_sFloat) 41 FVec3 normal; 42 43 @(Format.rgba32_sFloat) 44 FVec4 color; 45 } 46 47 struct Matrices { 48 FMat4 mvp; 49 FMat4 normal; 50 } 51 52 enum maxLights = 5; 53 54 struct Light { 55 FVec4 direction; 56 FVec4 color; 57 } 58 59 struct Lights { 60 Light[maxLights] lights; 61 uint num; 62 } 63 64 this(string[] args) { 65 super("Declarative API", args); 66 } 67 68 override void dispose() { 69 if (device) { 70 device.waitIdle(); 71 } 72 vertBuf.unload(); 73 indBuf.unload(); 74 matBuf.unload(); 75 ligBuf.unload(); 76 setLayout.unload(); 77 descPool.unload(); 78 layout.unload(); 79 pipeline.unload(); 80 renderPass.unload(); 81 declEng.dispose(); 82 super.dispose(); 83 } 84 85 override void prepare() { 86 87 super.prepare(); 88 89 prepareBuffers(); 90 preparePipeline(); 91 prepareDescriptorSet(); 92 } 93 94 void prepareBuffers() { 95 96 import gfx.genmesh.cube : genCube; 97 import gfx.genmesh.algorithm : indexCollectMesh, triangulate, vertices; 98 import gfx.genmesh.poly : quad; 99 import std.algorithm : map; 100 101 const cube = genCube() 102 .map!(f => quad( 103 Vertex( fvec(f[0].p), fvec(f[0].n) ), 104 Vertex( fvec(f[1].p), fvec(f[1].n) ), 105 Vertex( fvec(f[2].p), fvec(f[2].n) ), 106 Vertex( fvec(f[3].p), fvec(f[3].n) ), 107 )) 108 .triangulate() 109 .vertices() 110 .indexCollectMesh(); 111 112 cubeLen = cube.vertices.length; 113 114 const red = fvec( 1f, 0f, 0f, 1f ); 115 const green = fvec( 0f, 1f, 0f, 1f ); 116 const blue = fvec( 0f, 0f, 1f, 1f ); 117 const colors = [ red, green, blue ]; 118 119 Vertex[] verts; 120 foreach (c; 0 .. cubeCount) { 121 verts ~= cube.vertices; 122 const color = colors[c % 3]; 123 foreach (v; 0 .. cubeLen) { 124 verts[$ - cubeLen + v].color = color; 125 } 126 } 127 128 indices = cube.indices; 129 vertBuf = createStaticBuffer(verts, BufferUsage.vertex); 130 indBuf = createStaticBuffer(indices, BufferUsage.index); 131 132 const lights = Lights( [ 133 Light(normalize(fvec(1.0, 1.0, -1.0, 0.0)), fvec(0.8, 0.5, 0.2, 1.0)), 134 Light(normalize(fvec(-1.0, 1.0, -1.0, 0.0)), fvec(0.2, 0.5, 0.8, 1.0)), 135 Light.init, Light.init, Light.init 136 ], 2); 137 138 matBuf = createDynamicBuffer(cubeCount * Matrices.sizeof, BufferUsage.uniform); 139 ligBuf = createStaticBuffer(lights, BufferUsage.uniform); 140 } 141 142 override void prepareRenderPass() 143 { 144 // need a valid device and swapchain, so we prepare it here 145 declEng = new DeclarativeEngine(device); 146 declEng.declareStruct!Vertex(); 147 declEng.addView!"shader.vert.spv"(); 148 declEng.addView!"shader.frag.spv"(); 149 declEng.store.store("sc_format", swapchain.format); 150 declEng.store.store("depth_format", findDepthFormat()); 151 declEng.parseSDLView!"pipeline.sdl"(); 152 153 renderPass = declEng.store.expect!RenderPass("rp"); 154 } 155 156 class DeclApiFrameData : FrameData 157 { 158 PrimaryCommandBuffer cmdBuf; 159 Rc!Image depth; 160 Rc!Framebuffer framebuffer; 161 162 this(ImageBase swcColor, CommandBuffer tempBuf) 163 { 164 super(swcColor); 165 cmdBuf = cmdPool.allocatePrimary(1)[0]; 166 depth = this.outer.createDepthImage(size[0], size[1]); 167 168 auto colorView = swcColor.createView( 169 ImageType.d2, ImageSubresourceRange(ImageAspect.color), Swizzle.identity 170 ).rc; 171 auto depthView = depth.createView( 172 ImageType.d2, ImageSubresourceRange(ImageAspect.depth), Swizzle.identity 173 ).rc; 174 175 this.framebuffer = this.outer.device.createFramebuffer(this.outer.renderPass, [ 176 colorView.obj, depthView.obj 177 ], size[0], size[1], 1); 178 } 179 180 override void dispose() 181 { 182 framebuffer.unload(); 183 depth.unload(); 184 cmdPool.free([ cast(CommandBuffer)cmdBuf ]); 185 super.dispose(); 186 } 187 } 188 189 override FrameData makeFrameData(ImageBase swcColor, CommandBuffer tempBuf) 190 { 191 return new DeclApiFrameData(swcColor, tempBuf); 192 } 193 194 void preparePipeline() 195 { 196 setLayout = declEng.store.expect!DescriptorSetLayout("dsl"); 197 layout = declEng.store.expect!PipelineLayout("layout"); 198 pipeline = declEng.store.expect!Pipeline("pl"); 199 } 200 201 void prepareDescriptorSet() 202 { 203 const poolSizes = [ 204 DescriptorPoolSize(DescriptorType.uniformBufferDynamic, 1), 205 DescriptorPoolSize(DescriptorType.uniformBuffer, 1), 206 ]; 207 descPool = device.createDescriptorPool(1, poolSizes); 208 set = descPool.allocate([ setLayout.obj ])[0]; 209 210 auto writes = [ 211 WriteDescriptorSet(set, 0, 0, DescriptorWrite.make( 212 DescriptorType.uniformBufferDynamic, 213 matBuf.descriptor(0, Matrices.sizeof), 214 )), 215 216 WriteDescriptorSet(set, 1, 0, DescriptorWrite.make( 217 DescriptorType.uniformBuffer, 218 ligBuf.descriptor(0, Lights.sizeof), 219 )), 220 ]; 221 device.updateDescriptorSets(writes, []); 222 } 223 224 void updateMatrices(in Matrices[] mat) 225 { 226 auto mm = matBuf.boundMemory.map(); 227 auto v = mm.view!(Matrices[])(0, mat.length); 228 v[] = mat; 229 MappedMemorySet mms; 230 mm.addToSet(mms); 231 device.flushMappedMemory(mms); 232 } 233 234 override Submission[] recordCmds(FrameData frameData) 235 { 236 auto fd = cast(DeclApiFrameData)frameData; 237 238 const ccv = ClearColorValues(0.6f, 0.6f, 0.6f, hasAlpha ? 0.5f : 1f); 239 const dcv = ClearDepthStencilValues(1f, 0); 240 241 PrimaryCommandBuffer buf = fd.cmdBuf; 242 243 buf.begin(CommandBufferUsage.oneTimeSubmit); 244 245 buf.setViewport(0, [ Viewport(0f, 0f, cast(float)surfaceSize[0], cast(float)surfaceSize[1]) ]); 246 buf.setScissor(0, [ Rect(0, 0, surfaceSize[0], surfaceSize[1]) ]); 247 248 buf.beginRenderPass( 249 renderPass, fd.framebuffer, 250 Rect(0, 0, surfaceSize[0], surfaceSize[1]), 251 [ ClearValues(ccv), ClearValues(dcv) ] 252 ); 253 254 buf.bindPipeline(pipeline); 255 buf.bindIndexBuffer(indBuf, 0, IndexType.u16); 256 foreach(c; 0 .. cubeCount) { 257 buf.bindVertexBuffers(0, [ VertexBinding(vertBuf, Vertex.sizeof*c*cubeLen) ]); 258 buf.bindDescriptorSets(PipelineBindPoint.graphics, layout, 0, [ set ], [c * Matrices.sizeof]); 259 buf.drawIndexed(cast(uint)indices.length, 1, 0, 0, 0); 260 } 261 262 buf.endRenderPass(); 263 264 buf.end(); 265 266 return simpleSubmission([ buf ]); 267 } 268 269 } 270 271 int main(string[] args) 272 { 273 try { 274 auto example = new DeclApiExample(args); 275 example.prepare(); 276 scope(exit) example.dispose(); 277 278 example.window.onKeyOn = (KeyEvent ev) 279 { 280 if (ev.sym == KeySym.escape) { 281 example.window.closeFlag = true; 282 } 283 }; 284 285 // 6 RPM at 60 FPS 286 const puls = 6 * 2*PI / 3600f; 287 auto angle = 0f; 288 const view = lookAt(fvec(0, -7, 2), fvec(0, 0, 0), fvec(0, 0, 1)); 289 const proj = perspective!float(example.ndc, 45, 4f/3f, 1f, 10f); 290 const viewProj = proj*view; 291 292 DeclApiExample.Matrices[3] matrices; 293 294 while (!example.window.closeFlag) { 295 296 import gfx.math.inverse : affineInverse; 297 298 foreach (m; 0 .. 3) { 299 const posAngle = cast(float)(m * 2f * PI / 3f); 300 const model = rotation(posAngle + angle, fvec(0, 0, 1)) 301 * translation(2f, 0f, 0f) 302 * rotation(-angle, fvec(0, 0, 1)); 303 const mvp = viewProj*model; 304 matrices[m] = DeclApiExample.Matrices( 305 mvp.transpose(), 306 model.affineInverse(), // need the transpose of model inverse 307 ); 308 } 309 angle += puls; 310 example.updateMatrices(matrices); 311 312 example.render(); 313 314 example.frameTick(); 315 316 example.display.pollAndDispatch(); 317 } 318 319 return 0; 320 } 321 catch(Exception ex) { 322 stderr.writeln("error occured: ", ex.msg); 323 return 1; 324 } 325 }