all repos — mgba @ 4a502c2c03e8647e9a0baf148a9b1aca6f7d33e1

mGBA Game Boy Advance Emulator

OpenGL: Revamp shader backend
Jeffrey Pfau jeffrey@endrift.com
Sun, 01 Nov 2015 11:29:00 -0800
commit

4a502c2c03e8647e9a0baf148a9b1aca6f7d33e1

parent

8c6a57f7d9bf32a90e4315099f77d35ecb5040ad

2 files changed, 228 insertions(+), 112 deletions(-)

jump to
M src/platform/opengl/gles2.csrc/platform/opengl/gles2.c

@@ -21,7 +21,7 @@ "attribute vec4 position;\n"

"varying vec2 texCoord;\n" "void main() {\n" - " gl_Position = position * vec4(1.0, 1.0, 1.0, 1.0);\n" + " gl_Position = position;\n" " texCoord = (position.st + vec2(1.0, 1.0)) * vec2(0.5, 0.5);\n" "}";

@@ -36,7 +36,17 @@ "void main() {\n"

" vec4 color = texture2D(tex, texCoord);\n" " color.a = 1.;\n" " color.rgb = scale * pow(color.rgb, vec3(gamma, gamma, gamma)) + bias;\n" - " gl_FragColor = color;" + " gl_FragColor = color;\n" + "}"; + +static const char* const _nullFragmentShader = + "varying vec2 texCoord;\n" + "uniform sampler2D tex;\n" + + "void main() {\n" + " vec4 color = texture2D(tex, texCoord);\n" + " color.a = 1.;\n" + " gl_FragColor = color;\n" "}"; static const GLfloat _vertices[] = {

@@ -45,6 +55,7 @@ -1.f, 1.f,

1.f, 1.f, 1.f, -1.f, }; + static void GBAGLES2ContextInit(struct VideoBackend* v, WHandle handle) { UNUSED(handle);

@@ -64,44 +75,34 @@ #else

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); #endif - context->program = glCreateProgram(); - context->fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); - context->vertexShader = glCreateShader(GL_VERTEX_SHADER); - context->nullVertexShader = glCreateShader(GL_VERTEX_SHADER); + glClearColor(0.f, 0.f, 0.f, 1.f); - glShaderSource(context->fragmentShader, 1, (const GLchar**) &_fragmentShader, 0); - glShaderSource(context->vertexShader, 1, (const GLchar**) &_vertexShader, 0); - glShaderSource(context->nullVertexShader, 1, (const GLchar**) &_nullVertexShader, 0); - glAttachShader(context->program, context->vertexShader); - glAttachShader(context->program, context->fragmentShader); - char log[1024]; - glCompileShader(context->fragmentShader); - glCompileShader(context->vertexShader); - glCompileShader(context->nullVertexShader); - glGetShaderInfoLog(context->fragmentShader, 1024, 0, log); - printf("%s\n", log); - glGetShaderInfoLog(context->vertexShader, 1024, 0, log); - printf("%s\n", log); - glGetShaderInfoLog(context->nullVertexShader, 1024, 0, log); - printf("%s\n", log); - glLinkProgram(context->program); - glGetProgramInfoLog(context->program, 1024, 0, log); - printf("%s\n", log); - context->texLocation = glGetUniformLocation(context->program, "tex"); - context->gammaLocation = glGetUniformLocation(context->program, "gamma"); - context->biasLocation = glGetUniformLocation(context->program, "bias"); - context->scaleLocation = glGetUniformLocation(context->program, "scale"); - context->positionLocation = glGetAttribLocation(context->program, "position"); - glClearColor(0.f, 0.f, 0.f, 1.f); + struct GBAGLES2Uniform* uniforms = malloc(sizeof(struct GBAGLES2Uniform) * 3); + uniforms[0].name = "gamma"; + uniforms[0].type = GL_FLOAT; + uniforms[0].value.f = 1.0f; + uniforms[1].name = "scale"; + uniforms[1].type = GL_FLOAT_VEC3; + uniforms[1].value.fvec3[0] = 1.0f; + uniforms[1].value.fvec3[1] = 1.0f; + uniforms[1].value.fvec3[2] = 1.0f; + uniforms[2].name = "bias"; + uniforms[2].type = GL_FLOAT_VEC3; + uniforms[2].value.fvec3[0] = 0.0f; + uniforms[2].value.fvec3[1] = 0.0f; + uniforms[2].value.fvec3[2] = 0.0f; + GBAGLES2ShaderInit(&context->initialShader, _vertexShader, _fragmentShader, -1, -1, uniforms, 3); + GBAGLES2ShaderInit(&context->finalShader, 0, 0, 0, 0, 0, 0); + glDeleteFramebuffers(1, &context->finalShader.fbo); + context->finalShader.fbo = 0; } static void GBAGLES2ContextDeinit(struct VideoBackend* v) { struct GBAGLES2Context* context = (struct GBAGLES2Context*) v; glDeleteTextures(1, &context->tex); - glDeleteShader(context->fragmentShader); - glDeleteShader(context->vertexShader); - glDeleteShader(context->nullVertexShader); - glDeleteProgram(context->program); + GBAGLES2ShaderDeinit(&context->initialShader); + GBAGLES2ShaderDeinit(&context->finalShader); + free(context->initialShader.uniforms); } static void GBAGLES2ContextResized(struct VideoBackend* v, int w, int h) {

