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 }