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