@@ -126,46 +127,118 @@ glClearColor(0.f, 0.f, 0.f, 1.f);

glClear(GL_COLOR_BUFFER_BIT); } +void _drawShader(struct GBAGLES2Shader* shader) { + GLint viewport[4]; + glBindFramebuffer(GL_FRAMEBUFFER, shader->fbo); + if (shader->blend) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } else { + glDisable(GL_BLEND); + } + glGetIntegerv(GL_VIEWPORT, viewport); + glViewport(0, 0, shader->width ? shader->width : viewport[2], shader->height ? shader->height : viewport[3]); + if (!shader->width || !shader->height) { + GLint oldTex; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldTex); + glBindTexture(GL_TEXTURE_2D, shader->tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, shader->width ? shader->width : viewport[2], shader->height ? shader->height : viewport[3], 0, GL_RGB, GL_UNSIGNED_BYTE, 0); + glBindTexture(GL_TEXTURE_2D, oldTex); + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, shader->filter ? GL_LINEAR : GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, shader->filter ? GL_LINEAR : GL_NEAREST); + glUseProgram(shader->program); + glUniform1i(shader->texLocation, 0); + glVertexAttribPointer(shader->positionLocation, 2, GL_FLOAT, GL_FALSE, 0, _vertices); + glEnableVertexAttribArray(shader->positionLocation); + size_t u; + for (u = 0; u < shader->nUniforms; ++u) { + struct GBAGLES2Uniform* uniform = &shader->uniforms[u]; + switch (uniform->type) { + case GL_FLOAT: + glUniform1f(uniform->location, uniform->value.f); + break; + case GL_INT: + glUniform1f(uniform->location, uniform->value.i); + break; + case GL_UNSIGNED_INT: + glUniform1f(uniform->location, uniform->value.ui); + break; + case GL_FLOAT_VEC2: + glUniform2fv(uniform->location, 1, uniform->value.fvec2); + break; + case GL_FLOAT_VEC3: + glUniform3fv(uniform->location, 1, uniform->value.fvec3); + break; + case GL_FLOAT_VEC4: + glUniform4fv(uniform->location, 1, uniform->value.fvec4); + break; + case GL_INT_VEC2: + glUniform2iv(uniform->location, 1, uniform->value.ivec2); + break; + case GL_INT_VEC3: + glUniform3iv(uniform->location, 1, uniform->value.ivec3); + break; + case GL_INT_VEC4: + glUniform4iv(uniform->location, 1, uniform->value.ivec4); + break; + case GL_UNSIGNED_INT_VEC2: + glUniform2uiv(uniform->location, 1, uniform->value.uivec2); + break; + case GL_UNSIGNED_INT_VEC3: + glUniform3uiv(uniform->location, 1, uniform->value.uivec3); + break; + case GL_UNSIGNED_INT_VEC4: + glUniform4uiv(uniform->location, 1, uniform->value.uivec4); + break; + case GL_FLOAT_MAT2: + glUniformMatrix2fv(uniform->location, 1, GL_FALSE, uniform->value.fmat2x2); + break; + case GL_FLOAT_MAT2x3: + glUniformMatrix2x3fv(uniform->location, 1, GL_FALSE, uniform->value.fmat2x3); + break; + case GL_FLOAT_MAT2x4: + glUniformMatrix2x4fv(uniform->location, 1, GL_FALSE, uniform->value.fmat2x4); + break; + case GL_FLOAT_MAT3x2: + glUniformMatrix3x2fv(uniform->location, 1, GL_FALSE, uniform->value.fmat3x2); + break; + case GL_FLOAT_MAT3: + glUniformMatrix3fv(uniform->location, 1, GL_FALSE, uniform->value.fmat3x3); + break; + case GL_FLOAT_MAT3x4: + glUniformMatrix3x4fv(uniform->location, 1, GL_FALSE, uniform->value.fmat3x4); + break; + case GL_FLOAT_MAT4x2: + glUniformMatrix2fv(uniform->location, 1, GL_FALSE, uniform->value.fmat4x2); + break; + case GL_FLOAT_MAT4x3: + glUniformMatrix2x3fv(uniform->location, 1, GL_FALSE, uniform->value.fmat4x3); + break; + case GL_FLOAT_MAT4: + glUniformMatrix2x4fv(uniform->location, 1, GL_FALSE, uniform->value.fmat4x4); + break; + } + } + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glBindTexture(GL_TEXTURE_2D, shader->tex); + glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); +} + void GBAGLES2ContextDrawFrame(struct VideoBackend* v) { struct GBAGLES2Context* context = (struct GBAGLES2Context*) v; glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, context->tex); - if (context->shader) { - GLint viewport[4]; - if (context->shader->blend) { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } else { - glDisable(GL_BLEND); - } - glGetIntegerv(GL_VIEWPORT, viewport); - glViewport(0, 0, context->shader->width, context->shader->height); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, context->shader->filter ? GL_LINEAR : GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, context->shader->filter ? GL_LINEAR : GL_NEAREST); - glBindFramebuffer(GL_FRAMEBUFFER, context->shader->fbo); - glUseProgram(context->shader->program); - glUniform1i(context->shader->texLocation, 0); - glVertexAttribPointer(context->shader->positionLocation, 2, GL_FLOAT, GL_FALSE, 0, _vertices); - glEnableVertexAttribArray(context->shader->positionLocation); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - glBindTexture(GL_TEXTURE_2D, context->shader->tex); - glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); + context->finalShader.filter = v->filter; + _drawShader(&context->initialShader); + size_t n; + for (n = 0; n < context->nShaders; ++n) { + _drawShader(&context->shaders[n]); } - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, v->filter ? GL_LINEAR : GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, v->filter ? GL_LINEAR : GL_NEAREST); - glUseProgram(context->program); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glUniform1i(context->texLocation, 0); - glUniform1f(context->gammaLocation, context->gamma); - glUniform3f(context->biasLocation, context->bias[0], context->bias[1], context->bias[2]); - glUniform3f(context->scaleLocation, context->scale[0], context->scale[1], context->scale[2]); - glVertexAttribPointer(context->positionLocation, 2, GL_FLOAT, GL_FALSE, 0, _vertices); - glEnableVertexAttribArray(context->positionLocation); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - glDisable(GL_BLEND); + _drawShader(&context->finalShader); glUseProgram(0); }

