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