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