From ff4a9025bedda910ca7edf9627edf958618226ea Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Fri, 19 Dec 2025 18:13:13 -0500 Subject: [PATCH 1/2] reuse render ops for less garbage collection --- src/core/renderers/webgl/WebGlRenderOp.ts | 32 ++++++++-------- src/core/renderers/webgl/WebGlRenderer.ts | 44 +++++++++++++++++++--- src/core/text-rendering/SdfTextRenderer.ts | 7 +--- 3 files changed, 58 insertions(+), 25 deletions(-) diff --git a/src/core/renderers/webgl/WebGlRenderOp.ts b/src/core/renderers/webgl/WebGlRenderOp.ts index 4819d072..e427cccc 100644 --- a/src/core/renderers/webgl/WebGlRenderOp.ts +++ b/src/core/renderers/webgl/WebGlRenderOp.ts @@ -35,7 +35,7 @@ type ReqQuad = | 'clippingRect' | 'height' | 'width'; -type RenderOpQuadOptions = Pick & +export type RenderOpQuadOptions = Pick & Partial> & { sdfShaderProps?: Record; sdfBuffers?: BufferCollection; @@ -52,26 +52,28 @@ export class WebGlRenderOp extends CoreRenderOp { /** * need to improve this when TextRenderers are refactored */ - readonly sdfShaderProps: Record | undefined; - readonly sdfNode: CoreTextNode | undefined; - readonly maxTextures: number; - readonly buffers: BufferCollection; - readonly shader: WebGlShaderNode; - readonly width: number; - readonly height: number; - readonly clippingRect: RectWithValid; - readonly rtt: boolean; - readonly parentHasRenderTexture: boolean; - readonly framebufferDimensions?: Dimensions | null; - readonly alpha: number; - readonly pixelRatio: number; + sdfShaderProps: Record | undefined; + sdfNode: CoreTextNode | undefined; + maxTextures: number; + buffers: BufferCollection; + shader: WebGlShaderNode; + width: number; + height: number; + clippingRect: RectWithValid; + rtt: boolean; + parentHasRenderTexture: boolean; + framebufferDimensions?: Dimensions | null; + alpha: number; + pixelRatio: number; + bufferIdx: number; constructor( readonly renderer: WebGlRenderer, quad: RenderOpQuadOptions, - readonly bufferIdx: number, + bufferIdx: number, ) { super(); + this.bufferIdx = bufferIdx; this.buffers = quad.sdfBuffers || renderer.quadBufferCollection; this.shader = quad.shader as WebGlShaderNode; this.width = quad.width; diff --git a/src/core/renderers/webgl/WebGlRenderer.ts b/src/core/renderers/webgl/WebGlRenderer.ts index 9308983a..4feae258 100644 --- a/src/core/renderers/webgl/WebGlRenderer.ts +++ b/src/core/renderers/webgl/WebGlRenderer.ts @@ -24,7 +24,7 @@ import { type CoreRendererOptions, type QuadOptions, } from '../CoreRenderer.js'; -import { WebGlRenderOp } from './WebGlRenderOp.js'; +import { WebGlRenderOp, type RenderOpQuadOptions } from './WebGlRenderOp.js'; import type { CoreContextTexture } from '../CoreContextTexture.js'; import { createIndexBuffer, @@ -71,6 +71,7 @@ export class WebGlRenderer extends CoreRenderer { fQuadBuffer: Float32Array; uiQuadBuffer: Uint32Array; renderOps: WebGlRenderOp[] = []; + renderOpPool: WebGlRenderOp[] = []; //// Render Op / Buffer Filling State curBufferIdx = 0; @@ -188,6 +189,7 @@ export class WebGlRenderer extends CoreRenderer { const { glw } = this; this.curBufferIdx = 0; this.curRenderOp = null; + this.renderOpPool.push(...this.renderOps); this.renderOps.length = 0; glw.setScissorTest(false); glw.clear(); @@ -261,7 +263,7 @@ export class WebGlRenderer extends CoreRenderer { let ro = this.curRenderOp!; const reuse = this.reuseRenderOp(params) === false; if (reuse) { - this.newRenderOp(params, i); + this.createRenderOp(params, i); ro = this.curRenderOp!; } @@ -331,10 +333,42 @@ export class WebGlRenderer extends CoreRenderer { * @param shader * @param bufferIdx */ - private newRenderOp(quad: QuadOptions | WebGlRenderOp, bufferIdx: number) { - const curRenderOp = new WebGlRenderOp(this, quad, bufferIdx); + public createRenderOp( + quad: RenderOpQuadOptions | WebGlRenderOp, + bufferIdx: number, + ): WebGlRenderOp { + let curRenderOp = this.renderOpPool.pop(); + if (curRenderOp) { + curRenderOp.bufferIdx = bufferIdx; + curRenderOp.buffers = + (quad as RenderOpQuadOptions).sdfBuffers || this.quadBufferCollection; + curRenderOp.shader = quad.shader as WebGlShaderNode; + curRenderOp.width = quad.width; + curRenderOp.height = quad.height; + curRenderOp.clippingRect = quad.clippingRect; + curRenderOp.parentHasRenderTexture = quad.parentHasRenderTexture || false; + curRenderOp.framebufferDimensions = quad.framebufferDimensions; + curRenderOp.rtt = quad.rtt || false; + curRenderOp.alpha = quad.alpha; + curRenderOp.pixelRatio = + curRenderOp.parentHasRenderTexture === true ? 1 : this.stage.pixelRatio; + curRenderOp.sdfShaderProps = (quad as RenderOpQuadOptions).sdfShaderProps; + curRenderOp.maxTextures = curRenderOp.shader.program + .supportsIndexedTextures + ? (this.glw.getParameter( + this.glw.MAX_VERTEX_TEXTURE_IMAGE_UNITS, + ) as number) + : 1; + + curRenderOp.numQuads = 0; + curRenderOp.textures.length = 0; + } else { + curRenderOp = new WebGlRenderOp(this, quad, bufferIdx); + } + this.curRenderOp = curRenderOp; this.renderOps.push(curRenderOp); + return curRenderOp; } /** @@ -360,7 +394,7 @@ export class WebGlRenderer extends CoreRenderer { if (recursive) { throw new Error('Unable to add texture to render op'); } - this.newRenderOp(this.curRenderOp!, bufferIdx); + this.createRenderOp(this.curRenderOp!, bufferIdx); return this.addTexture(texture, bufferIdx, true); } return textureIdx; diff --git a/src/core/text-rendering/SdfTextRenderer.ts b/src/core/text-rendering/SdfTextRenderer.ts index 4d401fa3..b56188c6 100644 --- a/src/core/text-rendering/SdfTextRenderer.ts +++ b/src/core/text-rendering/SdfTextRenderer.ts @@ -29,7 +29,7 @@ import { hasZeroWidthSpace } from './Utils.js'; import * as SdfFontHandler from './SdfFontHandler.js'; import type { CoreRenderer } from '../renderers/CoreRenderer.js'; import { WebGlRenderer } from '../renderers/webgl/WebGlRenderer.js'; -import { WebGlRenderOp } from '../renderers/webgl/WebGlRenderOp.js'; + import { Sdf, type SdfShaderProps } from '../shaders/webgl/SdfShader.js'; import { BufferCollection } from '../renderers/webgl/internal/BufferCollection.js'; import type { WebGlCtxTexture } from '../renderers/webgl/WebGlCtxTexture.js'; @@ -238,8 +238,7 @@ const renderQuads = ( glw.arrayBufferData(buffer, vertexBuffer, glw.STATIC_DRAW as number); } - const renderOp = new WebGlRenderOp( - renderer as WebGlRenderer, + const renderOp = (renderer as WebGlRenderer).createRenderOp( { sdfShaderProps: { transform: globalTransform, @@ -265,8 +264,6 @@ const renderQuads = ( // Add atlas texture and set quad count renderOp.addTexture(atlasTexture.ctxTexture as WebGlCtxTexture); renderOp.numQuads = layout.glyphs.length; - - (renderer as WebGlRenderer).addRenderOp(renderOp); }; /** From 1aee19cfc77c89729f2476eb0c4f2860b63a5361 Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Mon, 12 Jan 2026 21:15:29 -0500 Subject: [PATCH 2/2] fix merge by exporting type --- src/core/renderers/webgl/WebGlRenderOp.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/renderers/webgl/WebGlRenderOp.ts b/src/core/renderers/webgl/WebGlRenderOp.ts index 10023885..edab0c41 100644 --- a/src/core/renderers/webgl/WebGlRenderOp.ts +++ b/src/core/renderers/webgl/WebGlRenderOp.ts @@ -36,7 +36,7 @@ type ReqQuad = | 'height' | 'width' | 'time'; -type RenderOpQuadOptions = Pick & +export type RenderOpQuadOptions = Pick & Partial> & { sdfShaderProps?: Record; sdfBuffers?: BufferCollection;