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