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 }