all repos — mgba @ bb997f9b461019cb9a75449987817e97295ef1ae

mGBA Game Boy Advance Emulator

GBA Video: Start mode 0 GL renderer
Vicki Pfau vi@endrift.com
Thu, 09 May 2019 15:15:16 -0700
commit

bb997f9b461019cb9a75449987817e97295ef1ae

parent

e99cd34b668d0b0449dd8e2062cc7dad918f6aeb

2 files changed, 482 insertions(+), 32 deletions(-)

jump to
A include/mgba/internal/gba/renderers/gl.h

@@ -0,0 +1,103 @@

+/* 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 VIDEO_GL_H +#define VIDEO_GL_H + +#include <mgba-util/common.h> + +CXX_GUARD_START + +#include <mgba/core/core.h> +#include <mgba/gba/interface.h> +#include <mgba/internal/gba/io.h> +#include <mgba/internal/gba/video.h> + +#ifdef USE_EPOXY +#include <epoxy/gl.h> +#elif defined(BUILD_GL) +#ifdef __APPLE__ +#include <OpenGL/gl3.h> +#else +#define GL_GLEXT_PROTOTYPES +#include <GL/gl.h> +#include <GL/glext.h> +#endif +#else +#include <GLES2/gl2.h> +#endif + +struct GBAVideoGLBackground { + GLuint fbo; + GLuint tex; + GLuint program; + + unsigned index; + int enabled; + unsigned priority; + uint32_t charBase; + int mosaic; + int multipalette; + uint32_t screenBase; + int overflow; + int size; + int target1; + int target2; + uint16_t x; + uint16_t y; + int32_t refx; + int32_t refy; + int16_t dx; + int16_t dmx; + int16_t dy; + int16_t dmy; + int32_t sx; + int32_t sy; +}; + +struct GBAVideoGLRenderer { + struct GBAVideoRenderer d; + + struct GBAVideoGLBackground bg[4]; + + GLuint fbo[2]; + GLuint layers[2]; + + color_t* outputBuffer; + int outputBufferStride; + + GLuint paletteTex; + bool paletteDirty; + + GLuint oamTex; + bool oamDirty; + + GLuint vramTex; + unsigned vramDirty; + + GLuint bgPrograms[6]; + GLuint objProgram; + + GLuint compositeProgram; + + GBARegisterDISPCNT dispcnt; + + unsigned target1Obj; + unsigned target1Bd; + unsigned target2Obj; + unsigned target2Bd; + enum GBAVideoBlendEffect blendEffect; + uint16_t blda; + uint16_t bldb; + uint16_t bldy; + + GBAMosaicControl mosaic; +}; + +void GBAVideoGLRendererCreate(struct GBAVideoGLRenderer* renderer); + +CXX_GUARD_END + +#endif
M src/gba/renderers/gl.csrc/gba/renderers/gl.c

@@ -22,33 +22,73 @@ static void GBAVideoGLRendererFinishFrame(struct GBAVideoRenderer* renderer);

static void GBAVideoGLRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels); static void GBAVideoGLRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels); +static void GBAVideoGLRendererUpdateDISPCNT(struct GBAVideoGLRenderer* renderer); +static void GBAVideoGLRendererWriteBGCNT(struct GBAVideoGLBackground* bg, uint16_t value); +static void GBAVideoGLRendererWriteBGX_LO(struct GBAVideoGLBackground* bg, uint16_t value); +static void GBAVideoGLRendererWriteBGX_HI(struct GBAVideoGLBackground* bg, uint16_t value); +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 const GLchar* const _gl3Header = - "#version 120\n"; + "#version 130\n"; static const char* const _vertexShader = - "attribute vec4 position;\n" + "attribute vec2 position;\n" + "uniform int y;\n" + "const ivec2 maxPos = ivec2(240, 160);\n" "varying vec2 texCoord;\n" "void main() {\n" - " gl_Position = position;\n" - " texCoord = (position.st + vec2(1.0, -1.0)) * vec2(0.5, 0.5);\n" + " vec2 local = (position + vec2(0, y)) / vec2(1., maxPos.y);\n" + " gl_Position = vec4(local * 2. - 1., 0., 1.);\n" + " texCoord = local * maxPos;\n" "}"; -static const char* const _fragmentShader = +static const char* const _renderTile16 = + "vec4 renderTile(int tile, int tileBase, int paletteId, ivec2 localCoord) {\n" + " int address = tileBase + tile * 16 + (localCoord.x >> 2) + (localCoord.y << 1);\n" + " vec4 halfrow = texelFetch(vram, ivec2(address & 255, address >> 8), 0);\n" + " int entry = int(halfrow[3 - (localCoord.x & 3)] * 16.);\n" + " vec4 color = texelFetch(palette, ivec2(entry, paletteId), 0);\n" + " if (entry > 0) {\n" + " color.a = 1.;\n" + " } else {\n" + " color.a = 0.;\n" + " }\n" + " return color;\n" + "}"; + +static const char* const _renderMode0 = "varying vec2 texCoord;\n" - "uniform sampler2D tex;\n" + "uniform sampler2D vram;\n" + "uniform sampler2D palette;\n" + "uniform int screenBase;\n" + "uniform int charBase;\n" + "uniform ivec2 offset;\n" + + "vec4 renderTile(int tile, int tileBase, int paletteId, ivec2 localCoord);\n" "void main() {\n" - " vec4 color = texture2D(tex, texCoord);\n" - " color.a = 1.;\n" - " gl_FragColor = color;\n" + " ivec2 coord = ivec2(texCoord) + offset;\n" + " int mapAddress = screenBase + (coord.x >> 3) + (coord.y >> 3) * 32;\n" + " vec4 map = texelFetch(vram, ivec2(mapAddress & 255, mapAddress >> 8), 0);\n" + " int flags = int(map.g * 15.9);\n" + " if ((flags & 4) == 4) {\n" + " coord.x ^= 7;\n" + " }\n" + " if ((flags & 8) == 8) {\n" + " coord.y ^= 7;\n" + " }\n" + " int tile = int(map.a * 15.9) + int(map.b * 15.9) * 16 + (flags & 0x3) * 256;\n" + " gl_FragColor = renderTile(tile, charBase, int(map.r * 15.9), coord & 7);\n" "}"; -static const GLfloat _vertices[] = { - -1.f, -1.f, - -1.f, 1.f, - 1.f, 1.f, - 1.f, -1.f, +static const GLint _vertices[] = { + 0, 0, + 0, 1, + 1, 1, + 1, 0, }; void GBAVideoGLRendererCreate(struct GBAVideoGLRenderer* renderer) {

@@ -73,12 +113,18 @@ }

void GBAVideoGLRendererInit(struct GBAVideoRenderer* renderer) { struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer; - glGenFramebuffers(6, glRenderer->fbo); - glGenTextures(6, glRenderer->layers); + glGenFramebuffers(2, glRenderer->fbo); + glGenTextures(2, glRenderer->layers); glGenTextures(1, &glRenderer->paletteTex); glBindTexture(GL_TEXTURE_2D, glRenderer->paletteTex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glGenTextures(1, &glRenderer->vramTex); + + glBindTexture(GL_TEXTURE_2D, glRenderer->vramTex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

@@ -87,34 +133,37 @@ }

void GBAVideoGLRendererDeinit(struct GBAVideoRenderer* renderer) { struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer; - glDeleteFramebuffers(6, glRenderer->fbo); - glDeleteTextures(6, glRenderer->layers); + glDeleteFramebuffers(2, glRenderer->fbo); + glDeleteTextures(2, glRenderer->layers); glDeleteTextures(1, &glRenderer->paletteTex); + glDeleteTextures(1, &glRenderer->vramTex); + glDeleteTextures(1, &glRenderer->oamTex); } void GBAVideoGLRendererReset(struct GBAVideoRenderer* renderer) { struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer; - glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[5]); - glBindTexture(GL_TEXTURE_2D, glRenderer->layers[5]); + glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[1]); + glBindTexture(GL_TEXTURE_2D, glRenderer->layers[1]); 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, GBA_VIDEO_VERTICAL_PIXELS, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, glRenderer->layers[5], 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, glRenderer->layers[1], 0); glBindFramebuffer(GL_FRAMEBUFFER, 0); glRenderer->compositeProgram = glCreateProgram(); GLuint vs = glCreateShader(GL_VERTEX_SHADER); GLuint fs = glCreateShader(GL_FRAGMENT_SHADER); - const GLchar* shaderBuffer[2]; + const GLchar* shaderBuffer[3]; const GLubyte* version = glGetString(GL_VERSION); shaderBuffer[0] = _gl3Header; shaderBuffer[1] = _vertexShader; glShaderSource(vs, 2, shaderBuffer, 0); - shaderBuffer[1] = _fragmentShader; - glShaderSource(fs, 2, shaderBuffer, 0); + shaderBuffer[1] = _renderMode0; + shaderBuffer[2] = _renderTile16; + glShaderSource(fs, 3, shaderBuffer, 0); glAttachShader(glRenderer->compositeProgram, vs); glAttachShader(glRenderer->compositeProgram, fs);

@@ -122,46 +171,294 @@ char log[1024];

glCompileShader(fs); glGetShaderInfoLog(fs, 1024, 0, log); + if (log[0]) { + mLOG(GBA_VIDEO, ERROR, "Fragment shader compilation failure: %s", log); + } glCompileShader(vs); glGetShaderInfoLog(vs, 1024, 0, log); + if (log[0]) { + mLOG(GBA_VIDEO, ERROR, "Vertex shader compilation failure: %s", log); + } glLinkProgram(glRenderer->compositeProgram); glGetProgramInfoLog(glRenderer->compositeProgram, 1024, 0, log); + if (log[0]) { + mLOG(GBA_VIDEO, ERROR, "Program link failure: %s", log); + } - glRenderer->paletteDirty = false; + glRenderer->paletteDirty = true; + glRenderer->vramDirty = 0xFFFFFF; + + glBindTexture(GL_TEXTURE_2D, glRenderer->vramTex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA4, 256, 192, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, 0); } void GBAVideoGLRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) { - + struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer; + glRenderer->vramDirty |= 1 << (address >> 12); } void GBAVideoGLRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) { - + UNUSED(oam); + struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer; + glRenderer->oamDirty = true; } void GBAVideoGLRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) { + UNUSED(address); + UNUSED(value); struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer; glRenderer->paletteDirty = true; } uint16_t GBAVideoGLRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) { + struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer; + if (renderer->cache) { + GBAVideoCacheWriteVideoRegister(renderer->cache, address, value); + } + switch (address) { + case REG_DISPCNT: + value &= 0xFFF7; + glRenderer->dispcnt = value; + //GBAVideoGLRendererUpdateDISPCNT(glRenderer); + break; + case REG_BG0CNT: + value &= 0xDFFF; + GBAVideoGLRendererWriteBGCNT(&glRenderer->bg[0], value); + //GBAVideoGLRendererUpdateDISPCNT(glRenderer); + break; + case REG_BG1CNT: + value &= 0xDFFF; + GBAVideoGLRendererWriteBGCNT(&glRenderer->bg[1], value); + //GBAVideoGLRendererUpdateDISPCNT(glRenderer); + break; + case REG_BG2CNT: + value &= 0xFFFF; + GBAVideoGLRendererWriteBGCNT(&glRenderer->bg[2], value); + //GBAVideoGLRendererUpdateDISPCNT(glRenderer); + break; + case REG_BG3CNT: + value &= 0xFFFF; + GBAVideoGLRendererWriteBGCNT(&glRenderer->bg[3], value); + //GBAVideoGLRendererUpdateDISPCNT(glRenderer); + break; + case REG_BG0HOFS: + value &= 0x01FF; + glRenderer->bg[0].x = value; + break; + case REG_BG0VOFS: + value &= 0x01FF; + glRenderer->bg[0].y = value; + break; + case REG_BG1HOFS: + value &= 0x01FF; + glRenderer->bg[1].x = value; + break; + case REG_BG1VOFS: + value &= 0x01FF; + glRenderer->bg[1].y = value; + break; + case REG_BG2HOFS: + value &= 0x01FF; + glRenderer->bg[2].x = value; + break; + case REG_BG2VOFS: + value &= 0x01FF; + glRenderer->bg[2].y = value; + break; + case REG_BG3HOFS: + value &= 0x01FF; + glRenderer->bg[3].x = value; + break; + case REG_BG3VOFS: + value &= 0x01FF; + glRenderer->bg[3].y = value; + break; + case REG_BG2PA: + glRenderer->bg[2].dx = value; + break; + case REG_BG2PB: + glRenderer->bg[2].dmx = value; + break; + case REG_BG2PC: + glRenderer->bg[2].dy = value; + break; + case REG_BG2PD: + glRenderer->bg[2].dmy = value; + break; + case REG_BG2X_LO: + GBAVideoGLRendererWriteBGX_LO(&glRenderer->bg[2], value); + break; + case REG_BG2X_HI: + GBAVideoGLRendererWriteBGX_HI(&glRenderer->bg[2], value); + break; + case REG_BG2Y_LO: + GBAVideoGLRendererWriteBGY_LO(&glRenderer->bg[2], value); + break; + case REG_BG2Y_HI: + GBAVideoGLRendererWriteBGY_HI(&glRenderer->bg[2], value); + break; + case REG_BG3PA: + glRenderer->bg[3].dx = value; + break; + case REG_BG3PB: + glRenderer->bg[3].dmx = value; + break; + case REG_BG3PC: + glRenderer->bg[3].dy = value; + break; + case REG_BG3PD: + glRenderer->bg[3].dmy = value; + break; + case REG_BG3X_LO: + GBAVideoGLRendererWriteBGX_LO(&glRenderer->bg[3], value); + break; + case REG_BG3X_HI: + GBAVideoGLRendererWriteBGX_HI(&glRenderer->bg[3], value); + break; + case REG_BG3Y_LO: + GBAVideoGLRendererWriteBGY_LO(&glRenderer->bg[3], value); + break; + case REG_BG3Y_HI: + GBAVideoGLRendererWriteBGY_HI(&glRenderer->bg[3], value); + break; + case REG_BLDCNT: + GBAVideoGLRendererWriteBLDCNT(glRenderer, value); + value &= 0x3FFF; + break; + case REG_BLDALPHA: + glRenderer->blda = value & 0x1F; + if (glRenderer->blda > 0x10) { + glRenderer->blda = 0x10; + } + glRenderer->bldb = (value >> 8) & 0x1F; + if (glRenderer->bldb > 0x10) { + glRenderer->bldb = 0x10; + } + value &= 0x1F1F; + break; + case REG_BLDY: + value &= 0x1F; + if (value > 0x10) { + value = 0x10; + } + if (glRenderer->bldy != value) { + glRenderer->bldy = value; + } + break; + 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: + value &= 0x3F3F; + //glRenderer->winN[0].control.packed = value; + //glRenderer->winN[1].control.packed = value >> 8; + break; + case REG_WINOUT: + value &= 0x3F3F; + //glRenderer->winout.packed = value; + //glRenderer->objwin.packed = value >> 8; + break; + case REG_MOSAIC: + glRenderer->mosaic = value; + break; + case REG_GREENSWP: + mLOG(GBA_VIDEO, STUB, "Stub video register write: 0x%03X", address); + break; + default: + mLOG(GBA_VIDEO, GAME_ERROR, "Invalid video register: 0x%03X", address); + } + return value; } void GBAVideoGLRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) { struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer; if (glRenderer->paletteDirty) { glBindTexture(GL_TEXTURE_2D, glRenderer->paletteTex); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB5_A1, 256, 2, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, glRenderer->d.palette); + 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; } - glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[5]); + 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))) { + continue; + } + // TODO: PBOs + glBindTexture(GL_TEXTURE_2D, glRenderer->vramTex); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 8 * i, 256, 8, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, &glRenderer->d.vram[2048 * i]); + } + glRenderer->vramDirty = 0; + + glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[1]); glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS); glUseProgram(glRenderer->compositeProgram); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, glRenderer->vramTex); + glActiveTexture(GL_TEXTURE0 + 1); glBindTexture(GL_TEXTURE_2D, glRenderer->paletteTex); - glUniform1i(0, 0); - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, _vertices); + glUniform1i(0, y); + glUniform1i(1, 0); + glUniform1i(2, 1); + glUniform1i(3, glRenderer->bg[0].screenBase); + glUniform1i(4, glRenderer->bg[0].charBase); + glUniform2i(5, glRenderer->bg[0].x, glRenderer->bg[0].y); + glVertexAttribPointer(0, 2, GL_INT, GL_FALSE, 0, _vertices); glEnableVertexAttribArray(0); glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

@@ -171,7 +468,7 @@

void GBAVideoGLRendererFinishFrame(struct GBAVideoRenderer* renderer) { struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer; glFinish(); - glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[5]); + glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[1]); glPixelStorei(GL_PACK_ROW_LENGTH, glRenderer->outputBufferStride); glReadPixels(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS, GL_RGBA, GL_UNSIGNED_BYTE, glRenderer->outputBuffer); glClearColor(1.f, 1.f, 0.f, 1.f);

@@ -187,3 +484,53 @@ void GBAVideoGLRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels) {

} +static void GBAVideoGLRendererWriteBGCNT(struct GBAVideoGLBackground* bg, uint16_t value) { + bg->priority = GBARegisterBGCNTGetPriority(value); + bg->charBase = GBARegisterBGCNTGetCharBase(value) << 13; + bg->mosaic = GBARegisterBGCNTGetMosaic(value); + bg->multipalette = GBARegisterBGCNTGet256Color(value); + bg->screenBase = GBARegisterBGCNTGetScreenBase(value) << 10; + bg->overflow = GBARegisterBGCNTGetOverflow(value); + bg->size = GBARegisterBGCNTGetSize(value); +} + +static void GBAVideoGLRendererWriteBGX_LO(struct GBAVideoGLBackground* bg, uint16_t value) { + bg->refx = (bg->refx & 0xFFFF0000) | value; + bg->sx = bg->refx; +} + +static void GBAVideoGLRendererWriteBGX_HI(struct GBAVideoGLBackground* bg, uint16_t value) { + bg->refx = (bg->refx & 0x0000FFFF) | (value << 16); + bg->refx <<= 4; + bg->refx >>= 4; + bg->sx = bg->refx; +} + +static void GBAVideoGLRendererWriteBGY_LO(struct GBAVideoGLBackground* bg, uint16_t value) { + bg->refy = (bg->refy & 0xFFFF0000) | value; + bg->sy = bg->refy; +} + +static void GBAVideoGLRendererWriteBGY_HI(struct GBAVideoGLBackground* bg, uint16_t value) { + bg->refy = (bg->refy & 0x0000FFFF) | (value << 16); + bg->refy <<= 4; + bg->refy >>= 4; + bg->sy = bg->refy; +} + +static void GBAVideoGLRendererWriteBLDCNT(struct GBAVideoGLRenderer* renderer, uint16_t value) { + renderer->bg[0].target1 = GBARegisterBLDCNTGetTarget1Bg0(value); + renderer->bg[1].target1 = GBARegisterBLDCNTGetTarget1Bg1(value); + renderer->bg[2].target1 = GBARegisterBLDCNTGetTarget1Bg2(value); + renderer->bg[3].target1 = GBARegisterBLDCNTGetTarget1Bg3(value); + renderer->bg[0].target2 = GBARegisterBLDCNTGetTarget2Bg0(value); + renderer->bg[1].target2 = GBARegisterBLDCNTGetTarget2Bg1(value); + renderer->bg[2].target2 = GBARegisterBLDCNTGetTarget2Bg2(value); + renderer->bg[3].target2 = GBARegisterBLDCNTGetTarget2Bg3(value); + + renderer->blendEffect = GBARegisterBLDCNTGetEffect(value); + renderer->target1Obj = GBARegisterBLDCNTGetTarget1Obj(value); + renderer->target1Bd = GBARegisterBLDCNTGetTarget1Bd(value); + renderer->target2Obj = GBARegisterBLDCNTGetTarget2Obj(value); + renderer->target2Bd = GBARegisterBLDCNTGetTarget2Bd(value); +}