1 /** @file dgl_draw.cpp  Drawing operations and vertex arrays.
2  *
3  * Emulates OpenGL 1.x drawing for legacy code.
4  *
5  * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
6  * @authors Copyright © 2007-2015 Daniel Swanson <danij@dengine.net>
7  *
8  * @par License
9  * GPL: http://www.gnu.org/licenses/gpl.html
10  *
11  * <small>This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by the
13  * Free Software Foundation; either version 2 of the License, or (at your
14  * option) any later version. This program is distributed in the hope that it
15  * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
16  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
17  * Public License for more details. You should have received a copy of the GNU
18  * General Public License along with this program; if not, see:
19  * http://www.gnu.org/licenses</small>
20  */
21 
22 #include "de_base.h"
23 #include "gl/gl_main.h"
24 
25 #include <cstdio>
26 #include <cstdlib>
27 #include <de/concurrency.h>
28 #include <de/GLInfo>
29 #include <de/GLBuffer>
30 #include <de/GLState>
31 #include <de/GLProgram>
32 #include <de/Matrix>
33 #include "sys_system.h"
34 #include "gl/gl_draw.h"
35 #include "gl/sys_opengl.h"
36 #include "clientapp.h"
37 
38 using namespace de;
39 
40 constexpr uint MAX_TEX_COORDS = 2;
41 constexpr int  MAX_BATCH      = 16;
42 
43 static unsigned s_drawCallCount = 0;
44 static unsigned s_primSwitchCount = 0;
45 static unsigned s_minBatchLength = 0;
46 static unsigned s_maxBatchLength = 0;
47 static unsigned s_totalBatchCount = 0;
48 
49 struct DGLDrawState
50 {
51     struct Vertex
52     {
53         float   vertex[3];
54         uint8_t color[4];
55         struct { float s, t; } texCoord[MAX_TEX_COORDS];
56         float   fragOffset[2];  // Multiplied by uFragmentOffset
57         float   batchIndex;
58     };
59 
60     // Indices for vertex attribute arrays.
61     enum {
62         VAA_VERTEX,
63         VAA_COLOR,
64         VAA_TEXCOORD0,
65         VAA_TEXCOORD1,
66         VAA_FRAG_OFFSET,
67         VAA_BATCH_INDEX,
68         NUM_VERTEX_ATTRIB_ARRAYS
69     };
70 
71     dglprimtype_t   primType      = DGL_NO_PRIMITIVE;
72     dglprimtype_t   batchPrimType = DGL_NO_PRIMITIVE;
73     int             primIndex     = 0;
74     duint           batchMaxSize;
75     duint           currentBatchIndex;
76     bool            resetPrimitive = false;
77     Vertex          currentVertex  = {
78         {0.f, 0.f, 0.f},
79         {255, 255, 255, 255},
80         {{0.f, 0.f}, {0.f, 0.f}},
81         {0.f, 0.f},
82         0.f};
83     Vertex          primVertices[4];
84     QVector<Vertex> vertices;
85 
86     struct GLData
87     {
88         GLProgram shader;
89 
90         GLState  batchState;
91         Matrix4f batchMvpMatrix[MAX_BATCH];
92         Matrix4f batchTexMatrix0[MAX_BATCH];
93         Matrix4f batchTexMatrix1[MAX_BATCH];
94         int      batchTexEnabled[MAX_BATCH];
95         int      batchTexMode[MAX_BATCH];
96         Vector4f batchTexModeColor[MAX_BATCH];
97         float    batchAlphaLimit[MAX_BATCH];
98         int      batchTexture0[MAX_BATCH];
99         int      batchTexture1[MAX_BATCH];
100 
101         // Batched uniforms:
102         GLUniform uMvpMatrix;
103         GLUniform uTexMatrix0;
104         GLUniform uTexMatrix1;
105         GLUniform uTexEnabled;
106         GLUniform uTexMode;
107         GLUniform uTexModeColor;
108         GLUniform uAlphaLimit;
109 
110         GLUniform uFragmentSize;
111         GLUniform uFogRange;
112         GLUniform uFogColor;
113 
114         struct DrawBuffer
115         {
116             GLuint   vertexArray = 0;
117             GLBuffer arrayData;
118 
releaseDGLDrawState::GLData::DrawBuffer119             void release()
120             {
121 #if defined (DENG_HAVE_VAOS)
122                 LIBGUI_GL.glDeleteVertexArrays(1, &vertexArray);
123 #endif
124                 arrayData.clear();
125             }
126         };
127 
GLDataDGLDrawState::GLData128         GLData(duint batchSize)
129             : uMvpMatrix    { "uMvpMatrix",    GLUniform::Mat4Array , batchSize }
130             , uTexMatrix0   { "uTexMatrix0",   GLUniform::Mat4Array , batchSize }
131             , uTexMatrix1   { "uTexMatrix1",   GLUniform::Mat4Array , batchSize }
132             , uTexEnabled   { "uTexEnabled",   GLUniform::IntArray  , batchSize }
133             , uTexMode      { "uTexMode",      GLUniform::IntArray  , batchSize }
134             , uTexModeColor { "uTexModeColor", GLUniform::Vec4Array , batchSize }
135             , uAlphaLimit   { "uAlphaLimit",   GLUniform::FloatArray, batchSize }
136             , uFragmentSize { "uFragmentSize", GLUniform::Vec2 }
137             , uFogRange     { "uFogRange",     GLUniform::Vec4 }
138             , uFogColor     { "uFogColor",     GLUniform::Vec4 }
139         {}
140 
141         QVector<DrawBuffer *> buffers;
142         int bufferPos = 0;
143     };
144     std::unique_ptr<GLData> gl;
145 
DGLDrawStateDGLDrawState146     DGLDrawState()
147     {
148         clearVertices();
149     }
150 
checkPrimitiveResetDGLDrawState151     void checkPrimitiveReset()
152     {
153         if (resetPrimitive)
154         {
155             DENG2_ASSERT(!vertices.empty());
156             DENG2_ASSERT(glPrimitive(batchPrimType) == GL_TRIANGLE_STRIP);
157 
158             // When committing multiple triangle strips, add a disconnection
159             // between batches.
160             vertices.push_back(vertices.back());
161             vertices.push_back(currentVertex);
162             resetPrimitive = false;
163         }
164     }
165 
commitLineDGLDrawState166     void commitLine(Vertex start, Vertex end)
167     {
168         const Vector2f lineDir = (Vector2f(end.vertex) - Vector2f(start.vertex)).normalize();
169         const Vector2f lineNormal{-lineDir.y, lineDir.x};
170 
171         const bool disjoint = !vertices.empty();
172         if (disjoint)
173         {
174             vertices.push_back(vertices.back());
175         }
176 
177         // Start cap.
178         {
179             start.fragOffset[0] = -lineNormal.x;
180             start.fragOffset[1] = -lineNormal.y;
181             vertices.push_back(start);
182             if (disjoint)
183             {
184                 vertices.push_back(start);
185             }
186             start.fragOffset[0] = lineNormal.x;
187             start.fragOffset[1] = lineNormal.y;
188             vertices.push_back(start);
189         }
190 
191         // End cap.
192         {
193             end.fragOffset[0] = -lineNormal.x;
194             end.fragOffset[1] = -lineNormal.y;
195             vertices.push_back(end);
196             end.fragOffset[0] = lineNormal.x;
197             end.fragOffset[1] = lineNormal.y;
198             vertices.push_back(end);
199         }
200     }
201 
commitVertexDGLDrawState202     void commitVertex()
203     {
204         currentVertex.batchIndex = float(currentBatchIndex);
205         ++primIndex;
206 
207         switch (primType)
208         {
209             case DGL_QUADS:
210                 primVertices[primIndex - 1] = currentVertex;
211                 if (primIndex == 4)
212                 {
213                     /* 4 vertices become 6:
214 
215                        0--1     0--1   5
216                        |  |      \ |   |\
217                        |  |  =>   \|   | \
218                        3--2        2   4--3  */
219 
220                     vertices.push_back(primVertices[0]);
221                     vertices.push_back(primVertices[1]);
222                     vertices.push_back(primVertices[2]);
223 
224                     vertices.push_back(primVertices[0]);
225                     vertices.push_back(primVertices[2]);
226                     vertices.push_back(primVertices[3]);
227 
228                     primIndex = 0;
229                 }
230                 break;
231 
232             case DGL_LINES:
233                 primVertices[primIndex - 1] = currentVertex;
234                 if (primIndex == 2)
235                 {
236                     commitLine(primVertices[0], primVertices[1]);
237                     primIndex = 0;
238                 }
239                 break;
240 
241             case DGL_LINE_LOOP:
242             case DGL_LINE_STRIP:
243                 if (primIndex == 1)
244                 {
245                     // Remember the first one for a loop.
246                     primVertices[0] = currentVertex;
247                 }
248                 if (primIndex > 1)
249                 {
250                     // Continue from the previous vertex.
251                     commitLine(primVertices[1], currentVertex);
252                 }
253                 primVertices[1] = currentVertex;
254                 break;
255 
256             case DGL_TRIANGLE_FAN:
257                 if (primIndex == 1)
258                 {
259                     if (!vertices.empty())
260                     {
261                         resetPrimitive = true;
262                     }
263                     checkPrimitiveReset();
264 
265                     // Fan origin.
266                     primVertices[0] = currentVertex;
267                 }
268                 else if (primIndex > 2)
269                 {
270                     vertices.push_back(primVertices[0]);
271                 }
272                 vertices.push_back(currentVertex);
273                 break;
274 
275             default:
276                 checkPrimitiveReset();
277                 vertices.push_back(currentVertex);
278                 break;
279         }
280     }
281 
clearPrimitiveDGLDrawState282     void clearPrimitive()
283     {
284         primIndex = 0;
285         primType  = DGL_NO_PRIMITIVE;
286     }
287 
clearVerticesDGLDrawState288     void clearVertices()
289     {
290         // currentVertex is unaffected.
291         vertices.clear();
292         clearPrimitive();
293         currentBatchIndex = 0;
294         resetPrimitive    = false;
295     }
296 
numVerticesDGLDrawState297     inline int numVertices() const
298     {
299         return vertices.size();
300     }
301 
colorFromFloatDGLDrawState302     static Vector4ub colorFromFloat(const Vector4f &color)
303     {
304         Vector4i rgba = (color * 255 + Vector4f(0.5f, 0.5f, 0.5f, 0.5f))
305                 .toVector4i()
306                 .max(Vector4i(0, 0, 0, 0))
307                 .min(Vector4i(255, 255, 255, 255));
308         return Vector4ub(dbyte(rgba.x), dbyte(rgba.y), dbyte(rgba.z), dbyte(rgba.w));
309     }
310 
beginPrimitiveDGLDrawState311     void beginPrimitive(dglprimtype_t primitive)
312     {
313         glInit();
314 
315         DENG2_ASSERT(primType == DGL_NO_PRIMITIVE);
316 
317         if (batchPrimType != DGL_NO_PRIMITIVE && !isCompatible(batchPrimType, primitive))
318         {
319             ++s_primSwitchCount;
320             flushBatches();
321         }
322         else
323         {
324             if (currentBatchIndex == MAX_BATCH - 1)
325             {
326                 flushBatches();
327             }
328         }
329 
330         // We enter a Begin/End section.
331         batchPrimType = primType = primitive;
332 
333         beginBatch();
334     }
335 
endPrimitiveDGLDrawState336     void endPrimitive()
337     {
338         if (primType != DGL_NO_PRIMITIVE && !vertices.empty())
339         {
340             if (primType == DGL_LINE_LOOP)
341             {
342                 // Close the loop.
343                 commitLine(currentVertex, primVertices[0]);
344             }
345             resetPrimitive = true;
346             DENG2_ASSERT(!vertices.empty());
347             endBatch();
348         }
349         clearPrimitive();
350     }
351 
getBoundTexturesDGLDrawState352     void getBoundTextures(int &id0, int &id1)
353     {
354         auto &GL = LIBGUI_GL;
355         GL.glActiveTexture(GL_TEXTURE0);
356         GL.glGetIntegerv(GL_TEXTURE_BINDING_2D, &id0);
357         GL.glActiveTexture(GL_TEXTURE1);
358         GL.glGetIntegerv(GL_TEXTURE_BINDING_2D, &id1);
359         GL.glActiveTexture(GLenum(GL_TEXTURE0 + DGL_GetInteger(DGL_ACTIVE_TEXTURE)));
360     }
361 
isLinePrimitiveDGLDrawState362     static inline bool isLinePrimitive(DGLenum p)
363     {
364         return p == DGL_LINES || p == DGL_LINE_STRIP || p == DGL_LINE_LOOP;
365     }
366 
isCompatibleDGLDrawState367     static inline bool isCompatible(DGLenum p1, DGLenum p2)
368     {
369         // Lines are not considered separate because they need the uFragmentSize uniform
370         // for calculating thickness offsets.
371         if (isLinePrimitive(p1) != isLinePrimitive(p2))
372         {
373             return false;
374         }
375         return glPrimitive(p1) == glPrimitive(p2);
376     }
377 
beginBatchDGLDrawState378     void beginBatch()
379     {
380         const duint idx = currentBatchIndex;
381 
382         auto &dynamicState = GLState::current();
383 
384         if (idx == 0)
385         {
386             gl->batchState = dynamicState;
387             zap(gl->batchTexture0);
388             zap(gl->batchTexture1);
389         }
390         else
391         {
392 #if defined (DENG2_DEBUG)
393             // GLState must not change while batches are begin collected.
394             // (Apart from the dynamic properties.)
395             GLState bat = gl->batchState;
396             GLState cur = dynamicState;
397             for (auto *st : {&bat, &cur})
398             {
399                 st->setAlphaLimit(0.f);
400                 st->setAlphaTest(false);
401             }
402             DENG2_ASSERT(bat == cur);
403 #endif
404         }
405 
406         gl->batchMvpMatrix[idx]  = DGL_Matrix(DGL_PROJECTION) * DGL_Matrix(DGL_MODELVIEW);
407         gl->batchTexMatrix0[idx] = DGL_Matrix(DGL_TEXTURE0);
408         gl->batchTexMatrix1[idx] = DGL_Matrix(DGL_TEXTURE1);
409         gl->batchTexEnabled[idx] =
410             (DGL_GetInteger(DGL_TEXTURE0) ? 0x1 : 0) | (DGL_GetInteger(DGL_TEXTURE1) ? 0x2 : 0);
411         gl->batchTexMode[idx]      = DGL_GetInteger(DGL_MODULATE_TEXTURE);
412         gl->batchTexModeColor[idx] = DGL_ModulationColor();
413         gl->batchAlphaLimit[idx]   = (dynamicState.alphaTest() ? dynamicState.alphaLimit() : -1.f);
414 
415         // TODO: There is no need to use OpenGL to remember the bound textures.
416         // However, all DGL textures must be bound via DGL_Bind and not directly via OpenGL.
417 
418         getBoundTextures(gl->batchTexture0[idx], gl->batchTexture1[idx]);
419     }
420 
endBatchDGLDrawState421     void endBatch()
422     {
423         currentBatchIndex++;
424         if (currentBatchIndex == batchMaxSize)
425         {
426             flushBatches();
427         }
428     }
429 
flushBatchesDGLDrawState430     void flushBatches()
431     {
432 #if defined (DENG2_DEBUG)
433         if (DGL_GetInteger(DGL_FLUSH_BACKTRACE))
434         {
435             DENG2_PRINT_BACKTRACE();
436         }
437 #endif
438         if (currentBatchIndex > 0)
439         {
440             drawBatches();
441         }
442         clearVertices();
443     }
444 
glInitDGLDrawState445     void glInit()
446     {
447         DENG_ASSERT_GL_CONTEXT_ACTIVE();
448 
449         if (!gl)
450         {
451             batchMaxSize = DGL_BatchMaxSize();
452 
453             gl.reset(new GLData(batchMaxSize));
454 
455             // Set up the shader.
456             ClientApp::shaders().build(gl->shader, "dgl.draw")
457                     << gl->uFragmentSize
458                     << gl->uMvpMatrix
459                     << gl->uTexMatrix0
460                     << gl->uTexMatrix1
461                     << gl->uTexEnabled
462                     << gl->uTexMode
463                     << gl->uTexModeColor
464                     << gl->uAlphaLimit
465                     << gl->uFogRange
466                     << gl->uFogColor;
467 
468             auto &GL = LIBGUI_GL;
469 
470             // Sampler uniforms.
471             {
472                 int samplers[2][MAX_BATCH];
473                 for (int i = 0; i < batchMaxSize; ++i)
474                 {
475                     samplers[0][i] = i;
476                     samplers[1][i] = batchMaxSize + i;
477                 }
478 
479                 auto prog = gl->shader.glName();
480                 GL.glUseProgram(prog);
481                 GL.glUniform1iv(GL.glGetUniformLocation(prog, "uTex0[0]"), GLsizei(batchMaxSize), samplers[0]);
482                 LIBGUI_ASSERT_GL_OK();
483                 GL.glUniform1iv(GL.glGetUniformLocation(prog, "uTex1[0]"), GLsizei(batchMaxSize), samplers[1]);
484                 LIBGUI_ASSERT_GL_OK();
485                 GL.glUseProgram(0);
486             }
487         }
488     }
489 
glDeinitDGLDrawState490     void glDeinit()
491     {
492         if (gl)
493         {
494             foreach (GLData::DrawBuffer *dbuf, gl->buffers)
495             {
496                 dbuf->release();
497                 delete dbuf;
498             }
499             gl.reset();
500         }
501     }
502 
nextBufferDGLDrawState503     GLData::DrawBuffer &nextBuffer()
504     {
505         if (gl->bufferPos == gl->buffers.size())
506         {
507             auto *dbuf = new GLData::DrawBuffer;
508 
509             // Vertex array object.
510 #if defined (DENG_HAVE_VAOS)
511             {
512                 auto &GL = LIBGUI_GL;
513                 GL.glGenVertexArrays(1, &dbuf->vertexArray);
514                 GL.glBindVertexArray(dbuf->vertexArray);
515                 for (uint i = 0; i < NUM_VERTEX_ATTRIB_ARRAYS; ++i)
516                 {
517                     GL.glEnableVertexAttribArray(i);
518                 }
519                 GL.glBindVertexArray(0);
520             }
521 #endif
522 
523             gl->buffers.append(dbuf);
524         }
525         return *gl->buffers[gl->bufferPos++];
526     }
527 
glBindArraysDGLDrawState528     void glBindArrays()
529     {
530         const uint stride = sizeof(Vertex);
531         auto &GL = LIBGUI_GL;
532 
533         // Upload the vertex data.
534         GLData::DrawBuffer &buf = nextBuffer();
535         buf.arrayData.setData(&vertices[0], sizeof(Vertex) * vertices.size(), gl::Dynamic);
536 
537 #if defined (DENG_HAVE_VAOS)
538         GL.glBindVertexArray(buf.vertexArray);
539 #else
540         for (uint i = 0; i < NUM_VERTEX_ATTRIB_ARRAYS; ++i)
541         {
542             GL.glEnableVertexAttribArray(i);
543         }
544 #endif
545         LIBGUI_ASSERT_GL_OK();
546 
547         GL.glBindBuffer(GL_ARRAY_BUFFER, buf.arrayData.glName());
548         LIBGUI_ASSERT_GL_OK();
549 
550         DENG2_ASSERT(GL.glGetAttribLocation(gl->shader.glName(), "aVertex") == VAA_VERTEX);
551         DENG2_ASSERT(GL.glGetAttribLocation(gl->shader.glName(), "aColor") == VAA_COLOR);
552         DENG2_ASSERT(GL.glGetAttribLocation(gl->shader.glName(), "aTexCoord") == VAA_TEXCOORD0);
553         DENG2_ASSERT(GL.glGetAttribLocation(gl->shader.glName(), "aFragOffset") == VAA_FRAG_OFFSET);
554         DENG2_ASSERT(GL.glGetAttribLocation(gl->shader.glName(), "aBatchIndex") == VAA_BATCH_INDEX);
555 
556         // Updated pointers.
557         GL.glVertexAttribPointer(VAA_VERTEX,      3, GL_FLOAT,         GL_FALSE, stride, DENG2_OFFSET_PTR(Vertex, vertex));
558         GL.glVertexAttribPointer(VAA_COLOR,       4, GL_UNSIGNED_BYTE, GL_TRUE,  stride, DENG2_OFFSET_PTR(Vertex, color));
559         GL.glVertexAttribPointer(VAA_TEXCOORD0,   2, GL_FLOAT,         GL_FALSE, stride, DENG2_OFFSET_PTR(Vertex, texCoord[0]));
560         GL.glVertexAttribPointer(VAA_TEXCOORD1,   2, GL_FLOAT,         GL_FALSE, stride, DENG2_OFFSET_PTR(Vertex, texCoord[1]));
561         GL.glVertexAttribPointer(VAA_FRAG_OFFSET, 2, GL_FLOAT,         GL_FALSE, stride, DENG2_OFFSET_PTR(Vertex, fragOffset[0]));
562         GL.glVertexAttribPointer(VAA_BATCH_INDEX, 1, GL_FLOAT,         GL_FALSE, stride, DENG2_OFFSET_PTR(Vertex, batchIndex));
563         LIBGUI_ASSERT_GL_OK();
564 
565         GL.glBindBuffer(GL_ARRAY_BUFFER, 0);
566     }
567 
glUnbindArraysDGLDrawState568     void glUnbindArrays()
569     {
570         auto &GL = LIBGUI_GL;
571 
572 #if defined (DENG_HAVE_VAOS)
573         GL.glBindVertexArray(0);
574 #else
575         for (uint i = 0; i < NUM_VERTEX_ATTRIB_ARRAYS; ++i)
576         {
577             GL.glDisableVertexAttribArray(i);
578             LIBGUI_ASSERT_GL_OK();
579         }
580 #endif
581     }
582 
glBindBatchTexturesDGLDrawState583     void glBindBatchTextures(duint count)
584     {
585         auto &GL = LIBGUI_GL;
586         for (duint i = 0; i < count; ++i)
587         {
588             GL.glActiveTexture(GLenum(GL_TEXTURE0 + i));
589             GL.glBindTexture(GL_TEXTURE_2D, GLuint(gl->batchTexture0[i]));
590             GL.glActiveTexture(GLenum(GL_TEXTURE0 + batchMaxSize + i));
591             GL.glBindTexture(GL_TEXTURE_2D, GLuint(gl->batchTexture1[i]));
592         }
593     }
594 
glPrimitiveDGLDrawState595     static GLenum glPrimitive(DGLenum primitive)
596     {
597         switch (primitive)
598         {
599         case DGL_POINTS:            return GL_POINTS;
600         case DGL_LINES:             return GL_TRIANGLE_STRIP;
601         case DGL_LINE_LOOP:         return GL_TRIANGLE_STRIP;
602         case DGL_LINE_STRIP:        return GL_TRIANGLE_STRIP;
603         case DGL_TRIANGLES:         return GL_TRIANGLES;
604         case DGL_TRIANGLE_FAN:      return GL_TRIANGLE_STRIP;
605         case DGL_TRIANGLE_STRIP:    return GL_TRIANGLE_STRIP;
606         case DGL_QUADS:             return GL_TRIANGLES;
607 
608         case DGL_NO_PRIMITIVE:      /*DENG2_ASSERT(!"No primitive type specified");*/ break;
609         }
610         return GL_NONE;
611     }
612 
613     /**
614      * Draws all the primitives currently stored in the vertex array.
615      */
drawBatchesDGLDrawState616     void drawBatches()
617     {
618         const auto batchLength = duint(currentBatchIndex);
619 
620         s_minBatchLength = de::min(s_minBatchLength, batchLength);
621         s_maxBatchLength = de::max(s_maxBatchLength, batchLength);
622         s_totalBatchCount += batchLength;
623 
624         // Batched uniforms.
625         gl->uMvpMatrix.set(gl->batchMvpMatrix, batchLength);
626         gl->uTexMatrix0.set(gl->batchTexMatrix0, batchLength);
627         gl->uTexMatrix1.set(gl->batchTexMatrix1, batchLength);
628         gl->uTexEnabled.set(gl->batchTexEnabled, batchLength);
629         gl->uTexMode.set(gl->batchTexMode, batchLength);
630         gl->uTexModeColor.set(gl->batchTexModeColor, batchLength);
631         gl->uAlphaLimit.set(gl->batchAlphaLimit, batchLength);
632 
633         const GLState &glState = gl->batchState;
634 
635         // Non-batched uniforms.
636         if (isLinePrimitive(batchPrimType))
637         {
638             // We can't draw a line thinner than one pixel.
639             const float lineWidth = std::max(.5f, GL_state.currentLineWidth);
640             gl->uFragmentSize     = Vector2f(lineWidth, lineWidth) / glState.target().size();
641         }
642         else
643         {
644             gl->uFragmentSize = Vector2f();
645         }
646         DGL_FogParams(gl->uFogRange, gl->uFogColor);
647 
648         auto &GL = LIBGUI_GL;
649 
650         glState.apply();
651 
652         int oldTex[2];
653         getBoundTextures(oldTex[0], oldTex[1]);
654 
655         glBindArrays();
656         gl->shader.beginUse();
657         glBindBatchTextures(batchLength);
658         DENG2_ASSERT(gl->shader.validate());
659         GL.glDrawArrays(glPrimitive(batchPrimType), 0, numVertices()); ++s_drawCallCount;
660         gl->shader.endUse();
661         LIBGUI_ASSERT_GL_OK();
662         glUnbindArrays();
663 
664         // Restore the previously bound OpenGL textures.
665         {
666             GL.glActiveTexture(GL_TEXTURE0);
667             GL.glBindTexture(GL_TEXTURE_2D, GLuint(oldTex[0]));
668             GL.glActiveTexture(GL_TEXTURE1);
669             GL.glBindTexture(GL_TEXTURE_2D, GLuint(oldTex[1]));
670             GL.glActiveTexture(GLenum(GL_TEXTURE0 + DGL_GetInteger(DGL_ACTIVE_TEXTURE)));
671         }
672     }
673 };
674 
675 static DGLDrawState dglDraw;
676 
DGL_BatchMaxSize()677 unsigned int DGL_BatchMaxSize()
678 {
679     auto &GL = LIBGUI_GL;
680 
681     // This determines how long DGL batch draws can be.
682     int maxFragSamplers;
683     GL.glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxFragSamplers);
684 
685     return duint(de::min(MAX_BATCH, de::max(8, maxFragSamplers / 2))); // DGL needs two per draw.
686 }
687 
DGL_Shutdown()688 void DGL_Shutdown()
689 {
690     dglDraw.glDeinit();
691 }
692 
DGL_BeginFrame()693 void DGL_BeginFrame()
694 {
695 //    qDebug() << "draw calls:" << s_drawCallCount << "prim switch:" << s_primSwitchCount
696 //             << "batch min/max/avg:" << s_minBatchLength << s_maxBatchLength
697 //             << (s_drawCallCount ? float(s_totalBatchCount) / float(s_drawCallCount) : 0.f);
698 
699     s_drawCallCount   = 0;
700     s_totalBatchCount = 0;
701     s_primSwitchCount = 0;
702     s_maxBatchLength  = 0;
703     s_minBatchLength  = std::numeric_limits<decltype(s_minBatchLength)>::max();
704 
705     if (dglDraw.gl)
706     {
707         // Reuse buffers every frame.
708         dglDraw.gl->bufferPos = 0;
709     }
710 }
711 
DGL_Flush()712 void DGL_Flush()
713 {
714     // Finish all batched draws.
715     dglDraw.flushBatches();
716 }
717 
DGL_CurrentColor(DGLubyte * rgba)718 void DGL_CurrentColor(DGLubyte *rgba)
719 {
720     std::memcpy(rgba, dglDraw.currentVertex.color, 4);
721 }
722 
DGL_CurrentColor(float * rgba)723 void DGL_CurrentColor(float *rgba)
724 {
725     Vector4f colorf = Vector4ub(dglDraw.currentVertex.color).toVector4f() / 255.0;
726     std::memcpy(rgba, colorf.constPtr(), sizeof(float) * 4);
727 }
728 
729 #undef DGL_Color3ub
DGL_Color3ub(DGLubyte r,DGLubyte g,DGLubyte b)730 DENG_EXTERN_C void DGL_Color3ub(DGLubyte r, DGLubyte g, DGLubyte b)
731 {
732     DENG2_ASSERT_IN_RENDER_THREAD();
733 
734     dglDraw.currentVertex.color[0] = r;
735     dglDraw.currentVertex.color[1] = g;
736     dglDraw.currentVertex.color[2] = b;
737     dglDraw.currentVertex.color[3] = 255;
738 }
739 
740 #undef DGL_Color3ubv
DGL_Color3ubv(DGLubyte const * vec)741 DENG_EXTERN_C void DGL_Color3ubv(DGLubyte const *vec)
742 {
743     DENG2_ASSERT_IN_RENDER_THREAD();
744 
745     dglDraw.currentVertex.color[0] = vec[0];
746     dglDraw.currentVertex.color[1] = vec[1];
747     dglDraw.currentVertex.color[2] = vec[2];
748     dglDraw.currentVertex.color[3] = 255;
749 }
750 
751 #undef DGL_Color4ub
DGL_Color4ub(DGLubyte r,DGLubyte g,DGLubyte b,DGLubyte a)752 DENG_EXTERN_C void DGL_Color4ub(DGLubyte r, DGLubyte g, DGLubyte b, DGLubyte a)
753 {
754     DENG2_ASSERT_IN_RENDER_THREAD();
755 
756     dglDraw.currentVertex.color[0] = r;
757     dglDraw.currentVertex.color[1] = g;
758     dglDraw.currentVertex.color[2] = b;
759     dglDraw.currentVertex.color[3] = a;
760 }
761 
762 #undef DGL_Color4ubv
DGL_Color4ubv(DGLubyte const * vec)763 DENG_EXTERN_C void DGL_Color4ubv(DGLubyte const *vec)
764 {
765     DENG2_ASSERT_IN_RENDER_THREAD();
766 
767     dglDraw.currentVertex.color[0] = vec[0];
768     dglDraw.currentVertex.color[1] = vec[1];
769     dglDraw.currentVertex.color[2] = vec[2];
770     dglDraw.currentVertex.color[3] = vec[3];
771 }
772 
773 #undef DGL_Color3f
DGL_Color3f(float r,float g,float b)774 DENG_EXTERN_C void DGL_Color3f(float r, float g, float b)
775 {
776     DENG2_ASSERT_IN_RENDER_THREAD();
777 
778     const auto color = DGLDrawState::colorFromFloat({r, g, b, 1.f});
779     dglDraw.currentVertex.color[0] = color.x;
780     dglDraw.currentVertex.color[1] = color.y;
781     dglDraw.currentVertex.color[2] = color.z;
782     dglDraw.currentVertex.color[3] = color.w;
783 }
784 
785 #undef DGL_Color3fv
DGL_Color3fv(float const * vec)786 DENG_EXTERN_C void DGL_Color3fv(float const *vec)
787 {
788     DENG2_ASSERT_IN_RENDER_THREAD();
789 
790     const auto color = DGLDrawState::colorFromFloat({Vector3f(vec), 1.f});
791     dglDraw.currentVertex.color[0] = color.x;
792     dglDraw.currentVertex.color[1] = color.y;
793     dglDraw.currentVertex.color[2] = color.z;
794     dglDraw.currentVertex.color[3] = color.w;
795 }
796 
797 #undef DGL_Color4f
DGL_Color4f(float r,float g,float b,float a)798 DENG_EXTERN_C void DGL_Color4f(float r, float g, float b, float a)
799 {
800     DENG2_ASSERT_IN_RENDER_THREAD();
801 
802     const auto color = DGLDrawState::colorFromFloat({r, g, b, a});
803     dglDraw.currentVertex.color[0] = color.x;
804     dglDraw.currentVertex.color[1] = color.y;
805     dglDraw.currentVertex.color[2] = color.z;
806     dglDraw.currentVertex.color[3] = color.w;
807 }
808 
809 #undef DGL_Color4fv
DGL_Color4fv(float const * vec)810 DENG_EXTERN_C void DGL_Color4fv(float const *vec)
811 {
812     DENG2_ASSERT_IN_RENDER_THREAD();
813 
814     const auto color = DGLDrawState::colorFromFloat(vec);
815     dglDraw.currentVertex.color[0] = color.x;
816     dglDraw.currentVertex.color[1] = color.y;
817     dglDraw.currentVertex.color[2] = color.z;
818     dglDraw.currentVertex.color[3] = color.w;
819 }
820 
821 #undef DGL_TexCoord2f
DGL_TexCoord2f(byte target,float s,float t)822 DENG_EXTERN_C void DGL_TexCoord2f(byte target, float s, float t)
823 {
824     DENG2_ASSERT_IN_RENDER_THREAD();
825     DENG2_ASSERT(target < MAX_TEX_COORDS);
826 
827     if (target < MAX_TEX_COORDS)
828     {
829         dglDraw.currentVertex.texCoord[target].s = s;
830         dglDraw.currentVertex.texCoord[target].t = t;
831     }
832 }
833 
834 #undef DGL_TexCoord2fv
DGL_TexCoord2fv(byte target,float const * vec)835 DENG_EXTERN_C void DGL_TexCoord2fv(byte target, float const *vec)
836 {
837     DENG2_ASSERT_IN_RENDER_THREAD();
838     DENG2_ASSERT(target < MAX_TEX_COORDS);
839 
840     if (target < MAX_TEX_COORDS)
841     {
842         dglDraw.currentVertex.texCoord[target].s = vec[0];
843         dglDraw.currentVertex.texCoord[target].t = vec[1];
844     }
845 }
846 
847 #undef DGL_Vertex2f
DGL_Vertex2f(float x,float y)848 DENG_EXTERN_C void DGL_Vertex2f(float x, float y)
849 {
850     DENG2_ASSERT_IN_RENDER_THREAD();
851 
852     dglDraw.currentVertex.vertex[0] = x;
853     dglDraw.currentVertex.vertex[1] = y;
854     dglDraw.currentVertex.vertex[2] = 0.f;
855     dglDraw.commitVertex();
856 }
857 
858 #undef DGL_Vertex2fv
DGL_Vertex2fv(const float * vec)859 DENG_EXTERN_C void DGL_Vertex2fv(const float* vec)
860 {
861     DENG2_ASSERT_IN_RENDER_THREAD();
862 
863     if (vec)
864     {
865         dglDraw.currentVertex.vertex[0] = vec[0];
866         dglDraw.currentVertex.vertex[1] = vec[1];
867         dglDraw.currentVertex.vertex[2] = 0.f;
868     }
869     dglDraw.commitVertex();
870 }
871 
872 #undef DGL_Vertex3f
DGL_Vertex3f(float x,float y,float z)873 DENG_EXTERN_C void DGL_Vertex3f(float x, float y, float z)
874 {
875     DENG2_ASSERT_IN_RENDER_THREAD();
876 
877     dglDraw.currentVertex.vertex[0] = x;
878     dglDraw.currentVertex.vertex[1] = y;
879     dglDraw.currentVertex.vertex[2] = z;
880 
881     dglDraw.commitVertex();
882 }
883 
884 #undef DGL_Vertex3fv
DGL_Vertex3fv(const float * vec)885 DENG_EXTERN_C void DGL_Vertex3fv(const float* vec)
886 {
887     DENG2_ASSERT_IN_RENDER_THREAD();
888 
889     if (vec)
890     {
891         dglDraw.currentVertex.vertex[0] = vec[0];
892         dglDraw.currentVertex.vertex[1] = vec[1];
893         dglDraw.currentVertex.vertex[2] = vec[2];
894     }
895     dglDraw.commitVertex();
896 }
897 
898 #undef DGL_Vertices2ftv
DGL_Vertices2ftv(int num,const dgl_ft2vertex_t * vec)899 DENG_EXTERN_C void DGL_Vertices2ftv(int num, const dgl_ft2vertex_t* vec)
900 {
901     DENG2_ASSERT_IN_RENDER_THREAD();
902 
903     for(; num > 0; num--, vec++)
904     {
905         DGL_TexCoord2fv(0, vec->tex);
906         DGL_Vertex2fv(vec->pos);
907     }
908 }
909 
910 #undef DGL_Vertices3ftv
DGL_Vertices3ftv(int num,const dgl_ft3vertex_t * vec)911 DENG_EXTERN_C void DGL_Vertices3ftv(int num, const dgl_ft3vertex_t* vec)
912 {
913     DENG2_ASSERT_IN_RENDER_THREAD();
914     DENG_ASSERT_GL_CONTEXT_ACTIVE();
915 
916     for(; num > 0; num--, vec++)
917     {
918         DGL_TexCoord2fv(0, vec->tex);
919         DGL_Vertex3fv(vec->pos);
920     }
921 }
922 
923 #undef DGL_Vertices3fctv
DGL_Vertices3fctv(int num,const dgl_fct3vertex_t * vec)924 DENG_EXTERN_C void DGL_Vertices3fctv(int num, const dgl_fct3vertex_t* vec)
925 {
926     DENG2_ASSERT_IN_RENDER_THREAD();
927     DENG_ASSERT_GL_CONTEXT_ACTIVE();
928 
929     for(; num > 0; num--, vec++)
930     {
931         DGL_Color4fv(vec->color);
932         DGL_TexCoord2fv(0, vec->tex);
933         DGL_Vertex3fv(vec->pos);
934     }
935 }
936 
937 #undef DGL_Begin
DGL_Begin(dglprimtype_t mode)938 DENG_EXTERN_C void DGL_Begin(dglprimtype_t mode)
939 {
940     if (novideo) return;
941 
942     DENG2_ASSERT_IN_RENDER_THREAD();
943     DENG_ASSERT_GL_CONTEXT_ACTIVE();
944 
945     dglDraw.beginPrimitive(mode);
946 }
947 
DGL_AssertNotInPrimitive(void)948 void DGL_AssertNotInPrimitive(void)
949 {
950     DENG_ASSERT(dglDraw.primType == DGL_NO_PRIMITIVE);
951 }
952 
953 #undef DGL_End
DGL_End(void)954 DENG_EXTERN_C void DGL_End(void)
955 {
956     if (novideo) return;
957 
958     DENG2_ASSERT_IN_RENDER_THREAD();
959     DENG_ASSERT_GL_CONTEXT_ACTIVE();
960 
961     dglDraw.endPrimitive();
962 }
963 
964 #undef DGL_DrawLine
DGL_DrawLine(float x1,float y1,float x2,float y2,float r,float g,float b,float a)965 DENG_EXTERN_C void DGL_DrawLine(float x1, float y1, float x2, float y2, float r,
966     float g, float b, float a)
967 {
968     GL_DrawLine(x1, y1, x2, y2, r, g, b, a);
969 }
970 
971 #undef DGL_DrawRect
DGL_DrawRect(RectRaw const * rect)972 DENG_EXTERN_C void DGL_DrawRect(RectRaw const *rect)
973 {
974     if (!rect) return;
975     GL_DrawRect(Rectanglei::fromSize(Vector2i(rect->origin.xy),
976                                      Vector2ui(rect->size.width, rect->size.height)));
977 }
978 
979 #undef DGL_DrawRect2
DGL_DrawRect2(int x,int y,int w,int h)980 DENG_EXTERN_C void DGL_DrawRect2(int x, int y, int w, int h)
981 {
982     GL_DrawRect2(x, y, w, h);
983 }
984 
985 #undef DGL_DrawRectf
DGL_DrawRectf(RectRawf const * rect)986 DENG_EXTERN_C void DGL_DrawRectf(RectRawf const *rect)
987 {
988     GL_DrawRectf(rect);
989 }
990 
991 #undef DGL_DrawRectf2
DGL_DrawRectf2(double x,double y,double w,double h)992 DENG_EXTERN_C void DGL_DrawRectf2(double x, double y, double w, double h)
993 {
994     GL_DrawRectf2(x, y, w, h);
995 }
996 
997 #undef DGL_DrawRectf2Color
DGL_DrawRectf2Color(double x,double y,double w,double h,float r,float g,float b,float a)998 DENG_EXTERN_C void DGL_DrawRectf2Color(double x, double y, double w, double h, float r, float g, float b, float a)
999 {
1000     DENG_ASSERT_IN_MAIN_THREAD();
1001 
1002     DGL_Color4f(r, g, b, a);
1003     GL_DrawRectf2(x, y, w, h);
1004 }
1005 
1006 #undef DGL_DrawRectf2Tiled
DGL_DrawRectf2Tiled(double x,double y,double w,double h,int tw,int th)1007 DENG_EXTERN_C void DGL_DrawRectf2Tiled(double x, double y, double w, double h, int tw, int th)
1008 {
1009     GL_DrawRectf2Tiled(x, y, w, h, tw, th);
1010 }
1011 
1012 #undef DGL_DrawCutRectfTiled
DGL_DrawCutRectfTiled(RectRawf const * rect,int tw,int th,int txoff,int tyoff,RectRawf const * cutRect)1013 DENG_EXTERN_C void DGL_DrawCutRectfTiled(RectRawf const *rect, int tw, int th, int txoff, int tyoff,
1014     RectRawf const *cutRect)
1015 {
1016     GL_DrawCutRectfTiled(rect, tw, th, txoff, tyoff, cutRect);
1017 }
1018 
1019 #undef DGL_DrawCutRectf2Tiled
DGL_DrawCutRectf2Tiled(double x,double y,double w,double h,int tw,int th,int txoff,int tyoff,double cx,double cy,double cw,double ch)1020 DENG_EXTERN_C void DGL_DrawCutRectf2Tiled(double x, double y, double w, double h, int tw, int th,
1021     int txoff, int tyoff, double cx, double cy, double cw, double ch)
1022 {
1023     GL_DrawCutRectf2Tiled(x, y, w, h, tw, th, txoff, tyoff, cx, cy, cw, ch);
1024 }
1025 
1026 #undef DGL_DrawQuadOutline
DGL_DrawQuadOutline(Point2Raw const * tl,Point2Raw const * tr,Point2Raw const * br,Point2Raw const * bl,float const color[4])1027 DENG_EXTERN_C void DGL_DrawQuadOutline(Point2Raw const *tl, Point2Raw const *tr,
1028     Point2Raw const *br, Point2Raw const *bl, float const color[4])
1029 {
1030     if(!tl || !tr || !br || !bl || (color && !(color[CA] > 0))) return;
1031 
1032     DENG_ASSERT_IN_MAIN_THREAD();
1033 
1034     if(color) DGL_Color4fv(color);
1035     DGL_Begin(DGL_LINE_STRIP);
1036         DGL_Vertex2f(tl->x, tl->y);
1037         DGL_Vertex2f(tr->x, tr->y);
1038         DGL_Vertex2f(br->x, br->y);
1039         DGL_Vertex2f(bl->x, bl->y);
1040         DGL_Vertex2f(tl->x, tl->y);
1041     DGL_End();
1042 }
1043 
1044 #undef DGL_DrawQuad2Outline
DGL_DrawQuad2Outline(int tlX,int tlY,int trX,int trY,int brX,int brY,int blX,int blY,const float color[4])1045 DENG_EXTERN_C void DGL_DrawQuad2Outline(int tlX, int tlY, int trX, int trY,
1046     int brX, int brY, int blX, int blY, const float color[4])
1047 {
1048     Point2Raw tl, tr, bl, br;
1049     tl.x = tlX;
1050     tl.y = tlY;
1051     tr.x = trX;
1052     tr.y = trY;
1053     br.x = brX;
1054     br.y = brY;
1055     bl.x = blX;
1056     bl.y = blY;
1057     DGL_DrawQuadOutline(&tl, &tr, &br, &bl, color);
1058 }
1059