diff --git a/client/renderers/EGL/CMakeLists.txt b/client/renderers/EGL/CMakeLists.txt index 1f7502cf..794be2ab 100644 --- a/client/renderers/EGL/CMakeLists.txt +++ b/client/renderers/EGL/CMakeLists.txt @@ -73,6 +73,7 @@ make_defines( add_library(renderer_EGL STATIC egl.c egldebug.c + state.c shader.c texture_util.c texture.c diff --git a/client/renderers/EGL/cursor.c b/client/renderers/EGL/cursor.c index ee94c00a..174cbb68 100644 --- a/client/renderers/EGL/cursor.c +++ b/client/renderers/EGL/cursor.c @@ -25,6 +25,7 @@ #include "texture.h" #include "shader.h" +#include "state.h" #include "model.h" #include "util.h" @@ -386,7 +387,7 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor, state.rect.x = max(0, state.rect.x - 1); state.rect.y = max(0, state.rect.y - 1); - glEnable(GL_BLEND); + egl_stateBlend(true); switch(cursor->type) { case LG_CURSOR_MONOCHROME: @@ -394,14 +395,14 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor, 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_stateBlendFunc(GL_ZERO, GL_SRC_COLOR); egl_modelSetTexture(cursor->model, cursor->norm.texture); egl_modelRender(cursor->model); 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_stateBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO); egl_modelSetTexture(cursor->model, cursor->mono.texture); egl_modelRender(cursor->model); break; @@ -412,14 +413,14 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor, 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_stateBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); egl_modelSetTexture(cursor->model, cursor->norm.texture); egl_modelRender(cursor->model); 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_stateBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO); egl_modelSetTexture(cursor->model, cursor->mono.texture); egl_modelRender(cursor->model); break; @@ -430,13 +431,13 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor, 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_stateBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); egl_modelSetTexture(cursor->model, cursor->norm.texture); egl_modelRender(cursor->model); break; } } - glDisable(GL_BLEND); + egl_stateBlend(false); return state; } diff --git a/client/renderers/EGL/damage.c b/client/renderers/EGL/damage.c index 31c4d404..e28e8a99 100644 --- a/client/renderers/EGL/damage.c +++ b/client/renderers/EGL/damage.c @@ -26,6 +26,7 @@ #include "app.h" #include "desktop_rects.h" #include "shader.h" +#include "state.h" #include "cimgui.h" #include @@ -140,8 +141,8 @@ bool egl_damageRender(EGL_Damage * damage, LG_RendererRotate rotate, const struc update_matrix(damage); } - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + egl_stateBlend(true); + egl_stateBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); egl_uniformMatrix3x2fv(damage->uTransform, 1, GL_FALSE, damage->transform); egl_shaderUse(damage->shader); @@ -152,6 +153,6 @@ bool egl_damageRender(EGL_Damage * damage, LG_RendererRotate rotate, const struc egl_desktopRectsRender(damage->mesh); - glDisable(GL_BLEND); + egl_stateBlend(false); return true; } diff --git a/client/renderers/EGL/desktop.c b/client/renderers/EGL/desktop.c index 14380790..22acd8ab 100644 --- a/client/renderers/EGL/desktop.c +++ b/client/renderers/EGL/desktop.c @@ -19,6 +19,7 @@ */ #include "desktop.h" +#include "state.h" #include "common/debug.h" #include "common/option.h" #include "common/locking.h" @@ -476,10 +477,9 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth, finalSizeY = height; } - glBindFramebuffer(GL_FRAMEBUFFER, 0); + egl_stateBindFramebuffer(0); egl_resetViewport(desktop->egl); - glActiveTexture(GL_TEXTURE0); egl_textureBind(texture); if (finalSizeX > width || finalSizeY > height) @@ -523,7 +523,6 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth, egl_uniform1i (shader->uMapHDRPQ , desktop->hdrPQ); egl_shaderUse(shader->shader); egl_desktopRectsRender(desktop->mesh); - glBindTexture(GL_TEXTURE_2D, 0); return true; } diff --git a/client/renderers/EGL/desktop_rects.c b/client/renderers/EGL/desktop_rects.c index a1ab0dfd..2c2069b8 100644 --- a/client/renderers/EGL/desktop_rects.c +++ b/client/renderers/EGL/desktop_rects.c @@ -19,6 +19,7 @@ */ #include "desktop_rects.h" +#include "state.h" #include "common/debug.h" #include "common/KVMFR.h" #include "common/locking.h" @@ -54,14 +55,13 @@ bool egl_desktopRectsInit(EGL_DesktopRects ** rects_, int maxCount) memset(rects, 0, sizeof(*rects)); glGenVertexArrays(1, &rects->vao); - glBindVertexArray(rects->vao); + egl_stateBindVertexArray(rects->vao); glGenBuffers(2, rects->buffers); - glBindBuffer(GL_ARRAY_BUFFER, rects->buffers[0]); + egl_stateBindBuffer(GL_ARRAY_BUFFER, rects->buffers[0]); glBufferData(GL_ARRAY_BUFFER, maxCount * 8 * sizeof(GLfloat), NULL, GL_STREAM_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, NULL); - glBindBuffer(GL_ARRAY_BUFFER, 0); GLushort indices[maxCount * 6]; for (int i = 0; i < maxCount; ++i) @@ -77,8 +77,6 @@ bool egl_desktopRectsInit(EGL_DesktopRects ** rects_, int maxCount) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, rects->buffers[1]); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof indices, indices, GL_STATIC_DRAW); - glBindVertexArray(0); - rects->count = 0; rects->maxCount = maxCount; return true; @@ -92,6 +90,7 @@ void egl_desktopRectsFree(EGL_DesktopRects ** rects_) glDeleteVertexArrays(1, &rects->vao); glDeleteBuffers(2, rects->buffers); + egl_stateInvalidateShared(); free(rects->lastVertices); free(rects); *rects_ = NULL; @@ -161,9 +160,8 @@ void egl_desktopRectsUpdate(EGL_DesktopRects * rects, const struct DamageRects * rects->lastVerticesCount = count; memcpy(rects->lastVertices, vertices, sizeof(GLfloat) * count); - glBindBuffer(GL_ARRAY_BUFFER, rects->buffers[0]); + egl_stateBindBuffer(GL_ARRAY_BUFFER, rects->buffers[0]); glBufferSubData(GL_ARRAY_BUFFER, 0, rects->count * 8 * sizeof(GLfloat), vertices); - glBindBuffer(GL_ARRAY_BUFFER, 0); } static void desktopToGLSpace(double matrix[6], int width, int height, double translateX, @@ -299,7 +297,6 @@ void egl_desktopRectsRender(EGL_DesktopRects * rects) if (unlikely(!rects->count)) return; - glBindVertexArray(rects->vao); + egl_stateBindVertexArray(rects->vao); glDrawElements(GL_TRIANGLES, 6 * rects->count, GL_UNSIGNED_SHORT, NULL); - glBindVertexArray(0); } diff --git a/client/renderers/EGL/effect.c b/client/renderers/EGL/effect.c index 0c54e3f2..9f292ff2 100644 --- a/client/renderers/EGL/effect.c +++ b/client/renderers/EGL/effect.c @@ -22,6 +22,7 @@ #include "filter.h" #include "framebuffer.h" +#include "state.h" #include "texture.h" #include "common/debug.h" @@ -74,16 +75,21 @@ void egl_effectFree(EGL_Effect ** effect) if (!*effect) return; + bool deletedSampler = false; EGL_EffectPass * pass = (*effect)->passes; while(pass) { EGL_EffectPass * next = pass->next; glDeleteSamplers(1, &pass->sampler); + deletedSampler = true; egl_framebufferFree(&pass->framebuffer); free(pass); pass = next; } + if (deletedSampler) + egl_stateInvalidateShared(); + free(*effect); *effect = NULL; } @@ -215,9 +221,7 @@ EGL_Texture * egl_effectRun(EGL_Effect * effect, EGL_FilterRects * rects, egl_framebufferBind(pass->framebuffer); - glActiveTexture(GL_TEXTURE0); - egl_textureBind(texture); - glBindSampler(0, pass->sampler); + egl_textureBindWithSampler(texture, pass->sampler); egl_uniformMatrix3x2fv(pass->uTransform, 1, GL_FALSE, rects->matrix); egl_uniform2f(pass->uDesktopSize, rects->width, rects->height); diff --git a/client/renderers/EGL/egl.c b/client/renderers/EGL/egl.c index 0b8cd2b8..6ff184ce 100644 --- a/client/renderers/EGL/egl.c +++ b/client/renderers/EGL/egl.c @@ -43,6 +43,7 @@ #include "egl_dynprocs.h" #include "model.h" #include "shader.h" +#include "state.h" #include "damage.h" #include "desktop.h" #include "cursor.h" @@ -298,7 +299,10 @@ static void egl_deinitialize(LG_Renderer * renderer) struct Inst * this = UPCAST(struct Inst, renderer); if (this->imgui) + { ImGui_ImplOpenGL3_Shutdown(); + egl_stateInvalidate(); + } ringbuffer_free(&this->importTimings); @@ -483,13 +487,14 @@ static void egl_update_scale_type(struct Inst * this) void egl_resetViewport(EGL * this) { - glViewport(0, 0, this->width, this->height); + egl_stateViewport(0, 0, this->width, this->height); } static void egl_onResize(LG_Renderer * renderer, const int width, const int height, const double scale, const LG_RendererRect destRect, LG_RendererRotate rotate) { struct Inst * this = UPCAST(struct Inst, renderer); + egl_stateCheckShared(); this->width = width * scale; this->height = height * scale; @@ -501,7 +506,7 @@ static void egl_onResize(LG_Renderer * renderer, const int width, const int heig this->destRect.w = destRect.w * scale; this->destRect.h = destRect.h * scale; - glViewport(0, 0, this->width, this->height); + egl_stateViewport(0, 0, this->width, this->height); if (destRect.valid) { @@ -540,6 +545,7 @@ static void egl_onResize(LG_Renderer * renderer, const int width, const int heig ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplOpenGL3_Init("#version 300 es"); ImGui_ImplOpenGL3_NewFrame(); + egl_stateInvalidate(); egl_damageResize(this->damage, this->translateX, this->translateY, this->scaleX, this->scaleY); egl_desktopResize(this->desktop, this->width, this->height); @@ -602,8 +608,12 @@ static bool egl_onFrameFormat(LG_Renderer * renderer, const LG_RendererFormat fo DEBUG_ERROR("Failed to make the frame context current"); return false; } + + egl_stateInvalidate(); } + egl_stateCheckShared(); + if (likely(this->scalePointer)) { float scale = max(1.0f, (float)format.screenWidth / this->width); @@ -649,6 +659,7 @@ static bool egl_onFrame(LG_Renderer * renderer, const FrameBuffer * frame, int d const FrameDamageRect * damageRects, int damageRectsCount) { struct Inst * this = UPCAST(struct Inst, renderer); + egl_stateCheckShared(); uint64_t start = nanotime(); if (unlikely(!egl_desktopUpdate( @@ -922,6 +933,7 @@ static bool egl_renderStartup(LG_Renderer * renderer, bool useDMA) } eglMakeCurrent(this->display, this->surface, this->surface, this->context); + egl_stateInvalidate(); const char * gl_exts = (const char *)glGetString(GL_EXTENSIONS); if (!gl_exts) { @@ -1038,6 +1050,7 @@ static bool egl_renderStartup(LG_Renderer * renderer, bool useDMA) DEBUG_ERROR("Failed to initialize ImGui"); return false; } + egl_stateInvalidate(); app_overlayConfigRegister("EGL", egl_configUI, this); @@ -1067,7 +1080,7 @@ inline static void renderLetterBox(struct Inst * this) if (hLB || vLB) { glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glEnable(GL_SCISSOR_TEST); + egl_stateScissor(true); if (hLB) { @@ -1094,7 +1107,7 @@ inline static void renderLetterBox(struct Inst * this) glClear(GL_COLOR_BUFFER_BIT); } - glDisable(GL_SCISSOR_TEST); + egl_stateScissor(false); } } @@ -1103,6 +1116,7 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate, void (*preSwap)(void * udata), void * udata) { struct Inst * this = UPCAST(struct Inst, renderer); + egl_stateCheckShared(); EGLint bufferAge = egl_bufferAge(this); bool renderAll = invalidateWindow || this->hadOverlay || bufferAge <= 0 || bufferAge > MAX_BUFFER_AGE || @@ -1213,6 +1227,7 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate, ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplOpenGL3_RenderDrawData(igGetDrawData()); + egl_stateInvalidate(); for (int i = 0; i < damageIdx; ++i) damage[i].y = this->height - damage[i].y - damage[i].h; @@ -1268,9 +1283,12 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate, static void * egl_createTexture(LG_Renderer * renderer, int width, int height, uint8_t * data) { + egl_stateCheckShared(); + GLuint tex; glGenTextures(1, &tex); - glBindTexture(GL_TEXTURE_2D, tex); + egl_stateBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + egl_stateBindTexture(0, GL_TEXTURE_2D, tex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); @@ -1289,8 +1307,6 @@ static void * egl_createTexture(LG_Renderer * renderer, GL_UNSIGNED_BYTE, data); - glBindTexture(GL_TEXTURE_2D, 0); - return (void*)(intptr_t)tex; } @@ -1298,11 +1314,13 @@ static void egl_freeTexture(LG_Renderer * renderer, void * texture) { GLuint tex = (GLuint)(intptr_t)texture; glDeleteTextures(1, &tex); + egl_stateInvalidateShared(); } static void egl_spiceConfigure(LG_Renderer * renderer, int width, int height) { struct Inst * this = UPCAST(struct Inst, renderer); + egl_stateCheckShared(); this->spiceWidth = width; this->spiceHeight = height; egl_desktopSpiceConfigure(this->desktop, width, height); @@ -1312,6 +1330,7 @@ static void egl_spiceDrawFill(LG_Renderer * renderer, int x, int y, int width, int height, uint32_t color) { struct Inst * this = UPCAST(struct Inst, renderer); + egl_stateCheckShared(); egl_desktopSpiceDrawFill(this->desktop, x, y, width, height, color); } @@ -1319,6 +1338,7 @@ static void egl_spiceDrawBitmap(LG_Renderer * renderer, int x, int y, int width, int height, int stride, uint8_t * data, bool topDown) { struct Inst * this = UPCAST(struct Inst, renderer); + egl_stateCheckShared(); egl_desktopSpiceDrawBitmap(this->desktop, x, y, width, height, stride, data, topDown); } diff --git a/client/renderers/EGL/framebuffer.c b/client/renderers/EGL/framebuffer.c index 6d429d92..9b4ec69a 100644 --- a/client/renderers/EGL/framebuffer.c +++ b/client/renderers/EGL/framebuffer.c @@ -19,6 +19,7 @@ */ #include "framebuffer.h" +#include "state.h" #include "texture.h" #include @@ -62,6 +63,7 @@ void egl_framebufferFree(EGL_Framebuffer ** fb) egl_textureFree(&this->tex); glDeleteFramebuffers(1, &this->fbo); + egl_stateInvalidate(); free(this); *fb = NULL; } @@ -78,11 +80,11 @@ bool egl_framebufferSetup(EGL_Framebuffer * this, enum EGL_PixelFormat pixFmt, GLuint tex; egl_textureGet(this->tex, &tex, NULL, NULL, NULL); - glBindTexture(GL_TEXTURE_2D, tex); + egl_stateBindTexture(0, GL_TEXTURE_2D, tex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glBindFramebuffer(GL_FRAMEBUFFER, this->fbo); + egl_stateBindFramebuffer(this->fbo); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0); glDrawBuffers(1, &(GLenum){GL_COLOR_ATTACHMENT0}); @@ -90,19 +92,20 @@ bool egl_framebufferSetup(EGL_Framebuffer * this, enum EGL_PixelFormat pixFmt, GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { - glBindFramebuffer(GL_FRAMEBUFFER, 0); + egl_stateBindFramebuffer(0); DEBUG_ERROR("Failed to setup the framebuffer: 0x%x", status); return false; } - glBindFramebuffer(GL_FRAMEBUFFER, 0); + egl_stateBindFramebuffer(0); return true; } void egl_framebufferBind(EGL_Framebuffer * this) { - glBindFramebuffer(GL_FRAMEBUFFER, this->fbo); - glViewport(0, 0, this->tex->format.width, this->tex->format.height); + egl_stateBindFramebuffer(this->fbo); + egl_stateViewport(0, 0, + this->tex->format.width, this->tex->format.height); } EGL_Texture * egl_framebufferGetTexture(EGL_Framebuffer * this) diff --git a/client/renderers/EGL/model.c b/client/renderers/EGL/model.c index 438ee340..a6d041cd 100644 --- a/client/renderers/EGL/model.c +++ b/client/renderers/EGL/model.c @@ -20,6 +20,7 @@ #include "model.h" #include "shader.h" +#include "state.h" #include "texture.h" #include "common/debug.h" @@ -88,6 +89,8 @@ void egl_modelFree(EGL_Model ** model) if ((*model)->vao) glDeleteVertexArrays(1, &(*model)->vao); + egl_stateInvalidateShared(); + free(*model); *model = NULL; } @@ -169,16 +172,19 @@ void egl_modelRender(EGL_Model * model) if (model->rebuild) { if (model->buffer) + { glDeleteBuffers(1, &model->buffer); + egl_stateInvalidateShared(); + } if (!model->vao) glGenVertexArrays(1, &model->vao); - glBindVertexArray(model->vao); + egl_stateBindVertexArray(model->vao); /* create a buffer large enough */ glGenBuffers(1, &model->buffer); - glBindBuffer(GL_ARRAY_BUFFER, model->buffer); + egl_stateBindBuffer(GL_ARRAY_BUFFER, model->buffer); glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * (model->vertexCount * 5), NULL, GL_STATIC_DRAW); GLintptr offset = 0; @@ -206,12 +212,10 @@ void egl_modelRender(EGL_Model * model) glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, (void*)(sizeof(GLfloat) * model->vertexCount * 3)); - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindVertexArray(0); model->rebuild = false; } - glBindVertexArray(model->vao); + egl_stateBindVertexArray(model->vao); if (model->shader) egl_shaderUse(model->shader); @@ -230,10 +234,6 @@ void egl_modelRender(EGL_Model * model) } ll_unlock(model->verticies); - /* unbind and cleanup */ - glBindTexture(GL_TEXTURE_2D, 0); - glBindVertexArray(0); - glUseProgram(0); } void egl_modelSetShader(EGL_Model * model, EGL_Shader * shader) diff --git a/client/renderers/EGL/shader.c b/client/renderers/EGL/shader.c index b22dfc2a..6c67315f 100644 --- a/client/renderers/EGL/shader.c +++ b/client/renderers/EGL/shader.c @@ -19,6 +19,7 @@ */ #include "shader.h" +#include "state.h" #include "common/debug.h" #include "common/stringutils.h" #include "util.h" @@ -78,7 +79,10 @@ void egl_shaderFree(EGL_Shader ** shader) return; if (this->hasShader) + { glDeleteProgram(this->shader); + egl_stateInvalidateShared(); + } EGL_Uniform * uniform = this->uniforms; while (uniform) @@ -161,6 +165,7 @@ static bool shaderCompile(EGL_Shader * this, const char * vertex_code, if (this->hasShader) { glDeleteProgram(this->shader); + egl_stateInvalidateShared(); this->hasShader = false; } @@ -714,7 +719,7 @@ void egl_shaderUse(EGL_Shader * this) return; } - glUseProgram(this->shader); + egl_stateUseProgram(this->shader); while(this->dirtyUniforms) { diff --git a/client/renderers/EGL/state.c b/client/renderers/EGL/state.c new file mode 100644 index 00000000..8246bcd2 --- /dev/null +++ b/client/renderers/EGL/state.c @@ -0,0 +1,55 @@ +/** + * 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 "state.h" + +#include +#include + +_Thread_local EGL_StateCache g_eglState; +static _Thread_local unsigned int localGeneration; +static atomic_uint sharedGeneration = 1; + +static void stateInvalidate(unsigned int generation) +{ + memset(&g_eglState, 0, sizeof(g_eglState)); + localGeneration = generation; +} + +void egl_stateInvalidate(void) +{ + stateInvalidate(atomic_load_explicit( + &sharedGeneration, memory_order_relaxed)); +} + +void egl_stateInvalidateShared(void) +{ + const unsigned int generation = atomic_fetch_add_explicit( + &sharedGeneration, 1, memory_order_release) + 1; + stateInvalidate(generation); +} + +void egl_stateCheckShared(void) +{ + const unsigned int generation = atomic_load_explicit( + &sharedGeneration, memory_order_acquire); + if (localGeneration != generation) + stateInvalidate(generation); +} diff --git a/client/renderers/EGL/state.h b/client/renderers/EGL/state.h new file mode 100644 index 00000000..2f68e3d8 --- /dev/null +++ b/client/renderers/EGL/state.h @@ -0,0 +1,266 @@ +/** + * 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 + +#include +#include + +#define EGL_STATE_TEXTURE_UNITS 16 + +/* + * GL bindings are context-local and the renderer keeps each context on its + * own thread, so the cache is thread-local too. Code that changes tracked GL + * state without these helpers must invalidate the cache afterwards. + */ + +typedef struct EGL_StateBinding +{ + GLuint value; + bool valid; +} +EGL_StateBinding; + +typedef struct EGL_StateTextureUnit +{ + EGL_StateBinding texture2D; + EGL_StateBinding textureExternal; + EGL_StateBinding sampler; +} +EGL_StateTextureUnit; + +typedef struct EGL_StateCache +{ + EGL_StateBinding program; + EGL_StateBinding framebuffer; + EGL_StateBinding vertexArray; + EGL_StateBinding arrayBuffer; + EGL_StateBinding pixelUnpackBuffer; + EGL_StateBinding activeTexture; + EGL_StateTextureUnit textureUnit[EGL_STATE_TEXTURE_UNITS]; + + bool viewportValid; + GLint viewportX; + GLint viewportY; + GLsizei viewportWidth; + GLsizei viewportHeight; + + bool blendValid; + bool blend; + bool blendFuncValid; + GLenum blendSrc; + GLenum blendDst; + + bool scissorValid; + bool scissor; +} +EGL_StateCache; + +extern _Thread_local EGL_StateCache g_eglState; + +/* Invalidates only the current context/thread cache. */ +void egl_stateInvalidate(void); + +/* Invalidates all context caches at their next synchronization point. */ +void egl_stateInvalidateShared(void); +void egl_stateCheckShared(void); + +static inline void egl_stateUseProgram(GLuint program) +{ + if (g_eglState.program.valid && g_eglState.program.value == program) + return; + + glUseProgram(program); + g_eglState.program.value = program; + g_eglState.program.valid = true; +} + +static inline void egl_stateBindFramebuffer(GLuint framebuffer) +{ + if (g_eglState.framebuffer.valid && + g_eglState.framebuffer.value == framebuffer) + return; + + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); + g_eglState.framebuffer.value = framebuffer; + g_eglState.framebuffer.valid = true; +} + +static inline void egl_stateBindVertexArray(GLuint vertexArray) +{ + if (g_eglState.vertexArray.valid && + g_eglState.vertexArray.value == vertexArray) + return; + + glBindVertexArray(vertexArray); + g_eglState.vertexArray.value = vertexArray; + g_eglState.vertexArray.valid = true; +} + +static inline void egl_stateBindBuffer(GLenum target, GLuint buffer) +{ + EGL_StateBinding * binding; + switch(target) + { + case GL_ARRAY_BUFFER: + binding = &g_eglState.arrayBuffer; + break; + + case GL_PIXEL_UNPACK_BUFFER: + binding = &g_eglState.pixelUnpackBuffer; + break; + + /* GL_ELEMENT_ARRAY_BUFFER belongs to the currently bound VAO. */ + default: + glBindBuffer(target, buffer); + return; + } + + if (binding->valid && binding->value == buffer) + return; + + glBindBuffer(target, buffer); + binding->value = buffer; + binding->valid = true; +} + +static inline bool egl_stateActiveTextureUnit(GLuint unit) +{ + const GLenum texture = GL_TEXTURE0 + unit; + if (!g_eglState.activeTexture.valid || + g_eglState.activeTexture.value != texture) + { + glActiveTexture(texture); + g_eglState.activeTexture.value = texture; + g_eglState.activeTexture.valid = true; + } + + return unit < EGL_STATE_TEXTURE_UNITS; +} + +static inline void egl_stateBindTexture(GLuint unit, GLenum target, + GLuint texture) +{ + if (!egl_stateActiveTextureUnit(unit)) + { + glBindTexture(target, texture); + return; + } + + EGL_StateBinding * binding; + switch(target) + { + case GL_TEXTURE_2D: + binding = &g_eglState.textureUnit[unit].texture2D; + break; + + case GL_TEXTURE_EXTERNAL_OES: + binding = &g_eglState.textureUnit[unit].textureExternal; + break; + + default: + glBindTexture(target, texture); + return; + } + + if (binding->valid && binding->value == texture) + return; + + glBindTexture(target, texture); + binding->value = texture; + binding->valid = true; +} + +static inline void egl_stateBindSampler(GLuint unit, GLuint sampler) +{ + if (unit >= EGL_STATE_TEXTURE_UNITS) + { + glBindSampler(unit, sampler); + return; + } + + EGL_StateBinding * binding = &g_eglState.textureUnit[unit].sampler; + if (binding->valid && binding->value == sampler) + return; + + glBindSampler(unit, sampler); + binding->value = sampler; + binding->valid = true; +} + +static inline void egl_stateViewport(GLint x, GLint y, GLsizei width, + GLsizei height) +{ + if (g_eglState.viewportValid && + g_eglState.viewportX == x && + g_eglState.viewportY == y && + g_eglState.viewportWidth == width && + g_eglState.viewportHeight == height) + return; + + glViewport(x, y, width, height); + g_eglState.viewportX = x; + g_eglState.viewportY = y; + g_eglState.viewportWidth = width; + g_eglState.viewportHeight = height; + g_eglState.viewportValid = true; +} + +static inline void egl_stateBlend(bool enabled) +{ + if (g_eglState.blendValid && g_eglState.blend == enabled) + return; + + if (enabled) + glEnable(GL_BLEND); + else + glDisable(GL_BLEND); + + g_eglState.blend = enabled; + g_eglState.blendValid = true; +} + +static inline void egl_stateBlendFunc(GLenum src, GLenum dst) +{ + if (g_eglState.blendFuncValid && + g_eglState.blendSrc == src && g_eglState.blendDst == dst) + return; + + glBlendFunc(src, dst); + g_eglState.blendSrc = src; + g_eglState.blendDst = dst; + g_eglState.blendFuncValid = true; +} + +static inline void egl_stateScissor(bool enabled) +{ + if (g_eglState.scissorValid && g_eglState.scissor == enabled) + return; + + if (enabled) + glEnable(GL_SCISSOR_TEST); + else + glDisable(GL_SCISSOR_TEST); + + g_eglState.scissor = enabled; + g_eglState.scissorValid = true; +} diff --git a/client/renderers/EGL/texture.c b/client/renderers/EGL/texture.c index 8b09b491..2f902357 100644 --- a/client/renderers/EGL/texture.c +++ b/client/renderers/EGL/texture.c @@ -19,6 +19,7 @@ */ #include "texture.h" +#include "state.h" #include #include @@ -88,6 +89,7 @@ void egl_textureFree(EGL_Texture ** tex) return; glDeleteSamplers(1, &this->sampler); + egl_stateInvalidateShared(); this->ops.free(this); *tex = NULL; @@ -207,7 +209,12 @@ enum EGL_TexStatus egl_textureProcess(EGL_Texture * this) enum EGL_TexStatus egl_textureBind(EGL_Texture * this) { - glActiveTexture(GL_TEXTURE0); - glBindSampler(0, this->sampler); + return egl_textureBindWithSampler(this, this->sampler); +} + +enum EGL_TexStatus egl_textureBindWithSampler(EGL_Texture * this, + GLuint sampler) +{ + egl_stateBindSampler(0, sampler); return this->ops.bind(this); } diff --git a/client/renderers/EGL/texture.h b/client/renderers/EGL/texture.h index 2e9c59d9..1988785d 100644 --- a/client/renderers/EGL/texture.h +++ b/client/renderers/EGL/texture.h @@ -145,6 +145,8 @@ static inline EGL_TexStatus egl_textureGet(EGL_Texture * texture, GLuint * tex, } enum EGL_TexStatus egl_textureBind(EGL_Texture * texture); +enum EGL_TexStatus egl_textureBindWithSampler(EGL_Texture * texture, + GLuint sampler); typedef void * PostProcessHandle; PostProcessHandle egl_textureAddFilter(EGL_Texture * texture, diff --git a/client/renderers/EGL/texture_buffer.c b/client/renderers/EGL/texture_buffer.c index 535163fc..b2bc54e1 100644 --- a/client/renderers/EGL/texture_buffer.c +++ b/client/renderers/EGL/texture_buffer.c @@ -19,6 +19,7 @@ */ #include "texture_buffer.h" +#include "state.h" #include "egldebug.h" @@ -35,7 +36,10 @@ static void egl_texBuffer_cleanup(TextureBuffer * this) egl_texUtilFreeBuffers(this->buf, this->texCount); if (this->tex[0]) + { glDeleteTextures(this->texCount, this->tex); + egl_stateInvalidateShared(); + } for (int i = 0; i < EGL_TEX_BUFFER_MAX; ++i) { @@ -89,9 +93,10 @@ bool egl_texBufferSetup(EGL_Texture * texture, const EGL_TexSetup * setup) egl_texBuffer_cleanup(this); glGenTextures(this->texCount, this->tex); + egl_stateBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); for(int i = 0; i < this->texCount; ++i) { - glBindTexture(GL_TEXTURE_2D, this->tex[i]); + egl_stateBindTexture(0, GL_TEXTURE_2D, this->tex[i]); glTexImage2D(GL_TEXTURE_2D, 0, texture->format.intFormat, @@ -103,7 +108,6 @@ bool egl_texBufferSetup(EGL_Texture * texture, const EGL_TexSetup * setup) NULL); } - glBindTexture(GL_TEXTURE_2D, 0); this->bufIndex = 0; this->rIndex = -1; @@ -118,7 +122,8 @@ static bool egl_texBufferUpdate(EGL_Texture * texture, const EGL_TexUpdate * upd TextureBuffer * this = UPCAST(TextureBuffer, texture); DEBUG_ASSERT(update->type == EGL_TEXTYPE_BUFFER); - glBindTexture(GL_TEXTURE_2D, this->tex[0]); + egl_stateBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + egl_stateBindTexture(0, GL_TEXTURE_2D, this->tex[0]); glPixelStorei(GL_UNPACK_ROW_LENGTH, update->stride); glTexSubImage2D(GL_TEXTURE_2D, 0, @@ -129,8 +134,6 @@ static bool egl_texBufferUpdate(EGL_Texture * texture, const EGL_TexUpdate * upd texture->format.format, texture->format.dataType, update->buffer); - glBindTexture(GL_TEXTURE_2D, 0); - return true; } @@ -292,8 +295,8 @@ EGL_TexStatus egl_texBufferStreamProcess(EGL_Texture * texture) if (!keepLocked) LG_UNLOCK(this->copyLock); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer->pbo); - glBindTexture(GL_TEXTURE_2D, tex); + egl_stateBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer->pbo); + egl_stateBindTexture(0, GL_TEXTURE_2D, tex); glPixelStorei(GL_UNPACK_ROW_LENGTH, texture->format.stride); glTexSubImage2D(GL_TEXTURE_2D, @@ -303,9 +306,6 @@ EGL_TexStatus egl_texBufferStreamProcess(EGL_Texture * texture) texture->format.format, texture->format.dataType, (const void *)0); - glBindTexture(GL_TEXTURE_2D, 0); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - this->sync[index] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); if (unlikely(!this->sync[index])) { @@ -348,7 +348,7 @@ EGL_TexStatus egl_texBufferBind(EGL_Texture * texture) if ((status = texture->ops.get(texture, &tex, NULL)) != EGL_TEX_STATUS_OK) return status; - glBindTexture(GL_TEXTURE_2D, tex); + egl_stateBindTexture(0, GL_TEXTURE_2D, tex); return EGL_TEX_STATUS_OK; } diff --git a/client/renderers/EGL/texture_dmabuf.c b/client/renderers/EGL/texture_dmabuf.c index 88568cce..20cb6076 100644 --- a/client/renderers/EGL/texture_dmabuf.c +++ b/client/renderers/EGL/texture_dmabuf.c @@ -20,6 +20,7 @@ #include "texture.h" #include "texture_buffer.h" +#include "state.h" #include "util.h" #include "common/array.h" @@ -82,7 +83,10 @@ static void egl_texDMABUFCleanup(EGL_Texture * texture) egl_texUtilFreeBuffers(parent->buf, parent->texCount); if (parent->tex[0]) + { glDeleteTextures(parent->texCount, parent->tex); + egl_stateInvalidateShared(); + } } // dmabuf functions @@ -245,7 +249,7 @@ static bool egl_texDMABUFUpdate(EGL_Texture * texture, fdImage->texIndex = slot; INTERLOCKED_SECTION(parent->copyLock, { - glBindTexture(GL_TEXTURE_EXTERNAL_OES, parent->tex[slot]); + egl_stateBindTexture(0, GL_TEXTURE_EXTERNAL_OES, parent->tex[slot]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR); @@ -332,7 +336,7 @@ static EGL_TexStatus egl_texDMABUFBind(EGL_Texture * texture) if ((status = egl_texDMABUFGet(texture, &tex, NULL)) != EGL_TEX_STATUS_OK) return status; - glBindTexture(GL_TEXTURE_EXTERNAL_OES, tex); + egl_stateBindTexture(0, GL_TEXTURE_EXTERNAL_OES, tex); return EGL_TEX_STATUS_OK; } diff --git a/client/renderers/EGL/texture_util.c b/client/renderers/EGL/texture_util.c index bac471e3..5b148da6 100644 --- a/client/renderers/EGL/texture_util.c +++ b/client/renderers/EGL/texture_util.c @@ -19,6 +19,7 @@ */ #include "texture.h" +#include "state.h" #include "texture_util.h" #include @@ -112,7 +113,7 @@ bool egl_texUtilGenBuffers(const EGL_TexFormat * fmt, EGL_TexBuffer * buffers, buffer->size = fmt->dataSize; glGenBuffers(1, &buffer->pbo); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer->pbo); + egl_stateBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer->pbo); g_egl_dynProcs.glBufferStorageEXT( GL_PIXEL_UNPACK_BUFFER, fmt->dataSize, @@ -131,6 +132,7 @@ bool egl_texUtilGenBuffers(const EGL_TexFormat * fmt, EGL_TexBuffer * buffers, void egl_texUtilFreeBuffers(EGL_TexBuffer * buffers, int count) { + bool deleted = false; for(int i = 0; i < count; ++i) { EGL_TexBuffer *buffer = &buffers[i]; @@ -140,13 +142,17 @@ void egl_texUtilFreeBuffers(EGL_TexBuffer * buffers, int count) egl_texUtilUnmapBuffer(buffer); glDeleteBuffers(1, &buffer->pbo); + deleted = true; buffer->pbo = 0; } + + if (deleted) + egl_stateInvalidateShared(); } bool egl_texUtilMapBuffer(EGL_TexBuffer * buffer) { - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer->pbo); + egl_stateBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer->pbo); buffer->map = glMapBufferRange( GL_PIXEL_UNPACK_BUFFER, 0, @@ -160,7 +166,7 @@ bool egl_texUtilMapBuffer(EGL_TexBuffer * buffer) if (!buffer->map) DEBUG_GL_ERROR("glMapBufferRange failed of %lu bytes", buffer->size); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + egl_stateBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); return buffer->map; } @@ -169,8 +175,8 @@ void egl_texUtilUnmapBuffer(EGL_TexBuffer * buffer) if (!buffer->map) return; - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer->pbo); + egl_stateBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer->pbo); glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + egl_stateBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); buffer->map = NULL; }