1 /**
2     Inochi2D DrawList Interface
3 
4     Copyright: 
5         Copyright © 2020-2026, Inochi2D Project
6     
7     License:
8         $(LINK2 https://github.com/Inochi2D/inochi2d/blob/main/LICENSE, BSD 2-clause License)
9     
10     Authors:
11         Luna Nielsen
12 */
13 module inochi2d.core.render.drawlist;
14 import inochi2d.core.render.state;
15 import inochi2d.core.render.texture;
16 import inochi2d.core.mesh;
17 import numath;
18 import nulib;
19 import numem;
20 
21 /**
22     A draw list containing the rendering state and commands
23     to submit to the GPU.
24 */
25 final
26 class DrawList : NuObject {
27 private:
28 @nogc:
29 
30     // Working set
31     DrawListAlloc _call;
32     DrawCmd _ccmd;
33 
34     // Draw Commands
35     vector!DrawCmd _cmds;
36     uint _cmdp;
37 
38     // Vertex Data
39     vector!VtxData _vtxs;
40     uint _vtxp;
41 
42     // Index Data
43     vector!uint _idxs;
44     uint _idxp;
45 
46     // Buffer allocations
47     vector!DrawListAlloc _allocs;
48     uint _allp;
49 
50     // Stacks
51     stack!(Texture[IN_MAX_ATTACHMENTS]) _targetsStack;
52 
53 public:
54 
55     // Destructor
56      ~this() {
57         _cmds.clear();
58         _vtxs.clear();
59         _idxs.clear();
60         _allocs.clear();
61         _targetsStack.clear();
62     }
63 
64     /**
65         Whether to use base vertex specification.
66     */
67     bool useBaseVertex = true;
68 
69     /**
70         Command Buffer
71     */
72     @property DrawCmd[] commands() => _cmds[0 .. _cmdp];
73 
74     /**
75         Vertex data
76     */
77     @property VtxData[] vertices() => _vtxs[0 .. _vtxp];
78 
79     /**
80         Index data
81     */
82     @property uint[] indices() => _idxs[0 .. _idxp];
83 
84     /**
85         Allocated meshes
86     */
87     @property DrawListAlloc[] allocations() => _allocs[0 .. _allp];
88 
89     /**
90         Allocates the given mesh in the draw list, allowing its
91         contents to be reused in draw commands.
92 
93         Params:
94             vtx = Vertex data to push.
95             idx = Index data to push.
96     
97         Returns:
98             A reference to the drawlist allocation on success,
99             $(D null) otherwise.
100     */
101     DrawListAlloc* allocate(VtxData[] vtx, uint[] idx) {
102 
103         // Invalid vertex buffer check.
104         if (vtx.length < 3)
105             return null;
106 
107         // Invalid index buffer check.
108         if (idx.length < 3)
109             return null;
110 
111         // Resize if stuff doesn't fit.
112         if (_vtxp + vtx.length >= _vtxs.length)
113             _vtxs.resize(_vtxp + vtx.length + 1);
114         if (_idxp + idx.length >= _idxs.length)
115             _idxs.resize(_idxp + idx.length + 1);
116 
117         // Meshes supply their own index data, as such
118         // we offset it here to fit within our buffer.
119         if (!useBaseVertex)
120             idx[0 .. $] += _idxp;
121 
122         _vtxs[_vtxp .. _vtxp + vtx.length] = vtx[0 .. $];
123         _idxs[_idxp .. _idxp + idx.length] = idx[0 .. $];
124         _vtxp += vtx.length;
125         _idxp += idx.length;
126 
127         _call.allocId = _allp;
128         _call.idxCount = cast(uint)idx.length;
129         _call.vtxCount = cast(uint)vtx.length;
130 
131         // Set up allocation.
132         if (_allp >= _allocs.length)
133             _allocs ~= _call;
134         else
135             _allocs[_allp] = _call;
136 
137         // prepare next alloc
138         _call = DrawListAlloc.init;
139         _call.idxOffset = _idxp;
140         _call.vtxOffset = _vtxp;
141         return &_allocs[_allp++];
142     }
143 
144     /**
145         Pushes render targets to the draw list's stack.
146     */
147     void beginComposite() {
148         _ccmd.state = DrawState.compositeBegin;
149         this.next();
150     }
151 
152     /**
153         Pops the top render target from the list's stack.
154     */
155     void endComposite() {
156         _ccmd.state = DrawState.compositeEnd;
157         this.next();
158     }
159 
160     /**
161         Enqueues a composite blit for a recently ended composite.
162     */
163     void blit() {
164         _ccmd.state = DrawState.compositeBlit;
165         this.next();
166     }
167 
168     /**
169         Pushes render targets to the draw list's stack.
170     */
171     void pushMask(MaskingMode mode) {
172         _ccmd.state = DrawState.pushMask;
173         _ccmd.maskMode = mode;
174         this.next();
175     }
176 
177     /**
178         Pops the top render target from the list's stack.
179     */
180     void popMask() {
181         _ccmd.state = DrawState.popMask;
182         this.next();
183     }
184 
185     /**
186         Sets sources for the current draw call.
187     */
188     void setSources(Texture[IN_MAX_ATTACHMENTS] sources) {
189         _ccmd.sources = sources;
190     }
191 
192     /**
193         Sets the blending mode for the current draw call.
194     */
195     void setBlending(BlendMode value) {
196         _ccmd.blendMode = value;
197     }
198 
199     /**
200         Sets the blending mode for the current draw call.
201     */
202     void setVariables(T)(uint nid, T value)
203     if (T.sizeof <= _ccmd.variables.sizeof) {
204         _ccmd.typeId = nid;
205         nu_memcpy(_ccmd.variables.ptr, cast(void*)&value, T.sizeof);
206     }
207 
208     /**
209         Sets the masking mode for the current draw call.
210     */
211     void setMasking(MaskingMode value) {
212         _ccmd.state = DrawState.defineMask;
213         _ccmd.maskMode = value;
214     }
215 
216     /**
217         Sets the mesh data for the current draw command.
218 
219         Params:
220             alloc = The vertex allocation cookie.
221     */
222     void setMesh(DrawListAlloc* alloc) {
223         if (!alloc)
224             return;
225 
226         _ccmd.allocId = alloc.allocId;
227         _ccmd.idxOffset = alloc.idxOffset;
228         _ccmd.vtxOffset = alloc.vtxOffset;
229         _ccmd.elemCount = alloc.idxCount;
230     }
231 
232     /**
233         Pushes the next draw command
234     */
235     void next() {
236         if (_cmdp >= _cmds.length)
237             _cmds ~= _ccmd;
238         else
239             _cmds[_cmdp] = _ccmd;
240 
241         _cmdp++;
242         _ccmd = DrawCmd.init;
243     }
244 
245     /**
246         Clears the draw list, making it ready for a new pass.
247     */
248     void clear() {
249         _vtxp = 0;
250         _idxp = 0;
251         _cmdp = 0;
252         _allp = 0;
253         _ccmd = DrawCmd.init;
254         _call = DrawListAlloc.init;
255         _targetsStack.clear();
256     }
257 }
258 
259 /**
260     Maximum number of texture attachments.
261 */
262 enum IN_MAX_ATTACHMENTS = 8;
263 
264 /**
265     An allocation within the drawlist
266 */
267 struct DrawListAlloc {
268 
269     /**
270         Vertex offset.
271     */
272     uint vtxOffset;
273 
274     /**
275         Index offset.
276     */
277     uint idxOffset;
278 
279     /**
280         Number of indices.
281     */
282     uint idxCount;
283 
284     /**
285         Number of vertices.
286     */
287     uint vtxCount;
288 
289     /**
290         Allocation ID.
291     */
292     uint allocId;
293 }
294 
295 /**
296     Draw state flags.
297 */
298 enum DrawState : uint {
299 
300     /**
301         Normal drawing.
302     */
303     normal = 0,
304 
305     /**
306         Draws a mask source to the top level mask buffer.
307     */
308     defineMask = 1,
309 
310     /**
311         Finalizes the current mask, then pushes it onto the stack.
312     */
313     pushMask = 2,
314 
315     /**
316         Pops the current mask from the stack, restoring the higher level
317         mask, or exits mask mode.
318     */
319     popMask = 3,
320 
321     /**
322         A composition into composition textures
323         has begun.
324     */
325     compositeBegin = 4,
326 
327     /**
328         A composition into composition textures
329         has ended.
330     */
331     compositeEnd = 5,
332 
333     /**
334         Sources should be drawn to targets using
335         the given blending mode.
336     */
337     compositeBlit = 6,
338 }
339 
340 /**
341     A drawing command that is sent to the GPU.
342 */
343 struct DrawCmd {
344 @nogc:
345 
346     /**
347         Source textures
348     */
349     Texture[IN_MAX_ATTACHMENTS] sources;
350 
351     /**
352         The current state of the drawing command.
353     */
354     DrawState state;
355 
356     /**
357         Blending mode to apply
358     */
359     BlendMode blendMode;
360 
361     /**
362         Masking mode to apply.
363     */
364     MaskingMode maskMode;
365 
366     /**
367         Allocation ID.
368     */
369     uint allocId;
370 
371     /**
372         Vertex offset.
373     */
374     uint vtxOffset;
375 
376     /**
377         Index offset.
378     */
379     uint idxOffset;
380 
381     /**
382         Number of indices.
383     */
384     uint elemCount;
385 
386     /**
387         Type ID of the node being drawn.
388     */
389     uint typeId;
390 
391     /**
392         Variables passed to the draw list.
393     */
394     void[64] variables;
395 
396     /**
397         Whether the command is empty.
398     */
399     @property bool isEmpty() => elemCount == 0;
400 }