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