BabylonJS/Babylon.js · error

updateWrappedNativeTexture: new handle layer count (${newLay

Error message

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

What it means

updateWrappedNativeTexture validates layer counts: if the wrapped texture is a 2D array its depth is compared against the new handle's layer count (via getTextureLayerCount); for non-array textures the expected count is 1. A mismatch throws because repointing would silently change the texture's effective layering.

Source

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

     * @param texture defines the new native texture handle to wrap
     */
    public updateWrappedNativeTexture(internalTexture: InternalTexture, texture: NativeTexture): void {
        if (internalTexture.source !== InternalTextureSource.External) {
            throw new Error("updateWrappedNativeTexture: target InternalTexture was not produced by wrapNativeTexture.");
        }

        const newWidth = this._engine.getTextureWidth(texture);
        const newHeight = this._engine.getTextureHeight(texture);
        if (newWidth !== internalTexture.baseWidth || newHeight !== internalTexture.baseHeight) {
            throw new Error(
                `updateWrappedNativeTexture: new handle dimensions (${newWidth}x${newHeight}) must match the wrapped texture's dimensions (${internalTexture.baseWidth}x${internalTexture.baseHeight}).`
            );
        }
        if (this._engine.getTextureLayerCount) {
            const newLayerCount = this._engine.getTextureLayerCount(texture);
            const oldLayerCount = internalTexture.is2DArray ? internalTexture.depth : 1;
            if (newLayerCount !== oldLayerCount) {
                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.");

View on GitHub (pinned to 0592b347b8)

Solutions

  1. Ensure the replacement native texture is created with the same layer count (same array size/cube structure).
  2. Dispose and re-wrap via wrapNativeTexture when the layer structure genuinely changes.
  3. Skip the update path entirely for array textures and recreate dependent pipelines/framebuffers instead.

Example fix

// before
engine.updateWrappedNativeTexture(wrappedArray, singleLayerTex);

// after
const newWrapped = engine.wrapNativeTexture(arrayTex, w, h, { layers: oldDepth });
Defensive patterns

Strategy: validation

Validate before calling

const newLayers = engine._engine.getTextureLayerCount?.(newNativeTex) ?? 1;
const expectedLayers = wrapped.is2DArray ? wrapped.depth : 1;
if (newLayers !== expectedLayers) throw new Error(`Layer count mismatch: ${newLayers} vs ${expectedLayers}`);

Try / catch

try {
  engine.updateWrappedNativeTexture(wrapped, newNativeTex);
} catch (e) {
  if (e.message.includes("layer count")) {
    wrapped.dispose();
    wrapped = engine.wrapNativeTexture(newNativeTex, w, h, sampling, ...);
  } else throw e;
}

Prevention

When it happens

Trigger: Swapping in a native texture handle whose layer/array-layer count differs from the wrapped texture (e.g. replacing a 6-layer array or cube-derived handle with a single-layer texture, or vice versa).

Common situations: Exchanging a texture array between passes with different layer counts; wrapping cube/2D-array textures then updating with a plain 2D handle.

Related errors


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