1 module gfx.gl3.queue;
2 
3 package:
4 
5 import gfx.bindings.opengl.gl;
6 import gfx.core.rc : Disposable;
7 import gfx.gl3 : gfxGlLog;
8 import gfx.graal.cmd;
9 import gfx.graal.queue;
10 
11 final class GlQueue : Queue, Disposable {
12     import gfx.gl3 : GlInfo, GlShare;
13     import gfx.graal.device : Device;
14     import gfx.graal.sync : Fence, Semaphore;
15 
16     private GlShare share;
17     private GlInfo info;
18     private Device _device;
19     private GLuint readFbo;
20     private GLuint vao;
21     private GlState state;
22     private uint _index;
23 
24     this(GlShare share, Device device, uint index) {
25         this.share = share;
26         this.info = share.info;
27         _device = device;
28         this._index = index;
29         auto gl = share.gl;
30         gl.GenFramebuffers(1, &readFbo);
31         gl.GenVertexArrays(1, &vao);
32         gl.BindVertexArray(vao);
33     }
34 
35     override void dispose() {
36         auto gl = share.gl;
37         gl.DeleteFramebuffers(1, &readFbo);
38         gl.DeleteVertexArrays(1, &vao);
39     }
40 
41     override @property Device device() {
42         return _device;
43     }
44 
45     override @property uint index() {
46         return _index;
47     }
48 
49     override void waitIdle() {
50     }
51 
52     override void submit(Submission[] submissions, Fence fence) {
53         auto gl = share.gl;
54         foreach (ref s; submissions) {
55             foreach (cmdBuf; s.cmdBufs) {
56                 auto glCmdBuf = cast(GlCommandBuffer) cmdBuf;
57                 foreach (cmd; glCmdBuf._cmds) {
58                     cmd.execute(this, gl);
59                 }
60                 if (glCmdBuf._usage & CommandBufferUsage.oneTimeSubmit) {
61                     glCmdBuf._cmds.length = 0;
62                 }
63             }
64         }
65     }
66 
67     override void present(Semaphore[] waitSems, PresentRequest[] prs) {
68         import gfx.gl3.resource : GlImage, GlImgType;
69         import gfx.gl3.swapchain : GlSurface, GlSwapchain;
70 
71         auto gl = share.gl;
72 
73         foreach (i, pr; prs) {
74             auto sc = cast(GlSwapchain) pr.swapChain;
75             auto surf = sc.surface;
76             auto img = cast(GlImage) sc.images[pr.imageIndex];
77             auto size = sc.size;
78 
79             share.ctx.makeCurrent(surf.handle);
80 
81             if (i == prs.length - 1)
82                 share.ctx.swapInterval = 1;
83             else
84                 share.ctx.swapInterval = 0;
85 
86             import gfx.gl3.error : glCheck;
87 
88             gl.BindFramebuffer(GL_READ_FRAMEBUFFER, readFbo);
89             final switch (img.glType) {
90             case GlImgType.renderBuf:
91                 gl.FramebufferRenderbuffer(GL_READ_FRAMEBUFFER,
92                         GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, img.name);
93                 break;
94             case GlImgType.tex:
95                 gl.FramebufferTexture2D(GL_READ_FRAMEBUFFER,
96                         GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, img.name, 0);
97                 break;
98             }
99 
100             gl.BindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
101             gl.BlitFramebuffer(0, 0, size[0], size[1], 0, 0, size[0], size[1],
102                     GL_COLOR_BUFFER_BIT, GL_NEAREST);
103             glCheck(gl, "blit framebuffer");
104 
105             share.ctx.swapBuffers(surf.handle);
106         }
107     }
108 }
109 
110 final class GlCommandPool : CommandPool {
111     import gfx.core.rc : atomicRcCode;
112     import gfx.gl3 : GlShare;
113     import gfx.graal.cmd : PrimaryCommandBuffer;
114 
115     mixin(atomicRcCode);
116 
117     private GlQueue queue;
118     private GlShare share;
119     private GLuint fbo;
120 
121     this(GlQueue queue) {
122         this.queue = queue;
123         this.share = queue.share;
124         auto gl = share.gl;
125         gl.GenFramebuffers(1, &fbo);
126     }
127 
128     override void dispose() {
129         auto gl = share.gl;
130         gl.DeleteFramebuffers(1, &fbo);
131     }
132 
133     override void reset() {
134     }
135 
136     override PrimaryCommandBuffer[] allocatePrimary(in size_t count) {
137         auto bufs = new PrimaryCommandBuffer[count];
138         foreach (i; 0 .. count) {
139             bufs[i] = new GlCommandBuffer(this, fbo, CommandBufferLevel.primary);
140         }
141         return bufs;
142     }
143 
144     override SecondaryCommandBuffer[] allocateSecondary(in size_t count) {
145         auto bufs = new SecondaryCommandBuffer[count];
146         foreach (i; 0 .. count) {
147             bufs[i] = new GlCommandBuffer(this, fbo, CommandBufferLevel.secondary);
148         }
149         return bufs;
150     }
151 
152     override void free(CommandBuffer[] buffers) {
153     }
154 }
155 
156 final class GlCommandBuffer : PrimaryCommandBuffer, SecondaryCommandBuffer {
157     import gfx.gl3 : GlShare, GlInfo;
158     import gfx.gl3.conv : toGl;
159     import gfx.gl3.pipeline : GlPipeline, GlRenderPass;
160     import gfx.graal.buffer : Buffer, IndexType;
161     import gfx.graal.image : ImageBase, ImageLayout, ImageSubresourceRange;
162     import gfx.graal.pipeline : ColorBlendInfo, DepthInfo, DescriptorSet, Pipeline, PipelineLayout,
163         Rasterizer, ShaderStage, VertexInputBinding, VertexInputAttrib, ViewportConfig;
164     import gfx.graal.renderpass : Framebuffer, RenderPass;
165     import gfx.graal.types : Rect, Trans, Viewport;
166     import std.typecons : Flag;
167 
168     private enum Dirty {
169         none = 0x00,
170         vertexBindings = 0x01,
171         pipeline = 0x02,
172 
173         all = 0xff,
174     }
175 
176     private CommandPool _pool;
177     private CommandBufferLevel _level;
178     private GLuint _fbo;
179     private CommandBufferUsage _usage;
180 
181     private GlCommand[] _cmds;
182     private Dirty _dirty;
183 
184     // render pass cache
185     private GlRenderPass _renderPass;
186     private GlColorAttachment[] _attachments; // TODO: static array
187     private uint _subpass;
188 
189     // pipeline cache
190     private GLenum _primitive;
191     private VertexInputBinding[] _inputBindings;
192     private VertexInputAttrib[] _inputAttribs;
193 
194     // index buffer cache
195     private size_t _indexOffset;
196     private GLenum _indexType;
197 
198     // vertex cache
199     private VertexBinding[] _vertexBindings;
200 
201     this(CommandPool pool, GLuint fbo, CommandBufferLevel level) {
202         _pool = pool;
203         _fbo = fbo;
204         _level = level;
205     }
206 
207     override @property CommandPool pool() {
208         return _pool;
209     }
210 
211     override @property CommandBufferLevel level() const {
212         return _level;
213     }
214 
215     override void reset() {
216         _cmds.length = 0;
217         _dirty = Dirty.none;
218         _vertexBindings.length = 0;
219     }
220 
221     override void begin(in CommandBufferUsage usage) {
222         _usage = usage;
223     }
224 
225     override void end() {
226     }
227 
228     override void pipelineBarrier(Trans!PipelineStage stageTrans,
229             BufferMemoryBarrier[] bufMbs, ImageMemoryBarrier[] imgMbs) {
230         gfxGlLog.warning("unimplemented GL command");
231     }
232 
233     override void clearColorImage(ImageBase image, ImageLayout layout,
234             in ClearColorValues clearValues, ImageSubresourceRange[] ranges) {
235         import gfx.gl3.resource : GlImage;
236 
237         _cmds ~= new SetupFramebufferCmd(_fbo, cast(GlImage) image);
238         _cmds ~= new ClearColorCmd(clearValues);
239     }
240 
241     override void clearDepthStencilImage(ImageBase image, ImageLayout layout,
242             in ClearDepthStencilValues clearValues, ImageSubresourceRange[] ranges) {
243         import gfx.gl3.resource : GlImage;
244 
245         _cmds ~= new SetupFramebufferCmd(_fbo, cast(GlImage) image);
246         _cmds ~= new ClearDepthStencilCmd(clearValues, true, true);
247     }
248 
249     override void fillBuffer(Buffer dst, in size_t offset, in size_t size, uint value) {
250         gfxGlLog.warning("unimplemented GL command");
251     }
252 
253     override void updateBuffer(Buffer dst, in size_t offset, in uint[] data) {
254         gfxGlLog.warning("unimplemented GL command");
255     }
256 
257     override void copyBuffer(Trans!Buffer buffers, in CopyRegion[] regions) {
258         import gfx.gl3.resource : GlBuffer;
259 
260         foreach (r; regions) {
261             _cmds ~= new CopyBufToBufCmd(cast(GlBuffer) buffers.from, cast(GlBuffer) buffers.to, r);
262         }
263     }
264 
265     override void copyBufferToImage(Buffer srcBuffer, ImageBase dstImage,
266             in ImageLayout dstLayout, in BufferImageCopy[] regions) {
267         import gfx.gl3.resource : GlBuffer, GlImage;
268 
269         foreach (r; regions) {
270             _cmds ~= new CopyBufToImgCmd(cast(GlBuffer) srcBuffer, cast(GlImage) dstImage, r);
271         }
272     }
273 
274     override void setViewport(in uint firstViewport, in Viewport[] viewports) {
275         _cmds ~= new SetViewportsCmd(firstViewport, viewports);
276     }
277 
278     override void setScissor(in uint firstScissor, in Rect[] scissors) {
279         _cmds ~= new SetScissorsCmd(firstScissor, scissors);
280     }
281 
282     override void setDepthBounds(in float minDepth, in float maxDepth) {
283         gfxGlLog.warning("unimplemented GL command");
284     }
285 
286     void setLineWidth(in float lineWidth) {
287         _cmds ~= new SetLineWidthCmd(lineWidth);
288     }
289 
290     override void setDepthBias(in float constFactor, in float clamp, in float slopeFactor) {
291         _cmds ~= new SetDepthBiasCmd(constFactor, clamp, slopeFactor);
292     }
293 
294     override void setStencilCompareMask(in StencilFace faceMask, in uint compareMask) {
295         gfxGlLog.warning("unimplemented GL command");
296     }
297 
298     override void setStencilWriteMask(in StencilFace faceMask, in uint writeMask) {
299         gfxGlLog.warning("unimplemented GL command");
300     }
301 
302     override void setStencilReference(in StencilFace faceMask, in uint reference) {
303         gfxGlLog.warning("unimplemented GL command");
304     }
305 
306     override void setBlendConstants(in float[4] blendConstants) {
307         _cmds ~= new SetBlendConstantsCmd(blendConstants);
308     }
309 
310     override void beginRenderPass(RenderPass rp, Framebuffer fb, in Rect area,
311             in ClearValues[] clearValues) {
312         import gfx.gl3.pipeline : GlFramebuffer;
313         import gfx.graal.pipeline : ColorBlendAttachment;
314         import std.algorithm : map;
315         import std.array : array;
316 
317         _renderPass = cast(GlRenderPass) rp;
318         _attachments = _renderPass.attachments.map!(ad => GlColorAttachment(false,
319                 ad, ColorBlendAttachment.init)).array;
320         setActiveSubpass(0);
321 
322         const glFb = cast(GlFramebuffer) fb;
323         _cmds ~= new BindFramebufferCmd(glFb.name);
324         foreach (cv; clearValues) {
325             if (cv.type == ClearValues.Type.color) {
326                 _cmds ~= new ClearColorCmd(cv.values.color);
327             }
328             if (cv.type == ClearValues.Type.depthStencil) {
329                 _cmds ~= new ClearDepthStencilCmd(cv.values.depthStencil, true, true);
330             }
331         }
332     }
333 
334     override void beginWithinRenderPass(in CommandBufferUsage usage,
335             RenderPass rp, Framebuffer fb, uint subpass) {
336         import gfx.core.util : unsafeCast;
337         import gfx.gl3.pipeline : GlFramebuffer;
338         import gfx.graal.pipeline : ColorBlendAttachment;
339         import std.algorithm : map;
340         import std.array : array;
341 
342         assert(_level == CommandBufferLevel.secondary);
343         _usage = usage;
344         _renderPass = cast(GlRenderPass) rp;
345         _attachments = _renderPass.attachments.map!(ad => GlColorAttachment(false,
346                 ad, ColorBlendAttachment.init)).array;
347         setActiveSubpass(0);
348 
349         const glFb = cast(GlFramebuffer) fb;
350         _cmds ~= new BindFramebufferCmd(glFb.name);
351         _subpass = subpass;
352     }
353 
354     override void nextSubpass() {
355         assert(_level == CommandBufferLevel.primary);
356         setActiveSubpass(_subpass + 1);
357     }
358 
359     override void endRenderPass() {
360         assert(_level == CommandBufferLevel.primary);
361     }
362 
363     override void bindPipeline(Pipeline pipeline) {
364         auto glPipeline = cast(GlPipeline) pipeline;
365 
366         _cmds ~= new BindProgramCmd(glPipeline.prog);
367         _cmds ~= new SetViewportConfigsCmd(glPipeline.info.viewports);
368         _cmds ~= new SetRasterizerCmd(glPipeline.info.rasterizer);
369         _cmds ~= new SetDepthInfoCmd(glPipeline.info.depthInfo);
370         _cmds ~= new SetStencilInfoCmd(glPipeline.info.stencilInfo);
371 
372         uint curAttach = 0;
373         foreach (ref a; _attachments) {
374             if (a.enabled) {
375                 a.attachment = glPipeline.info.blendInfo.attachments[curAttach++];
376             }
377         }
378         assert(curAttach == glPipeline.info.blendInfo.attachments.length);
379         _cmds ~= new BindBlendSlotsCmd(_attachments.dup);
380 
381         _primitive = toGl(glPipeline.info.assembly.primitive);
382         _inputBindings = glPipeline.info.inputBindings;
383         _inputAttribs = glPipeline.info.inputAttribs;
384 
385         dirty(Dirty.vertexBindings | Dirty.pipeline);
386     }
387 
388     override void bindVertexBuffers(uint firstBinding, VertexBinding[] bindings) {
389         const minLen = firstBinding + bindings.length;
390         if (_vertexBindings.length < minLen)
391             _vertexBindings.length = minLen;
392         _vertexBindings[firstBinding .. firstBinding + bindings.length] = bindings;
393         dirty(Dirty.vertexBindings);
394     }
395 
396     override void bindIndexBuffer(Buffer indexBuf, size_t offset, IndexType type) {
397         import gfx.gl3.resource : GlBuffer;
398 
399         auto glBuf = cast(GlBuffer) indexBuf;
400         _cmds ~= new BindIndexBufCmd(glBuf.name);
401         _indexOffset = offset;
402         _indexType = toGl(type);
403     }
404 
405     override void bindDescriptorSets(PipelineBindPoint bindPoint, PipelineLayout layout,
406             uint firstSet, DescriptorSet[] sets, in size_t[] dynamicOffsets) {
407         import gfx.gl3.pipeline : GlDescriptorSet;
408         import gfx.graal.pipeline : DescriptorType;
409 
410         size_t dynInd = 0;
411 
412         foreach (si, ds; sets) {
413             auto glSet = cast(GlDescriptorSet) ds;
414 
415             foreach (bi, b; glSet.bindings) {
416 
417                 foreach (di, d; b.descriptors) {
418 
419                     switch (b.layout.descriptorType) {
420                     case DescriptorType.uniformBuffer:
421                         _cmds ~= new BindUniformBufferCmd(b.layout.binding, d.buffer, 0);
422                         break;
423                     case DescriptorType.uniformBufferDynamic:
424                         _cmds ~= new BindUniformBufferCmd(b.layout.binding,
425                                 d.buffer, dynamicOffsets[dynInd++]);
426                         break;
427                     case DescriptorType.combinedImageSampler:
428                         _cmds ~= new BindSamplerImageCmd(b.layout.binding, d.imageSampler);
429                         break;
430                     default:
431                         gfxGlLog.warning("unhandled descriptor set");
432                         break;
433                     }
434                 }
435             }
436         }
437     }
438 
439     override void pushConstants(PipelineLayout layout, ShaderStage stages,
440             size_t offset, size_t size, const(void)* data) {
441         gfxGlLog.warning("unimplemented GL command");
442     }
443 
444     override void draw(uint vertexCount, uint instanceCount, uint firstVertex, uint firstInstance) {
445         ensureBindings();
446         _cmds ~= new DrawCmd(_primitive, cast(GLint) firstVertex, cast(GLsizei) vertexCount,
447                 cast(GLsizei) instanceCount, cast(GLuint) firstInstance);
448     }
449 
450     override void drawIndexed(uint indexCount, uint instanceCount,
451             uint firstVertex, int vertexOffset, uint firstInstance) {
452         ensureBindings();
453 
454         const factor = _indexType == GL_UNSIGNED_SHORT ? 2 : 4;
455         const offset = factor * firstVertex + _indexOffset;
456 
457         _cmds ~= new DrawIndexedCmd(_primitive, cast(GLsizei) indexCount, _indexType, offset,
458                 cast(GLint) vertexOffset, cast(GLsizei) instanceCount, cast(GLuint) firstInstance);
459     }
460 
461     override void execute(SecondaryCommandBuffer[] buffers) {
462         assert(_level == CommandBufferLevel.primary);
463         assert(false, "not implemented");
464     }
465 
466     private void dirty(Dirty flag) {
467         _dirty |= flag;
468     }
469 
470     private void clean(Dirty flag) {
471         _dirty &= ~flag;
472     }
473 
474     private bool allDirty(Dirty flags) {
475         return (_dirty & flags) == flags;
476     }
477 
478     private bool someDirty(Dirty flags) {
479         return (_dirty & flags) != Dirty.none;
480     }
481 
482     private void ensureBindings() {
483         if (someDirty(Dirty.vertexBindings)) {
484             bindAttribs();
485             clean(Dirty.vertexBindings);
486         }
487     }
488 
489     private void bindAttribs() {
490         assert(_vertexBindings.length == _inputBindings.length);
491         assert(someDirty(Dirty.vertexBindings));
492 
493         import gfx.gl3.conv : glVertexFormat, vertexFormatSupported;
494         import gfx.gl3.resource : GlBuffer;
495         import gfx.graal.format : formatDesc;
496         import std.algorithm : filter;
497 
498         foreach (bi, vb; _vertexBindings) {
499             const bindingInfo = _inputBindings[bi];
500 
501             GlVertexAttrib[] attribs;
502 
503             foreach (ai; _inputAttribs.filter!(ia => ia.binding == bi)) {
504                 const f = ai.format;
505                 assert(vertexFormatSupported(f));
506 
507                 attribs ~= GlVertexAttrib(ai.location, glVertexFormat(f), vb.offset + ai.offset);
508             }
509 
510             auto buf = cast(GlBuffer) vb.buffer;
511             _cmds ~= new BindVertexBufCmd(buf.name, cast(GLsizei) bindingInfo.stride, attribs);
512         }
513     }
514 
515     void setActiveSubpass(uint subpass) {
516         assert(_renderPass);
517         assert(_attachments.length == _renderPass.attachments.length);
518         const sp = _renderPass.subpasses[subpass];
519         foreach (ref a; _attachments) {
520             a.enabled = false;
521         }
522         foreach (ar; sp.colors) {
523             _attachments[ar.attachment].enabled = true;
524         }
525         _subpass = subpass;
526     }
527 
528 }
529 
530 private:
531 
532 struct GlState {
533     import gfx.graal.pipeline : DepthInfo, Rasterizer, StencilInfo, ViewportConfig;
534     import gfx.graal.types : Rect, Viewport;
535 
536     GLuint prog;
537     const(ViewportConfig)[] vcs;
538     const(Viewport)[] viewports;
539     const(Rect)[] scissors;
540     Rasterizer rasterizer;
541     DepthInfo depthInfo;
542     StencilInfo stencilInfo;
543 }
544 
545 struct GlColorAttachment {
546     import gfx.graal.pipeline : ColorBlendAttachment;
547     import gfx.graal.renderpass : AttachmentDescription;
548 
549     bool enabled;
550     AttachmentDescription desc;
551     ColorBlendAttachment attachment;
552 }
553 
554 abstract class GlCommand {
555     abstract void execute(GlQueue queue, Gl gl);
556 }
557 
558 string allFieldsCtor(T)() {
559     import std.array : join;
560     import std.traits : FieldNameTuple, Fields;
561 
562     alias names = FieldNameTuple!T;
563     alias types = Fields!T;
564 
565     string[] fields;
566     static foreach (i, n; names) {
567         fields ~= types[i].stringof ~ " " ~ n;
568     }
569     string code = "this(" ~ fields.join(", ") ~ ") {\n";
570     static foreach (n; names) {
571         code ~= "    this." ~ n ~ " = " ~ n ~ ";\n";
572     }
573     return code ~ "}";
574 }
575 
576 final class CopyBufToBufCmd : GlCommand {
577     import gfx.gl3.resource : GlBuffer;
578 
579     GlBuffer src;
580     GlBuffer dst;
581     CopyRegion region;
582 
583     mixin(allFieldsCtor!(typeof(this)));
584 
585     override void execute(GlQueue queue, Gl gl) {
586         gl.BindBuffer(GL_COPY_READ_BUFFER, src.name);
587         gl.BindBuffer(GL_COPY_WRITE_BUFFER, dst.name);
588         gl.CopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER,
589                 cast(GLintptr) region.offset.from,
590                 cast(GLintptr) region.offset.to, cast(GLsizeiptr) region.size);
591         gl.BindBuffer(GL_COPY_READ_BUFFER, 0);
592         gl.BindBuffer(GL_COPY_WRITE_BUFFER, 0);
593 
594         import gfx.gl3.error : glCheck;
595 
596         glCheck(gl, "copy buffer to buffer");
597     }
598 }
599 
600 final class CopyBufToImgCmd : GlCommand {
601     import gfx.gl3.resource : GlBuffer, GlImage;
602 
603     GlBuffer buf;
604     GlImage img;
605     BufferImageCopy region;
606 
607     mixin(allFieldsCtor!(typeof(this)));
608 
609     override void execute(GlQueue queue, Gl gl) {
610         gl.ActiveTexture(GL_TEXTURE0);
611         gl.BindBuffer(GL_PIXEL_UNPACK_BUFFER, buf.name);
612         gl.BindTexture(img.texTarget, img.name);
613         img.texSubImage(region);
614         gl.BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
615 
616         import gfx.gl3.error : glCheck;
617 
618         glCheck(gl, "copy buffer to image");
619     }
620 }
621 
622 final class BindFramebufferCmd : GlCommand {
623     GLuint fbo;
624     this(GLuint fbo) {
625         this.fbo = fbo;
626     }
627 
628     override void execute(GlQueue queue, Gl gl) {
629         gl.BindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
630     }
631 }
632 
633 final class SetupFramebufferCmd : GlCommand {
634     import gfx.gl3.resource : GlImage, GlImgType;
635 
636     GLuint fbo;
637     GlImage img;
638 
639     this(GLuint fbo, GlImage img) {
640         this.fbo = fbo;
641         this.img = img;
642     }
643 
644     override void execute(GlQueue queue, Gl gl) {
645         gl.BindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
646         final switch (img.glType) {
647         case GlImgType.renderBuf:
648             gl.FramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER,
649                     GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, img.name);
650             break;
651         case GlImgType.tex:
652             gl.FramebufferTexture2D(GL_DRAW_FRAMEBUFFER,
653                     GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, img.name, 0);
654             break;
655         }
656         const GLenum drawBuf = GL_COLOR_ATTACHMENT0;
657         gl.DrawBuffers(1, &drawBuf);
658     }
659 }
660 
661 final class SetViewportConfigsCmd : GlCommand {
662     import gfx.graal.pipeline : ViewportConfig;
663 
664     ViewportConfig[] viewports;
665     this(ViewportConfig[] viewports) {
666         this.viewports = viewports;
667     }
668 
669     override void execute(GlQueue queue, Gl gl) {
670 
671         if (queue.state.vcs == viewports)
672             return;
673 
674         if (viewports.length > 1 && !queue.info.viewportArray) {
675             gfxGlLog.error("ARB_viewport_array not supported");
676             viewports = viewports[0 .. 1];
677         }
678 
679         if (viewports.length > 1) {
680             foreach (i, vc; viewports) {
681                 const vp = vc.viewport;
682                 gl.ViewportIndexedf(cast(GLuint) i, vp.x, vp.y, vp.width, vp.height);
683                 gl.DepthRangeIndexed(cast(GLuint) i, vp.minDepth, vp.maxDepth);
684 
685                 const sc = vc.scissors;
686                 gl.ScissorIndexed(cast(GLuint) i, cast(GLint) sc.x,
687                         cast(GLint) sc.y, cast(GLsizei) sc.width, cast(GLsizei) sc.height);
688             }
689         } else if (viewports.length == 1) {
690             const vp = viewports[0].viewport;
691             gl.Viewport(cast(GLint) vp.x, cast(GLint) vp.y,
692                     cast(GLsizei) vp.width, cast(GLsizei) vp.height);
693             gl.DepthRangef(vp.minDepth, vp.maxDepth);
694 
695             const sc = viewports[0].scissors;
696             gl.Scissor(cast(GLint) sc.x, cast(GLint) sc.y, cast(GLsizei) sc.width,
697                     cast(GLsizei) sc.height);
698         }
699 
700         queue.state.vcs = viewports;
701     }
702 }
703 
704 final class SetViewportsCmd : GlCommand {
705     import gfx.graal.types : Viewport;
706 
707     uint firstViewport;
708     const(Viewport)[] viewports;
709 
710     this(in uint firstViewport, const(Viewport)[] viewports) {
711         this.firstViewport = firstViewport;
712         this.viewports = viewports;
713     }
714 
715     override void execute(GlQueue queue, Gl gl) {
716 
717         if (queue.state.viewports == viewports)
718             return;
719 
720         bool useArray = viewports.length > 1 || firstViewport > 0;
721 
722         if (useArray && !queue.info.viewportArray) {
723             gfxGlLog.error("ARB_viewport_array not supported");
724             viewports = viewports[0 .. 1];
725             firstViewport = 0;
726             useArray = false;
727         }
728 
729         if (useArray) {
730             foreach (i, vp; viewports) {
731                 gl.ViewportIndexedf(cast(GLuint)(i + firstViewport), vp.x, vp.y,
732                         vp.width, vp.height);
733                 gl.DepthRangeIndexed(cast(GLuint)(i + firstViewport), vp.minDepth, vp.maxDepth);
734             }
735         } else if (viewports.length == 1) {
736             const vp = viewports[0];
737             gl.Viewport(cast(GLint) vp.x, cast(GLint) vp.y,
738                     cast(GLsizei) vp.width, cast(GLsizei) vp.height);
739             gl.DepthRangef(vp.minDepth, vp.maxDepth);
740         }
741 
742         queue.state.viewports = viewports;
743     }
744 }
745 
746 final class SetScissorsCmd : GlCommand {
747     import gfx.graal.types : Rect;
748 
749     uint firstScissor;
750     const(Rect)[] scissors;
751 
752     this(in uint firstScissor, const(Rect)[] scissors) {
753         this.firstScissor = firstScissor;
754         this.scissors = scissors;
755     }
756 
757     override void execute(GlQueue queue, Gl gl) {
758         import std.algorithm : equal, map;
759 
760         if (queue.state.scissors == scissors)
761             return;
762 
763         bool useArray = scissors.length > 1 || firstScissor > 0;
764         if (useArray && !queue.info.viewportArray) {
765             gfxGlLog.error("ARB_viewport_array not supported");
766             scissors = scissors[0 .. 1];
767             firstScissor = 0;
768             useArray = false;
769         }
770 
771         if (useArray) {
772             foreach (i, sc; scissors) {
773                 gl.ScissorIndexed(cast(GLuint)(i + firstScissor),
774                         cast(GLint) sc.x, cast(GLint) sc.y,
775                         cast(GLsizei) sc.width, cast(GLsizei) sc.height);
776             }
777         } else if (scissors.length == 1) {
778             const sc = scissors[0];
779             gl.Scissor(cast(GLint) sc.x, cast(GLint) sc.y, cast(GLsizei) sc.width,
780                     cast(GLsizei) sc.height);
781         }
782 
783         queue.state.scissors = scissors;
784     }
785 }
786 
787 final class SetLineWidthCmd : GlCommand {
788     float lineWidth;
789     this(in float lineWidth) {
790         this.lineWidth = lineWidth;
791     }
792 
793     override void execute(GlQueue queue, Gl gl) {
794         gl.LineWidth(this.lineWidth);
795     }
796 }
797 
798 final class SetDepthBiasCmd : GlCommand {
799     float constFactor;
800     float clamp;
801     float slopeFactor;
802 
803     mixin(allFieldsCtor!(typeof(this)));
804 
805     override void execute(GlQueue queue, Gl gl) {
806         if (queue.info.polygonOffsetClamp) {
807             gl.PolygonOffsetClamp(slopeFactor, constFactor, clamp);
808         } else {
809             gl.PolygonOffset(slopeFactor, constFactor);
810         }
811     }
812 }
813 
814 final class SetBlendConstantsCmd : GlCommand {
815     float[4] constants;
816 
817     mixin(allFieldsCtor!(typeof(this)));
818 
819     override void execute(GlQueue queue, Gl gl) {
820         gl.BlendColor(constants[0], constants[1], constants[2], constants[3]);
821     }
822 }
823 
824 final class ClearColorCmd : GlCommand {
825 
826     ClearColorValues values;
827 
828     this(ClearColorValues values) {
829         this.values = values;
830     }
831 
832     override void execute(GlQueue queue, Gl gl) {
833         gl.ColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
834         final switch (values.type) {
835         case ClearColorValues.Type.f32:
836             gl.ClearBufferfv(GL_COLOR, 0, &values.values.f32[0]);
837             break;
838         case ClearColorValues.Type.i32:
839             gl.ClearBufferiv(GL_COLOR, 0, &values.values.i32[0]);
840             break;
841         case ClearColorValues.Type.u32:
842             gl.ClearBufferuiv(GL_COLOR, 0, &values.values.u32[0]);
843             break;
844         }
845     }
846 }
847 
848 final class ClearDepthStencilCmd : GlCommand {
849 
850     ClearDepthStencilValues values;
851     bool depth;
852     bool stencil;
853 
854     this(ClearDepthStencilValues values, bool depth, bool stencil) {
855         this.values = values;
856         this.depth = depth;
857         this.stencil = stencil;
858     }
859 
860     override void execute(GlQueue queue, Gl gl) {
861         if (depth) {
862             gl.DepthMask(GL_TRUE);
863             gl.ClearBufferfv(GL_DEPTH, 0, &values.depth);
864         }
865         if (stencil) {
866             const val = cast(GLint) values.stencil;
867             gl.StencilMask(GLuint.max);
868             gl.ClearBufferiv(GL_STENCIL, 0, &val);
869         }
870     }
871 }
872 
873 final class BindProgramCmd : GlCommand {
874     GLuint prog;
875     this(GLuint prog) {
876         this.prog = prog;
877     }
878 
879     override void execute(GlQueue queue, Gl gl) {
880         if (queue.state.prog == prog)
881             return;
882         gl.UseProgram(prog);
883         queue.state.prog = prog;
884     }
885 }
886 
887 final class SetRasterizerCmd : GlCommand {
888     import gfx.graal.pipeline : Rasterizer;
889 
890     Rasterizer rasterizer;
891     this(Rasterizer rasterizer) {
892         this.rasterizer = rasterizer;
893     }
894 
895     override void execute(GlQueue queue, Gl gl) {
896         import gfx.gl3.conv : toGl;
897         import gfx.graal.pipeline : Cull, PolygonMode;
898 
899         if (queue.state.rasterizer == rasterizer)
900             return;
901 
902         void polygonBias(GLenum polygonMode, GLenum depthBias) {
903             gl.PolygonMode(GL_FRONT_AND_BACK, polygonMode);
904             if (rasterizer.depthBias.isSome) {
905                 const db = rasterizer.depthBias.get;
906                 gl.Enable(depthBias);
907                 if (queue.info.polygonOffsetClamp) {
908                     gl.PolygonOffsetClamp(db.slopeFactor, db.constantFactor, db.clamp);
909                 } else {
910                     gl.PolygonOffset(db.slopeFactor, db.constantFactor);
911                 }
912             } else {
913                 gl.Disable(depthBias);
914             }
915         }
916 
917         final switch (rasterizer.mode) {
918         case PolygonMode.point:
919             polygonBias(GL_POINT, GL_POLYGON_OFFSET_POINT);
920             break;
921         case PolygonMode.line:
922             polygonBias(GL_LINE, GL_POLYGON_OFFSET_LINE);
923             gl.LineWidth(rasterizer.lineWidth);
924             break;
925         case PolygonMode.fill:
926             polygonBias(GL_FILL, GL_POLYGON_OFFSET_FILL);
927             break;
928         }
929 
930         if (rasterizer.cull == Cull.none) {
931             gl.Disable(GL_CULL_FACE);
932         } else {
933             gl.Enable(GL_CULL_FACE);
934             switch (rasterizer.cull) {
935             case Cull.back:
936                 gl.CullFace(GL_BACK);
937                 break;
938             case Cull.front:
939                 gl.CullFace(GL_FRONT);
940                 break;
941             case Cull.frontAndBack:
942                 gl.CullFace(GL_FRONT_AND_BACK);
943                 break;
944             default:
945                 break;
946             }
947             gl.FrontFace(toGl(rasterizer.front));
948         }
949 
950         if (rasterizer.depthClamp) {
951             gl.Enable(GL_DEPTH_CLAMP);
952         } else {
953             gl.Disable(GL_DEPTH_CLAMP);
954         }
955 
956         queue.state.rasterizer = rasterizer;
957     }
958 }
959 
960 final class SetDepthInfoCmd : GlCommand {
961     import gfx.graal.pipeline : DepthInfo;
962 
963     DepthInfo info;
964     this(DepthInfo info) {
965         this.info = info;
966     }
967 
968     override void execute(GlQueue queue, Gl gl) {
969         import gfx.gl3.conv : toGl;
970 
971         if (queue.state.depthInfo == info)
972             return;
973 
974         if (info.enabled) {
975             gl.Enable(GL_DEPTH_TEST);
976             gl.DepthFunc(toGl(info.compareOp));
977             gl.DepthMask(cast(GLboolean) info.write);
978             if (info.boundsTest) {
979                 gfxGlLog.warning("no support for depth bounds test");
980             }
981         } else {
982             gl.Disable(GL_DEPTH_TEST);
983         }
984 
985         queue.state.depthInfo = info;
986     }
987 }
988 
989 final class SetStencilInfoCmd : GlCommand {
990     import gfx.graal.pipeline : StencilInfo;
991 
992     StencilInfo info;
993     this(StencilInfo info) {
994         this.info = info;
995     }
996 
997     override void execute(GlQueue queue, Gl gl) {
998         import gfx.gl3.conv : toGl;
999         import gfx.graal.pipeline : StencilOpState;
1000 
1001         if (queue.state.stencilInfo == info)
1002             return;
1003 
1004         if (info.enabled) {
1005             gl.Enable(GL_STENCIL_TEST);
1006             void bindFace(GLenum face, StencilOpState state) {
1007                 gl.StencilOpSeparate(face, toGl(state.failOp),
1008                         toGl(state.depthFailOp), toGl(state.passOp));
1009                 gl.StencilFuncSeparate(face, toGl(state.compareOp),
1010                         state.refMask, state.compareMask);
1011                 gl.StencilMaskSeparate(face, state.writeMask);
1012             }
1013 
1014             bindFace(GL_FRONT, info.front);
1015             bindFace(GL_BACK, info.back);
1016         } else {
1017             gl.Disable(GL_STENCIL_TEST);
1018         }
1019     }
1020 }
1021 
1022 struct GlVertexAttrib {
1023     import gfx.gl3.conv : GlVertexFormat;
1024 
1025     GLuint index;
1026     GlVertexFormat format;
1027     size_t offset;
1028 }
1029 
1030 final class BindVertexBufCmd : GlCommand {
1031     GLuint buffer;
1032     GLsizei stride;
1033     GlVertexAttrib[] attribs;
1034 
1035     this(GLuint buffer, GLsizei stride, GlVertexAttrib[] attribs) {
1036         this.buffer = buffer;
1037         this.stride = stride;
1038         this.attribs = attribs;
1039     }
1040 
1041     override void execute(GlQueue queue, Gl gl) {
1042         import gfx.gl3.conv : VAOAttribFun;
1043 
1044         gl.BindBuffer(GL_ARRAY_BUFFER, buffer);
1045         foreach (at; attribs) {
1046             final switch (at.format.fun) {
1047             case VAOAttribFun.f:
1048                 gl.VertexAttribPointer(at.index, at.format.size,
1049                         at.format.type, at.format.normalized, stride,
1050                         cast(const(void*)) at.offset);
1051                 break;
1052             case VAOAttribFun.i:
1053                 gl.VertexAttribIPointer(at.index,
1054                         at.format.size, at.format.type, stride, cast(const(void*)) at.offset);
1055                 break;
1056             case VAOAttribFun.d:
1057                 gl.VertexAttribLPointer(at.index,
1058                         at.format.size, at.format.type, stride, cast(const(void*)) at.offset);
1059                 break;
1060             }
1061             gl.EnableVertexAttribArray(at.index);
1062         }
1063         gl.BindBuffer(GL_ARRAY_BUFFER, 0);
1064     }
1065 }
1066 
1067 final class BindIndexBufCmd : GlCommand {
1068     GLuint buf;
1069 
1070     this(GLuint buf) {
1071         this.buf = buf;
1072     }
1073 
1074     override void execute(GlQueue queue, Gl gl) {
1075         gl.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf);
1076     }
1077 }
1078 
1079 final class BindUniformBufferCmd : GlCommand {
1080     import gfx.gl3.resource : GlBuffer;
1081     import gfx.graal.pipeline : BufferDescriptor;
1082 
1083     GLuint binding;
1084     BufferDescriptor bufferDesc;
1085     size_t dynamicOffset;
1086 
1087     this(GLuint binding, BufferDescriptor bd, in size_t dynOffset) {
1088         this.binding = binding;
1089         this.bufferDesc = bd;
1090         this.dynamicOffset = dynOffset;
1091     }
1092 
1093     override void execute(GlQueue queue, Gl gl) {
1094         auto glBuf = cast(GlBuffer) bufferDesc.buffer;
1095 
1096         gl.BindBufferRange(GL_UNIFORM_BUFFER, binding, glBuf.name,
1097                 cast(GLintptr)(bufferDesc.offset + dynamicOffset),
1098                 cast(GLintptr)bufferDesc.size);
1099     }
1100 }
1101 
1102 final class BindSamplerImageCmd : GlCommand {
1103     import gfx.graal.pipeline : ImageSamplerDescriptor;
1104 
1105     GLuint binding;
1106     ImageSamplerDescriptor sid;
1107 
1108     mixin(allFieldsCtor!(typeof(this)));
1109 
1110     override void execute(GlQueue queue, Gl gl) {
1111         import gfx.gl3.conv : toGl;
1112         import gfx.gl3.error : glCheck;
1113         import gfx.gl3.resource : GlImageView, GlSampler;
1114 
1115         auto view = cast(GlImageView) sid.view;
1116         auto sampler = cast(GlSampler) sid.sampler;
1117 
1118         gl.ActiveTexture(GL_TEXTURE0 + binding);
1119         gl.BindTexture(view.target, view.name);
1120         glCheck(gl, "bind texture");
1121 
1122         sampler.bind(view.target, binding);
1123         glCheck(gl, "bind sampler");
1124 
1125         const swizzle = toGl(view.swizzle);
1126         gl.TexParameteriv(view.target, GL_TEXTURE_SWIZZLE_RGBA, &swizzle[0]);
1127     }
1128 }
1129 
1130 final class BindBlendSlotsCmd : GlCommand {
1131     import gfx.graal.pipeline : ColorMask;
1132 
1133     GlColorAttachment[] attachments;
1134 
1135     this(GlColorAttachment[] attachments) {
1136         this.attachments = attachments;
1137     }
1138 
1139     override void execute(GlQueue queue, Gl gl) {
1140         import gfx.gl3.conv : toGl;
1141 
1142         foreach (const ind, a; attachments) {
1143             const slot = cast(GLuint) ind;
1144             if (a.enabled) {
1145                 // TODO logicOp
1146                 if (a.attachment.enabled) {
1147                     // TODO
1148                     gl.Enablei(GL_BLEND, slot);
1149                     gl.BlendEquationSeparatei(slot, toGl(a.attachment.colorBlend.op),
1150                             toGl(a.attachment.alphaBlend.op),);
1151                     gl.BlendFuncSeparatei(slot, toGl(a.attachment.colorBlend.factor.from),
1152                             toGl(a.attachment.colorBlend.factor.to), toGl(a.attachment.alphaBlend.factor.from),
1153                             toGl(a.attachment.alphaBlend.factor.to),);
1154                 } else {
1155                     gl.Disablei(GL_BLEND, slot);
1156                 }
1157                 import std.typecons : BitFlags, Yes;
1158 
1159                 BitFlags!(ColorMask, Yes.unsafe) cm = a.attachment.colorMask;
1160                 gl.ColorMaski(slot, (cm & ColorMask.r) ? GL_TRUE : GL_FALSE,
1161                         (cm & ColorMask.g) ? GL_TRUE : GL_FALSE, (cm & ColorMask.b)
1162                         ? GL_TRUE : GL_FALSE, (cm & ColorMask.a) ? GL_TRUE : GL_FALSE);
1163             } else {
1164                 gl.ColorMaski(slot, GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
1165             }
1166         }
1167     }
1168 }
1169 
1170 final class DrawCmd : GlCommand {
1171     GLenum primitive;
1172     GLint first;
1173     GLsizei count;
1174     GLsizei instanceCount;
1175     GLuint baseInstance;
1176 
1177     this(GLenum primitive, GLint first, GLsizei count, GLsizei instanceCount, GLuint baseInstance) {
1178         this.primitive = primitive;
1179         this.first = first;
1180         this.count = count;
1181         this.instanceCount = instanceCount;
1182         this.baseInstance = baseInstance;
1183     }
1184 
1185     override void execute(GlQueue queue, Gl gl) {
1186         if (baseInstance != 0 && !queue.info.baseInstance) {
1187             gfxGlLog.error("No support for ARB_base_instance");
1188             return;
1189         }
1190         if (instanceCount <= 1) {
1191             gl.DrawArrays(primitive, first, count);
1192         } else if (instanceCount > 1 && baseInstance == 0) {
1193             gl.DrawArraysInstanced(primitive, first, count, instanceCount);
1194         } else if (instanceCount > 1 && baseInstance != 0) {
1195             gl.DrawArraysInstancedBaseInstance(primitive, first, count,
1196                     instanceCount, baseInstance);
1197         }
1198         import gfx.gl3.error : glCheck;
1199 
1200         glCheck(gl, "draw");
1201     }
1202 }
1203 
1204 final class DrawIndexedCmd : GlCommand {
1205     GLenum primitive;
1206     GLsizei count;
1207     GLenum type;
1208     size_t indexBufOffset;
1209     GLint baseVertex;
1210     GLsizei instanceCount;
1211     GLuint baseInstance;
1212 
1213     this(GLenum primitive, GLsizei count, GLenum type, size_t indexBufOffset,
1214             GLint baseVertex, GLsizei instanceCount, GLuint baseInstance) {
1215         this.primitive = primitive;
1216         this.count = count;
1217         this.type = type;
1218         this.indexBufOffset = indexBufOffset;
1219         this.baseVertex = baseVertex;
1220         this.instanceCount = instanceCount;
1221         this.baseInstance = baseInstance;
1222     }
1223 
1224     override void execute(GlQueue queue, Gl gl) {
1225         const offset = cast(const(void*)) indexBufOffset;
1226 
1227         if (baseVertex != 0 && !queue.info.drawElementsBaseVertex) {
1228             gfxGlLog.error("No support for ARB_draw_elements_base_vertex");
1229             return;
1230         }
1231         if (baseInstance != 0 && !queue.info.baseInstance) {
1232             gfxGlLog.error("No support for ARB_base_instance");
1233             return;
1234         }
1235 
1236         if (instanceCount <= 1 && baseVertex == 0) {
1237             gl.DrawElements(primitive, count, type, offset);
1238         } else if (instanceCount <= 1 && baseVertex != 0) {
1239             gl.DrawElementsBaseVertex(primitive, count, type, offset, baseVertex);
1240         } else if (instanceCount > 1 && baseInstance == 0 && baseVertex == 0) {
1241             gl.DrawElementsInstanced(primitive, count, type, offset, instanceCount);
1242         } else if (instanceCount > 1 && baseInstance == 0 && baseVertex != 0) {
1243             gl.DrawElementsInstancedBaseVertex(primitive, count, type, offset,
1244                     instanceCount, baseVertex);
1245         } else if (instanceCount > 1 && baseInstance != 0 && baseVertex == 0) {
1246             gl.DrawElementsInstancedBaseInstance(primitive, count, type,
1247                     offset, instanceCount, baseInstance);
1248         } else {
1249             gl.DrawElementsInstancedBaseVertexBaseInstance(primitive, count,
1250                     type, offset, instanceCount, baseVertex, baseInstance);
1251         }
1252         import gfx.gl3.error : glCheck;
1253 
1254         glCheck(gl, "draw indexed");
1255     }
1256 }