new batching implementation

This commit is contained in:
gazer 2017-05-31 11:57:55 +03:00
parent 34172b8a31
commit e288e6fd40
2 changed files with 327 additions and 448 deletions

View File

@ -65,7 +65,7 @@ class GLDrawBuf : DrawBuf, GLConfigCallback {
glSupport.setOrthoProjection(Rect(0, 0, _dx, _dy), Rect(0, 0, _dx, _dy));
}
/// reserved for hardware-accelerated drawing - begins drawing batch
/// reserved for hardware-accelerated drawing - begins drawing queue
override void beforeDrawing() {
_alpha = 0;
if (_scene !is null) {
@ -74,11 +74,12 @@ class GLDrawBuf : DrawBuf, GLConfigCallback {
_scene = new Scene(this);
}
/// reserved for hardware-accelerated drawing - ends drawing batch
/// reserved for hardware-accelerated drawing - ends drawing queue
override void afterDrawing() {
glSupport.setOrthoProjection(Rect(0, 0, _dx, _dy), Rect(0, 0, _dx, _dy));
glSupport.beforeRenderGUI();
_scene.draw();
glSupport.batch.flush();
glSupport.queue.flush();
GLProgram.unbind();
glSupport.destroyBuffers();
glSupport.flushGL();
@ -142,7 +143,6 @@ class GLDrawBuf : DrawBuf, GLConfigCallback {
assert(_scene !is null);
Rect dstrect = Rect(x,y, x + glyph.correctedBlackBoxX, y + glyph.blackBoxY);
Rect srcrect = Rect(0, 0, glyph.correctedBlackBoxX, glyph.blackBoxY);
//Log.v("GLDrawBuf.drawGlyph dst=", dstrect, " src=", srcrect, " color=", color);
color = applyAlpha(color);
if (!isFullyTransparentColor(color) && applyClipping(dstrect, srcrect)) {
if (!glGlyphCache.isInCache(glyph.id))
@ -158,7 +158,7 @@ class GLDrawBuf : DrawBuf, GLConfigCallback {
if (applyClipping(dstrect, srcrect)) {
if (!glImageCache.isInCache(src.id))
glImageCache.put(src);
_scene.add(new TextureSceneItem(src.id, dstrect, srcrect, applyAlpha(0xFFFFFF), 0, null, 0));
_scene.add(new TextureSceneItem(src.id, dstrect, srcrect, applyAlpha(0xFFFFFF), 0, null));
}
}
/// draw source buffer rectangle contents to destination buffer rectangle applying rescaling
@ -168,7 +168,7 @@ class GLDrawBuf : DrawBuf, GLConfigCallback {
if (applyClipping(dstrect, srcrect)) {
if (!glImageCache.isInCache(src.id))
glImageCache.put(src);
_scene.add(new TextureSceneItem(src.id, dstrect, srcrect, applyAlpha(0xFFFFFF), 0, null, 0));
_scene.add(new TextureSceneItem(src.id, dstrect, srcrect, applyAlpha(0xFFFFFF), 0, null));
}
}
@ -545,17 +545,12 @@ private class GLImageCache : GLCache
_needUpdateTexture = true;
return cacheItem;
}
void drawItem(GLCacheItem item, Rect dstrc, Rect srcrc, uint color, uint options, Rect * clip, int rotationAngle) {
void drawItem(GLCacheItem item, Rect dstrc, Rect srcrc, uint color, uint options, Rect * clip) {
if (_needUpdateTexture)
updateTexture();
if (_texture && _texture.ID != 0) {
//rotationAngle = 0;
int rx = dstrc.middlex;
int ry = dstrc.middley;
if (rotationAngle) {
//rotationAngle = 0;
//setRotation(rx, ry, rotationAngle);
}
// convert coordinates to cached texture
srcrc.offset(item._rc.left, item._rc.top);
if (clip) {
@ -577,17 +572,7 @@ private class GLImageCache : GLCache
dstrc.bottom -= clip.bottom;
}
if (!dstrc.empty)
glSupport.batch.addTexturedRect(_texture, _tdx, _tdy, color, color, color, color, srcrc, dstrc, srcrc.width() != dstrc.width() || srcrc.height() != dstrc.height());
//glSupport.drawColorAndTextureRects(_texture, _tdx, _tdy, [srcrc], [dstrc], [color, color, color, color], srcrc.width() != dstrc.width() || srcrc.height() != dstrc.height());
//drawColorAndTextureRect(vertices, texcoords, color, _texture);
if (rotationAngle) {
// unset rotation
glSupport.setRotation(rx, ry, 0);
// glMatrixMode(GL_PROJECTION);
// checkgl!glPopMatrix();
}
glSupport.queue.addTexturedRect(_texture, _tdx, _tdy, color, color, color, color, srcrc, dstrc, srcrc.width() != dstrc.width() || srcrc.height() != dstrc.height());
}
}
}
@ -619,11 +604,11 @@ private class GLImageCache : GLCache
_map[img.id] = res;
}
/// draw cached item
void drawItem(uint objectId, Rect dstrc, Rect srcrc, uint color, int options, Rect * clip, int rotationAngle) {
void drawItem(uint objectId, Rect dstrc, Rect srcrc, uint color, int options, Rect * clip) {
GLCacheItem* item = objectId in _map;
if (item) {
auto page = (cast(GLImageCachePage)item.page);
page.drawItem(*item, dstrc, srcrc, color, options, clip, rotationAngle);
page.drawItem(*item, dstrc, srcrc, color, options, clip);
}
}
}
@ -673,9 +658,7 @@ private class GLGlyphCache : GLCache
}
if (!dstrc.empty) {
//Log.d("drawing glyph with color ", color);
//glSupport.drawColorAndTextureRect(_texture, _tdx, _tdy, srcrc, dstrc, color, false);
//glSupport.drawColorAndTextureRects(_texture, _tdx, _tdy, [srcrc], [dstrc], [color, color, color, color], false);
glSupport.batch.addTexturedRect(_texture, _tdx, _tdy, color, color, color, color, srcrc, dstrc, false);
glSupport.queue.addTexturedRect(_texture, _tdx, _tdy, color, color, color, color, srcrc, dstrc, false);
}
}
}
@ -723,8 +706,7 @@ public:
_color = color;
}
override void draw() {
//glSupport.drawLines([Rect(_p1, _p2)], [_color, _color]);
glSupport.batch.addLine(Rect(_p1, _p2), _color, _color);
glSupport.queue.addLine(Rect(_p1, _p2), _color, _color);
}
}
@ -743,7 +725,7 @@ public:
_color = color;
}
override void draw() {
glSupport.batch.addTriangle(_p1, _p2, _p3, _color, _color, _color);
glSupport.queue.addTriangle(_p1, _p2, _p3, _color, _color, _color);
}
}
@ -758,8 +740,7 @@ public:
_color = color;
}
override void draw() {
glSupport.batch.addSolidRect(_rc, _color);
//glSupport.drawSolidFillRects([_rc], [_color, _color, _color, _color]);
glSupport.queue.addSolidRect(_rc, _color);
}
}
@ -781,9 +762,7 @@ public:
for (int y = _rc.top; y < _rc.bottom; y++) {
for (int x = _rc.left; x < _rc.right; x++)
if ((x ^ y) & 1) {
//glSupport.drawSolidFillRect(Rect(x, y, x + 1, y + 1), _color, _color, _color, _color);
//glSupport.drawSolidFillRects([Rect(x, y, x + 1, y + 1)], [_color, _color, _color, _color]);
glSupport.batch.addSolidRect(Rect(x, y, x + 1, y + 1), _color);
glSupport.queue.addSolidRect(Rect(x, y, x + 1, y + 1), _color);
}
}
}
@ -798,15 +777,14 @@ private:
uint color;
uint options;
Rect * clip;
int rotationAngle;
public:
override void draw() {
if (glImageCache)
glImageCache.drawItem(objectId, dstrc, srcrc, color, options, clip, rotationAngle);
glImageCache.drawItem(objectId, dstrc, srcrc, color, options, clip);
}
this(uint _objectId, Rect _dstrc, Rect _srcrc, uint _color, uint _options, Rect * _clip, int _rotationAngle)
this(uint _objectId, Rect _dstrc, Rect _srcrc, uint _color, uint _options, Rect * _clip)
{
objectId = _objectId;
dstrc = _dstrc;
@ -814,7 +792,6 @@ public:
color = _color;
options = _options;
clip = _clip;
rotationAngle = _rotationAngle;
}
}
@ -856,13 +833,12 @@ public:
}
override void draw() {
if (_handler) {
glSupport.batch.flush();
glSupport.queue.flush();
glSupport.setOrthoProjection(_windowRect, _rc);
glSupport.clearDepthBuffer();
//glEnable(GL_BLEND);
//checkgl!glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
_handler(_windowRect, _rc);
glSupport.batch.flush();
glSupport.setOrthoProjection(_windowRect, _windowRect);
}
}

