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 }