{"record":{"id":"e0d8a6d463426500","repo":"BabylonJS/Babylon.js","slug":"updatewrappednativetexture-wrapped-texture-is-par","errorCode":null,"errorMessage":"updateWrappedNativeTexture: wrapped texture is part of a multi render-target; not supported. Dispose and re-wrap.","messagePattern":"updateWrappedNativeTexture: wrapped texture is part of a multi render-target; not supported\\. Dispose and re-wrap\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/dev/core/src/Engines/thinNativeEngine.pure.ts","lineNumber":2184,"sourceCode":"            );\r\n        }\r\n        if (this._engine.getTextureLayerCount) {\r\n            const newLayerCount = this._engine.getTextureLayerCount(texture);\r\n            const oldLayerCount = internalTexture.is2DArray ? internalTexture.depth : 1;\r\n            if (newLayerCount !== oldLayerCount) {\r\n                throw new Error(`updateWrappedNativeTexture: new handle layer count (${newLayerCount}) must match the wrapped texture's layer count (${oldLayerCount}).`);\r\n            }\r\n        }\r\n\r\n        // Pre-validate before mutating any state so a thrown precondition leaves the InternalTexture untouched.\r\n        // Note: rtWrapper.texture only returns _textures[0]; walk every attachment to catch the multi-RT case where\r\n        // the wrapped texture is at index > 0.\r\n        for (const rtWrapper of this._renderTargetWrapperCache) {\r\n            if (!rtWrapper.textures?.includes(internalTexture)) {\r\n                continue;\r\n            }\r\n            if (rtWrapper.isMulti) {\r\n                throw new Error(\"updateWrappedNativeTexture: wrapped texture is part of a multi render-target; not supported. Dispose and re-wrap.\");\r\n            }\r\n            if (rtWrapper._depthStencilTexture) {\r\n                // After a DisableRendering / EnableRendering cycle the bgfx framebuffer + the depth/stencil texture's\r\n                // bgfx handle are both stale. Rebuilding the depth/stencil texture from the wrapper's stored settings\r\n                // is feasible but non-trivial; v1 rejects and asks the caller to dispose + re-wrap.\r\n                throw new Error(\"updateWrappedNativeTexture: wrapped texture's render-target wrapper has a depth/stencil texture; not supported. Dispose and re-wrap.\");\r\n            }\r\n        }\r\n\r\n        internalTexture._hardwareTexture = new NativeHardwareTexture(texture, this._engine);\r\n        internalTexture.isReady = true;\r\n        this.updateTextureSamplingMode(internalTexture.samplingMode, internalTexture);\r\n\r\n        // Rebuild the framebuffer of any render-target wrapper holding this wrapped texture as its color attachment.\r\n        // After a DisableRendering / EnableRendering cycle the bgfx framebuffer handle is stale; the consumer-supplied\r\n        // new texture is the moment we have a fresh handle to rebuild against.\r\n        for (const rtWrapper of this._renderTargetWrapperCache) {\r\n            if (rtWrapper.texture !== internalTexture) {\r","sourceCodeStart":2166,"sourceCodeEnd":2202,"githubUrl":"https://github.com/BabylonJS/Babylon.js/blob/0592b347b8a4ee0236089ea86a749cacfdb266d8/packages/dev/core/src/Engines/thinNativeEngine.pure.ts#L2166-L2202","documentation":"updateWrappedNativeTexture scans the render-target wrapper cache; if the wrapped texture belongs to a multi render-target (MRT) wrapper, updating the underlying handle is unsupported and throws, because MRT framebuffers cannot be safely rebuilt around the swap in this version.","triggerScenarios":"Calling engine.updateWrappedNativeTexture on a texture that appears in rtWrapper.textures of a wrapper with isMulti == true (a texture bound in a multi render-target pass).","commonSituations":"Deferred-rendering pipelines that swap G-buffer textures created as MRT attachments (position at index > 0); attempting hot-swap of MRT color attachments.","solutions":["Dispose the MRT wrapper and its textures, then recreate the render target wrapping the new native textures.","Keep hot-swappable textures in single-attachment render targets instead of MRT.","Restructure the pass so the swapped texture is not part of the MRT set."],"exampleFix":"// before\nengine.updateWrappedNativeTexture(gbuffer.tex[1], newTex); // MRT attachment\n\n// after\nmrtWrapper.dispose();\ngbuffer = recreateMRT(scene, { texture1: newTex });","handlingStrategy":"validation","validationCode":"const inMrt = engine._renderTargetWrapperCache.some(rt => rt.isMulti && rt.textures?.includes(wrapped));\nif (inMrt) throw new Error(\"Texture is an MRT attachment; dispose and recreate the render target instead of updating.\");","typeGuard":null,"tryCatchPattern":"try {\n  engine.updateWrappedNativeTexture(wrapped, newNativeTex);\n} catch (e) {\n  if (e.message.includes(\"multi render-target\")) {\n    recreateRenderTargetWithNewTexture(wrapped, newNativeTex);\n  } else throw e;\n}","preventionTips":["Design G-buffer pipelines to rebuild render targets on texture swap instead of hot-swapping.","Keep hot-swapped textures out of MRT attachment sets.","Track which textures belong to MRT wrappers in your renderer state."],"tags":["native","texture-wrapping","mrt","unsupported"],"backgroundTag":"unsupported-mrt-update","analyzedSha":"0592b347b8a4ee0236089ea86a749cacfdb266d8","analyzedAt":"2026-08-30T15:11:20.442Z","schemaVersion":2},"datasetVersion":"2026-08-30T18:17:15.746Z"}