Skip to content

Commit 3278638

Browse files
PR #1710: replace cube_texture_polyfill.js with native CubeTexture polyfill
Per review: this polyfill should be available to every Babylon Native consumer using Chakra, not just the Playground app. Move it from a Playground-only LoadScript() of cube_texture_polyfill.js into a proper C++ polyfill library under Polyfills/ that any embedder can link. The polyfill patches BABYLON.NativeEngine.prototype.createCubeTexture to transparently retry single-URL .dds / .ktx / .ktx2 cubemap loads (which BN does not currently handle) as their .env counterpart that Babylon's CDN co-hosts at the same path. On 404 it falls through to the original (which throws), preserving existing error semantics. Implementation pattern follows the existing ErrorCause / StringTrim native polyfills: - New Polyfills/CubeTexture/ library. - Public header Babylon/Polyfills/CubeTexture.h declares void BABYLON_API Initialize(Napi::Env env). - Source/CubeTexture.cpp embeds the polyfill IIFE verbatim from the prior cube_texture_polyfill.js (same semantics, same idempotency marker __cubeTexturePolyfillInstalled) and dispatches it via the free function Napi::Eval(env, source, url). Napi::Eval is declared by every engine-specific <napi/env.h> across both N-API trees (Chakra / V8 / JavaScriptCore in Core/Node-API/Include/Engine/<X>/ and JSI in Core/Node-API-JSI/Include/napi/) so the same source builds uniformly across all backends; no __has_include switch needed for this entry point. Wiring: - Top-level CMakeLists.txt: add BABYLON_NATIVE_POLYFILL_CUBETEXTURE option (default ON). - Polyfills/CMakeLists.txt: guarded add_subdirectory(CubeTexture). - Apps/Playground/CMakeLists.txt and Apps/Playground/Android/BabylonNative/CMakeLists.txt: PRIVATE link CubeTexture into the Playground / BabylonNativeJNI executables so AppContext.cpp can use the published header on every platform. - Apps/Playground/Shared/AppContext.cpp: include the new header and replace LoadScript("app:///Scripts/cube_texture_polyfill.js"); with Dispatch([](Napi::Env env) { Babylon::Polyfills::CubeTexture::Initialize(env); }); scheduled right after LoadScript("babylon.max.js"). ScriptLoader's Dispatch and LoadScript queue onto the same ordered task chain (see jsruntimehost ScriptLoader::Impl), so Initialize is guaranteed to run after babylon.max.js finishes evaluating - same timing the prior LoadScript provided. Delete Apps/Playground/Scripts/cube_texture_polyfill.js (no longer shipped). Verified locally on Chakra build (build/win32 RelWithDebInfo, headless) - all 7 cubemap-affected tests re-enabled by this PR pass: Clear Coat diff=519 px PBR diff=482 px (12 sub-tests skipped, expected) Anisotropic diff=308 px PBRMetallicRoughnessMaterial diff=482 px NMEGLTF diff=631 px PBRSpecularGlossinessMaterial diff=522 px Console log confirms the polyfill ran: [CubeTexture polyfill] NativeEngine.createCubeTexture patched with .env fallback Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 9f41d6f commit 3278638

8 files changed

Lines changed: 99 additions & 18 deletions

File tree