@@ -195,21 +268,17 @@ context->d.postFrame = GBAGLES2ContextPostFrame;

context->d.drawFrame = GBAGLES2ContextDrawFrame; context->d.setMessage = 0; context->d.clearMessage = 0; - context->shader = 0; - context->gamma = 1.0f; - context->bias[0] = 0.0f; - context->bias[1] = 0.0f; - context->bias[2] = 0.0f; - context->scale[0] = 1.0f; - context->scale[1] = 1.0f; - context->scale[2] = 1.0f; + context->shaders = 0; + context->nShaders = 0; } -void GBAGLES2ShaderInit(struct GBAGLES2Shader* shader, const char* src, int width, int height) { - shader->width = width > 0 ? width : VIDEO_HORIZONTAL_PIXELS; - shader->height = height > 0 ? height : VIDEO_VERTICAL_PIXELS; +void GBAGLES2ShaderInit(struct GBAGLES2Shader* shader, const char* vs, const char* fs, int width, int height, struct GBAGLES2Uniform* uniforms, size_t nUniforms) { + shader->width = width >= 0 ? width : VIDEO_HORIZONTAL_PIXELS; + shader->height = height >= 0 ? height : VIDEO_VERTICAL_PIXELS; shader->filter = false; shader->blend = false; + shader->uniforms = uniforms; + shader->nUniforms = nUniforms; glGenFramebuffers(1, &shader->fbo); glBindFramebuffer(GL_FRAMEBUFFER, shader->fbo);

@@ -219,16 +288,43 @@ 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, shader->width, shader->height, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); + if (shader->width && shader->height) { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, shader->width, shader->height, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); + } glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, shader->tex, 0); shader->program = glCreateProgram(); + shader->vertexShader = glCreateShader(GL_VERTEX_SHADER); shader->fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(shader->fragmentShader, 1, (const GLchar**) &src, 0); + if (vs) { + glShaderSource(shader->vertexShader, 1, (const GLchar**) &vs, 0); + } else { + glShaderSource(shader->vertexShader, 1, (const GLchar**) &_nullVertexShader, 0); + } + if (fs) { + glShaderSource(shader->fragmentShader, 1, (const GLchar**) &fs, 0); + } else { + glShaderSource(shader->fragmentShader, 1, (const GLchar**) &_nullFragmentShader, 0); + } + glAttachShader(shader->program, shader->vertexShader); glAttachShader(shader->program, shader->fragmentShader); - glCompileShader(shader->fragmentShader); char log[1024]; + glCompileShader(shader->fragmentShader); glGetShaderInfoLog(shader->fragmentShader, 1024, 0, log); + printf("%s\n", log); + glCompileShader(shader->vertexShader); + glGetShaderInfoLog(shader->vertexShader, 1024, 0, log); + printf("%s\n", log); + glLinkProgram(shader->program); + glGetProgramInfoLog(shader->program, 1024, 0, log); + printf("%s\n", log); + + shader->texLocation = glGetUniformLocation(shader->program, "tex"); + shader->positionLocation = glGetAttribLocation(shader->program, "position"); + size_t i; + for (i = 0; i < shader->nUniforms; ++i) { + shader->uniforms[i].location = glGetUniformLocation(shader->program, shader->uniforms[i].name); + } glBindFramebuffer(GL_FRAMEBUFFER, 0); }

