BabylonJS/Babylon.js · error

updateWrappedNativeTexture: wrapped texture's render-target

Error message

updateWrappedNativeTexture: wrapped texture's render-target wrapper has a depth/stencil texture; not supported. Dispose and re-wrap.

What it means

updateWrappedNativeTexture refuses to update a wrapped texture whose render-target wrapper owns a depth/stencil texture. After a DisableRendering/EnableRendering cycle the bgfx framebuffer and depth/stencil handle become stale, and rebuilding them during the swap is non-trivial, so v1 rejects and asks the caller to dispose and re-wrap.

Source

Thrown at packages/dev/core/src/Engines/thinNativeEngine.pure.ts:2190

                throw new Error(`updateWrappedNativeTexture: new handle layer count (${newLayerCount}) must match the wrapped texture's layer count (${oldLayerCount}).`);
            }
        }

        // 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("updateWrappedNativeTexture: wrapped texture is part of a multi render-target; not supported. Dispose and re-wrap.");
            }
            if (rtWrapper._depthStencilTexture) {
                // After a DisableRendering / EnableRendering cycle the bgfx framebuffer + the depth/stencil texture's
                // bgfx handle are both stale. Rebuilding the depth/stencil texture from the wrapper's stored settings
                // is feasible but non-trivial; v1 rejects and asks the caller to dispose + re-wrap.
                throw new Error("updateWrappedNativeTexture: wrapped texture's render-target wrapper has a depth/stencil texture; not supported. Dispose and re-wrap.");
            }
        }

        internalTexture._hardwareTexture = new NativeHardwareTexture(texture, this._engine);
        internalTexture.isReady = true;
        this.updateTextureSamplingMode(internalTexture.samplingMode, internalTexture);

        // Rebuild the framebuffer of any render-target wrapper holding this wrapped texture as its color attachment.
        // After a DisableRendering / EnableRendering cycle the bgfx framebuffer handle is stale; the consumer-supplied
        // new texture is the moment we have a fresh handle to rebuild against.
        for (const rtWrapper of this._renderTargetWrapperCache) {
            if (rtWrapper.texture !== internalTexture) {
                continue;
            }
            const nativeRTWrapper = rtWrapper as NativeRenderTargetWrapper;
            // NativeRenderTargetWrapper._framebuffer setter releases the old framebuffer before assigning,
            // so no manual _releaseFramebufferObjects call is needed (and would double-delete the handle).
            nativeRTWrapper._framebuffer = this._engine.createFrameBuffer(

View on GitHub (pinned to 0592b347b8)

Solutions

  1. Dispose the render-target wrapper (which frees the depth/stencil texture) and recreate it with the new native texture via wrapNativeTexture.
  2. Create the render target without a depth/stencil attachment if depth is not needed.
  3. Avoid DisableRendering/EnableRendering cycles around the wrapped RT, or recreate state after such cycles.

Example fix

// before
engine.updateWrappedNativeTexture(rt.texture, newNativeTex); // rt has depth/stencil

// after
rtWrapper.dispose();
rtWrapper = engine.createRenderTargetTexture(size, { ...opts, texture: engine.wrapNativeTexture(newNativeTex, w, h, ...) });
Defensive patterns

Strategy: validation

Validate before calling

const rt = engine._renderTargetWrapperCache.find(rt => rt.textures?.includes(wrapped));
if (rt && rt._depthStencilTexture) throw new Error("RT wrapper has depth/stencil texture; dispose and re-wrap required.");

Try / catch

try {
  engine.updateWrappedNativeTexture(wrapped, newNativeTex);
} catch (e) {
  if (e.message.includes("depth/stencil")) {
    rtWrapper.dispose();
    rtWrapper = createRTWithWrappedTexture(newNativeTex, depthStencilOpts);
  } else throw e;
}

Prevention

When it happens

Trigger: engine.updateWrappedNativeTexture called on a texture whose rtWrapper._depthStencilTexture is set (render target created with a depth/stencil attachment), even if it is not an MRT.

Common situations: Offscreen scene rendering with depth testing where the color texture's native handle is recreated externally (e.g. by a compositor) and the code tries to repoint it.

Related errors


AI-assisted analysis of BabylonJS/Babylon.js@0592b347b8 (2026-08-30). Data as JSON: /api/errors/6fadcc066b80cdf4. Report an issue: GitHub.