GBA Video: Emulate sprite cycle limits in OpenGL renderer (fixes #1635)
Vicki Pfau vi@endrift.com
Sat, 26 Sep 2020 02:30:54 -0700
3 files changed,
35 insertions(+),
3 deletions(-)
M
CHANGES
→
CHANGES
@@ -46,6 +46,7 @@ - GBA Video: Invalidate map cache when modifying BGCNT (fixes mgba.io/i/1846)
- GBA Video: Don't draw sprites using unmapped VRAM in GL renderer (fixes mgba.io/i/1865) - GBA Video: Implement green swap (fixes mgba.io/i/1609) - GBA Video: Fix rare regression blending semitransparent sprites (fixes mgba.io/i/1876) + - GBA Video: Emulate sprite cycle limits in OpenGL renderer (fixes mgba.io/i/1635) - SM83: Emulate HALT bug Other fixes: - 3DS: Redo video sync to be more precise
M
include/mgba/internal/gba/renderers/gl.h
→
include/mgba/internal/gba/renderers/gl.h
@@ -109,6 +109,7 @@ GBA_GL_OBJ_TRANSFORM,
GBA_GL_OBJ_DIMS, GBA_GL_OBJ_OBJWIN, GBA_GL_OBJ_MOSAIC, + GBA_GL_OBJ_CYCLES, GBA_GL_WIN_DISPCNT = 2, GBA_GL_WIN_BLEND,@@ -123,7 +124,7 @@ GBA_GL_FINALIZE_WINDOW,
GBA_GL_FINALIZE_BACKDROP, GBA_GL_FINALIZE_BACKDROPFLAGS, - GBA_GL_UNIFORM_MAX = 12 + GBA_GL_UNIFORM_MAX = 14 }; struct GBAVideoGLShader {@@ -183,6 +184,7 @@ GBAWindowControl control;
} winN[2]; GLint winNHistory[2][GBA_VIDEO_VERTICAL_PIXELS * 4]; + GLint spriteCycles[GBA_VIDEO_VERTICAL_PIXELS]; GBAWindowControl winout; GBAWindowControl objwin;@@ -200,4 +202,4 @@ #endif
CXX_GUARD_END -#endif+#endif
M
src/gba/renderers/gl.c
→
src/gba/renderers/gl.c
@@ -413,6 +413,7 @@ { "transform", GBA_GL_OBJ_TRANSFORM, },
{ "dims", GBA_GL_OBJ_DIMS, }, { "objwin", GBA_GL_OBJ_OBJWIN, }, { "mosaic", GBA_GL_OBJ_MOSAIC, }, + { "cyclesRemaining", GBA_GL_OBJ_CYCLES, }, { 0 } };@@ -428,6 +429,7 @@ "uniform mat2x2 transform;\n"
"uniform ivec4 dims;\n" "uniform ivec4 objwin;\n" "uniform ivec4 mosaic;\n" + "uniform int cyclesRemaining[160];\n" "OUT(0) out vec4 color;\n" "OUT(1) out ivec4 flags;\n" "OUT(2) out ivec4 window;\n"@@ -442,6 +444,9 @@ " incoord.x = float(clamp(x - (mosaic.z + x) % mosaic.x, 0, dims.z - 1));\n"
" } else if (mosaic.x < -1) {\n" " int x = dims.z - int(incoord.x) - 1;\n" " incoord.x = float(clamp(dims.z - x + (mosaic.z + x) % -mosaic.x - 1, 0, dims.z - 1));\n" + " }\n" + " if (cyclesRemaining[int(incoord.y) + mosaic.w] <= 0) {\n" + " discard;\n" " }\n" " if (mosaic.y > 1) {\n" " int y = int(incoord.y);\n"@@ -1383,6 +1388,11 @@ glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->bg[i].fbo);
glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 }); glClear(GL_COLOR_BUFFER_BIT); } + + int spriteCyclesRemaining = GBARegisterDISPCNTIsHblankIntervalFree(glRenderer->dispcnt) ? OBJ_HBLANK_FREE_LENGTH : OBJ_LENGTH; + for (i = 0; i < GBA_VIDEO_VERTICAL_PIXELS; ++i) { + glRenderer->spriteCycles[i] = spriteCyclesRemaining; + } } if (GBARegisterDISPCNTGetMode(glRenderer->dispcnt) != 0) {@@ -1428,6 +1438,24 @@ continue;
} GBAVideoGLRendererDrawSprite(glRenderer, &sprite->obj, y, sprite->y); + + int startY = sprite->y; + int endY = sprite->endY; + + if (endY >= 256) { + startY -= 256; + endY -= 256; + } + if (startY < glRenderer->firstY) { + startY = glRenderer->firstY; + } + if (endY > y) { + endY = y; + } + int j; + for (j = startY; j <= endY; ++j) { + glRenderer->spriteCycles[j] -= sprite->cycles; + } } glDisable(GL_DEPTH_TEST); glDisable(GL_STENCIL_TEST);@@ -1668,6 +1696,7 @@ glUniform1i(uniforms[GBA_GL_OBJ_LOCALPALETTE], GBAObjAttributesCGetPalette(sprite->c));
glUniform4i(uniforms[GBA_GL_OBJ_INFLAGS], GBAObjAttributesCGetPriority(sprite->c), (renderer->target1Obj || GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_SEMITRANSPARENT) | (renderer->target2Obj * 2) | (renderer->blendEffect * 4), renderer->blda, GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_SEMITRANSPARENT); + glUniform1iv(uniforms[GBA_GL_OBJ_CYCLES], GBA_VIDEO_VERTICAL_PIXELS, renderer->spriteCycles); if (GBAObjAttributesAIsTransformed(sprite->a)) { struct GBAOAMMatrix mat; LOAD_16(mat.a, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].a);@@ -1705,7 +1734,7 @@ mosaicH = -mosaicH;
} glUniform4i(uniforms[GBA_GL_OBJ_MOSAIC], mosaicH, GBAMosaicControlGetObjV(renderer->mosaic) + 1, x, spriteY); } else { - glUniform4i(uniforms[GBA_GL_OBJ_MOSAIC], 0, 0, 0, 0); + glUniform4i(uniforms[GBA_GL_OBJ_MOSAIC], 0, 0, x, spriteY); } glStencilFunc(GL_ALWAYS, 1, 1); if (GBAObjAttributesAGetMode(sprite->a) != OBJ_MODE_OBJWIN || GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt)) {