Skip to content

Commit 78898c9

Browse files
committed
Implement batched rendering in OpenGL
1 parent 633327c commit 78898c9

File tree

4 files changed

+201
-99
lines changed

4 files changed

+201
-99
lines changed

src/plugins/opengl/src/opengl_batch.cpp

+170-94
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "growl/core/graphics/window.h"
99
#include "opengl_shader.h"
1010
#include "opengl_texture.h"
11+
#include <OpenGL/OpenGL.h>
1112
#include <cmath>
1213
#include <vector>
1314

@@ -36,7 +37,8 @@ OpenGLBatch::OpenGLBatch(
3637
glGenBuffers(1, &ubo);
3738
glBindBuffer(GL_UNIFORM_BUFFER, ubo);
3839
glBufferData(
39-
GL_UNIFORM_BUFFER, 2 * sizeof(glm::mat4), NULL, GL_STATIC_DRAW);
40+
GL_UNIFORM_BUFFER, (MAX_BATCH_SIZE + 1) * sizeof(glm::mat4), NULL,
41+
GL_STATIC_DRAW);
4042
glBindBuffer(GL_UNIFORM_BUFFER, 0);
4143
}
4244

@@ -68,9 +70,11 @@ void OpenGLBatch::begin() {
6870
glBindBufferRange(GL_UNIFORM_BUFFER, 0, ubo, 0, sizeof(glm::mat4));
6971
glBufferSubData(
7072
GL_UNIFORM_BUFFER, 0, sizeof(glm::mat4), glm::value_ptr(projection));
73+
glBindBuffer(GL_UNIFORM_BUFFER, 0);
7174
}
7275

