1 module gfx.gl3.queue; 2 3 package: 4 5 import gfx.bindings.opengl.gl; 6 import gfx.core.rc : Disposable; 7 import gfx.gl3 : gfxGlLog; 8 import gfx.graal.cmd; 9 import gfx.graal.queue; 10 11 final class GlQueue : Queue, Disposable { 12 import gfx.gl3 : GlInfo, GlShare; 13 import gfx.graal.device : Device; 14 import gfx.graal.sync : Fence, Semaphore; 15 16 private GlShare share; 17 private GlInfo info; 18 private Device _device; 19 private GLuint readFbo; 20 private GLuint vao; 21 private GlState state; 22 private uint _index; 23 24 this(GlShare share, Device device, uint index) { 25 this.share = share; 26 this.info = share.info; 27 _device = device; 28 this._index = index; 29 auto gl = share.gl; 30 gl.GenFramebuffers(1, &readFbo); 31 gl.GenVertexArrays(1, &vao); 32 gl.BindVertexArray(vao); 33 } 34 35 override void dispose() { 36 auto gl = share.gl; 37 gl.DeleteFramebuffers(1, &readFbo); 38 gl.DeleteVertexArrays(1, &vao); 39 } 40 41 override @property Device device() { 42 return _device; 43 } 44 45 override @property uint index() { 46 return _index; 47 } 48 49 override void waitIdle() { 50 } 51 52 override void submit(Submission[] submissions, Fence fence) { 53 auto gl = share.gl; 54 foreach (ref s; submissions) { 55 foreach (cmdBuf; s.cmdBufs) { 56 auto glCmdBuf = cast(GlCommandBuffer) cmdBuf; 57 foreach (cmd; glCmdBuf._cmds) { 58 cmd.execute(this, gl); 59 } 60 if (glCmdBuf._usage & CommandBufferUsage.oneTimeSubmit) { 61 glCmdBuf._cmds.length = 0; 62 } 63 } 64 } 65 } 66 67 override void present(Semaphore[] waitSems, PresentRequest[] prs) { 68 import gfx.gl3.resource : GlImage, GlImgType; 69 import gfx.gl3.swapchain : GlSurface, GlSwapchain; 70 71 auto gl = share.gl; 72 73 foreach (i, pr; prs) { 74 auto sc = cast(GlSwapchain) pr.swapChain; 75 auto surf = sc.surface; 76 auto img = cast(GlImage) sc.images[pr.imageIndex]; 77 auto size = sc.size; 78 79 share.ctx.makeCurrent(surf.handle); 80 81 if (i == prs.length - 1) 82 share.ctx.swapInterval = 1; 83 else 84 share.ctx.swapInterval = 0; 85 86 import gfx.gl3.error : glCheck; 87 88 gl.BindFramebuffer(GL_READ_FRAMEBUFFER, readFbo); 89 final switch (img.glType) { 90 case GlImgType.renderBuf: 91 gl.FramebufferRenderbuffer(GL_READ_FRAMEBUFFER, 92 GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, img.name); 93 break; 94 case GlImgType.tex: 95 gl.FramebufferTexture2D(GL_READ_FRAMEBUFFER, 96 GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, img.name, 0); 97 break; 98 } 99 100 gl.BindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); 101 gl.BlitFramebuffer(0, 0, size[0], size[1], 0, 0, size[0], size[1], 102 GL_COLOR_BUFFER_BIT, GL_NEAREST); 103 glCheck(gl, "blit framebuffer"); 104 105 share.ctx.swapBuffers(surf.handle); 106 } 107 } 108 } 109 110 final class GlCommandPool : CommandPool { 111 import gfx.core.rc : atomicRcCode; 112 import gfx.gl3 : GlShare; 113 import gfx.graal.cmd : PrimaryCommandBuffer; 114 115 mixin(atomicRcCode); 116 117 private GlQueue queue; 118 private GlShare share; 119 private GLuint fbo; 120 121 this(GlQueue queue) { 122 this.queue = queue; 123 this.share = queue.share; 124 auto gl = share.gl; 125 gl.GenFramebuffers(1, &fbo); 126 } 127 128 override void dispose() { 129 auto gl = share.gl; 130 gl.DeleteFramebuffers(1, &fbo); 131 } 132 133 override void reset() { 134 } 135 136 override PrimaryCommandBuffer[] allocatePrimary(in size_t count) { 137 auto bufs = new PrimaryCommandBuffer[count]; 138 foreach (i; 0 .. count) { 139 bufs[i] = new GlCommandBuffer(this, fbo, CommandBufferLevel.primary); 140 } 141 return bufs; 142 } 143 144 override SecondaryCommandBuffer[] allocateSecondary(in size_t count) { 145 auto bufs = new SecondaryCommandBuffer[count]; 146 foreach (i; 0 .. count) { 147 bufs[i] = new GlCommandBuffer(this, fbo, CommandBufferLevel.secondary); 148 } 149 return bufs; 150 } 151 152 override void free(CommandBuffer[] buffers) { 153 } 154 } 155 156 final class GlCommandBuffer : PrimaryCommandBuffer, SecondaryCommandBuffer { 157 import gfx.gl3 : GlShare, GlInfo; 158 import gfx.gl3.conv : toGl; 159 import gfx.gl3.pipeline : GlPipeline, GlRenderPass; 160 import gfx.graal.buffer : Buffer, IndexType; 161 import gfx.graal.image : ImageBase, ImageLayout, ImageSubresourceRange; 162 import gfx.graal.pipeline : ColorBlendInfo, DepthInfo, DescriptorSet, Pipeline, PipelineLayout, 163 Rasterizer, ShaderStage, VertexInputBinding, VertexInputAttrib, ViewportConfig; 164 import gfx.graal.renderpass : Framebuffer, RenderPass; 165 import gfx.graal.types : Rect, Trans, Viewport; 166 import std.typecons : Flag; 167 168 private enum Dirty { 169 none = 0x00, 170 vertexBindings = 0x01, 171 pipeline = 0x02, 172 173 all = 0xff, 174 } 175 176 private CommandPool _pool; 177 private CommandBufferLevel _level; 178 private GLuint _fbo; 179 private CommandBufferUsage _usage; 180 181 private GlCommand[] _cmds; 182 private Dirty _dirty; 183 184 // render pass cache 185 private GlRenderPass _renderPass; 186 private GlColorAttachment[] _attachments; // TODO: static array 187 private uint _subpass; 188 189 // pipeline cache 190 private GLenum _primitive; 191 private VertexInputBinding[] _inputBindings; 192 private VertexInputAttrib[] _inputAttribs; 193 194 // index buffer cache 195 private size_t _indexOffset; 196 private GLenum _indexType; 197 198 // vertex cache 199 private VertexBinding[] _vertexBindings; 200 201 this(CommandPool pool, GLuint fbo, CommandBufferLevel level) { 202 _pool = pool; 203 _fbo = fbo; 204 _level = level; 205 } 206 207 override @property CommandPool pool() { 208 return _pool; 209 } 210 211 override @property CommandBufferLevel level() const { 212 return _level; 213 } 214 215 override void reset() { 216 _cmds.length = 0; 217 _dirty = Dirty.none; 218 _vertexBindings.length = 0; 219 } 220 221 override void begin(in CommandBufferUsage usage) { 222 _usage = usage; 223 } 224 225 override void end() { 226 } 227 228 override void pipelineBarrier(Trans!PipelineStage stageTrans, 229 BufferMemoryBarrier[] bufMbs, ImageMemoryBarrier[] imgMbs) { 230 gfxGlLog.warning("unimplemented GL command"); 231 } 232 233 override void clearColorImage(ImageBase image, ImageLayout layout, 234 in ClearColorValues clearValues, ImageSubresourceRange[] ranges) { 235 import gfx.gl3.resource : GlImage; 236 237 _cmds ~= new SetupFramebufferCmd(_fbo, cast(GlImage) image); 238 _cmds ~= new ClearColorCmd(clearValues); 239 } 240 241 override void clearDepthStencilImage(ImageBase image, ImageLayout layout, 242 in ClearDepthStencilValues clearValues, ImageSubresourceRange[] ranges) { 243 import gfx.gl3.resource : GlImage; 244 245 _cmds ~= new SetupFramebufferCmd(_fbo, cast(GlImage) image); 246 _cmds ~= new ClearDepthStencilCmd(clearValues, true, true); 247 } 248 249 override void fillBuffer(Buffer dst, in size_t offset, in size_t size, uint value) { 250 gfxGlLog.warning("unimplemented GL command"); 251 } 252 253 override void updateBuffer(Buffer dst, in size_t offset, in uint[] data) { 254 gfxGlLog.warning("unimplemented GL command"); 255 } 256 257 override void copyBuffer(Trans!Buffer buffers, in CopyRegion[] regions) { 258 import gfx.gl3.resource : GlBuffer; 259 260 foreach (r; regions) { 261 _cmds ~= new CopyBufToBufCmd(cast(GlBuffer) buffers.from, cast(GlBuffer) buffers.to, r); 262 } 263 } 264 265 override void copyBufferToImage(Buffer srcBuffer, ImageBase dstImage, 266 in ImageLayout dstLayout, in BufferImageCopy[] regions) { 267 import gfx.gl3.resource : GlBuffer, GlImage; 268 269 foreach (r; regions) { 270 _cmds ~= new CopyBufToImgCmd(cast(GlBuffer) srcBuffer, cast(GlImage) dstImage, r); 271 } 272 } 273 274 override void setViewport(in uint firstViewport, in Viewport[] viewports) { 275 _cmds ~= new SetViewportsCmd(firstViewport, viewports); 276 } 277 278 override void setScissor(in uint firstScissor, in Rect[] scissors) { 279 _cmds ~= new SetScissorsCmd(firstScissor, scissors); 280 } 281 282 override void setDepthBounds(in float minDepth, in float maxDepth) { 283 gfxGlLog.warning("unimplemented GL command"); 284 } 285 286 void setLineWidth(in float lineWidth) { 287 _cmds ~= new SetLineWidthCmd(lineWidth); 288 } 289 290 override void setDepthBias(in float constFactor, in float clamp, in float slopeFactor) { 291 _cmds ~= new SetDepthBiasCmd(constFactor, clamp, slopeFactor); 292 } 293 294 override void setStencilCompareMask(in StencilFace faceMask, in uint compareMask) { 295 gfxGlLog.warning("unimplemented GL command"); 296 } 297 298 override void setStencilWriteMask(in StencilFace faceMask, in uint writeMask) { 299 gfxGlLog.warning("unimplemented GL command"); 300 } 301 302 override void setStencilReference(in StencilFace faceMask, in uint reference) { 303 gfxGlLog.warning("unimplemented GL command"); 304 } 305 306 override void setBlendConstants(in float[4] blendConstants) { 307 _cmds ~= new SetBlendConstantsCmd(blendConstants); 308 } 309 310 override void beginRenderPass(RenderPass rp, Framebuffer fb, in Rect area, 311 in ClearValues[] clearValues) { 312 import gfx.gl3.pipeline : GlFramebuffer; 313 import gfx.graal.pipeline : ColorBlendAttachment; 314 import std.algorithm : map; 315 import std.array : array; 316 317 _renderPass = cast(GlRenderPass) rp; 318 _attachments = _renderPass.attachments.map!(ad => GlColorAttachment(false, 319 ad, ColorBlendAttachment.init)).array; 320 setActiveSubpass(0); 321 322 const glFb = cast(GlFramebuffer) fb; 323 _cmds ~= new BindFramebufferCmd(glFb.name); 324 foreach (cv; clearValues) { 325 if (cv.type == ClearValues.Type.color) { 326 _cmds ~= new ClearColorCmd(cv.values.color); 327 } 328 if (cv.type == ClearValues.Type.depthStencil) { 329 _cmds ~= new ClearDepthStencilCmd(cv.values.depthStencil, true, true); 330 } 331 } 332 } 333 334 override void beginWithinRenderPass(in CommandBufferUsage usage, 335 RenderPass rp, Framebuffer fb, uint subpass) { 336 import gfx.core.util : unsafeCast; 337 import gfx.gl3.pipeline : GlFramebuffer; 338 import gfx.graal.pipeline : ColorBlendAttachment; 339 import std.algorithm : map; 340 import std.array : array; 341 342 assert(_level == CommandBufferLevel.secondary); 343 _usage = usage; 344 _renderPass = cast(GlRenderPass) rp; 345 _attachments = _renderPass.attachments.map!(ad => GlColorAttachment(false, 346 ad, ColorBlendAttachment.init)).array; 347 setActiveSubpass(0); 348 349 const glFb = cast(GlFramebuffer) fb; 350 _cmds ~= new BindFramebufferCmd(glFb.name); 351 _subpass = subpass; 352 } 353 354 override void nextSubpass() { 355 assert(_level == CommandBufferLevel.primary); 356 setActiveSubpass(_subpass + 1); 357 } 358 359 override void endRenderPass() { 360 assert(_level == CommandBufferLevel.primary); 361 } 362 363 override void bindPipeline(Pipeline pipeline) { 364 auto glPipeline = cast(GlPipeline) pipeline; 365 366 _cmds ~= new BindProgramCmd(glPipeline.prog); 367 _cmds ~= new SetViewportConfigsCmd(glPipeline.info.viewports); 368 _cmds ~= new SetRasterizerCmd(glPipeline.info.rasterizer); 369 _cmds ~= new SetDepthInfoCmd(glPipeline.info.depthInfo); 370 _cmds ~= new SetStencilInfoCmd(glPipeline.info.stencilInfo); 371 372 uint curAttach = 0; 373 foreach (ref a; _attachments) { 374 if (a.enabled) { 375 a.attachment = glPipeline.info.blendInfo.attachments[curAttach++]; 376 } 377 } 378 assert(curAttach == glPipeline.info.blendInfo.attachments.length); 379 _cmds ~= new BindBlendSlotsCmd(_attachments.dup); 380 381 _primitive = toGl(glPipeline.info.assembly.primitive); 382 _inputBindings = glPipeline.info.inputBindings; 383 _inputAttribs = glPipeline.info.inputAttribs; 384 385 dirty(Dirty.vertexBindings | Dirty.pipeline); 386 } 387 388 override void bindVertexBuffers(uint firstBinding, VertexBinding[] bindings) { 389 const minLen = firstBinding + bindings.length; 390 if (_vertexBindings.length < minLen) 391 _vertexBindings.length = minLen; 392 _vertexBindings[firstBinding .. firstBinding + bindings.length] = bindings; 393 dirty(Dirty.vertexBindings); 394 } 395 396 override void bindIndexBuffer(Buffer indexBuf, size_t offset, IndexType type) { 397 import gfx.gl3.resource : GlBuffer; 398 399 auto glBuf = cast(GlBuffer) indexBuf; 400 _cmds ~= new BindIndexBufCmd(glBuf.name); 401 _indexOffset = offset; 402 _indexType = toGl(type); 403 } 404 405 override void bindDescriptorSets(PipelineBindPoint bindPoint, PipelineLayout layout, 406 uint firstSet, DescriptorSet[] sets, in size_t[] dynamicOffsets) { 407 import gfx.gl3.pipeline : GlDescriptorSet; 408 import gfx.graal.pipeline : DescriptorType; 409 410 size_t dynInd = 0; 411 412 foreach (si, ds; sets) { 413 auto glSet = cast(GlDescriptorSet) ds; 414 415 foreach (bi, b; glSet.bindings) { 416 417 foreach (di, d; b.descriptors) { 418 419 switch (b.layout.descriptorType) { 420 case DescriptorType.uniformBuffer: 421 _cmds ~= new BindUniformBufferCmd(b.layout.binding, d.buffer, 0); 422 break; 423 case DescriptorType.uniformBufferDynamic: 424 _cmds ~= new BindUniformBufferCmd(b.layout.binding, 425 d.buffer, dynamicOffsets[dynInd++]); 426 break; 427 case DescriptorType.combinedImageSampler: 428 _cmds ~= new BindSamplerImageCmd(b.layout.binding, d.imageSampler); 429 break; 430 default: 431 gfxGlLog.warning("unhandled descriptor set"); 432 break; 433 } 434 } 435 } 436 } 437 } 438 439 override void pushConstants(PipelineLayout layout, ShaderStage stages, 440 size_t offset, size_t size, const(void)* data) { 441 gfxGlLog.warning("unimplemented GL command"); 442 } 443 444 override void draw(uint vertexCount, uint instanceCount, uint firstVertex, uint firstInstance) { 445 ensureBindings(); 446 _cmds ~= new DrawCmd(_primitive, cast(GLint) firstVertex, cast(GLsizei) vertexCount, 447 cast(GLsizei) instanceCount, cast(GLuint) firstInstance); 448 } 449 450 override void drawIndexed(uint indexCount, uint instanceCount, 451 uint firstVertex, int vertexOffset, uint firstInstance) { 452 ensureBindings(); 453 454 const factor = _indexType == GL_UNSIGNED_SHORT ? 2 : 4; 455 const offset = factor * firstVertex + _indexOffset; 456 457 _cmds ~= new DrawIndexedCmd(_primitive, cast(GLsizei) indexCount, _indexType, offset, 458 cast(GLint) vertexOffset, cast(GLsizei) instanceCount, cast(GLuint) firstInstance); 459 } 460 461 override void execute(SecondaryCommandBuffer[] buffers) { 462 assert(_level == CommandBufferLevel.primary); 463 assert(false, "not implemented"); 464 } 465 466 private void dirty(Dirty flag) { 467 _dirty |= flag; 468 } 469 470 private void clean(Dirty flag) { 471 _dirty &= ~flag; 472 } 473 474 private bool allDirty(Dirty flags) { 475 return (_dirty & flags) == flags; 476 } 477 478 private bool someDirty(Dirty flags) { 479 return (_dirty & flags) != Dirty.none; 480 } 481 482 private void ensureBindings() { 483 if (someDirty(Dirty.vertexBindings)) { 484 bindAttribs(); 485 clean(Dirty.vertexBindings); 486 } 487 } 488 489 private void bindAttribs() { 490 assert(_vertexBindings.length == _inputBindings.length); 491 assert(someDirty(Dirty.vertexBindings)); 492 493 import gfx.gl3.conv : glVertexFormat, vertexFormatSupported; 494 import gfx.gl3.resource : GlBuffer; 495 import gfx.graal.format : formatDesc; 496 import std.algorithm : filter; 497 498 foreach (bi, vb; _vertexBindings) { 499 const bindingInfo = _inputBindings[bi]; 500 501 GlVertexAttrib[] attribs; 502 503 foreach (ai; _inputAttribs.filter!(ia => ia.binding == bi)) { 504 const f = ai.format; 505 assert(vertexFormatSupported(f)); 506 507 attribs ~= GlVertexAttrib(ai.location, glVertexFormat(f), vb.offset + ai.offset); 508 } 509 510 auto buf = cast(GlBuffer) vb.buffer; 511 _cmds ~= new BindVertexBufCmd(buf.name, cast(GLsizei) bindingInfo.stride, attribs); 512 } 513 } 514 515 void setActiveSubpass(uint subpass) { 516 assert(_renderPass); 517 assert(_attachments.length == _renderPass.attachments.length); 518 const sp = _renderPass.subpasses[subpass]; 519 foreach (ref a; _attachments) { 520 a.enabled = false; 521 } 522 foreach (ar; sp.colors) { 523 _attachments[ar.attachment].enabled = true; 524 } 525 _subpass = subpass; 526 } 527 528 } 529 530 private: 531 532 struct GlState { 533 import gfx.graal.pipeline : DepthInfo, Rasterizer, StencilInfo, ViewportConfig; 534 import gfx.graal.types : Rect, Viewport; 535 536 GLuint prog; 537 const(ViewportConfig)[] vcs; 538 const(Viewport)[] viewports; 539 const(Rect)[] scissors; 540 Rasterizer rasterizer; 541 DepthInfo depthInfo; 542 StencilInfo stencilInfo; 543 } 544 545 struct GlColorAttachment { 546 import gfx.graal.pipeline : ColorBlendAttachment; 547 import gfx.graal.renderpass : AttachmentDescription; 548 549 bool enabled; 550 AttachmentDescription desc; 551 ColorBlendAttachment attachment; 552 } 553 554 abstract class GlCommand { 555 abstract void execute(GlQueue queue, Gl gl); 556 } 557 558 string allFieldsCtor(T)() { 559 import std.array : join; 560 import std.traits : FieldNameTuple, Fields; 561 562 alias names = FieldNameTuple!T; 563 alias types = Fields!T; 564 565 string[] fields; 566 static foreach (i, n; names) { 567 fields ~= types[i].stringof ~ " " ~ n; 568 } 569 string code = "this(" ~ fields.join(", ") ~ ") {\n"; 570 static foreach (n; names) { 571 code ~= " this." ~ n ~ " = " ~ n ~ ";\n"; 572 } 573 return code ~ "}"; 574 } 575 576 final class CopyBufToBufCmd : GlCommand { 577 import gfx.gl3.resource : GlBuffer; 578 579 GlBuffer src; 580 GlBuffer dst; 581 CopyRegion region; 582 583 mixin(allFieldsCtor!(typeof(this))); 584 585 override void execute(GlQueue queue, Gl gl) { 586 gl.BindBuffer(GL_COPY_READ_BUFFER, src.name); 587 gl.BindBuffer(GL_COPY_WRITE_BUFFER, dst.name); 588 gl.CopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 589 cast(GLintptr) region.offset.from, 590 cast(GLintptr) region.offset.to, cast(GLsizeiptr) region.size); 591 gl.BindBuffer(GL_COPY_READ_BUFFER, 0); 592 gl.BindBuffer(GL_COPY_WRITE_BUFFER, 0); 593 594 import gfx.gl3.error : glCheck; 595 596 glCheck(gl, "copy buffer to buffer"); 597 } 598 } 599 600 final class CopyBufToImgCmd : GlCommand { 601 import gfx.gl3.resource : GlBuffer, GlImage; 602 603 GlBuffer buf; 604 GlImage img; 605 BufferImageCopy region; 606 607 mixin(allFieldsCtor!(typeof(this))); 608 609 override void execute(GlQueue queue, Gl gl) { 610 gl.ActiveTexture(GL_TEXTURE0); 611 gl.BindBuffer(GL_PIXEL_UNPACK_BUFFER, buf.name); 612 gl.BindTexture(img.texTarget, img.name); 613 img.texSubImage(region); 614 gl.BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); 615 616 import gfx.gl3.error : glCheck; 617 618 glCheck(gl, "copy buffer to image"); 619 } 620 } 621 622 final class BindFramebufferCmd : GlCommand { 623 GLuint fbo; 624 this(GLuint fbo) { 625 this.fbo = fbo; 626 } 627 628 override void execute(GlQueue queue, Gl gl) { 629 gl.BindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); 630 } 631 } 632 633 final class SetupFramebufferCmd : GlCommand { 634 import gfx.gl3.resource : GlImage, GlImgType; 635 636 GLuint fbo; 637 GlImage img; 638 639 this(GLuint fbo, GlImage img) { 640 this.fbo = fbo; 641 this.img = img; 642 } 643 644 override void execute(GlQueue queue, Gl gl) { 645 gl.BindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); 646 final switch (img.glType) { 647 case GlImgType.renderBuf: 648 gl.FramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, 649 GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, img.name); 650 break; 651 case GlImgType.tex: 652 gl.FramebufferTexture2D(GL_DRAW_FRAMEBUFFER, 653 GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, img.name, 0); 654 break; 655 } 656 const GLenum drawBuf = GL_COLOR_ATTACHMENT0; 657 gl.DrawBuffers(1, &drawBuf); 658 } 659 } 660 661 final class SetViewportConfigsCmd : GlCommand { 662 import gfx.graal.pipeline : ViewportConfig; 663 664 ViewportConfig[] viewports; 665 this(ViewportConfig[] viewports) { 666 this.viewports = viewports; 667 } 668 669 override void execute(GlQueue queue, Gl gl) { 670 671 if (queue.state.vcs == viewports) 672 return; 673 674 if (viewports.length > 1 && !queue.info.viewportArray) { 675 gfxGlLog.error("ARB_viewport_array not supported"); 676 viewports = viewports[0 .. 1]; 677 } 678 679 if (viewports.length > 1) { 680 foreach (i, vc; viewports) { 681 const vp = vc.viewport; 682 gl.ViewportIndexedf(cast(GLuint) i, vp.x, vp.y, vp.width, vp.height); 683 gl.DepthRangeIndexed(cast(GLuint) i, vp.minDepth, vp.maxDepth); 684 685 const sc = vc.scissors; 686 gl.ScissorIndexed(cast(GLuint) i, cast(GLint) sc.x, 687 cast(GLint) sc.y, cast(GLsizei) sc.width, cast(GLsizei) sc.height); 688 } 689 } else if (viewports.length == 1) { 690 const vp = viewports[0].viewport; 691 gl.Viewport(cast(GLint) vp.x, cast(GLint) vp.y, 692 cast(GLsizei) vp.width, cast(GLsizei) vp.height); 693 gl.DepthRangef(vp.minDepth, vp.maxDepth); 694 695 const sc = viewports[0].scissors; 696 gl.Scissor(cast(GLint) sc.x, cast(GLint) sc.y, cast(GLsizei) sc.width, 697 cast(GLsizei) sc.height); 698 } 699 700 queue.state.vcs = viewports; 701 } 702 } 703 704 final class SetViewportsCmd : GlCommand { 705 import gfx.graal.types : Viewport; 706 707 uint firstViewport; 708 const(Viewport)[] viewports; 709 710 this(in uint firstViewport, const(Viewport)[] viewports) { 711 this.firstViewport = firstViewport; 712 this.viewports = viewports; 713 } 714 715 override void execute(GlQueue queue, Gl gl) { 716 717 if (queue.state.viewports == viewports) 718 return; 719 720 bool useArray = viewports.length > 1 || firstViewport > 0; 721 722 if (useArray && !queue.info.viewportArray) { 723 gfxGlLog.error("ARB_viewport_array not supported"); 724 viewports = viewports[0 .. 1]; 725 firstViewport = 0; 726 useArray = false; 727 } 728 729 if (useArray) { 730 foreach (i, vp; viewports) { 731 gl.ViewportIndexedf(cast(GLuint)(i + firstViewport), vp.x, vp.y, 732 vp.width, vp.height); 733 gl.DepthRangeIndexed(cast(GLuint)(i + firstViewport), vp.minDepth, vp.maxDepth); 734 } 735 } else if (viewports.length == 1) { 736 const vp = viewports[0]; 737 gl.Viewport(cast(GLint) vp.x, cast(GLint) vp.y, 738 cast(GLsizei) vp.width, cast(GLsizei) vp.height); 739 gl.DepthRangef(vp.minDepth, vp.maxDepth); 740 } 741 742 queue.state.viewports = viewports; 743 } 744 } 745 746 final class SetScissorsCmd : GlCommand { 747 import gfx.graal.types : Rect; 748 749 uint firstScissor; 750 const(Rect)[] scissors; 751 752 this(in uint firstScissor, const(Rect)[] scissors) { 753 this.firstScissor = firstScissor; 754 this.scissors = scissors; 755 } 756 757 override void execute(GlQueue queue, Gl gl) { 758 import std.algorithm : equal, map; 759 760 if (queue.state.scissors == scissors) 761 return; 762 763 bool useArray = scissors.length > 1 || firstScissor > 0; 764 if (useArray && !queue.info.viewportArray) { 765 gfxGlLog.error("ARB_viewport_array not supported"); 766 scissors = scissors[0 .. 1]; 767 firstScissor = 0; 768 useArray = false; 769 } 770 771 if (useArray) { 772 foreach (i, sc; scissors) { 773 gl.ScissorIndexed(cast(GLuint)(i + firstScissor), 774 cast(GLint) sc.x, cast(GLint) sc.y, 775 cast(GLsizei) sc.width, cast(GLsizei) sc.height); 776 } 777 } else if (scissors.length == 1) { 778 const sc = scissors[0]; 779 gl.Scissor(cast(GLint) sc.x, cast(GLint) sc.y, cast(GLsizei) sc.width, 780 cast(GLsizei) sc.height); 781 } 782 783 queue.state.scissors = scissors; 784 } 785 } 786 787 final class SetLineWidthCmd : GlCommand { 788 float lineWidth; 789 this(in float lineWidth) { 790 this.lineWidth = lineWidth; 791 } 792 793 override void execute(GlQueue queue, Gl gl) { 794 gl.LineWidth(this.lineWidth); 795 } 796 } 797 798 final class SetDepthBiasCmd : GlCommand { 799 float constFactor; 800 float clamp; 801 float slopeFactor; 802 803 mixin(allFieldsCtor!(typeof(this))); 804 805 override void execute(GlQueue queue, Gl gl) { 806 if (queue.info.polygonOffsetClamp) { 807 gl.PolygonOffsetClamp(slopeFactor, constFactor, clamp); 808 } else { 809 gl.PolygonOffset(slopeFactor, constFactor); 810 } 811 } 812 } 813 814 final class SetBlendConstantsCmd : GlCommand { 815 float[4] constants; 816 817 mixin(allFieldsCtor!(typeof(this))); 818 819 override void execute(GlQueue queue, Gl gl) { 820 gl.BlendColor(constants[0], constants[1], constants[2], constants[3]); 821 } 822 } 823 824 final class ClearColorCmd : GlCommand { 825 826 ClearColorValues values; 827 828 this(ClearColorValues values) { 829 this.values = values; 830 } 831 832 override void execute(GlQueue queue, Gl gl) { 833 gl.ColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); 834 final switch (values.type) { 835 case ClearColorValues.Type.f32: 836 gl.ClearBufferfv(GL_COLOR, 0, &values.values.f32[0]); 837 break; 838 case ClearColorValues.Type.i32: 839 gl.ClearBufferiv(GL_COLOR, 0, &values.values.i32[0]); 840 break; 841 case ClearColorValues.Type.u32: 842 gl.ClearBufferuiv(GL_COLOR, 0, &values.values.u32[0]); 843 break; 844 } 845 } 846 } 847 848 final class ClearDepthStencilCmd : GlCommand { 849 850 ClearDepthStencilValues values; 851 bool depth; 852 bool stencil; 853 854 this(ClearDepthStencilValues values, bool depth, bool stencil) { 855 this.values = values; 856 this.depth = depth; 857 this.stencil = stencil; 858 } 859 860 override void execute(GlQueue queue, Gl gl) { 861 if (depth) { 862 gl.DepthMask(GL_TRUE); 863 gl.ClearBufferfv(GL_DEPTH, 0, &values.depth); 864 } 865 if (stencil) { 866 const val = cast(GLint) values.stencil; 867 gl.StencilMask(GLuint.max); 868 gl.ClearBufferiv(GL_STENCIL, 0, &val); 869 } 870 } 871 } 872 873 final class BindProgramCmd : GlCommand { 874 GLuint prog; 875 this(GLuint prog) { 876 this.prog = prog; 877 } 878 879 override void execute(GlQueue queue, Gl gl) { 880 if (queue.state.prog == prog) 881 return; 882 gl.UseProgram(prog); 883 queue.state.prog = prog; 884 } 885 } 886 887 final class SetRasterizerCmd : GlCommand { 888 import gfx.graal.pipeline : Rasterizer; 889 890 Rasterizer rasterizer; 891 this(Rasterizer rasterizer) { 892 this.rasterizer = rasterizer; 893 } 894 895 override void execute(GlQueue queue, Gl gl) { 896 import gfx.gl3.conv : toGl; 897 import gfx.graal.pipeline : Cull, PolygonMode; 898 899 if (queue.state.rasterizer == rasterizer) 900 return; 901 902 void polygonBias(GLenum polygonMode, GLenum depthBias) { 903 gl.PolygonMode(GL_FRONT_AND_BACK, polygonMode); 904 if (rasterizer.depthBias.isSome) { 905 const db = rasterizer.depthBias.get; 906 gl.Enable(depthBias); 907 if (queue.info.polygonOffsetClamp) { 908 gl.PolygonOffsetClamp(db.slopeFactor, db.constantFactor, db.clamp); 909 } else { 910 gl.PolygonOffset(db.slopeFactor, db.constantFactor); 911 } 912 } else { 913 gl.Disable(depthBias); 914 } 915 } 916 917 final switch (rasterizer.mode) { 918 case PolygonMode.point: 919 polygonBias(GL_POINT, GL_POLYGON_OFFSET_POINT); 920 break; 921 case PolygonMode.line: 922 polygonBias(GL_LINE, GL_POLYGON_OFFSET_LINE); 923 gl.LineWidth(rasterizer.lineWidth); 924 break; 925 case PolygonMode.fill: 926 polygonBias(GL_FILL, GL_POLYGON_OFFSET_FILL); 927 break; 928 } 929 930 if (rasterizer.cull == Cull.none) { 931 gl.Disable(GL_CULL_FACE); 932 } else { 933 gl.Enable(GL_CULL_FACE); 934 switch (rasterizer.cull) { 935 case Cull.back: 936 gl.CullFace(GL_BACK); 937 break; 938 case Cull.front: 939 gl.CullFace(GL_FRONT); 940 break; 941 case Cull.frontAndBack: 942 gl.CullFace(GL_FRONT_AND_BACK); 943 break; 944 default: 945 break; 946 } 947 gl.FrontFace(toGl(rasterizer.front)); 948 } 949 950 if (rasterizer.depthClamp) { 951 gl.Enable(GL_DEPTH_CLAMP); 952 } else { 953 gl.Disable(GL_DEPTH_CLAMP); 954 } 955 956 queue.state.rasterizer = rasterizer; 957 } 958 } 959 960 final class SetDepthInfoCmd : GlCommand { 961 import gfx.graal.pipeline : DepthInfo; 962 963 DepthInfo info; 964 this(DepthInfo info) { 965 this.info = info; 966 } 967 968 override void execute(GlQueue queue, Gl gl) { 969 import gfx.gl3.conv : toGl; 970 971 if (queue.state.depthInfo == info) 972 return; 973 974 if (info.enabled) { 975 gl.Enable(GL_DEPTH_TEST); 976 gl.DepthFunc(toGl(info.compareOp)); 977 gl.DepthMask(cast(GLboolean) info.write); 978 if (info.boundsTest) { 979 gfxGlLog.warning("no support for depth bounds test"); 980 } 981 } else { 982 gl.Disable(GL_DEPTH_TEST); 983 } 984 985 queue.state.depthInfo = info; 986 } 987 } 988 989 final class SetStencilInfoCmd : GlCommand { 990 import gfx.graal.pipeline : StencilInfo; 991 992 StencilInfo info; 993 this(StencilInfo info) { 994 this.info = info; 995 } 996 997 override void execute(GlQueue queue, Gl gl) { 998 import gfx.gl3.conv : toGl; 999 import gfx.graal.pipeline : StencilOpState; 1000 1001 if (queue.state.stencilInfo == info) 1002 return; 1003 1004 if (info.enabled) { 1005 gl.Enable(GL_STENCIL_TEST); 1006 void bindFace(GLenum face, StencilOpState state) { 1007 gl.StencilOpSeparate(face, toGl(state.failOp), 1008 toGl(state.depthFailOp), toGl(state.passOp)); 1009 gl.StencilFuncSeparate(face, toGl(state.compareOp), 1010 state.refMask, state.compareMask); 1011 gl.StencilMaskSeparate(face, state.writeMask); 1012 } 1013 1014 bindFace(GL_FRONT, info.front); 1015 bindFace(GL_BACK, info.back); 1016 } else { 1017 gl.Disable(GL_STENCIL_TEST); 1018 } 1019 } 1020 } 1021 1022 struct GlVertexAttrib { 1023 import gfx.gl3.conv : GlVertexFormat; 1024 1025 GLuint index; 1026 GlVertexFormat format; 1027 size_t offset; 1028 } 1029 1030 final class BindVertexBufCmd : GlCommand { 1031 GLuint buffer; 1032 GLsizei stride; 1033 GlVertexAttrib[] attribs; 1034 1035 this(GLuint buffer, GLsizei stride, GlVertexAttrib[] attribs) { 1036 this.buffer = buffer; 1037 this.stride = stride; 1038 this.attribs = attribs; 1039 } 1040 1041 override void execute(GlQueue queue, Gl gl) { 1042 import gfx.gl3.conv : VAOAttribFun; 1043 1044 gl.BindBuffer(GL_ARRAY_BUFFER, buffer); 1045 foreach (at; attribs) { 1046 final switch (at.format.fun) { 1047 case VAOAttribFun.f: 1048 gl.VertexAttribPointer(at.index, at.format.size, 1049 at.format.type, at.format.normalized, stride, 1050 cast(const(void*)) at.offset); 1051 break; 1052 case VAOAttribFun.i: 1053 gl.VertexAttribIPointer(at.index, 1054 at.format.size, at.format.type, stride, cast(const(void*)) at.offset); 1055 break; 1056 case VAOAttribFun.d: 1057 gl.VertexAttribLPointer(at.index, 1058 at.format.size, at.format.type, stride, cast(const(void*)) at.offset); 1059 break; 1060 } 1061 gl.EnableVertexAttribArray(at.index); 1062 } 1063 gl.BindBuffer(GL_ARRAY_BUFFER, 0); 1064 } 1065 } 1066 1067 final class BindIndexBufCmd : GlCommand { 1068 GLuint buf; 1069 1070 this(GLuint buf) { 1071 this.buf = buf; 1072 } 1073 1074 override void execute(GlQueue queue, Gl gl) { 1075 gl.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf); 1076 } 1077 } 1078 1079 final class BindUniformBufferCmd : GlCommand { 1080 import gfx.gl3.resource : GlBuffer; 1081 import gfx.graal.pipeline : BufferDescriptor; 1082 1083 GLuint binding; 1084 BufferDescriptor bufferDesc; 1085 size_t dynamicOffset; 1086 1087 this(GLuint binding, BufferDescriptor bd, in size_t dynOffset) { 1088 this.binding = binding; 1089 this.bufferDesc = bd; 1090 this.dynamicOffset = dynOffset; 1091 } 1092 1093 override void execute(GlQueue queue, Gl gl) { 1094 auto glBuf = cast(GlBuffer) bufferDesc.buffer; 1095 1096 gl.BindBufferRange(GL_UNIFORM_BUFFER, binding, glBuf.name, 1097 cast(GLintptr)(bufferDesc.offset + dynamicOffset), 1098 cast(GLintptr)bufferDesc.size); 1099 } 1100 } 1101 1102 final class BindSamplerImageCmd : GlCommand { 1103 import gfx.graal.pipeline : ImageSamplerDescriptor; 1104 1105 GLuint binding; 1106 ImageSamplerDescriptor sid; 1107 1108 mixin(allFieldsCtor!(typeof(this))); 1109 1110 override void execute(GlQueue queue, Gl gl) { 1111 import gfx.gl3.conv : toGl; 1112 import gfx.gl3.error : glCheck; 1113 import gfx.gl3.resource : GlImageView, GlSampler; 1114 1115 auto view = cast(GlImageView) sid.view; 1116 auto sampler = cast(GlSampler) sid.sampler; 1117 1118 gl.ActiveTexture(GL_TEXTURE0 + binding); 1119 gl.BindTexture(view.target, view.name); 1120 glCheck(gl, "bind texture"); 1121 1122 sampler.bind(view.target, binding); 1123 glCheck(gl, "bind sampler"); 1124 1125 const swizzle = toGl(view.swizzle); 1126 gl.TexParameteriv(view.target, GL_TEXTURE_SWIZZLE_RGBA, &swizzle[0]); 1127 } 1128 } 1129 1130 final class BindBlendSlotsCmd : GlCommand { 1131 import gfx.graal.pipeline : ColorMask; 1132 1133 GlColorAttachment[] attachments; 1134 1135 this(GlColorAttachment[] attachments) { 1136 this.attachments = attachments; 1137 } 1138 1139 override void execute(GlQueue queue, Gl gl) { 1140 import gfx.gl3.conv : toGl; 1141 1142 foreach (const ind, a; attachments) { 1143 const slot = cast(GLuint) ind; 1144 if (a.enabled) { 1145 // TODO logicOp 1146 if (a.attachment.enabled) { 1147 // TODO 1148 gl.Enablei(GL_BLEND, slot); 1149 gl.BlendEquationSeparatei(slot, toGl(a.attachment.colorBlend.op), 1150 toGl(a.attachment.alphaBlend.op),); 1151 gl.BlendFuncSeparatei(slot, toGl(a.attachment.colorBlend.factor.from), 1152 toGl(a.attachment.colorBlend.factor.to), toGl(a.attachment.alphaBlend.factor.from), 1153 toGl(a.attachment.alphaBlend.factor.to),); 1154 } else { 1155 gl.Disablei(GL_BLEND, slot); 1156 } 1157 import std.typecons : BitFlags, Yes; 1158 1159 BitFlags!(ColorMask, Yes.unsafe) cm = a.attachment.colorMask; 1160 gl.ColorMaski(slot, (cm & ColorMask.r) ? GL_TRUE : GL_FALSE, 1161 (cm & ColorMask.g) ? GL_TRUE : GL_FALSE, (cm & ColorMask.b) 1162 ? GL_TRUE : GL_FALSE, (cm & ColorMask.a) ? GL_TRUE : GL_FALSE); 1163 } else { 1164 gl.ColorMaski(slot, GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); 1165 } 1166 } 1167 } 1168 } 1169 1170 final class DrawCmd : GlCommand { 1171 GLenum primitive; 1172 GLint first; 1173 GLsizei count; 1174 GLsizei instanceCount; 1175 GLuint baseInstance; 1176 1177 this(GLenum primitive, GLint first, GLsizei count, GLsizei instanceCount, GLuint baseInstance) { 1178 this.primitive = primitive; 1179 this.first = first; 1180 this.count = count; 1181 this.instanceCount = instanceCount; 1182 this.baseInstance = baseInstance; 1183 } 1184 1185 override void execute(GlQueue queue, Gl gl) { 1186 if (baseInstance != 0 && !queue.info.baseInstance) { 1187 gfxGlLog.error("No support for ARB_base_instance"); 1188 return; 1189 } 1190 if (instanceCount <= 1) { 1191 gl.DrawArrays(primitive, first, count); 1192 } else if (instanceCount > 1 && baseInstance == 0) { 1193 gl.DrawArraysInstanced(primitive, first, count, instanceCount); 1194 } else if (instanceCount > 1 && baseInstance != 0) { 1195 gl.DrawArraysInstancedBaseInstance(primitive, first, count, 1196 instanceCount, baseInstance); 1197 } 1198 import gfx.gl3.error : glCheck; 1199 1200 glCheck(gl, "draw"); 1201 } 1202 } 1203 1204 final class DrawIndexedCmd : GlCommand { 1205 GLenum primitive; 1206 GLsizei count; 1207 GLenum type; 1208 size_t indexBufOffset; 1209 GLint baseVertex; 1210 GLsizei instanceCount; 1211 GLuint baseInstance; 1212 1213 this(GLenum primitive, GLsizei count, GLenum type, size_t indexBufOffset, 1214 GLint baseVertex, GLsizei instanceCount, GLuint baseInstance) { 1215 this.primitive = primitive; 1216 this.count = count; 1217 this.type = type; 1218 this.indexBufOffset = indexBufOffset; 1219 this.baseVertex = baseVertex; 1220 this.instanceCount = instanceCount; 1221 this.baseInstance = baseInstance; 1222 } 1223 1224 override void execute(GlQueue queue, Gl gl) { 1225 const offset = cast(const(void*)) indexBufOffset; 1226 1227 if (baseVertex != 0 && !queue.info.drawElementsBaseVertex) { 1228 gfxGlLog.error("No support for ARB_draw_elements_base_vertex"); 1229 return; 1230 } 1231 if (baseInstance != 0 && !queue.info.baseInstance) { 1232 gfxGlLog.error("No support for ARB_base_instance"); 1233 return; 1234 } 1235 1236 if (instanceCount <= 1 && baseVertex == 0) { 1237 gl.DrawElements(primitive, count, type, offset); 1238 } else if (instanceCount <= 1 && baseVertex != 0) { 1239 gl.DrawElementsBaseVertex(primitive, count, type, offset, baseVertex); 1240 } else if (instanceCount > 1 && baseInstance == 0 && baseVertex == 0) { 1241 gl.DrawElementsInstanced(primitive, count, type, offset, instanceCount); 1242 } else if (instanceCount > 1 && baseInstance == 0 && baseVertex != 0) { 1243 gl.DrawElementsInstancedBaseVertex(primitive, count, type, offset, 1244 instanceCount, baseVertex); 1245 } else if (instanceCount > 1 && baseInstance != 0 && baseVertex == 0) { 1246 gl.DrawElementsInstancedBaseInstance(primitive, count, type, 1247 offset, instanceCount, baseInstance); 1248 } else { 1249 gl.DrawElementsInstancedBaseVertexBaseInstance(primitive, count, 1250 type, offset, instanceCount, baseVertex, baseInstance); 1251 } 1252 import gfx.gl3.error : glCheck; 1253 1254 glCheck(gl, "draw indexed"); 1255 } 1256 }