all repos — mgba @ bb1e598a7862892cb848f34e68a2ea85ce3f0576

mGBA Game Boy Advance Emulator

Start GLSL renderer
Jeffrey Pfau jeffrey@endrift.com
Tue, 07 May 2013 01:04:36 -0700
commit

bb1e598a7862892cb848f34e68a2ea85ce3f0576

parent

db96be98dcd40796597d96afe4776b7f4ceff928

4 files changed, 453 insertions(+), 79 deletions(-)

jump to
A src/gba/renderers/video-glsl.c

@@ -0,0 +1,177 @@

+#include "video-glsl.h" + +#include <string.h> + +#define UNIFORM_LOCATION(UNIFORM) (glGetUniformLocation(glslRenderer->program, UNIFORM)) + +static const GLfloat _vertices[4] = { + -1, 0, + 1, 0 +}; + +static const GLchar* _fragmentShader[] = { + "uniform float y;", + "uniform sampler2D palette;", + + "void main() {", + " gl_FragColor = texture2D(palette, vec2(0, y / 256.0));", + "}" +}; + +static const GLchar* _vertexShader[] = { + "attribute vec2 vert;", + "uniform float y;", + + "void main() {", + " gl_Position = vec4(vert.x, 1.0 - y / 80.0, 0, 1.0);", + "}" +}; + +static void GBAVideoGLSLRendererInit(struct GBAVideoRenderer* renderer); +static void GBAVideoGLSLRendererDeinit(struct GBAVideoRenderer* renderer); +static void GBAVideoGLSLRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value); +static uint16_t GBAVideoGLSLRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value); +static void GBAVideoGLSLRendererDrawScanline(struct GBAVideoRenderer* renderer, int y); +static void GBAVideoGLSLRendererFinishFrame(struct GBAVideoRenderer* renderer); + +void GBAVideoGLSLRendererCreate(struct GBAVideoGLSLRenderer* glslRenderer) { + glslRenderer->d.init = GBAVideoGLSLRendererInit; + glslRenderer->d.deinit = GBAVideoGLSLRendererDeinit; + glslRenderer->d.writeVideoRegister = GBAVideoGLSLRendererWriteVideoRegister; + glslRenderer->d.writePalette = GBAVideoGLSLRendererWritePalette; + glslRenderer->d.drawScanline = GBAVideoGLSLRendererDrawScanline; + glslRenderer->d.finishFrame = GBAVideoGLSLRendererFinishFrame; + + glslRenderer->d.turbo = 0; + glslRenderer->d.framesPending = 0; + glslRenderer->d.frameskip = 0; + + glslRenderer->fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + glslRenderer->vertexShader = glCreateShader(GL_VERTEX_SHADER); + glslRenderer->program = glCreateProgram(); + + glShaderSource(glslRenderer->fragmentShader, 5, _fragmentShader, 0); + glShaderSource(glslRenderer->vertexShader, 5, _vertexShader, 0); + + glAttachShader(glslRenderer->program, glslRenderer->vertexShader); + glAttachShader(glslRenderer->program, glslRenderer->fragmentShader); + char log[1024]; + glCompileShader(glslRenderer->fragmentShader); + glCompileShader(glslRenderer->vertexShader); + glGetShaderInfoLog(glslRenderer->fragmentShader, 1024, 0, log); + glGetShaderInfoLog(glslRenderer->vertexShader, 1024, 0, log); + glLinkProgram(glslRenderer->program); + + glGenTextures(1, &glslRenderer->vramTexture); + glBindTexture(GL_TEXTURE_2D, glslRenderer->vramTexture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + memset(glslRenderer->vram, 0, sizeof (glslRenderer->vram)); + + { + pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + glslRenderer->mutex = mutex; + pthread_cond_t cond = PTHREAD_COND_INITIALIZER; + glslRenderer->upCond = cond; + glslRenderer->downCond = cond; + } +} + +void GBAVideoGLSLRendererProcessEvents(struct GBAVideoGLSLRenderer* glslRenderer) { + glUseProgram(glslRenderer->program); + glUniform1i(UNIFORM_LOCATION("palette"), 0); + + glEnable(GL_TEXTURE_2D); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, glslRenderer->vramTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 512, 256, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, glslRenderer->vram); + + GLuint location = glGetAttribLocation(glslRenderer->program, "vert"); + glEnableVertexAttribArray(location); + glVertexAttribPointer(location, 2, GL_FLOAT, GL_FALSE, 0, _vertices); + int y; + for (y = 0; y < VIDEO_VERTICAL_PIXELS; ++y) { + glUniform1f(UNIFORM_LOCATION("y"), y); + glDrawArrays(GL_LINES, 0, 2); + } + glDisableVertexAttribArray(location); + glFlush(); +} + +static void GBAVideoGLSLRendererInit(struct GBAVideoRenderer* renderer) { + struct GBAVideoGLSLRenderer* glslRenderer = (struct GBAVideoGLSLRenderer*) renderer; + + glslRenderer->state = GLSL_NONE; + glslRenderer->y = 0; + + pthread_mutex_init(&glslRenderer->mutex, 0); + pthread_cond_init(&glslRenderer->upCond, 0); + pthread_cond_init(&glslRenderer->downCond, 0); +} + +static void GBAVideoGLSLRendererDeinit(struct GBAVideoRenderer* renderer) { + struct GBAVideoGLSLRenderer* glslRenderer = (struct GBAVideoGLSLRenderer*) renderer; + + /*glDeleteShader(glslRenderer->fragmentShader); + glDeleteShader(glslRenderer->vertexShader); + glDeleteProgram(glslRenderer->program); + + glDeleteTextures(1, &glslRenderer->paletteTexture);*/ + + pthread_mutex_lock(&glslRenderer->mutex); + pthread_cond_broadcast(&glslRenderer->upCond); + pthread_mutex_unlock(&glslRenderer->mutex); + + pthread_mutex_destroy(&glslRenderer->mutex); + pthread_cond_destroy(&glslRenderer->upCond); + pthread_cond_destroy(&glslRenderer->downCond); +} + +static void GBAVideoGLSLRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) { + struct GBAVideoGLSLRenderer* glslRenderer = (struct GBAVideoGLSLRenderer*) renderer; + GLshort color = 0; + color |= (value & 0x001F) << 11; + color |= (value & 0x03E0) << 1; + color |= (value & 0x7C00) >> 9; + glslRenderer->vram[(address >> 1) + glslRenderer->y * 512] = color; +} + +static uint16_t GBAVideoGLSLRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) { + struct GBAVideoGLSLRenderer* glslRenderer = (struct GBAVideoGLSLRenderer*) renderer; + (void)(glslRenderer); + (void)(address); + + return value; +} + +static void GBAVideoGLSLRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) { + struct GBAVideoGLSLRenderer* glslRenderer = (struct GBAVideoGLSLRenderer*) renderer; + + glslRenderer->y = y + 1; + if (y + 1 < VIDEO_VERTICAL_PIXELS) { + memcpy(&glslRenderer->vram[(y + 1) * 512], &glslRenderer->vram[y * 512], 1024); + } else { + glslRenderer->y = 0; + memcpy(&glslRenderer->vram[0], &glslRenderer->vram[y * 512], 1024); + } +} + +static void GBAVideoGLSLRendererFinishFrame(struct GBAVideoRenderer* renderer) { + struct GBAVideoGLSLRenderer* glslRenderer = (struct GBAVideoGLSLRenderer*) renderer; + + pthread_mutex_lock(&glslRenderer->mutex); + glslRenderer->state = GLSL_NONE; + if (renderer->frameskip > 0) { + --renderer->frameskip; + } else { + renderer->framesPending++; + pthread_cond_broadcast(&glslRenderer->upCond); + if (!renderer->turbo) { + pthread_cond_wait(&glslRenderer->downCond, &glslRenderer->mutex); + } + } + pthread_mutex_unlock(&glslRenderer->mutex); +}
A src/gba/renderers/video-glsl.h

