Skip to content

Commit 9e8f947

Browse files
committed
Add shader-based viewports emulating API viewports
Viewport clipping can be emulated in shaders by passing in the viewport extents via primitive attributes and discarding fragments in the fragment shader with `discard` statements. This allows us to merge more draw calls across previously uncrossable batch boundaries that arose from viewport changes as those caused GPU state changes that must be between draw calls.
1 parent 24dcef9 commit 9e8f947

1 file changed

Lines changed: 51 additions & 37 deletions

File tree

engine/render/r_main.cpp

Lines changed: 51 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ struct Vertex {
222222
float x, y;
223223
float u, v;
224224
float r, g, b, a;
225+
float viewX, viewY, viewW, viewH;
225226
float texId;
226227
};
227228

@@ -238,6 +239,7 @@ struct Batch {
238239
GLint uvAttr;
239240
GLint tintAttr;
240241
GLint texIdAttr;
242+
GLint viewportAttr;
241243

242244
std::vector<Vertex> vertices;
243245

@@ -251,13 +253,15 @@ Batch::Batch(GLuint prog)
251253
uvAttr = glGetAttribLocation(prog, "a_texcoord");
252254
tintAttr = glGetAttribLocation(prog, "a_tint");
253255
texIdAttr = glGetAttribLocation(prog, "a_texId");
256+
viewportAttr = glGetAttribLocation(prog, "a_viewport");
254257
}
255258

256259
Batch::Batch(Batch&& rhs)
257260
: prog(rhs.prog)
258261
, xyAttr(rhs.xyAttr)
259262
, uvAttr(rhs.uvAttr)
260263
, tintAttr(rhs.tintAttr)
264+
, viewportAttr(rhs.viewportAttr)
261265
, vertices(std::move(rhs.vertices))
262266
{
263267
}
@@ -267,6 +271,8 @@ Batch& Batch::operator = (Batch&& rhs) {
267271
xyAttr = rhs.xyAttr;
268272
uvAttr = rhs.uvAttr;
269273
tintAttr = rhs.tintAttr;
274+
texIdAttr = rhs.texIdAttr;
275+
viewportAttr = rhs.viewportAttr;
270276
vertices = std::move(rhs.vertices);
271277

272278
return *this;
@@ -289,15 +295,18 @@ void Batch::Execute(GLuint sharedVbo, size_t vertexBase)
289295
glVertexAttribPointer(uvAttr, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void const*)offsetof(Vertex, u));
290296
glVertexAttribPointer(tintAttr, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void const*)offsetof(Vertex, r));
291297
glVertexAttribPointer(texIdAttr, 1, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void const*)offsetof(Vertex, texId));
298+
glVertexAttribPointer(viewportAttr, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void const*)offsetof(Vertex, viewX));
292299
glEnableVertexAttribArray(xyAttr);
293300
glEnableVertexAttribArray(uvAttr);
294301
glEnableVertexAttribArray(tintAttr);
295302
glEnableVertexAttribArray(texIdAttr);
303+
glEnableVertexAttribArray(viewportAttr);
296304
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)vertices.size());
297305
glDisableVertexAttribArray(xyAttr);
298306
glDisableVertexAttribArray(uvAttr);
299307
glDisableVertexAttribArray(tintAttr);
300308
glDisableVertexAttribArray(texIdAttr);
309+
glDisableVertexAttribArray(viewportAttr);
301310
glBindBuffer(GL_ARRAY_BUFFER, 0);
302311
vertices.clear();
303312
}
@@ -340,22 +349,9 @@ struct AdjacentMergeStrategy : RenderStrategy {
340349
}
341350

