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 }