all repos — mgba @ ef2a2e5002e14598d7f67e8dc47c7ffc20b79242

mGBA Game Boy Advance Emulator

GBA Video: Move window drawing to a shader
Vicki Pfau vi@endrift.com
Mon, 27 May 2019 20:48:42 -0700
commit

ef2a2e5002e14598d7f67e8dc47c7ffc20b79242

parent

ba2d702fb567134cf5a3b36b9360a57852ad12b7

2 files changed, 171 insertions(+), 140 deletions(-)

jump to
M include/mgba/internal/gba/renderers/gl.hinclude/mgba/internal/gba/renderers/gl.h

@@ -112,6 +112,12 @@ GBA_GL_OBJ_DIMS,

GBA_GL_OBJ_OBJWIN, GBA_GL_OBJ_MOSAIC, + GBA_GL_WIN_DISPCNT = 2, + GBA_GL_WIN_BLEND, + GBA_GL_WIN_FLAGS, + GBA_GL_WIN_WIN0, + GBA_GL_WIN_WIN1, + GBA_GL_FINALIZE_SCALE = 2, GBA_GL_FINALIZE_LAYERS, GBA_GL_FINALIZE_FLAGS,

@@ -157,6 +163,7 @@ uint64_t regsDirty;

struct GBAVideoGLShader bgShader[6]; struct GBAVideoGLShader objShader[2]; + struct GBAVideoGLShader windowShader; struct GBAVideoGLShader finalizeShader; GBARegisterDISPCNT dispcnt;

@@ -173,10 +180,12 @@

GBAMosaicControl mosaic; struct GBAVideoGLWindowN { - struct GBAVideoWindowRegion h[2]; + struct GBAVideoWindowRegion h; struct GBAVideoWindowRegion v; GBAWindowControl control; } winN[2]; + + GLint winNHistory[2][GBA_VIDEO_VERTICAL_PIXELS * 4]; GBAWindowControl winout; GBAWindowControl objwin;
M src/gba/renderers/gl.csrc/gba/renderers/gl.c

@@ -496,6 +496,75 @@ " gl_FragDepth = float(flags.x) / 16.;\n"

