Skip to content

Commit c5e9ef3

Browse files
committed
fix(virtual-bg): avoid GPU/canvas reallocation every frame
- texMaskFiltered, texBlurred1, texBlurred2 were reallocated on every single render() call - reallocating GPU texture every frame is a confirmed overhead on some GPUs/drivers - needs to be only reallocated when the actual size changes - similar with output canvas element - impact is negligible, so more a hardening Assisted-by: ClaudeCode:claude-sonnet-5
1 parent ea53cec commit c5e9ef3

2 files changed

Lines changed: 39 additions & 10 deletions

File tree

‎src/utils/media/effects/virtual-background/VideoStreamBackgroundEffect.js‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,9 @@ export default class VideoStreamBackgroundEffect {
136136
'./vendor/models/selfie_segmenter.tflite',
137137
import.meta.url,
138138
).pathname,
139+
// delegate: 'CPU' was tried as a GPU-load reduction; it
140+
// made things worse (no GPU win, added main-thread
141+
// stalls). Keeping GPU.
139142
delegate: 'GPU',
140143
},
141144
runningMode: 'VIDEO',
@@ -486,8 +489,13 @@ export default class VideoStreamBackgroundEffect {
486489
return
487490
}
488491

489-
this._outputCanvasElement.width = width
490-
this._outputCanvasElement.height = height
492+
// Assigning canvas dimensions clears and reallocates its backing
493+
// buffer even when the values are unchanged, so only set them on
494+
// an actual resize.
495+
if (this._outputCanvasElement.width !== width || this._outputCanvasElement.height !== height) {
496+
this._outputCanvasElement.width = width
497+
this._outputCanvasElement.height = height
498+
}
491499

492500
if (this._useWebGL) {
493501
if (!this._glFx) {

‎src/utils/media/effects/virtual-background/WebGLCompositor.js‎

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,14 @@ export default class WebGLCompositor {
291291
this.bgScale = [1.0, 1.0]
292292
this.lastOutW = 0
293293
this.lastOutH = 0
294+
295+
// Tracks the size texMaskFiltered/texBlurred{1,2} are currently
296+
// allocated at, so their storage is only reallocated on resize
297+
// instead of on every render() call.
298+
this._maskFilteredW = 0
299+
this._maskFilteredH = 0
300+
this._blurW = 0
301+
this._blurH = 0
294302
}
295303

296304
/**
@@ -527,11 +535,17 @@ export default class WebGLCompositor {
527535
const texelWidth = 1 / blurWidth
528536
const texelHeight = 1 / blurHeight
529537

530-
// Allocate blur textures
531-
gl.bindTexture(gl.TEXTURE_2D, this.texBlurred1)
532-
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, blurWidth, blurHeight, 0, gl.RGBA, gl.UNSIGNED_BYTE, null)
533-
gl.bindTexture(gl.TEXTURE_2D, this.texBlurred2)
534-
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, blurWidth, blurHeight, 0, gl.RGBA, gl.UNSIGNED_BYTE, null)
538+
// Allocate blur textures, only reallocating storage when the blur
539+
// size actually changes (see the matching comment on texMaskFiltered
540+
// in render() for why this check matters).
541+
if (this._blurW !== blurWidth || this._blurH !== blurHeight) {
542+
gl.bindTexture(gl.TEXTURE_2D, this.texBlurred1)
543+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, blurWidth, blurHeight, 0, gl.RGBA, gl.UNSIGNED_BYTE, null)
544+
gl.bindTexture(gl.TEXTURE_2D, this.texBlurred2)
545+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, blurWidth, blurHeight, 0, gl.RGBA, gl.UNSIGNED_BYTE, null)
546+
this._blurW = blurWidth
547+
this._blurH = blurHeight
548+
}
535549

536550
// Setup FBOs
537551
gl.bindFramebuffer(gl.FRAMEBUFFER, this.fboBlur1)
@@ -718,9 +732,16 @@ export default class WebGLCompositor {
718732
return
719733
}
720734

721-
// Allocate mask filtered texture
722-
gl.bindTexture(gl.TEXTURE_2D, this.texMaskFiltered)
723-
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, outW, outH, 0, gl.RGBA, gl.UNSIGNED_BYTE, null)
735+
// Allocate mask filtered texture, only reallocating storage when the
736+
// output size actually changes (texImage2D(null) forces the GPU to
737+
// allocate new backing storage every call, which is wasted work when
738+
// called every frame at an unchanged size).
739+
if (this._maskFilteredW !== outW || this._maskFilteredH !== outH) {
740+
gl.bindTexture(gl.TEXTURE_2D, this.texMaskFiltered)
741+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, outW, outH, 0, gl.RGBA, gl.UNSIGNED_BYTE, null)
742+
this._maskFilteredW = outW
743+
this._maskFilteredH = outH
744+
}
724745

725746
// Upload and process mask
726747
if (mask) {

0 commit comments

Comments
 (0)