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 }