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