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