7376
void OpenGLBatch::end() {
77+
flush();
7478
glBindVertexArray(0);
7579
if (fbo) {
7680
glBindFramebuffer(GL_FRAMEBUFFER, 0);
@@ -81,82 +85,106 @@ void OpenGLBatch::setColor(float r, float g, float b, float a) {
8185
color = {r, g, b, a};
8286
}
8387

84-
void OpenGLBatch::setTransform(glm::mat4x4 transform) {
85-
glBindBuffer(GL_UNIFORM_BUFFER, ubo);
86-
glBufferSubData(
87-
GL_UNIFORM_BUFFER, sizeof(glm::mat4), sizeof(glm::mat4),
88-
glm::value_ptr(transform));
89-
glBindBuffer(GL_UNIFORM_BUFFER, 0);
90-
}
91-
9288
void OpenGLBatch::draw(
9389
const Texture& texture, float x, float y, float width, float height,
9490
glm::mat4x4 transform) {
9591
auto& tex = static_cast<const OpenGLTexture&>(texture);
96-
tex.bind();
97-
setTransform(transform);
98-
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
99-
glEnable(GL_BLEND);
92+
if (&tex != bound_tex || default_shader != bound_shader) {
93+
flush();
94+
}
95+
uniforms.insert(uniforms.end(), SpriteBlock{transform});
96+
bound_tex = &tex;
97+
bound_shader = default_shader;
10098
float right = x + width;
10199
float bottom = y + height;
102-
float quad_vertex_data[] = {
103-
x, y, 0.0f, 0.0f, // Top-left
104-
right, y, 1.0f, 0.0f, // Top-right
105-
right, bottom, 1.0f, 1.0f, // Bottom-right
106-
x, bottom, 0.0f, 1.0f // Bottom-left
107-
};
108-
GLuint elements[] = {0, 1, 2, 2, 3, 0};
109-
glBindBuffer(GL_ARRAY_BUFFER, vbo);
110-
glBufferData(
111-
GL_ARRAY_BUFFER, sizeof(quad_vertex_data), quad_vertex_data,
112-
GL_STATIC_DRAW);
113-
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
114-
glBufferData(
115-
GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);
116-
default_shader->bind(color);
117-
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
100+
vertices.insert(
101+
vertices.end(), {
102+
x,
103+
y,
104+
0.0f,
105+
0.0f,
106+
static_cast<GLfloat>(idx), // Top-left
107+
right,
108+
y,
109+
1.0f,
110+
0.0f,
111+
static_cast<GLfloat>(idx), // Top-right
112+
right,
113+
bottom,
114+
1.0f,
115+
1.0f,
116+
static_cast<GLfloat>(idx), // Bottom-right
117+
x,
118+
bottom,
119+
0.0f,
120+
1.0f,
121+
static_cast<GLfloat>(idx) // Bottom-left
122+
});
123+
elements.insert(
124+
elements.end(),
125+
{verts, verts + 1, verts + 2, verts + 2, verts + 3, verts});
126+
idx++;
127+
verts += 4;
118128
}
119129

120130
void OpenGLBatch::draw(
121131
const TextureAtlasRegion& region, float x, float y, float width,
122132
float height, glm::mat4x4 transform) {
123133
auto& tex = static_cast<const OpenGLTexture&>(region.atlas->getTexture());
124-
tex.bind();
125-
setTransform(transform);
126-
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
127-
glEnable(GL_BLEND);
134+
if (&tex != bound_tex || default_shader != bound_shader) {
135+
flush();
136+
}
137+
uniforms.insert(uniforms.end(), SpriteBlock{transform});
138+
bound_tex = &tex;
139+
bound_shader = default_shader;
128140
float right = x + width;
129141
float bottom = y + height;
130-
float quad_vertex_data[] = {
131-
x, y, region.region.u0, region.region.v0,
132-
right, y, region.region.u1, region.region.v0,
133-
right, bottom, region.region.u1, region.region.v1,
134-
x, bottom, region.region.u0, region.region.v1};
135-
GLuint elements[] = {0, 1, 2, 2, 3, 0};
136-
glBindBuffer(GL_ARRAY_BUFFER, vbo);
137-
glBufferData(
138-
GL_ARRAY_BUFFER, sizeof(quad_vertex_data), quad_vertex_data,
139-
GL_STATIC_DRAW);
140-
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
141-
glBufferData(
142-
GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);
143-
default_shader->bind(color);
144-
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
142+
vertices.insert(
143+
vertices.end(), {
144+
x,
145+
y,
146+
region.region.u0,
147+
region.region.v0,
148+
static_cast<GLfloat>(idx),
149+
right,
150+
y,
151+
region.region.u1,
152+
region.region.v0,
153+
static_cast<GLfloat>(idx),
154+
right,
155+
bottom,
156+
region.region.u1,
157+
region.region.v1,
158+
static_cast<GLfloat>(idx),
159+
x,
160+
bottom,
161+
region.region.u0,
162+
region.region.v1,
163+
static_cast<GLfloat>(idx),
164+
});
165+
elements.insert(
166+
elements.end(),
167+
{verts, verts + 1, verts + 2, verts + 2, verts + 3, verts});
168+
idx++;
169+
verts += 4;
145170
}
146171

147172
void OpenGLBatch::draw(
148173
const GlyphLayout& glyph_layout, const FontTextureAtlas& font_texture_atlas,
149174
float x, float y, glm::mat4x4 transform) {
150175
auto& tex =
151176
static_cast<const OpenGLTexture&>(font_texture_atlas.getTexture());
152-
tex.bind();
153-
setTransform(transform);
154-
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
155-
glEnable(GL_BLEND);
177+
auto shader = font_texture_atlas.getType() == FontFaceType::MSDF
178+
? sdf_shader
179+
: default_shader;
180+
if (&tex != bound_tex || shader != bound_shader) {
181+
flush();
182+
}
183+
uniforms.insert(uniforms.end(), SpriteBlock{transform});
184+
bound_tex = &tex;
185+
bound_shader = shader;
156186

157-
std::vector<float> vertices;
158-
std::vector<GLuint> indices;
159-
GLuint i = 0;
187+
GLuint i = verts;
160188
for (auto& glyph : glyph_layout.getLayout()) {
161189
float gx = std::round(x + glyph.x);
162190
float gy = std::round(y + glyph.y);
@@ -172,28 +200,16 @@ void OpenGLBatch::draw(
172200
auto& region = region_result.get();
173201

174202
vertices.insert(
175-
vertices.end(), {gx, gy, region.u0, region.v0, right, gy, region.u1,
176-
region.v0, right, bottom, region.u1, region.v1, gx,
177-
bottom, region.u0, region.v1});
178-
indices.insert(indices.end(), {i, i + 1, i + 2, i + 2, i + 3, i});
203+
vertices.end(),
204+
{gx, gy, region.u0, region.v0, static_cast<GLfloat>(idx),
205+
right, gy, region.u1, region.v0, static_cast<GLfloat>(idx),
206+
right, bottom, region.u1, region.v1, static_cast<GLfloat>(idx),
207+
gx, bottom, region.u0, region.v1, static_cast<GLfloat>(idx)});
208+
elements.insert(elements.end(), {i, i + 1, i + 2, i + 2, i + 3, i});
179209
i += 4;
210+
verts = i;
180211
}
181-
182-
glBindBuffer(GL_ARRAY_BUFFER, vbo);
183-
glBufferData(
184-
GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(),
185-
GL_STATIC_DRAW);
186-
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
187-
glBufferData(
188-
GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint),
189-
indices.data(), GL_STATIC_DRAW);
190-
if (font_texture_atlas.getType() == FontFaceType::MSDF) {
191-
sdf_shader->bind(color);
192-
} else {
193-
default_shader->bind(color);
194-
}
195-
glDrawElements(
196-
GL_TRIANGLES, static_cast<GLsizei>(indices.size()), GL_UNSIGNED_INT, 0);
212+
idx++;
197213
}
198214

199215
void OpenGLBatch::drawRect(
@@ -204,28 +220,45 @@ void OpenGLBatch::drawRect(
204220
void OpenGLBatch::drawRect(
205221
float x, float y, float width, float height, Shader& shader,
206222
glm::mat4x4 transform) {
207-
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
208-
glEnable(GL_BLEND);
209-
setTransform(transform);
223+
224+
auto& gl_shader = static_cast<OpenGLShader&>(shader);
225+
uniforms.insert(uniforms.end(), SpriteBlock{transform});
226+
227+
if (bound_tex || bound_shader != &gl_shader) {
228+
flush();
229+
}
230+
bound_shader = &gl_shader;
210231

211232
float right = x + width;
212233
float bottom = y + height;
213-
float quad_vertex_data[] = {
214-
x, y, 0.0f, 0.0f, // Top-left
215-
right, y, 1.0f, 0.0f, // Top-right
216-
right, bottom, 1.0f, 1.0f, // Bottom-right
217-
x, bottom, 0.0f, 1.0f // Bottom-left
218-
};
219-
GLuint elements[] = {0, 1, 2, 2, 3, 0};
220-
glBindBuffer(GL_ARRAY_BUFFER, vbo);
221-
glBufferData(
222-
GL_ARRAY_BUFFER, sizeof(quad_vertex_data), quad_vertex_data,
223-
GL_STATIC_DRAW);
224-
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
225-
glBufferData(
226-
GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);
227-
static_cast<OpenGLShader&>(shader).bind(color);
228-
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
234+
vertices.insert(
235+
vertices.end(), {
236+
x,
237+
y,
238+
0.0f,
239+
0.0f,
240+
static_cast<GLfloat>(idx), // Top-left
241+
right,
242+
y,
243+
1.0f,
244+
0.0f,
245+
static_cast<GLfloat>(idx), // Top-right
246+
right,
247+
bottom,
248+
1.0f,
249+
1.0f,
250+
static_cast<GLfloat>(idx), // Bottom-right
251+
x,
252+
bottom,
253+
0.0f,
254+
1.0f,
255+
static_cast<GLfloat>(idx) // Bottom-left
256+
});
257+
elements.insert(
258+
elements.end(),
259+
{verts, verts + 1, verts + 2, verts + 2, verts + 3, verts});
260+
verts += 4;
261+
idx++;
229262
}
230263

231264
int OpenGLBatch::getTargetWidth() {
@@ -235,3 +268,46 @@ int OpenGLBatch::getTargetWidth() {
235268
int OpenGLBatch::getTargetHeight() {
236269
return height;
237270
}
271+
272+
void OpenGLBatch::flush() {
273+
if (!idx) {
274+
return;
275+
}
276+
277+
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
278+
glEnable(GL_BLEND);
279+
280+
glBindBuffer(GL_ARRAY_BUFFER, vbo);
281+
glBufferData(
282+
GL_ARRAY_BUFFER, vertices.size() * sizeof(GLfloat), vertices.data(),
283+
GL_STATIC_DRAW);
284+
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
285+
glBufferData(
286+
GL_ELEMENT_ARRAY_BUFFER, elements.size() * sizeof(GLuint),
287+
elements.data(), GL_STATIC_DRAW);
288+
289+
if (uniforms.size()) {
290+
glBindBuffer(GL_UNIFORM_BUFFER, ubo);
291+
glBufferSubData(
292+
GL_UNIFORM_BUFFER, sizeof(glm::mat4),
293+
sizeof(SpriteBlock) * uniforms.size(), uniforms.data());
294+
glBindBuffer(GL_UNIFORM_BUFFER, 0);
295+
}
296+
297+
if (bound_tex) {
298+
bound_tex->bind();
299+
}
300+
if (bound_shader) {
301+
bound_shader->bind(color);
302+
}
303+
304+
glDrawElements(GL_TRIANGLES, elements.size(), GL_UNSIGNED_INT, 0);
305+
306+
idx = 0;
307+
verts = 0;
308+
vertices.clear();
309+
elements.clear();
310+
uniforms.clear();
311+
bound_tex = nullptr;
312+
bound_shader = nullptr;
313+
}

src/plugins/opengl/src/opengl_batch.h

+14-1
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,16 @@ namespace Growl {
99

1010
class Window;
1111
class OpenGLShader;
12+
class OpenGLTexture;
1213
class FontTextureAtlas;
1314
class GlyphLayout;
1415
class Texture;
1516
struct TextureAtlasRegion;
1617

18+
struct SpriteBlock {
19+
glm::mat4x4 transform;
20+
};
21+
1722
class OpenGLBatch : public Batch {
1823
public:
1924
OpenGLBatch(
@@ -63,7 +68,15 @@ class OpenGLBatch : public Batch {
6368
GLuint fbo = 0;
6469
GLuint ubo = 0;
6570

66-
void setTransform(glm::mat4x4 transform);
71+
unsigned int idx = 0;
72+
unsigned int verts = 0;
73+
const OpenGLTexture* bound_tex = nullptr;
74+
OpenGLShader* bound_shader = nullptr;
75+
std::vector<GLfloat> vertices;
76+
std::vector<GLuint> elements;
77+
std::vector<SpriteBlock> uniforms;
78+
79+
void flush();
6780
};
6881

6982
} // namespace Growl

0 commit comments

Comments
 (0)