1 module gfx.gl3.pipeline;
2 
3 package:
4 
5 import gfx.bindings.opengl.gl : Gl, GLenum, GLint, GLuint;
6 import gfx.graal.device : Device;
7 import gfx.graal.pipeline;
8 import gfx.graal.renderpass;
9 
10 final class GlShaderModule : ShaderModule
11 {
12     import gfx.bindings.opengl.gl : GLuint;
13     import gfx.core.rc : atomicRcCode, Rc;
14     import gfx.gl3 : GlShare;
15 
16     mixin(atomicRcCode);
17 
18     private Rc!Device _dev;
19     private Gl gl;
20     private GLuint _name;
21     private string _code;
22     private ShaderStage _stage;
23 
24     this (Device dev, GlShare share, in const(uint)[] code) {
25         import spirv_cross : SpvCompilerGlsl;
26         _dev = dev;
27         gl = share.gl;
28         auto cl = new SpvCompilerGlsl(code);
29         scope(exit) cl.dispose();
30         auto opts = cl.options;
31         opts.ver = share.info.glslVer;
32         opts.enable_420pack = false;
33         opts.vertex_invert_y = true;
34         // opts.vertex_transform_clip_space = true;
35         cl.options = opts;
36         _code = cl.compile();
37     }
38 
39     override void dispose() {
40         if (_name != 0) {
41             gl.DeleteShader(_name);
42         }
43         _dev.unload();
44     }
45 
46     override @property Device device() {
47         return _dev;
48     }
49 
50     override @property string entryPoint() {
51         return "main";
52     }
53 
54     GLuint compile(in ShaderStage stage)
55     {
56         if (_name) {
57             import std.exception : enforce;
58             enforce(stage == _stage, "unsupported: try to compile the same shader for different stages");
59             return _name;
60         }
61 
62         import gfx.bindings.opengl.gl : GLchar, GLint;
63         import gfx.gl3.conv : toGl;
64 
65         const target = toGl(stage);
66         _name = gl.CreateShader(target);
67 
68         const GLint len = cast(GLint)_code.length;
69         const GLchar* str = cast(const(GLchar)*)_code.ptr;
70         gl.ShaderSource(_name, 1, &str, &len);
71 
72         gl.CompileShader(_name);
73 
74         const log = getShaderLog(gl, _name);
75         if (!getShaderCompileStatus(gl, _name)) {
76             import gfx.gl3.error : Gl3ShaderCompileException;
77             throw new Gl3ShaderCompileException(stage, _code, log);
78         }
79         else if (log.length) {
80             import std.conv : to;
81             import gfx.gl3 : gfxGlLog;
82             gfxGlLog.infof("%s shader compile message: %s", stage.to!string, log);
83         }
84         _stage = stage;
85         return _name;
86     }
87 
88 }
89 
90 
91 private SubpassDescription clone(const(SubpassDescription) sd) {
92     return SubpassDescription(
93         sd.inputs.dup, sd.colors.dup, sd.depthStencil.save, sd.preserves.dup
94     );
95 }
96 
97 private SubpassDescription[] clone(in SubpassDescription[] descs) {
98     import std.array : uninitializedArray;
99     auto duplicate = uninitializedArray!(SubpassDescription[])(descs.length);
100     foreach (i; 0 .. descs.length) {
101         duplicate[i] = descs[i].clone();
102     }
103     return duplicate;
104 }
105 
106 
107 final class GlRenderPass : RenderPass
108 {
109     import gfx.core.rc : atomicRcCode, Rc;
110 
111     mixin(atomicRcCode);
112 
113     private Rc!Device _dev;
114     package AttachmentDescription[] attachments;
115     package SubpassDescription[]    subpasses;
116     package SubpassDependency[]     deps;
117 
118     this(   Device device,
119             in AttachmentDescription[] attachments,
120             in SubpassDescription[] subpasses,
121             in SubpassDependency[] dependencies)
122     {
123         _dev = device;
124         this.attachments = attachments.dup;
125         this.subpasses = subpasses.clone();
126         this.deps = dependencies.dup;
127     }
128 
129     override void dispose() {
130         _dev.unload();
131     }
132 
133     override @property Device device() {
134         return _dev;
135     }
136 }
137 
138 final class GlPipelineLayout : PipelineLayout
139 {
140     import gfx.core.rc : atomicRcCode, Rc;
141 
142     mixin(atomicRcCode);
143 
144     private Rc!Device _dev;
145     private DescriptorSetLayout[] _layouts;
146     private PushConstantRange[] _push;
147 
148     this (Device device, DescriptorSetLayout[] layouts, PushConstantRange[] push) {
149         import gfx.core.rc : retainArr;
150         _dev = device;
151         _layouts = layouts;
152         retainArr(_layouts);
153         _push = push;
154     }
155 
156     override void dispose() {
157         import gfx.core.rc : releaseArr;
158         releaseArr(_layouts);
159         _dev.unload();
160     }
161 
162     override @property Device device() {
163         return _dev;
164     }
165 }
166 
167 
168 final class GlFramebuffer : Framebuffer
169 {
170     import gfx.bindings.opengl.gl : Gl, GLuint;
171     import gfx.core.rc : atomicRcCode, Rc;
172     import gfx.gl3 : GlShare;
173     import gfx.gl3.resource : GlImageView;
174 
175     mixin (atomicRcCode);
176 
177     private Rc!Device _dev;
178     private GlRenderPass rp;
179     private GlImageView[] attachments;
180     private uint width;
181     private uint height;
182     private uint layers;
183 
184     private Gl gl;
185     private GLuint _name;
186 
187     this (GlShare share, GlRenderPass rp, GlImageView[] attachments, uint width, uint height, uint layers)
188     {
189         import gfx.core.rc : retainArr;
190         import std.exception : enforce;
191 
192         enforce(
193             rp.attachments.length == attachments.length,
194             "Render pass do not fit with attachments in Framebuffer creation"
195         );
196 
197         _dev = rp.device;
198         this.rp = rp;
199         this.attachments = attachments;
200         this.width = width;
201         this.height = height;
202         this.layers = layers;
203 
204         rp.retain();
205         retainArr(this.attachments);
206 
207         this.gl = share.gl;
208 
209         gl.GenFramebuffers(1, &_name);
210 
211         import gfx.bindings.opengl.gl : GLenum, GLsizei, GL_COLOR_ATTACHMENT0,
212                                         GL_DRAW_FRAMEBUFFER;
213 
214         gl.BindFramebuffer(GL_DRAW_FRAMEBUFFER, _name);
215 
216         uint colorNum = 0;
217 
218         foreach(attachment; attachments) {
219             attachment.attachToFbo(GL_DRAW_FRAMEBUFFER, colorNum);
220         }
221 
222         auto drawBufs = new GLenum[colorNum];
223         foreach(i; 0 .. colorNum) {
224             drawBufs[i] = GL_COLOR_ATTACHMENT0 + i;
225         }
226         gl.DrawBuffers(cast(GLsizei)colorNum, drawBufs.ptr);
227 
228         gl.BindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
229     }
230 
231     @property GLuint name() const {
232         return _name;
233     }
234 
235     override void dispose()
236     {
237         import gfx.core.rc : releaseArr;
238 
239         gl.DeleteFramebuffers(1, &_name);
240 
241         releaseArr(attachments);
242         rp.release();
243         _dev.unload();
244     }
245 
246     override @property Device device() {
247         return _dev;
248     }
249 }
250 
251 final class GlPipeline : Pipeline
252 {
253     import gfx.bindings.opengl.gl : GLuint;
254     import gfx.core.rc : atomicRcCode, Rc;
255     import gfx.gl3 : GlShare;
256 
257     mixin(atomicRcCode);
258 
259     private Rc!Device _dev;
260     private Gl gl;
261     package GLuint prog;
262     package PipelineInfo info;
263 
264     this(Device dev, GlShare share, PipelineInfo info) {
265         _dev = dev;
266         this.gl = share.gl;
267         this.info = info;
268 
269         prog = gl.CreateProgram();
270 
271         import std.exception : enforce;
272         enforce(info.shaders.vertex, "Vertex input shader is mandatory");
273 
274         void attachShader(ShaderStage stage, ShaderModule mod) {
275             if (!mod) return;
276             auto glMod = cast(GlShaderModule)mod;
277             const name = glMod.compile(stage);
278             gl.AttachShader(prog, name);
279         }
280 
281         attachShader(ShaderStage.vertex, info.shaders.vertex);
282         attachShader(ShaderStage.tessellationControl, info.shaders.tessControl);
283         attachShader(ShaderStage.tessellationEvaluation, info.shaders.tessEval);
284         attachShader(ShaderStage.geometry, info.shaders.geometry);
285         attachShader(ShaderStage.fragment, info.shaders.fragment);
286 
287         gl.LinkProgram(prog);
288 
289         const log = getProgramLog(gl, prog);
290         if (!getProgramLinkStatus(gl, prog)) {
291             import gfx.gl3.error : Gl3ProgramLinkException;
292             throw new Gl3ProgramLinkException(log);
293         }
294 
295         if (log.length) {
296             import gfx.gl3 : gfxGlLog;
297             gfxGlLog.infof("Program link message: %s", log);
298         }
299     }
300 
301     override void dispose() {
302         gl.DeleteProgram(prog);
303         prog = 0;
304         _dev.unload();
305     }
306 
307     override @property Device device() {
308         return _dev;
309     }
310 }
311 
312 final class GlDescriptorSetLayout : DescriptorSetLayout
313 {
314     import gfx.core.rc : atomicRcCode, Rc;
315     mixin(atomicRcCode);
316 
317     private Rc!Device _dev;
318     const(PipelineLayoutBinding[]) bindings;
319 
320     this(Device dev, in PipelineLayoutBinding[] bindings) {
321         _dev = dev;
322         this.bindings = bindings;
323     }
324     override void dispose() {
325         _dev.unload();
326     }
327 
328     override @property Device device() {
329         return _dev;
330     }
331 }
332 
333 final class GlDescriptorPool : DescriptorPool
334 {
335     import gfx.core.rc : atomicRcCode, Rc;
336     mixin(atomicRcCode);
337 
338     private Rc!Device _dev;
339     private uint maxSets;
340     private const(DescriptorPoolSize[]) sizes;
341 
342     this(Device dev, in uint maxSets, in DescriptorPoolSize[] bindings) {
343         this._dev = dev;
344         this.maxSets = maxSets;
345         this.sizes = sizes;
346     }
347     override void dispose() {
348         _dev.unload();
349     }
350 
351     override @property Device device() {
352         return _dev;
353     }
354 
355     override DescriptorSet[] allocate(DescriptorSetLayout[] layouts) {
356         import std.algorithm : map;
357         import std.array : array;
358         return layouts.map!(l => cast(DescriptorSet)new GlDescriptorSet(this, l)).array;
359     }
360 
361     override void reset() {}
362 }
363 
364 union GlDescriptor {
365     import gfx.graal.image : Sampler;
366     import gfx.graal.buffer : BufferView;
367 
368     Sampler                 sampler;
369     CombinedImageSampler    combinedImageSampler;
370     ImageViewLayout         imageViewLayout;
371     BufferRange             bufferRange;
372     BufferView              bufferView;
373 }
374 
375 struct GlDescriptorSetBinding
376 {
377     PipelineLayoutBinding layout;
378     GlDescriptor[] descriptors;
379 
380     @property uint index() const { return layout.binding; }
381 }
382 
383 final class GlDescriptorSet : DescriptorSet
384 {
385     private GlDescriptorPool _pool;
386 
387     GlDescriptorSetBinding[] bindings;
388 
389     this(GlDescriptorPool pool, DescriptorSetLayout layout) {
390         _pool = pool;
391         const layoutBindings = (cast(GlDescriptorSetLayout)layout).bindings;
392         bindings = new GlDescriptorSetBinding[layoutBindings.length];
393         foreach (i, ref b; bindings) {
394             b.layout = layoutBindings[i];
395             b.descriptors = new GlDescriptor[b.layout.descriptorCount];
396         }
397     }
398     override @property DescriptorPool pool() {
399         return _pool;
400     }
401 
402     void write(uint dstBinding, uint dstArrayElem, DescriptorWrites writes)
403     {
404         assert(writes.type == bindings[dstBinding].layout.descriptorType);
405         assign(
406             bindings[dstBinding].descriptors[
407                 dstArrayElem .. dstArrayElem+writes.count
408             ],
409             writes
410         );
411     }
412 
413     private void assign(GlDescriptor[] descs, DescriptorWrites writes) {
414         import gfx.core.util : unsafeCast;
415         import gfx.graal.image : Sampler;
416         import gfx.graal.buffer : BufferView;
417 
418         final switch (writes.type) {
419         case DescriptorType.sampler:
420             auto w = unsafeCast!(SamplerDescWrites)(writes);
421             foreach (i, ref d; descs) {
422                 d.sampler = w.descs[i];
423             }
424             break;
425         case DescriptorType.combinedImageSampler:
426             auto w = unsafeCast!(CombinedImageSamplerDescWrites)(writes);
427             foreach (i, ref d; descs) {
428                 d.combinedImageSampler = w.descs[i];
429             }
430             break;
431         case DescriptorType.sampledImage:
432         case DescriptorType.storageImage:
433         case DescriptorType.inputAttachment:
434             auto w = unsafeCast!(TDescWritesBase!ImageViewLayout)(writes);
435             foreach (i, ref d; descs) {
436                 d.imageViewLayout = w.descs[i];
437             }
438             break;
439         case DescriptorType.uniformBuffer:
440         case DescriptorType.storageBuffer:
441         case DescriptorType.uniformBufferDynamic:
442         case DescriptorType.storageBufferDynamic:
443             auto w = unsafeCast!(TDescWritesBase!(BufferRange))(writes);
444             foreach (i, ref d; descs) {
445                 d.bufferRange = w.descs[i];
446             }
447             break;
448         case DescriptorType.uniformTexelBuffer:
449         case DescriptorType.storageTexelBuffer:
450             auto w = unsafeCast!(TDescWritesBase!(BufferView))(writes);
451             foreach (i, ref d; descs) {
452                 d.bufferView = w.descs[i];
453             }
454             break;
455         }
456     }
457 
458 }
459 
460 private GLint getShaderInt(Gl gl, in GLuint name, in GLenum pname) {
461     GLint res;
462     gl.GetShaderiv(name, pname, &res);
463     return res;
464 }
465 
466 private bool getShaderCompileStatus(Gl gl, in GLuint name) {
467     import gfx.bindings.opengl.gl : GL_COMPILE_STATUS, GL_FALSE;
468     return getShaderInt(gl, name, GL_COMPILE_STATUS) != GL_FALSE;
469 }
470 
471 private string getShaderLog(Gl gl, in GLuint name) {
472     import gfx.bindings.opengl.gl : GL_INFO_LOG_LENGTH;
473     auto len = getShaderInt(gl, name, GL_INFO_LOG_LENGTH);
474     if (len > 1) { // some return 1 for empty string (null char)
475         auto msg = new char[len];
476         gl.GetShaderInfoLog(name, len, &len, msg.ptr);
477         return msg[0 .. len-1].idup;
478     }
479     else {
480         return null;
481     }
482 }
483 
484 
485 
486 private GLint getProgramInt(Gl gl, in GLuint name, in GLenum pname) {
487     GLint res;
488     gl.GetProgramiv(name, pname, &res);
489     return res;
490 }
491 
492 private bool getProgramLinkStatus(Gl gl, in GLuint name) {
493     import gfx.bindings.opengl.gl : GL_FALSE, GL_LINK_STATUS;
494     return getProgramInt(gl, name, GL_LINK_STATUS) != GL_FALSE;
495 }
496 
497 
498 private string getProgramLog(Gl gl, in GLuint name) {
499     import gfx.bindings.opengl.gl : GL_INFO_LOG_LENGTH;
500     auto len = getProgramInt(gl, name, GL_INFO_LOG_LENGTH);
501     if (len > 1) {
502         auto msg = new char[len];
503         gl.GetProgramInfoLog(name, len, &len, msg.ptr);
504         return msg[0 .. len-1].idup;
505     }
506     else {
507         return null;
508     }
509 }