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