Skip to content

Commit b6ecf80

Browse files
authored
Add synchronous CreateForJavaScript and deprecate AddToContextAsync in ExternalTexture (#1646)
Adds synchronous CreateForJavaScript (replacing the deprecated AddToContextAsync shim), fixes a recursive-mutex deadlock, encapsulates the impl mutex, and adds single-slice array-layer (layerIndex) selection. Existing ExternalTexture render/MSAA/DeviceLoss tests are migrated to the sync API.
1 parent 7ed98f5 commit b6ecf80

29 files changed

Lines changed: 1497 additions & 420 deletions

Apps/HeadlessScreenshotApp/Win32/App.cpp

Lines changed: 19 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -127,46 +127,34 @@ int main()
127127
// Create a render target texture for the output.
128128
winrt::com_ptr<ID3D11Texture2D> outputTexture = CreateD3DRenderTargetTexture(d3dDevice.get());
129129

130-
std::promise<void> addToContext{};
131-
std::promise<void> startup{};
132-
133-
// Create an external texture for the render target texture and pass it to
134-
// the `startup` JavaScript function.
135-
loader.Dispatch([externalTexture = Babylon::Plugins::ExternalTexture{outputTexture.get()}, &addToContext, &startup](Napi::Env env) {
136-
auto jsPromise = externalTexture.AddToContextAsync(env);
137-
addToContext.set_value();
138-
139-
auto jsOnFulfilled = Napi::Function::New(env, [&startup](const Napi::CallbackInfo& info) {
140-
auto nativeTexture = info[0];
141-
info.Env().Global().Get("startup").As<Napi::Function>().Call(
142-
{
143-
nativeTexture,
144-
Napi::Value::From(info.Env(), WIDTH),
145-
Napi::Value::From(info.Env(), HEIGHT),
146-
});
147-
startup.set_value();
148-
});
149-
150-
jsPromise = jsPromise.Get("then").As<Napi::Function>().Call(jsPromise, {jsOnFulfilled}).As<Napi::Promise>();
151-
152-
CatchAndLogError(jsPromise);
153-
});
154-
155-
// Wait for `AddToContextAsync` to be called.
156-
addToContext.get_future().wait();
157-
158-
// Render a frame so that `AddToContextAsync` will complete.
130+
// Close the script-load frame.
159131
deviceUpdate.Finish();
160132
device.FinishRenderingCurrentFrame();
161133

162-
// Reopen the gate so JS can continue running (startup may issue bgfx commands).
134+
// Open a new frame for `startup` so the JS-side resource creation and
135+
// startup() call run in the same frame as the wait that observes them.
163136
device.StartRenderingCurrentFrame();
164137
deviceUpdate.Start();
165138

139+
std::promise<void> startup{};
140+
141+
// Create an external texture for the render target texture and pass it to
142+
// the `startup` JavaScript function.
143+
loader.Dispatch([externalTexture = Babylon::Plugins::ExternalTexture{outputTexture.get()}, &startup](Napi::Env env) {
144+
auto nativeTexture = externalTexture.CreateForJavaScript(env);
145+
env.Global().Get("startup").As<Napi::Function>().Call(
146+
{
147+
nativeTexture,
148+
Napi::Value::From(env, WIDTH),
149+
Napi::Value::From(env, HEIGHT),
150+
});
151+
startup.set_value();
152+
});
153+
166154
// Wait for `startup` to finish.
167155
startup.get_future().wait();
168156

169-
// Close the frame opened above.
157+
// Close the startup frame.
170158
deviceUpdate.Finish();
171159
device.FinishRenderingCurrentFrame();
172160

Apps/PrecompiledShaderTest/Source/App.cpp

Lines changed: 19 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -135,45 +135,34 @@ int RunApp(
135135
Babylon::ScriptLoader loader{runtime};
136136
loader.LoadScript("app:///index.js");
137137

138-
std::promise<void> addToContext{};
139-
std::promise<void> startup{};
140-
141-
// Create an external texture for the render target texture and pass it to
142-
// the `startup` JavaScript function.
143-
loader.Dispatch([externalTexture = std::move(externalTexture), &addToContext, &startup](Napi::Env env) {
144-
auto jsPromise = externalTexture.AddToContextAsync(env);
145-
addToContext.set_value();
146-
147-
auto jsOnFulfilled = Napi::Function::New(env, [&startup](const Napi::CallbackInfo& info) {
148-
auto nativeTexture = info[0];
149-
info.Env().Global().Get("startup").As<Napi::Function>().Call(
150-
{
151-
nativeTexture,
152-
Napi::Value::From(info.Env(), WIDTH),
153-
Napi::Value::From(info.Env(), HEIGHT),
154-
});
155-
startup.set_value();
156-
});
157-
158-
jsPromise = jsPromise.Get("then").As<Napi::Function>().Call(jsPromise, {jsOnFulfilled}).As<Napi::Promise>();
159-
CatchAndLogError(jsPromise);
160-
});
161-
162-
// Wait for `AddToContextAsync` to be called.
163-
addToContext.get_future().wait();
164-
165-
// Render a frame so that `AddToContextAsync` will complete.
138+
// Close the script-load frame.
166139
deviceUpdate.Finish();
167140
device.FinishRenderingCurrentFrame();
168141

169-
// Reopen the gate so JS can continue running (startup may issue bgfx commands).
142+
// Open a new frame for `startup` so the JS-side resource creation and
143+
// startup() call run in the same frame as the wait that observes them.
170144
device.StartRenderingCurrentFrame();
171145
deviceUpdate.Start();
172146

147+
std::promise<void> startup{};
148+
149+
// Create an external texture for the render target texture and pass it to
150+
// the `startup` JavaScript function.
151+
loader.Dispatch([externalTexture = std::move(externalTexture), &startup](Napi::Env env) {
152+
auto nativeTexture = externalTexture.CreateForJavaScript(env);
153+
env.Global().Get("startup").As<Napi::Function>().Call(
154+
{
155+
nativeTexture,
156+
Napi::Value::From(env, WIDTH),
157+
Napi::Value::From(env, HEIGHT),
158+
});
159+
startup.set_value();
160+
});
161+
173162
// Wait for `startup` to finish.
174163
startup.get_future().wait();
175164

176-
// Close the frame opened above.
165+
// Close the startup frame.
177166
deviceUpdate.Finish();
178167
device.FinishRenderingCurrentFrame();
179168

Apps/StyleTransferApp/Win32/App.cpp

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -334,42 +334,34 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
334334
loader.LoadScript("app:///Scripts/babylonjs.loaders.js");
335335
loader.LoadScript("app:///Scripts/index.js");
336336

337-
std::promise<void> addToContext{};
338-
std::promise<void> startup{};
339-
340-
// Create an external texture for the render target texture and pass it to
341-
// the `startup` JavaScript function.
342-
loader.Dispatch([externalTexture = Babylon::Plugins::ExternalTexture{g_BabylonRenderTexture.get()}, &addToContext, &startup](Napi::Env env) {
343-
auto jsPromise = externalTexture.AddToContextAsync(env);
344-
addToContext.set_value();
345-
346-
jsPromise.Get("then").As<Napi::Function>().Call(jsPromise, {Napi::Function::New(env, [&startup](const Napi::CallbackInfo& info) {
347-
auto nativeTexture = info[0];
348-
info.Env().Global().Get("startup").As<Napi::Function>().Call(
349-
{
350-
nativeTexture,
351-
Napi::Value::From(info.Env(), WIDTH),
352-
Napi::Value::From(info.Env(), HEIGHT),
353-
});
354-
startup.set_value();
355-
})});
356-
});
357-
358-
// Wait for `AddToContextAsync` to be called.
359-
addToContext.get_future().wait();
360-
361-
// Render a frame so that `AddToContextAsync` will complete.
337+
// Close the script-load frame.
362338
g_update->Finish();
363339
g_device->FinishRenderingCurrentFrame();
364340

365-
// Reopen the gate so JS can continue running (startup may issue bgfx commands).
341+
// Open a new frame for `startup` so the JS-side resource creation and
342+
// startup() call run in the same frame as the wait that observes them.
366343
g_device->StartRenderingCurrentFrame();
367344
g_update->Start();
368345

346+
std::promise<void> startup{};
347+
348+
// Create an external texture for the render target texture and pass it to
349+
// the `startup` JavaScript function.
350+
loader.Dispatch([externalTexture = Babylon::Plugins::ExternalTexture{g_BabylonRenderTexture.get()}, &startup](Napi::Env env) {
351+
auto nativeTexture = externalTexture.CreateForJavaScript(env);
352+
env.Global().Get("startup").As<Napi::Function>().Call(
353+
{
354+
nativeTexture,
355+
Napi::Value::From(env, WIDTH),
356+
Napi::Value::From(env, HEIGHT),
357+
});
358+
startup.set_value();
359+
});
360+
369361
// Wait for `startup` to finish.
370362
startup.get_future().wait();
371363

372-
// Close the frame opened above.
364+
// Close the startup frame.
373365
g_update->Finish();
374366
g_device->FinishRenderingCurrentFrame();
375367

Apps/UnitTests/CMakeLists.txt

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ set(BABYLONJS_MATERIALS_ASSETS
1313
set(TEST_ASSETS
1414
"JavaScript/dist/tests.externalTexture.deviceLoss.js"
1515
"JavaScript/dist/tests.externalTexture.msaa.js"
16+
"JavaScript/dist/tests.externalTexture.render.js"
1617
"JavaScript/dist/tests.javaScript.all.js"
1718
"JavaScript/dist/tests.shaderCache.basicScene.js"
1819
"JavaScript/dist/tests.shaderCompilation.comprehensiveGLSL.js")
@@ -26,6 +27,7 @@ set(SOURCES
2627
"Source/Tests.ExternalTexture.cpp"
2728
"Source/Tests.ExternalTexture.DeviceLoss.cpp"
2829
"Source/Tests.ExternalTexture.Msaa.cpp"
30+
"Source/Tests.ExternalTexture.Render.cpp"
2931
"Source/Tests.JavaScript.cpp"
3032
"Source/Tests.ShaderCache.cpp"
3133
"Source/Tests.ShaderCompilation.cpp"
@@ -40,8 +42,7 @@ endif()
4042

4143
if(GRAPHICS_API STREQUAL "D3D11")
4244
set(SOURCES ${SOURCES}
43-
"Source/Tests.Device.${GRAPHICS_API}.cpp"
44-
"Source/Tests.ExternalTexture.${GRAPHICS_API}.cpp")
45+
"Source/Tests.Device.${GRAPHICS_API}.cpp")
4546
endif()
4647

4748
if(APPLE)
@@ -57,7 +58,11 @@ elseif(UNIX AND NOT ANDROID)
5758
set(SOURCES ${SOURCES} "Source/App.X11.cpp")
5859
set(ADDITIONAL_COMPILE_DEFINITIONS PRIVATE SKIP_EXTERNAL_TEXTURE_TESTS)
5960
elseif(WIN32)
60-
set(SOURCES ${SOURCES} "Source/App.Win32.cpp")
61+
set(SOURCES ${SOURCES}
62+
"Source/App.Win32.cpp"
63+
"Source/RenderDoc.h"
64+
"Source/RenderDoc.cpp")
65+
set(ADDITIONAL_COMPILE_DEFINITIONS ${ADDITIONAL_COMPILE_DEFINITIONS} PRIVATE HAS_RENDERDOC)
6166
endif()
6267

6368
add_executable(UnitTests ${BABYLONJS_ASSETS} ${BABYLONJS_MATERIALS_ASSETS} ${TEST_ASSETS} ${TEST_SCRIPTS} ${SOURCES})

0 commit comments

Comments
 (0)