[NativeEngine] Add updateTextureDirectly texture-loader sink (#218)#1778
Draft
bkaradzic-microsoft wants to merge 4 commits into
Draft
[NativeEngine] Add updateTextureDirectly texture-loader sink (#218)#1778bkaradzic-microsoft wants to merge 4 commits into
bkaradzic-microsoft wants to merge 4 commits into
Conversation
Implement the native sink for Babylon's _uploadDataToTextureDirectly / _uploadCompressedDataToTextureDirectly so single-file container textures (.dds/.ktx/.ktx2, plus Basis/IES/HDR/EXR/TGA) load through the same JS texture loaders WebGL/WebGPU use. The loaders upload one (face, mip) at a time WebGL texImage2D-style; bgfx needs the whole texture allocated first, so the underlying texture is created lazily on the first upload. Validates JS-provided dimensions against maxTextureSize before uint16 narrowing and the payload size against bimg::imageGetSize, uses bgfx::copy for async-owned upload memory, and matches the existing loader flip conventions (skipping row-flips for compressed formats). Addresses BabylonJS#218 (paired with the Babylon.js single-file cubemap loader change). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a new N-API entry point on NativeEngine to support Babylon’s shared JS “upload-to-texture directly” sink, enabling reuse of the stock JS texture loaders (DDS/KTX/KTX2/etc.) while handling bgfx’s requirement to allocate the full texture before sub-updates.
Changes:
- Declares
NativeEngine::UpdateTextureDirectlyin the engine header. - Registers the new JS-exposed instance method
updateTextureDirectly. - Implements lazy texture creation + per-(face,mip) uploads with dimension and payload-size validation and optional Y-flip behavior.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| Plugins/NativeEngine/Source/NativeEngine.h | Adds the UpdateTextureDirectly method declaration. |
| Plugins/NativeEngine/Source/NativeEngine.cpp | Exposes and implements updateTextureDirectly, performing validation, lazy create, and bgfx uploads. |
- Guard the body with BABYLON_NATIVE_PLUGIN_NATIVEENGINE_LOAD_IMAGES (matches LoadRawTexture/LoadCubeTexture); throw when image loading is off so the plugin still compiles with LOAD_IMAGES=OFF (bimg headers are gated). - Read face index and lod as uint32 and validate BEFORE narrowing to uint8_t. - Reject non-square cube dimensions and out-of-range face indices (cube face < 6, 2D face == 0). - Reject lod values that exceed the mip chain implied by the base dimensions. - Reject uploads whose size would truncate bgfx::copy's 32-bit length. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Member
Author
|
Addressed the Copilot review in c823821 (
I skipped the suggested |
Reject UpdateTextureDirectly calls whose mipWidth/mipHeight don't match max(1, base >> lod), so an inconsistent update region can't drive a bgfx assertion or backend-dependent undefined behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Read the JS-provided format as uint32 and reject values >= bimg::TextureFormat::Count before narrowing to the enum, so an out-of-range value can't index past bimg/bgfx's format tables in Create*/imageGetSize. Mirrors the existing pre-narrow validation of faceIndex/lod/dimensions in this method. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds a single new N-API method,
updateTextureDirectly, toNativeEngine— the native half of #218 (single-file.dds/.ktx/.ktx2cubemap loading).It implements the sink for Babylon's shared JS texture-loader path (
_uploadDataToTextureDirectly/_uploadCompressedDataToTextureDirectly), soNativeEnginecan reuse the exact same stock loaders that WebGL/WebGPU use (DDS/KTX/KTX2/Basis/IES/HDR/EXR/TGA), rather than needing a bespoke native path per container format.The loaders upload one
(face, mip)at a time, WebGLtexImage2D-style. bgfx instead needs the whole texture allocated before any update, so the texture is created lazily on the first upload and subsequent(face, mip)calls update into it.Why draft / paired change
This is a dormant sink: nothing in the current published
babylonjscallsupdateTextureDirectly, so this PR is a no-op at runtime until the paired Babylon.js JS change ships. It compiles cleanly and is safe to merge independently.BabylonJS/Babylon.jswhen ready.Apps/Playground/Scripts/config.jsonthat this feature fixes — held until the JS half is published to npm (Apps/package.jsonpinsbabylonjs: "^9.3.4", which will auto-resolve), so the test re-enable doesn't turn CI red before the runtime dependency exists.Implementation notes
All inputs to this binding come from JS, so every scalar is validated before it is narrowed or used to index a table:
BABYLON_NATIVE_PLUGIN_NATIVEENGINE_LOAD_IMAGES(matchingLoadRawTexture/LoadCubeTexture) and throws when image loading is disabled, so the plugin still compiles withLOAD_IMAGES=OFF(thebimgheaders are gated on that flag).bgfx::getCaps()->limits.maxTextureSize(and non-zero) before narrowing touint16_t, so an out-of-range value can't wrap into an in-range one and drive an OOB read/upload.uint32and validated before narrowing touint8_t— cube face< 6, 2D face== 0, andlodwithin the mip chain implied by the base dimensions (sohasMips == falserestricts lod to 0).mipWidth/mipHeightmust equalmax(1, base >> lod), so an inconsistent update region can't reachbgfx::updateTexture*and assert / hit backend UB.uint32and rejected when>= bimg::TextureFormat::Countbefore the enum cast, so an out-of-range value can't index past bimg/bgfx's format tables inCreate*/imageGetSize.data.ByteLength()is validated againstbimg::imageGetSize(...)for the given mip dimensions and format, and the size is rejected if it would exceedbgfx::copy's 32-bit length.bgfx::copyso bgfx owns the buffer and releases it after the GPU consumes it.LoadCubeTextureFromImages; 2D follows the raw/loadTextureconvention. Compressed block data is never row-flipped.Testing
Validated locally against the paired JS build via the Playground headless validation harness — the 7 PBR/IBL tests that previously exceeded the pixel-diff budget (because prefiltered-cube SH wasn't loading) now pass well within budget (all << 240,000-px budget):
The config.json re-enable of these tests is held for the follow-up PR (see above).
Review
Addressed multiple rounds of Copilot review: the
LOAD_IMAGESbuild guard, the pre-narrow validation of face index / lod / format, the cube-square and mip-dimension-consistency checks, and thebgfx::copyoverflow guard were all added in response. One suggestion (re-deriving and comparing an already-valid texture's dimensions/format on every update) was intentionally declined as redundant — the loader is the sole caller and always drives consistent parameters.