GBA Video: GL sprite initial work
@@ -0,0 +1,25 @@
+/* Copyright (c) 2013-2019 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef GBA_RENDERER_COMMON_H +#define GBA_RENDERER_COMMON_H + +#include <mgba-util/common.h> + +CXX_GUARD_START + +#include <mgba/internal/gba/video.h> + +struct GBAVideoRendererSprite { + struct GBAObj obj; + int16_t y; + int16_t endY; +}; + +int GBAVideoRendererCleanOAM(struct GBAObj* oam, struct GBAVideoRendererSprite* sprites, int offsetY); + +CXX_GUARD_END + +#endif
@@ -13,6 +13,7 @@
#include <mgba/core/core.h> #include <mgba/gba/interface.h> #include <mgba/internal/gba/io.h> +#include <mgba/internal/gba/renderers/common.h> #include <mgba/internal/gba/video.h> #ifdef USE_EPOXY@@ -61,10 +62,22 @@
struct GBAVideoGLAffine affine[4]; }; +enum { + GBA_GL_FBO_OBJ = 0, + GBA_GL_FBO_COMPOSITE = 1, + + GBA_GL_TEX_OBJ_COLOR = 0, + GBA_GL_TEX_OBJ_FLAGS = 1, + GBA_GL_TEX_COMPOSITE_FLAGS = 2, +}; + struct GBAVideoGLRenderer { struct GBAVideoRenderer d; struct GBAVideoGLBackground bg[4]; + + int oamMax; + struct GBAVideoRendererSprite sprites[128]; GLuint fbo[2]; GLuint layers[3];@@ -81,7 +94,7 @@ GLuint vramTex;
unsigned vramDirty; GLuint bgProgram[6]; - GLuint objProgram; + GLuint objProgram[4]; GLuint compositeProgram;
@@ -13,13 +13,8 @@
#include <mgba/core/core.h> #include <mgba/gba/interface.h> #include <mgba/internal/gba/io.h> +#include <mgba/internal/gba/renderers/common.h> #include <mgba/internal/gba/video.h> - -struct GBAVideoSoftwareSprite { - struct GBAObj obj; - int y; - int endY; -}; struct GBAVideoSoftwareBackground { unsigned index;@@ -140,7 +135,7 @@ struct GBAVideoSoftwareBackground bg[4];
int oamDirty; int oamMax; - struct GBAVideoSoftwareSprite sprites[128]; + struct GBAVideoRendererSprite sprites[128]; int16_t objOffsetX; int16_t objOffsetY;
@@ -263,10 +263,7 @@
static void _GBACoreDesiredVideoDimensions(struct mCore* core, unsigned* width, unsigned* height) { struct GBACore* gbacore = (struct GBACore*) core; int fakeBool; - int scale = 1; - if (mCoreConfigGetIntValue(&core->config, "hwaccelVideo", &fakeBool) && fakeBool) { - scale = gbacore->glRenderer.scale; - } + int scale = gbacore->glRenderer.scale; *width = GBA_VIDEO_HORIZONTAL_PIXELS * scale; *height = GBA_VIDEO_VERTICAL_PIXELS * scale;
@@ -0,0 +1,33 @@
+/* Copyright (c) 2013-2019 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include <mgba/internal/gba/renderers/common.h> + +#include <mgba/gba/interface.h> + +int GBAVideoRendererCleanOAM(struct GBAObj* oam, struct GBAVideoRendererSprite* sprites, int offsetY) { + int i; + int oamMax = 0; + for (i = 0; i < 128; ++i) { + struct GBAObj obj; + LOAD_16LE(obj.a, 0, &oam[i].a); + LOAD_16LE(obj.b, 0, &oam[i].b); + LOAD_16LE(obj.c, 0, &oam[i].c); + if (GBAObjAttributesAIsTransformed(obj.a) || !GBAObjAttributesAIsDisable(obj.a)) { + int height = GBAVideoObjSizes[GBAObjAttributesAGetShape(obj.a) * 4 + GBAObjAttributesBGetSize(obj.b)][1]; + if (GBAObjAttributesAIsTransformed(obj.a)) { + height <<= GBAObjAttributesAGetDoubleSize(obj.a); + } + if (GBAObjAttributesAGetY(obj.a) < GBA_VIDEO_VERTICAL_PIXELS || GBAObjAttributesAGetY(obj.a) + height >= VIDEO_VERTICAL_TOTAL_PIXELS) { + int y = GBAObjAttributesAGetY(obj.a) + offsetY; + sprites[oamMax].y = y; + sprites[oamMax].endY = y + height; + sprites[oamMax].obj = obj; + ++oamMax; + } + } + } + return oamMax; +}
@@ -30,11 +30,14 @@ static void GBAVideoGLRendererWriteBGY_LO(struct GBAVideoGLBackground* bg, uint16_t value);
static void GBAVideoGLRendererWriteBGY_HI(struct GBAVideoGLBackground* bg, uint16_t value); static void GBAVideoGLRendererWriteBLDCNT(struct GBAVideoGLRenderer* renderer, uint16_t value); +static void GBAVideoGLRendererDrawSprite(struct GBAVideoGLRenderer* renderer, struct GBAObj* sprite, int y, int spriteY); static void GBAVideoGLRendererDrawBackgroundMode0(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y); static void GBAVideoGLRendererDrawBackgroundMode2(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y); static void GBAVideoGLRendererDrawBackgroundMode3(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y); static void GBAVideoGLRendererDrawBackgroundMode4(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y); static void GBAVideoGLRendererDrawBackgroundMode5(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y); + +static void _compositeLayer(struct GBAVideoGLRenderer* renderer, GLuint tex, int y, int priority, int flags); #define TEST_LAYER_ENABLED(X) !renderer->disableBG[X] && glRenderer->bg[X].enabled == 4 && glRenderer->bg[X].priority == priority@@ -44,13 +47,13 @@
static const char* const _vertexShader = "attribute vec2 position;\n" "uniform ivec2 loc;\n" - "const ivec2 maxPos = ivec2(240, 160);\n" + "uniform ivec2 maxPos;\n" "varying vec2 texCoord;\n" "void main() {\n" - " vec2 local = (position * loc.x + vec2(0, loc.y)) / vec2(1., maxPos.y);\n" - " gl_Position = vec4(local * 2. - 1., 0., 1.);\n" - " texCoord = local * maxPos.xy;\n" + " vec2 local = vec2(position.x, float(position.y * loc.x + loc.y) / abs(maxPos.y));\n" + " gl_Position = vec4((local * 2. - 1.) * sign(maxPos), 0., 1.);\n" + " texCoord = local * abs(maxPos);\n" "}"; static const char* const _renderTile16 =@@ -60,10 +63,9 @@ " vec4 halfrow = texelFetch(vram, ivec2(address & 255, address >> 8), 0);\n"
" int entry = int(halfrow[3 - (localCoord.x & 3)] * 15.9);\n" " vec4 color = texelFetch(palette, ivec2(entry, paletteId), 0);\n" " if (entry == 0) {\n" - " color.a = 0;\n" - " } else {\n" - " color.a = 1;\n" + " discard;\n" " }\n" + " color.a = 1;\n" " return color;\n" "}";@@ -74,11 +76,10 @@ " vec4 halfrow = texelFetch(vram, ivec2(address & 255, address >> 8), 0);\n"
" int entry = int(halfrow[3 - 2 * (localCoord.x & 1)] * 15.9);\n" " int pal2 = int(halfrow[2 - 2 * (localCoord.x & 1)] * 15.9);\n" " vec4 color = texelFetch(palette, ivec2(entry, pal2 + (paletteId & 16)), 0);\n" - " if (pal2 > 0 || entry > 0) {\n" - " color.a = 1.;\n" - " } else {\n" - " color.a = 0.;\n" + " if (pal2 + entry == 0) {\n" + " discard;\n" " }\n" + " color.a = 1.;\n" " return color;\n" "}";@@ -179,6 +180,26 @@ " vec2 mixedOffset = interpolate(offset, lin);\n"
" gl_FragColor = fetchTile(ivec2(mixedTransform * texCoord.x + mixedOffset));\n" "}"; +static const char* const _renderObjNoTransform = + "varying vec2 texCoord;\n" + "uniform sampler2D vram;\n" + "uniform sampler2D palette;\n" + "uniform int charBase;\n" + "uniform int stride;\n" + "uniform int localPalette;\n" + "uniform ivec3 inflags;\n" + "out vec4 color;\n" + "out vec3 flags;\n" + "const vec3 flagCoeff = vec3(32., 8., 4.);\n" + + "vec4 renderTile(int tile, int paletteId, ivec2 localCoord);\n" + + "void main() {\n" + " ivec2 coord = ivec2(texCoord);\n" + " color = renderTile((coord.x >> 3) + (coord.y >> 3) * stride, 16 + localPalette, coord & 7);\n" + " flags = inflags / flagCoeff;\n" + "}"; + static const char* const _composite = "varying vec2 texCoord;\n" "uniform ivec3 inflags;\n"@@ -186,20 +207,21 @@ "uniform int scale;\n"
"uniform vec3 blend;\n" "uniform sampler2D layer;\n" "uniform sampler2D oldLayer;\n" - "uniform sampler2D buffer;\n" + "uniform sampler2D oldFlags;\n" "out vec4 color;\n" "out vec3 flags;\n" + "const vec3 flagCoeff = vec3(32., 8., 4.);\n" "void main() {\n" " vec4 pix = texelFetch(layer, ivec2(texCoord * scale), 0);\n" " if (pix.a == 0) {\n" " discard;\n" " }\n" - " ivec3 oldFlags = ivec3(texelFetch(buffer, ivec2(texCoord * scale), 0).xyz * vec3(32., 4., 1.));\n" + " ivec3 oflags = ivec3(texelFetch(oldFlags, ivec2(texCoord * scale), 0).xyz * flagCoeff);\n" " ivec3 outflags = ivec3(0, 0, 0);\n" - " if (inflags.x < oldFlags.x) {\n" + " if (inflags.x < oflags.x) {\n" " outflags = inflags;\n" - " if (inflags.z == 1 && (inflags.y & 1) == 1 && (oldFlags.y & 2) == 2) {\n" + " if (inflags.z == 1 && (inflags.y & 1) == 1 && (oflags.y & 2) == 2) {\n" " vec4 oldpix = texelFetch(oldLayer, ivec2(texCoord * scale), 0);\n" " pix *= blend.x;\n" " pix += oldpix * blend.y;\n"@@ -208,7 +230,7 @@ " } else {\n"
" pix = texelFetch(oldLayer, ivec2(texCoord * scale), 0);\n" " }\n" " color = pix;\n" - " flags = outflags / vec3(32., 4., 1.);\n" + " flags = outflags / flagCoeff;\n" "}"; static const GLint _vertices[] = {@@ -240,7 +262,7 @@
renderer->scale = 1; } -void _compileBackground(struct GBAVideoGLRenderer* glRenderer, GLuint program, const char** shaderBuffer, int shaderBufferLines, GLuint vs, char* log) { +void _compileShader(struct GBAVideoGLRenderer* glRenderer, GLuint program, const char** shaderBuffer, int shaderBufferLines, GLuint vs, char* log) { GLuint fs = glCreateShader(GL_FRAGMENT_SHADER); glAttachShader(program, vs); glAttachShader(program, fs);@@ -279,7 +301,24 @@ glBindTexture(GL_TEXTURE_2D, glRenderer->oamTex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[1]); + glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_OBJ]); + glBindTexture(GL_TEXTURE_2D, glRenderer->layers[GBA_GL_TEX_OBJ_COLOR]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, GBA_VIDEO_HORIZONTAL_PIXELS * glRenderer->scale, GBA_VIDEO_VERTICAL_PIXELS * glRenderer->scale, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, glRenderer->layers[GBA_GL_TEX_OBJ_COLOR], 0); + + glBindTexture(GL_TEXTURE_2D, glRenderer->layers[GBA_GL_TEX_OBJ_FLAGS]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, GBA_VIDEO_HORIZONTAL_PIXELS * glRenderer->scale, GBA_VIDEO_VERTICAL_PIXELS * glRenderer->scale, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, glRenderer->layers[GBA_GL_TEX_OBJ_FLAGS], 0); + + glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_COMPOSITE]); glBindTexture(GL_TEXTURE_2D, glRenderer->outputTex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);@@ -288,13 +327,13 @@ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, GBA_VIDEO_HORIZONTAL_PIXELS * glRenderer->scale, GBA_VIDEO_VERTICAL_PIXELS * glRenderer->scale, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, glRenderer->outputTex, 0); - glBindTexture(GL_TEXTURE_2D, glRenderer->layers[2]); + glBindTexture(GL_TEXTURE_2D, glRenderer->layers[GBA_GL_TEX_COMPOSITE_FLAGS]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, GBA_VIDEO_HORIZONTAL_PIXELS * glRenderer->scale, GBA_VIDEO_VERTICAL_PIXELS * glRenderer->scale, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, glRenderer->layers[2], 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, glRenderer->layers[GBA_GL_TEX_COMPOSITE_FLAGS], 0); glBindFramebuffer(GL_FRAMEBUFFER, 0);@@ -315,7 +354,10 @@ glBindFramebuffer(GL_FRAMEBUFFER, 0);
} glRenderer->compositeProgram = glCreateProgram(); - glRenderer->objProgram = glCreateProgram(); + glRenderer->objProgram[0] = glCreateProgram(); + glRenderer->objProgram[1] = glCreateProgram(); + glRenderer->objProgram[2] = glCreateProgram(); + glRenderer->objProgram[3] = glCreateProgram(); glRenderer->bgProgram[0] = glCreateProgram(); glRenderer->bgProgram[1] = glCreateProgram(); glRenderer->bgProgram[2] = glCreateProgram();@@ -339,21 +381,26 @@
shaderBuffer[1] = _renderMode0; shaderBuffer[2] = _renderTile16; - _compileBackground(glRenderer, glRenderer->bgProgram[0], shaderBuffer, 3, vs, log); + _compileShader(glRenderer, glRenderer->bgProgram[0], shaderBuffer, 3, vs, log); shaderBuffer[2] = _renderTile256; - _compileBackground(glRenderer, glRenderer->bgProgram[1], shaderBuffer, 3, vs, log); + _compileShader(glRenderer, glRenderer->bgProgram[1], shaderBuffer, 3, vs, log); shaderBuffer[1] = _renderMode2; shaderBuffer[2] = _fetchTileOverflow; - _compileBackground(glRenderer, glRenderer->bgProgram[2], shaderBuffer, 3, vs, log); + _compileShader(glRenderer, glRenderer->bgProgram[2], shaderBuffer, 3, vs, log); shaderBuffer[2] = _fetchTileNoOverflow; - _compileBackground(glRenderer, glRenderer->bgProgram[3], shaderBuffer, 3, vs, log); + _compileShader(glRenderer, glRenderer->bgProgram[3], shaderBuffer, 3, vs, log); + + shaderBuffer[1] = _renderObjNoTransform; + + shaderBuffer[2] = _renderTile16; + _compileShader(glRenderer, glRenderer->objProgram[0], shaderBuffer, 3, vs, log); shaderBuffer[1] = _composite; - _compileBackground(glRenderer, glRenderer->compositeProgram, shaderBuffer, 2, vs, log); + _compileShader(glRenderer, glRenderer->compositeProgram, shaderBuffer, 2, vs, log); glBindFragDataLocation(glRenderer->compositeProgram, 0, "color"); glBindFragDataLocation(glRenderer->compositeProgram, 1, "flags");@@ -369,6 +416,19 @@ glDeleteTextures(3, glRenderer->layers);
glDeleteTextures(1, &glRenderer->paletteTex); glDeleteTextures(1, &glRenderer->vramTex); glDeleteTextures(1, &glRenderer->oamTex); + + glDeleteProgram(glRenderer->bgProgram[0]); + glDeleteProgram(glRenderer->bgProgram[1]); + glDeleteProgram(glRenderer->bgProgram[2]); + glDeleteProgram(glRenderer->bgProgram[3]); + glDeleteProgram(glRenderer->bgProgram[4]); + glDeleteProgram(glRenderer->bgProgram[5]); + glDeleteProgram(glRenderer->bgProgram[6]); + glDeleteProgram(glRenderer->objProgram[0]); + glDeleteProgram(glRenderer->objProgram[1]); + glDeleteProgram(glRenderer->objProgram[2]); + glDeleteProgram(glRenderer->objProgram[3]); + glDeleteProgram(glRenderer->compositeProgram); } void GBAVideoGLRendererReset(struct GBAVideoRenderer* renderer) {@@ -610,11 +670,6 @@ glBindTexture(GL_TEXTURE_2D, glRenderer->paletteTex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB5_A1, 16, 32, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, glRenderer->d.palette); glRenderer->paletteDirty = false; } - if (glRenderer->oamDirty) { - glBindTexture(GL_TEXTURE_2D, glRenderer->oamTex); - glTexImage2D(GL_TEXTURE_2D, 0, GL_R16UI, 4, 128, 0, GL_RED_INTEGER, GL_UNSIGNED_SHORT, glRenderer->d.oam); - glRenderer->oamDirty = false; - } int i; for (i = 0; i < 24; ++i) { if (!(glRenderer->vramDirty & (1 << i))) {@@ -628,16 +683,25 @@ glRenderer->vramDirty = 0;
uint32_t backdrop = M_RGB5_TO_RGB8(renderer->palette[0]); glClearColor(((backdrop >> 16) & 0xFF) / 256., ((backdrop >> 8) & 0xFF) / 256., (backdrop & 0xFF) / 256., 0.f); - glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[1]); + glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_COMPOSITE]); glEnable(GL_SCISSOR_TEST); glScissor(0, y * glRenderer->scale, GBA_VIDEO_HORIZONTAL_PIXELS * glRenderer->scale, glRenderer->scale); glClear(GL_COLOR_BUFFER_BIT); glDisable(GL_SCISSOR_TEST); if (y == 0) { glDrawBuffer(GL_COLOR_ATTACHMENT1); - glClearColor(1, (glRenderer->target1Bd | (glRenderer->target2Bd * 2)) / 4.f, 0, 1); + glClearColor(1, (glRenderer->target1Bd | (glRenderer->target2Bd * 2)) / 8.f, 0, 1); glClear(GL_COLOR_BUFFER_BIT); glDrawBuffer(GL_COLOR_ATTACHMENT0); + + glClearColor(0, 0, 0, 0); + glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_OBJ]); + glClear(GL_COLOR_BUFFER_BIT); + + for (i = 0; i < 4; ++i) { + glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->bg[i].fbo); + glClear(GL_COLOR_BUFFER_BIT); + } } glBindFramebuffer(GL_FRAMEBUFFER, 0);@@ -658,6 +722,27 @@ } else {
glRenderer->firstAffine = -1; } + glEnable(GL_SCISSOR_TEST); + int spriteLayers = 0; + if (GBARegisterDISPCNTIsObjEnable(glRenderer->dispcnt) && !glRenderer->d.disableOBJ) { + if (glRenderer->oamDirty) { + glBindTexture(GL_TEXTURE_2D, glRenderer->oamTex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_R16UI, 4, 128, 0, GL_RED_INTEGER, GL_UNSIGNED_SHORT, glRenderer->d.oam); + glRenderer->oamMax = GBAVideoRendererCleanOAM(glRenderer->d.oam->obj, glRenderer->sprites, 0); + glRenderer->oamDirty = false; + } + int i; + for (i = glRenderer->oamMax; i--;) { + struct GBAVideoRendererSprite* sprite = &glRenderer->sprites[i]; + if ((y < sprite->y && (sprite->endY - 256 < 0 || y >= sprite->endY - 256)) || y >= sprite->endY) { + continue; + } + + GBAVideoGLRendererDrawSprite(glRenderer, &sprite->obj, y, sprite->y); + spriteLayers |= 1 << GBAObjAttributesCGetPriority(sprite->obj.c); + } + } + unsigned priority; for (priority = 4; priority--;) { if (TEST_LAYER_ENABLED(0) && GBARegisterDISPCNTGetMode(glRenderer->dispcnt) < 2) {@@ -697,6 +782,7 @@ break;
} } } + _compositeLayer(glRenderer, glRenderer->layers[GBA_GL_TEX_OBJ_COLOR], y, 0, 0); if (GBARegisterDISPCNTGetMode(glRenderer->dispcnt) != 0) { memcpy(&glRenderer->bg[2].affine[3], &glRenderer->bg[2].affine[2], sizeof(struct GBAVideoGLAffine));@@ -804,34 +890,72 @@ renderer->target2Obj = GBARegisterBLDCNTGetTarget2Obj(value);
renderer->target2Bd = GBARegisterBLDCNTGetTarget2Bd(value); } -static void _compositeLayer(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y, int flags) { +static void _compositeLayer(struct GBAVideoGLRenderer* renderer, GLuint tex, int y, int priority, int flags) { if ((y & 0x1F) != 0x1F) { return; } - glBindFramebuffer(GL_FRAMEBUFFER, renderer->fbo[1]); + glBindFramebuffer(GL_FRAMEBUFFER, renderer->fbo[GBA_GL_FBO_COMPOSITE]); glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale); - glScissor(0, (y * renderer->scale) % (0x20 * renderer->scale), GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, 0x20 * renderer->scale); + glScissor(0, (y & ~0x1F) * renderer->scale, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, 0x20 * renderer->scale); glUseProgram(renderer->compositeProgram); glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, background->tex); + glBindTexture(GL_TEXTURE_2D, tex); glActiveTexture(GL_TEXTURE0 + 1); glBindTexture(GL_TEXTURE_2D, renderer->outputTex); glActiveTexture(GL_TEXTURE0 + 2); - glBindTexture(GL_TEXTURE_2D, renderer->layers[2]); + glBindTexture(GL_TEXTURE_2D, renderer->layers[GBA_GL_TEX_COMPOSITE_FLAGS]); glUniform2i(0, 0x20, y & ~0x1F); - glUniform3i(1, (background->priority << 3) + (background->index << 1) + 1, flags, renderer->blendEffect); - glUniform1i(2, renderer->scale); - glUniform3f(3, renderer->blda / 16.f, renderer->bldb / 16.f, renderer->bldy / 16.f); - glUniform1i(4, 0); - glUniform1i(5, 1); - glUniform1i(6, 2); + glUniform2i(1, GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS); + glUniform3i(2, priority, flags, renderer->blendEffect); + glUniform1i(3, renderer->scale); + glUniform3f(4, renderer->blda / 16.f, renderer->bldb / 16.f, renderer->bldy / 16.f); + glUniform1i(5, 0); + glUniform1i(6, 1); + glUniform1i(7, 2); glVertexAttribPointer(0, 2, GL_INT, GL_FALSE, 0, _vertices); glEnableVertexAttribArray(0); glDrawBuffers(2, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 }); + glBindFramebuffer(GL_FRAMEBUFFER, 0); } +void GBAVideoGLRendererDrawSprite(struct GBAVideoGLRenderer* renderer, struct GBAObj* sprite, int y, int spriteY) { + int width = GBAVideoObjSizes[GBAObjAttributesAGetShape(sprite->a) * 4 + GBAObjAttributesBGetSize(sprite->b)][0]; + int height = GBAVideoObjSizes[GBAObjAttributesAGetShape(sprite->a) * 4 + GBAObjAttributesBGetSize(sprite->b)][1]; + int32_t x = (uint32_t) GBAObjAttributesBGetX(sprite->b) << 23; + x >>= 23; + + int align = GBAObjAttributesAIs256Color(sprite->a) && !GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt); + unsigned charBase = (BASE_TILE >> 1) + (GBAObjAttributesCGetTile(sprite->c) & ~align) * 0x10; + int stride = GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt) ? (width >> 3) : (0x10 >> !GBAObjAttributesAIs256Color(sprite->a)); + + if (spriteY + height >= 256) { + spriteY -= 256; + } + + glBindFramebuffer(GL_FRAMEBUFFER, renderer->fbo[GBA_GL_FBO_OBJ]); + glViewport(x * renderer->scale, spriteY * renderer->scale, width * renderer->scale, height * renderer->scale); + glScissor(x * renderer->scale, y * renderer->scale, width * renderer->scale, renderer->scale); + glUseProgram(renderer->objProgram[0]); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, renderer->vramTex); + glActiveTexture(GL_TEXTURE0 + 1); + glBindTexture(GL_TEXTURE_2D, renderer->paletteTex); + glUniform2i(0, 1, y - spriteY); + glUniform2i(1, GBAObjAttributesBIsHFlip(sprite->b) ? -width : width, height); + glUniform1i(2, 0); + glUniform1i(3, 1); + glUniform1i(4, charBase); + glUniform1i(5, stride); + glUniform1i(6, GBAObjAttributesCGetPalette(sprite->c)); + glUniform3i(7, GBAObjAttributesCGetPriority(sprite->c) << 3, 0, 0); + glVertexAttribPointer(0, 2, GL_INT, GL_FALSE, 0, _vertices); + glEnableVertexAttribArray(0); + glDrawBuffers(2, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });} + void GBAVideoGLRendererDrawBackgroundMode0(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) { int inY = y + background->y; int yBase = inY & 0xFF;@@ -849,19 +973,18 @@ glBindTexture(GL_TEXTURE_2D, renderer->vramTex);
glActiveTexture(GL_TEXTURE0 + 1); glBindTexture(GL_TEXTURE_2D, renderer->paletteTex); glUniform2i(0, 1, y); - glUniform1i(1, 0); - glUniform1i(2, 1); - glUniform1i(3, background->screenBase); - glUniform1i(4, background->charBase); - glUniform1i(5, background->size); - glUniform2i(6, background->x, yBase - y); + glUniform2i(1, GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS); + glUniform1i(2, 0); + glUniform1i(3, 1); + glUniform1i(4, background->screenBase); + glUniform1i(5, background->charBase); + glUniform1i(6, background->size); + glUniform2i(7, background->x, yBase - y); glVertexAttribPointer(0, 2, GL_INT, GL_FALSE, 0, _vertices); glEnableVertexAttribArray(0); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - _compositeLayer(renderer, background, y, background->target1 | (background->target2 * 2)); - - glBindFramebuffer(GL_FRAMEBUFFER, 0); + _compositeLayer(renderer, background->tex, y, (background->priority << 3) + (background->index << 1) + 1, background->target1 | (background->target2 * 2)); } void GBAVideoGLRendererDrawBackgroundMode2(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) {@@ -874,32 +997,33 @@ glBindTexture(GL_TEXTURE_2D, renderer->vramTex);
glActiveTexture(GL_TEXTURE0 + 1); glBindTexture(GL_TEXTURE_2D, renderer->paletteTex); glUniform2i(0, 1, y); - glUniform1i(1, 0); - glUniform1i(2, 1); - glUniform1i(3, background->screenBase); - glUniform1i(4, background->charBase); - glUniform1i(5, background->size); + glUniform2i(1, GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS); + glUniform1i(2, 0); + glUniform1i(3, 1); + glUniform1i(4, background->screenBase); + glUniform1i(5, background->charBase); + glUniform1i(6, background->size); if (renderer->scale > 1) { - glUniform2iv(6, 4, (GLint[]) { + glUniform2iv(7, 4, (GLint[]) { background->affine[0].sx, background->affine[0].sy, background->affine[1].sx, background->affine[1].sy, background->affine[2].sx, background->affine[2].sy, background->affine[3].sx, background->affine[3].sy, }); - glUniform2iv(10, 4, (GLint[]) { + glUniform2iv(11, 4, (GLint[]) { background->affine[0].dx, background->affine[0].dy, background->affine[1].dx, background->affine[1].dy, background->affine[2].dx, background->affine[2].dy, background->affine[3].dx, background->affine[3].dy, }); } else { - glUniform2iv(6, 4, (GLint[]) { + glUniform2iv(7, 4, (GLint[]) { background->affine[0].sx, background->affine[0].sy, background->affine[0].sx, background->affine[0].sy, background->affine[0].sx, background->affine[0].sy, background->affine[0].sx, background->affine[0].sy, }); - glUniform2iv(10, 4, (GLint[]) { + glUniform2iv(11, 4, (GLint[]) { background->affine[0].dx, background->affine[0].dy, background->affine[0].dx, background->affine[0].dy, background->affine[0].dx, background->affine[0].dy,@@ -910,7 +1034,5 @@ glVertexAttribPointer(0, 2, GL_INT, GL_FALSE, 0, _vertices);
glEnableVertexAttribArray(0); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - _compositeLayer(renderer, background, y, background->target1 | (background->target2 * 2)); - - glBindFramebuffer(GL_FRAMEBUFFER, 0); + _compositeLayer(renderer, background->tex, y, (background->priority << 3) + (background->index << 1) + 1, background->target1 | (background->target2 * 2)); }
@@ -36,7 +36,6 @@ static void GBAVideoSoftwareRendererWriteBGY_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value);
static void GBAVideoSoftwareRendererWriteBGY_HI(struct GBAVideoSoftwareBackground* bg, uint16_t value); static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value); -static void _cleanOAM(struct GBAVideoSoftwareRenderer* renderer); static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y); static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer);@@ -498,32 +497,6 @@ }
#endif } -static void _cleanOAM(struct GBAVideoSoftwareRenderer* renderer) { - int i; - int oamMax = 0; - for (i = 0; i < 128; ++i) { - struct GBAObj obj; - LOAD_16(obj.a, 0, &renderer->d.oam->obj[i].a); - LOAD_16(obj.b, 0, &renderer->d.oam->obj[i].b); - LOAD_16(obj.c, 0, &renderer->d.oam->obj[i].c); - if (GBAObjAttributesAIsTransformed(obj.a) || !GBAObjAttributesAIsDisable(obj.a)) { - int height = GBAVideoObjSizes[GBAObjAttributesAGetShape(obj.a) * 4 + GBAObjAttributesBGetSize(obj.b)][1]; - if (GBAObjAttributesAIsTransformed(obj.a)) { - height <<= GBAObjAttributesAGetDoubleSize(obj.a); - } - if (GBAObjAttributesAGetY(obj.a) < GBA_VIDEO_VERTICAL_PIXELS || GBAObjAttributesAGetY(obj.a) + height >= VIDEO_VERTICAL_TOTAL_PIXELS) { - int y = GBAObjAttributesAGetY(obj.a) + renderer->objOffsetY; - renderer->sprites[oamMax].y = y; - renderer->sprites[oamMax].endY = y + height; - renderer->sprites[oamMax].obj = obj; - ++oamMax; - } - } - } - renderer->oamMax = oamMax; - renderer->oamDirty = 0; -} - static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) { struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;@@ -821,14 +794,15 @@ int w;
int spriteLayers = 0; if (GBARegisterDISPCNTIsObjEnable(renderer->dispcnt) && !renderer->d.disableOBJ) { if (renderer->oamDirty) { - _cleanOAM(renderer); + renderer->oamMax = GBAVideoRendererCleanOAM(renderer->d.oam->obj, renderer->sprites, renderer->objOffsetY); + renderer->oamDirty = false; } renderer->spriteCyclesRemaining = GBARegisterDISPCNTIsHblankIntervalFree(renderer->dispcnt) ? OBJ_HBLANK_FREE_LENGTH : OBJ_LENGTH; int mosaicV = GBAMosaicControlGetObjV(renderer->mosaic) + 1; int mosaicY = y - (y % mosaicV); int i; for (i = 0; i < renderer->oamMax; ++i) { - struct GBAVideoSoftwareSprite* sprite = &renderer->sprites[i]; + struct GBAVideoRendererSprite* sprite = &renderer->sprites[i]; int localY = y; renderer->end = 0; if (GBAObjAttributesAIsMosaic(sprite->obj.a)) {