Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 24 additions & 74 deletions Apps/Playground/Scripts/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -486,40 +486,30 @@
"title": "GLTF Node NegativeScale (0)",
"playgroundId": "#DS8AA7#27",
"replace": "__folder__, Node_NegativeScale, __page__, 0, __disableSRGBBuffers__, 0",
"referenceImage": "gltfNodeNegativeScale0.png",
"excludeFromAutomaticTesting": true,
"reason": "Disabled to land sync PR; V8 cascade victim that crashes Win32 V8 D3D11 with ACCESS_VIOLATION (-1073741819). Will be re-enabled in follow-up fix PR."
"referenceImage": "gltfNodeNegativeScale0.png"
},
{
"title": "GLTF Node NegativeScale (1)",
"playgroundId": "#DS8AA7#27",
"replace": "__folder__, Node_NegativeScale, __page__, 1, __disableSRGBBuffers__, 0",
"referenceImage": "gltfNodeNegativeScale1.png",
"excludeFromAutomaticTesting": true,
"reason": "Disabled to land sync PR; V8 cascade victim that crashes Win32 V8 D3D11 with ACCESS_VIOLATION (-1073741819). Will be re-enabled in follow-up fix PR."
"referenceImage": "gltfNodeNegativeScale1.png"
},
{
"title": "GLTF Texture Sampler (0)",
"playgroundId": "#DS8AA7#27",
"replace": "__folder__, Texture_Sampler, __page__, 0, __disableSRGBBuffers__, 0",
"referenceImage": "gltfTextureSampler0.png",
"excludeFromAutomaticTesting": true,
"reason": "Disabled to land sync PR; V8 cascade victim that crashes Win32 V8 D3D11 with ACCESS_VIOLATION (-1073741819). Will be re-enabled in follow-up fix PR."
"referenceImage": "gltfTextureSampler0.png"
},
{
"title": "GLTF Texture Sampler (1)",
"playgroundId": "#DS8AA7#27",
"replace": "__folder__, Texture_Sampler, __page__, 1, __disableSRGBBuffers__, 0",
"referenceImage": "gltfTextureSampler1.png",
"excludeFromAutomaticTesting": true,
"reason": "Disabled to land sync PR; V8 cascade victim that crashes Win32 V8 D3D11 with ACCESS_VIOLATION (-1073741819). Will be re-enabled in follow-up fix PR."
"referenceImage": "gltfTextureSampler1.png"
},
{
"title": "GLTF Alien",
"playgroundId": "#XN37SR#5",
"referenceImage": "gltfAlien.png",
"excludeFromAutomaticTesting": true,
"reason": "Disabled to land sync PR; V8 cascade victim that crashes Win32 V8 D3D11 with ACCESS_VIOLATION (-1073741819). Will be re-enabled in follow-up fix PR."
"referenceImage": "gltfAlien.png"
},
{
"title": "Interleaved buffers test",
Expand Down Expand Up @@ -622,8 +612,6 @@
{
"title": "NPE - Angle align",
"playgroundId": "#H5RP91",
"excludeFromAutomaticTesting": true,
"reason": "Test crashes or hangs on Babylon Native",
"referenceImage": "npe-angle-align.png"
},
{
Expand Down Expand Up @@ -657,9 +645,7 @@
{
"title": "RH billboard2",
"playgroundId": "#QDVYS4#4",
"referenceImage": "rh-billboard2.png",
"excludeFromAutomaticTesting": true,
"reason": "Disabled to land sync PR; V8 cascade victim that crashes Win32 V8 D3D11 with ACCESS_VIOLATION (-1073741819). Will be re-enabled in follow-up fix PR."
"referenceImage": "rh-billboard2.png"
},
{
"title": "MSDF",
Expand Down Expand Up @@ -789,9 +775,7 @@
{
"title": "Lattice",
"playgroundId": "#MDVD75#19",
"referenceImage": "lattice.png",
"excludeFromAutomaticTesting": true,
"reason": "Disabled to land sync PR; V8 cascade victim that crashes Win32 V8 D3D11 with ACCESS_VIOLATION (-1073741819). Will be re-enabled in follow-up fix PR."
"referenceImage": "lattice.png"
},
{
"title": "HAL Lattice",
Expand All @@ -803,38 +787,28 @@
{
"title": "NME Loop Block",
"playgroundId": "#D8AK3Z#115",
"referenceImage": "nme-loop.png",
"excludeFromAutomaticTesting": true,
"reason": "Test crashes on Win32 V8 D3D11 (access violation)"
"referenceImage": "nme-loop.png"
},
{
"title": "Gaussian Splatting Loading",
"playgroundId": "#CID4NN#204",
"renderCount": 15,
"referenceImage": "gsplat-loading.png",
"excludeFromAutomaticTesting": true,
"reason": "Test crashes on Win32 D3D11 / Sanitizers / V8 D3D11"
"referenceImage": "gsplat-loading.png"
},
{
"title": "Simple refraction",
"playgroundId": "#22KZUW#652",
"referenceImage": "simple-refraction.png",
"excludeFromAutomaticTesting": true,
"reason": "Test crashes on Win32 V8 D3D11 (access violation)"
"referenceImage": "simple-refraction.png"
},
{
"title": "Detail map",
"playgroundId": "#F8FDYT#2",
"referenceImage": "detail-map.png",
"excludeFromAutomaticTesting": true,
"reason": "Test crashes on Win32 D3D11 / Sanitizers (STATUS_BREAKPOINT)"
"referenceImage": "detail-map.png"
},
{
"title": "needDepthPrePass",
"playgroundId": "#7A66KI#1",
"referenceImage": "needDepthPrePass.png",
"excludeFromAutomaticTesting": true,
"reason": "Test crashes on Win32 V8 D3D11 (access violation)"
"referenceImage": "needDepthPrePass.png"
},
{
"title": "Decal map PP",
Expand All @@ -847,16 +821,12 @@
{
"title": "Convolution Post Process",
"playgroundId": "#FBH4J7#281",
"referenceImage": "convolution.png",
"excludeFromAutomaticTesting": true,
"reason": "Test crashes on Win32 D3D11 / Sanitizers (STATUS_BREAKPOINT)"
"referenceImage": "convolution.png"
},
{
"title": "Volumetric Light Scattering Post Process with Skeleton",
"playgroundId": "#5E318S#6",
"referenceImage": "volumetricLightScatteringSkeleton.png",
"excludeFromAutomaticTesting": true,
"reason": "Triggers ACCESS_VIOLATION cascade on Win32 V8 D3D11 -- the next test always crashes regardless of which one it is. Likely heap corruption."
"referenceImage": "volumetricLightScatteringSkeleton.png"
},
{
"title": "Volumetric Light Scattering Post Process with Morph Targets",
Expand Down Expand Up @@ -902,9 +872,7 @@
{
"title": "Trailmesh tapered and untapered",
"playgroundId": "#XGGSWJ#4",
"referenceImage": "trailMesh.png",
"excludeFromAutomaticTesting": true,
"reason": "Test crashes on Win32 V8 D3D11 (access violation cascade)"
"referenceImage": "trailMesh.png"
},
{
"title": "GUI Input Text Area with Placeholder",
Expand All @@ -916,16 +884,12 @@
{
"title": "Ground Projection",
"playgroundId": "#XG08YC#0",
"referenceImage": "groundProjection.png",
"excludeFromAutomaticTesting": true,
"reason": "Test crashes on Win32 V8 D3D11 (access violation cascade)"
"referenceImage": "groundProjection.png"
},
{
"title": "Node geometry",
"playgroundId": "#WGZLGJ#9152",
"referenceImage": "nodeGeometry.png",
"excludeFromAutomaticTesting": true,
"reason": "Test crashes on Win32 V8 D3D11 (access violation cascade)"
"referenceImage": "nodeGeometry.png"
},
{
"title": "Node Geometry - Building generator",
Expand Down Expand Up @@ -965,30 +929,22 @@
{
"title": "Negative scaling with instances",
"playgroundId": "#Z3YS9T#0",
"referenceImage": "negative-scaling-with-instances.png",
"excludeFromAutomaticTesting": true,
"reason": "Test crashes on Win32 V8 D3D11 (access violation cascade)"
"referenceImage": "negative-scaling-with-instances.png"
},
{
"title": "Iridescence",
"playgroundId": "#2FDQT5#1505",
"referenceImage": "iridescence.png",
"excludeFromAutomaticTesting": true,
"reason": "Test crashes on Win32 V8 D3D11 (access violation cascade)"
"referenceImage": "iridescence.png"
},
{
"title": "Merge Meshes",
"playgroundId": "#484RHA#0",
"referenceImage": "mergemeshes.png",
"excludeFromAutomaticTesting": true,
"reason": "Test crashes on Win32 V8 D3D11 (access violation cascade)"
"referenceImage": "mergemeshes.png"
},
{
"title": "Sprites",
"playgroundId": "#ZX8DJ3#1",
"referenceImage": "Sprites.png",
"excludeFromAutomaticTesting": true,
"reason": "Disabled to land sync PR; V8 cascade victim that crashes Win32 V8 D3D11 with ACCESS_VIOLATION (-1073741819). Will be re-enabled in follow-up fix PR."
"referenceImage": "Sprites.png"
},
{
"title": "Procedural texture with NME",
Expand Down Expand Up @@ -1034,9 +990,7 @@
{
"title": "Node material 3",
"playgroundId": "#LWGVT0#2",
"referenceImage": "node-material3.png",
"excludeFromAutomaticTesting": true,
"reason": "Newly added test crashes Win32 V8 D3D11 with ACCESS_VIOLATION (-1073741819)."
"referenceImage": "node-material3.png"
},
{
"title": "Node material 4",
Expand All @@ -1048,9 +1002,7 @@
{
"title": "Node material 5",
"playgroundId": "#V8VH7B#0",
"referenceImage": "node-material5.png",
"excludeFromAutomaticTesting": true,
"reason": "Newly added test crashes Win32 V8 D3D11 with ACCESS_VIOLATION (-1073741819)."
"referenceImage": "node-material5.png"
},
{
"title": "Node material 6",
Expand Down Expand Up @@ -1170,9 +1122,7 @@
{
"title": "Color Grading",
"playgroundId": "#8EDB5N#2",
"referenceImage": "colorGrading.png",
"excludeFromAutomaticTesting": true,
"reason": "Newly added test crashes Win32 V8 D3D11 with ACCESS_VIOLATION (-1073741819)."
"referenceImage": "colorGrading.png"
},
{
"title": "Clip planes",
Expand Down
11 changes: 11 additions & 0 deletions Dependencies/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,17 @@ target_compile_definitions(bgfx PRIVATE BGFX_CONFIG_UNIFORM_BUFFER_RESIZE_THRESH
target_compile_definitions(bgfx PRIVATE BGFX_CONFIG_UNIFORM_BUFFER_RESIZE_INCREMENT_SIZE=1024)
target_compile_definitions(bgfx PUBLIC BGFX_PLATFORM_SUPPORTS_WGSL=0)

# Canvas plugin allocates one bgfx framebuffer per JS Canvas object and per
# text-rendering operation; combined with V8 GC pacing this can exceed the
# default 128 limit during long playground sweeps. We enforce a floor of 512:
# CI workflows pass -DBGFX_CONFIG_MAX_FRAME_BUFFERS, and any value below 512
# (including a smaller CI override) is clamped up so the pool never regresses
# below the size the Canvas sweeps need.
if(NOT BGFX_CONFIG_MAX_FRAME_BUFFERS OR BGFX_CONFIG_MAX_FRAME_BUFFERS LESS 512)
set(BGFX_CONFIG_MAX_FRAME_BUFFERS 512)
endif()
target_compile_definitions(bgfx PRIVATE BGFX_CONFIG_MAX_FRAME_BUFFERS=${BGFX_CONFIG_MAX_FRAME_BUFFERS})
Comment thread
bkaradzic-microsoft marked this conversation as resolved.

# Temporary disable uniform debug.
target_compile_definitions(bgfx PRIVATE BGFX_CONFIG_DEBUG_UNIFORM=0)

Expand Down
14 changes: 13 additions & 1 deletion Polyfills/Canvas/Source/Canvas.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,19 @@ namespace Babylon::Polyfills::Internal
attachments[0].init(textures[0], bgfx::Access::Write, 0, 1, 0, colorResolve);
attachments[1].init(textures[1], bgfx::Access::Write, 0, 1, 0, BGFX_RESOLVE_NONE);
auto handle = bgfx::createFrameBuffer(static_cast<uint8_t>(attachments.size()), attachments.data(), true);
assert(handle.idx != bgfx::kInvalidHandle);
if (!bgfx::isValid(handle))
{
// Free any textures we managed to allocate so they don't leak alongside the failed FB.
// Guard with isValid: a failed createTexture2D returns an invalid handle that bgfx::destroy would assert on.
for (auto texture : textures)
{
if (bgfx::isValid(texture))
{
bgfx::destroy(texture);
}
}
throw std::runtime_error{"bgfx::createFrameBuffer returned invalid handle (framebuffer pool exhausted; raise BGFX_CONFIG_MAX_FRAME_BUFFERS or audit Canvas/Context lifetime)"};
}
m_frameBuffer = std::make_unique<Graphics::FrameBuffer>(m_graphicsContext, handle, m_width, m_height, false, false, false);
m_dirty = false;

Expand Down
89 changes: 50 additions & 39 deletions Polyfills/Canvas/Source/Context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -661,52 +661,63 @@ namespace Babylon::Polyfills::Internal
}
}

void Context::Flush(const Napi::CallbackInfo&)
void Context::Flush(const Napi::CallbackInfo& info)
{
EnsureFontsLoaded();

bool needClear = m_canvas->UpdateRenderTarget();

Graphics::FrameBuffer& frameBuffer = m_canvas->GetFrameBuffer();

auto updateToken{m_update.GetUpdateToken()};
bgfx::Encoder* encoder = updateToken.GetEncoder();
frameBuffer.Bind(*encoder);
if (needClear)
try
{
frameBuffer.Clear(*encoder, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH | BGFX_CLEAR_STENCIL, 0, 1.f, 0);
}
frameBuffer.SetViewPort(*encoder, 0.f, 0.f, 1.f, 1.f);
const auto width = m_canvas->GetWidth();
const auto height = m_canvas->GetHeight();
// The entire flush is wrapped: bgfx framebuffer pool exhaustion can throw both from
// UpdateRenderTarget() and from FrameBufferPool::Acquire() reached via nvgEndFrame()
// below. Converting any such C++ failure into a catchable JS error lets the offending
// test fail cleanly instead of aborting the whole sweep.
const bool needClear = m_canvas->UpdateRenderTarget();

Graphics::FrameBuffer& frameBuffer = m_canvas->GetFrameBuffer();

auto updateToken{m_update.GetUpdateToken()};
bgfx::Encoder* encoder = updateToken.GetEncoder();
frameBuffer.Bind(*encoder);
if (needClear)
{
frameBuffer.Clear(*encoder, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH | BGFX_CLEAR_STENCIL, 0, 1.f, 0);
}
frameBuffer.SetViewPort(*encoder, 0.f, 0.f, 1.f, 1.f);
const auto width = m_canvas->GetWidth();
const auto height = m_canvas->GetHeight();

for (auto& buffer : m_canvas->m_frameBufferPool.GetPoolBuffers())
{
// sanity check no buffers should have been acquired yet
assert(buffer.isAvailable == true);
for (auto& buffer : m_canvas->m_frameBufferPool.GetPoolBuffers())
{
// sanity check no buffers should have been acquired yet
assert(buffer.isAvailable == true);
}
std::function<Babylon::Graphics::FrameBuffer*()> acquire = [this, encoder]() -> Babylon::Graphics::FrameBuffer* {
Babylon::Graphics::FrameBuffer *frameBuffer = this->m_canvas->m_frameBufferPool.Acquire();
frameBuffer->Bind(*encoder);
return frameBuffer;
};
std::function<void(Babylon::Graphics::FrameBuffer*)> release = [this, encoder](Babylon::Graphics::FrameBuffer* frameBuffer) -> void {
// clear framebuffer when released
frameBuffer->Clear(*encoder, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH | BGFX_CLEAR_STENCIL, 0, 1.f, 0);
this->m_canvas->m_frameBufferPool.Release(frameBuffer);
frameBuffer->Unbind(*encoder);
};

nvgBeginFrame(*m_nvg, float(width), float(height), 1.0f);
nvgSetFrameBufferAndEncoder(*m_nvg, frameBuffer, encoder);
nvgSetFrameBufferPool(*m_nvg, { acquire, release });
nvgEndFrame(*m_nvg);
frameBuffer.Unbind(*encoder);

for (auto& buffer : m_canvas->m_frameBufferPool.GetPoolBuffers())
{
// sanity check no unreleased buffers
assert(buffer.isAvailable == true);
}
}
std::function<Babylon::Graphics::FrameBuffer*()> acquire = [this, encoder]() -> Babylon::Graphics::FrameBuffer* {
Babylon::Graphics::FrameBuffer *frameBuffer = this->m_canvas->m_frameBufferPool.Acquire();
frameBuffer->Bind(*encoder);
return frameBuffer;
};
std::function<void(Babylon::Graphics::FrameBuffer*)> release = [this, encoder](Babylon::Graphics::FrameBuffer* frameBuffer) -> void {
// clear framebuffer when released
frameBuffer->Clear(*encoder, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH | BGFX_CLEAR_STENCIL, 0, 1.f, 0);
this->m_canvas->m_frameBufferPool.Release(frameBuffer);
frameBuffer->Unbind(*encoder);
};

nvgBeginFrame(*m_nvg, float(width), float(height), 1.0f);
nvgSetFrameBufferAndEncoder(*m_nvg, frameBuffer, encoder);
nvgSetFrameBufferPool(*m_nvg, { acquire, release });
nvgEndFrame(*m_nvg);
frameBuffer.Unbind(*encoder);

for (auto& buffer : m_canvas->m_frameBufferPool.GetPoolBuffers())
catch (const std::exception& ex)
{
// sanity check no unreleased buffers
assert(buffer.isAvailable == true);
throw Napi::Error::New(info.Env(), ex.what());
}
}

Expand Down
13 changes: 13 additions & 0 deletions Polyfills/Canvas/Source/FrameBufferPool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,19 @@ namespace Babylon::Polyfills
attachments[1].init(textures[1], bgfx::Access::Write, 0, 1, 0, BGFX_RESOLVE_NONE);
TextBuffer = bgfx::createFrameBuffer(static_cast<uint8_t>(attachments.size()), attachments.data(), true);

if (!bgfx::isValid(TextBuffer))
{
// Guard with isValid: a failed createTexture2D returns an invalid handle that bgfx::destroy would assert on.
for (auto texture : textures)
{
if (bgfx::isValid(texture))
{
bgfx::destroy(texture);
}
}
throw std::runtime_error{"FrameBufferPool::Add: bgfx::createFrameBuffer returned invalid handle (pool exhausted)"};
}

FrameBuffer = new Graphics::FrameBuffer(*m_graphicsContext, TextBuffer, m_width, m_height, false, false, false);
m_available++;
mPoolBuffers.push_back({FrameBuffer, true});
Expand Down
Loading