1 // Written in the D programming language.
2 
3 /**
4 This module contains OpenGL access layer.
5 
6 To enable OpenGL support, build with version(USE_OPENGL);
7 
8 Synopsis:
9 
10 ----
11 import dlangui.graphics.glsupport;
12 
13 ----
14 
15 Copyright: Vadim Lopatin, 2014
16 License:   Boost License 1.0
17 Authors:   Vadim Lopatin, coolreader.org@gmail.com
18 */
19 module dlangui.graphics.glsupport;
20 
21 public import dlangui.core.config;
22 static if (BACKEND_GUI):
23 static if (ENABLE_OPENGL):
24 
25 public import dlangui.core.math3d;
26 
27 import dlangui.core.logger;
28 import dlangui.core.types;
29 import dlangui.core.math3d;
30 
31 import std.conv;
32 import std.string;
33 import std.array;
34 
35 version (Android) {
36     enum SUPPORT_LEGACY_OPENGL = false;
37     public import EGL.eglplatform : EGLint;
38     public import EGL.egl;
39     //public import GLES2.gl2;
40     public import GLES3.gl3;
41 
42     static if (SUPPORT_LEGACY_OPENGL) {
43         public import GLES.gl : glEnableClientState, glLightfv, glColor4f, GL_ALPHA_TEST, GL_VERTEX_ARRAY,
44             GL_COLOR_ARRAY, glVertexPointer, glColorPointer, glDisableClientState,
45             GL_TEXTURE_COORD_ARRAY, glTexCoordPointer, glColorPointer, glMatrixMode,
46             glLoadMatrixf, glLoadIdentity, GL_PROJECTION, GL_MODELVIEW;
47     }
48 
49 } else {
50     enum SUPPORT_LEGACY_OPENGL = false; //true;
51     public import bindbc.opengl;
52     public import bindbc.opengl.bind.arb.core_30;
53     import loader = bindbc.loader.sharedlib;
54 
55 
56 private void gl3CheckMissingSymFunc(const(loader.ErrorInfo)[] errors)
57 {
58     import std.algorithm : equal;
59     immutable names = ["glGetError", "glShaderSource", "glCompileShader",
60             "glGetShaderiv", "glGetShaderInfoLog", "glGetString",
61             "glCreateProgram", "glUseProgram", "glDeleteProgram",
62             "glDeleteShader", "glEnable", "glDisable", "glBlendFunc",
63             "glUniformMatrix4fv", "glGetAttribLocation", "glGetUniformLocation",
64             "glGenVertexArrays", "glBindVertexArray", "glBufferData",
65             "glBindBuffer", "glBufferSubData"];
66     foreach(info; errors)
67     {
68         import std.array;
69         import std.algorithm;
70         import std.exception;
71         import core.stdc.string;
72         // NOTE: this has crappy complexity as it was just updated as is
73         //     it also does not checks if the symbol was actually loaded
74         auto errMsg = cast(string) info.message[0 .. info.message.strlen];
75         bool found = names.any!(s => s.indexOf(errMsg) != -1);
76         enforce(!found, { return errMsg.idup; });
77     }
78 }
79 
80 import dlangui.graphics.scene.mesh;
81 import dlangui.graphics.scene.effect;
82 
83 
84 //extern (C) void func(int n);
85 //pragma(msg, __traits(identifier, func));
86 
87 /**
88  * Convenient wrapper around glGetError()
89  * Using: checkgl!glFunction(funcParams);
90  * TODO use one of the DEBUG extensions
91  */
92 template checkgl(alias func)
93 {
94     debug auto checkgl(string functionName=__FUNCTION__, int line=__LINE__, Args...)(Args args)
95     {
96         scope(success) checkError(__traits(identifier, func), functionName, line);
97         return func(args);
98     } else
99         alias checkgl = func;
100 }
101 bool checkError(string context="", string functionName=__FUNCTION__, int line=__LINE__)
102 {
103     GLenum err = glGetError();
104     if (err != GL_NO_ERROR) {
105         Log.e("OpenGL error ", glerrorToString(err), " at ", functionName, ":", line, " -- ", context);
106         return true;
107     }
108     return false;
109 }
110 
111 /**
112 * Convenient wrapper around glGetError()
113 * Using: checkgl!glFunction(funcParams);
114 * TODO use one of the DEBUG extensions
115 */
116 template assertgl(alias func)
117 {
118     auto assertgl(string functionName=__FUNCTION__, int line=__LINE__, Args...)(Args args)
119     {
120         scope(success) assertNoError(func.stringof, functionName, line);
121         return func(args);
122     };
123 }
124 void assertNoError(string context="", string functionName=__FUNCTION__, int line=__LINE__)
125 {
126     GLenum err = glGetError();
127     if (err != GL_NO_ERROR) {
128         Log.e("fatal OpenGL error ", glerrorToString(err), " at ", functionName, ":", line, " -- ", context);
129         assert(false);
130     }
131 }
132 
133 /* For reporting OpenGL errors, it's nicer to get a human-readable symbolic name for the
134  * error instead of the numeric form. Derelict's GLenum is just an alias for uint, so we
135  * can't depend on D's nice toString() for enums.
136  */
137 string glerrorToString(in GLenum err) pure nothrow {
138     switch(err) {
139         case 0x0500: return "GL_INVALID_ENUM";
140         case 0x0501: return "GL_INVALID_VALUE";
141         case 0x0502: return "GL_INVALID_OPERATION";
142         case 0x0505: return "GL_OUT_OF_MEMORY";
143         case 0x0506: return "GL_INVALID_FRAMEBUFFER_OPERATION";
144         case 0x0507: return "GL_CONTEXT_LOST";
145         case GL_NO_ERROR: return "No GL error";
146         default: return "Unknown GL error: " ~ to!string(err);
147     }
148 }
149 
150 class GLProgram : dlangui.graphics.scene.mesh.GraphicsEffect {
151     @property abstract string vertexSource();
152     @property abstract string fragmentSource();
153     protected GLuint program;
154     protected bool initialized;
155     protected bool error;
156 
157     private GLuint vertexShader;
158     private GLuint fragmentShader;
159     private string glslversion;
160     private int glslversionInt;
161     private char[] glslversionString;
162 
163     private void compatibilityFixes(ref char[] code, GLuint type) {
164         if (glslversionInt < 150)
165             code = replace(code, " texture(", " texture2D(");
166         if (glslversionInt < 140) {
167             if(type == GL_VERTEX_SHADER) {
168                 code = replace(code, "in ", "attribute ");
169                 code = replace(code, "out ", "varying ");
170             } else {
171                 code = replace(code, "in ", "varying ");
172                 code = replace(code, "out vec4 outColor;", "");
173                 code = replace(code, "outColor", "gl_FragColor");
174             }
175         }
176     }
177 
178     private GLuint compileShader(string src, GLuint type) {
179         import std.string : toStringz, fromStringz;
180 
181         char[] sourceCode;
182         if (glslversionString.length) {
183             sourceCode ~= "#version ";
184             sourceCode ~= glslversionString;
185             sourceCode ~= "\n";
186         }
187         sourceCode ~= src;
188         compatibilityFixes(sourceCode, type);
189 
190         Log.d("compileShader: glslVersion = ", glslversion, ", type: ", (type == GL_VERTEX_SHADER ? "GL_VERTEX_SHADER" : (type == GL_FRAGMENT_SHADER ? "GL_FRAGMENT_SHADER" : "UNKNOWN")));
191         //Log.v("Shader code:\n", sourceCode);
192         GLuint shader = checkgl!glCreateShader(type);
193         const char * psrc = sourceCode.toStringz;
194         checkgl!glShaderSource(shader, 1, &psrc, null);
195         checkgl!glCompileShader(shader);
196         GLint compiled;
197         checkgl!glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
198         if (compiled) {
199             // compiled successfully
200             return shader;
201         } else {
202             Log.e("Failed to compile shader source:\n", sourceCode);
203             GLint blen = 0;
204             GLsizei slen = 0;
205             checkgl!glGetShaderiv(shader, GL_INFO_LOG_LENGTH , &blen);
206             if (blen > 1)
207             {
208                 GLchar[] msg = new GLchar[blen + 1];
209                 checkgl!glGetShaderInfoLog(shader, blen, &slen, msg.ptr);
210                 Log.e("Shader compilation error: ", fromStringz(msg.ptr));
211             }
212             return 0;
213         }
214     }
215 
216     bool compile() {
217         glslversion = checkgl!fromStringz(cast(const char *)glGetString(GL_SHADING_LANGUAGE_VERSION)).dup;
218         glslversionString.length = 0;
219         glslversionInt = 0;
220         foreach(ch; glslversion) {
221             if (ch >= '0' && ch <= '9') {
222                 glslversionString ~= ch;
223                 glslversionInt = glslversionInt * 10 + (ch - '0');
224             } else if (ch != '.')
225                 break;
226         }
227         version (Android) {
228             glslversionInt = 130;
229         }
230 
231         vertexShader = compileShader(vertexSource, GL_VERTEX_SHADER);
232         fragmentShader = compileShader(fragmentSource, GL_FRAGMENT_SHADER);
233         if (!vertexShader || !fragmentShader) {
234             error = true;
235             return false;
236         }
237         program = checkgl!glCreateProgram();
238         checkgl!glAttachShader(program, vertexShader);
239         checkgl!glAttachShader(program, fragmentShader);
240         checkgl!glLinkProgram(program);
241         GLint isLinked = 0;
242         checkgl!glGetProgramiv(program, GL_LINK_STATUS, &isLinked);
243         if (!isLinked) {
244             GLint maxLength = 0;
245             checkgl!glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength);
246             GLchar[] msg = new GLchar[maxLength + 1];
247             checkgl!glGetProgramInfoLog(program, maxLength, &maxLength, msg.ptr);
248             Log.e("Error while linking program: ", fromStringz(msg.ptr));
249             error = true;
250             return false;
251         }
252         Log.d("Program linked successfully");
253 
254         initStandardLocations();
255         if (!initLocations()) {
256             Log.e("some of locations were not found");
257             error = true;
258         }
259         initialized = true;
260         Log.v("Program is initialized successfully");
261         return !error;
262     }
263 
264 
265     void initStandardLocations() {
266         for(DefaultUniform id = DefaultUniform.min; id <= DefaultUniform.max; id++) {
267             _uniformIdLocations[id] = getUniformLocation(to!string(id));
268         }
269         for(DefaultAttribute id = DefaultAttribute.min; id <= DefaultAttribute.max; id++) {
270             _attribIdLocations[id] = getAttribLocation(to!string(id));
271         }
272     }
273 
274     /// override to init shader code locations
275     abstract bool initLocations();
276 
277     ~this() {
278         // TODO: cleanup
279         if (program)
280             glDeleteProgram(program);
281         if (vertexShader)
282             glDeleteShader(vertexShader);
283         if (fragmentShader)
284             glDeleteShader(fragmentShader);
285         program = vertexShader = fragmentShader = 0;
286         initialized = false;
287     }
288 
289     /// returns true if program is ready for use
290     bool check() {
291         return !error && initialized;
292     }
293 
294     static GLuint currentProgram;
295     /// binds program to current context
296     void bind() {
297         if(program != currentProgram) {
298             checkgl!glUseProgram(program);
299             currentProgram = program;
300         }
301     }
302 
303     /// unbinds program from current context
304     static void unbind() {
305         checkgl!glUseProgram(0);
306         currentProgram = 0;
307     }
308 
309     protected int[string] _uniformLocations;
310     protected int[string] _attribLocations;
311     protected int[DefaultUniform.max + 1] _uniformIdLocations;
312     protected int[DefaultAttribute.max + 1] _attribIdLocations;
313 
314     /// get location for vertex attribute
315     override int getVertexElementLocation(VertexElementType type) {
316         return VERTEX_ELEMENT_NOT_FOUND;
317     }
318 
319 
320     /// get uniform location from program by uniform id, returns -1 if location is not found
321     int getUniformLocation(DefaultUniform uniform) {
322         return _uniformIdLocations[uniform];
323     }
324 
325     /// get uniform location from program, returns -1 if location is not found
326     int getUniformLocation(string variableName) {
327         if (auto p = variableName in _uniformLocations)
328             return *p;
329         int res = checkgl!glGetUniformLocation(program, variableName.toStringz);
330         //if (res == -1)
331         //    Log.e("glGetUniformLocation failed for " ~ variableName);
332         _uniformLocations[variableName] = res;
333         return res;
334     }
335 
336     /// get attribute location from program by uniform id, returns -1 if location is not found
337     int getAttribLocation(DefaultAttribute id) {
338         return _attribIdLocations[id];
339     }
340 
341     /// get attribute location from program, returns -1 if location is not found
342     int getAttribLocation(string variableName) {
343         if (auto p = variableName in _attribLocations)
344             return *p;
345         int res = checkgl!glGetAttribLocation(program, variableName.toStringz);
346         //if (res == -1)
347         //    Log.e("glGetAttribLocation failed for " ~ variableName);
348         _attribLocations[variableName] = res;
349         return res;
350     }
351 
352     override void setUniform(string uniformName, const vec2[] vec) {
353         checkgl!glUniform2fv(getUniformLocation(uniformName), cast(int)vec.length, cast(const(float)*)vec.ptr);
354     }
355 
356     override void setUniform(DefaultUniform id, const vec2[] vec) {
357         checkgl!glUniform2fv(getUniformLocation(id), cast(int)vec.length, cast(const(float)*)vec.ptr);
358     }
359 
360     override void setUniform(string uniformName, vec2 vec) {
361         checkgl!glUniform2fv(getUniformLocation(uniformName), 1, vec.vec.ptr);
362     }
363 
364     override void setUniform(DefaultUniform id, vec2 vec) {
365         checkgl!glUniform2fv(getUniformLocation(id), 1, vec.vec.ptr);
366     }
367 
368     override void setUniform(string uniformName, vec3 vec) {
369         checkgl!glUniform3fv(getUniformLocation(uniformName), 1, vec.vec.ptr);
370     }
371 
372     override void setUniform(DefaultUniform id, vec3 vec) {
373         checkgl!glUniform3fv(getUniformLocation(id), 1, vec.vec.ptr);
374     }
375 
376     override void setUniform(string uniformName, const vec3[] vec) {
377         checkgl!glUniform3fv(getUniformLocation(uniformName), cast(int)vec.length, cast(const(float)*)vec.ptr);
378     }
379 
380     override void setUniform(DefaultUniform id, const vec3[] vec) {
381         checkgl!glUniform3fv(getUniformLocation(id), cast(int)vec.length, cast(const(float)*)vec.ptr);
382     }
383 
384     override void setUniform(string uniformName, vec4 vec) {
385         checkgl!glUniform4fv(getUniformLocation(uniformName), 1, vec.vec.ptr);
386     }
387 
388     override void setUniform(DefaultUniform id, vec4 vec) {
389         checkgl!glUniform4fv(getUniformLocation(id), 1, vec.vec.ptr);
390     }
391 
392     override void setUniform(string uniformName, const vec4[] vec) {
393         checkgl!glUniform4fv(getUniformLocation(uniformName), cast(int)vec.length, cast(const(float)*)vec.ptr);
394     }
395 
396     override void setUniform(DefaultUniform id, const vec4[] vec) {
397         checkgl!glUniform4fv(getUniformLocation(id), cast(int)vec.length, cast(const(float)*)vec.ptr);
398     }
399 
400     override void setUniform(string uniformName, ref const(mat4) matrix) {
401         checkgl!glUniformMatrix4fv(getUniformLocation(uniformName), 1, false, matrix.m.ptr);
402     }
403 
404     override void setUniform(DefaultUniform id, ref const(mat4) matrix) {
405         checkgl!glUniformMatrix4fv(getUniformLocation(id), 1, false, matrix.m.ptr);
406     }
407 
408     override void setUniform(string uniformName, const(mat4)[] matrix) {
409         checkgl!glUniformMatrix4fv(getUniformLocation(uniformName), cast(int)matrix.length, false, cast(const(float)*)matrix.ptr);
410     }
411 
412     override void setUniform(DefaultUniform id, const(mat4)[] matrix) {
413         checkgl!glUniformMatrix4fv(getUniformLocation(id), cast(int)matrix.length, false, cast(const(float)*)matrix.ptr);
414     }
415 
416     override void setUniform(string uniformName, float v) {
417         checkgl!glUniform1f(getUniformLocation(uniformName), v);
418     }
419 
420     override void setUniform(DefaultUniform id, float v) {
421         checkgl!glUniform1f(getUniformLocation(id), v);
422     }
423 
424     override void setUniform(string uniformName, const float[] v) {
425         checkgl!glUniform1fv(getUniformLocation(uniformName), cast(int)v.length, cast(const(float)*)v.ptr);
426     }
427 
428     override void setUniform(DefaultUniform id, const float[] v) {
429         checkgl!glUniform1fv(getUniformLocation(id), cast(int)v.length, cast(const(float)*)v.ptr);
430     }
431 
432     /// returns true if effect has uniform
433     override bool hasUniform(DefaultUniform id) {
434         return getUniformLocation(id) >= 0;
435     }
436 
437     /// returns true if effect has uniform
438     override bool hasUniform(string uniformName) {
439         return getUniformLocation(uniformName) >= 0;
440     }
441 
442     /// draw mesh using this program (program should be bound by this time and all uniforms should be set)
443     override void draw(Mesh mesh, bool wireframe) {
444         VertexBuffer vb = mesh.vertexBuffer;
445         if (!vb) {
446             vb = new GLVertexBuffer();
447             mesh.vertexBuffer = vb;
448         }
449         vb.draw(this, wireframe);
450     }
451 }
452 
453 class SolidFillProgram : GLProgram {
454     @property override string vertexSource() {
455         return q{
456             in vec3 a_position;
457             in vec4 a_color;
458             out vec4 col;
459             uniform mat4 u_worldViewProjectionMatrix;
460             void main(void)
461             {
462                 gl_Position = u_worldViewProjectionMatrix * vec4(a_position, 1);
463                 col = a_color;
464             }
465         };
466     }
467 
468     @property override string fragmentSource() {
469         return q{
470             in vec4 col;
471             out vec4 outColor;
472             void main(void)
473             {
474                 outColor = col;
475             }
476         };
477     }
478 
479     protected GLint matrixLocation;
480     protected GLint vertexLocation;
481     protected GLint colAttrLocation;
482     override bool initLocations() {
483         matrixLocation = getUniformLocation(DefaultUniform.u_worldViewProjectionMatrix);
484         vertexLocation = getAttribLocation(DefaultAttribute.a_position);
485         colAttrLocation = getAttribLocation(DefaultAttribute.a_color);
486         return matrixLocation >= 0 && vertexLocation >= 0 && colAttrLocation >= 0;
487     }
488     /// get location for vertex attribute
489     override int getVertexElementLocation(VertexElementType type) {
490         switch(type) with(VertexElementType) {
491             case POSITION:
492                 return vertexLocation;
493             case COLOR:
494                 return colAttrLocation;
495             default:
496                 return VERTEX_ELEMENT_NOT_FOUND;
497         }
498     }
499 
500     VAO vao;
501 
502     protected void beforeExecute() {
503         bind();
504         setUniform(DefaultUniform.u_worldViewProjectionMatrix, glSupport.projectionMatrix);
505     }
506 
507     protected void createVAO(size_t verticesBufferLength) {
508         vao = new VAO;
509 
510         glVertexAttribPointer(vertexLocation, 3, GL_FLOAT, GL_FALSE, 0, cast(void*) 0);
511         glVertexAttribPointer(colAttrLocation, 4, GL_FLOAT, GL_FALSE, 0, cast(void*) (verticesBufferLength * float.sizeof));
512 
513         glEnableVertexAttribArray(vertexLocation);
514         glEnableVertexAttribArray(colAttrLocation);
515     }
516 
517     bool drawBatch(int length, int start, bool areLines = false) {
518         if(!check())
519             return false;
520         beforeExecute();
521 
522         vao.bind();
523 
524         checkgl!glDrawElements(areLines ? GL_LINES : GL_TRIANGLES, cast(int)length, GL_UNSIGNED_INT, cast(void*)(start * 4));
525 
526         return true;
527     }
528 
529     void destroyBuffers() {
530         destroy(vao);
531         vao = null;
532     }
533 }
534 
535 class TextureProgram : SolidFillProgram {
536     @property override string vertexSource() {
537         return q{
538             in vec3 a_position;
539             in vec4 a_color;
540             in vec2 a_texCoord;
541             out vec4 col;
542             out vec2 UV;
543             uniform mat4 u_worldViewProjectionMatrix;
544             void main(void)
545             {
546                 gl_Position = u_worldViewProjectionMatrix * vec4(a_position, 1);
547                 col = a_color;
548                 UV = a_texCoord;
549             }
550         };
551     }
552     @property override string fragmentSource() {
553         return q{
554             uniform sampler2D tex;
555             in vec4 col;
556             in vec2 UV;
557             out vec4 outColor;
558             void main(void)
559             {
560                 outColor = texture(tex, UV) * col;
561             }
562         };
563     }
564 
565     GLint texCoordLocation;
566     override bool initLocations() {
567         bool res = super.initLocations();
568         texCoordLocation = getAttribLocation(DefaultAttribute.a_texCoord);
569         return res && texCoordLocation >= 0;
570     }
571     /// get location for vertex attribute
572     override int getVertexElementLocation(VertexElementType type) {
573         switch(type) with(VertexElementType) {
574             case TEXCOORD0:
575                 return texCoordLocation;
576             default:
577                 return super.getVertexElementLocation(type);
578         }
579     }
580 
581     protected void createVAO(size_t verticesBufferLength, size_t colorsBufferLength) {
582         vao = new VAO;
583 
584         glVertexAttribPointer(vertexLocation, 3, GL_FLOAT, GL_FALSE, 0, cast(void*) 0);
585         glVertexAttribPointer(colAttrLocation, 4, GL_FLOAT, GL_FALSE, 0, cast(void*) (verticesBufferLength * float.sizeof));
586         glVertexAttribPointer(texCoordLocation, 2, GL_FLOAT, GL_FALSE, 0, cast(void*) ((verticesBufferLength + colorsBufferLength) * float.sizeof));
587 
588         glEnableVertexAttribArray(vertexLocation);
589         glEnableVertexAttribArray(colAttrLocation);
590         glEnableVertexAttribArray(texCoordLocation);
591     }
592 
593     bool drawBatch(Tex2D texture, bool linear, int length, int start) {
594         if(!check())
595             return false;
596         beforeExecute();
597 
598         texture.setup();
599         texture.setSamplerParams(linear);
600 
601         vao.bind();
602 
603         checkgl!glDrawElements(GL_TRIANGLES, cast(int)length, GL_UNSIGNED_INT, cast(void*)(start * 4));
604 
605         texture.unbind();
606         return true;
607     }
608 }
609 
610 
611 struct Color
612 {
613     float r, g, b, a;
614 }
615 
616 // utility function to fill 4-float array of vertex colors with converted CR 32bit color
617 private void FillColor(uint color, Color[] buf_slice) {
618     float r = ((color >> 16) & 255) / 255.0;
619     float g = ((color >> 8) & 255) / 255.0;
620     float b = ((color >> 0) & 255) / 255.0;
621     float a = (((color >> 24) & 255) ^ 255) / 255.0;
622     foreach(ref col; buf_slice) {
623         col.r = r;
624         col.g = g;
625         col.b = b;
626         col.a = a;
627     }
628 }
629 
630 
631 import std.functional;
632 alias convertColors = memoize!(convertColorsImpl);
633 
634 float[] convertColorsImpl(uint[] cols) pure nothrow {
635     float[] colors;
636     colors.length = cols.length * 4;
637     foreach(i; 0 .. cols.length) {
638         uint color = cols[i];
639         float r = ((color >> 16) & 255) / 255.0;
640         float g = ((color >> 8) & 255) / 255.0;
641         float b = ((color >> 0) & 255) / 255.0;
642         float a = (((color >> 24) & 255) ^ 255) / 255.0;
643         colors[i * 4 + 0] = r;
644         colors[i * 4 + 1] = g;
645         colors[i * 4 + 2] = b;
646         colors[i * 4 + 3] = a;
647     }
648     return colors;
649 }
650 
651 private __gshared GLSupport _glSupport;
652 @property GLSupport glSupport() {
653     if (!_glSupport) {
654         Log.f("GLSupport is not initialized");
655         assert(false, "GLSupport is not initialized");
656     }
657     if (!_glSupport.valid) {
658         Log.e("GLSupport programs are not initialized");
659     }
660     return _glSupport;
661 }
662 
663 __gshared bool glNoContext;
664 
665 /// initialize OpenGL support helper (call when current OpenGL context is initialized)
666 bool initGLSupport(bool legacy = false) {
667     import dlangui.platforms.common.platform : setOpenglEnabled;
668     import loader = bindbc.loader.sharedlib;
669     if (_glSupport && _glSupport.valid)
670         return true;
671     version(Android) {
672         Log.d("initGLSupport");
673     } else {
674             static bool BINDBC_GL3_RELOADED;
675             static bool gl3ReloadedOk;
676             static bool glReloadedOk;
677             if (!BINDBC_GL3_RELOADED) {
678                 BINDBC_GL3_RELOADED = true;
679                 try {
680                     Log.v("Reloading bindbc-opengl");
681                     import bindbc.opengl;
682                     loadOpenGL();
683                     gl3CheckMissingSymFunc(loader.errors);
684                     gl3ReloadedOk = true;
685                 } catch (Exception e) {
686                     Log.e("bindbc-opengl exception while reloading opengl", e);
687                 }
688                 try {
689                     Log.v("Reloading bindbc-opengl");
690                     import bindbc.opengl;
691                     loadOpenGL();
692                     gl3CheckMissingSymFunc(loader.errors);
693                     glReloadedOk = true;
694                 } catch (Exception e) {
695                     Log.e("bindbc-opengl exception while reloading opengl", e);
696                 }
697             }
698             if (!gl3ReloadedOk && !glReloadedOk) {
699                 Log.e("bindbc-opengl reloaded unsuccessfully");
700             return false;
701         }
702         if (!gl3ReloadedOk)
703             legacy = true;
704         else if (!glReloadedOk)
705             legacy = false;
706     }
707     if (!_glSupport) { // TODO_GRIM: Legacy looks very broken to me.
708         Log.d("glSupport not initialized: trying to create");
709         int major = *cast(int*)(glGetString(GL_VERSION)[0 .. 1].ptr);
710         legacy = legacy || (major < 3);
711         _glSupport = new GLSupport(legacy);
712         if (!_glSupport.valid) {
713             Log.e("Failed to compile shaders");
714             version (Android) {
715                 // do not recreate legacy mode
716             } else {
717                 // try opposite legacy flag
718                 if (_glSupport.legacyMode == legacy) {
719                     Log.i("Trying to reinit GLSupport with legacy flag ", !legacy);
720                     _glSupport = new GLSupport(!legacy);
721                 }
722                 // Situation when opposite legacy flag is true and GL version is 3+ with no old functions
723                 if (_glSupport.legacyMode) {
724                     if (major >= 3) {
725                         Log.e("Try to create OpenGL context with <= 3.1 version");
726                         return false;
727                     }
728                 }
729             }
730         }
731     }
732     if (_glSupport.valid) {
733         setOpenglEnabled();
734         Log.v("OpenGL is initialized ok");
735         return true;
736     } else {
737         Log.e("Failed to compile shaders");
738         return false;
739     }
740 }
741 
742 /// OpenGL support helper
743 final class GLSupport {
744 
745     private bool _legacyMode;
746     @property bool legacyMode() { return _legacyMode; }
747     @property queue() { return _queue; }
748 
749     @property bool valid() {
750         return _legacyMode || _shadersAreInitialized;
751     }
752 
753     this(bool legacy = false) {
754         _queue = new OpenGLQueue;
755         version (Android) {
756             Log.d("creating GLSupport");
757         } else {
758             if (legacy /*&& !glLightfv*/) {
759                 Log.w("GLSupport legacy API is not supported");
760                 legacy = false;
761             }
762         }
763         _legacyMode = legacy;
764         if (!_legacyMode)
765             _shadersAreInitialized = initShaders();
766     }
767 
768     ~this() {
769         uninitShaders();
770     }
771 
772     private OpenGLQueue _queue;
773 
774     private SolidFillProgram _solidFillProgram;
775     private TextureProgram _textureProgram;
776 
777     private bool _shadersAreInitialized;
778     private bool initShaders() {
779         if (_solidFillProgram is null) {
780             Log.v("Compiling solid fill program");
781             _solidFillProgram = new SolidFillProgram();
782             _solidFillProgram.compile();
783             if (!_solidFillProgram.check())
784                 return false;
785         }
786         if (_textureProgram is null) {
787             Log.v("Compiling texture program");
788             _textureProgram = new TextureProgram();
789             _textureProgram.compile();
790             if (!_textureProgram.check())
791                 return false;
792         }
793         Log.d("Shaders compiled successfully");
794         return true;
795     }
796 
797     private void uninitShaders() {
798         Log.d("Uniniting shaders");
799         if (_solidFillProgram !is null) {
800             destroy(_solidFillProgram);
801             _solidFillProgram = null;
802         }
803         if (_textureProgram !is null) {
804             destroy(_textureProgram);
805             _textureProgram = null;
806         }
807     }
808 
809     void beforeRenderGUI() {
810         glEnable(GL_BLEND);
811         checkgl!glDisable(GL_CULL_FACE);
812         checkgl!glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
813     }
814 
815     private VBO vbo;
816     private EBO ebo;
817 
818     private void fillBuffers(float[] vertices, float[] colors, float[] texcoords, int[] indices) {
819         resetBindings();
820 
821         if(_legacyMode)
822             return;
823 
824         vbo = new VBO;
825         ebo = new EBO;
826 
827         vbo.bind();
828         vbo.fill([vertices, colors, texcoords]);
829 
830         ebo.bind();
831         ebo.fill(indices);
832 
833         // create vertex array objects and bind vertex buffers to them
834         _solidFillProgram.createVAO(vertices.length);
835         vbo.bind();
836         ebo.bind();
837         _textureProgram.createVAO(vertices.length, colors.length);
838         vbo.bind();
839         ebo.bind();
840     }
841 
842     /// This function is needed to draw custom OpenGL scene correctly (especially on legacy API)
843     private void resetBindings() {
844         import std.traits : isFunction;
845         if (isFunction!glUseProgram)
846             GLProgram.unbind();
847         if (isFunction!glBindVertexArray)
848             VAO.unbind();
849         if (isFunction!glBindBuffer)
850             VBO.unbind();
851     }
852 
853     private void destroyBuffers() {
854         resetBindings();
855 
856         if(_legacyMode)
857             return;
858 
859         if (_solidFillProgram)
860             _solidFillProgram.destroyBuffers();
861         if (_textureProgram)
862             _textureProgram.destroyBuffers();
863 
864         destroy(vbo);
865         destroy(ebo);
866         vbo = null;
867         ebo = null;
868     }
869 
870     private void drawLines(int length, int start) {
871         if (_legacyMode) {
872             static if (SUPPORT_LEGACY_OPENGL) {
873                 glEnableClientState(GL_VERTEX_ARRAY);
874                 glEnableClientState(GL_COLOR_ARRAY);
875                 glVertexPointer(3, GL_FLOAT, 0, cast(void*)_queue._vertices.data.ptr);
876                 glColorPointer(4, GL_FLOAT, 0, cast(void*)_queue._colors.data.ptr);
877 
878                 checkgl!glDrawElements(GL_LINES, cast(int)length, GL_UNSIGNED_INT, cast(void*)(_queue._indices.data[start .. start + length].ptr));
879 
880                 glDisableClientState(GL_COLOR_ARRAY);
881                 glDisableClientState(GL_VERTEX_ARRAY);
882             }
883         } else {
884             if (_solidFillProgram !is null) {
885                 _solidFillProgram.drawBatch(length, start, true);
886             } else
887                 Log.e("No program");
888         }
889     }
890 
891     private void drawSolidFillTriangles(int length, int start) {
892         if (_legacyMode) {
893             static if (SUPPORT_LEGACY_OPENGL) {
894                 glEnableClientState(GL_VERTEX_ARRAY);
895                 glEnableClientState(GL_COLOR_ARRAY);
896                 glVertexPointer(3, GL_FLOAT, 0, cast(void*)_queue._vertices.data.ptr);
897                 glColorPointer(4, GL_FLOAT, 0, cast(void*)_queue._colors.data.ptr);
898 
899                 checkgl!glDrawElements(GL_TRIANGLES, cast(int)length, GL_UNSIGNED_INT, cast(void*)(_queue._indices.data[start .. start + length].ptr));
900 
901                 glDisableClientState(GL_COLOR_ARRAY);
902                 glDisableClientState(GL_VERTEX_ARRAY);
903             }
904         } else {
905             if (_solidFillProgram !is null) {
906                 _solidFillProgram.drawBatch(length, start);
907             } else
908                 Log.e("No program");
909         }
910     }
911 
912     private void drawColorAndTextureTriangles(Tex2D texture, bool linear, int length, int start) {
913         if (_legacyMode) {
914             static if (SUPPORT_LEGACY_OPENGL) {
915                 glEnable(GL_TEXTURE_2D);
916                 texture.setup();
917                 texture.setSamplerParams(linear);
918 
919                 glEnableClientState(GL_COLOR_ARRAY);
920                 glEnableClientState(GL_VERTEX_ARRAY);
921                 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
922                 glVertexPointer(3, GL_FLOAT, 0, cast(void*)_queue._vertices.data.ptr);
923                 glTexCoordPointer(2, GL_FLOAT, 0, cast(void*)_queue._texCoords.data.ptr);
924                 glColorPointer(4, GL_FLOAT, 0, cast(void*)_queue._colors.data.ptr);
925 
926                 checkgl!glDrawElements(GL_TRIANGLES, cast(int)length, GL_UNSIGNED_INT, cast(void*)(_queue._indices.data[start .. start + length].ptr));
927 
928                 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
929                 glDisableClientState(GL_VERTEX_ARRAY);
930                 glDisableClientState(GL_COLOR_ARRAY);
931                 glDisable(GL_TEXTURE_2D);
932             }
933         } else {
934             _textureProgram.drawBatch(texture, linear, length, start);
935         }
936     }
937 
938     /// call glFlush
939     void flushGL() {
940         // TODO: Is this really needed?
941         // checkgl!glFlush();
942     }
943 
944     bool generateMipmap(int dx, int dy, ubyte * pixels, int level, ref ubyte[] dst) {
945         if ((dx & 1) || (dy & 1) || dx < 2 || dy < 2)
946             return false; // size is not even
947         int newdx = dx / 2;
948         int newdy = dy / 2;
949         int newlen = newdx * newdy * 4;
950         if (newlen > dst.length)
951             dst.length = newlen;
952         ubyte * dstptr = dst.ptr;
953         ubyte * srcptr = pixels;
954         int srcstride = dx * 4;
955         for (int y = 0; y < newdy; y++) {
956             for (int x = 0; x < newdx; x++) {
957                 dstptr[0] = cast(ubyte)((srcptr[0+0] + srcptr[0+4] + srcptr[0+srcstride] + srcptr[0+srcstride + 4])>>2);
958                 dstptr[1] = cast(ubyte)((srcptr[1+0] + srcptr[1+4] + srcptr[1+srcstride] + srcptr[1+srcstride + 4])>>2);
959                 dstptr[2] = cast(ubyte)((srcptr[2+0] + srcptr[2+4] + srcptr[2+srcstride] + srcptr[2+srcstride + 4])>>2);
960                 dstptr[3] = cast(ubyte)((srcptr[3+0] + srcptr[3+4] + srcptr[3+srcstride] + srcptr[3+srcstride + 4])>>2);
961                 dstptr += 4;
962                 srcptr += 8;
963             }
964             srcptr += srcstride; // skip srcline
965         }
966         checkgl!glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, newdx, newdy, 0, GL_RGBA, GL_UNSIGNED_BYTE, dst.ptr);
967         return true;
968     }
969 
970     bool setTextureImage(Tex2D texture, int dx, int dy, ubyte * pixels, int mipmapLevels = 0) {
971         checkError("before setTextureImage");
972         texture.bind();
973         checkgl!glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
974         texture.setSamplerParams(true, true);
975 
976         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
977         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, mipmapLevels > 0 ? mipmapLevels - 1 : 0);
978         // ORIGINAL: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, dx, dy, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
979         checkgl!glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, dx, dy, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
980         if (checkError("updateTexture - glTexImage2D")) {
981             Log.e("Cannot set image for texture");
982             return false;
983         }
984         if (mipmapLevels > 1) {
985             ubyte[] buffer;
986             ubyte * src = pixels;
987             int ndx = dx;
988             int ndy = dy;
989             for (int i = 1; i < mipmapLevels; i++) {
990                 if (!generateMipmap(ndx, ndy, src, i, buffer))
991                     break;
992                 ndx /= 2;
993                 ndy /= 2;
994                 src = buffer.ptr;
995             }
996         }
997         texture.unbind();
998         return true;
999     }
1000 
1001     bool setTextureImageAlpha(Tex2D texture, int dx, int dy, ubyte * pixels) {
1002         checkError("before setTextureImageAlpha");
1003         texture.bind();
1004         checkgl!glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
1005         texture.setSamplerParams(true, true);
1006 
1007         glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, dx, dy, 0, GL_ALPHA, GL_UNSIGNED_BYTE, pixels);
1008         if (checkError("setTextureImageAlpha - glTexImage2D")) {
1009             Log.e("Cannot set image for texture");
1010             return false;
1011         }
1012         texture.unbind();
1013         return true;
1014     }
1015 
1016     private FBO currentFBO;
1017 
1018     /// returns texture for buffer, null if failed
1019     bool createFramebuffer(out Tex2D texture, out FBO fbo, int dx, int dy) {
1020         checkError("before createFramebuffer");
1021         bool res = true;
1022         texture = new Tex2D();
1023         if (!texture.ID)
1024             return false;
1025         checkError("glBindTexture GL_TEXTURE_2D");
1026         FBO f = new FBO();
1027         if (!f.ID)
1028             return false;
1029         fbo = f;
1030 
1031         checkgl!glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, dx, dy, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, null);
1032 
1033         texture.setSamplerParams(true, true);
1034 
1035         checkgl!glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.ID, 0);
1036         // Always check that our framebuffer is ok
1037         if(checkgl!glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
1038             Log.e("glFramebufferTexture2D failed");
1039             res = false;
1040         }
1041         checkgl!glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
1042         checkgl!glClear(GL_COLOR_BUFFER_BIT);
1043         currentFBO = fbo;
1044 
1045         texture.unbind();
1046         fbo.unbind();
1047 
1048         return res;
1049     }
1050 
1051     void deleteFramebuffer(ref FBO fbo) {
1052         if (fbo.ID != 0) {
1053             destroy(fbo);
1054         }
1055         currentFBO = null;
1056     }
1057 
1058     bool bindFramebuffer(FBO fbo) {
1059         fbo.bind();
1060         currentFBO = fbo;
1061         return !checkError("glBindFramebuffer");
1062     }
1063 
1064     void clearDepthBuffer() {
1065         glClear(GL_DEPTH_BUFFER_BIT);
1066         //glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
1067     }
1068 
1069     /// projection matrix
1070     /// current gl buffer width
1071     private int bufferDx;
1072     /// current gl buffer height
1073     private int bufferDy;
1074     private mat4 _projectionMatrix;
1075 
1076     @property ref mat4 projectionMatrix() {
1077         return _projectionMatrix;
1078     }
1079 
1080     void setOrthoProjection(Rect windowRect, Rect view) {
1081         flushGL();
1082         bufferDx = windowRect.width;
1083         bufferDy = windowRect.height;
1084         _projectionMatrix.setOrtho(view.left, view.right, view.top, view.bottom, 0.5f, 50.0f);
1085 
1086         if (_legacyMode) {
1087             static if (SUPPORT_LEGACY_OPENGL) {
1088                 glMatrixMode(GL_PROJECTION);
1089                 //checkgl!glPushMatrix();
1090                 //glLoadIdentity();
1091                 glLoadMatrixf(_projectionMatrix.m.ptr);
1092                 //glOrthof(0, _dx, 0, _dy, -1.0f, 1.0f);
1093                 glMatrixMode(GL_MODELVIEW);
1094                 //checkgl!glPushMatrix();
1095                 glLoadIdentity();
1096             }
1097         }
1098         checkgl!glViewport(view.left, currentFBO ? view.top : windowRect.height - view.bottom, view.width, view.height);
1099     }
1100 
1101     void setPerspectiveProjection(Rect windowRect, Rect view, float fieldOfView, float nearPlane, float farPlane) {
1102         flushGL();
1103         bufferDx = windowRect.width;
1104         bufferDy = windowRect.height;
1105         float aspectRatio = cast(float)view.width / cast(float)view.height;
1106         _projectionMatrix.setPerspective(fieldOfView, aspectRatio, nearPlane, farPlane);
1107         if (_legacyMode) {
1108             static if (SUPPORT_LEGACY_OPENGL) {
1109                 glMatrixMode(GL_PROJECTION);
1110                 //checkgl!glPushMatrix();
1111                 //glLoadIdentity();
1112                 glLoadMatrixf(_projectionMatrix.m.ptr);
1113                 //glOrthof(0, _dx, 0, _dy, -1.0f, 1.0f);
1114                 glMatrixMode(GL_MODELVIEW);
1115                 //checkgl!glPushMatrix();
1116                 glLoadIdentity();
1117             }
1118         }
1119         checkgl!glViewport(view.left, currentFBO ? view.top : windowRect.height - view.bottom, view.width, view.height);
1120     }
1121 }
1122 
1123 enum GLObjectTypes { Buffer, VertexArray, Texture, Framebuffer };
1124 /** RAII OpenGL object template.
1125   * Note: on construction it binds itself to the target, and it binds 0 to target on destruction.
1126   * All methods (except ctor, dtor, bind(), unbind() and setup()) does not perform binding.
1127 */
1128 
1129 
1130 class GLObject(GLObjectTypes type, GLuint target = 0) {
1131     immutable GLuint ID;
1132     //alias ID this; // good, but it confuses destroy()
1133 
1134     this() {
1135         GLuint handle;
1136         mixin("checkgl!glGen" ~ to!string(type) ~ "s(1, &handle);");
1137         ID = handle;
1138         bind();
1139     }
1140 
1141     ~this() {
1142         if (!glNoContext) {
1143             unbind();
1144             mixin("checkgl!glDelete" ~ to!string(type) ~ "s(1, &ID);");
1145         }
1146     }
1147 
1148     void bind() {
1149         static if(target != 0)
1150             mixin("glBind" ~ to!string(type) ~ "(" ~ to!string(target) ~ ", ID);");
1151         else
1152             mixin("glBind" ~ to!string(type) ~ "(ID);");
1153     }
1154 
1155     static void unbind() {
1156         static if(target != 0)
1157             mixin("checkgl!glBind" ~ to!string(type) ~ "(" ~ to!string(target) ~ ", 0);");
1158         else
1159             mixin("checkgl!glBind" ~ to!string(type) ~ "(0);");
1160     }
1161 
1162     static if(type == GLObjectTypes.Buffer)
1163     {
1164         void fill(float[][] buffs) {
1165             int length;
1166             foreach(b; buffs)
1167                 length += b.length;
1168             checkgl!glBufferData(target,
1169                          length * float.sizeof,
1170                          null,
1171                          GL_STATIC_DRAW);
1172             int offset;
1173             foreach(b; buffs) {
1174                 checkgl!glBufferSubData(target,
1175                                 offset,
1176                                 b.length * float.sizeof,
1177                                 b.ptr);
1178                 offset += b.length * float.sizeof;
1179             }
1180         }
1181 
1182         static if (target == GL_ELEMENT_ARRAY_BUFFER) {
1183             void fill(int[] indexes) {
1184                 checkgl!glBufferData(target, cast(int)(indexes.length * int.sizeof), indexes.ptr, GL_STATIC_DRAW);
1185             }
1186         }
1187     }
1188 
1189     static if(type == GLObjectTypes.Texture)
1190     {
1191         void setSamplerParams(bool linear, bool clamp = false, bool mipmap = false) {
1192             glTexParameteri(target, GL_TEXTURE_MAG_FILTER, linear ? GL_LINEAR : GL_NEAREST);
1193             glTexParameteri(target, GL_TEXTURE_MIN_FILTER, linear ?
1194                             (!mipmap ? GL_LINEAR : GL_LINEAR_MIPMAP_LINEAR) :
1195                             (!mipmap ? GL_NEAREST : GL_NEAREST_MIPMAP_NEAREST)); //GL_NEAREST_MIPMAP_NEAREST
1196             checkError("filtering - glTexParameteri");
1197             if(clamp) {
1198                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1199                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1200                 checkError("clamp - glTexParameteri");
1201             }
1202         }
1203 
1204         void setup(GLuint binding = 0) {
1205             glActiveTexture(GL_TEXTURE0 + binding);
1206             glBindTexture(target, ID);
1207             checkError("setup texture");
1208         }
1209     }
1210 }
1211 
1212 alias VAO = GLObject!(GLObjectTypes.VertexArray);
1213 alias VBO = GLObject!(GLObjectTypes.Buffer, GL_ARRAY_BUFFER);
1214 alias EBO = GLObject!(GLObjectTypes.Buffer, GL_ELEMENT_ARRAY_BUFFER);
1215 alias Tex2D = GLObject!(GLObjectTypes.Texture, GL_TEXTURE_2D);
1216 alias FBO = GLObject!(GLObjectTypes.Framebuffer, GL_FRAMEBUFFER);
1217 
1218 class GLVertexBuffer : VertexBuffer {
1219     protected VertexFormat _format;
1220     protected IndexFragment[] _indexFragments;
1221     protected int _vertexCount;
1222     protected GLuint _vertexBuffer;
1223     protected GLuint _indexBuffer;
1224     protected GLuint _vao;
1225 
1226     this() {
1227     version (Android) {
1228             checkgl!glGenBuffers(1, &_vertexBuffer);
1229             checkgl!glGenBuffers(1, &_indexBuffer);
1230             checkgl!glGenVertexArrays(1, &_vao);
1231     } else {
1232             assertgl!glGenBuffers(1, &_vertexBuffer);
1233             assertgl!glGenBuffers(1, &_indexBuffer);
1234             assertgl!glGenVertexArrays(1, &_vao);
1235     }
1236     }
1237 
1238     ~this() {
1239         checkgl!glDeleteBuffers(1, &_vertexBuffer);
1240         checkgl!glDeleteBuffers(1, &_indexBuffer);
1241         checkgl!glDeleteVertexArrays(1, &_vao);
1242     }
1243 
1244     ///// bind into current context
1245     //override void bind() {
1246     //    checkgl!glBindVertexArray(_vao);
1247     //
1248     //    // TODO: is it necessary to bind vertex/index buffers?
1249     //    // specify vertex buffer
1250     //    checkgl!glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
1251     //    // specify index buffer
1252     //    checkgl!glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer);
1253     //}
1254     //
1255     ///// unbind from current context
1256     //override void unbind() {
1257     //    checkgl!glBindVertexArray(0);
1258     //    checkgl!glBindBuffer(GL_ARRAY_BUFFER, 0);
1259     //    checkgl!glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
1260     //}
1261 
1262     /// update vertex element locations for effect/shader program
1263     void enableAttributes(GraphicsEffect effect) {
1264         checkgl!glBindVertexArray(_vao);
1265         // specify vertex buffer
1266         checkgl!glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
1267         // specify index buffer
1268         checkgl!glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer);
1269         int offset = 0;
1270         //Log.v("=== enableAttributes for ", _format);
1271         for(int i = 0; i < _format.length; i++) {
1272             int loc = effect.getVertexElementLocation(_format[i].type);
1273             if (loc >= 0) {
1274                 //Log.v("setting attrib pointer for type ", _format[i].type, " offset=", offset, " location=", loc);
1275                 checkgl!glVertexAttribPointer(loc, _format[i].size, GL_FLOAT, cast(ubyte)GL_FALSE, _format.vertexSize, cast(char*)(offset));
1276                 checkgl!glEnableVertexAttribArray(loc);
1277             } else {
1278                 //Log.v("Attribute location not found for ", _format[i].type);
1279             }
1280             offset += _format[i].byteSize;
1281         }
1282     }
1283 
1284     void disableAttributes(GraphicsEffect effect) {
1285         for(int i = 0; i < _format.length; i++) {
1286             int loc = effect.getVertexElementLocation(_format[i].type);
1287             if (loc >= 0) {
1288                 checkgl!glDisableVertexAttribArray(loc);
1289             }
1290         }
1291         checkgl!glBindVertexArray(0);
1292         checkgl!glBindBuffer(GL_ARRAY_BUFFER, 0);
1293         checkgl!glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
1294         //unbind();
1295     }
1296 
1297     /// set or change data
1298     override void setData(Mesh mesh) {
1299         _format = mesh.vertexFormat;
1300         _indexFragments = mesh.indexFragments;
1301         _vertexCount = mesh.vertexCount;
1302         const(ushort[]) indexData = mesh.indexData;
1303 
1304         Log.d("GLVertexBuffer.setData vertex data size=", mesh.vertexData.length, " index data size=", indexData.length, " vertex count=", _vertexCount, " indexBuffer=", _indexBuffer, " vertexBuffer=", _vertexBuffer, " vao=", _vao);
1305 
1306         // vertex buffer
1307         checkgl!glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
1308         checkgl!glBufferData(GL_ARRAY_BUFFER, _format.vertexSize * mesh.vertexCount, mesh.vertexData.ptr, GL_STATIC_DRAW);
1309         checkgl!glBindBuffer(GL_ARRAY_BUFFER, 0);
1310         // index buffer
1311         checkgl!glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer);
1312         checkgl!glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexData.length * ushort.sizeof, indexData.ptr, GL_STATIC_DRAW);
1313         checkgl!glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
1314         // vertex layout
1315         //checkgl!glBindVertexArray(_vao);
1316         // specify vertex buffer
1317         //checkgl!glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
1318         // specify index buffer
1319         //checkgl!glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer);
1320 
1321         //unbind();
1322     }
1323 
1324     /// draw mesh using specified effect
1325     override void draw(GraphicsEffect effect, bool wireframe) {
1326         //bind();
1327         enableAttributes(effect);
1328         foreach (fragment; _indexFragments) {
1329             if (wireframe && fragment.type == PrimitiveType.triangles) {
1330                 // TODO: support wireframe not only for triangles
1331                 int triangleCount = fragment.end - fragment.start;
1332                 //triangleCount /= 3;
1333                 //checkgl!glDisable(GL_CULL_FACE);
1334                 for (int i = 0; i < triangleCount; i += 3) {
1335                     // GL line loop works strange; use GL_LINES instead
1336                     checkgl!glDrawRangeElements(GL_LINE_LOOP, //GL_TRIANGLES,
1337                                 0, _vertexCount - 1, // The first to last vertex  start, end
1338                                 3, // count of indexes used to draw elements
1339                                 GL_UNSIGNED_SHORT,
1340                                 cast(char*)((fragment.start + i) * short.sizeof) // offset from index buffer beginning to fragment start
1341                                     );
1342                     //checkgl!glDrawRangeElements(GL_LINES, //GL_TRIANGLES,
1343                     //            0, _vertexCount - 1, // The first to last vertex  start, end
1344                     //            2, // count of indexes used to draw elements
1345                     //            GL_UNSIGNED_SHORT,
1346                     //            cast(char*)((fragment.start + i + 1) * short.sizeof) // offset from index buffer beginning to fragment start
1347                     //                );
1348                 }
1349                 //checkgl!glEnable(GL_CULL_FACE);
1350             } else {
1351                 checkgl!glDrawRangeElements(primitiveTypeToGL(fragment.type),
1352                         0, _vertexCount - 1, // The first to last vertex
1353                         fragment.end - fragment.start, // count of indexes used to draw elements
1354                         GL_UNSIGNED_SHORT,
1355                         cast(char*)(fragment.start * short.sizeof) // offset from index buffer beginning to fragment start
1356                 );
1357             }
1358         }
1359         disableAttributes(effect);
1360         //unbind();
1361     }
1362 }
1363 
1364 class DummyVertexBuffer : VertexBuffer {
1365     protected VertexFormat _format;
1366     protected IndexFragment[] _indexFragments;
1367     protected int _vertexCount;
1368     protected const(float)[] _vertexData;
1369     protected const(ushort)[] _indexData;
1370 
1371     this() {
1372     }
1373 
1374     ~this() {
1375     }
1376 
1377     ///// bind into current context
1378     //override void bind() {
1379     //}
1380     //
1381     ///// unbind from current context
1382     //override void unbind() {
1383     //}
1384 
1385     /// update vertex element locations for effect/shader program
1386     void enableAttributes(GraphicsEffect effect) {
1387         int offset = 0;
1388         for(int i = 0; i < _format.length; i++) {
1389             int loc = effect.getVertexElementLocation(_format[i].type);
1390             if (loc >= 0) {
1391                 checkgl!glVertexAttribPointer(loc, _format[i].size, GL_FLOAT, cast(ubyte)GL_FALSE, _format.vertexSize, cast(char*)(offset));
1392                 checkgl!glEnableVertexAttribArray(loc);
1393             } else {
1394                 //Log.d("Attribute location not found for ", _format[i].type);
1395             }
1396             offset += _format[i].byteSize;
1397         }
1398     }
1399 
1400     void disableAttributes(GraphicsEffect effect) {
1401         for(int i = 0; i < _format.length; i++) {
1402             int loc = effect.getVertexElementLocation(_format[i].type);
1403             if (loc >= 0) {
1404                 checkgl!glDisableVertexAttribArray(loc);
1405             }
1406         }
1407     }
1408 
1409     /// set or change data
1410     override void setData(Mesh mesh) {
1411         _format = mesh.vertexFormat;
1412         _indexFragments = mesh.indexFragments;
1413         _vertexCount = mesh.vertexCount;
1414         _vertexData = mesh.vertexData;
1415         _indexData = mesh.indexData;
1416     }
1417 
1418     /// draw mesh using specified effect
1419     override void draw(GraphicsEffect effect, bool wireframe) {
1420         //bind();
1421         enableAttributes(effect);
1422         foreach (fragment; _indexFragments) {
1423             // TODO: support wireframe
1424             checkgl!glDrawRangeElements(primitiveTypeToGL(fragment.type),
1425                                         0, _vertexCount,
1426                                         fragment.end - fragment.start,
1427                                         GL_UNSIGNED_SHORT, cast(char*)(fragment.start * short.sizeof));
1428         }
1429         disableAttributes(effect);
1430         //unbind();
1431     }
1432 }
1433 
1434 GLenum primitiveTypeToGL(PrimitiveType type) {
1435     switch(type) with (PrimitiveType) {
1436         case triangles:
1437             return GL_TRIANGLES;
1438         case triangleStripes:
1439             return GL_TRIANGLE_STRIP;
1440         case lines:
1441             return GL_LINES;
1442         case lineStripes:
1443             return GL_LINE_STRIP;
1444         case points:
1445         default:
1446             return GL_POINTS;
1447     }
1448 }
1449 
1450 
1451 
1452 /// OpenGL GUI rendering queue. It collects gui draw calls, fills a big buffer for vertex data and draws everything
1453 private final class OpenGLQueue {
1454 
1455     /// OpenGL batch structure - to draw several triangles in single OpenGL call
1456     private struct OpenGLBatch {
1457 
1458         enum BatchType { Line = 0, Rect, Triangle, TexturedRect }
1459         BatchType type;
1460 
1461         Tex2D texture;
1462         int textureDx;
1463         int textureDy;
1464         bool textureLinear;
1465 
1466         // length of batch in indices
1467         int length;
1468         // offset in index buffer
1469         int start;
1470     }
1471 
1472     import std.array: Appender;
1473     Appender!(OpenGLBatch[]) batches;
1474     // a big buffer
1475     Appender!(float[]) _vertices;
1476     Appender!(float[]) _colors;
1477     Appender!(float[]) _texCoords;
1478     Appender!(int[]) _indices;
1479 
1480     /// draw all
1481     void flush() {
1482         glSupport.fillBuffers(_vertices.data, _colors.data, _texCoords.data, _indices.data);
1483         foreach(b; batches.data) {
1484             switch(b.type) with(OpenGLBatch.BatchType)
1485             {
1486                 case Line:          glSupport.drawLines(b.length, b.start); break;
1487                 case Rect:          glSupport.drawSolidFillTriangles(b.length, b.start); break;
1488                 case Triangle:      glSupport.drawSolidFillTriangles(b.length, b.start); break;
1489                 case TexturedRect:  glSupport.drawColorAndTextureTriangles(b.texture, b.textureLinear, b.length, b.start); break;
1490                 default: break;
1491             }
1492         }
1493         //Log.d(batches.length, " ", _vertices.data.length, " ", _colors.data.length, " ", _texCoords.data.length, " ", _indices.data.length);
1494         glSupport.destroyBuffers();
1495         batches.clear;
1496         _vertices.clear;
1497         _colors.clear;
1498         _texCoords.clear;
1499         _indices.clear;
1500     }
1501 
1502     static immutable float Z_2D = -2.0f;
1503 
1504     /// add textured rectangle to queue
1505     void addTexturedRect(Tex2D texture, int textureDx, int textureDy, uint color1, uint color2, uint color3, uint color4, Rect srcrc, Rect dstrc, bool linear) {
1506         if (batches.data.length == 0
1507             || batches.data[$-1].type != OpenGLBatch.BatchType.TexturedRect
1508             || batches.data[$-1].texture.ID != texture.ID
1509             || batches.data[$-1].textureLinear != linear)
1510         {
1511             batches ~= OpenGLBatch();
1512             batches.data[$-1].type = OpenGLBatch.BatchType.TexturedRect;
1513             batches.data[$-1].texture = texture;
1514             batches.data[$-1].textureDx = textureDx;
1515             batches.data[$-1].textureDy = textureDy;
1516             batches.data[$-1].textureLinear = linear;
1517             if(batches.data.length > 1)
1518                 batches.data[$-1].start = batches.data[$-2].start + batches.data[$-2].length;
1519         }
1520 
1521         uint[4] colorsARGB = [color1, color2, color3, color4];
1522         float[] colors = convertColors(colorsARGB);
1523 
1524         float dstx0 = cast(float)dstrc.left;
1525         float dsty0 = cast(float)(glSupport.currentFBO ? dstrc.top : (glSupport.bufferDy - dstrc.top));
1526         float dstx1 = cast(float)dstrc.right;
1527         float dsty1 = cast(float)(glSupport.currentFBO ? dstrc.bottom : (glSupport.bufferDy - dstrc.bottom));
1528 
1529         float srcx0 = srcrc.left / cast(float)textureDx;
1530         float srcy0 = srcrc.top / cast(float)textureDy;
1531         float srcx1 = srcrc.right / cast(float)textureDx;
1532         float srcy1 = srcrc.bottom / cast(float)textureDy;
1533 
1534         float[3 * 4] vertices = [
1535             dstx0,dsty0,Z_2D,
1536             dstx0,dsty1,Z_2D,
1537             dstx1,dsty0,Z_2D,
1538             dstx1,dsty1,Z_2D ];
1539 
1540         float[2 * 4] texCoords = [srcx0,srcy0, srcx0,srcy1, srcx1,srcy0, srcx1,srcy1];
1541 
1542         enum verts = 4;
1543         mixin(add);
1544     }
1545 
1546     /// add solid rectangle to queue
1547     void addSolidRect(Rect dstRect, uint color) {
1548         addGradientRect(dstRect, color, color, color, color);
1549     }
1550 
1551     /// add gradient rectangle to queue
1552     void addGradientRect(Rect rc, uint color1, uint color2, uint color3, uint color4) {
1553         if (batches.data.length == 0 || batches.data[$-1].type != OpenGLBatch.BatchType.Rect) {
1554             batches ~= OpenGLBatch();
1555             batches.data[$-1].type = OpenGLBatch.BatchType.Rect;
1556             if(batches.data.length > 1)
1557                 batches.data[$-1].start = batches.data[$-2].start + batches.data[$-2].length;
1558         }
1559 
1560         uint[4] colorsARGB = [color1, color2, color3, color4];
1561         float[] colors = convertColors(colorsARGB);
1562 
1563         float x0 = cast(float)(rc.left);
1564         float y0 = cast(float)(glSupport.currentFBO ? rc.top : (glSupport.bufferDy - rc.top));
1565         float x1 = cast(float)(rc.right);
1566         float y1 = cast(float)(glSupport.currentFBO ? rc.bottom : (glSupport.bufferDy - rc.bottom));
1567 
1568         float[3 * 4] vertices = [
1569             x0,y0,Z_2D,
1570             x0,y1,Z_2D,
1571             x1,y0,Z_2D,
1572             x1,y1,Z_2D ];
1573         // fill texture coords buffer with zeros
1574         float[2 * 4] texCoords = 0;
1575 
1576         enum verts = 4;
1577         mixin(add);
1578     }
1579 
1580     /// add triangle to queue
1581     void addTriangle(PointF p1, PointF p2, PointF p3, uint color1, uint color2, uint color3) {
1582         if (batches.data.length == 0 || batches.data[$-1].type != OpenGLBatch.BatchType.Triangle) {
1583             batches ~= OpenGLBatch();
1584             batches.data[$-1].type = OpenGLBatch.BatchType.Triangle;
1585             if(batches.data.length > 1)
1586                 batches.data[$-1].start = batches.data[$-2].start + batches.data[$-2].length;
1587         }
1588 
1589         uint[3] colorsARGB = [color1, color2, color3];
1590         float[] colors = convertColors(colorsARGB);
1591 
1592         float x0 = p1.x;
1593         float y0 = glSupport.currentFBO ? p1.y : (glSupport.bufferDy - p1.y);
1594         float x1 = p2.x;
1595         float y1 = glSupport.currentFBO ? p2.y : (glSupport.bufferDy - p2.y);
1596         float x2 = p3.x;
1597         float y2 = glSupport.currentFBO ? p3.y : (glSupport.bufferDy - p3.y);
1598 
1599         float[3 * 3] vertices = [
1600             x0,y0,Z_2D,
1601             x1,y1,Z_2D,
1602             x2,y2,Z_2D ];
1603         // fill texture coords buffer with zeros
1604         float[2 * 3] texCoords = 0;
1605 
1606         enum verts = 3;
1607         mixin(add);
1608     }
1609 
1610     /// add line to queue
1611     /// rc is a line (left, top) - (right, bottom)
1612     void addLine(Rect rc, uint color1, uint color2) {
1613         if (batches.data.length == 0 || batches.data[$-1].type != OpenGLBatch.BatchType.Line) {
1614             batches ~= OpenGLBatch();
1615             batches.data[$-1].type = OpenGLBatch.BatchType.Line;
1616             if(batches.data.length > 1)
1617                 batches.data[$-1].start = batches.data[$-2].start + batches.data[$-2].length;
1618         }
1619 
1620         uint[2] colorsARGB = [color1, color2];
1621         float[] colors = convertColors(colorsARGB);
1622 
1623         float x0 = cast(float)(rc.left);
1624         float y0 = cast(float)(glSupport.currentFBO ? rc.top : (glSupport.bufferDy - rc.top));
1625         float x1 = cast(float)(rc.right);
1626         float y1 = cast(float)(glSupport.currentFBO ? rc.bottom : (glSupport.bufferDy - rc.bottom));
1627 
1628         float[3 * 2] vertices = [
1629             x0, y0, Z_2D,
1630             x1, y1, Z_2D ];
1631         // fill texture coords buffer with zeros
1632         float[2 * 2] texCoords = 0;
1633 
1634         enum verts = 2;
1635         mixin(add);
1636     }
1637 
1638     enum add = q{
1639         int offset = cast(int)_vertices.data.length / 3;
1640         static if(verts == 4) {
1641             // make indices for rectangle (2 triangles == 6 vertexes per rect)
1642             int[6] indices = [
1643                 offset + 0,
1644                 offset + 1,
1645                 offset + 2,
1646                 offset + 1,
1647                 offset + 2,
1648                 offset + 3 ];
1649         } else
1650         static if(verts == 3) {
1651             // make indices for triangles
1652             int[3] indices = [
1653                 offset + 0,
1654                 offset + 1,
1655                 offset + 2 ];
1656         } else
1657         static if(verts == 2) {
1658             // make indices for lines
1659             int[2] indices = [
1660                 offset + 0,
1661                 offset + 1 ];
1662         } else
1663             static assert(0);
1664 
1665         batches.data[$-1].length += cast(int)indices.length;
1666 
1667         _vertices ~= cast(float[])vertices;
1668         _colors ~= cast(float[])colors;
1669         _texCoords ~= cast(float[])texCoords;
1670         _indices ~= cast(int[])indices;
1671     };
1672 }
1673 }