@@ -236,30 +332,23 @@ void GBAGLES2ShaderDeinit(struct GBAGLES2Shader* shader) {

glDeleteTextures(1, &shader->tex); glDeleteShader(shader->fragmentShader); glDeleteProgram(shader->program); + glDeleteFramebuffers(1, &shader->fbo); } -void GBAGLES2ShaderAttach(struct GBAGLES2Context* context, struct GBAGLES2Shader* shader) { - if (context->shader) { - if (context->shader == shader) { +void GBAGLES2ShaderAttach(struct GBAGLES2Context* context, struct GBAGLES2Shader* shaders, size_t nShaders) { + if (context->shaders) { + if (context->shaders == shaders && context->nShaders == nShaders) { return; } GBAGLES2ShaderDetach(context); } - context->shader = shader; - glAttachShader(shader->program, context->nullVertexShader); - - char log[1024]; - glLinkProgram(shader->program); - glGetProgramInfoLog(shader->program, 1024, 0, log); - printf("%s\n", log); - shader->texLocation = glGetUniformLocation(shader->program, "tex"); - shader->positionLocation = glGetAttribLocation(shader->program, "position"); + context->shaders = shaders; + context->nShaders = nShaders; } void GBAGLES2ShaderDetach(struct GBAGLES2Context* context) { - if (!context->shader) { + if (!context->shaders) { return; } - glDetachShader(context->shader->program, context->nullVertexShader); - context->shader = 0; + context->shaders = 0; }
M src/platform/opengl/gles2.hsrc/platform/opengl/gles2.h

@@ -8,7 +8,7 @@ #define GLES2_H

#ifdef BUILD_GL #ifdef __APPLE__ -#include <OpenGL/gl.h> +#include <OpenGL/gl3.h> #else #include <GL/gl.h> #endif

@@ -18,6 +18,37 @@ #endif

#include "platform/video-backend.h" +union GBAGLES2UniformValue { + GLfloat f; + GLint i; + GLuint ui; + GLfloat fvec2[2]; + GLfloat fvec3[3]; + GLfloat fvec4[4]; + GLint ivec2[2]; + GLint ivec3[3]; + GLint ivec4[4]; + GLuint uivec2[2]; + GLuint uivec3[3]; + GLuint uivec4[4]; + GLfloat fmat2x2[4]; + GLfloat fmat2x3[6]; + GLfloat fmat2x4[8]; + GLfloat fmat3x2[6]; + GLfloat fmat3x3[9]; + GLfloat fmat3x4[12]; + GLfloat fmat4x2[8]; + GLfloat fmat4x3[12]; + GLfloat fmat4x4[16]; +}; + +struct GBAGLES2Uniform { + const char* name; + GLenum type; + union GBAGLES2UniformValue value; + GLuint location; +}; + struct GBAGLES2Shader { unsigned width; unsigned height;

@@ -26,38 +57,34 @@ bool blend;

GLuint tex; GLuint fbo; GLuint fragmentShader; + GLuint vertexShader; GLuint program; GLuint texLocation; GLuint positionLocation; + + struct GBAGLES2Uniform* uniforms; + size_t nUniforms; }; struct GBAGLES2Context { struct VideoBackend d; GLuint tex; - GLuint fragmentShader; - GLuint vertexShader; - GLuint nullVertexShader; - GLuint program; - GLuint bufferObject; GLuint texLocation; GLuint positionLocation; - GLuint gammaLocation; - GLuint biasLocation; - GLuint scaleLocation; - GLfloat gamma; - GLfloat bias[3]; - GLfloat scale[3]; + struct GBAGLES2Shader initialShader; + struct GBAGLES2Shader finalShader; - struct GBAGLES2Shader* shader; + struct GBAGLES2Shader* shaders; + size_t nShaders; }; void GBAGLES2ContextCreate(struct GBAGLES2Context*); -void GBAGLES2ShaderInit(struct GBAGLES2Shader*, const char*, int width, int height); +void GBAGLES2ShaderInit(struct GBAGLES2Shader*, const char* vs, const char* fs, int width, int height, struct GBAGLES2Uniform* uniforms, size_t nUniforms); void GBAGLES2ShaderDeinit(struct GBAGLES2Shader*); -void GBAGLES2ShaderAttach(struct GBAGLES2Context*, struct GBAGLES2Shader*); +void GBAGLES2ShaderAttach(struct GBAGLES2Context*, struct GBAGLES2Shader*, size_t nShaders); void GBAGLES2ShaderDetach(struct GBAGLES2Context*); #endif