342351
struct BatchKey {
343-
r_viewport_s viewport = { -1, -1, -1, -1 };
344352
int blendMode = -1;
345353

346354
bool operator < (BatchKey const& rhs) const {
347-
if (viewport.x != rhs.viewport.x) {
348-
return viewport.x < rhs.viewport.x;
349-
}
350-
if (viewport.y != rhs.viewport.y) {
351-
return viewport.y < rhs.viewport.y;
352-
}
353-
if (viewport.width != rhs.viewport.width) {
354-
return viewport.width < rhs.viewport.width;
355-
}
356-
if (viewport.height != rhs.viewport.height) {
357-
return viewport.height < rhs.viewport.height;
358-
}
359355
return blendMode < rhs.blendMode;
360356
}
361357

@@ -371,7 +367,7 @@ struct AdjacentMergeStrategy : RenderStrategy {
371367
void ProcessCommand(r_layerCmd_s* cmd) override {
372368
switch (cmd->cmd) {
373369
case r_layerCmd_s::VIEWPORT:
374-
latchKey_.viewport = cmd->viewport;
370+
nextViewport_ = cmd->viewport;
375371
if (showStats_) {
376372
// ImGui::Text("VIEWPORT: %dx%d @ %d,%d", cmd->viewport.width, cmd->viewport.height, cmd->viewport.x, cmd->viewport.y);
377373
}
@@ -421,15 +417,21 @@ struct AdjacentMergeStrategy : RenderStrategy {
421417

422418
Vertex quad[4];
423419
for (int v = 0; v < 4; v++) {
424-
quad[v].u = (float)cmd->quad.s[v];
425-
quad[v].v = (float)cmd->quad.t[v];
426-
quad[v].x = (float)cmd->quad.x[v];
427-
quad[v].y = (float)cmd->quad.y[v];
428-
quad[v].r = tint_[0];
429-
quad[v].g = tint_[1];
430-
quad[v].b = tint_[2];
431-
quad[v].a = tint_[3];
432-
quad[v].texId = (float)texSlot;
420+
auto& q = quad[v];
421+
auto& vp = nextViewport_;
422+
q.u = (float)cmd->quad.s[v];
423+
q.v = (float)cmd->quad.t[v];
424+
q.x = (float)cmd->quad.x[v];
425+
q.y = (float)cmd->quad.y[v];
426+
q.r = tint_[0];
427+
q.g = tint_[1];
428+
q.b = tint_[2];
429+
q.a = tint_[3];
430+
q.texId = (float)texSlot;
431+
q.viewX = (float)vp.x;
432+
q.viewY = (float)vp.y;
433+
q.viewW = (float)vp.width;
434+
q.viewH = (float)vp.height;
433435
}
434436
// 3-2
435437
// |/|
@@ -468,23 +470,15 @@ struct AdjacentMergeStrategy : RenderStrategy {
468470
ImGui::Text("Batch %d", batchIndex);
469471
ImGui::Text("%d verts", batch.vertices.size());
470472
}
471-
if (!lastKey || lastKey->viewport.x != key.viewport.x || lastKey->viewport.y != key.viewport.y ||
472-
lastKey->viewport.width != key.viewport.width || lastKey->viewport.height != key.viewport.height)
473+
473474
{
474-
if (showStats_) {
475-
ImGui::Text("New viewport %dx%d @ %d,%d",
476-
key.viewport.width, key.viewport.height, key.viewport.x, key.viewport.y);
477-
}
478475
auto& vid = renderer_->sys->video->vid;
479476
float fbScaleX = vid.fbSize[0] / (float)vid.size[0];
480477
float fbScaleY = vid.fbSize[1] / (float)vid.size[1];
478+
int virtualW = renderer_->VirtualScreenWidth();
481479
int virtualH = renderer_->VirtualScreenHeight();
482-
float x = key.viewport.x * fbScaleX;
483-
float y = (virtualH - key.viewport.y - key.viewport.height) * fbScaleY;
484-
float width = key.viewport.width * fbScaleX;
485-
float height = key.viewport.height * fbScaleY;
486-
glViewport((int)x, (int)y, (int)width, (int)height);
487-
Mat4 mvpMatrix = OrthoMatrix(0, key.viewport.width, key.viewport.height, 0, -9999, 9999);
480+
glViewport(0, 0, virtualW, virtualH);
481+
Mat4 mvpMatrix = OrthoMatrix(0, virtualW, virtualH, 0, -9999, 9999);
488482
glUniformMatrix4fv(mvpMatrixLoc_, 1, GL_FALSE, mvpMatrix.data());
489483
}
490484
if (!lastKey || lastKey->blendMode != key.blendMode) {
@@ -552,6 +546,7 @@ struct AdjacentMergeStrategy : RenderStrategy {
552546
};
553547

554548
BatchKey latchKey_{};
549+
r_viewport_s nextViewport_{};
555550
r_tex_c* nextTex_{};
556551
std::optional<BatchKey> lastDispatchKey_;
557552
TexturedBatch batch_;
@@ -645,7 +640,7 @@ r_renderer_c::r_renderer_c(sys_IMain* sysHnd)
645640
r_compress = sys->con->Cvar_Add("r_compress", CV_ARCHIVE, "0");
646641
r_screenshotFormat = sys->con->Cvar_Add("r_screenshotFormat", CV_ARCHIVE, "jpg");
647642
r_layerDebug = sys->con->Cvar_Add("r_layerDebug", CV_ARCHIVE, "0");
648-
r_layerOptimize = sys->con->Cvar_Add("r_layerOptimize", CV_ARCHIVE | CV_CLAMP, "0", 0, 2);
643+
r_layerOptimize = sys->con->Cvar_Add("r_layerOptimize", CV_ARCHIVE | CV_CLAMP, "1", 0, 1);
649644
r_layerShuffle = sys->con->Cvar_Add("r_layerShuffle", CV_ARCHIVE | CV_CLAMP, "0", 0, 1);
650645
r_elideFrames = sys->con->Cvar_Add("r_elideFrames", CV_ARCHIVE | CV_CLAMP, "1", 0, 1);
651646

@@ -692,17 +687,27 @@ attribute vec2 a_vertex;
692687
attribute vec2 a_texcoord;
693688
attribute vec4 a_tint;
694689
attribute float a_texId;
690+
attribute vec4 a_viewport;
695691
692+
varying vec2 v_screenPos;
696693
varying vec2 v_texcoord;
697694
varying vec4 v_tint;
698695
varying float v_texId;
696+
varying vec4 v_viewport;
699697
700698
void main(void)
701699
{
702700
v_texcoord = a_texcoord;
703701
v_tint = a_tint;
704702
v_texId = a_texId;
705-
gl_Position = mvp_matrix * vec4(a_vertex, 0.0, 1.0);
703+
vec2 vp0 = a_viewport.xy + vec2(0.0, a_viewport.w);
704+
vec2 vp1 = a_viewport.xy + vec2(a_viewport.z, 0.0);
705+
v_viewport = vec4(
706+
(mvp_matrix * vec4(vp0, 0.0, 1.0)).xy,
707+
(mvp_matrix * vec4(vp1, 0.0, 1.0)).xy);
708+
vec4 pos = mvp_matrix * vec4(a_vertex + a_viewport.xy, 0.0, 1.0);
709+
v_screenPos = pos.xy;
710+
gl_Position = pos;
706711
}
707712
)";
708713

@@ -712,12 +717,21 @@ precision mediump float;
712717
uniform sampler2D s_tex[{SG_TEXTURE_COUNT}];
713718
uniform vec4 i_tint;
714719
720+
varying vec2 v_screenPos;
715721
varying vec2 v_texcoord;
716722
varying vec4 v_tint;
717723
varying float v_texId;
724+
varying vec4 v_viewport; // x0, y0, x1, y1
718725
719726
void main(void)
720727
{{
728+
float x = v_screenPos[0], y = v_screenPos[1];
729+
if (x < v_viewport[0] ||
730+
y < v_viewport[1] ||
731+
x >= v_viewport[2] ||
732+
y >= v_viewport[3]) {{
733+
discard;
734+
}}
721735
vec4 color;
722736
{SG_TEXTURE_SWITCH}
723737
gl_FragColor = color * v_tint;

0 commit comments

Comments
 (0)