@@ -0,0 +1,39 @@

+#ifndef VIDEO_GLSL_H +#define VIDEO_GLSL_H + +#include "gba-video.h" + +#include <pthread.h> + +#ifdef __APPLE__ +#include <OpenGL/gl.h> +#else +#include <GL/gl.h> +#endif + +struct GBAVideoGLSLRenderer { + struct GBAVideoRenderer d; + + int y; + enum { + GLSL_NONE, + GLSL_DRAW_SCANLINE, + GLSL_FINISH_FRAME + } state; + + GLuint fragmentShader; + GLuint vertexShader; + GLuint program; + + GLuint vramTexture; + GLushort vram[512 * 256]; + + pthread_mutex_t mutex; + pthread_cond_t upCond; + pthread_cond_t downCond; +}; + +void GBAVideoGLSLRendererCreate(struct GBAVideoGLSLRenderer* renderer); +void GBAVideoGLSLRendererProcessEvents(struct GBAVideoGLSLRenderer* renderer); + +#endif
M src/main.csrc/main.c

@@ -1,7 +1,7 @@

#include "debugger.h" #include "gba-thread.h" #include "gba.h" -#include "renderers/video-software.h" +#include "renderers/video-glsl.h" #include <SDL.h> #ifdef __APPLE__

@@ -16,30 +16,11 @@ #include <signal.h>