" window = objwin.yzw;\n" "}"; +static const struct GBAVideoGLUniform _uniformsWindow[] = { + { "loc", GBA_GL_VS_LOC, }, + { "maxPos", GBA_GL_VS_MAXPOS, }, + { "dispcnt", GBA_GL_WIN_DISPCNT, }, + { "blend", GBA_GL_WIN_BLEND, }, + { "flags", GBA_GL_WIN_FLAGS, }, + { "win0", GBA_GL_WIN_WIN0, }, + { "win1", GBA_GL_WIN_WIN1, }, + { 0 } +}; + +static const char* const _renderWindow = + "in vec2 texCoord;\n" + "uniform int dispcnt;\n" + "uniform ivec2 blend;\n" + "uniform ivec3 flags;\n" + "uniform ivec4 win0[160];\n" + "uniform ivec4 win1[160];\n" + "OUT(0) out ivec3 window;\n" + + "void crop(vec4 windowParams, int flags, inout ivec3 windowFlags) {\n" + " bvec4 compare = lessThan(texCoord.xxyy, windowParams);\n" + " compare = equal(compare, bvec4(true, false, true, false));\n" + " if (any(compare)) {\n" + " vec2 h = windowParams.xy;\n" + " vec2 v = windowParams.zw;\n" + " if (v.x > v.y) {\n" + " if (compare.z && compare.w) {\n" + " return;\n" + " }\n" + " } else if (compare.z || compare.w) {\n" + " return;\n" + " }\n" + " if (h.x > h.y) {\n" + " if (compare.x && compare.y) {\n" + " return;\n" + " }\n" + " } else if (compare.x || compare.y) {\n" + " return;\n" + " }\n" + " }\n" + " windowFlags.x = flags;\n" + "}\n" + + "vec4 interpolate(ivec4 win[160]) {\n" + " vec4 bottom = vec4(win[int(texCoord.y) - 1]);\n" + " vec4 top = vec4(win[int(texCoord.y)]);\n" + " if (distance(top, bottom) > 40.) {\n" + " return top;\n" + " }\n" + " return vec4(mix(bottom.xy, top.xy, fract(texCoord.y)), top.zw);\n" + "}\n" + + "void main() {\n" + " int dispflags = (dispcnt & 0x1F) | 0x20;\n" + " if ((dispcnt & 0xE0) == 0) {\n" + " window = ivec3(dispflags, blend);\n" + " } else {\n" + " ivec3 windowFlags = ivec3(flags.z, blend);\n" + " if ((dispcnt & 0x40) != 0) { \n" + " crop(interpolate(win1), flags.y, windowFlags);\n" + " }\n" + " if ((dispcnt & 0x20) != 0) { \n" + " crop(interpolate(win0), flags.x, windowFlags);\n" + " }\n" + " window = windowFlags;\n" + " }\n" + "}\n"; + static const struct GBAVideoGLUniform _uniformsFinalize[] = { { "loc", GBA_GL_VS_LOC, }, { "maxPos", GBA_GL_VS_MAXPOS, },

@@ -805,6 +874,12 @@ #ifndef BUILD_GLES3

glBindFragDataLocation(glRenderer->objShader[1].program, 2, "window"); #endif + shaderBuffer[1] = _renderWindow; + _compileShader(glRenderer, &glRenderer->windowShader, shaderBuffer, 2, vs, _uniformsWindow, log); +#ifndef BUILD_GLES3 + glBindFragDataLocation(glRenderer->windowShader.program, 0, "window"); +#endif + shaderBuffer[1] = _finalize; _compileShader(glRenderer, &glRenderer->finalizeShader, shaderBuffer, 2, vs, _uniformsFinalize, log);

@@ -878,14 +953,16 @@ if (renderer->cache) {

GBAVideoCacheWriteVideoRegister(renderer->cache, address, value); } - bool dirty = true; + bool dirty = false; switch (address) { case REG_DISPCNT: value &= 0xFFF7; + dirty = true; break; case REG_BG0CNT: case REG_BG1CNT: value &= 0xDFFF; + dirty = true; break; case REG_BG0HOFS: case REG_BG0VOFS:

@@ -896,87 +973,126 @@ case REG_BG2VOFS:

case REG_BG3HOFS: case REG_BG3VOFS: value &= 0x01FF; + dirty = true; break; case REG_BG2PA: glRenderer->bg[2].affine.dx = value; - dirty = false; break; case REG_BG2PB: glRenderer->bg[2].affine.dmx = value; - dirty = false; break; case REG_BG2PC: glRenderer->bg[2].affine.dy = value; - dirty = false; break; case REG_BG2PD: glRenderer->bg[2].affine.dmy = value; - dirty = false; break; case REG_BG2X_LO: GBAVideoGLRendererWriteBGX_LO(&glRenderer->bg[2], value); - dirty = false; break; case REG_BG2X_HI: GBAVideoGLRendererWriteBGX_HI(&glRenderer->bg[2], value); - dirty = false; break; case REG_BG2Y_LO: GBAVideoGLRendererWriteBGY_LO(&glRenderer->bg[2], value); - dirty = false; break; case REG_BG2Y_HI: GBAVideoGLRendererWriteBGY_HI(&glRenderer->bg[2], value); - dirty = false; break; case REG_BG3PA: glRenderer->bg[3].affine.dx = value; - dirty = false; break; case REG_BG3PB: glRenderer->bg[3].affine.dmx = value; - dirty = false; break; case REG_BG3PC: glRenderer->bg[3].affine.dy = value; - dirty = false; break; case REG_BG3PD: glRenderer->bg[3].affine.dmy = value; - dirty = false; break; case REG_BG3X_LO: GBAVideoGLRendererWriteBGX_LO(&glRenderer->bg[3], value); - dirty = false; break; case REG_BG3X_HI: GBAVideoGLRendererWriteBGX_HI(&glRenderer->bg[3], value); - dirty = false; break; case REG_BG3Y_LO: GBAVideoGLRendererWriteBGY_LO(&glRenderer->bg[3], value); - dirty = false; break; case REG_BG3Y_HI: GBAVideoGLRendererWriteBGY_HI(&glRenderer->bg[3], value); - dirty = false; break; case REG_BLDALPHA: value &= 0x1F1F; + dirty = true; break; case REG_BLDY: value &= 0x1F; if (value > 0x10) { value = 0x10; } + dirty = true; break; - case REG_WININ: - value &= 0x3F3F; + case REG_WIN0H: + glRenderer->winN[0].h.end = value; + glRenderer->winN[0].h.start = value >> 8; + if (glRenderer->winN[0].h.start > GBA_VIDEO_HORIZONTAL_PIXELS && glRenderer->winN[0].h.start > glRenderer->winN[0].h.end) { + glRenderer->winN[0].h.start = 0; + } + if (glRenderer->winN[0].h.end > GBA_VIDEO_HORIZONTAL_PIXELS) { + glRenderer->winN[0].h.end = GBA_VIDEO_HORIZONTAL_PIXELS; + if (glRenderer->winN[0].h.start > GBA_VIDEO_HORIZONTAL_PIXELS) { + glRenderer->winN[0].h.start = GBA_VIDEO_HORIZONTAL_PIXELS; + } + } break; + case REG_WIN1H: + glRenderer->winN[1].h.end = value; + glRenderer->winN[1].h.start = value >> 8; + if (glRenderer->winN[1].h.start > GBA_VIDEO_HORIZONTAL_PIXELS && glRenderer->winN[1].h.start > glRenderer->winN[1].h.end) { + glRenderer->winN[1].h.start = 0; + } + if (glRenderer->winN[1].h.end > GBA_VIDEO_HORIZONTAL_PIXELS) { + glRenderer->winN[1].h.end = GBA_VIDEO_HORIZONTAL_PIXELS; + if (glRenderer->winN[1].h.start > GBA_VIDEO_HORIZONTAL_PIXELS) { + glRenderer->winN[1].h.start = GBA_VIDEO_HORIZONTAL_PIXELS; + } + } + break; + case REG_WIN0V: + glRenderer->winN[0].v.end = value; + glRenderer->winN[0].v.start = value >> 8; + if (glRenderer->winN[0].v.start > GBA_VIDEO_VERTICAL_PIXELS && glRenderer->winN[0].v.start > glRenderer->winN[0].v.end) { + glRenderer->winN[0].v.start = 0; + } + if (glRenderer->winN[0].v.end > GBA_VIDEO_VERTICAL_PIXELS) { + glRenderer->winN[0].v.end = GBA_VIDEO_VERTICAL_PIXELS; + if (glRenderer->winN[0].v.start > GBA_VIDEO_VERTICAL_PIXELS) { + glRenderer->winN[0].v.start = GBA_VIDEO_VERTICAL_PIXELS; + } + } + break; + case REG_WIN1V: + glRenderer->winN[1].v.end = value; + glRenderer->winN[1].v.start = value >> 8; + if (glRenderer->winN[1].v.start > GBA_VIDEO_VERTICAL_PIXELS && glRenderer->winN[1].v.start > glRenderer->winN[1].v.end) { + glRenderer->winN[1].v.start = 0; + } + if (glRenderer->winN[1].v.end > GBA_VIDEO_VERTICAL_PIXELS) { + glRenderer->winN[1].v.end = GBA_VIDEO_VERTICAL_PIXELS; + if (glRenderer->winN[1].v.start > GBA_VIDEO_VERTICAL_PIXELS) { + glRenderer->winN[1].v.start = GBA_VIDEO_VERTICAL_PIXELS; + } + } + break; + case REG_WININ: case REG_WINOUT: value &= 0x3F3F; + dirty = true; break; default: + dirty = true; break; } if (glRenderer->shadowRegs[address >> 1] == value) {

@@ -1050,58 +1166,6 @@ break;

case REG_BLDY: glRenderer->bldy = value; break; - case REG_WIN0H: - glRenderer->winN[0].h[0].end = value; - glRenderer->winN[0].h[0].start = value >> 8; - if (glRenderer->winN[0].h[0].start > GBA_VIDEO_HORIZONTAL_PIXELS && glRenderer->winN[0].h[0].start > glRenderer->winN[0].h[0].end) { - glRenderer->winN[0].h[0].start = 0; - } - if (glRenderer->winN[0].h[0].end > GBA_VIDEO_HORIZONTAL_PIXELS) { - glRenderer->winN[0].h[0].end = GBA_VIDEO_HORIZONTAL_PIXELS; - if (glRenderer->winN[0].h[0].start > GBA_VIDEO_HORIZONTAL_PIXELS) { - glRenderer->winN[0].h[0].start = GBA_VIDEO_HORIZONTAL_PIXELS; - } - } - break; - case REG_WIN1H: - glRenderer->winN[1].h[0].end = value; - glRenderer->winN[1].h[0].start = value >> 8; - if (glRenderer->winN[1].h[0].start > GBA_VIDEO_HORIZONTAL_PIXELS && glRenderer->winN[1].h[0].start > glRenderer->winN[1].h[0].end) { - glRenderer->winN[1].h[0].start = 0; - } - if (glRenderer->winN[1].h[0].end > GBA_VIDEO_HORIZONTAL_PIXELS) { - glRenderer->winN[1].h[0].end = GBA_VIDEO_HORIZONTAL_PIXELS; - if (glRenderer->winN[1].h[0].start > GBA_VIDEO_HORIZONTAL_PIXELS) { - glRenderer->winN[1].h[0].start = GBA_VIDEO_HORIZONTAL_PIXELS; - } - } - break; - case REG_WIN0V: - glRenderer->winN[0].v.end = value; - glRenderer->winN[0].v.start = value >> 8; - if (glRenderer->winN[0].v.start > GBA_VIDEO_VERTICAL_PIXELS && glRenderer->winN[0].v.start > glRenderer->winN[0].v.end) { - glRenderer->winN[0].v.start = 0; - } - if (glRenderer->winN[0].v.end > GBA_VIDEO_VERTICAL_PIXELS) { - glRenderer->winN[0].v.end = GBA_VIDEO_VERTICAL_PIXELS; - if (glRenderer->winN[0].v.start > GBA_VIDEO_VERTICAL_PIXELS) { - glRenderer->winN[0].v.start = GBA_VIDEO_VERTICAL_PIXELS; - } - } - break; - case REG_WIN1V: - glRenderer->winN[1].v.end = value; - glRenderer->winN[1].v.start = value >> 8; - if (glRenderer->winN[1].v.start > GBA_VIDEO_VERTICAL_PIXELS && glRenderer->winN[1].v.start > glRenderer->winN[1].v.end) { - glRenderer->winN[1].v.start = 0; - } - if (glRenderer->winN[1].v.end > GBA_VIDEO_VERTICAL_PIXELS) { - glRenderer->winN[1].v.end = GBA_VIDEO_VERTICAL_PIXELS; - if (glRenderer->winN[1].v.start > GBA_VIDEO_VERTICAL_PIXELS) { - glRenderer->winN[1].v.start = GBA_VIDEO_VERTICAL_PIXELS; - } - } - break; case REG_WININ: glRenderer->winN[0].control = value; glRenderer->winN[1].control = value >> 8;

@@ -1223,8 +1287,6 @@

void GBAVideoGLRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) { struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer; - memcpy(&glRenderer->affine[0][y], &glRenderer->bg[2].affine, sizeof(struct GBAVideoGLAffine)); - memcpy(&glRenderer->affine[1][y], &glRenderer->bg[3].affine, sizeof(struct GBAVideoGLAffine)); if (GBARegisterDISPCNTGetMode(glRenderer->dispcnt) != 0) { if (glRenderer->firstAffine < 0) { glRenderer->firstAffine = y;

@@ -1243,9 +1305,6 @@ if (glRenderer->firstY < 0) {

glRenderer->firstY = y; } - memcpy(&glRenderer->winN[0].h[1], &glRenderer->winN[0].h[0], sizeof(struct GBAVideoWindowRegion)); - memcpy(&glRenderer->winN[1].h[1], &glRenderer->winN[1].h[0], sizeof(struct GBAVideoWindowRegion)); - int i; for (i = 0; i < 0x30; ++i) { if (!(glRenderer->regsDirty & (1ULL << i))) {

@@ -1255,6 +1314,17 @@ _cleanRegister(glRenderer, i << 1, glRenderer->shadowRegs[i]);

} glRenderer->regsDirty = 0; + memcpy(&glRenderer->affine[0][y], &glRenderer->bg[2].affine, sizeof(struct GBAVideoGLAffine)); + memcpy(&glRenderer->affine[1][y], &glRenderer->bg[3].affine, sizeof(struct GBAVideoGLAffine)); + glRenderer->winNHistory[0][y * 4 + 0] = glRenderer->winN[0].h.start; + glRenderer->winNHistory[0][y * 4 + 1] = glRenderer->winN[0].h.end; + glRenderer->winNHistory[0][y * 4 + 2] = glRenderer->winN[0].v.start; + glRenderer->winNHistory[0][y * 4 + 3] = glRenderer->winN[0].v.end; + glRenderer->winNHistory[1][y * 4 + 0] = glRenderer->winN[1].h.start; + glRenderer->winNHistory[1][y * 4 + 1] = glRenderer->winN[1].h.end; + glRenderer->winNHistory[1][y * 4 + 2] = glRenderer->winN[1].v.start; + glRenderer->winNHistory[1][y * 4 + 3] = glRenderer->winN[1].v.end; + if (glRenderer->paletteDirty) { for (i = 0; i < 512; ++i) { glRenderer->shadowPalette[i] = glRenderer->d.palette[i];

@@ -1284,9 +1354,6 @@ glRenderer->oamDirty = false;

} if (y == 0) { - memcpy(&glRenderer->winN[0].h[1], &glRenderer->winN[0].h[0], sizeof(struct GBAVideoWindowRegion)); - memcpy(&glRenderer->winN[1].h[1], &glRenderer->winN[1].h[0], sizeof(struct GBAVideoWindowRegion)); - glDisable(GL_SCISSOR_TEST); glClearColor(0, 0, 0, 0); #ifdef BUILD_GLES3

@@ -1758,68 +1825,23 @@ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 }); } -static void _scissorWindow(struct GBAVideoGLRenderer* renderer, int window, int start, int end, int y, int lines) { - if (start > end) { - _scissorWindow(renderer, window, start, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, y, lines); - _scissorWindow(renderer, window, 0, end, y, lines); - return; - } - glScissor(start, y, end - start, lines); - glClearBufferiv(GL_COLOR, 0, (GLint[]) { window, renderer->bldb, renderer->bldy, 0 }); -} - -static void _scissorWindowN(struct GBAVideoGLRenderer* renderer, const struct GBAVideoGLWindowN* window, const struct GBAVideoWindowRegion* y, int dispcnt) { - int sdelta = window->h[0].start - window->h[1].start; - int edelta = window->h[0].end - window->h[1].end; - int maxDelta = 0; - if (sdelta > maxDelta) { - maxDelta = sdelta; - } else if (-sdelta > maxDelta) { - maxDelta = -sdelta; - } - if (edelta > maxDelta) { - maxDelta = edelta; - } else if (-edelta > maxDelta) { - maxDelta = -edelta; - } - int startY = y->start; - int endY = y->end; - if (startY < window->v.start) { - startY = window->v.start; - } - if (endY >= window->v.end) { - endY = window->v.end - 1; - } - if (!(sdelta | edelta) || maxDelta >= GBA_VIDEO_VERTICAL_PIXELS / 2) { - _scissorWindow(renderer, window->control & dispcnt, window->h[0].start * renderer->scale, window->h[0].end * renderer->scale, startY * renderer->scale, (endY - startY + 1) * renderer->scale); - } else { - int i; - for (i = 0; i < renderer->scale * (endY - startY + 1); ++i) { - int start = window->h[1].start * renderer->scale + sdelta * i; - int end = window->h[1].end * renderer->scale + edelta * i; - _scissorWindow(renderer, window->control & dispcnt, start, end, startY * renderer->scale + i, 1); - } - } -} - void GBAVideoGLRendererDrawWindow(struct GBAVideoGLRenderer* renderer, int y) { + const struct GBAVideoGLShader* shader = &renderer->windowShader; + const GLuint* uniforms = shader->uniforms; glBindFramebuffer(GL_FRAMEBUFFER, renderer->fbo[GBA_GL_FBO_WINDOW]); - int dispcnt = ((renderer->dispcnt >> 8) & 0x1F) | 0x20; - if (!(renderer->dispcnt & 0xE000)) { - _scissorWindow(renderer, dispcnt, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, renderer->firstY * renderer->scale, (y - renderer->firstY + 1) * renderer->scale); - } else { - _scissorWindow(renderer, renderer->winout & dispcnt, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, renderer->firstY * renderer->scale, (y - renderer->firstY + 1) * renderer->scale); - struct GBAVideoWindowRegion yRegion = { - y, - renderer->firstY - }; - if (GBARegisterDISPCNTIsWin1Enable(renderer->dispcnt) && y >= renderer->winN[1].v.start && renderer->firstY < renderer->winN[1].v.end) { - _scissorWindowN(renderer, &renderer->winN[1], &yRegion, dispcnt); - } - if (GBARegisterDISPCNTIsWin0Enable(renderer->dispcnt) && y >= renderer->winN[0].v.start && renderer->firstY < renderer->winN[0].v.end) { - _scissorWindowN(renderer, &renderer->winN[0], &yRegion, dispcnt); - } - } + glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale); + glScissor(0, renderer->firstY * renderer->scale, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, renderer->scale * (y - renderer->firstY + 1)); + glUseProgram(shader->program); + glBindVertexArray(shader->vao); + glUniform2i(uniforms[GBA_GL_VS_LOC], y - renderer->firstY + 1, renderer->firstY); + glUniform2i(uniforms[GBA_GL_VS_MAXPOS], GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS); + glUniform1i(uniforms[GBA_GL_WIN_DISPCNT], renderer->dispcnt >> 8); + glUniform2i(uniforms[GBA_GL_WIN_BLEND], renderer->bldb, renderer->bldy); + glUniform3i(uniforms[GBA_GL_WIN_FLAGS], renderer->winN[0].control, renderer->winN[1].control, renderer->winout); + glUniform4iv(uniforms[GBA_GL_WIN_WIN0], GBA_VIDEO_VERTICAL_PIXELS, renderer->winNHistory[0]); + glUniform4iv(uniforms[GBA_GL_WIN_WIN1], GBA_VIDEO_VERTICAL_PIXELS, renderer->winNHistory[1]); + glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 }); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } #endif