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