BabylonJS/Babylon.js · error
updateWrappedWebGLTexture: wrapped texture is part of a mult
Error message
updateWrappedWebGLTexture: wrapped texture is part of a multi render-target; not supported. Dispose and re-wrap.
What it means
When restoring a wrapped texture, Babylon checks render-target wrappers that reference it. If the texture is an attachment of a multi render-target (rtWrapper.isMulti) or has a depth/stencil attachment, simply swapping the GL handle would leave sibling attachments inconsistent, so v1 rejects and asks the caller to dispose and re-wrap the whole construct.
Source
Thrown at packages/dev/core/src/Engines/engine.pure.ts:826
* render-target wrapper, or if the wrapper has a depth/stencil texture (these are not supported in this version;
* dispose and re-wrap).
* @param internalTexture defines the wrapped InternalTexture to repoint
* @param texture defines the new WebGL handle to wrap
*/
public updateWrappedWebGLTexture(internalTexture: InternalTexture, texture: WebGLTexture): void {
if (internalTexture.source !== InternalTextureSource.External) {
throw new Error("updateWrappedWebGLTexture: target InternalTexture was not produced by wrapWebGLTexture.");
}
// Pre-validate before mutating any state so a thrown precondition leaves the InternalTexture untouched.
// Note: rtWrapper.texture only returns _textures[0]; walk every attachment to catch the multi-RT case where
// the wrapped texture is at index > 0.
for (const rtWrapper of this._renderTargetWrapperCache) {
if (!rtWrapper.textures?.includes(internalTexture)) {
continue;
}
if (rtWrapper.isMulti) {
throw new Error("updateWrappedWebGLTexture: wrapped texture is part of a multi render-target; not supported. Dispose and re-wrap.");
}
if (rtWrapper._depthStencilTexture) {
// The depth/stencil texture's GL handle was also lost on context restore. Rebuilding it from the
// wrapper's stored depth settings + re-attaching is feasible but non-trivial; v1 rejects and asks
// the caller to dispose + re-wrap (which also recreates the depth/stencil texture via the public API).
throw new Error("updateWrappedWebGLTexture: wrapped texture's render-target wrapper has a depth/stencil texture; not supported. Dispose and re-wrap.");
}
}
internalTexture._hardwareTexture = new WebGLHardwareTexture(texture, this._gl);
internalTexture.isReady = true;
// The new GL texture has default sampler state; clear the per-InternalTexture cached sampler params so the
// next _setTexture re-applies them, then drop any binding-cache slot pointing at this InternalTexture so the
// identity short-circuit (this._boundTexturesCache[channel] === internalTexture) doesn't skip the rebind.
internalTexture._cachedCoordinatesMode = null;
internalTexture._cachedWrapU = null;
internalTexture._cachedWrapV = null;
View on GitHub (pinned to 0592b347b8)
Solutions
- Dispose the render target wrapper and recreate it, wrapping the new WebGL textures via wrapWebGLTexture
- Exclude textures belonging to isMulti render-target wrappers (and those with depth/stencil attachments) from handle-swap restoration
- Restructure so wrapped textures used with MRT are restored through the public render-target creation API
- Check rtWrapper.isMulti / rtWrapper._depthStencilTexture before calling updateWrappedWebGLTexture and branch to dispose+re-wrap
Example fix
// before
engine.updateWrappedWebGLTexture(mrtTexture, newHandle); // throws: part of MRT
// after
if (rtWrapper.isMulti || rtWrapper._depthStencilTexture) {
rtWrapper.dispose();
const newRt = engine.createMultipleRenderTarget(size, mrtOptions); // then wrap/rebind handles
} else {
engine.updateWrappedWebGLTexture(mrtTexture, newHandle);
} Defensive patterns
Strategy: validation
Validate before calling
const wrapper = engine._renderTargetWrapperCache.find(rtw => rtw.textures?.includes(internalTexture));
if (wrapper && (wrapper.isMulti || wrapper._depthStencilTexture)) {
// dispose and recreate instead of updateWrappedWebGLTexture
} Type guard
function canRepointInPlace(engine: AbstractEngine, t: InternalTexture): boolean {
return !engine._renderTargetWrapperCache.some(rtw => rtw.textures?.includes(t) && (rtw.isMulti || !!rtw._depthStencilTexture));
} Try / catch
try {
engine.updateWrappedWebGLTexture(t, newHandle);
} catch (e) {
if (e instanceof Error && e.message.includes('multi render-target')) {
rtWrapper.dispose();
const rt = engine.createMultipleRenderTarget(size, mrtOptions); // dispose + re-wrap path
} else throw e;
} Prevention
- Check rtWrapper.isMulti and _depthStencilTexture before repointing handles
- Treat MRT wrappers as atomic: dispose and recreate the whole wrapper on restore
- Keep MRT setups on the public render-target API rather than manual wrapping
- Document which textures belong to MRT wrappers in your restore pipeline
When it happens
Trigger: Calling engine.updateWrappedWebGLTexture on a texture that is an attachment of a multi-render-target wrapper (multiple textures) or whose wrapper has a _depthStencilTexture, e.g. MRT pipelines or depth-enabled wrapped RTs after context loss.
Common situations: Context-restoration recovery for MRT (G-buffer) setups using wrapped textures; deferred rendering engines that wrapped each MRT attachment and try to repoint them one by one.
Related errors
- updateWrappedNativeTexture: wrapped texture is part of a mul
- Unable to create multi sampled framebuffer
- updateWrappedWebGLTexture: target InternalTexture was not pr
- updateWrappedWebGLTexture: wrapped texture's render-target w
- updateWrappedNativeTexture: wrapped texture's render-target
AI-assisted analysis of BabylonJS/Babylon.js@0592b347b8 (2026-08-30).
Data as JSON: /api/errors/f6214696e76ed9e9.
Report an issue: GitHub.