[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.
This commit is contained in:
Geoffrey McRae
2026-07-17 21:05:47 +10:00
parent af47e7c1e9
commit e3616ed4b9
15 changed files with 843 additions and 657 deletions

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 <stdlib.h>
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;
}

View File

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

View File

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

View File

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

View File

@@ -19,11 +19,10 @@
*/
#include "filter.h"
#include "framebuffer.h"
#include "effect.h"
#include <math.h>
#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 =

View File

@@ -19,11 +19,10 @@
*/
#include "filter.h"
#include "framebuffer.h"
#include "effect.h"
#include <math.h>
#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 =

View File

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

View File

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

View File

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

View File

@@ -23,18 +23,40 @@
#include "common/stringutils.h"
#include "util.h"
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
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);
}

View File

@@ -25,9 +25,8 @@
#include <GLES3/gl3.h>
#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);