Apps/Playground/Android/BabylonNative/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ target_link_libraries(BabylonNativeJNI
3030
PRIVATE Blob
3131
PRIVATE Canvas
3232
PRIVATE Console
33+
PRIVATE CubeTexture
3334
PRIVATE File
3435
PRIVATE GraphicsDevice
3536
PRIVATE NativeCamera

Apps/Playground/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ set(DEPENDENCIES
1313
"../Dependencies/recast.js")
1414

1515
set(SCRIPTS
16-
"Scripts/cube_texture_polyfill.js"
1716
"Scripts/experience.js"
1817
"Scripts/playground_runner.js"
1918
"Scripts/validation_native.js"
@@ -140,6 +139,7 @@ target_link_libraries(Playground
140139
PRIVATE bx
141140
PRIVATE Canvas
142141
PRIVATE Console
142+
PRIVATE CubeTexture
143143
PRIVATE ExternalTexture
144144
PRIVATE File
145145
PRIVATE GraphicsDevice

Apps/Playground/Shared/AppContext.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <Babylon/Polyfills/Blob.h>
2121
#include <Babylon/Polyfills/Canvas.h>
2222
#include <Babylon/Polyfills/Console.h>
23+
#include <Babylon/Polyfills/CubeTexture.h>
2324
#include <Babylon/Polyfills/File.h>
2425
#include <Babylon/Polyfills/Performance.h>
2526
#include <Babylon/Polyfills/TextDecoder.h>
@@ -217,7 +218,13 @@ AppContext::AppContext(
217218
// Commenting out recast.js for now because v8jsi is incompatible with asm.js.
218219
// m_scriptLoader->LoadScript("app:///Scripts/recast.js");
219220
m_scriptLoader->LoadScript("app:///Scripts/babylon.max.js");
220-
m_scriptLoader->LoadScript("app:///Scripts/cube_texture_polyfill.js");
221+
// CubeTexture polyfill must run AFTER babylon.max.js is evaluated because
222+
// it patches BABYLON.NativeEngine.prototype.createCubeTexture. The
223+
// ScriptLoader's Dispatch is ordered against LoadScript on the same task
224+
// chain, so this is guaranteed to run after babylon.max.js completes.
225+
m_scriptLoader->Dispatch([](Napi::Env env) {
226+
Babylon::Polyfills::CubeTexture::Initialize(env);
227+
});
221228
m_scriptLoader->LoadScript("app:///Scripts/babylonjs.loaders.js");
222229
m_scriptLoader->LoadScript("app:///Scripts/babylonjs.materials.js");
223230
m_scriptLoader->LoadScript("app:///Scripts/babylon.gui.js");

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ option(BABYLON_NATIVE_PLUGIN_TESTUTILS "Include Babylon Native Plugin TestUtils.
142142
# Polyfills
143143
option(BABYLON_NATIVE_POLYFILL_WINDOW "Include Babylon Native Polyfill Window." ON)
144144
option(BABYLON_NATIVE_POLYFILL_CANVAS "Include Babylon Native Polyfill Canvas." ON)
145+
option(BABYLON_NATIVE_POLYFILL_CUBETEXTURE "Include Babylon Native Polyfill CubeTexture (.dds/.ktx/.ktx2 -> .env fallback for NativeEngine.createCubeTexture)." ON)
145146

146147
# Sanitizers
147148
option(ENABLE_SANITIZERS "Enable AddressSanitizer and UBSan" OFF)

Polyfills/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,7 @@ endif()
55
if(BABYLON_NATIVE_POLYFILL_CANVAS)
66
add_subdirectory(Canvas)
77
endif()
8+
9+
if(BABYLON_NATIVE_POLYFILL_CUBETEXTURE)
10+
add_subdirectory(CubeTexture)
11+
endif()
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
set(SOURCES
2+
"Include/Babylon/Polyfills/CubeTexture.h"
3+
"Source/CubeTexture.cpp")
4+
5+
add_library(CubeTexture ${SOURCES})
6+
warnings_as_errors(CubeTexture)
7+
8+
target_include_directories(CubeTexture
9+
PUBLIC "Include")
10+
11+
target_link_libraries(CubeTexture
12+
PUBLIC napi
13+
PUBLIC Foundation)
14+
15+
set_property(TARGET CubeTexture PROPERTY FOLDER Polyfills)
16+
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SOURCES})
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#pragma once
2+
3+
#include <napi/env.h>
4+
#include <Babylon/Api.h>
5+
6+
namespace Babylon::Polyfills::CubeTexture
7+
{
8+
// Patches BABYLON.NativeEngine.prototype.createCubeTexture to transparently
9+
// retry .dds / .ktx / .ktx2 single-URL cubemap loads as .env (a format BN
10+
// does support). Babylon's CDN co-hosts both, so the swap is invisible to
11+
// consumer JS. Falls back to the original (which throws "Cannot load
12+
// cubemap because 6 files were not defined") on 404 - preserving existing
13+
// error semantics.
14+
//
15+
// Call Initialize AFTER babylon.max.js has been evaluated; the patch
16+
// requires BABYLON.NativeEngine.prototype to exist. When using the
17+
// jsruntimehost ScriptLoader, the simplest pattern is:
18+
//
19+
// scriptLoader.LoadScript("app:///Scripts/babylon.max.js");
20+
// scriptLoader.Dispatch([](Napi::Env env) {
21+
// Babylon::Polyfills::CubeTexture::Initialize(env);
22+
// });
23+
//
24+
// The Dispatch queues onto the same ordered task chain as LoadScript, so
25+
// Initialize is guaranteed to run after babylon.max.js finishes evaluating.
26+
//
27+
// This is a tactical bridge until Plugins/NativeEngine wires a proper
28+
// loader registry path for cubemap formats. Safe to call multiple times;
29+
// the patch is idempotent.
30+
void BABYLON_API Initialize(Napi::Env env);
31+
}

Apps/Playground/Scripts/cube_texture_polyfill.js renamed to Polyfills/CubeTexture/Source/CubeTexture.cpp

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
1-
// Cube texture fallback for BN's NativeEngine.
2-
//
3-
// BN's NativeEngine.createCubeTexture only natively handles .env single-file
4-
// cubemaps and 6-file face arrays. Snippets that load .dds (or .ktx) cubemaps
5-
// without an explicit forcedExtension or 6-file array throw "Cannot load
6-
// cubemap because 6 files were not defined".
7-
//
8-
// Babylon's standard environment cubemaps are hosted both as <name>.dds and
9-
// <name>.env at the same path on assets.babylonjs.com and
10-
// playground.babylonjs.com. This polyfill detects the failure pre-condition
11-
// and transparently retries with the .env URL. If the .env counterpart 404s,
12-
// it falls through to the original (which throws), preserving existing
13-
// behavior.
14-
1+
#include <Babylon/Polyfills/CubeTexture.h>
2+
3+
#include <napi/env.h>
4+
5+
namespace Babylon::Polyfills::CubeTexture
6+
{
7+
namespace
8+
{
9+
// Embedded polyfill source. Kept verbatim from the original Playground
10+
// cube_texture_polyfill.js (PR #1710 v0) so behaviour stays identical:
11+
// - Patches BABYLON.NativeEngine.prototype.createCubeTexture.
12+
// - Triggers only when the URL ends in .dds / .ktx / .ktx2 and no
13+
// forcedExtension, 6-file array, or raw buffer was supplied.
14+
// - Retries via NativeEngine._loadFile to fetch the .env counterpart;
15+
// on 404 falls back to the original (which throws), preserving the
16+
// existing failure mode.
17+
// - Idempotent via the __cubeTexturePolyfillInstalled marker on the
18+
// prototype.
19+
constexpr const char* JS_SOURCE = R"javascript(
1520
(function () {
1621
"use strict";
1722
@@ -92,7 +97,7 @@
9297
});
9398
};
9499
95-
var onEnvFailed = function (request, exception) {
100+
var onEnvFailed = function (/* request, exception */) {
96101
settle(function () {
97102
original.call(self, rootUrl, scene, files, noMipmap, onLoad, onError, format, forcedExtension, createPolynomials, lodScale, lodOffset, texture, loaderOptions, useSRGBBuffer, buffer);
98103
});
@@ -108,6 +113,22 @@
108113
};
109114
110115
if (typeof console !== "undefined" && console.log) {
111-
console.log("[cube_texture_polyfill] NativeEngine.createCubeTexture patched with .env fallback");
116+
console.log("[CubeTexture polyfill] NativeEngine.createCubeTexture patched with .env fallback");
112117
}
113118
})();
119+
)javascript";
120+
121+
constexpr const char* JS_SOURCE_URL = "babylon-native://polyfills/CubeTexture.js";
122+
}
123+
124+
void Initialize(Napi::Env env)
125+
{
126+
// The free function Napi::Eval(env, source, url) is declared by every
127+
// engine-specific <napi/env.h> across both N-API trees (Chakra, V8,
128+
// JavaScriptCore in Core/Node-API/Include/Engine/<X>/, and JSI in
129+
// Core/Node-API-JSI/Include/napi/). Using it uniformly avoids the
130+
// Shared-only env.RunScript() which is missing from the JSI tree.
131+
Napi::HandleScope scope{env};
132+
Napi::Eval(env, JS_SOURCE, JS_SOURCE_URL);
133+
}
134+
}

0 commit comments

Comments
 (0)