OpenGL: Create video backend for GLES2, used on Raspberry Pi

This commit is contained in:
Jeffrey Pfau 2015-07-30 14:41:40 -07:00
parent 657640c9d5
commit 46e24e84da
8 changed files with 224 additions and 79 deletions

View File

@ -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 @@ if(CMAKE_SYSTEM_NAME MATCHES .*BSD)
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")
@ -176,6 +178,10 @@ if(BUILD_BBB OR BUILD_RASPI OR BUILD_PANDORA)
endif()
endif()
if(BUILD_RASPI)
set(BUILD_GL OFF CACHE BOOL "OpenGL not supported" FORCE)
endif()
if(BUILD_PANDORA)
add_definitions(-DBUILD_PANDORA)
endif()
@ -398,6 +404,10 @@ if(BUILD_GL)
add_definitions(-DBUILD_GL)
endif()
if(BUILD_GLES2)
add_definitions(-DBUILD_GLES2)
endif()
if(BUILD_LIBRETRO)
file(GLOB RETRO_SRC ${CMAKE_SOURCE_DIR}/src/platform/libretro/*.c)
add_library(${BINARY_NAME}_libretro SHARED ${CORE_SRC} ${RETRO_SRC})

136
src/platform/opengl/gles2.c Normal file
View File

@ -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;
}

View File

@ -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

View File

@ -186,7 +186,7 @@ static void _GBAPerfRunloop(struct GBAThread* context, int* frames, bool quiet)
}
}
GBASyncWaitFrameEnd(&context->sync);
if (*frames == duration) {
if (duration > 0 && *frames == duration) {
_GBAPerfShutdown(0);
}
if (_dispatchExiting) {

View File

@ -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()

View File

@ -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 @@ bool GBASDLInit(struct SDLSoftwareRenderer* renderer) {
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 @@ void GBASDLRunloop(struct GBAThread* context, struct SDLSoftwareRenderer* render
}
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();
}

View File

@ -86,6 +86,8 @@ int main(int argc, char** argv) {
#ifdef BUILD_GL
GBASDLGLCreate(&renderer);
#elif defined(BUILD_GLES2)
GBASDLGLES2Create(&renderer);
#else
GBASDLSWCreate(&renderer);
#endif

View File

@ -20,13 +20,16 @@
#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
#include <pixman.h>
#endif
@ -69,13 +72,7 @@ struct SDLSoftwareRenderer {
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
@ -90,4 +87,8 @@ void GBASDLSWCreate(struct SDLSoftwareRenderer* renderer);
#ifdef BUILD_GL
void GBASDLGLCreate(struct SDLSoftwareRenderer* renderer);
#endif
#ifdef BUILD_GLES2
void GBASDLGLES2Create(struct SDLSoftwareRenderer* renderer);
#endif
#endif