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