all repos — mgba @ 46e24e84da8e4a98bee6dcd673a6ad39b0de9758

mGBA Game Boy Advance Emulator

OpenGL: Create video backend for GLES2, used on Raspberry Pi
Jeffrey Pfau jeffrey@endrift.com
Thu, 30 Jul 2015 14:41:40 -0700
commit

46e24e84da8e4a98bee6dcd673a6ad39b0de9758

parent

657640c9d56034c073d28e41d39a4337eca33500

M CMakeLists.txtCMakeLists.txt

@@ -21,6 +21,7 @@ set(BUILD_PERF OFF CACHE BOOL "Build performance profiling tool")

set(BUILD_STATIC OFF CACHE BOOL "Build a static library") set(BUILD_SHARED ON CACHE BOOL "Build a shared library") set(BUILD_GL ON CACHE STRING "Build with OpenGL") +set(BUILD_GLES2 OFF CACHE STRING "Build with OpenGL|ES 2") file(GLOB ARM_SRC ${CMAKE_SOURCE_DIR}/src/arm/*.c) file(GLOB GBA_SRC ${CMAKE_SOURCE_DIR}/src/gba/*.c) file(GLOB GBA_CHEATS_SRC ${CMAKE_SOURCE_DIR}/src/gba/cheats/*.c)

@@ -122,10 +123,11 @@ endif()

else() find_feature(USE_CLI_DEBUGGER "libedit") endif() -if(BUILD_GL) +if(BUILD_GL OR BUILD_GLES2) find_package(OpenGL QUIET) if(NOT OPENGL_FOUND) - set(BUILD_GL OFF) + set(BUILD_GL OFF CACHE BOOL "OpenGL not found" FORCE) + set(BUILD_GLES2 OFF CACHE BOOL "OpenGL|ES 2 not found" FORCE) endif() endif() find_feature(USE_FFMPEG "libavcodec;libavformat;libavresample;libavutil;libswscale")

@@ -174,6 +176,10 @@ if(BUILD_BBB OR BUILD_RASPI OR BUILD_PANDORA)

if(NOT BUILD_EGL) add_definitions(-DCOLOR_16_BIT -DCOLOR_5_6_5) endif() +endif() + +if(BUILD_RASPI) + set(BUILD_GL OFF CACHE BOOL "OpenGL not supported" FORCE) endif() if(BUILD_PANDORA)

@@ -396,6 +402,10 @@ set_target_properties(${BINARY_NAME} PROPERTIES VERSION ${LIB_VERSION_STRING} SOVERSION ${LIB_VERSION_ABI} COMPILE_DEFINITIONS "${FEATURE_DEFINES}")

if(BUILD_GL) add_definitions(-DBUILD_GL) +endif() + +if(BUILD_GLES2) + add_definitions(-DBUILD_GLES2) endif() if(BUILD_LIBRETRO)
A src/platform/opengl/gles2.c

@@ -0,0 +1,136 @@

+/* Copyright (c) 2013-2015 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 "gles2.h" + +#include "gba/video.h" + +static const char* const _vertexShader = + "attribute vec4 position;\n" + "varying vec2 texCoord;\n" + + "void main() {\n" + " gl_Position = position;\n" + " texCoord = (position.st + vec2(1.0, -1.0)) * vec2(0.46875, -0.3125);\n" + "}"; + +static const char* const _fragmentShader = + "varying vec2 texCoord;\n" + "uniform sampler2D tex;\n" + + "void main() {\n" + " vec4 color = texture2D(tex, texCoord);\n" + " color.a = 1.;\n" + " gl_FragColor = color;" + "}"; + +static const GLfloat _vertices[] = { + -1.f, -1.f, + -1.f, 1.f, + 1.f, 1.f, + 1.f, -1.f, +}; + +static void GBAGLES2ContextInit(struct VideoBackend* v, WHandle handle) { + UNUSED(handle); + struct GBAGLES2Context* context = (struct GBAGLES2Context*) v; + glGenTextures(1, &context->tex); + glBindTexture(GL_TEXTURE_2D, context->tex); + 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); + +#ifdef COLOR_16_BIT +#ifdef COLOR_5_6_5 + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 0); +#else + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, 0); +#endif +#else + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); +#endif + + glShaderSource(context->fragmentShader, 1, (const GLchar**) &_fragmentShader, 0); + glShaderSource(context->vertexShader, 1, (const GLchar**) &_vertexShader, 0); + glAttachShader(context->program, context->vertexShader); + glAttachShader(context->program, context->fragmentShader); + char log[1024]; + glCompileShader(context->fragmentShader); + glCompileShader(context->vertexShader); + glGetShaderInfoLog(context->fragmentShader, 1024, 0, log); + glGetShaderInfoLog(context->vertexShader, 1024, 0, log); + glLinkProgram(context->program); + glGetProgramInfoLog(context->program, 1024, 0, log); + printf("%s\n", log); + context->texLocation = glGetUniformLocation(context->program, "tex"); + context->positionLocation = glGetAttribLocation(context->program, "position"); + glClearColor(0.f, 0.f, 0.f, 1.f); +} + +static void GBAGLES2ContextDeinit(struct VideoBackend* v) { + struct GBAGLES2Context* context = (struct GBAGLES2Context*) v; + glDeleteTextures(1, &context->tex); +} + +static void GBAGLES2ContextResized(struct VideoBackend* v, int w, int h) { + int drawW = w; + int drawH = h; + if (v->lockAspectRatio) { + if (w * 2 > h * 3) { + drawW = h * 3 / 2; + } else if (w * 2 < h * 3) { + drawH = w * 2 / 3; + } + } + glViewport(0, 0, 240, 160); + glClearColor(0.f, 0.f, 0.f, 1.f); + glClear(GL_COLOR_BUFFER_BIT); + glViewport((w - drawW) / 2, (h - drawH) / 2, drawW, drawH); +} + +static void GBAGLES2ContextClear(struct VideoBackend* v) { + UNUSED(v); + glClearColor(0.f, 0.f, 0.f, 1.f); + glClear(GL_COLOR_BUFFER_BIT); +} + +void GBAGLES2ContextDrawFrame(struct VideoBackend* v) { + struct GBAGLES2Context* context = (struct GBAGLES2Context*) v; + glUseProgram(context->program); + glUniform1i(context->texLocation, 0); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, context->tex); + glVertexAttribPointer(context->positionLocation, 2, GL_FLOAT, GL_FALSE, 0, _vertices); + glEnableVertexAttribArray(context->positionLocation); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + glUseProgram(0); +} + +void GBAGLES2ContextPostFrame(struct VideoBackend* v, const void* frame) { + struct GBAGLES2Context* context = (struct GBAGLES2Context*) v; + glBindTexture(GL_TEXTURE_2D, context->tex); +#ifdef COLOR_16_BIT +#ifdef COLOR_5_6_5 + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, frame); +#else + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, frame); +#endif +#else + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, frame); +#endif +} + +void GBAGLES2ContextCreate(struct GBAGLES2Context* context) { + context->d.init = GBAGLES2ContextInit; + context->d.deinit = GBAGLES2ContextDeinit; + context->d.resized = GBAGLES2ContextResized; + context->d.swap = 0; + context->d.clear = GBAGLES2ContextClear; + context->d.postFrame = GBAGLES2ContextPostFrame; + context->d.drawFrame = GBAGLES2ContextDrawFrame; + context->d.setMessage = 0; + context->d.clearMessage = 0; +}
A src/platform/opengl/gles2.h

@@ -0,0 +1,27 @@

+/* Copyright (c) 2013-2015 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 GLES2_H +#define GLES2_H + +#include <GLES2/gl2.h> + +#include "platform/video-backend.h" + +struct GBAGLES2Context { + struct VideoBackend d; + + GLuint tex; + GLuint fragmentShader; + GLuint vertexShader; + GLuint program; + GLuint bufferObject; + GLuint texLocation; + GLuint positionLocation; +}; + +void GBAGLES2ContextCreate(struct GBAGLES2Context*); + +#endif
M src/platform/perf-main.csrc/platform/perf-main.c

@@ -186,7 +186,7 @@ }

} } GBASyncWaitFrameEnd(&context->sync); - if (*frames == duration) { + if (duration > 0 && *frames == duration) { _GBAPerfShutdown(0); } if (_dispatchExiting) {
M src/platform/sdl/CMakeLists.txtsrc/platform/sdl/CMakeLists.txt

@@ -51,12 +51,14 @@ set(MAIN_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/main.c)

if(BUILD_RASPI) add_definitions(-DBUILD_RASPI) - set(EGL_MAIN_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/egl-sdl.c) - set(EGL_LIBRARY "-lEGL -lGLESv2 -lbcm_host") + list(APPEND PLATFORM_SRC ${CMAKE_SOURCE_DIR}/src/platform/opengl/gles2.c) + list(APPEND MAIN_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/egl-sdl.c) + list(APPEND OPENGL_LIBRARY "-lEGL -lGLESv2 -lbcm_host") + set(BUILD_GLES2 ON CACHE BOOL "Using OpenGL|ES 2" FORCE) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fgnu89-inline") - add_executable(${BINARY_NAME}-rpi ${PLATFORM_SRC} ${MAIN_SRC} ${EGL_MAIN_SRC}) + add_executable(${BINARY_NAME}-rpi ${PLATFORM_SRC} ${MAIN_SRC}) set_target_properties(${BINARY_NAME}-rpi PROPERTIES COMPILE_DEFINITIONS "${FEATURE_DEFINES}") - target_link_libraries(${BINARY_NAME}-rpi ${BINARY_NAME} ${PLATFORM_LIBRARY} ${EGL_LIBRARY}) + target_link_libraries(${BINARY_NAME}-rpi ${BINARY_NAME} ${PLATFORM_LIBRARY} ${OPENGL_LIBRARY}) install(TARGETS ${BINARY_NAME}-rpi DESTINATION bin COMPONENT ${BINARY_NAME}-rpi) endif()
M src/platform/sdl/egl-sdl.csrc/platform/sdl/egl-sdl.c

@@ -1,37 +1,26 @@

-/* Copyright (c) 2013-2014 Jeffrey Pfau +/* Copyright (c) 2013-2015 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 "main.h" -static const char* _vertexShader = - "attribute vec4 position;\n" - "varying vec2 texCoord;\n" +static void _sdlSwap(struct VideoBackend* context) { + UNUSED(context); + SDL_GL_SwapBuffers(); +} - "void main() {\n" - " gl_Position = position;\n" - " texCoord = (position.st + vec2(1.0, -1.0)) * vec2(0.46875, -0.3125);\n" - "}"; +static bool GBASDLGLES2Init(struct SDLSoftwareRenderer* renderer); +static void GBASDLGLES2Runloop(struct GBAThread* context, struct SDLSoftwareRenderer* renderer); +static void GBASDLGLES2Deinit(struct SDLSoftwareRenderer* renderer); -static const char* _fragmentShader = - "varying vec2 texCoord;\n" - "uniform sampler2D tex;\n" +void GBASDLGLES2Create(struct SDLSoftwareRenderer* renderer) { + renderer->init = GBASDLGLES2Init; + renderer->deinit = GBASDLGLES2Deinit; + renderer->runloop = GBASDLGLES2Runloop; +} - "void main() {\n" - " vec4 color = texture2D(tex, texCoord);\n" - " color.a = 1.;\n" - " gl_FragColor = color;" - "}"; - -static const GLfloat _vertices[] = { - -1.f, -1.f, - -1.f, 1.f, - 1.f, 1.f, - 1.f, -1.f, -}; - -bool GBASDLInit(struct SDLSoftwareRenderer* renderer) { +bool GBASDLGLES2Init(struct SDLSoftwareRenderer* renderer) { bcm_host_init(); renderer->display = eglGetDisplay(EGL_DEFAULT_DISPLAY); int major, minor;

@@ -103,36 +92,19 @@ }

renderer->d.outputBuffer = memalign(16, 256 * 256 * 4); renderer->d.outputBufferStride = 256; - glGenTextures(1, &renderer->tex); - glBindTexture(GL_TEXTURE_2D, renderer->tex); - 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, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - renderer->fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); - renderer->vertexShader = glCreateShader(GL_VERTEX_SHADER); - renderer->program = glCreateProgram(); - glShaderSource(renderer->fragmentShader, 1, (const GLchar**) &_fragmentShader, 0); - glShaderSource(renderer->vertexShader, 1, (const GLchar**) &_vertexShader, 0); - glAttachShader(renderer->program, renderer->vertexShader); - glAttachShader(renderer->program, renderer->fragmentShader); - char log[1024]; - glCompileShader(renderer->fragmentShader); - glCompileShader(renderer->vertexShader); - glGetShaderInfoLog(renderer->fragmentShader, 1024, 0, log); - glGetShaderInfoLog(renderer->vertexShader, 1024, 0, log); - glLinkProgram(renderer->program); - glGetProgramInfoLog(renderer->program, 1024, 0, log); - printf("%s\n", log); - renderer->texLocation = glGetUniformLocation(renderer->program, "tex"); - renderer->positionLocation = glGetAttribLocation(renderer->program, "position"); - glClearColor(1.f, 0.f, 0.f, 1.f); + GBAGLES2ContextCreate(&renderer->gl); + renderer->gl.d.user = renderer; + renderer->gl.d.lockAspectRatio = renderer->lockAspectRatio; + renderer->gl.d.filter = renderer->filter; + renderer->gl.d.swap = _sdlSwap; + renderer->gl.d.init(&renderer->gl.d, 0); + return true; } -void GBASDLRunloop(struct GBAThread* context, struct SDLSoftwareRenderer* renderer) { +void GBASDLGLES2Runloop(struct GBAThread* context, struct SDLSoftwareRenderer* renderer) { SDL_Event event; + struct VideoBackend* v = &renderer->gl.d; while (context->state < THREAD_EXITING) { while (SDL_PollEvent(&event)) {

@@ -140,27 +112,22 @@ GBASDLHandleEvent(context, &renderer->player, &event);

} if (GBASyncWaitFrameStart(&context->sync, context->frameskip)) { - glViewport(0, 0, 240, 160); - glClear(GL_COLOR_BUFFER_BIT); - glUseProgram(renderer->program); - glUniform1i(renderer->texLocation, 0); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, renderer->tex); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, renderer->d.outputBuffer); - glVertexAttribPointer(renderer->positionLocation, 2, GL_FLOAT, GL_FALSE, 0, _vertices); - glEnableVertexAttribArray(renderer->positionLocation); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - glUseProgram(0); - eglSwapBuffers(renderer->display, renderer->surface); + v->postFrame(v, renderer->d.outputBuffer); } + v->drawFrame(v); GBASyncWaitFrameEnd(&context->sync); + eglSwapBuffers(renderer->display, renderer->surface); } } -void GBASDLDeinit(struct SDLSoftwareRenderer* renderer) { +void GBASDLGLES2Deinit(struct SDLSoftwareRenderer* renderer) { + if (renderer->gl.d.deinit) { + renderer->gl.d.deinit(&renderer->gl.d); + } eglMakeCurrent(renderer->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglDestroySurface(renderer->display, renderer->surface); eglDestroyContext(renderer->display, renderer->context); eglTerminate(renderer->display); + free(renderer->d.outputBuffer); bcm_host_deinit(); }
M src/platform/sdl/main.csrc/platform/sdl/main.c

@@ -86,6 +86,8 @@ renderer.filter = opts.resampleVideo;

#ifdef BUILD_GL GBASDLGLCreate(&renderer); +#elif defined(BUILD_GLES2) + GBASDLGLES2Create(&renderer); #else GBASDLSWCreate(&renderer); #endif
M src/platform/sdl/main.hsrc/platform/sdl/main.h

@@ -20,11 +20,14 @@ #pragma GCC diagnostic push

#pragma GCC diagnostic ignored "-Wunused-function" #pragma GCC diagnostic ignored "-Wunused-but-set-variable" #include <SDL/SDL.h> -#include <GLES2/gl2.h> #include <EGL/egl.h> #include <bcm_host.h> #pragma GCC diagnostic pop +#endif + +#ifdef BUILD_GLES2 +#include "platform/opengl/gles2.h" #endif #ifdef USE_PIXMAN

@@ -69,13 +72,7 @@ EGLDisplay display;

EGLSurface surface; EGLContext context; EGL_DISPMANX_WINDOW_T window; - GLuint tex; - GLuint fragmentShader; - GLuint vertexShader; - GLuint program; - GLuint bufferObject; - GLuint texLocation; - GLuint positionLocation; + struct GBAGLES2Context gl; #endif #ifdef BUILD_PANDORA

@@ -89,5 +86,9 @@ void GBASDLSWCreate(struct SDLSoftwareRenderer* renderer);

#ifdef BUILD_GL void GBASDLGLCreate(struct SDLSoftwareRenderer* renderer); +#endif + +#ifdef BUILD_GLES2 +void GBASDLGLES2Create(struct SDLSoftwareRenderer* renderer); #endif #endif