From e3616ed4b9525e97dedbc2e80105765ff1278215 Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Fri, 17 Jul 2026 21:05:47 +1000 Subject: [PATCH] [client] egl: add cached uniforms and multipass effects Replace the per-frame uniform list with stable, name-cached handles that retain their values across shader recompilation. Track dirty uniforms so only changed values are uploaded, reducing redundant GL calls and buffer management. Add a reusable effect-pass pipeline with per-pass shaders, samplers, formats, resolutions, and intermediate render targets. This makes external effects easier to integrate and supports arbitrary multipass chains. Migrate the desktop, cursor, damage, 24-bit, downscale, CAS, and FSR rendering paths to the new APIs. Also ensure framebuffer objects are released correctly. --- client/renderers/EGL/CMakeLists.txt | 2 +- client/renderers/EGL/cursor.c | 34 +- client/renderers/EGL/damage.c | 4 +- client/renderers/EGL/desktop.c | 93 +--- client/renderers/EGL/effect.c | 231 +++++++++ client/renderers/EGL/effect.h | 53 ++ client/renderers/EGL/filter.c | 30 -- client/renderers/EGL/filter.h | 2 - client/renderers/EGL/filter_24bit.c | 60 +-- client/renderers/EGL/filter_downscale.c | 79 ++- client/renderers/EGL/filter_ffx_cas.c | 65 +-- client/renderers/EGL/filter_ffx_fsr1.c | 118 ++--- client/renderers/EGL/framebuffer.c | 4 + client/renderers/EGL/shader.c | 659 +++++++++++++----------- client/renderers/EGL/shader.h | 66 ++- 15 files changed, 843 insertions(+), 657 deletions(-) create mode 100644 client/renderers/EGL/effect.c create mode 100644 client/renderers/EGL/effect.h delete mode 100644 client/renderers/EGL/filter.c diff --git a/client/renderers/EGL/CMakeLists.txt b/client/renderers/EGL/CMakeLists.txt index e6dbafa5..1f7502cf 100644 --- a/client/renderers/EGL/CMakeLists.txt +++ b/client/renderers/EGL/CMakeLists.txt @@ -85,9 +85,9 @@ add_library(renderer_EGL STATIC cursor.c damage.c framebuffer.c + effect.c postprocess.c ffx.c - filter.c filter_24bit.c filter_ffx_cas.c filter_ffx_fsr1.c diff --git a/client/renderers/EGL/cursor.c b/client/renderers/EGL/cursor.c index 079fad57..ee94c00a 100644 --- a/client/renderers/EGL/cursor.c +++ b/client/renderers/EGL/cursor.c @@ -41,12 +41,12 @@ struct CursorTex { struct EGL_Texture * texture; struct EGL_Shader * shader; - GLuint uMousePos; - GLuint uScale; - GLuint uRotate; - GLuint uCBMode; - GLint uMapSDRtoPQ; - GLint uSDRWhiteLevel; + EGL_Uniform * uMousePos; + EGL_Uniform * uScale; + EGL_Uniform * uRotate; + EGL_Uniform * uCBMode; + EGL_Uniform * uMapSDRtoPQ; + EGL_Uniform * uSDRWhiteLevel; }; struct CursorPos @@ -124,12 +124,12 @@ static inline void setCursorTexUniforms(EGL_Cursor * cursor, struct CursorTex * t, bool mono, float x, float y, float w, float h, float scale) { - glUniform4f(t->uMousePos , x, y, w, mono ? h / 2 : h); - glUniform1f(t->uScale , scale); - glUniform1i(t->uRotate , cursor->rotate); - glUniform1i(t->uCBMode , cursor->cbMode); - glUniform1i(t->uMapSDRtoPQ , !mono && atomic_load(&cursor->mapSDRtoPQ)); - glUniform1f(t->uSDRWhiteLevel, atomic_load(&cursor->sdrWhiteLevel)); + egl_uniform4f(t->uMousePos , x, y, w, mono ? h / 2 : h); + egl_uniform1f(t->uScale , scale); + egl_uniform1i(t->uRotate , cursor->rotate); + egl_uniform1i(t->uCBMode , cursor->cbMode); + egl_uniform1i(t->uMapSDRtoPQ , !mono && atomic_load(&cursor->mapSDRtoPQ)); + egl_uniform1f(t->uSDRWhiteLevel, atomic_load(&cursor->sdrWhiteLevel)); } static void cursorTexFree(struct CursorTex * t) @@ -391,16 +391,16 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor, { case LG_CURSOR_MONOCHROME: { - egl_shaderUse(cursor->norm.shader); setCursorTexUniforms(cursor, &cursor->norm, true, pos.x, pos.y, size.w, size.h, scale); + egl_shaderUse(cursor->norm.shader); glBlendFunc(GL_ZERO, GL_SRC_COLOR); egl_modelSetTexture(cursor->model, cursor->norm.texture); egl_modelRender(cursor->model); - egl_shaderUse(cursor->mono.shader); setCursorTexUniforms(cursor, &cursor->mono, true, pos.x, pos.y, size.w, size.h, scale); + egl_shaderUse(cursor->mono.shader); glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO); egl_modelSetTexture(cursor->model, cursor->mono.texture); egl_modelRender(cursor->model); @@ -409,16 +409,16 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor, case LG_CURSOR_MASKED_COLOR: { - egl_shaderUse(cursor->norm.shader); setCursorTexUniforms(cursor, &cursor->norm, false, pos.x, pos.y, size.w, size.h, scale); + egl_shaderUse(cursor->norm.shader); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); egl_modelSetTexture(cursor->model, cursor->norm.texture); egl_modelRender(cursor->model); - egl_shaderUse(cursor->mono.shader); setCursorTexUniforms(cursor, &cursor->mono, false, pos.x, pos.y, size.w, size.h, scale); + egl_shaderUse(cursor->mono.shader); glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO); egl_modelSetTexture(cursor->model, cursor->mono.texture); egl_modelRender(cursor->model); @@ -427,9 +427,9 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor, case LG_CURSOR_COLOR: { - egl_shaderUse(cursor->norm.shader); setCursorTexUniforms(cursor, &cursor->norm, false, pos.x, pos.y, size.w, size.h, scale); + egl_shaderUse(cursor->norm.shader); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); egl_modelSetTexture(cursor->model, cursor->norm.texture); egl_modelRender(cursor->model); diff --git a/client/renderers/EGL/damage.c b/client/renderers/EGL/damage.c index 0e3eb56c..31c4d404 100644 --- a/client/renderers/EGL/damage.c +++ b/client/renderers/EGL/damage.c @@ -49,7 +49,7 @@ struct EGL_Damage LG_RendererRotate rotate; // uniforms - GLint uTransform; + EGL_Uniform * uTransform; }; void egl_damageConfigUI(EGL_Damage * damage) @@ -143,8 +143,8 @@ bool egl_damageRender(EGL_Damage * damage, LG_RendererRotate rotate, const struc glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + egl_uniformMatrix3x2fv(damage->uTransform, 1, GL_FALSE, damage->transform); egl_shaderUse(damage->shader); - glUniformMatrix3x2fv(damage->uTransform, 1, GL_FALSE, damage->transform); if (data && data->count != 0) egl_desktopRectsUpdate(damage->mesh, (const struct DamageRects *) data, diff --git a/client/renderers/EGL/desktop.c b/client/renderers/EGL/desktop.c index be1b08a5..14380790 100644 --- a/client/renderers/EGL/desktop.c +++ b/client/renderers/EGL/desktop.c @@ -22,7 +22,6 @@ #include "common/debug.h" #include "common/option.h" #include "common/locking.h" -#include "common/array.h" #include "app.h" #include "texture.h" @@ -44,16 +43,15 @@ struct DesktopShader { EGL_Shader * shader; - GLint uTransform; - GLint uDesktopSize; - GLint uSamplerType; - GLint uScaleAlgo; - GLint uNVGain; - GLint uCBMode; - GLint uIsHDR; - GLint uMapHDRtoSDR; - GLint uMapHDRGain; - GLint uMapHDRPQ; + EGL_Uniform * uTransform; + EGL_Uniform * uDesktopSize; + EGL_Uniform * uScaleAlgo; + EGL_Uniform * uNVGain; + EGL_Uniform * uCBMode; + EGL_Uniform * uIsHDR; + EGL_Uniform * uMapHDRtoSDR; + EGL_Uniform * uMapHDRGain; + EGL_Uniform * uMapHDRPQ; }; struct EGL_Desktop @@ -64,7 +62,7 @@ struct EGL_Desktop EGL_Texture * texture; struct DesktopShader dmaShader, shader; EGL_DesktopRects * mesh; - CountedBuffer * matrix; + GLfloat matrix[6]; // internals int width, height; @@ -160,13 +158,6 @@ bool egl_desktopInit(EGL * egl, EGL_Desktop ** desktop_, EGLDisplay * display, return false; } - desktop->matrix = countedBufferNew(6 * sizeof(GLfloat)); - if (!desktop->matrix) - { - DEBUG_ERROR("Failed to allocate the desktop matrix buffer"); - return false; - } - if (!egl_initDesktopShader( &desktop->shader, b_shader_desktop_vert , b_shader_desktop_vert_size, @@ -248,8 +239,6 @@ void egl_desktopFree(EGL_Desktop ** desktop) egl_shaderFree (&(*desktop)->shader .shader); egl_shaderFree (&(*desktop)->dmaShader.shader); egl_desktopRectsFree(&(*desktop)->mesh ); - countedBufferRelease(&(*desktop)->matrix ); - egl_postProcessFree(&(*desktop)->pp); free(*desktop); @@ -467,7 +456,7 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth, int scaleAlgo = EGL_SCALE_NEAREST; - egl_desktopRectsMatrix((float *)desktop->matrix->data, + egl_desktopRectsMatrix(desktop->matrix, width, height, x, y, scaleX, scaleY, rotate); egl_desktopRectsUpdate(desktop->mesh, rects, width, height); @@ -523,57 +512,15 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth, const float mapHDRGain = (float)desktop->maxCLL / desktop->peakLuminance; - EGL_Uniform uniforms[] = - { - { - .type = EGL_UNIFORM_TYPE_1I, - .location = shader->uScaleAlgo, - .i = { scaleAlgo }, - }, - { - .type = EGL_UNIFORM_TYPE_2F, - .location = shader->uDesktopSize, - .f = { width, height }, - }, - { - .type = EGL_UNIFORM_TYPE_M3x2FV, - .location = shader->uTransform, - .m.transpose = GL_FALSE, - .m.v = desktop->matrix - }, - { - .type = EGL_UNIFORM_TYPE_1F, - .location = shader->uNVGain, - .f = { (float)desktop->nvGain } - }, - { - .type = EGL_UNIFORM_TYPE_1I, - .location = shader->uCBMode, - .i = { desktop->cbMode } - }, - { - .type = EGL_UNIFORM_TYPE_1I, - .location = shader->uIsHDR, - .i = { desktop->hdr } - }, - { - .type = EGL_UNIFORM_TYPE_1I, - .location = shader->uMapHDRtoSDR, - .i = { desktop->mapHDRtoSDR && !desktop->nativeHDR } - }, - { - .type = EGL_UNIFORM_TYPE_1F, - .location = shader->uMapHDRGain, - .f = { mapHDRGain } - }, - { - .type = EGL_UNIFORM_TYPE_1I, - .location = shader->uMapHDRPQ, - .i = { desktop->hdrPQ } - } - }; - - egl_shaderSetUniforms(shader->shader, uniforms, ARRAY_LENGTH(uniforms)); + egl_uniform1i (shader->uScaleAlgo , scaleAlgo); + egl_uniform2f (shader->uDesktopSize, width, height); + egl_uniformMatrix3x2fv(shader->uTransform , 1, GL_FALSE, desktop->matrix); + egl_uniform1f (shader->uNVGain , desktop->nvGain); + egl_uniform1i (shader->uCBMode , desktop->cbMode); + egl_uniform1i (shader->uIsHDR , desktop->hdr); + egl_uniform1i (shader->uMapHDRtoSDR, desktop->mapHDRtoSDR && !desktop->nativeHDR); + egl_uniform1f (shader->uMapHDRGain , mapHDRGain); + egl_uniform1i (shader->uMapHDRPQ , desktop->hdrPQ); egl_shaderUse(shader->shader); egl_desktopRectsRender(desktop->mesh); glBindTexture(GL_TEXTURE_2D, 0); diff --git a/client/renderers/EGL/effect.c b/client/renderers/EGL/effect.c new file mode 100644 index 00000000..0c54e3f2 --- /dev/null +++ b/client/renderers/EGL/effect.c @@ -0,0 +1,231 @@ +/** + * Looking Glass + * Copyright © 2017-2026 The Looking Glass Authors + * https://looking-glass.io + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "effect.h" + +#include "filter.h" +#include "framebuffer.h" +#include "texture.h" + +#include "common/debug.h" + +#include + +struct EGL_EffectPass +{ + EGL_Shader * shader; + EGL_Framebuffer * framebuffer; + GLuint sampler; + + EGL_Uniform * uTransform; + EGL_Uniform * uDesktopSize; + + GLenum minFilter; + GLenum magFilter; + GLenum wrapS; + GLenum wrapT; + + bool enabled; + bool configured; + enum EGL_PixelFormat pixFmt; + unsigned int width; + unsigned int height; + + struct EGL_EffectPass * next; +}; + +struct EGL_Effect +{ + EGL_EffectPass * passes; + EGL_EffectPass * passesTail; +}; + +bool egl_effectInit(EGL_Effect ** effect) +{ + *effect = calloc(1, sizeof(**effect)); + if (!*effect) + { + DEBUG_ERROR("Failed to allocate EGL effect"); + return false; + } + + return true; +} + +void egl_effectFree(EGL_Effect ** effect) +{ + if (!*effect) + return; + + EGL_EffectPass * pass = (*effect)->passes; + while(pass) + { + EGL_EffectPass * next = pass->next; + glDeleteSamplers(1, &pass->sampler); + egl_framebufferFree(&pass->framebuffer); + free(pass); + pass = next; + } + + free(*effect); + *effect = NULL; +} + +void egl_effectPassSetShader(EGL_EffectPass * pass, EGL_Shader * shader) +{ + if (pass->shader == shader) + return; + + pass->shader = shader; + if (!shader) + { + pass->uTransform = NULL; + pass->uDesktopSize = NULL; + return; + } + + pass->uTransform = egl_shaderGetUniform(shader, "transform"); + pass->uDesktopSize = egl_shaderGetUniform(shader, "desktopSize"); + egl_shaderAssocTextures(shader, 1); +} + +bool egl_effectAddPass(EGL_Effect * effect, EGL_Shader * shader, + EGL_EffectPass ** result) +{ + EGL_EffectPass * pass = calloc(1, sizeof(*pass)); + if (!pass) + { + DEBUG_ERROR("Failed to allocate EGL effect pass"); + return false; + } + + if (!egl_framebufferInit(&pass->framebuffer)) + { + free(pass); + return false; + } + + glGenSamplers(1, &pass->sampler); + glSamplerParameteri(pass->sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glSamplerParameteri(pass->sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glSamplerParameteri(pass->sampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glSamplerParameteri(pass->sampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + pass->minFilter = GL_LINEAR; + pass->magFilter = GL_LINEAR; + pass->wrapS = GL_CLAMP_TO_EDGE; + pass->wrapT = GL_CLAMP_TO_EDGE; + + pass->enabled = true; + egl_effectPassSetShader(pass, shader); + + if (effect->passesTail) + effect->passesTail->next = pass; + else + effect->passes = pass; + effect->passesTail = pass; + + if (result) + *result = pass; + return true; +} + +void egl_effectPassSetEnabled(EGL_EffectPass * pass, bool enabled) +{ + pass->enabled = enabled; +} + +void egl_effectPassSetFilter(EGL_EffectPass * pass, GLenum minFilter, + GLenum magFilter) +{ + if (pass->minFilter != minFilter) + { + glSamplerParameteri(pass->sampler, GL_TEXTURE_MIN_FILTER, minFilter); + pass->minFilter = minFilter; + } + + if (pass->magFilter != magFilter) + { + glSamplerParameteri(pass->sampler, GL_TEXTURE_MAG_FILTER, magFilter); + pass->magFilter = magFilter; + } +} + +void egl_effectPassSetWrap(EGL_EffectPass * pass, GLenum wrapS, GLenum wrapT) +{ + if (pass->wrapS != wrapS) + { + glSamplerParameteri(pass->sampler, GL_TEXTURE_WRAP_S, wrapS); + pass->wrapS = wrapS; + } + + if (pass->wrapT != wrapT) + { + glSamplerParameteri(pass->sampler, GL_TEXTURE_WRAP_T, wrapT); + pass->wrapT = wrapT; + } +} + +bool egl_effectPassSetup(EGL_EffectPass * pass, enum EGL_PixelFormat pixFmt, + unsigned int width, unsigned int height) +{ + if (pass->configured && pass->pixFmt == pixFmt && + pass->width == width && pass->height == height) + return true; + + if (!egl_framebufferSetup(pass->framebuffer, pixFmt, width, height)) + return false; + + pass->pixFmt = pixFmt; + pass->width = width; + pass->height = height; + pass->configured = true; + return true; +} + +EGL_Texture * egl_effectRun(EGL_Effect * effect, EGL_FilterRects * rects, + EGL_Texture * texture) +{ + for(EGL_EffectPass * pass = effect->passes; pass; pass = pass->next) + { + if (!pass->enabled) + continue; + + if (!pass->configured || !pass->shader) + { + DEBUG_ERROR("Attempted to run an unconfigured EGL effect pass"); + return NULL; + } + + egl_framebufferBind(pass->framebuffer); + + glActiveTexture(GL_TEXTURE0); + egl_textureBind(texture); + glBindSampler(0, pass->sampler); + + egl_uniformMatrix3x2fv(pass->uTransform, 1, GL_FALSE, rects->matrix); + egl_uniform2f(pass->uDesktopSize, rects->width, rects->height); + egl_shaderUse(pass->shader); + egl_desktopRectsRender(rects->rects); + + texture = egl_framebufferGetTexture(pass->framebuffer); + } + + return texture; +} diff --git a/client/renderers/EGL/effect.h b/client/renderers/EGL/effect.h new file mode 100644 index 00000000..8084cf10 --- /dev/null +++ b/client/renderers/EGL/effect.h @@ -0,0 +1,53 @@ +/** + * Looking Glass + * Copyright © 2017-2026 The Looking Glass Authors + * https://looking-glass.io + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#pragma once + +#include "egltypes.h" +#include "shader.h" + +typedef struct EGL_Effect EGL_Effect; +typedef struct EGL_EffectPass EGL_EffectPass; +typedef struct EGL_Texture EGL_Texture; +struct EGL_FilterRects; + +/* + * Effects execute their enabled passes in insertion order. Each pass owns an + * intermediate render target and sampler, while shaders remain caller-owned + * so they can be shared or selected at run time. + */ +bool egl_effectInit(EGL_Effect ** effect); +void egl_effectFree(EGL_Effect ** effect); + +bool egl_effectAddPass(EGL_Effect * effect, EGL_Shader * shader, + EGL_EffectPass ** pass); + +void egl_effectPassSetShader(EGL_EffectPass * pass, EGL_Shader * shader); +void egl_effectPassSetEnabled(EGL_EffectPass * pass, bool enabled); +void egl_effectPassSetFilter(EGL_EffectPass * pass, GLenum minFilter, + GLenum magFilter); +void egl_effectPassSetWrap(EGL_EffectPass * pass, GLenum wrapS, + GLenum wrapT); + +bool egl_effectPassSetup(EGL_EffectPass * pass, enum EGL_PixelFormat pixFmt, + unsigned int width, unsigned int height); + +EGL_Texture * egl_effectRun(EGL_Effect * effect, + struct EGL_FilterRects * rects, EGL_Texture * texture); diff --git a/client/renderers/EGL/filter.c b/client/renderers/EGL/filter.c deleted file mode 100644 index bcd04176..00000000 --- a/client/renderers/EGL/filter.c +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Looking Glass - * Copyright © 2017-2026 The Looking Glass Authors - * https://looking-glass.io - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., 59 - * Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "filter.h" - -void egl_filterRectsRender(EGL_Shader * shader, EGL_FilterRects * rects) -{ - glUniformMatrix3x2fv(egl_shaderGetUniform(shader, "transform"), - 1, GL_FALSE, rects->matrix); - glUniform2f(egl_shaderGetUniform(shader, "desktopSize"), - rects->width, rects->height); - egl_desktopRectsRender(rects->rects); -} diff --git a/client/renderers/EGL/filter.h b/client/renderers/EGL/filter.h index 64636bf2..a505c137 100644 --- a/client/renderers/EGL/filter.h +++ b/client/renderers/EGL/filter.h @@ -183,5 +183,3 @@ static inline void egl_filterRelease(EGL_Filter * filter) if (filter->ops.release) filter->ops.release(filter); } - -void egl_filterRectsRender(EGL_Shader * shader, EGL_FilterRects * rects); diff --git a/client/renderers/EGL/filter_24bit.c b/client/renderers/EGL/filter_24bit.c index eaa807aa..714d99c4 100644 --- a/client/renderers/EGL/filter_24bit.c +++ b/client/renderers/EGL/filter_24bit.c @@ -19,11 +19,10 @@ */ #include "filter.h" -#include "framebuffer.h" +#include "effect.h" #include -#include "common/array.h" #include "common/debug.h" #include "common/option.h" #include "cimgui.h" @@ -43,11 +42,11 @@ typedef struct EGL_Filter24bit unsigned int desktopWidth, desktopHeight; bool prepared; - EGL_Uniform uOutputSize; + EGL_Uniform * uOutputSize; - EGL_Shader * shader; - EGL_Framebuffer * fb; - GLuint sampler[2]; + EGL_Shader * shader; + EGL_Effect * effect; + EGL_EffectPass * pass; } EGL_Filter24bit; @@ -68,25 +67,27 @@ static bool egl_filter24bitInit(EGL_Filter ** filter) goto error_this; } - if (!egl_framebufferInit(&this->fb)) + if (!egl_effectInit(&this->effect)) { - DEBUG_ERROR("Failed to initialize the framebuffer"); + DEBUG_ERROR("Failed to initialize the effect"); goto error_shader; } - glGenSamplers(ARRAY_LENGTH(this->sampler), this->sampler); - glSamplerParameteri(this->sampler[0], GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glSamplerParameteri(this->sampler[0], GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glSamplerParameteri(this->sampler[0], GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE); - glSamplerParameteri(this->sampler[0], GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE); - glSamplerParameteri(this->sampler[1], GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glSamplerParameteri(this->sampler[1], GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glSamplerParameteri(this->sampler[1], GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE); - glSamplerParameteri(this->sampler[1], GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE); + if (!egl_effectAddPass(this->effect, this->shader, &this->pass)) + { + DEBUG_ERROR("Failed to initialize the effect pass"); + goto error_effect; + } + + egl_effectPassSetFilter(this->pass, GL_NEAREST, GL_NEAREST); + this->uOutputSize = egl_shaderGetUniform(this->shader, "outputSize"); *filter = &this->base; return true; +error_effect: + egl_effectFree(&this->effect); + error_shader: egl_shaderFree(&this->shader); @@ -99,9 +100,8 @@ static void egl_filter24bitFree(EGL_Filter * filter) { EGL_Filter24bit * this = UPCAST(EGL_Filter24bit, filter); + egl_effectFree(&this->effect); egl_shaderFree(&this->shader); - egl_framebufferFree(&this->fb); - glDeleteSamplers(ARRAY_LENGTH(this->sampler), this->sampler); free(this); } @@ -133,10 +133,6 @@ static bool egl_filter24bitSetup(EGL_Filter * filter, return false; } - this->uOutputSize.type = EGL_UNIFORM_TYPE_2F; - this->uOutputSize.location = - egl_shaderGetUniform(this->shader, "outputSize"); - this->useDMA = useDMA; this->prepared = false; } @@ -148,7 +144,7 @@ static bool egl_filter24bitSetup(EGL_Filter * filter, this->desktopHeight == desktopHeight) return true; - if (!egl_framebufferSetup(this->fb, EGL_PF_BGRA, desktopWidth, desktopHeight)) + if (!egl_effectPassSetup(this->pass, EGL_PF_BGRA, desktopWidth, desktopHeight)) return false; this->format = pixFmt; @@ -177,9 +173,7 @@ static bool egl_filter24bitPrepare(EGL_Filter * filter) if (this->prepared) return true; - this->uOutputSize.f[0] = this->desktopWidth; - this->uOutputSize.f[1] = this->desktopHeight; - egl_shaderSetUniforms(this->shader, &this->uOutputSize, 1); + egl_uniform2f(this->uOutputSize, this->desktopWidth, this->desktopHeight); this->prepared = true; return true; @@ -190,17 +184,7 @@ static EGL_Texture * egl_filter24bitRun(EGL_Filter * filter, { EGL_Filter24bit * this = UPCAST(EGL_Filter24bit, filter); - egl_framebufferBind(this->fb); - - glActiveTexture(GL_TEXTURE0); - egl_textureBind(texture); - - glBindSampler(0, this->sampler[0]); - - egl_shaderUse(this->shader); - egl_filterRectsRender(this->shader, rects); - - return egl_framebufferGetTexture(this->fb); + return egl_effectRun(this->effect, rects, texture); } EGL_FilterOps egl_filter24bitOps = diff --git a/client/renderers/EGL/filter_downscale.c b/client/renderers/EGL/filter_downscale.c index d2807c53..89e7ec93 100644 --- a/client/renderers/EGL/filter_downscale.c +++ b/client/renderers/EGL/filter_downscale.c @@ -19,11 +19,10 @@ */ #include "filter.h" -#include "framebuffer.h" +#include "effect.h" #include -#include "common/array.h" #include "common/debug.h" #include "common/option.h" #include "cimgui.h" @@ -55,9 +54,11 @@ typedef struct EGL_FilterDownscale bool enable; EGL_Shader * nearest; - EGL_Uniform uNearest; + EGL_Uniform * uNearest; EGL_Shader * linear; EGL_Shader * lanczos2; + EGL_Effect * effect; + EGL_EffectPass * pass; DownscaleFilter filter; int useDMA; @@ -67,8 +68,6 @@ typedef struct EGL_FilterDownscale float vOffset, hOffset; bool prepared; - EGL_Framebuffer * fb; - GLuint sampler[2]; } EGL_FilterDownscale; @@ -163,48 +162,48 @@ static bool egl_filterDownscaleInit(EGL_Filter ** filter) if (!egl_shaderInit(&this->nearest)) { DEBUG_ERROR("Failed to initialize the shader"); - goto error_this; + goto error_shader; } if (!egl_shaderInit(&this->linear)) { DEBUG_ERROR("Failed to initialize the shader"); - goto error_this; + goto error_shader; } if (!egl_shaderInit(&this->lanczos2)) { DEBUG_ERROR("Failed to initialize the shader"); - goto error_this; - } - - if (!egl_framebufferInit(&this->fb)) - { - DEBUG_ERROR("Failed to initialize the framebuffer"); goto error_shader; } - glGenSamplers(ARRAY_LENGTH(this->sampler), this->sampler); - glSamplerParameteri(this->sampler[0], GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glSamplerParameteri(this->sampler[0], GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glSamplerParameteri(this->sampler[0], GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE); - glSamplerParameteri(this->sampler[0], GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE); - glSamplerParameteri(this->sampler[1], GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glSamplerParameteri(this->sampler[1], GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glSamplerParameteri(this->sampler[1], GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE); - glSamplerParameteri(this->sampler[1], GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE); + if (!egl_effectInit(&this->effect)) + { + DEBUG_ERROR("Failed to initialize the effect"); + goto error_shader; + } + + if (!egl_effectAddPass(this->effect, this->nearest, &this->pass)) + { + DEBUG_ERROR("Failed to initialize the effect pass"); + goto error_effect; + } + + this->uNearest = egl_shaderGetUniform(this->nearest, "uConfig"); egl_filterDownscaleLoadState(&this->base); *filter = &this->base; return true; +error_effect: + egl_effectFree(&this->effect); + error_shader: egl_shaderFree(&this->nearest); egl_shaderFree(&this->linear); egl_shaderFree(&this->lanczos2); -error_this: free(this); return false; } @@ -213,11 +212,10 @@ static void egl_filterDownscaleFree(EGL_Filter * filter) { EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter); + egl_effectFree(&this->effect); egl_shaderFree(&this->nearest); egl_shaderFree(&this->linear); egl_shaderFree(&this->lanczos2); - egl_framebufferFree(&this->fb); - glDeleteSamplers(ARRAY_LENGTH(this->sampler), this->sampler); free(this); } @@ -345,10 +343,6 @@ static bool egl_filterDownscaleSetup(EGL_Filter * filter, return false; } - this->uNearest.type = EGL_UNIFORM_TYPE_3F; - this->uNearest.location = - egl_shaderGetUniform(this->nearest, "uConfig"); - this->useDMA = useDMA; } @@ -358,7 +352,7 @@ static bool egl_filterDownscaleSetup(EGL_Filter * filter, this->height == height) return this->pixelSize > 1.0f; - if (!egl_framebufferSetup(this->fb, pixFmt, width, height)) + if (!egl_effectPassSetup(this->pass, pixFmt, width, height)) return false; this->pixFmt = pixFmt; @@ -388,10 +382,8 @@ static bool egl_filterDownscalePrepare(EGL_Filter * filter) switch (this->filter) { case DOWNSCALE_NEAREST: - this->uNearest.f[0] = this->pixelSize; - this->uNearest.f[1] = this->vOffset; - this->uNearest.f[2] = this->hOffset; - egl_shaderSetUniforms(this->nearest, &this->uNearest, 1); + egl_uniform3f(this->uNearest, this->pixelSize, + this->vOffset, this->hOffset); break; default: @@ -407,38 +399,33 @@ static EGL_Texture * egl_filterDownscaleRun(EGL_Filter * filter, { EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter); - egl_framebufferBind(this->fb); - - glActiveTexture(GL_TEXTURE0); - egl_textureBind(texture); - EGL_Shader * shader; + GLenum sampling; switch (this->filter) { case DOWNSCALE_NEAREST: - glBindSampler(0, this->sampler[0]); shader = this->nearest; + sampling = GL_NEAREST; break; case DOWNSCALE_LINEAR: - glBindSampler(0, this->sampler[1]); shader = this->linear; + sampling = GL_LINEAR; break; case DOWNSCALE_LANCZOS2: - glBindSampler(0, this->sampler[0]); shader = this->lanczos2; + sampling = GL_NEAREST; break; default: DEBUG_UNREACHABLE(); } - egl_shaderUse(shader); - egl_filterRectsRender(shader, rects); - - return egl_framebufferGetTexture(this->fb); + egl_effectPassSetShader(this->pass, shader); + egl_effectPassSetFilter(this->pass, sampling, sampling); + return egl_effectRun(this->effect, rects, texture); } EGL_FilterOps egl_filterDownscaleOps = diff --git a/client/renderers/EGL/filter_ffx_cas.c b/client/renderers/EGL/filter_ffx_cas.c index f1de1d6e..08639b87 100644 --- a/client/renderers/EGL/filter_ffx_cas.c +++ b/client/renderers/EGL/filter_ffx_cas.c @@ -19,9 +19,8 @@ */ #include "filter.h" -#include "framebuffer.h" +#include "effect.h" -#include "common/countedbuffer.h" #include "common/debug.h" #include "common/option.h" #include "cimgui.h" @@ -34,18 +33,18 @@ typedef struct EGL_FilterFFXCAS { EGL_Filter base; - EGL_Shader * shader; - bool enable; + EGL_Shader * shader; + EGL_Uniform * uConsts; + EGL_Effect * effect; + EGL_EffectPass * pass; + bool enable; int useDMA; enum EGL_PixelFormat pixFmt; unsigned int width, height; float sharpness; - CountedBuffer * consts; + GLuint consts[8]; bool prepared; - - EGL_Framebuffer * fb; - GLuint sampler; } EGL_FilterFFXCAS; @@ -77,9 +76,10 @@ static void egl_filterFFXCASEarlyInit(void) static void casUpdateConsts(EGL_FilterFFXCAS * this) { - ffxCasConst((uint32_t *) this->consts->data, this->sharpness, + ffxCasConst((uint32_t *)this->consts, this->sharpness, this->width, this->height, this->width, this->height); + egl_uniform4uiv(this->uConsts, 2, this->consts); } static void egl_filterFFXCASSaveState(EGL_Filter * filter) @@ -115,32 +115,26 @@ static bool egl_filterFFXCASInit(EGL_Filter ** filter) goto error_this; } - this->consts = countedBufferNew(8 * sizeof(GLuint)); - if (!this->consts) + if (!egl_effectInit(&this->effect)) { - DEBUG_ERROR("Failed to allocate consts buffer"); + DEBUG_ERROR("Failed to initialize the effect"); goto error_shader; } - egl_filterFFXCASLoadState(&this->base); - - if (!egl_framebufferInit(&this->fb)) + if (!egl_effectAddPass(this->effect, this->shader, &this->pass)) { - DEBUG_ERROR("Failed to initialize the framebuffer"); - goto error_consts; + DEBUG_ERROR("Failed to initialize the effect pass"); + goto error_effect; } - glGenSamplers(1, &this->sampler); - glSamplerParameteri(this->sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glSamplerParameteri(this->sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glSamplerParameteri(this->sampler, GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE); - glSamplerParameteri(this->sampler, GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE); + this->uConsts = egl_shaderGetUniform(this->shader, "uConsts"); + egl_filterFFXCASLoadState(&this->base); *filter = &this->base; return true; -error_consts: - countedBufferRelease(&this->consts); +error_effect: + egl_effectFree(&this->effect); error_shader: egl_shaderFree(&this->shader); @@ -154,10 +148,8 @@ static void egl_filterFFXCASFree(EGL_Filter * filter) { EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter); + egl_effectFree(&this->effect); egl_shaderFree(&this->shader); - countedBufferRelease(&this->consts); - egl_framebufferFree(&this->fb); - glDeleteSamplers(1, &this->sampler); free(this); } @@ -229,19 +221,13 @@ static bool egl_filterFFXCASSetup(EGL_Filter * filter, return false; } - egl_shaderSetUniforms(this->shader, &(EGL_Uniform) { - .type = EGL_UNIFORM_TYPE_4UIV, - .location = egl_shaderGetUniform(this->shader, "uConsts"), - .v = this->consts, - }, 1); - this->useDMA = useDMA; } if (pixFmt == this->pixFmt && this->width == width && this->height == height) return true; - if (!egl_framebufferSetup(this->fb, pixFmt, width, height)) + if (!egl_effectPassSetup(this->pass, pixFmt, width, height)) return false; this->pixFmt = pixFmt; @@ -279,16 +265,7 @@ static EGL_Texture * egl_filterFFXCASRun(EGL_Filter * filter, { EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter); - egl_framebufferBind(this->fb); - - glActiveTexture(GL_TEXTURE0); - egl_textureBind(texture); - glBindSampler(0, this->sampler); - - egl_shaderUse(this->shader); - egl_filterRectsRender(this->shader, rects); - - return egl_framebufferGetTexture(this->fb); + return egl_effectRun(this->effect, rects, texture); } EGL_FilterOps egl_filterFFXCASOps = diff --git a/client/renderers/EGL/filter_ffx_fsr1.c b/client/renderers/EGL/filter_ffx_fsr1.c index 954819bc..dab3165e 100644 --- a/client/renderers/EGL/filter_ffx_fsr1.c +++ b/client/renderers/EGL/filter_ffx_fsr1.c @@ -19,10 +19,8 @@ */ #include "filter.h" -#include "framebuffer.h" +#include "effect.h" -#include "common/array.h" -#include "common/countedbuffer.h" #include "common/debug.h" #include "common/option.h" #include "cimgui.h" @@ -36,11 +34,16 @@ typedef struct EGL_FilterFFXFSR1 { EGL_Filter base; - EGL_Shader * easu, * rcas; - bool enable, active; - float sharpness; - CountedBuffer * consts; - EGL_Uniform easuUniform[2], rcasUniform; + EGL_Shader * easu, * rcas; + EGL_Uniform * uEasuConsts; + EGL_Uniform * uEasuOutRes; + EGL_Uniform * uRcasConsts; + EGL_Effect * effect; + EGL_EffectPass * easuPass, * rcasPass; + bool enable, active; + float sharpness; + GLuint easuConsts[16]; + GLuint rcasConsts[4]; int useDMA; enum EGL_PixelFormat pixFmt; @@ -49,8 +52,6 @@ typedef struct EGL_FilterFFXFSR1 bool sizeChanged; bool prepared; - EGL_Framebuffer * easuFb, * rcasFb; - GLuint sampler; } EGL_FilterFFXFSR1; @@ -82,7 +83,8 @@ static void egl_filterFFXFSR1EarlyInit(void) static void rcasUpdateUniform(EGL_FilterFFXFSR1 * this) { - ffxFsrRcasConst(this->rcasUniform.ui, 2.0f - this->sharpness * 2.0f); + ffxFsrRcasConst(this->rcasConsts, 2.0f - this->sharpness * 2.0f); + egl_uniform4uiv(this->uRcasConsts, 1, this->rcasConsts); } static void egl_filterFFXFSR1SaveState(EGL_Filter * filter) @@ -134,45 +136,36 @@ static bool egl_filterFFXFSR1Init(EGL_Filter ** filter) goto error_rcas; } - this->consts = countedBufferNew(16 * sizeof(GLuint)); - if (!this->consts) + if (!egl_effectInit(&this->effect)) { - DEBUG_ERROR("Failed to allocate consts buffer"); + DEBUG_ERROR("Failed to initialize the effect"); goto error_rcas; } + if (!egl_effectAddPass(this->effect, this->easu, &this->easuPass)) + { + DEBUG_ERROR("Failed to initialize the Easu effect pass"); + goto error_effect; + } + + if (!egl_effectAddPass(this->effect, this->rcas, &this->rcasPass)) + { + DEBUG_ERROR("Failed to initialize the Rcas effect pass"); + goto error_effect; + } + + this->uEasuConsts = egl_shaderGetUniform(this->easu, "uConsts"); + this->uEasuOutRes = egl_shaderGetUniform(this->easu, "uOutRes"); + this->uRcasConsts = egl_shaderGetUniform(this->rcas, "uConsts"); + egl_filterFFXFSR1LoadState(&this->base); - - this->rcasUniform.type = EGL_UNIFORM_TYPE_4UI; - this->rcasUniform.location = egl_shaderGetUniform(this->rcas, "uConsts"); rcasUpdateUniform(this); - if (!egl_framebufferInit(&this->easuFb)) - { - DEBUG_ERROR("Failed to initialize the Easu framebuffer"); - goto error_consts; - } - - if (!egl_framebufferInit(&this->rcasFb)) - { - DEBUG_ERROR("Failed to initialize the Rcas framebuffer"); - goto error_easuFb; - } - - glGenSamplers(1, &this->sampler); - glSamplerParameteri(this->sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glSamplerParameteri(this->sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glSamplerParameteri(this->sampler, GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE); - glSamplerParameteri(this->sampler, GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE); - *filter = &this->base; return true; -error_easuFb: - egl_framebufferFree(&this->rcasFb); - -error_consts: - countedBufferRelease(&this->consts); +error_effect: + egl_effectFree(&this->effect); error_rcas: egl_shaderFree(&this->rcas); @@ -189,12 +182,9 @@ static void egl_filterFFXFSR1Free(EGL_Filter * filter) { EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter); + egl_effectFree(&this->effect); egl_shaderFree(&this->easu); egl_shaderFree(&this->rcas); - countedBufferRelease(&this->consts); - egl_framebufferFree(&this->easuFb); - egl_framebufferFree(&this->rcasFb); - glDeleteSamplers(1, &this->sampler); free(this); } @@ -343,14 +333,6 @@ static bool egl_filterFFXFSR1Setup(EGL_Filter * filter, return false; } - this->easuUniform[0].type = EGL_UNIFORM_TYPE_4UIV; - this->easuUniform[0].location = - egl_shaderGetUniform(this->easu, "uConsts"); - this->easuUniform[0].v = this->consts; - this->easuUniform[1].type = EGL_UNIFORM_TYPE_2F; - this->easuUniform[1].location = - egl_shaderGetUniform(this->easu, "uOutRes"); - this->useDMA = useDMA; } @@ -362,10 +344,10 @@ static bool egl_filterFFXFSR1Setup(EGL_Filter * filter, width == this->inWidth && height == this->inHeight) return true; - if (!egl_framebufferSetup(this->easuFb, pixFmt, this->width, this->height)) + if (!egl_effectPassSetup(this->easuPass, pixFmt, this->width, this->height)) return false; - if (!egl_framebufferSetup(this->rcasFb, pixFmt, this->width, this->height)) + if (!egl_effectPassSetup(this->rcasPass, pixFmt, this->width, this->height)) return false; this->inWidth = width; @@ -374,10 +356,10 @@ static bool egl_filterFFXFSR1Setup(EGL_Filter * filter, this->pixFmt = pixFmt; this->prepared = false; - this->easuUniform[1].f[0] = this->width; - this->easuUniform[1].f[1] = this->height; - ffxFsrEasuConst((uint32_t *)this->consts->data, this->inWidth, this->inHeight, + egl_uniform2f(this->uEasuOutRes, this->width, this->height); + ffxFsrEasuConst(this->easuConsts, this->inWidth, this->inHeight, this->inWidth, this->inHeight, this->width, this->height); + egl_uniform4uiv(this->uEasuConsts, 4, this->easuConsts); return true; } @@ -401,8 +383,6 @@ static bool egl_filterFFXFSR1Prepare(EGL_Filter * filter) if (this->prepared) return true; - egl_shaderSetUniforms(this->easu, this->easuUniform, ARRAY_LENGTH(this->easuUniform)); - egl_shaderSetUniforms(this->rcas, &this->rcasUniform, 1); this->prepared = true; return true; @@ -413,25 +393,7 @@ static EGL_Texture * egl_filterFFXFSR1Run(EGL_Filter * filter, { EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter); - // pass 1, Easu - egl_framebufferBind(this->easuFb); - glActiveTexture(GL_TEXTURE0); - egl_textureBind(texture); - glBindSampler(0, this->sampler); - egl_shaderUse(this->easu); - egl_filterRectsRender(this->easu, rects); - texture = egl_framebufferGetTexture(this->easuFb); - - // pass 2, Rcas - egl_framebufferBind(this->rcasFb); - glActiveTexture(GL_TEXTURE0); - egl_textureBind(texture); - glBindSampler(0, this->sampler); - egl_shaderUse(this->rcas); - egl_filterRectsRender(this->rcas, rects); - texture = egl_framebufferGetTexture(this->rcasFb); - - return texture; + return egl_effectRun(this->effect, rects, texture); } EGL_FilterOps egl_filterFFXFSR1Ops = diff --git a/client/renderers/EGL/framebuffer.c b/client/renderers/EGL/framebuffer.c index b31b7515..6d429d92 100644 --- a/client/renderers/EGL/framebuffer.c +++ b/client/renderers/EGL/framebuffer.c @@ -57,7 +57,11 @@ void egl_framebufferFree(EGL_Framebuffer ** fb) { EGL_Framebuffer * this = *fb; + if (!this) + return; + egl_textureFree(&this->tex); + glDeleteFramebuffers(1, &this->fbo); free(this); *fb = NULL; } diff --git a/client/renderers/EGL/shader.c b/client/renderers/EGL/shader.c index b2d749ca..b22dfc2a 100644 --- a/client/renderers/EGL/shader.c +++ b/client/renderers/EGL/shader.c @@ -23,18 +23,40 @@ #include "common/stringutils.h" #include "util.h" +#include #include #include #include +struct EGL_Uniform +{ + char * name; + GLint location; + + enum EGL_UniformType type; + GLsizei count; + GLboolean transpose; + bool initialized; + bool dirty; + + void * data; + size_t size; + size_t capacity; + + EGL_Shader * shader; + struct EGL_Uniform * next; + struct EGL_Uniform * dirtyNext; +}; + struct EGL_Shader { bool hasShader; GLuint shader; EGL_Uniform * uniforms; - int uniformCount; - int uniformUsed; + EGL_Uniform * uniformsTail; + EGL_Uniform * dirtyUniforms; + EGL_Uniform * dirtyUniformsTail; }; bool egl_shaderInit(EGL_Shader ** this) @@ -58,8 +80,15 @@ void egl_shaderFree(EGL_Shader ** shader) if (this->hasShader) glDeleteProgram(this->shader); - egl_shaderFreeUniforms(this); - free(this->uniforms); + EGL_Uniform * uniform = this->uniforms; + while (uniform) + { + EGL_Uniform * next = uniform->next; + free(uniform->name); + free(uniform->data); + free(uniform); + uniform = next; + } free(this); *shader = NULL; @@ -98,6 +127,34 @@ bool egl_shaderLoad(EGL_Shader * this, return ret; } +static void uniformMarkDirty(EGL_Uniform * uniform) +{ + if (uniform->dirty) + return; + + uniform->dirty = true; + if (uniform->shader->dirtyUniformsTail) + uniform->shader->dirtyUniformsTail->dirtyNext = uniform; + else + uniform->shader->dirtyUniforms = uniform; + uniform->shader->dirtyUniformsTail = uniform; +} + +static void shaderResolveUniforms(EGL_Shader * this) +{ + this->dirtyUniforms = NULL; + this->dirtyUniformsTail = NULL; + + for(EGL_Uniform * uniform = this->uniforms; uniform; uniform = uniform->next) + { + uniform->location = glGetUniformLocation(this->shader, uniform->name); + uniform->dirty = false; + uniform->dirtyNext = NULL; + if (uniform->initialized) + uniformMarkDirty(uniform); + } +} + static bool shaderCompile(EGL_Shader * this, const char * vertex_code, size_t vertex_size, const char * fragment_code, size_t fragment_size) { @@ -187,10 +244,15 @@ static bool shaderCompile(EGL_Shader * this, const char * vertex_code, if (logLength > 0) { char *log = malloc(logLength + 1); - glGetProgramInfoLog(this->shader, logLength, NULL, log); - log[logLength] = 0; - DEBUG_ERROR("%s", log); - free(log); + if (!log) + DEBUG_ERROR("out of memory"); + else + { + glGetProgramInfoLog(this->shader, logLength, NULL, log); + log[logLength] = 0; + DEBUG_ERROR("%s", log); + free(log); + } } glDetachShader(this->shader, vertexShader ); @@ -198,6 +260,7 @@ static bool shaderCompile(EGL_Shader * this, const char * vertex_code, glDeleteShader(fragmentShader); glDeleteShader(vertexShader ); glDeleteProgram(this->shader ); + this->shader = 0; return false; } @@ -207,6 +270,7 @@ static bool shaderCompile(EGL_Shader * this, const char * vertex_code, glDeleteShader(vertexShader ); this->hasShader = true; + shaderResolveUniforms(this); return true; } @@ -353,310 +417,325 @@ exit: return result; } -void egl_shaderSetUniforms(EGL_Shader * this, EGL_Uniform * uniforms, int count) +static bool uniformLayout(enum EGL_UniformType type, size_t * scalarSize, + size_t * components) { - egl_shaderFreeUniforms(this); - if (count > this->uniformCount) + switch(type) { - free(this->uniforms); - this->uniforms = malloc(sizeof(*this->uniforms) * count); - if (!this->uniforms) - { - DEBUG_ERROR("out of memory"); - return; - } + case EGL_UNIFORM_TYPE_1F: + case EGL_UNIFORM_TYPE_1FV: + *scalarSize = sizeof(GLfloat); *components = 1; break; + case EGL_UNIFORM_TYPE_2F: + case EGL_UNIFORM_TYPE_2FV: + *scalarSize = sizeof(GLfloat); *components = 2; break; + case EGL_UNIFORM_TYPE_3F: + case EGL_UNIFORM_TYPE_3FV: + *scalarSize = sizeof(GLfloat); *components = 3; break; + case EGL_UNIFORM_TYPE_4F: + case EGL_UNIFORM_TYPE_4FV: + *scalarSize = sizeof(GLfloat); *components = 4; break; - this->uniformCount = count; + case EGL_UNIFORM_TYPE_1I: + case EGL_UNIFORM_TYPE_1IV: + *scalarSize = sizeof(GLint); *components = 1; break; + case EGL_UNIFORM_TYPE_2I: + case EGL_UNIFORM_TYPE_2IV: + *scalarSize = sizeof(GLint); *components = 2; break; + case EGL_UNIFORM_TYPE_3I: + case EGL_UNIFORM_TYPE_3IV: + *scalarSize = sizeof(GLint); *components = 3; break; + case EGL_UNIFORM_TYPE_4I: + case EGL_UNIFORM_TYPE_4IV: + *scalarSize = sizeof(GLint); *components = 4; break; + + case EGL_UNIFORM_TYPE_1UI: + case EGL_UNIFORM_TYPE_1UIV: + *scalarSize = sizeof(GLuint); *components = 1; break; + case EGL_UNIFORM_TYPE_2UI: + case EGL_UNIFORM_TYPE_2UIV: + *scalarSize = sizeof(GLuint); *components = 2; break; + case EGL_UNIFORM_TYPE_3UI: + case EGL_UNIFORM_TYPE_3UIV: + *scalarSize = sizeof(GLuint); *components = 3; break; + case EGL_UNIFORM_TYPE_4UI: + case EGL_UNIFORM_TYPE_4UIV: + *scalarSize = sizeof(GLuint); *components = 4; break; + + case EGL_UNIFORM_TYPE_M2FV: + *scalarSize = sizeof(GLfloat); *components = 4; break; + case EGL_UNIFORM_TYPE_M3FV: + *scalarSize = sizeof(GLfloat); *components = 9; break; + case EGL_UNIFORM_TYPE_M4FV: + *scalarSize = sizeof(GLfloat); *components = 16; break; + case EGL_UNIFORM_TYPE_M2x3FV: + case EGL_UNIFORM_TYPE_M3x2FV: + *scalarSize = sizeof(GLfloat); *components = 6; break; + case EGL_UNIFORM_TYPE_M2x4FV: + case EGL_UNIFORM_TYPE_M4x2FV: + *scalarSize = sizeof(GLfloat); *components = 8; break; + case EGL_UNIFORM_TYPE_M3x4FV: + case EGL_UNIFORM_TYPE_M4x3FV: + *scalarSize = sizeof(GLfloat); *components = 12; break; + + default: + return false; } + return true; - this->uniformUsed = count; - memcpy(this->uniforms, uniforms, sizeof(*this->uniforms) * count); + return false; +} - for(int i = 0; i < this->uniformUsed; ++i) - { - switch(this->uniforms[i].type) - { - case EGL_UNIFORM_TYPE_1FV: - case EGL_UNIFORM_TYPE_2FV: - case EGL_UNIFORM_TYPE_3FV: - case EGL_UNIFORM_TYPE_4FV: - case EGL_UNIFORM_TYPE_1IV: - case EGL_UNIFORM_TYPE_2IV: - case EGL_UNIFORM_TYPE_3IV: - case EGL_UNIFORM_TYPE_4IV: - case EGL_UNIFORM_TYPE_1UIV: - case EGL_UNIFORM_TYPE_2UIV: - case EGL_UNIFORM_TYPE_3UIV: - case EGL_UNIFORM_TYPE_4UIV: - countedBufferAddRef(this->uniforms[i].v); - break; - - case EGL_UNIFORM_TYPE_M2FV: - case EGL_UNIFORM_TYPE_M3FV: - case EGL_UNIFORM_TYPE_M4FV: - case EGL_UNIFORM_TYPE_M2x3FV: - case EGL_UNIFORM_TYPE_M3x2FV: - case EGL_UNIFORM_TYPE_M2x4FV: - case EGL_UNIFORM_TYPE_M4x2FV: - case EGL_UNIFORM_TYPE_M3x4FV: - case EGL_UNIFORM_TYPE_M4x3FV: - countedBufferAddRef(this->uniforms[i].m.v); - break; - - default: - break; - } - } -}; - -void egl_shaderFreeUniforms(EGL_Shader * this) +EGL_Uniform * egl_shaderGetUniform(EGL_Shader * this, const char * name) { - for(int i = 0; i < this->uniformUsed; ++i) + for(EGL_Uniform * uniform = this->uniforms; uniform; uniform = uniform->next) + if (!strcmp(uniform->name, name)) + return uniform; + + EGL_Uniform * uniform = calloc(1, sizeof(*uniform)); + if (!uniform) { - switch(this->uniforms[i].type) - { - case EGL_UNIFORM_TYPE_1FV: - case EGL_UNIFORM_TYPE_2FV: - case EGL_UNIFORM_TYPE_3FV: - case EGL_UNIFORM_TYPE_4FV: - case EGL_UNIFORM_TYPE_1IV: - case EGL_UNIFORM_TYPE_2IV: - case EGL_UNIFORM_TYPE_3IV: - case EGL_UNIFORM_TYPE_4IV: - case EGL_UNIFORM_TYPE_1UIV: - case EGL_UNIFORM_TYPE_2UIV: - case EGL_UNIFORM_TYPE_3UIV: - case EGL_UNIFORM_TYPE_4UIV: - countedBufferRelease(&this->uniforms[i].v); - break; - - case EGL_UNIFORM_TYPE_M2FV: - case EGL_UNIFORM_TYPE_M3FV: - case EGL_UNIFORM_TYPE_M4FV: - case EGL_UNIFORM_TYPE_M2x3FV: - case EGL_UNIFORM_TYPE_M3x2FV: - case EGL_UNIFORM_TYPE_M2x4FV: - case EGL_UNIFORM_TYPE_M4x2FV: - case EGL_UNIFORM_TYPE_M3x4FV: - case EGL_UNIFORM_TYPE_M4x3FV: - countedBufferRelease(&this->uniforms[i].m.v); - break; - - default: - break; - } + DEBUG_ERROR("Failed to allocate uniform `%s`", name); + return NULL; + } + + const size_t nameSize = strlen(name) + 1; + uniform->name = malloc(nameSize); + if (!uniform->name) + { + DEBUG_ERROR("Failed to allocate uniform name `%s`", name); + free(uniform); + return NULL; + } + + memcpy(uniform->name, name, nameSize); + uniform->shader = this; + uniform->location = this->hasShader ? + glGetUniformLocation(this->shader, name) : -1; + + if (this->uniformsTail) + this->uniformsTail->next = uniform; + else + this->uniforms = uniform; + this->uniformsTail = uniform; + + return uniform; +} + +bool egl_uniformSet(EGL_Uniform * uniform, enum EGL_UniformType type, + GLsizei count, GLboolean transpose, const void * data) +{ + if (!uniform || !data || count < 1) + return false; + + size_t scalarSize, components; + if (!uniformLayout(type, &scalarSize, &components) || + components > SIZE_MAX / scalarSize || + (size_t)count > SIZE_MAX / (scalarSize * components)) + return false; + + const size_t size = scalarSize * components * count; + const bool changed = !uniform->initialized || + uniform->type != type || + uniform->count != count || + uniform->transpose != transpose || + uniform->size != size || + memcmp(uniform->data, data, size); + + if (!changed) + return true; + + if (size > uniform->capacity) + { + void * newData = realloc(uniform->data, size); + if (!newData) + { + DEBUG_ERROR("Failed to allocate data for uniform `%s`", uniform->name); + return false; + } + + uniform->data = newData; + uniform->capacity = size; + } + + memcpy(uniform->data, data, size); + uniform->type = type; + uniform->count = count; + uniform->transpose = transpose; + uniform->size = size; + uniform->initialized = true; + uniformMarkDirty(uniform); + return true; +} + +bool egl_uniform1f(EGL_Uniform * uniform, GLfloat x) +{ + return egl_uniformSet(uniform, EGL_UNIFORM_TYPE_1F, 1, GL_FALSE, &x); +} + +bool egl_uniform2f(EGL_Uniform * uniform, GLfloat x, GLfloat y) +{ + const GLfloat value[] = { x, y }; + return egl_uniformSet(uniform, EGL_UNIFORM_TYPE_2F, 1, GL_FALSE, value); +} + +bool egl_uniform3f(EGL_Uniform * uniform, GLfloat x, GLfloat y, GLfloat z) +{ + const GLfloat value[] = { x, y, z }; + return egl_uniformSet(uniform, EGL_UNIFORM_TYPE_3F, 1, GL_FALSE, value); +} + +bool egl_uniform4f(EGL_Uniform * uniform, GLfloat x, GLfloat y, GLfloat z, + GLfloat w) +{ + const GLfloat value[] = { x, y, z, w }; + return egl_uniformSet(uniform, EGL_UNIFORM_TYPE_4F, 1, GL_FALSE, value); +} + +bool egl_uniform1i(EGL_Uniform * uniform, GLint x) +{ + return egl_uniformSet(uniform, EGL_UNIFORM_TYPE_1I, 1, GL_FALSE, &x); +} + +bool egl_uniform1ui(EGL_Uniform * uniform, GLuint x) +{ + return egl_uniformSet(uniform, EGL_UNIFORM_TYPE_1UI, 1, GL_FALSE, &x); +} + +bool egl_uniform4ui(EGL_Uniform * uniform, GLuint x, GLuint y, GLuint z, + GLuint w) +{ + const GLuint value[] = { x, y, z, w }; + return egl_uniformSet(uniform, EGL_UNIFORM_TYPE_4UI, 1, GL_FALSE, value); +} + +bool egl_uniform4uiv(EGL_Uniform * uniform, GLsizei count, + const GLuint * value) +{ + return egl_uniformSet(uniform, EGL_UNIFORM_TYPE_4UIV, count, GL_FALSE, + value); +} + +bool egl_uniformMatrix3x2fv(EGL_Uniform * uniform, GLsizei count, + GLboolean transpose, const GLfloat * value) +{ + return egl_uniformSet(uniform, EGL_UNIFORM_TYPE_M3x2FV, count, transpose, + value); +} + +static void uniformUpload(EGL_Uniform * uniform) +{ + switch(uniform->type) + { + case EGL_UNIFORM_TYPE_1F: + glUniform1fv(uniform->location, uniform->count, uniform->data); break; + case EGL_UNIFORM_TYPE_2F: + glUniform2fv(uniform->location, uniform->count, uniform->data); break; + case EGL_UNIFORM_TYPE_3F: + glUniform3fv(uniform->location, uniform->count, uniform->data); break; + case EGL_UNIFORM_TYPE_4F: + glUniform4fv(uniform->location, uniform->count, uniform->data); break; + case EGL_UNIFORM_TYPE_1I: + glUniform1iv(uniform->location, uniform->count, uniform->data); break; + case EGL_UNIFORM_TYPE_2I: + glUniform2iv(uniform->location, uniform->count, uniform->data); break; + case EGL_UNIFORM_TYPE_3I: + glUniform3iv(uniform->location, uniform->count, uniform->data); break; + case EGL_UNIFORM_TYPE_4I: + glUniform4iv(uniform->location, uniform->count, uniform->data); break; + case EGL_UNIFORM_TYPE_1UI: + glUniform1uiv(uniform->location, uniform->count, uniform->data); break; + case EGL_UNIFORM_TYPE_2UI: + glUniform2uiv(uniform->location, uniform->count, uniform->data); break; + case EGL_UNIFORM_TYPE_3UI: + glUniform3uiv(uniform->location, uniform->count, uniform->data); break; + case EGL_UNIFORM_TYPE_4UI: + glUniform4uiv(uniform->location, uniform->count, uniform->data); break; + + case EGL_UNIFORM_TYPE_1FV: + glUniform1fv(uniform->location, uniform->count, uniform->data); break; + case EGL_UNIFORM_TYPE_2FV: + glUniform2fv(uniform->location, uniform->count, uniform->data); break; + case EGL_UNIFORM_TYPE_3FV: + glUniform3fv(uniform->location, uniform->count, uniform->data); break; + case EGL_UNIFORM_TYPE_4FV: + glUniform4fv(uniform->location, uniform->count, uniform->data); break; + case EGL_UNIFORM_TYPE_1IV: + glUniform1iv(uniform->location, uniform->count, uniform->data); break; + case EGL_UNIFORM_TYPE_2IV: + glUniform2iv(uniform->location, uniform->count, uniform->data); break; + case EGL_UNIFORM_TYPE_3IV: + glUniform3iv(uniform->location, uniform->count, uniform->data); break; + case EGL_UNIFORM_TYPE_4IV: + glUniform4iv(uniform->location, uniform->count, uniform->data); break; + case EGL_UNIFORM_TYPE_1UIV: + glUniform1uiv(uniform->location, uniform->count, uniform->data); break; + case EGL_UNIFORM_TYPE_2UIV: + glUniform2uiv(uniform->location, uniform->count, uniform->data); break; + case EGL_UNIFORM_TYPE_3UIV: + glUniform3uiv(uniform->location, uniform->count, uniform->data); break; + case EGL_UNIFORM_TYPE_4UIV: + glUniform4uiv(uniform->location, uniform->count, uniform->data); break; + + case EGL_UNIFORM_TYPE_M2FV: + glUniformMatrix2fv(uniform->location, uniform->count, + uniform->transpose, uniform->data); break; + case EGL_UNIFORM_TYPE_M3FV: + glUniformMatrix3fv(uniform->location, uniform->count, + uniform->transpose, uniform->data); break; + case EGL_UNIFORM_TYPE_M4FV: + glUniformMatrix4fv(uniform->location, uniform->count, + uniform->transpose, uniform->data); break; + case EGL_UNIFORM_TYPE_M2x3FV: + glUniformMatrix2x3fv(uniform->location, uniform->count, + uniform->transpose, uniform->data); break; + case EGL_UNIFORM_TYPE_M3x2FV: + glUniformMatrix3x2fv(uniform->location, uniform->count, + uniform->transpose, uniform->data); break; + case EGL_UNIFORM_TYPE_M2x4FV: + glUniformMatrix2x4fv(uniform->location, uniform->count, + uniform->transpose, uniform->data); break; + case EGL_UNIFORM_TYPE_M4x2FV: + glUniformMatrix4x2fv(uniform->location, uniform->count, + uniform->transpose, uniform->data); break; + case EGL_UNIFORM_TYPE_M3x4FV: + glUniformMatrix3x4fv(uniform->location, uniform->count, + uniform->transpose, uniform->data); break; + case EGL_UNIFORM_TYPE_M4x3FV: + glUniformMatrix4x3fv(uniform->location, uniform->count, + uniform->transpose, uniform->data); break; } - this->uniformUsed = 0; } void egl_shaderUse(EGL_Shader * this) { - if (this->hasShader) - glUseProgram(this->shader); - else - DEBUG_ERROR("Shader program has not been compiled"); - - for(int i = 0; i < this->uniformUsed; ++i) + if (!this->hasShader) { - EGL_Uniform * uniform = &this->uniforms[i]; - switch(uniform->type) - { - case EGL_UNIFORM_TYPE_1F: - glUniform1f(uniform->location, uniform->f[0]); - break; - - case EGL_UNIFORM_TYPE_2F: - glUniform2f(uniform->location, uniform->f[0], uniform->f[1]); - break; - - case EGL_UNIFORM_TYPE_3F: - glUniform3f(uniform->location, uniform->f[0], uniform->f[1], - uniform->f[2]); - break; - - case EGL_UNIFORM_TYPE_4F: - glUniform4f(uniform->location, uniform->f[0], uniform->f[1], - uniform->f[2], uniform->f[3]); - break; - - case EGL_UNIFORM_TYPE_1I: - glUniform1i(uniform->location, uniform->i[0]); - break; - - case EGL_UNIFORM_TYPE_2I: - glUniform2i(uniform->location, uniform->i[0], uniform->i[1]); - break; - - case EGL_UNIFORM_TYPE_3I: - glUniform3i(uniform->location, uniform->i[0], uniform->i[1], - uniform->i[2]); - break; - - case EGL_UNIFORM_TYPE_4I: - glUniform4i(uniform->location, uniform->i[0], uniform->i[1], - uniform->i[2], uniform->i[3]); - break; - - case EGL_UNIFORM_TYPE_1UI: - glUniform1ui(uniform->location, uniform->ui[0]); - break; - - case EGL_UNIFORM_TYPE_2UI: - glUniform2ui(uniform->location, uniform->ui[0], uniform->ui[1]); - break; - - case EGL_UNIFORM_TYPE_3UI: - glUniform3ui(uniform->location, uniform->ui[0], uniform->ui[1], - uniform->ui[2]); - break; - - case EGL_UNIFORM_TYPE_4UI: - glUniform4ui(uniform->location, uniform->ui[0], uniform->ui[1], - uniform->ui[2], uniform->ui[3]); - break; - - case EGL_UNIFORM_TYPE_1FV: - glUniform1fv(uniform->location, uniform->v->size / sizeof(GLfloat), - (const GLfloat *)uniform->v->data); - break; - - case EGL_UNIFORM_TYPE_2FV: - glUniform2fv(uniform->location, uniform->v->size / sizeof(GLfloat) / 2, - (const GLfloat *)uniform->v->data); - break; - - case EGL_UNIFORM_TYPE_3FV: - glUniform3fv(uniform->location, uniform->v->size / sizeof(GLfloat) / 3, - (const GLfloat *)uniform->v->data); - break; - - case EGL_UNIFORM_TYPE_4FV: - glUniform4fv(uniform->location, uniform->v->size / sizeof(GLfloat) / 4, - (const GLfloat *)uniform->v->data); - break; - - case EGL_UNIFORM_TYPE_1IV: - glUniform1iv(uniform->location, uniform->v->size / sizeof(GLint), - (const GLint *)uniform->v->data); - break; - - case EGL_UNIFORM_TYPE_2IV: - glUniform2iv(uniform->location, uniform->v->size / sizeof(GLint) / 2, - (const GLint *)uniform->v->data); - break; - - case EGL_UNIFORM_TYPE_3IV: - glUniform3iv(uniform->location, uniform->v->size / sizeof(GLint) / 3, - (const GLint *)uniform->v->data); - break; - - case EGL_UNIFORM_TYPE_4IV: - glUniform4iv(uniform->location, uniform->v->size / sizeof(GLint) / 4, - (const GLint *)uniform->v->data); - break; - - case EGL_UNIFORM_TYPE_1UIV: - glUniform1uiv(uniform->location, uniform->v->size / sizeof(GLuint), - (const GLuint *)uniform->v->data); - break; - - case EGL_UNIFORM_TYPE_2UIV: - glUniform2uiv(uniform->location, uniform->v->size / sizeof(GLuint) / 2, - (const GLuint *)uniform->v->data); - break; - - case EGL_UNIFORM_TYPE_3UIV: - glUniform3uiv(uniform->location, uniform->v->size / sizeof(GLuint) / 3, - (const GLuint *)uniform->v->data); - break; - - case EGL_UNIFORM_TYPE_4UIV: - glUniform4uiv(uniform->location, uniform->v->size / sizeof(GLuint) / 4, - (const GLuint *)uniform->v->data); - break; - - case EGL_UNIFORM_TYPE_M2FV: - glUniformMatrix2fv(uniform->location, - uniform->v->size / sizeof(GLfloat) / 2, - uniform->m.transpose, (const GLfloat *)uniform->m.v->data); - break; - - case EGL_UNIFORM_TYPE_M3FV: - glUniformMatrix3fv(uniform->location, - uniform->v->size / sizeof(GLfloat) / 3, - uniform->m.transpose, (const GLfloat *)uniform->m.v->data); - break; - - case EGL_UNIFORM_TYPE_M4FV: - glUniformMatrix4fv(uniform->location, - uniform->v->size / sizeof(GLfloat) / 4, - uniform->m.transpose, (const GLfloat *)uniform->m.v->data); - break; - - case EGL_UNIFORM_TYPE_M2x3FV: - glUniformMatrix2x3fv(uniform->location, - uniform->v->size / sizeof(GLfloat) / 6, - uniform->m.transpose, (const GLfloat *)uniform->m.v->data); - break; - - case EGL_UNIFORM_TYPE_M3x2FV: - glUniformMatrix3x2fv(uniform->location, - uniform->v->size / sizeof(GLfloat) / 6, - uniform->m.transpose, (const GLfloat *)uniform->m.v->data); - break; - - case EGL_UNIFORM_TYPE_M2x4FV: - glUniformMatrix2x4fv(uniform->location, - uniform->v->size / sizeof(GLfloat) / 8, - uniform->m.transpose, (const GLfloat *)uniform->m.v->data); - break; - - case EGL_UNIFORM_TYPE_M4x2FV: - glUniformMatrix4x2fv(uniform->location, - uniform->v->size / sizeof(GLfloat) / 8, - uniform->m.transpose, (const GLfloat *)uniform->m.v->data); - break; - - case EGL_UNIFORM_TYPE_M3x4FV: - glUniformMatrix3x4fv(uniform->location, - uniform->v->size / sizeof(GLfloat) / 12, - uniform->m.transpose, (const GLfloat *)uniform->m.v->data); - break; - - case EGL_UNIFORM_TYPE_M4x3FV: - glUniformMatrix4x3fv(uniform->location, - uniform->v->size / sizeof(GLfloat) / 12, - uniform->m.transpose, (const GLfloat *)uniform->m.v->data); - break; - } + DEBUG_ERROR("Shader program has not been compiled"); + return; } -} -void egl_shaderAssocTextures(EGL_Shader * this, const int count) -{ - char name[] = "sampler1"; glUseProgram(this->shader); - for(int i = 0; i < count; ++i, name[7]++) - { - GLint loc = glGetUniformLocation(this->shader, name); - if (loc == -1) - { - DEBUG_WARN("Shader uniform location `%s` not found", name); - continue; - } - glUniform1i(loc, i); + while(this->dirtyUniforms) + { + EGL_Uniform * uniform = this->dirtyUniforms; + this->dirtyUniforms = uniform->dirtyNext; + uniform->dirtyNext = NULL; + uniform->dirty = false; + + if (uniform->location != -1) + uniformUpload(uniform); } - glUseProgram(0); + + this->dirtyUniformsTail = NULL; } -GLint egl_shaderGetUniform(EGL_Shader * this, const char * name) +void egl_shaderAssocTextures(EGL_Shader * this, int count) { - if (!this->shader) + char name[32]; + for(int i = 0; i < count; ++i) { - DEBUG_ERROR("Shader program has not been compiled"); - return 0; + snprintf(name, sizeof(name), "sampler%d", i + 1); + egl_uniform1i(egl_shaderGetUniform(this, name), i); } - - return glGetUniformLocation(this->shader, name); } diff --git a/client/renderers/EGL/shader.h b/client/renderers/EGL/shader.h index 356fb0ee..d20906ea 100644 --- a/client/renderers/EGL/shader.h +++ b/client/renderers/EGL/shader.h @@ -25,9 +25,8 @@ #include -#include "common/countedbuffer.h" - -typedef struct EGL_Shader EGL_Shader; +typedef struct EGL_Shader EGL_Shader; +typedef struct EGL_Uniform EGL_Uniform; enum EGL_UniformType { @@ -70,29 +69,6 @@ enum EGL_UniformType EGL_UNIFORM_TYPE_M4x3FV }; -typedef struct EGL_Uniform -{ - enum EGL_UniformType type; - GLint location; - - union - { - GLfloat f [4]; - GLint i [4]; - GLuint ui[4]; - - CountedBuffer * v; - - struct - { - CountedBuffer * v; - GLboolean transpose; - } - m; - }; -} -EGL_Uniform; - typedef struct EGL_ShaderDefine { const char * name; @@ -103,19 +79,37 @@ EGL_ShaderDefine; bool egl_shaderInit(EGL_Shader ** shader); void egl_shaderFree(EGL_Shader ** shader); -bool egl_shaderLoad(EGL_Shader * model, const char * vertex_file, - const char * fragment_file, bool useDMA, const EGL_ShaderDefine * defines); +bool egl_shaderLoad(EGL_Shader * shader, const char * vertex_file, + const char * fragment_file, bool useDMA, + const EGL_ShaderDefine * defines); -bool egl_shaderCompile(EGL_Shader * model, const char * vertex_code, +bool egl_shaderCompile(EGL_Shader * shader, const char * vertex_code, size_t vertex_size, const char * fragment_code, size_t fragment_size, bool useDMA, const EGL_ShaderDefine * defines); -void egl_shaderSetUniforms(EGL_Shader * shader, EGL_Uniform * uniforms, - int count); -void egl_shaderFreeUniforms(EGL_Shader * shader); +/* + * Uniform handles remain valid when a shader is recompiled. Values are copied + * into the shader and are only uploaded when they have changed or the program + * has been relinked. count is the number of vectors or matrices in data. + */ +EGL_Uniform * egl_shaderGetUniform(EGL_Shader * shader, const char * name); +bool egl_uniformSet(EGL_Uniform * uniform, enum EGL_UniformType type, + GLsizei count, GLboolean transpose, const void * data); + +bool egl_uniform1f (EGL_Uniform * uniform, GLfloat x); +bool egl_uniform2f (EGL_Uniform * uniform, GLfloat x, GLfloat y); +bool egl_uniform3f (EGL_Uniform * uniform, GLfloat x, GLfloat y, GLfloat z); +bool egl_uniform4f (EGL_Uniform * uniform, GLfloat x, GLfloat y, GLfloat z, + GLfloat w); +bool egl_uniform1i (EGL_Uniform * uniform, GLint x); +bool egl_uniform1ui(EGL_Uniform * uniform, GLuint x); +bool egl_uniform4ui(EGL_Uniform * uniform, GLuint x, GLuint y, GLuint z, + GLuint w); + +bool egl_uniform4uiv(EGL_Uniform * uniform, GLsizei count, + const GLuint * value); +bool egl_uniformMatrix3x2fv(EGL_Uniform * uniform, GLsizei count, + GLboolean transpose, const GLfloat * value); void egl_shaderUse(EGL_Shader * shader); - -void egl_shaderAssocTextures(EGL_Shader * shader, const int count); - -GLint egl_shaderGetUniform(EGL_Shader * shader, const char * name); +void egl_shaderAssocTextures(EGL_Shader * shader, int count);