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 import gfx.window;
19 import gfx.window.keys;
20 
21 import gfx.math;
22 
23 import std.exception;
24 import std.stdio;
25 import std.typecons;
26 import std.math;
27 
28 class DepthExample : Example
29 {
30     Rc!RenderPass renderPass;
31     Rc!Pipeline pipeline;
32     Rc!PipelineLayout layout;
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     struct Vertex {
45         FVec3 position;
46         FVec3 normal;
47         FVec4 color;
48     }
49 
50     struct Matrices {
51         FMat4 mvp;
52         FMat4 normal;
53     }
54 
55     enum maxLights = 5;
56 
57     struct Light {
58         FVec4 direction;
59         FVec4 color;
60     }
61 
62     struct Lights {
63         Light[maxLights] lights;
64         uint num;
65     }
66 
67     this(string[] args) {
68         super("Depth", args);
69     }
70 
71     override void dispose() {
72         if (device) {
73             device.waitIdle();
74         }
75         vertBuf.unload();
76         indBuf.unload();
77         matBuf.unload();
78         ligBuf.unload();
79         setLayout.unload();
80         descPool.unload();
81         layout.unload();
82         pipeline.unload();
83         renderPass.unload();
84         super.dispose();
85     }
86 
87     override void prepare() {
88         super.prepare();
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         const attachments = [
144             AttachmentDescription(swapchain.format, 1,
145                 AttachmentOps(LoadOp.clear, StoreOp.store),
146                 AttachmentOps(LoadOp.dontCare, StoreOp.dontCare),
147                 trans(ImageLayout.undefined, ImageLayout.presentSrc),
148                 No.mayAlias
149             ),
150             AttachmentDescription(findDepthFormat(), 1,
151                 AttachmentOps(LoadOp.clear, StoreOp.dontCare),
152                 AttachmentOps(LoadOp.dontCare, StoreOp.dontCare),
153                 trans(ImageLayout.undefined, ImageLayout.depthStencilAttachmentOptimal),
154                 No.mayAlias
155             )
156         ];
157         const subpasses = [
158             SubpassDescription(
159                 [], [ AttachmentRef(0, ImageLayout.colorAttachmentOptimal) ],
160                 some(AttachmentRef(1, ImageLayout.depthStencilAttachmentOptimal)),
161                 []
162             )
163         ];
164 
165         renderPass = device.createRenderPass(attachments, subpasses, []);
166     }
167 
168     class DepthFrameData : FrameData
169     {
170         PrimaryCommandBuffer cmdBuf;
171         Rc!Image depth;
172         Rc!Framebuffer framebuffer;
173 
174         this(ImageBase swcColor, CommandBuffer tempBuf)
175         {
176             super(swcColor);
177             cmdBuf = cmdPool.allocatePrimary(1)[0];
178             depth = this.outer.createDepthImage(size[0], size[1]);
179 
180             auto colorView = swcColor.createView(
181                 ImageType.d2, ImageSubresourceRange(ImageAspect.color), Swizzle.identity
182             ).rc;
183             auto depthView = depth.createView(
184                 ImageType.d2, ImageSubresourceRange(ImageAspect.depth), Swizzle.identity
185             ).rc;
186 
187             this.framebuffer = this.outer.device.createFramebuffer(this.outer.renderPass, [
188                 colorView.obj, depthView.obj
189             ], size[0], size[1], 1);
190         }
191 
192         override void dispose()
193         {
194             framebuffer.unload();
195             depth.unload();
196             cmdPool.free([ cast(CommandBuffer)cmdBuf ]);
197             super.dispose();
198         }
199     }
200 
201     override FrameData makeFrameData(ImageBase swcColor, CommandBuffer tempBuf)
202     {
203         return new DepthFrameData(swcColor, tempBuf);
204     }
205 
206     void preparePipeline()
207     {
208         const spv = [
209             import("shader.vert.spv"), import("shader.frag.spv")
210         ];
211         Rc!ShaderModule vtxShader;
212         Rc!ShaderModule fragShader;
213 
214         vtxShader = device.createShaderModule(
215             cast(immutable(uint)[])spv[0], "main"
216         );
217         fragShader = device.createShaderModule(
218             cast(immutable(uint)[])spv[1], "main"
219         );
220 
221         const layoutBindings = [
222             PipelineLayoutBinding(0, DescriptorType.uniformBufferDynamic, 1, ShaderStage.vertex),
223             PipelineLayoutBinding(1, DescriptorType.uniformBuffer, 1, ShaderStage.fragment),
224         ];
225 
226         setLayout = device.createDescriptorSetLayout(layoutBindings);
227         layout = device.createPipelineLayout([setLayout.obj], []);
228 
229         PipelineInfo info;
230         info.shaders.vertex = vtxShader;
231         info.shaders.fragment = fragShader;
232         info.inputBindings = [
233             VertexInputBinding(0, Vertex.sizeof, No.instanced)
234         ];
235         info.inputAttribs = [
236             VertexInputAttrib(0, 0, Format.rgb32_sFloat, 0),
237             VertexInputAttrib(1, 0, Format.rgb32_sFloat, Vertex.normal.offsetof),
238             VertexInputAttrib(2, 0, Format.rgba32_sFloat, Vertex.color.offsetof),
239         ];
240         info.assembly = InputAssembly(Primitive.triangleList, No.primitiveRestart);
241         info.rasterizer = Rasterizer(
242             PolygonMode.fill, Cull.back, FrontFace.ccw, No.depthClamp,
243             none!DepthBias, 1f
244         );
245         // info.viewports = [
246         //     ViewportConfig(
247         //         Viewport(0, 0, cast(float)surfaceSize[0], cast(float)surfaceSize[1]),
248         //         Rect(0, 0, surfaceSize[0], surfaceSize[1])
249         //     )
250         // ];
251         info.depthInfo = DepthInfo(
252             Yes.enabled, Yes.write, CompareOp.less, No.boundsTest, 0f, 1f
253         );
254         info.blendInfo = ColorBlendInfo(
255             none!LogicOp, [
256                 ColorBlendAttachment(No.enabled,
257                     BlendState(trans(BlendFactor.one, BlendFactor.zero), BlendOp.add),
258                     BlendState(trans(BlendFactor.one, BlendFactor.zero), BlendOp.add),
259                     ColorMask.all
260                 )
261             ],
262             [ 0f, 0f, 0f, 0f ]
263         );
264         info.dynamicStates = [ DynamicState.viewport, DynamicState.scissor ];
265         info.layout = layout;
266         info.renderPass = renderPass;
267         info.subpassIndex = 0;
268 
269         auto pls = device.createPipelines( [info] );
270         pipeline = pls[0];
271     }
272 
273     void prepareDescriptorSet() {
274         const poolSizes = [
275             DescriptorPoolSize(DescriptorType.uniformBufferDynamic, 1),
276             DescriptorPoolSize(DescriptorType.uniformBuffer, 1),
277         ];
278         descPool = device.createDescriptorPool(1, poolSizes);
279         set = descPool.allocate([ setLayout.obj ])[0];
280 
281         auto writes = [
282             WriteDescriptorSet(set, 0, 0, DescriptorWrite.make(
283                 DescriptorType.uniformBufferDynamic,
284                 matBuf.descriptor(0, Matrices.sizeof),
285             )),
286 
287             WriteDescriptorSet(set, 1, 0, DescriptorWrite.make(
288                 DescriptorType.uniformBuffer,
289                 ligBuf.descriptor(0, Lights.sizeof),
290             )),
291         ];
292         device.updateDescriptorSets(writes, []);
293     }
294 
295     void updateMatrices(in Matrices[] mat) {
296         auto mm = matBuf.boundMemory.map();
297         auto v = mm.view!(Matrices[])(0, mat.length);
298         v[] = mat;
299         MappedMemorySet mms;
300         mm.addToSet(mms);
301         device.flushMappedMemory(mms);
302     }
303 
304     override Submission[] recordCmds(FrameData frameData)
305     {
306 
307         auto dfd = cast(DepthFrameData)frameData;
308 
309         const ccv = ClearColorValues(0.6f, 0.6f, 0.6f, hasAlpha ? 0.5f : 1f);
310         const dcv = ClearDepthStencilValues(1f, 0);
311 
312         PrimaryCommandBuffer buf = dfd.cmdBuf;
313 
314         buf.begin(CommandBufferUsage.oneTimeSubmit);
315 
316         buf.setViewport(0, [ Viewport(0f, 0f, cast(float)surfaceSize[0], cast(float)surfaceSize[1]) ]);
317         buf.setScissor(0, [ Rect(0, 0, surfaceSize[0], surfaceSize[1]) ]);
318 
319         buf.beginRenderPass(
320             renderPass, dfd.framebuffer,
321             Rect(0, 0, surfaceSize[0], surfaceSize[1]),
322             [ ClearValues(ccv), ClearValues(dcv) ]
323         );
324 
325         buf.bindPipeline(pipeline);
326         buf.bindIndexBuffer(indBuf, 0, IndexType.u16);
327         foreach(c; 0 .. cubeCount) {
328             buf.bindVertexBuffers(0, [ VertexBinding(vertBuf, Vertex.sizeof*c*cubeLen) ]);
329             buf.bindDescriptorSets(PipelineBindPoint.graphics, layout, 0, [ set ], [c * Matrices.sizeof]);
330             buf.drawIndexed(cast(uint)indices.length, 1, 0, 0, 0);
331         }
332 
333         buf.endRenderPass();
334 
335         buf.end();
336 
337         return simpleSubmission([ buf ]);
338     }
339 
340 }
341 
342 int main(string[] args) {
343 
344     try {
345         auto example = new DepthExample(args);
346         example.prepare();
347         scope(exit) example.dispose();
348 
349         example.window.onKeyOn = (KeyEvent ev)
350         {
351             if (ev.sym == KeySym.escape) {
352                 example.window.closeFlag = true;
353             }
354         };
355 
356         // 6 RPM at 60 FPS
357         const puls = 6 * 2*PI / 3600f;
358         auto angle = 0f;
359         const view = lookAt(fvec(0, -7, 2), fvec(0, 0, 0), fvec(0, 0, 1));
360         const proj = perspective!float(example.ndc, 45, 4f/3f, 1f, 10f);
361         const viewProj = proj*view;
362 
363         DepthExample.Matrices[3] matrices;
364 
365         while (!example.window.closeFlag) {
366 
367             import gfx.math.inverse : affineInverse;
368 
369             foreach (m; 0 .. 3) {
370                 const posAngle = cast(float)(m * 2f * PI / 3f);
371                 const model = rotation(posAngle + angle, fvec(0, 0, 1))
372                         * translation(2f, 0f, 0f)
373                         * rotation(-angle, fvec(0, 0, 1));
374                 const mvp = viewProj*model;
375                 matrices[m] = DepthExample.Matrices(
376                     mvp.transpose(),
377                     model.affineInverse(), // need the transpose of model inverse
378                 );
379             }
380             angle += puls;
381             example.updateMatrices(matrices);
382 
383             example.render();
384             example.frameTick();
385 
386             example.display.pollAndDispatch();
387         }
388 
389         return 0;
390     }
391     catch(Exception ex) {
392         stderr.writeln("error occured: ", ex.msg);
393         return 1;
394     }
395 }