#include <sys/time.h> #include <unistd.h> -struct GLSoftwareRenderer { - struct GBAVideoSoftwareRenderer d; - - GLuint tex; -}; - -static int _GBASDLInit(struct GLSoftwareRenderer* renderer); -static void _GBASDLDeinit(struct GLSoftwareRenderer* renderer); -static void _GBASDLRunloop(struct GBAThread* context, struct GLSoftwareRenderer* renderer); +static int _GBASDLInit(void); +static void _GBASDLDeinit(void); +static void _GBASDLRunloop(struct GBAThread* context, struct GBAVideoGLSLRenderer* renderer); static void _GBASDLHandleKeypress(struct GBAThread* context, const struct SDL_KeyboardEvent* event); -static const GLint _glVertices[] = { - 0, 0, - 256, 0, - 256, 256, - 0, 256 -}; - -static const GLint _glTexCoords[] = { - 0, 0, - 1, 0, - 1, 1, - 0, 1 -}; int main(int argc, char** argv) { const char* fname = "test.rom";

@@ -51,21 +32,16 @@ if (fd < 0) {

return 1; } - sigset_t signals; - sigaddset(&signals, SIGINT); - sigaddset(&signals, SIGTRAP); - pthread_sigmask(SIG_BLOCK, &signals, 0); - struct GBAThread context; - struct GLSoftwareRenderer renderer; - GBAVideoSoftwareRendererCreate(&renderer.d); + struct GBAVideoGLSLRenderer renderer; - if (!_GBASDLInit(&renderer)) { + if (!_GBASDLInit()) { return 1; } + GBAVideoGLSLRendererCreate(&renderer); context.fd = fd; - context.renderer = &renderer.d.d; + context.renderer = &renderer.d; GBAThreadStart(&context); _GBASDLRunloop(&context, &renderer);

@@ -73,12 +49,12 @@

GBAThreadJoin(&context); close(fd); - _GBASDLDeinit(&renderer); + _GBASDLDeinit(); return 0; } -static int _GBASDLInit(struct GLSoftwareRenderer* renderer) { +static int _GBASDLInit() { if (SDL_Init(SDL_INIT_VIDEO) < 0) { return 0; }

@@ -90,41 +66,21 @@ SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);

SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); SDL_SetVideoMode(240, 160, 32, SDL_OPENGL); - renderer->d.outputBuffer = malloc(256 * 256 * 4); - renderer->d.outputBufferStride = 256; - glGenTextures(1, &renderer->tex); - glBindTexture(GL_TEXTURE_2D, renderer->tex); - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glViewport(0, 0, 240, 160); return 1; } -static void _GBASDLRunloop(struct GBAThread* context, struct GLSoftwareRenderer* renderer) { +static void _GBASDLRunloop(struct GBAThread* context, struct GBAVideoGLSLRenderer* renderer) { SDL_Event event; - int err; glEnable(GL_TEXTURE_2D); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glEnableClientState(GL_VERTEX_ARRAY); - glVertexPointer(2, GL_INT, 0, _glVertices); - glTexCoordPointer(2, GL_INT, 0, _glTexCoords); - glMatrixMode (GL_PROJECTION); - glLoadIdentity(); - glOrtho(0, 240, 160, 0, 0, 1); - while (context->started) { - pthread_mutex_lock(&renderer->d.mutex); - if (renderer->d.d.framesPending) { - renderer->d.d.framesPending = 0; - pthread_mutex_unlock(&renderer->d.mutex); - glBindTexture(GL_TEXTURE_2D, renderer->tex); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, renderer->d.outputBuffer); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + while (context->started && context->debugger->state != DEBUGGER_EXITING) { + GBAVideoGLSLRendererProcessEvents(renderer); + pthread_mutex_lock(&renderer->mutex); + if (renderer->d.framesPending) { + renderer->d.framesPending = 0; + pthread_mutex_unlock(&renderer->mutex); SDL_GL_SwapBuffers();

@@ -140,29 +96,17 @@ _GBASDLHandleKeypress(context, &event.key);

break; } } - pthread_mutex_lock(&renderer->d.mutex); - pthread_cond_broadcast(&renderer->d.downCond); - pthread_mutex_unlock(&renderer->d.mutex); + pthread_mutex_lock(&renderer->mutex); + pthread_cond_broadcast(&renderer->downCond); } else { - while (!renderer->d.d.framesPending) { - struct timeval tv; - struct timespec ts; - gettimeofday(&tv, 0); - ts.tv_sec = tv.tv_sec; - ts.tv_nsec = tv.tv_usec * 1000 + 800000; - err = pthread_cond_timedwait(&renderer->d.upCond, &renderer->d.mutex, &ts); - if (err == ETIMEDOUT) { - break; - } - } - pthread_mutex_unlock(&renderer->d.mutex); + pthread_cond_broadcast(&renderer->downCond); + pthread_cond_wait(&renderer->upCond, &renderer->mutex); } + pthread_mutex_unlock(&renderer->mutex); } } -static void _GBASDLDeinit(struct GLSoftwareRenderer* renderer) { - free(renderer->d.outputBuffer); - +static void _GBASDLDeinit() { SDL_Quit(); }
A src/sdl/main.c

@@ -0,0 +1,214 @@

+#include "debugger.h" +#include "gba-thread.h" +#include "gba.h" +#include "renderers/video-software.h" + +#include <SDL.h> +#ifdef __APPLE__ +#include <OpenGL/gl.h> +#else +#include <GL/gl.h> +#endif + +#include <fcntl.h> +#include <errno.h> +#include <signal.h> +#include <sys/time.h> +#include <unistd.h> + +struct GLSoftwareRenderer { + struct GBAVideoSoftwareRenderer d; + + GLuint tex; +}; + +static int _GBASDLInit(struct GLSoftwareRenderer* renderer); +static void _GBASDLDeinit(struct GLSoftwareRenderer* renderer); +static void _GBASDLRunloop(struct GBAThread* context, struct GLSoftwareRenderer* renderer); +static void _GBASDLHandleKeypress(struct GBAThread* context, const struct SDL_KeyboardEvent* event); + +static const GLint _glVertices[] = { + 0, 0, + 256, 0, + 256, 256, + 0, 256 +}; + +static const GLint _glTexCoords[] = { + 0, 0, + 1, 0, + 1, 1, + 0, 1 +}; + +int main(int argc, char** argv) { + const char* fname = "test.rom"; + if (argc > 1) { + fname = argv[1]; + } + int fd = open(fname, O_RDONLY); + if (fd < 0) { + return 1; + } + + sigset_t signals; + sigaddset(&signals, SIGINT); + sigaddset(&signals, SIGTRAP); + pthread_sigmask(SIG_BLOCK, &signals, 0); + + struct GBAThread context; + struct GLSoftwareRenderer renderer; + GBAVideoSoftwareRendererCreate(&renderer.d); + + if (!_GBASDLInit(&renderer)) { + return 1; + } + + context.fd = fd; + context.renderer = &renderer.d.d; + GBAThreadStart(&context); + + _GBASDLRunloop(&context, &renderer); + + GBAThreadJoin(&context); + close(fd); + + _GBASDLDeinit(&renderer); + + return 0; +} + +static int _GBASDLInit(struct GLSoftwareRenderer* renderer) { + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + return 0; + } + + SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1); + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); + SDL_SetVideoMode(240, 160, 32, SDL_OPENGL); + + renderer->d.outputBuffer = malloc(256 * 256 * 4); + renderer->d.outputBufferStride = 256; + glGenTextures(1, &renderer->tex); + glBindTexture(GL_TEXTURE_2D, renderer->tex); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glViewport(0, 0, 240, 160); + + return 1; +} + +static void _GBASDLRunloop(struct GBAThread* context, struct GLSoftwareRenderer* renderer) { + SDL_Event event; + + int err; + glEnable(GL_TEXTURE_2D); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2, GL_INT, 0, _glVertices); + glTexCoordPointer(2, GL_INT, 0, _glTexCoords); + glMatrixMode (GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, 240, 160, 0, 0, 1); + while (context->started) { + pthread_mutex_lock(&renderer->d.mutex); + if (renderer->d.d.framesPending) { + renderer->d.d.framesPending = 0; + pthread_mutex_unlock(&renderer->d.mutex); + glBindTexture(GL_TEXTURE_2D, renderer->tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, renderer->d.outputBuffer); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + SDL_GL_SwapBuffers(); + + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_QUIT: + // FIXME: this isn't thread-safe + context->debugger->state = DEBUGGER_EXITING; + break; + case SDL_KEYDOWN: + case SDL_KEYUP: + _GBASDLHandleKeypress(context, &event.key); + break; + } + } + pthread_mutex_lock(&renderer->d.mutex); + pthread_cond_broadcast(&renderer->d.downCond); + pthread_mutex_unlock(&renderer->d.mutex); + } else { + while (!renderer->d.d.framesPending) { + struct timeval tv; + struct timespec ts; + gettimeofday(&tv, 0); + ts.tv_sec = tv.tv_sec; + ts.tv_nsec = tv.tv_usec * 1000 + 800000; + err = pthread_cond_timedwait(&renderer->d.upCond, &renderer->d.mutex, &ts); + if (err == ETIMEDOUT) { + break; + } + } + pthread_mutex_unlock(&renderer->d.mutex); + } + } +} + +static void _GBASDLDeinit(struct GLSoftwareRenderer* renderer) { + free(renderer->d.outputBuffer); + + SDL_Quit(); +} + +static void _GBASDLHandleKeypress(struct GBAThread* context, const struct SDL_KeyboardEvent* event) { + enum GBAKey key = 0; + switch (event->keysym.sym) { + case SDLK_z: + key = GBA_KEY_A; + break; + case SDLK_x: + key = GBA_KEY_B; + break; + case SDLK_a: + key = GBA_KEY_L; + break; + case SDLK_s: + key = GBA_KEY_R; + break; + case SDLK_RETURN: + key = GBA_KEY_START; + break; + case SDLK_BACKSPACE: + key = GBA_KEY_SELECT; + break; + case SDLK_UP: + key = GBA_KEY_UP; + break; + case SDLK_DOWN: + key = GBA_KEY_DOWN; + break; + case SDLK_LEFT: + key = GBA_KEY_LEFT; + break; + case SDLK_RIGHT: + key = GBA_KEY_RIGHT; + break; + case SDLK_TAB: + context->renderer->turbo = !context->renderer->turbo; + return; + default: + return; + } + + if (event->type == SDL_KEYDOWN) { + context->activeKeys |= 1 << key; + } else { + context->activeKeys &= ~(1 << key); + } +}