From 24bd8ecb299b1be7001a872671dbbe807abd446b Mon Sep 17 00:00:00 2001 From: jfboeve Date: Tue, 24 Jun 2025 10:48:27 +0200 Subject: [PATCH 1/3] read / cache all uniform locations per program --- src/core/lib/WebGlContextWrapper.ts | 140 +++++++++--------- .../renderers/webgl/WebGlShaderProgram.ts | 51 +++++-- 2 files changed, 103 insertions(+), 88 deletions(-) diff --git a/src/core/lib/WebGlContextWrapper.ts b/src/core/lib/WebGlContextWrapper.ts index bdc1fab5..6a27376b 100644 --- a/src/core/lib/WebGlContextWrapper.ts +++ b/src/core/lib/WebGlContextWrapper.ts @@ -50,6 +50,7 @@ export class WebGlContextWrapper { private boundArrayBuffer: WebGLBuffer | null; private boundElementArrayBuffer: WebGLBuffer | null; private curProgram: WebGLProgram | null; + private curUniformLocations: Record = {}; //#endregion Cached WebGL State //#region Canvas @@ -689,16 +690,21 @@ export class WebGlContextWrapper { * @param program * @returns object with numbers */ - getUniformLocations(program: WebGLProgram): Record { + getUniformLocations( + program: WebGLProgram, + ): Record { const gl = this.gl; const length = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS, ) as number; - const result = {} as Record; + const result = {} as Record; for (let i = 0; i < length; i++) { const { name } = gl.getActiveUniform(program, i) as WebGLActiveInfo; - result[name] = i; + result[name] = gl.getUniformLocation( + program, + name, + ) as WebGLUniformLocation; } return result; } @@ -730,12 +736,16 @@ export class WebGlContextWrapper { * @param program * @returns */ - useProgram(program: WebGLProgram | null) { + useProgram( + program: WebGLProgram | null, + uniformLocations: Record, + ) { if (this.curProgram === program) { return; } this.gl.useProgram(program); this.curProgram = program; + this.curUniformLocations = uniformLocations; } /** @@ -745,10 +755,7 @@ export class WebGlContextWrapper { * @param v0 - The value to set. */ uniform1f(location: string, v0: number) { - this.gl.uniform1f( - this.gl.getUniformLocation(this.curProgram!, location), - v0, - ); + this.gl.uniform1f(this.curUniformLocations[location] || null, v0); } /** @@ -758,10 +765,7 @@ export class WebGlContextWrapper { * @param value - The array of values to set. */ uniform1fv(location: string, value: Float32Array) { - this.gl.uniform1fv( - this.gl.getUniformLocation(this.curProgram!, location), - value, - ); + this.gl.uniform1fv(this.curUniformLocations[location] || null, value); } /** @@ -771,10 +775,7 @@ export class WebGlContextWrapper { * @param v0 - The value to set. */ uniform1i(location: string, v0: number) { - this.gl.uniform1i( - this.gl.getUniformLocation(this.curProgram!, location), - v0, - ); + this.gl.uniform1i(this.curUniformLocations[location] || null, v0); } /** @@ -784,10 +785,7 @@ export class WebGlContextWrapper { * @param value - The array of values to set. */ uniform1iv(location: string, value: Int32Array) { - this.gl.uniform1iv( - this.gl.getUniformLocation(this.curProgram!, location), - value, - ); + this.gl.uniform1iv(this.curUniformLocations[location] || null, value); } /** @@ -798,11 +796,7 @@ export class WebGlContextWrapper { * @param v1 - The second component of the vector. */ uniform2f(location: string, v0: number, v1: number) { - this.gl.uniform2f( - this.gl.getUniformLocation(this.curProgram!, location), - v0, - v1, - ); + this.gl.uniform2f(this.curUniformLocations[location] || null, v0, v1); } /** @@ -813,7 +807,7 @@ export class WebGlContextWrapper { */ uniform2fa(location: string, value: Vec2) { this.gl.uniform2f( - this.gl.getUniformLocation(this.curProgram!, location), + this.curUniformLocations[location] || null, value[0], value[1], ); @@ -826,10 +820,7 @@ export class WebGlContextWrapper { * @param value - The array of vec2 values to set. */ uniform2fv(location: string, value: Float32Array) { - this.gl.uniform2fv( - this.gl.getUniformLocation(this.curProgram!, location), - value, - ); + this.gl.uniform2fv(this.curUniformLocations[location] || null, value); } /** @@ -840,11 +831,7 @@ export class WebGlContextWrapper { * @param v1 - The second component of the vector. */ uniform2i(location: string, v0: number, v1: number) { - this.gl.uniform2i( - this.gl.getUniformLocation(this.curProgram!, location), - v0, - v1, - ); + this.gl.uniform2i(this.curUniformLocations[location] || null, v0, v1); } /** @@ -854,10 +841,7 @@ export class WebGlContextWrapper { * @param value - The array of ivec2 values to set. */ uniform2iv(location: string, value: Int32Array) { - this.gl.uniform2iv( - this.gl.getUniformLocation(this.curProgram!, location), - value, - ); + this.gl.uniform2iv(this.curUniformLocations[location] || null, value); } /** @@ -869,12 +853,7 @@ export class WebGlContextWrapper { * @param v2 - The third component of the vector. */ uniform3f(location: string, v0: number, v1: number, v2: number) { - this.gl.uniform3f( - this.gl.getUniformLocation(this.curProgram!, location), - v0, - v1, - v2, - ); + this.gl.uniform3f(this.curUniformLocations[location] || null, v0, v1, v2); } /** @@ -885,7 +864,7 @@ export class WebGlContextWrapper { */ uniform3fa(location: string, value: Vec3) { this.gl.uniform3f( - this.gl.getUniformLocation(this.curProgram!, location), + this.curUniformLocations[location] || null, value[0], value[1], value[2], @@ -899,10 +878,7 @@ export class WebGlContextWrapper { * @param value - The array of vec3 values to set. */ uniform3fv(location: string, value: Float32Array) { - this.gl.uniform3fv( - this.gl.getUniformLocation(this.curProgram!, location), - value, - ); + this.gl.uniform3fv(this.curUniformLocations[location] || null, value); } /** @@ -914,12 +890,7 @@ export class WebGlContextWrapper { * @param v2 - The third component of the vector. */ uniform3i(location: string, v0: number, v1: number, v2: number) { - this.gl.uniform3i( - this.gl.getUniformLocation(this.curProgram!, location), - v0, - v1, - v2, - ); + this.gl.uniform3i(this.curUniformLocations[location] || null, v0, v1, v2); } /** @@ -929,10 +900,7 @@ export class WebGlContextWrapper { * @param value - The array of ivec3 values to set. */ uniform3iv(location: string, value: Int32Array) { - this.gl.uniform3iv( - this.gl.getUniformLocation(this.curProgram!, location), - value, - ); + this.gl.uniform3iv(this.curUniformLocations[location] || null, value); } /** @@ -946,7 +914,7 @@ export class WebGlContextWrapper { */ uniform4f(location: string, v0: number, v1: number, v2: number, v3: number) { this.gl.uniform4f( - this.gl.getUniformLocation(this.curProgram!, location), + this.curUniformLocations[location] || null, v0, v1, v2, @@ -962,7 +930,7 @@ export class WebGlContextWrapper { */ uniform4fa(location: string, value: Vec4) { this.gl.uniform4f( - this.gl.getUniformLocation(this.curProgram!, location), + this.curUniformLocations[location] || null, value[0], value[1], value[2], @@ -977,10 +945,7 @@ export class WebGlContextWrapper { * @param value - The array of vec4 values to set. */ uniform4fv(location: string, value: Float32Array) { - this.gl.uniform4fv( - this.gl.getUniformLocation(this.curProgram!, location), - value, - ); + this.gl.uniform4fv(this.curUniformLocations[location] || null, value); } /** @@ -994,7 +959,7 @@ export class WebGlContextWrapper { */ uniform4i(location: string, v0: number, v1: number, v2: number, v3: number) { this.gl.uniform4i( - this.gl.getUniformLocation(this.curProgram!, location), + this.curUniformLocations[location] || null, v0, v1, v2, @@ -1009,10 +974,7 @@ export class WebGlContextWrapper { * @param value - The array of ivec4 values to set. */ uniform4iv(location: string, value: Int32Array) { - this.gl.uniform4iv( - this.gl.getUniformLocation(this.curProgram!, location), - value, - ); + this.gl.uniform4iv(this.curUniformLocations[location] || null, value); } /** @@ -1024,7 +986,7 @@ export class WebGlContextWrapper { */ uniformMatrix2fv(location: string, value: Float32Array) { this.gl.uniformMatrix2fv( - this.gl.getUniformLocation(this.curProgram!, location), + this.curUniformLocations[location] || null, false, value, ); @@ -1037,7 +999,7 @@ export class WebGlContextWrapper { */ uniformMatrix3fv(location: string, value: Float32Array) { this.gl.uniformMatrix3fv( - this.gl.getUniformLocation(this.curProgram!, location), + this.curUniformLocations[location] || null, false, value, ); @@ -1050,7 +1012,7 @@ export class WebGlContextWrapper { */ uniformMatrix4fv(location: string, value: Float32Array) { this.gl.uniformMatrix4fv( - this.gl.getUniformLocation(this.curProgram!, location), + this.curUniformLocations[location] || null, false, value, ); @@ -1319,6 +1281,36 @@ export class WebGlContextWrapper { deleteShader(shader: WebGLShader) { this.gl.deleteShader(shader); } + + /** + * ``` + * gl.deleteBuffer(buffer); + * ``` + * + * @param buffer - The buffer to delete + */ + deleteBuffer(buffer: WebGLBuffer) { + const { gl } = this; + gl.deleteBuffer(buffer); + + // Reset bound buffers if they match the deleted buffer + if (this.boundArrayBuffer === buffer) { + this.boundArrayBuffer = null; + } + } + + /** + * ``` + * gl.deleteVertexArray(vertexArray); + * ``` + * + * @param vertexArray - The vertex array object to delete + */ + deleteVertexArray(vertexArray: WebGLVertexArrayObject) { + if (this.isWebGl2()) { + (this.gl as WebGL2RenderingContext).deleteVertexArray(vertexArray); + } + } } // prettier-ignore diff --git a/src/core/renderers/webgl/WebGlShaderProgram.ts b/src/core/renderers/webgl/WebGlShaderProgram.ts index 921d01ee..9e94626a 100644 --- a/src/core/renderers/webgl/WebGlShaderProgram.ts +++ b/src/core/renderers/webgl/WebGlShaderProgram.ts @@ -35,8 +35,7 @@ import { } from './internal/ShaderUtils.js'; export class WebGlShaderProgram implements CoreShaderProgram { - protected boundBufferCollection: BufferCollection | null = null; - protected program: WebGLProgram; + protected program: WebGLProgram | null; /** * Vertex Array Object * @@ -47,9 +46,11 @@ export class WebGlShaderProgram implements CoreShaderProgram { protected renderer: WebGlRenderer; protected glw: WebGlContextWrapper; protected attributeLocations: Record; + protected uniformLocations: Record | null; protected lifecycle: Pick; protected useSystemAlpha = false; protected useSystemDimensions = false; + private isDestroyed = false; supportsIndexedTextures = false; constructor( @@ -117,10 +118,10 @@ export class WebGlShaderProgram implements CoreShaderProgram { this.program = program; this.attributeLocations = glw.getAttributeLocations(program); - this.useSystemAlpha = - this.glw.getUniformLocation(program, 'u_alpha') !== null; - this.useSystemDimensions = - this.glw.getUniformLocation(program, 'u_dimensions') !== null; + const uniLocs = (this.uniformLocations = glw.getUniformLocations(program)); + + this.useSystemAlpha = uniLocs['u_alpha'] !== undefined; + this.useSystemDimensions = uniLocs['u_dimensions'] !== undefined; this.lifecycle = { update: config.update, @@ -133,7 +134,7 @@ export class WebGlShaderProgram implements CoreShaderProgram { } disableAttributes() { - const { glw } = this; + const glw = this.glw; const attribs = Object.keys(this.attributeLocations); const attribLen = attribs.length; for (let i = 0; i < attribLen; i++) { @@ -221,13 +222,13 @@ export class WebGlShaderProgram implements CoreShaderProgram { ); } - // if (this.useSystemAlpha) { - this.glw.uniform1f('u_alpha', renderOp.alpha); - // } + if (this.useSystemAlpha === true) { + this.glw.uniform1f('u_alpha', renderOp.alpha); + } - // if (this.useSystemDimensions) { - this.glw.uniform2f('u_dimensions', renderOp.width, renderOp.height); - // } + if (this.useSystemDimensions === true) { + this.glw.uniform2f('u_dimensions', renderOp.width, renderOp.height); + } /**temporary fix to make sdf texts work */ if (renderOp.sdfShaderProps !== undefined) { @@ -306,7 +307,10 @@ export class WebGlShaderProgram implements CoreShaderProgram { } attach(): void { - this.glw.useProgram(this.program); + if (this.isDestroyed === true) { + return; + } + this.glw.useProgram(this.program, this.uniformLocations!); if (this.glw.isWebGl2() && this.vao) { this.glw.bindVertexArray(this.vao); } @@ -315,4 +319,23 @@ export class WebGlShaderProgram implements CoreShaderProgram { detach(): void { this.disableAttributes(); } + + destroy() { + if (this.isDestroyed === true) { + return; + } + const glw = this.glw; + + this.detach(); + + glw.deleteProgram(this.program!); + this.program = null; + this.uniformLocations = null; + + const attribs = Object.keys(this.attributeLocations); + const attribLen = attribs.length; + for (let i = 0; i < attribLen; i++) { + this.glw.deleteBuffer(attribs[i]!); + } + } } From cdaa4a8721460c1e64a5e0e4f906441ef14729a3 Mon Sep 17 00:00:00 2001 From: jfboeve Date: Wed, 25 Jun 2025 11:35:49 +0200 Subject: [PATCH 2/3] make isDestroyed public --- src/core/renderers/webgl/WebGlShaderProgram.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/renderers/webgl/WebGlShaderProgram.ts b/src/core/renderers/webgl/WebGlShaderProgram.ts index 9e94626a..c38da885 100644 --- a/src/core/renderers/webgl/WebGlShaderProgram.ts +++ b/src/core/renderers/webgl/WebGlShaderProgram.ts @@ -50,7 +50,7 @@ export class WebGlShaderProgram implements CoreShaderProgram { protected lifecycle: Pick; protected useSystemAlpha = false; protected useSystemDimensions = false; - private isDestroyed = false; + public isDestroyed = false; supportsIndexedTextures = false; constructor( From 1bd186e68cba24700a385b4a48d90e2b57286ba3 Mon Sep 17 00:00:00 2001 From: jfboeve Date: Wed, 25 Jun 2025 12:45:36 +0200 Subject: [PATCH 3/3] fix array uniforms --- src/core/lib/WebGlContextWrapper.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/lib/WebGlContextWrapper.ts b/src/core/lib/WebGlContextWrapper.ts index 6a27376b..8cd1be36 100644 --- a/src/core/lib/WebGlContextWrapper.ts +++ b/src/core/lib/WebGlContextWrapper.ts @@ -700,7 +700,9 @@ export class WebGlContextWrapper { ) as number; const result = {} as Record; for (let i = 0; i < length; i++) { - const { name } = gl.getActiveUniform(program, i) as WebGLActiveInfo; + const info = gl.getActiveUniform(program, i) as WebGLActiveInfo; + //remove bracket + value from uniform name; + let name = info.name.replace(/\[.*?\]/g, ''); result[name] = gl.getUniformLocation( program, name,