From a3da708ffb247e0c5bff6d3ab07a295db89611d6 Mon Sep 17 00:00:00 2001 From: Gary Hsu Date: Thu, 30 Jul 2026 13:48:50 -0700 Subject: [PATCH 1/3] Let embedders override Babylon Native's bgfx configuration Babylon Native tunes a number of bgfx compile-time settings in Dependencies/CMakeLists.txt. They were applied unconditionally, so an embedder could not change any of them. The definitions are emitted after bgfx.cmake's, which puts them last on the compile line, so they take effect regardless of what the embedder asked for. For a setting bgfx.cmake does not forward, such as BGFX_CONFIG_MAX_VERTEX_STREAMS, the embedder's value never reached the compile line at all and was silently ignored. For one it does forward, both values reached it and Babylon Native's won, leaving a conflicting duplicate behind. Embedders have worked around this by reading COMPILE_DEFINITIONS and INTERFACE_COMPILE_DEFINITIONS back off the bgfx target and filtering entries out. Route these through babylon_native_bgfx_config(), which uses the embedder's value when one is set and Babylon Native's otherwise. Being emitted last still decides the outcome; the value being emitted is now the embedder's whenever they expressed one. The value is used as written rather than being required to be numeric, since bgfx spells most of its own defaults as expressions such as (4<<10). CMake booleans are the exception and fall back to the default, because the C preprocessor cannot use them and bgfx.cmake declares several of these names as options that can already hold ON or OFF. Also gate the BGFX_CONFIG_MAX_FRAME_BUFFERS floor of 512 on BABYLON_NATIVE_POLYFILL_CANVAS. The floor exists because the Canvas polyfill allocates a framebuffer per canvas and per text-rendering operation; an embedder that disables the polyfill draws into a handful of them and should be able to select a smaller pool. No behaviour change by default. Verified by configuring three ways and reading the generated definitions. Default: MAX_VERTEX_STREAMS=18, MAX_FRAME_BUFFERS=512, MIN_UNIFORM_BUFFER_SIZE=4096, unchanged, and no setting emitted more than once. Canvas off with overrides: 12, 4 and 2048 respectively, plus MAX_TEXTURES=(4<<10) passed through verbatim, so the filtering workaround is no longer needed. Canvas on with an explicit smaller frame-buffer pool: still clamped up to the 512 the Canvas sweeps need. UnitTests pass (17/17). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 714b4495-258e-4645-abf7-26c17bc29d5b --- Dependencies/CMakeLists.txt | 70 ++++++++++++++++++++++++++----------- 1 file changed, 50 insertions(+), 20 deletions(-) diff --git a/Dependencies/CMakeLists.txt b/Dependencies/CMakeLists.txt index 9f7a5a6f4..3e8bcaa22 100644 --- a/Dependencies/CMakeLists.txt +++ b/Dependencies/CMakeLists.txt @@ -40,38 +40,68 @@ set(BGFX_USE_DEBUG_SUFFIX OFF) FetchContent_MakeAvailable_With_Message(bgfx.cmake) +# Babylon Native tunes a number of bgfx compile-time settings. Every value passed to +# babylon_native_bgfx_config below is a *default*: an embedder can override any of them +# by setting the matching BGFX_CONFIG_ variable before adding Babylon Native. +# +# The value is used as written, so an embedder can spell one the way bgfx spells its own +# defaults -- (4<<10) and the like. CMake booleans are the one thing that cannot be used, +# because the C preprocessor has nothing to do with them, and bgfx.cmake declares several +# of these names as options that can already hold ON or OFF. Those fall back to the +# default below. +# +# These are emitted here rather than left to bgfx.cmake because emitting the resolved +# value here is what makes overriding work at all: these definitions are added after +# bgfx.cmake's, so they are last on the compile line and win. Before this indirection they +# were hard-coded, so an embedder's value produced a conflicting duplicate definition and +# the hard-coded one took effect. +function(babylon_native_bgfx_config NAME DEFAULT) + set(value "${BGFX_CONFIG_${NAME}}") + string(TOUPPER "${value}" value_upper) + if(value STREQUAL "" OR value_upper MATCHES "^(ON|OFF|TRUE|FALSE|YES|NO|Y|N|IGNORE|NOTFOUND|.*-NOTFOUND)$") + set(value "${DEFAULT}") + endif() + target_compile_definitions(bgfx PRIVATE BGFX_CONFIG_${NAME}=${value}) +endfunction() + # Turn off debug annotations as it causes an access violation in D3D12. # This flag is set using compile definitions because the bgfx.cmake option is ignored in debug configuration. # See https://github.com/BabylonJS/bgfx.cmake/blob/0af3c9865a66aff1748a51bb466b24f05a123043/cmake/bgfx/bgfx.cmake#L126. -target_compile_definitions(bgfx PRIVATE BGFX_CONFIG_DEBUG_ANNOTATION=0) - -target_compile_definitions(bgfx PRIVATE BGFX_CONFIG_DEFAULT_MAX_ENCODERS=2) -target_compile_definitions(bgfx PRIVATE BGFX_CONFIG_MAX_VERTEX_STREAMS=18) -target_compile_definitions(bgfx PRIVATE BGFX_CONFIG_MIN_RESOURCE_COMMAND_BUFFER_SIZE=16) -target_compile_definitions(bgfx PRIVATE BGFX_CONFIG_MIN_UNIFORM_BUFFER_SIZE=4096) -target_compile_definitions(bgfx PRIVATE BGFX_CONFIG_UNIFORM_BUFFER_RESIZE_THRESHOLD_SIZE=256) -target_compile_definitions(bgfx PRIVATE BGFX_CONFIG_UNIFORM_BUFFER_RESIZE_INCREMENT_SIZE=1024) +babylon_native_bgfx_config(DEBUG_ANNOTATION 0) + +babylon_native_bgfx_config(DEFAULT_MAX_ENCODERS 2) +babylon_native_bgfx_config(MAX_VERTEX_STREAMS 18) +babylon_native_bgfx_config(MIN_RESOURCE_COMMAND_BUFFER_SIZE 16) +babylon_native_bgfx_config(MIN_UNIFORM_BUFFER_SIZE 4096) +babylon_native_bgfx_config(UNIFORM_BUFFER_RESIZE_THRESHOLD_SIZE 256) +babylon_native_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) +# The Canvas polyfill 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, so Canvas builds need a floor of 512. +# That floor is a Canvas requirement, not a universal one: an embedder that disables +# the polyfill draws into a handful of framebuffers and should be able to select a +# much smaller pool. Clamp only when Canvas is actually being built, and otherwise +# leave bgfx on its own default unless the embedder asked for something. +if(BABYLON_NATIVE_POLYFILL_CANVAS) + if(NOT BGFX_CONFIG_MAX_FRAME_BUFFERS OR BGFX_CONFIG_MAX_FRAME_BUFFERS LESS 512) + set(BGFX_CONFIG_MAX_FRAME_BUFFERS 512) + endif() +endif() + +if(BGFX_CONFIG_MAX_FRAME_BUFFERS) + babylon_native_bgfx_config(MAX_FRAME_BUFFERS ${BGFX_CONFIG_MAX_FRAME_BUFFERS}) endif() -target_compile_definitions(bgfx PRIVATE BGFX_CONFIG_MAX_FRAME_BUFFERS=${BGFX_CONFIG_MAX_FRAME_BUFFERS}) # Temporary disable uniform debug. -target_compile_definitions(bgfx PRIVATE BGFX_CONFIG_DEBUG_UNIFORM=0) +babylon_native_bgfx_config(DEBUG_UNIFORM 0) # Disable video decoding support (not used by Babylon Native). -target_compile_definitions(bgfx PRIVATE BGFX_CONFIG_VIDEO=0) +babylon_native_bgfx_config(VIDEO 0) # Disable the C99 API (Babylon Native uses the C++ API only); saves binary size. -target_compile_definitions(bgfx PRIVATE BGFX_CONFIG_C99_API=0) +babylon_native_bgfx_config(C99_API 0) if(GRAPHICS_API STREQUAL "D3D11") target_compile_definitions(bgfx PRIVATE BGFX_CONFIG_RENDERER_DIRECT3D11=1) From e24c02bc5eb52bbecb9675ae93529b11b72767bd Mon Sep 17 00:00:00 2001 From: Gary Hsu Date: Fri, 31 Jul 2026 08:58:46 -0700 Subject: [PATCH 2/3] Set Babylon Native's bgfx settings as defaults before bgfx.cmake is added The settings were applied with target_compile_definitions after bgfx.cmake had already been added. That put them last on the compile line, so they took effect regardless of what an embedder asked for: a setting bgfx.cmake forwards ended up defined twice with Babylon Native's value winning, and one it does not forward never reached the compile line at all. Embedders work around this by reading COMPILE_DEFINITIONS and INTERFACE_COMPILE_DEFINITIONS back off the bgfx target and filtering entries out. Set them as ordinary variables before bgfx.cmake is added instead, each only when nothing has supplied a value, and let bgfx.cmake forward whichever value survives. A setting is now defined once, and an embedder's value is used as written -- including expressions such as (4<<10), which is how bgfx spells most of its own defaults. This depends on BabylonJS/bgfx.cmake#137. Before it, a set() in this scope is erased by bgfx.cmake's own set( "" CACHE STRING ...) and never seen. BGFX_CONFIG_DEBUG_ANNOTATION stays a compile definition. bgfx.cmake forwards it through a generator expression that turns it on in Debug regardless of the value, so overriding it after the fact is the only thing that works. BGFX_CONFIG_MAX_FRAME_BUFFERS becomes a plain default of 512 rather than a floor. The floor could not do its job anyway: LESS returns false rather than erroring on a non-numeric value, so an expression passed straight through unclamped -- '(1<<3)' gave 8 framebuffers where 512 was required. As a default it is honored when nothing else is set and stays out of the way when something is. The CI workflows passed 256 and relied on being clamped up, so they no longer pass it at all. Verified by configuring and reading the definitions off the bgfx target. Default build: every Babylon Native value present exactly once, MAX_FRAME_BUFFERS=512. With overrides and Canvas off: MAX_VERTEX_STREAMS=12, MIN_UNIFORM_BUFFER_SIZE=2048, DRAW_CALL_BLOCK=128 and MAX_TEXTURES=(4<<10) all honored, no MAX_FRAME_BUFFERS, and the remaining Babylon Native defaults untouched. UnitTests pass (17/17). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 714b4495-258e-4645-abf7-26c17bc29d5b --- .github/workflows/build-win32-shader.yml | 1 - .github/workflows/build-win32.yml | 1 - Dependencies/CMakeLists.txt | 71 ++++++++---------------- nightly.yml | 2 +- 4 files changed, 25 insertions(+), 50 deletions(-) diff --git a/.github/workflows/build-win32-shader.yml b/.github/workflows/build-win32-shader.yml index 3a0ac37a2..7a6d7b5a0 100644 --- a/.github/workflows/build-win32-shader.yml +++ b/.github/workflows/build-win32-shader.yml @@ -29,7 +29,6 @@ jobs: -A ${{ inputs.platform }} ^ -D BX_CONFIG_DEBUG=ON ^ -D GRAPHICS_API=D3D11 ^ - -D BGFX_CONFIG_MAX_FRAME_BUFFERS=256 ^ -D BABYLON_DEBUG_TRACE=ON ^ -D BABYLON_NATIVE_PLUGIN_NATIVEENGINE_COMPILESHADERS=OFF diff --git a/.github/workflows/build-win32.yml b/.github/workflows/build-win32.yml index 64198185b..d260818ae 100644 --- a/.github/workflows/build-win32.yml +++ b/.github/workflows/build-win32.yml @@ -65,7 +65,6 @@ jobs: ${{ steps.vars.outputs.js_define }} ^ -D BX_CONFIG_DEBUG=ON ^ -D GRAPHICS_API=${{ inputs.graphics-api }} ^ - -D BGFX_CONFIG_MAX_FRAME_BUFFERS=256 ^ -D BABYLON_DEBUG_TRACE=ON ^ -D BABYLON_NATIVE_PLUGIN_NATIVEDRACO=ON ^ -D BABYLON_NATIVE_PLUGIN_NATIVEMESHOPT=ON ^ diff --git a/Dependencies/CMakeLists.txt b/Dependencies/CMakeLists.txt index 3e8bcaa22..f9d787d20 100644 --- a/Dependencies/CMakeLists.txt +++ b/Dependencies/CMakeLists.txt @@ -38,36 +38,14 @@ set(BGFX_INSTALL OFF) set(BGFX_OPENGL_USE_EGL ON) set(BGFX_USE_DEBUG_SUFFIX OFF) -FetchContent_MakeAvailable_With_Message(bgfx.cmake) - -# Babylon Native tunes a number of bgfx compile-time settings. Every value passed to -# babylon_native_bgfx_config below is a *default*: an embedder can override any of them -# by setting the matching BGFX_CONFIG_ variable before adding Babylon Native. -# -# The value is used as written, so an embedder can spell one the way bgfx spells its own -# defaults -- (4<<10) and the like. CMake booleans are the one thing that cannot be used, -# because the C preprocessor has nothing to do with them, and bgfx.cmake declares several -# of these names as options that can already hold ON or OFF. Those fall back to the -# default below. -# -# These are emitted here rather than left to bgfx.cmake because emitting the resolved -# value here is what makes overriding work at all: these definitions are added after -# bgfx.cmake's, so they are last on the compile line and win. Before this indirection they -# were hard-coded, so an embedder's value produced a conflicting duplicate definition and -# the hard-coded one took effect. -function(babylon_native_bgfx_config NAME DEFAULT) - set(value "${BGFX_CONFIG_${NAME}}") - string(TOUPPER "${value}" value_upper) - if(value STREQUAL "" OR value_upper MATCHES "^(ON|OFF|TRUE|FALSE|YES|NO|Y|N|IGNORE|NOTFOUND|.*-NOTFOUND)$") - set(value "${DEFAULT}") +# Babylon Native's bgfx settings are defaults. A value already supplied, on the command +# line or by a project embedding Babylon Native, is left alone. bgfx.cmake forwards +# whichever value survives, so each setting reaches the compiler exactly once. +macro(babylon_native_bgfx_config NAME DEFAULT) + if("${BGFX_CONFIG_${NAME}}" STREQUAL "") + set(BGFX_CONFIG_${NAME} "${DEFAULT}") endif() - target_compile_definitions(bgfx PRIVATE BGFX_CONFIG_${NAME}=${value}) -endfunction() - -# Turn off debug annotations as it causes an access violation in D3D12. -# This flag is set using compile definitions because the bgfx.cmake option is ignored in debug configuration. -# See https://github.com/BabylonJS/bgfx.cmake/blob/0af3c9865a66aff1748a51bb466b24f05a123043/cmake/bgfx/bgfx.cmake#L126. -babylon_native_bgfx_config(DEBUG_ANNOTATION 0) +endmacro() babylon_native_bgfx_config(DEFAULT_MAX_ENCODERS 2) babylon_native_bgfx_config(MAX_VERTEX_STREAMS 18) @@ -75,24 +53,6 @@ babylon_native_bgfx_config(MIN_RESOURCE_COMMAND_BUFFER_SIZE 16) babylon_native_bgfx_config(MIN_UNIFORM_BUFFER_SIZE 4096) babylon_native_bgfx_config(UNIFORM_BUFFER_RESIZE_THRESHOLD_SIZE 256) babylon_native_bgfx_config(UNIFORM_BUFFER_RESIZE_INCREMENT_SIZE 1024) -target_compile_definitions(bgfx PUBLIC BGFX_PLATFORM_SUPPORTS_WGSL=0) - -# The Canvas polyfill 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, so Canvas builds need a floor of 512. -# That floor is a Canvas requirement, not a universal one: an embedder that disables -# the polyfill draws into a handful of framebuffers and should be able to select a -# much smaller pool. Clamp only when Canvas is actually being built, and otherwise -# leave bgfx on its own default unless the embedder asked for something. -if(BABYLON_NATIVE_POLYFILL_CANVAS) - if(NOT BGFX_CONFIG_MAX_FRAME_BUFFERS OR BGFX_CONFIG_MAX_FRAME_BUFFERS LESS 512) - set(BGFX_CONFIG_MAX_FRAME_BUFFERS 512) - endif() -endif() - -if(BGFX_CONFIG_MAX_FRAME_BUFFERS) - babylon_native_bgfx_config(MAX_FRAME_BUFFERS ${BGFX_CONFIG_MAX_FRAME_BUFFERS}) -endif() # Temporary disable uniform debug. babylon_native_bgfx_config(DEBUG_UNIFORM 0) @@ -103,6 +63,23 @@ babylon_native_bgfx_config(VIDEO 0) # Disable the C99 API (Babylon Native uses the C++ API only); saves binary size. babylon_native_bgfx_config(C99_API 0) +# The Canvas polyfill allocates one bgfx framebuffer per JS Canvas object and per +# text-rendering operation; combined with V8 GC pacing this can exceed the bgfx default +# of 128 during long playground sweeps. An embedder that disables the polyfill draws +# into a handful of framebuffers and has no reason to pay for the larger pool. +if(BABYLON_NATIVE_POLYFILL_CANVAS) + babylon_native_bgfx_config(MAX_FRAME_BUFFERS 512) +endif() + +FetchContent_MakeAvailable_With_Message(bgfx.cmake) + +# Turn off debug annotations as it causes an access violation in D3D12. +# This flag is set using compile definitions because the bgfx.cmake option is ignored in debug configuration. +# See https://github.com/BabylonJS/bgfx.cmake/blob/0af3c9865a66aff1748a51bb466b24f05a123043/cmake/bgfx/bgfx.cmake#L126. +target_compile_definitions(bgfx PRIVATE BGFX_CONFIG_DEBUG_ANNOTATION=0) + +target_compile_definitions(bgfx PUBLIC BGFX_PLATFORM_SUPPORTS_WGSL=0) + if(GRAPHICS_API STREQUAL "D3D11") target_compile_definitions(bgfx PRIVATE BGFX_CONFIG_RENDERER_DIRECT3D11=1) elseif(GRAPHICS_API STREQUAL "D3D12") diff --git a/nightly.yml b/nightly.yml index b85eba28a..95ba71035 100644 --- a/nightly.yml +++ b/nightly.yml @@ -21,7 +21,7 @@ jobs: - checkout: self - script: | - cmake -G "Visual Studio 17 2022" -B build -A x64 -D BX_CONFIG_DEBUG=ON -D BGFX_CONFIG_MAX_FRAME_BUFFERS=256 -D BABYLON_DEBUG_TRACE=ON + cmake -G "Visual Studio 17 2022" -B build -A x64 -D BX_CONFIG_DEBUG=ON -D BABYLON_DEBUG_TRACE=ON displayName: 'Generate solution' - script: | From 013b708030a15045ba41c933ec05519f9a93a279 Mon Sep 17 00:00:00 2001 From: Gary Hsu Date: Mon, 3 Aug 2026 13:26:17 -0700 Subject: [PATCH 3/3] Bump bgfx.cmake to the commit that honors parent BGFX_CONFIG_ settings Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 714b4495-258e-4645-abf7-26c17bc29d5b --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 298e4413f..4ca182cbd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,7 +35,7 @@ FetchContent_Declare(base-n EXCLUDE_FROM_ALL) FetchContent_Declare(bgfx.cmake GIT_REPOSITORY https://github.com/BabylonJS/bgfx.cmake.git - GIT_TAG 5c98749de48e8609a62e5c1fd2cfffbbd970588e + GIT_TAG e7773eed1d0c37f663420e9b8e15d16903aef68f EXCLUDE_FROM_ALL) FetchContent_Declare(CMakeExtensions GIT_REPOSITORY https://github.com/BabylonJS/CMakeExtensions.git