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