View File

@ -38,7 +38,7 @@ version (Android) {
public import EGL.egl;
//public import GLES2.gl2;
public import GLES3.gl3;
static if (SUPPORT_LEGACY_OPENGL) {
public import GLES.gl : glEnableClientState, glLightfv, glColor4f, GL_ALPHA_TEST, GL_VERTEX_ARRAY,
GL_COLOR_ARRAY, glVertexPointer, glColorPointer, glDisableClientState,
@ -268,7 +268,7 @@ class GLProgram : dlangui.graphics.scene.mesh.GraphicsEffect {
/// override to init shader code locations
abstract bool initLocations();
~this() {
// TODO: cleanup
if (program)
@ -499,88 +499,50 @@ class SolidFillProgram : GLProgram {
}
VAO vao;
VBO vbo;
EBO ebo;
protected void createVAO(float[] vertices, float[] colors) {
if (!vao) {
vao = new VAO;
vbo = new VBO;
ebo = new EBO;
}
vbo.bind();
ebo.bind();
vao.bind();
protected void beforeExecute() {
bind();
setUniform(DefaultUniform.u_worldViewProjectionMatrix, glSupport.projectionMatrix);
}
protected void createVAO(size_t verticesBufferLength) {
vao = new VAO;
glVertexAttribPointer(vertexLocation, 3, GL_FLOAT, GL_FALSE, 0, cast(void*) 0);
glVertexAttribPointer(colAttrLocation, 4, GL_FLOAT, GL_FALSE, 0, cast(void*) (vertices.length * float.sizeof));
glVertexAttribPointer(colAttrLocation, 4, GL_FLOAT, GL_FALSE, 0, cast(void*) (verticesBufferLength * float.sizeof));
glEnableVertexAttribArray(vertexLocation);
glEnableVertexAttribArray(colAttrLocation);
}
protected void beforeExecute() {
glEnable(GL_BLEND);
checkgl!glDisable(GL_CULL_FACE);
checkgl!glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
bind();
setUniform(DefaultUniform.u_worldViewProjectionMatrix, glSupport.projectionMatrix);
//checkgl!glUniformMatrix4fv(matrixLocation, 1, false, glSupport.projectionMatrix.m.ptr);
}
bool execute(float[] vertices, float[] colors, int[] indexes) {
bool drawBatch(int length, int start) {
if(!check())
return false;
beforeExecute();
createVAO(vertices, colors);
vbo.bind();
vbo.fill([vertices, colors]);
ebo.bind();
ebo.fill(indexes);
vao.bind();
checkgl!glDrawElements(GL_TRIANGLES, cast(int)indexes.length, GL_UNSIGNED_INT, cast(void*)null);
vao.unbind();
vbo.unbind();
ebo.unbind();
checkgl!glDrawElements(GL_TRIANGLES, cast(int)length, GL_UNSIGNED_INT, cast(void*)(start * 4));
return true;
}
void destroyBuffers() {
destroy(vao);
destroy(vbo);
destroy(ebo);
vao = null;
vbo = null;
ebo = null;
}
}
class LineProgram : SolidFillProgram {
override bool execute(float[] vertices, float[] colors, int[] indexes) {
override bool drawBatch(int length, int start) {
if(!check())
return false;
beforeExecute();
createVAO(vertices, colors);
vbo.bind();
vbo.fill([vertices, colors]);
ebo.bind();
ebo.fill(indexes);
vao.bind();
checkgl!glDrawElements(GL_LINES, cast(int)indexes.length, GL_UNSIGNED_INT, cast(void*)null);
vao.unbind();
vbo.unbind();
ebo.unbind();
checkgl!glDrawElements(GL_LINES, cast(int)length, GL_UNSIGNED_INT, cast(void*)(start * 4));
return true;
}
}
@ -624,33 +586,26 @@ class TextureProgram : SolidFillProgram {
/// get location for vertex attribute
override int getVertexElementLocation(VertexElementType type) {
switch(type) with(VertexElementType) {
case TEXCOORD0:
case TEXCOORD0:
return texCoordLocation;
default:
return super.getVertexElementLocation(type);
}
}
protected void createVAO(float[] vertices, float[] colors, float[] texcoords) {
if (!vao) {
vao = new VAO;
vbo = new VBO;
ebo = new EBO;
}
vbo.bind();
ebo.bind();
vao.bind();
protected void createVAO(size_t verticesBufferLength, size_t colorsBufferLength) {
vao = new VAO;
glVertexAttribPointer(vertexLocation, 3, GL_FLOAT, GL_FALSE, 0, cast(void*) 0);
glVertexAttribPointer(colAttrLocation, 4, GL_FLOAT, GL_FALSE, 0, cast(void*) (vertices.length * float.sizeof));
glVertexAttribPointer(texCoordLocation, 2, GL_FLOAT, GL_FALSE, 0, cast(void*) ((vertices.length + colors.length) * float.sizeof));
glVertexAttribPointer(colAttrLocation, 4, GL_FLOAT, GL_FALSE, 0, cast(void*) (verticesBufferLength * float.sizeof));
glVertexAttribPointer(texCoordLocation, 2, GL_FLOAT, GL_FALSE, 0, cast(void*) ((verticesBufferLength + colorsBufferLength) * float.sizeof));
glEnableVertexAttribArray(vertexLocation);
glEnableVertexAttribArray(colAttrLocation);
glEnableVertexAttribArray(texCoordLocation);
}
bool execute(float[] vertices, float[] colors, float[] texcoords, Tex2D texture, bool linear, int[] indexes) {
bool drawBatch(Tex2D texture, bool linear, int length, int start) {
if(!check())
return false;
beforeExecute();
@ -658,21 +613,9 @@ class TextureProgram : SolidFillProgram {
texture.setup();
texture.setSamplerParams(linear);
createVAO(vertices, colors, texcoords);
vbo.bind();
vbo.fill([vertices, colors, texcoords]);
ebo.bind();
ebo.fill(indexes);
vao.bind();
checkgl!glDrawElements(GL_TRIANGLES, cast(int)indexes.length, GL_UNSIGNED_INT, cast(void*)null);
vao.unbind();
vbo.unbind();
ebo.unbind();
checkgl!glDrawElements(GL_TRIANGLES, cast(int)length, GL_UNSIGNED_INT, cast(void*)(start * 4));
texture.unbind();
return true;
@ -699,6 +642,24 @@ private void FillColor(uint color, Color[] buf_slice) {
}
}
private float[] convertColors(uint[] cols) {
float[] colors;
colors.assumeSafeAppend();
colors.length = cols.length * 4;
for (uint i = 0; i < cols.length; i++) {
uint color = cols[i];
float r = ((color >> 16) & 255) / 255.0;
float g = ((color >> 8) & 255) / 255.0;
float b = ((color >> 0) & 255) / 255.0;
float a = (((color >> 24) & 255) ^ 255) / 255.0;
colors[i * 4 + 0] = r;
colors[i * 4 + 1] = g;
colors[i * 4 + 2] = b;
colors[i * 4 + 3] = a;
}
return colors;
}
__gshared GLSupport _glSupport;
@property GLSupport glSupport() {
if (!_glSupport) {
@ -794,10 +755,10 @@ final class GLSupport {
private bool _legacyMode;
@property bool legacyMode() { return _legacyMode; }
@property batch() { return _batch; }
@property queue() { return _queue; }
this(bool legacy = false) {
_batch = new OpenGLBatch();
_queue = new OpenGLQueue;
version (Android) {
Log.d("creating GLSupport");
} else {
@ -809,7 +770,7 @@ final class GLSupport {
_legacyMode = legacy;
}
OpenGLBatch _batch;
OpenGLQueue _queue;
SolidFillProgram _solidFillProgram;
LineProgram _lineProgram;
@ -860,67 +821,48 @@ final class GLSupport {
return true;
}
void beforeRenderGUI() {
glEnable(GL_BLEND);
checkgl!glDisable(GL_CULL_FACE);
checkgl!glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
VBO vbo;
EBO ebo;
void fillBuffers(float[] vertices, float[] colors, float[] texcoords, int[] indices) {
vbo = new VBO;
ebo = new EBO;
vbo.bind();
vbo.fill([vertices, colors, texcoords]);
ebo.bind();
ebo.fill(indices);
// create vertex array objects and bind vertex buffers to them
_solidFillProgram.createVAO(vertices.length);
vbo.bind();
ebo.bind();
_lineProgram.vao = _solidFillProgram.vao;
_textureProgram.createVAO(vertices.length, colors.length);
vbo.bind();
ebo.bind();
}
void destroyBuffers() {
if (_solidFillProgram)
_solidFillProgram.destroyBuffers();
if (_lineProgram)
_lineProgram.destroyBuffers();
if (_textureProgram)
_textureProgram.destroyBuffers();
destroy(vbo);
destroy(ebo);
vbo = null;
ebo = null;
}
void setRotation(int x, int y, int rotationAngle) {
/*
this->rotationAngle = rotationAngle;
rotationX = x;
rotationY = y;
if (!currentFramebufferId) {
rotationY = bufferDy - rotationY;
}
QMatrix4x4 matrix2;
matrix2.ortho(0, bufferDx, 0, bufferDy, 0.5f, 5.0f);
if (rotationAngle) {
matrix2.translate(rotationX, rotationY, 0);
matrix2.rotate(rotationAngle, 0, 0, 1);
matrix2.translate(-rotationX, -rotationY, 0);
}
matrix2.copyDataTo(m);
*/
}
/// one rect is one line (left, top) - (right, bottom); for one line there are two color items
void drawLines(Rect[] lines, uint[] vertexColors) {
Color[] colors;
colors.length = vertexColors.length;
for (uint i = 0; i < vertexColors.length; i++)
FillColor(vertexColors[i], colors[i .. i + 1]);
float[] vertexArray;
vertexArray.assumeSafeAppend();
for (uint i = 0; i < lines.length; i++) {
Rect rc = lines[i];
float x0 = cast(float)(rc.left);
float y0 = cast(float)(bufferDy-rc.top);
float x1 = cast(float)(rc.right);
float y1 = cast(float)(bufferDy-rc.bottom);
// don't flip for framebuffer
if (currentFBO) {
y0 = cast(float)(rc.top);
y1 = cast(float)(rc.bottom);
}
vertexArray ~= x0;
vertexArray ~= y0;
vertexArray ~= Z_2D;
vertexArray ~= x1;
vertexArray ~= y1;
vertexArray ~= Z_2D;
}
int[] indexes = makeLineIndexesArray(lines.length);
void drawLines(int length, int start) {
if (_legacyMode) {
static if (SUPPORT_LEGACY_OPENGL) {
@ -931,10 +873,10 @@ final class GLSupport {
checkgl!glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
checkgl!glEnableClientState(GL_VERTEX_ARRAY);
checkgl!glEnableClientState(GL_COLOR_ARRAY);
checkgl!glVertexPointer(3, GL_FLOAT, 0, cast(void*)vertexArray.ptr);
checkgl!glColorPointer(4, GL_FLOAT, 0, cast(void*)colors.ptr);
checkgl!glVertexPointer(3, GL_FLOAT, 0, cast(void*)_queue._vertices.ptr);
checkgl!glColorPointer(4, GL_FLOAT, 0, cast(void*)_queue._colors.ptr);
checkgl!glDrawElements(GL_LINES, cast(int)indexes.length, GL_UNSIGNED_INT, cast(void*)indexes.ptr);
checkgl!glDrawElements(GL_LINES, cast(int)length, GL_UNSIGNED_INT, cast(void*)(_queue._indices.ptr + start * 4));
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
@ -943,83 +885,13 @@ final class GLSupport {
}
} else {
if (_lineProgram !is null) {
_lineProgram.execute(vertexArray, cast(float[])colors, indexes);
_lineProgram.drawBatch(length, start);
} else
Log.e("No program");
}
}
static immutable float Z_2D = -2.0f;
/// make indexes for rectangle TRIANGLES (2 triangles == 6 vertexes per rect)
protected int[] makeRectangleIndexesArray(size_t rectCount) {
int[] indexes;
indexes.assumeSafeAppend();
for (uint i = 0; i < rectCount; i++) {
indexes ~= i * 4 + 0;
indexes ~= i * 4 + 1;
indexes ~= i * 4 + 2;
indexes ~= i * 4 + 1;
indexes ~= i * 4 + 2;
indexes ~= i * 4 + 3;
}
return indexes;
}
/// make indexes for rectangle TRIANGLES (2 triangles == 6 vertexes per rect)
protected int[] makeTriangleIndexesArray(size_t pointCount) {
int[] indexes;
indexes.assumeSafeAppend();
for (uint i = 0; i + 2 < pointCount; i += 3) {
indexes ~= i + 0;
indexes ~= i + 1;
indexes ~= i + 2;
}
return indexes;
}
/// make indexes for LINES
protected int[] makeLineIndexesArray(size_t lineCount) {
int[] indexes;
indexes.assumeSafeAppend();
for (uint i = 0; i < lineCount; i++) {
indexes ~= i * 2 + 0;
indexes ~= i * 2 + 1;
}
return indexes;
}
void drawSolidFillRects(Rect[] rects, uint[] vertexColors) {
//Log.v("drawSolidFillRects rects:", rects.length, " colors:", vertexColors.length);
float[] colors = convertColors(vertexColors);
float[] vertexArray;
vertexArray.assumeSafeAppend();
for (uint i = 0; i < rects.length; i++) {
Rect rc = rects[i];
float x0 = cast(float)(rc.left);
float y0 = cast(float)(bufferDy-rc.top);
float x1 = cast(float)(rc.right);
float y1 = cast(float)(bufferDy-rc.bottom);
// don't flip for framebuffer
if (currentFBO) {
y0 = cast(float)(rc.top);
y1 = cast(float)(rc.bottom);
}
float[3 * 4] vertices = [
x0,y0,Z_2D,
x0,y1,Z_2D,
x1,y0,Z_2D,
x1,y1,Z_2D];
vertexArray ~= vertices;
}
int[] indexes = makeRectangleIndexesArray(rects.length);
void drawSolidFillTriangles(int length, int start) {
if (_legacyMode) {
static if (SUPPORT_LEGACY_OPENGL) {
@ -1030,10 +902,10 @@ final class GLSupport {
checkgl!glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
checkgl!glEnableClientState(GL_VERTEX_ARRAY);
checkgl!glEnableClientState(GL_COLOR_ARRAY);
checkgl!glVertexPointer(3, GL_FLOAT, 0, cast(void*)vertexArray.ptr);
checkgl!glColorPointer(4, GL_FLOAT, 0, cast(void*)colors.ptr);
checkgl!glVertexPointer(3, GL_FLOAT, 0, cast(void*)_queue._vertices.ptr);
checkgl!glColorPointer(4, GL_FLOAT, 0, cast(void*)_queue._colors.ptr);
checkgl!glDrawElements(GL_TRIANGLES, cast(int)indexes.length, GL_UNSIGNED_INT, cast(void*)indexes.ptr);
checkgl!glDrawElements(GL_TRIANGLES, cast(int)length, GL_UNSIGNED_INT, cast(void*)(_queue._indices.ptr + start * 4));
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
@ -1042,121 +914,13 @@ final class GLSupport {
}
} else {
if (_solidFillProgram !is null) {
_solidFillProgram.execute(vertexArray, colors, indexes);
_solidFillProgram.drawBatch(length, start);
} else
Log.e("No program");
}
}
void drawSolidFillTriangles(PointF[] points, uint[] vertexColors) {
//Log.v("drawSolidFillRects rects:", rects.length, " colors:", vertexColors.length);
float[] colors = convertColors(vertexColors);
float[] vertexArray;
vertexArray.assumeSafeAppend();
for (uint i = 0; i + 2 < points.length; i += 3) {
float x0 = points[i+0].x;
float y0 = currentFBO ? points[i+0].y : (bufferDy - points[i+0].y);
float x1 = points[i+1].x;
float y1 = currentFBO ? points[i+1].y : (bufferDy - points[i+1].y);
float x2 = points[i+2].x;
float y2 = currentFBO ? points[i+2].y : (bufferDy - points[i+2].y);
float[3 * 3] vertices = [
x0,y0,Z_2D,
x1,y1,Z_2D,
x2,y2,Z_2D];
vertexArray ~= vertices;
}
int[] indexes = makeTriangleIndexesArray(points.length);
if (_legacyMode) {
static if (SUPPORT_LEGACY_OPENGL) {
glColor4f(1,1,1,1);
glDisable(GL_CULL_FACE);
glEnable(GL_BLEND);
glDisable(GL_ALPHA_TEST);
checkgl!glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
checkgl!glEnableClientState(GL_VERTEX_ARRAY);
checkgl!glEnableClientState(GL_COLOR_ARRAY);
checkgl!glVertexPointer(3, GL_FLOAT, 0, cast(void*)vertexArray.ptr);
checkgl!glColorPointer(4, GL_FLOAT, 0, cast(void*)colors.ptr);
checkgl!glDrawElements(GL_TRIANGLES, cast(int)indexes.length, GL_UNSIGNED_INT, cast(void*)indexes.ptr);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glDisable(GL_ALPHA_TEST);
glDisable(GL_BLEND);
}
} else {
if (_solidFillProgram !is null) {
_solidFillProgram.execute(vertexArray, colors, indexes);
} else
Log.e("No program");
}
}
float[] convertColors(uint[] cols) {
float[] colors;
colors.assumeSafeAppend();
colors.length = cols.length * 4;
for (uint i = 0; i < cols.length; i++) {
uint color = cols[i];
float r = ((color >> 16) & 255) / 255.0;
float g = ((color >> 8) & 255) / 255.0;
float b = ((color >> 0) & 255) / 255.0;
float a = (((color >> 24) & 255) ^ 255) / 255.0;
colors[i * 4 + 0] = r;
colors[i * 4 + 1] = g;
colors[i * 4 + 2] = b;
colors[i * 4 + 3] = a;
}
return colors;
}
void drawColorAndTextureRects(Tex2D texture, int tdx, int tdy, Rect[] srcRects, Rect[] dstRects, uint[] vertexColors, bool linear) {
float[] colors = convertColors(vertexColors);
float[] vertexArray;
vertexArray.assumeSafeAppend();
float[] txcoordArray;
txcoordArray.assumeSafeAppend();
for (uint i = 0; i < srcRects.length; i++) {
Rect srcrc = srcRects[i];
Rect dstrc = dstRects[i];
float dstx0 = cast(float)dstrc.left;
float dsty0 = cast(float)(bufferDy - (dstrc.top));
float dstx1 = cast(float)dstrc.right;
float dsty1 = cast(float)(bufferDy - (dstrc.bottom));
// don't flip for framebuffer
if (currentFBO) {
dsty0 = cast(float)(dstrc.top);
dsty1 = cast(float)(dstrc.bottom);
}
float srcx0 = srcrc.left / cast(float)tdx;
float srcy0 = srcrc.top / cast(float)tdy;
float srcx1 = srcrc.right / cast(float)tdx;
float srcy1 = srcrc.bottom / cast(float)tdy;
float[3 * 4] vertices = [
dstx0,dsty0,Z_2D,
dstx0,dsty1,Z_2D,
dstx1,dsty0,Z_2D,
dstx1,dsty1,Z_2D];
float[2 * 4] texcoords = [srcx0,srcy0, srcx0,srcy1, srcx1,srcy0, srcx1,srcy1];
vertexArray ~= vertices;
txcoordArray ~= texcoords;
}
int[] indexes = makeRectangleIndexesArray(srcRects.length);
//Log.v("drawColorAndTextureRects srcrects:", srcRects.length, " dstrects:", dstRects.length, " colors:", vertexColors.length, " indexes: ", indexes);
void drawColorAndTextureTriangles(Tex2D texture, bool linear, int length, int start) {
if (_legacyMode) {
static if (SUPPORT_LEGACY_OPENGL) {
@ -1174,11 +938,11 @@ final class GLSupport {
checkgl!glEnableClientState(GL_COLOR_ARRAY);
checkgl!glEnableClientState(GL_VERTEX_ARRAY);
checkgl!glEnableClientState(GL_TEXTURE_COORD_ARRAY);
checkgl!glVertexPointer(3, GL_FLOAT, 0, cast(void*)vertexArray.ptr);
checkgl!glTexCoordPointer(2, GL_FLOAT, 0, cast(void*)txcoordArray.ptr);
checkgl!glColorPointer(4, GL_FLOAT, 0, cast(void*)colors.ptr);
checkgl!glVertexPointer(3, GL_FLOAT, 0, cast(void*)_queue._vertices.ptr);
checkgl!glTexCoordPointer(2, GL_FLOAT, 0, cast(void*)_queue._texCoords.ptr);
checkgl!glColorPointer(4, GL_FLOAT, 0, cast(void*)_queue._colors.ptr);
checkgl!glDrawElements(GL_TRIANGLES, cast(int)indexes.length, GL_UNSIGNED_INT, cast(void*)indexes.ptr);
checkgl!glDrawElements(GL_TRIANGLES, cast(int)length, GL_UNSIGNED_INT, cast(void*)(_queue._indices.ptr + start * 4));
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
@ -1188,7 +952,7 @@ final class GLSupport {
glDisable(GL_TEXTURE_2D);
}
} else {
_textureProgram.execute(vertexArray, colors, txcoordArray, texture, linear, indexes);
_textureProgram.drawBatch(texture, linear, length, start);
}
}
@ -1412,7 +1176,7 @@ class GLObject(GLObjectTypes type, GLuint target = 0) {
else
mixin("checkgl!glBind" ~ to!string(type) ~ "(0);");
}
static if(type == GLObjectTypes.Buffer)
{
void fill(float[][] buffs) {
@ -1422,7 +1186,7 @@ class GLObject(GLObjectTypes type, GLuint target = 0) {
checkgl!glBufferData(target,
length * float.sizeof,
null,
GL_STREAM_DRAW);
GL_STATIC_DRAW);
int offset;
foreach(b; buffs) {
checkgl!glBufferSubData(target,
@ -1435,7 +1199,7 @@ class GLObject(GLObjectTypes type, GLuint target = 0) {
static if (target == GL_ELEMENT_ARRAY_BUFFER) {
void fill(int[] indexes) {
checkgl!glBufferData(target, cast(int)(indexes.length * int.sizeof), indexes.ptr, GL_STREAM_DRAW);
checkgl!glBufferData(target, cast(int)(indexes.length * int.sizeof), indexes.ptr, GL_STATIC_DRAW);
}
}
}
@ -1678,95 +1442,234 @@ GLenum primitiveTypeToGL(PrimitiveType type) {
}
/// OpenGL batch buffer - to draw several triangles in single OpenGL call
class OpenGLBatch {
private Tex2D _currentTexture;
private int _currentTextureDx;
private int _currentTextureDy;
private bool _currentTextureLinear;
private Rect[] _srcRects;
private Rect[] _dstRects;
private uint[] _colors;
/// clear buffers
void reset() {
_colors.length = 0;
_colors.assumeSafeAppend();
_srcRects.length = 0;
_srcRects.assumeSafeAppend();
_dstRects.length = 0;
_dstRects.assumeSafeAppend();
_currentTexture = null;
_currentTextureDx = 0;
_currentTextureDy = 0;
_currentTextureLinear = false;
/// OpenGL GUI rendering queue. It collects gui draw calls, fills a big buffer for vertex data and draws everything
private final class OpenGLQueue {
OpenGLBatch[] batches;
/// OpenGL batch structure - to draw several triangles in single OpenGL call
private struct OpenGLBatch {
enum BatchType { Line = 0, Rect, Triangle, TexturedRect }
BatchType type;
Tex2D texture;
int textureDx;
int textureDy;
bool textureLinear;
// length of batch in indices
int length;
// offset in index buffer
int start;
}
/// draw buffered items
// a big buffer
float[] _vertices;
float[] _colors;
float[] _texCoords;
int[] _indices;
/// draw all
void flush() {
if (_dstRects.length == 0)
return; // nothing to draw
if (_currentTexture) {
// draw with texture
//Log.v("flush ", _dstRects.length, " texture rectangles");
glSupport.drawColorAndTextureRects(_currentTexture, _currentTextureDx, _currentTextureDy, _srcRects, _dstRects, _colors, _currentTextureLinear);
} else {
// draw solid fill
//Log.v("flush ", _dstRects.length, " solid rectangles");
glSupport.drawSolidFillRects(_dstRects, _colors);
glSupport.fillBuffers(_vertices, _colors, _texCoords, _indices);
foreach(b; batches) {
switch(b.type) with(OpenGLBatch.BatchType)
{
case Line: glSupport.drawLines(b.length, b.start); break;
case Rect: glSupport.drawSolidFillTriangles(b.length, b.start); break;
case Triangle: glSupport.drawSolidFillTriangles(b.length, b.start); break;
case TexturedRect: glSupport.drawColorAndTextureTriangles(b.texture, b.textureLinear, b.length, b.start); break;
default: break;
}
}
reset();
//Log.d(batches.length, " ", _vertices.length, " ", _colors.length, " ", _texCoords.length, " ", _indices.length);
destroy(batches);
destroy(_vertices);
destroy(_colors);
destroy(_texCoords);
destroy(_indices);
batches = null;
_vertices = null;
_colors = null;
_texCoords = null;
_indices = null;
}
/// add textured rect
void addTexturedRect(Tex2D texture, int textureDx, int textureDy, uint color1, uint color2, uint color3, uint color4, Rect srcRect, Rect dstRect, bool linear) {
if (!_currentTexture
|| _currentTexture.ID != texture.ID
|| _currentTextureLinear != linear
//|| (textureDx != _currentTextureDx)
//|| (textureDy != _currentTextureDy)
//|| true
)
static immutable float Z_2D = -2.0f;
/// add textured rectangle to queue
void addTexturedRect(Tex2D texture, int textureDx, int textureDy, uint color1, uint color2, uint color3, uint color4, Rect srcrc, Rect dstrc, bool linear) {
if (batches is null
|| batches[$-1].type != OpenGLBatch.BatchType.TexturedRect
|| batches[$-1].texture.ID != texture.ID
|| batches[$-1].textureLinear != linear)
{
flush();
_currentTexture = texture;
_currentTextureDx = textureDx;
_currentTextureDy = textureDy;
_currentTextureLinear = linear;
batches ~= OpenGLBatch();
batches[$-1].type = OpenGLBatch.BatchType.TexturedRect;
batches[$-1].texture = texture;
batches[$-1].textureDx = textureDx;
batches[$-1].textureDy = textureDy;
batches[$-1].textureLinear = linear;
if(batches.length > 1)
batches[$-1].start = batches[$-2].start + batches[$-2].length;
}
if (_currentTexture) {
_srcRects ~= srcRect;
}
_dstRects ~= dstRect;
_colors ~= color1;
_colors ~= color2;
_colors ~= color3;
_colors ~= color4;
float[] colors = convertColors([color1, color2, color3, color4]);
float dstx0 = cast(float)dstrc.left;
float dsty0 = cast(float)(glSupport.currentFBO ? dstrc.top : (glSupport.bufferDy - dstrc.top));
float dstx1 = cast(float)dstrc.right;
float dsty1 = cast(float)(glSupport.currentFBO ? dstrc.bottom : (glSupport.bufferDy - dstrc.bottom));
float srcx0 = srcrc.left / cast(float)textureDx;
float srcy0 = srcrc.top / cast(float)textureDy;
float srcx1 = srcrc.right / cast(float)textureDx;
float srcy1 = srcrc.bottom / cast(float)textureDy;
float[3 * 4] vertices = [
dstx0,dsty0,Z_2D,
dstx0,dsty1,Z_2D,
dstx1,dsty0,Z_2D,
dstx1,dsty1,Z_2D ];
float[2 * 4] texCoords = [srcx0,srcy0, srcx0,srcy1, srcx1,srcy0, srcx1,srcy1];
int[] indices = makeRectangleIndicesArray(cast(int)_vertices.length / 3);
mixin(calcLengthAndAppendAllToBuffer);
}
/// add solid rect
/// add solid rectangle to queue
void addSolidRect(Rect dstRect, uint color) {
addGradientRect(dstRect, color, color, color, color);
}
/// add gradient rect
void addGradientRect(Rect dstRect, uint color1, uint color2, uint color3, uint color4) {
if (_currentTexture)
flush();
_dstRects ~= dstRect;
_colors ~= color1;
_colors ~= color2;
_colors ~= color3;
_colors ~= color4;
/// add gradient rectangle to queue
void addGradientRect(Rect rc, uint color1, uint color2, uint color3, uint color4) {
if (batches is null || batches[$-1].type != OpenGLBatch.BatchType.Rect) {
batches ~= OpenGLBatch();
batches[$-1].type = OpenGLBatch.BatchType.Rect;
if(batches.length > 1)
batches[$-1].start = batches[$-2].start + batches[$-2].length;
}
float[] colors = convertColors([color1, color2, color3, color4]);
float x0 = cast(float)(rc.left);
float y0 = cast(float)(glSupport.currentFBO ? rc.top : (glSupport.bufferDy - rc.top));
float x1 = cast(float)(rc.right);
float y1 = cast(float)(glSupport.currentFBO ? rc.bottom : (glSupport.bufferDy - rc.bottom));
float[3 * 4] vertices = [
x0,y0,Z_2D,
x0,y1,Z_2D,
x1,y0,Z_2D,
x1,y1,Z_2D ];
// fill texture coords buffer with zeros
float[2 * 4] texCoords = 0;
int[] indices = makeRectangleIndicesArray(cast(int)_vertices.length / 3);
mixin(calcLengthAndAppendAllToBuffer);
}
/// add triangle to queue
void addTriangle(PointF p1, PointF p2, PointF p3, uint color1, uint color2, uint color3) {
flush();
// TODO: batch triangles
glSupport.drawSolidFillTriangles([p1, p2, p3], [color1, color2, color3]);
if (batches is null || batches[$-1].type != OpenGLBatch.BatchType.Triangle) {
batches ~= OpenGLBatch();
batches[$-1].type = OpenGLBatch.BatchType.Triangle;
if(batches.length > 1)
batches[$-1].start = batches[$-2].start + batches[$-2].length;
}
float[] colors = convertColors([color1, color2, color3]);
float x0 = p1.x;
float y0 = glSupport.currentFBO ? p1.y : (glSupport.bufferDy - p1.y);
float x1 = p2.x;
float y1 = glSupport.currentFBO ? p2.y : (glSupport.bufferDy - p2.y);
float x2 = p3.x;
float y2 = glSupport.currentFBO ? p3.y : (glSupport.bufferDy - p3.y);
float[3 * 3] vertices = [
x0,y0,Z_2D,
x1,y1,Z_2D,
x2,y2,Z_2D ];
// fill texture coords buffer with zeros
float[2 * 3] texCoords = 0;
int[] indices = makeTriangleIndicesArray(cast(int)_vertices.length / 3);
mixin(calcLengthAndAppendAllToBuffer);
}
/// add gradient rect
void addLine(Rect dstRect, uint color1, uint color2) {
flush();
// TODO: batch lines, too
glSupport.drawLines([dstRect], [color1, color2]);
/// add line to queue
/// rc is a line (left, top) - (right, bottom)
void addLine(Rect rc, uint color1, uint color2) {
if (batches is null || batches[$-1].type != OpenGLBatch.BatchType.Line) {
batches ~= OpenGLBatch();
batches[$-1].type = OpenGLBatch.BatchType.Line;
if(batches.length > 1)
batches[$-1].start = batches[$-2].start + batches[$-2].length;
}
float[] colors = convertColors([color1, color2]);
float x0 = cast(float)(rc.left);
float y0 = cast(float)(glSupport.currentFBO ? rc.top : (glSupport.bufferDy - rc.top));
float x1 = cast(float)(rc.right);
float y1 = cast(float)(glSupport.currentFBO ? rc.bottom : (glSupport.bufferDy - rc.bottom));
float[3 * 2] vertices = [
x0, y0, Z_2D,
x1, y1, Z_2D ];
// fill texture coords buffer with zeros
float[2 * 2] texCoords = 0;
int[] indices = makeLineIndicesArray(cast(int)_vertices.length / 3);
mixin(calcLengthAndAppendAllToBuffer);
}
enum calcLengthAndAppendAllToBuffer = q{
batches[$-1].length += cast(int)indices.length;
_vertices ~= vertices;
_colors ~= colors;
_texCoords ~= texCoords;
_indices ~= indices;
};
/// make indices for rectangle (2 triangles == 6 vertexes per rect)
int[6] makeRectangleIndicesArray(int offset) {
int[6] indices;
indices[0] = offset + 0;
indices[1] = offset + 1;
indices[2] = offset + 2;
indices[3] = offset + 1;
indices[4] = offset + 2;
indices[5] = offset + 3;
return indices;
}
/// make indices for triangles
int[3] makeTriangleIndicesArray(int offset) {
int[3] indices;
indices[0] = offset + 0;
indices[1] = offset + 1;
indices[2] = offset + 2;
return indices;
}
/// make indices for lines
int[2] makeLineIndicesArray(int offset) {
int[2] indices;
indices[0] = offset + 0;
indices[1] = offset + 1;
return indices;
}
}