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 }