1 module gfx.gl3.resource; 2 3 package: 4 5 import gfx.bindings.opengl.gl : Gl, GLenum, GLuint; 6 import gfx.graal.buffer : Buffer; 7 import gfx.graal.image : Image, ImageView, Sampler, SamplerInfo; 8 import gfx.graal.memory : DeviceMemory; 9 10 final class GlDeviceMemory : DeviceMemory 11 { 12 import gfx.core.rc : atomicRcCode; 13 import gfx.graal.memory : MemProps; 14 15 //mixin(atomicRcCode); 16 private size_t _refCount=0; 17 import std.typecons : Flag, Yes; 18 19 public final shared override @property size_t refCountShared() const 20 { 21 import core.atomic : atomicLoad; 22 return atomicLoad(_refCount); 23 } 24 25 public final shared override shared(typeof(this)) retainShared() 26 { 27 import core.atomic : atomicOp; 28 immutable rc = atomicOp!"+="(_refCount, 1); 29 debug(rc) { 30 import std.experimental.logger : logf; 31 logf("retain %s: %s -> %s", typeof(this).stringof, rc-1, rc); 32 } 33 return this; 34 } 35 36 public final shared override shared(typeof(this)) releaseShared(in Flag!"disposeOnZero" disposeOnZero = Yes.disposeOnZero) 37 { 38 import core.atomic : atomicOp; 39 immutable rc = atomicOp!"-="(_refCount, 1); 40 41 debug(rc) { 42 import std.experimental.logger : logf; 43 logf("release %s: %s -> %s", typeof(this).stringof, rc+1, rc); 44 } 45 if (rc == 0 && disposeOnZero) { 46 debug(rc) { 47 import std.experimental.logger : logf; 48 logf("dispose %s", typeof(this).stringof); 49 } 50 synchronized(this) { 51 // cast shared away 52 import gfx.core.rc : Disposable; 53 auto obj = cast(Disposable)this; 54 obj.dispose(); 55 } 56 return null; 57 } 58 else { 59 return this; 60 } 61 } 62 63 public final shared override shared(typeof(this)) rcLockShared() 64 { 65 import core.atomic : atomicLoad, cas; 66 while (1) { 67 immutable c = atomicLoad(_refCount); 68 69 if (c == 0) { 70 debug(rc) { 71 import std.experimental.logger : logf; 72 logf("rcLock %s: %s", typeof(this).stringof, c); 73 } 74 return null; 75 } 76 if (cas(&_refCount, c, c+1)) { 77 debug(rc) { 78 import std.experimental.logger : logf; 79 logf("rcLock %s: %s", typeof(this).stringof, c+1); 80 } 81 return this; 82 } 83 } 84 } 85 86 private uint _typeIndex; 87 private MemProps _props; 88 private size_t _size; 89 private GlBuffer _buffer; 90 91 92 this (in uint typeIndex, in MemProps props, in size_t size) { 93 _typeIndex = typeIndex; 94 _props = props; 95 _size = size; 96 } 97 98 override void dispose() { 99 } 100 101 override @property uint typeIndex() { 102 return _typeIndex; 103 } 104 override @property MemProps props() { 105 return _props; 106 } 107 override @property size_t size() { 108 return _size; 109 } 110 111 override void* mapRaw(in size_t offset, in size_t size) { 112 import std.exception : enforce; 113 enforce(_buffer, "GL backend does not support mapping without bound buffer"); 114 return _buffer.map(offset, size); 115 } 116 117 override void unmapRaw() { 118 import std.exception : enforce; 119 enforce(_buffer, "GL backend does not support mapping without bound buffer"); 120 _buffer.unmap(); 121 } 122 } 123 124 125 final class GlBuffer : Buffer 126 { 127 import gfx.bindings.opengl.gl; 128 import gfx.core.rc : atomicRcCode, Rc; 129 import gfx.gl3 : GlInfo, GlShare; 130 import gfx.graal.buffer : BufferUsage, BufferView; 131 import gfx.graal.format : Format; 132 import gfx.graal.memory : DeviceMemory, MemoryRequirements; 133 134 mixin(atomicRcCode); 135 136 private GlInfo glInfo; 137 private Gl gl; 138 private BufferUsage _usage; 139 private size_t _size; 140 private GLuint _name; 141 private GLbitfield _accessFlags; 142 private Rc!GlDeviceMemory _mem; 143 144 this(GlShare share, in BufferUsage usage, in size_t size) { 145 import gfx.gl3.conv : toGl; 146 gl = share.gl; 147 glInfo = share.info; 148 _usage = usage; 149 _size = size; 150 gl.GenBuffers(1, &_name); 151 152 import gfx.gl3.error : glCheck; 153 glCheck(gl, "generating buffer"); 154 } 155 156 override void dispose() { 157 gl.DeleteBuffers(1, &_name); 158 _name = 0; 159 _mem.unload(); 160 } 161 162 @property GLuint name() const { 163 return _name; 164 } 165 166 override @property size_t size() { 167 return _size; 168 } 169 override @property BufferUsage usage() { 170 return _usage; 171 } 172 override @property MemoryRequirements memoryRequirements() { 173 import gfx.graal.memory : MemProps; 174 MemoryRequirements mr; 175 mr.alignment = 4; 176 mr.size = _size; 177 mr.memTypeMask = 1; 178 return mr; 179 } 180 181 override void bindMemory(DeviceMemory mem, in size_t offset) { 182 import gfx.gl3.error : glCheck; 183 _mem = cast(GlDeviceMemory)mem; 184 _mem._buffer = this; 185 186 const props = mem.props; 187 188 gl.BindBuffer(GL_ARRAY_BUFFER, _name); 189 190 if (glInfo.bufferStorage) { 191 GLbitfield flags = 0; 192 if (props.hostVisible) flags |= (GL_MAP_READ_BIT | GL_MAP_WRITE_BIT); 193 // if (props.hostCoherent) flags |= GL_MAP_COHERENT_BIT; 194 gl.BufferStorage(GL_ARRAY_BUFFER, cast(GLsizeiptr)_size, null, flags); 195 glCheck(gl, "buffer storage"); 196 } 197 else { 198 const glUsage = GL_STATIC_DRAW; //? 199 gl.BufferData(GL_ARRAY_BUFFER, cast(GLsizeiptr)_size, null, glUsage); 200 glCheck(gl, "buffer data"); 201 } 202 203 204 gl.BindBuffer(GL_ARRAY_BUFFER, 0); 205 } 206 207 override @property DeviceMemory boundMemory() { 208 return _mem.obj; 209 } 210 211 override BufferView createView(Format format, size_t offset, size_t size) { 212 return null; 213 } 214 215 private void* map(in size_t offset, in size_t size) { 216 import gfx.gl3.error : glCheck; 217 const props = _mem.props; 218 219 GLbitfield flags = 0; 220 if (props.hostVisible) flags |= (GL_MAP_READ_BIT | GL_MAP_WRITE_BIT); 221 // if (props.hostCoherent) flags |= GL_MAP_COHERENT_BIT; 222 // else flags |= GL_MAP_FLUSH_EXPLICIT_BIT; 223 224 gl.BindBuffer(GL_ARRAY_BUFFER, _name); 225 auto ptr = gl.MapBufferRange(GL_ARRAY_BUFFER, cast(GLintptr)offset, cast(GLsizeiptr)size, flags); 226 glCheck(gl, "buffer map"); 227 gl.BindBuffer(GL_ARRAY_BUFFER, 0); 228 229 return ptr; 230 } 231 232 private void unmap() { 233 import gfx.gl3.error : glCheck; 234 gl.BindBuffer(GL_ARRAY_BUFFER, _name); 235 gl.UnmapBuffer(GL_ARRAY_BUFFER); 236 glCheck(gl, "buffer unmap"); 237 gl.BindBuffer(GL_ARRAY_BUFFER, 0); 238 } 239 } 240 241 enum GlImgType { 242 tex, renderBuf 243 } 244 245 final class GlImage : Image 246 { 247 import gfx.bindings.opengl.gl : Gl, GLenum, GLuint; 248 import gfx.core.rc : atomicRcCode, Rc; 249 import gfx.graal.cmd : BufferImageCopy; 250 import gfx.graal.format : Format, NumFormat; 251 import gfx.graal.image; 252 import gfx.graal.memory : MemoryRequirements; 253 import gfx.gl3 : GlInfo, GlShare; 254 255 mixin(atomicRcCode); 256 257 private ImageInfo _info; 258 private NumFormat _numFormat; 259 260 private GlImgType _glType; 261 private GLuint _name; 262 private GLenum _glTexTarget; 263 private GLenum _glFormat; 264 private Gl gl; 265 private GlInfo glInfo; 266 private Rc!GlDeviceMemory _mem; 267 268 this(GlShare share, ImageInfo info) 269 { 270 import gfx.graal.format : formatDesc; 271 import std.exception : enforce; 272 _info = info; 273 _numFormat = formatDesc(info.format).numFormat; 274 275 gl = share.gl; 276 glInfo = share.info; 277 278 const notRB = ~(ImageUsage.colorAttachment | ImageUsage.depthStencilAttachment); 279 if ((info.usage & notRB) == ImageUsage.none && info.tiling == ImageTiling.optimal) { 280 _glType = GlImgType.renderBuf; 281 enforce( 282 _info.type == ImageType.d2, 283 "Gfx-GL3: ImageUsage indicates the use of a RenderBuffer, which only supports 2D images" 284 ); 285 gl.GenRenderbuffers(1, &_name); 286 } 287 else { 288 _glType = GlImgType.tex; 289 gl.GenTextures(1, &_name); 290 } 291 292 import gfx.gl3.conv : toGlImgFmt, toGlTexTarget; 293 _glFormat = toGlImgFmt(_info.format); 294 _glTexTarget = toGlTexTarget(_info.type, _info.samples > 1); 295 } 296 297 @property GLuint name() { 298 return _name; 299 } 300 301 @property GLenum texTarget() { 302 return _glTexTarget; 303 } 304 305 @property GlImgType glType() { 306 return _glType; 307 } 308 309 @property NumFormat numFormat() { 310 return _numFormat; 311 } 312 313 override void dispose() { 314 final switch(_glType) { 315 case GlImgType.tex: 316 gl.DeleteTextures(1, &_name); 317 break; 318 case GlImgType.renderBuf: 319 gl.DeleteRenderbuffers(1, &_name); 320 break; 321 } 322 _mem.unload(); 323 } 324 325 override @property ImageInfo info() { 326 return _info; 327 } 328 329 override ImageView createView(ImageType viewType, ImageSubresourceRange isr, Swizzle swizzle) 330 { 331 return new GlImageView(this, viewType, isr, swizzle); 332 } 333 334 override @property MemoryRequirements memoryRequirements() { 335 import gfx.graal.format : formatDesc, totalBits; 336 import gfx.graal.memory : MemProps; 337 338 const fd = formatDesc(_info.format); 339 340 MemoryRequirements mr; 341 mr.alignment = 4; 342 mr.size = _info.dims.width * _info.dims.height * _info.dims.depth * 343 _info.layers * fd.surfaceType.totalBits / 8; 344 mr.memTypeMask = 1; 345 return mr; 346 } 347 348 void bindMemory(DeviceMemory mem, in size_t offset) 349 { 350 import gfx.bindings.opengl.gl : GL_RENDERBUFFER, GL_TRUE, GLsizei; 351 352 _mem = cast(GlDeviceMemory)mem; // ignored 353 354 final switch(_glType) { 355 case GlImgType.tex: 356 gl.BindTexture(_glTexTarget, _name); 357 if (glInfo.textureStorage || (_info.samples > 1 && glInfo.textureStorageMS)) { 358 final switch (_info.type) { 359 case ImageType.d1: 360 gl.TexStorage1D(_glTexTarget, _info.levels, _glFormat, _info.dims.width); 361 break; 362 case ImageType.d1Array: 363 gl.TexStorage2D(_glTexTarget, _info.levels, _glFormat, _info.dims.width, _info.layers); 364 break; 365 case ImageType.d2: 366 if (_info.samples <= 1) 367 gl.TexStorage2D(_glTexTarget, _info.levels, _glFormat, _info.dims.width, _info.dims.height); 368 else 369 gl.TexStorage2DMultisample( 370 _glTexTarget, _info.samples, _glFormat, _info.dims.width, _info.dims.height, GL_TRUE 371 ); 372 break; 373 case ImageType.d2Array: 374 if (_info.samples <= 1) 375 gl.TexStorage3D(_glTexTarget, _info.levels, _glFormat, _info.dims.width, _info.dims.height, _info.layers); 376 else 377 gl.TexStorage3DMultisample( 378 _glTexTarget, _info.samples, _glFormat, _info.dims.width, _info.dims.height, _info.layers, GL_TRUE 379 ); 380 break; 381 case ImageType.d3: 382 gl.TexStorage3D(_glTexTarget, _info.levels, _glFormat, _info.dims.width, _info.dims.height, _info.dims.depth); 383 break; 384 case ImageType.cube: 385 gl.TexStorage2D(_glTexTarget, _info.levels, _glFormat, _info.dims.width, _info.dims.height); 386 break; 387 case ImageType.cubeArray: 388 gl.TexStorage3D(_glTexTarget, _info.levels, _glFormat, _info.dims.width, _info.dims.height, _info.layers*6); 389 break; 390 } 391 } 392 else { 393 394 GLsizei width = _info.dims.width; 395 GLsizei height = _info.dims.height; 396 GLsizei depth = _info.dims.depth; 397 398 foreach (l; 0.._info.levels) { 399 400 final switch (_info.type) { 401 case ImageType.d1: 402 gl.TexImage1D(_glTexTarget, l, _glFormat, width, 0, 0, 0, null); 403 break; 404 case ImageType.d1Array: 405 gl.TexImage2D(_glTexTarget, l, _glFormat, width, _info.layers, 0, 0, 0, null); 406 break; 407 case ImageType.d2: 408 if (_info.samples <= 1) 409 gl.TexImage2D(_glTexTarget, l, _glFormat, width, height, 0, 0, 0, null); 410 else 411 gl.TexImage2DMultisample(_glTexTarget, _info.samples, _glFormat, width, height, GL_TRUE); 412 break; 413 case ImageType.d2Array: 414 if (_info.samples <= 1) 415 gl.TexImage3D(_glTexTarget, l, _glFormat, width, height, _info.layers, 0, 0, 0, null); 416 else 417 gl.TexImage3DMultisample( 418 _glTexTarget, _info.samples, _glFormat, width, height, _info.layers, GL_TRUE 419 ); 420 break; 421 case ImageType.d3: 422 gl.TexImage3D(_glTexTarget, l, _glFormat, width, height, depth, 0, 0, 0, null); 423 break; 424 case ImageType.cube: 425 gl.TexImage2D(_glTexTarget, l, _glFormat, width, height, 0, 0, 0, null); 426 break; 427 case ImageType.cubeArray: 428 gl.TexImage3D(_glTexTarget, l, _glFormat, width, height, _info.layers*6, 0, 0, 0, null); 429 break; 430 } 431 432 if (width > 1) width /= 2; 433 if (height > 1) height /= 2; 434 if (depth > 1) depth /= 2; 435 } 436 } 437 gl.BindTexture(_glTexTarget, 0); 438 break; 439 440 case GlImgType.renderBuf: 441 gl.BindRenderbuffer(GL_RENDERBUFFER, _name); 442 if (_info.samples > 1) { 443 gl.RenderbufferStorageMultisample( 444 GL_RENDERBUFFER, _info.samples, _glFormat, _info.dims.width, _info.dims.height 445 ); 446 } 447 else { 448 gl.RenderbufferStorage(GL_RENDERBUFFER, _glFormat, _info.dims.width, _info.dims.height); 449 } 450 gl.BindRenderbuffer(GL_RENDERBUFFER, 0); 451 break; 452 } 453 454 import gfx.gl3.error : glCheck; 455 glCheck(gl, "bind image memory"); 456 } 457 458 void texSubImage(BufferImageCopy region) { 459 import gfx.gl3.conv : toSubImgFmt, toSubImgType; 460 gl.TexSubImage2D( 461 _glTexTarget, region.imageLayers.mipLevel, region.offset[0], 462 region.offset[1], region.extent[0], region.extent[1], toSubImgFmt(_info.format), 463 toSubImgType(_info.format), null 464 ); 465 } 466 } 467 468 final class GlImageView : ImageView 469 { 470 import gfx.core.rc : atomicRcCode, Rc; 471 import gfx.gl3 : GlInfo; 472 import gfx.graal.image : ImageBase, ImageDims, ImageSubresourceRange, 473 ImageType, Swizzle; 474 475 //mixin(atomicRcCode); 476 private size_t _refCount=0; 477 import std.typecons : Flag, Yes; 478 479 public final shared override @property size_t refCountShared() const 480 { 481 import core.atomic : atomicLoad; 482 return atomicLoad(_refCount); 483 } 484 485 public final shared override shared(typeof(this)) retainShared() 486 { 487 import core.atomic : atomicOp; 488 immutable rc = atomicOp!"+="(_refCount, 1); 489 debug(rc) { 490 import std.experimental.logger : logf; 491 logf("retain %s: %s -> %s", typeof(this).stringof, rc-1, rc); 492 } 493 return this; 494 } 495 496 public final shared override shared(typeof(this)) releaseShared(in Flag!"disposeOnZero" disposeOnZero = Yes.disposeOnZero) 497 { 498 import core.atomic : atomicOp; 499 immutable rc = atomicOp!"-="(_refCount, 1); 500 501 debug(rc) { 502 import std.experimental.logger : logf; 503 logf("release %s: %s -> %s", typeof(this).stringof, rc+1, rc); 504 } 505 if (rc == 0 && disposeOnZero) { 506 debug(rc) { 507 import std.experimental.logger : logf; 508 logf("dispose %s", typeof(this).stringof); 509 } 510 synchronized(this) { 511 // cast shared away 512 import gfx.core.rc : Disposable; 513 auto obj = cast(Disposable)this; 514 obj.dispose(); 515 } 516 return null; 517 } 518 else { 519 return this; 520 } 521 } 522 523 public final shared override shared(typeof(this)) rcLockShared() 524 { 525 import core.atomic : atomicLoad, cas; 526 while (1) { 527 immutable c = atomicLoad(_refCount); 528 529 if (c == 0) { 530 debug(rc) { 531 import std.experimental.logger : logf; 532 logf("rcLock %s: %s", typeof(this).stringof, c); 533 } 534 return null; 535 } 536 if (cas(&_refCount, c, c+1)) { 537 debug(rc) { 538 import std.experimental.logger : logf; 539 logf("rcLock %s: %s", typeof(this).stringof, c+1); 540 } 541 return this; 542 } 543 } 544 } 545 546 private Gl gl; 547 private GlInfo glInfo; 548 private Rc!GlImage img; 549 private ImageDims imgDims; 550 private ImageType type; 551 private uint layers; 552 private ImageSubresourceRange isr; 553 private Swizzle swzl; 554 555 private GlImgType glType; 556 package GLuint name; 557 package GLuint target; 558 559 this(GlImage img, ImageType type, ImageSubresourceRange isr, Swizzle swizzle) { 560 this.img = img; 561 this.gl = img.gl; 562 this.glInfo = img.glInfo; 563 this.imgDims = img.info.dims; 564 this.type = type; 565 this.layers = img.info.layers; 566 this.isr = isr; 567 this.swzl = swizzle; 568 569 glType = img._glType; 570 name = img._name; 571 if (glType == GlImgType.tex) { 572 import gfx.gl3.conv : toGlTexTarget; 573 target = toGlTexTarget(type, img._info.samples > 1); 574 } 575 } 576 577 override void dispose() { 578 img.unload(); 579 } 580 581 override @property ImageBase image() { 582 return img.obj; 583 } 584 override @property ImageSubresourceRange subresourceRange() { 585 return isr; 586 } 587 override @property Swizzle swizzle() { 588 return swzl; 589 } 590 591 void attachToFbo(GLenum target, ref uint colorNum) { 592 import gfx.bindings.opengl.gl : GLint, 593 GL_RENDERBUFFER, GL_COLOR_ATTACHMENT0, 594 GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT, 595 GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X, 596 GL_TEXTURE_1D, GL_TEXTURE_2D, GL_TEXTURE_3D; 597 import gfx.graal.image : ImageAspect; 598 599 GLenum glAttachment; 600 final switch (isr.aspect) { 601 case ImageAspect.color: 602 glAttachment = GL_COLOR_ATTACHMENT0 + colorNum++; 603 break; 604 case ImageAspect.depth: 605 glAttachment = GL_DEPTH_ATTACHMENT; 606 break; 607 case ImageAspect.stencil: 608 glAttachment = GL_STENCIL_ATTACHMENT; 609 break; 610 case ImageAspect.depthStencil: 611 glAttachment = GL_DEPTH_STENCIL_ATTACHMENT; 612 break; 613 } 614 615 final switch(glType) { 616 case GlImgType.tex: 617 if (layers > 1 && isr.layers == 1) { 618 gl.FramebufferTextureLayer( 619 target, glAttachment, name, cast(GLint)isr.firstLevel, cast(GLint)isr.firstLayer 620 ); 621 } 622 else { 623 final switch (type) { 624 case ImageType.d1: 625 case ImageType.d1Array: 626 gl.FramebufferTexture1D( 627 target, glAttachment, GL_TEXTURE_1D, name, cast(GLint)isr.firstLevel 628 ); 629 break; 630 case ImageType.d2: 631 case ImageType.d2Array: 632 gl.FramebufferTexture2D( 633 target, glAttachment, GL_TEXTURE_2D, name, cast(GLint)isr.firstLevel 634 ); 635 break; 636 case ImageType.d3: 637 gl.FramebufferTexture3D( 638 target, glAttachment, GL_TEXTURE_3D, name, 639 cast(GLint)isr.firstLevel, cast(GLint)isr.firstLayer 640 ); 641 break; 642 case ImageType.cube: 643 case ImageType.cubeArray: 644 const layer = GL_TEXTURE_CUBE_MAP_POSITIVE_X+cast(GLenum)isr.firstLayer; 645 gl.FramebufferTexture2D( 646 target, glAttachment, layer, name, cast(GLint)isr.firstLevel 647 ); 648 break; 649 } 650 } 651 break; 652 case GlImgType.renderBuf: 653 gl.FramebufferRenderbuffer(target, glAttachment, GL_RENDERBUFFER, name); 654 break; 655 } 656 } 657 } 658 659 660 661 final class GlSampler : Sampler 662 { 663 import gfx.bindings.opengl.gl : Gl, GLenum, GLint, GLfloat, GLuint; 664 import gfx.core.rc : atomicRcCode; 665 import gfx.graal.image : BorderColor, isInt, SamplerInfo; 666 import gfx.graal.pipeline : CompareOp; 667 import gfx.gl3 : GlInfo, GlShare; 668 import gfx.gl3.conv : toGl, toGlMag, toGlMin; 669 670 mixin(atomicRcCode); 671 672 private Gl gl; 673 private GlInfo glInfo; 674 private SamplerInfo _info; 675 private GLuint _name; 676 677 this(GlShare share, in SamplerInfo info) 678 { 679 gl = share.gl; 680 glInfo = share.info; 681 _info = info; 682 683 if (glInfo.samplerObject) { 684 gl.GenSamplers(1, &_name); 685 686 setupSampler!( 687 (GLenum pname, GLint param) { gl.SamplerParameteri(_name, pname, param); }, 688 (GLenum pname, GLfloat param) { gl.SamplerParameterf(_name, pname, param); }, 689 (GLenum pname, const(GLint)* param) { gl.SamplerParameteriv(_name, pname, param); }, 690 (GLenum pname, const(GLfloat)* param) { gl.SamplerParameterfv(_name, pname, param); }, 691 )(info); 692 } 693 } 694 695 override void dispose() { 696 if (glInfo.samplerObject) { 697 gl.DeleteSamplers(1, &_name); 698 } 699 } 700 701 void bind (GLuint target, GLuint unit) { 702 if (glInfo.samplerObject) { 703 gl.BindSampler(unit, _name); 704 } 705 else { 706 setupSampler!( 707 (GLenum pname, GLint param) { gl.TexParameteri(target, pname, param); }, 708 (GLenum pname, GLfloat param) { gl.TexParameterf(target, pname, param); }, 709 (GLenum pname, const(GLint)* param) { gl.TexParameteriv(target, pname, param); }, 710 (GLenum pname, const(GLfloat)* param) { gl.TexParameterfv(target, pname, param); }, 711 )(_info); 712 } 713 } 714 } 715 716 private void setupSampler(alias fi, alias ff, alias fiv, alias ffv)(in SamplerInfo glInfo) 717 { 718 import gfx.bindings.opengl.gl : GL_TEXTURE_MAX_ANISOTROPY, 719 GL_TEXTURE_MIN_FILTER, 720 GL_TEXTURE_MAG_FILTER, 721 GL_TEXTURE_WRAP_S, 722 GL_TEXTURE_WRAP_T, 723 GL_TEXTURE_WRAP_R, 724 GL_TEXTURE_LOD_BIAS, 725 GL_TEXTURE_MAX_LOD, GL_TEXTURE_MIN_LOD, 726 GL_TEXTURE_BORDER_COLOR, 727 GL_TEXTURE_COMPARE_MODE, 728 GL_TEXTURE_COMPARE_FUNC, 729 GL_COMPARE_REF_TO_TEXTURE, GL_NONE; 730 import gfx.gl3.conv : toGl, toGlMin, toGlMag; 731 import gfx.graal.image : BorderColor, isInt; 732 import gfx.graal.pipeline : CompareOp; 733 734 import std.algorithm : each; 735 glInfo.anisotropy.save.each!(m => ff(GL_TEXTURE_MAX_ANISOTROPY, m)); 736 737 const min = toGlMin(glInfo.minFilter, glInfo.mipmapFilter); 738 const mag = toGlMag(glInfo.magFilter); 739 fi(GL_TEXTURE_MIN_FILTER, min); 740 fi(GL_TEXTURE_MAG_FILTER, mag); 741 742 fi(GL_TEXTURE_WRAP_S, toGl(glInfo.wrapMode[0])); 743 fi(GL_TEXTURE_WRAP_T, toGl(glInfo.wrapMode[1])); 744 fi(GL_TEXTURE_WRAP_R, toGl(glInfo.wrapMode[2])); 745 746 import std.math : isNaN; 747 if (!glInfo.lodBias.isNaN) { 748 ff(GL_TEXTURE_LOD_BIAS, glInfo.lodBias); 749 } 750 if (!glInfo.lodRange[0].isNaN) { 751 ff(GL_TEXTURE_MIN_LOD, glInfo.lodRange[0]); 752 ff(GL_TEXTURE_MAX_LOD, glInfo.lodRange[1]); 753 } 754 755 import gfx.core.typecons : ifNone, ifSome; 756 glInfo.compare.save.ifSome!((CompareOp op) { 757 fi(GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); 758 fi(GL_TEXTURE_COMPARE_FUNC, toGl(op)); 759 }).ifNone!(() { 760 fi(GL_TEXTURE_COMPARE_MODE, GL_NONE); 761 }); 762 763 if (glInfo.borderColor.isInt) { 764 int[4] color; 765 switch (glInfo.borderColor) { 766 case BorderColor.intTransparent: 767 color = [ 0, 0, 0, 0]; 768 break; 769 case BorderColor.intBlack: 770 color = [ 0, 0, 0, int.max ]; 771 break; 772 case BorderColor.intWhite: 773 color = [ int.max, int.max, int.max, int.max ]; 774 break; 775 default: break; 776 } 777 fiv(GL_TEXTURE_BORDER_COLOR, &color[0]); 778 } 779 else { 780 float[4] color; 781 switch (glInfo.borderColor) { 782 case BorderColor.intTransparent: 783 color = [ 0f, 0f, 0f, 0f]; 784 break; 785 case BorderColor.intBlack: 786 color = [ 0f, 0f, 0f, 1f ]; 787 break; 788 case BorderColor.intWhite: 789 color = [ 1f, 1f, 1f, 1f ]; 790 break; 791 default: break; 792 } 793 ffv(GL_TEXTURE_BORDER_COLOR, &color[0]); 794 } 795 }