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 }