Skip to content
Open
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
113 changes: 113 additions & 0 deletions Apps/Playground/Shared/Diagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <cstdlib>
#include <cstring>
#include <string>
#include <thread>

#if defined(_MSC_VER)
#define WIN32_LEAN_AND_MEAN
Expand All @@ -21,6 +22,8 @@
#include <stdlib.h>
#include <io.h>
#include <wchar.h>
#include <psapi.h>
#pragma comment(lib, "psapi.lib")
#else
#include <unistd.h>
#endif
Expand All @@ -37,6 +40,66 @@ namespace

bool s_ansiEnabled{false};

// Heartbeat: prints "[hb] T=Ns WS=Mb" every N seconds so CI logs reveal
// long-running native work (e.g. EXR decode under WARP+ASAN) instead of
// appearing as opaque silence. Stops at PrintFinishLine.
std::atomic<bool> s_heartbeatStop{false};
std::thread s_heartbeatThread;

void HeartbeatLoop(int intervalSeconds)
{
// Stagger first tick so any "Running <test>" line lands in the log
// before our first heartbeat marker.
const auto interval = std::chrono::seconds(intervalSeconds);

while (!s_heartbeatStop.load(std::memory_order_relaxed))
{
// Sleep in short slices so shutdown is responsive.
for (int i = 0; i < intervalSeconds * 10; ++i)
{
if (s_heartbeatStop.load(std::memory_order_relaxed))
{
return;
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}

const auto elapsed = std::chrono::steady_clock::now() - s_startTime;
const long long sec = std::chrono::duration_cast<std::chrono::seconds>(elapsed).count();

long long wsMb = -1;
#if defined(_MSC_VER)
PROCESS_MEMORY_COUNTERS_EX pmc{};
pmc.cb = sizeof(pmc);
if (::GetProcessMemoryInfo(::GetCurrentProcess(),
reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&pmc),
sizeof(pmc)))
{
wsMb = static_cast<long long>(pmc.WorkingSetSize / (1024 * 1024));
}
#endif

if (wsMb >= 0)
{
std::fprintf(stdout, "[hb] T=%llds WS=%lldMB\n", sec, wsMb);
}
else
{
std::fprintf(stdout, "[hb] T=%llds\n", sec);
}
std::fflush(stdout);
}
}

void StopHeartbeat()
{
s_heartbeatStop.store(true, std::memory_order_relaxed);
if (s_heartbeatThread.joinable())
{
s_heartbeatThread.join();
}
}

#if defined(_MSC_VER)
void __cdecl OnInvalidParameter(
const wchar_t* expression,
Expand Down Expand Up @@ -120,7 +183,21 @@ namespace Diagnostics
return;
}

#if defined(__SANITIZE_ADDRESS__)
// Under ASAN the sanitizer runtime installs its own SEH /
// _set_invalid_parameter_handler / abort hooks and provides its
// own crash diagnostics. Letting bx install its handlers on top
// (since bgfx commit eed706f, "Suppress MSVC CRT assert dialogs")
// adds a _set_thread_local_invalid_parameter_handler + SEH
// top-level filter + _set_purecall_handler that BN doesn't
// override -- inside those, bx walks the callstack via dbghelp,
// which can deadlock against ASAN's allocator lock when a
// sanitizer-instrumented allocation races with handler entry.
// BN's own _set_invalid_parameter_handler / SIGABRT / CRT report
// hooks below cover the diagnostics paths we actually rely on.
#else
bx::installExceptionHandler();
#endif

#if defined(_MSC_VER)
// Route assert() to stderr instead of UCRT's modal dialog. Covers the
Expand Down Expand Up @@ -188,6 +265,40 @@ namespace Diagnostics
// via s_finishPrinted; whichever fires first wins.
std::atexit(&PrintFinishLine);
std::at_quick_exit(&PrintFinishLine);

// Heartbeat thread: only enabled when explicitly opted-in via env
// var BN_HEARTBEAT_SECONDS=<positive int>, or always under ASAN where
// long bimg/WARP work otherwise looks like a deadlock in CI logs.
int interval = 0;
#if defined(__SANITIZE_ADDRESS__)
interval = 10;
#endif
const char* envInterval = nullptr;
#if defined(_MSC_VER)
char* envBuf = nullptr;
size_t envLen = 0;
if (::_dupenv_s(&envBuf, &envLen, "BN_HEARTBEAT_SECONDS") == 0)
{
envInterval = envBuf;
}
#else
envInterval = std::getenv("BN_HEARTBEAT_SECONDS");
#endif
if (envInterval != nullptr)
{
interval = std::atoi(envInterval);
}
#if defined(_MSC_VER)
if (envBuf != nullptr)
{
std::free(envBuf);
}
#endif
if (interval > 0)
{
s_heartbeatStop.store(false, std::memory_order_relaxed);
s_heartbeatThread = std::thread(&HeartbeatLoop, interval);
}
}

void SetExitCode(int code)
Expand All @@ -203,6 +314,8 @@ namespace Diagnostics
return;
}

StopHeartbeat();

const int code = s_exitCode.load(std::memory_order_relaxed);
const bool success = (code == 0);

Expand Down
9 changes: 8 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,14 @@ FetchContent_Declare(base-n
EXCLUDE_FROM_ALL)
FetchContent_Declare(bgfx.cmake
GIT_REPOSITORY https://github.com/BabylonJS/bgfx.cmake.git
GIT_TAG e5f3f31c1ec8a376dde75f8b20366e2696810ea0
# Temporary bisection pin to bgfx.cmake#119 (5a00e8d) to narrow down
# the bgfx commit that introduced the Win32_x64_D3D11_Sanitizers /
# EXR Loader CI hang. This is the first clean bgfx pin after master
# (waypoint A = 98531b5 has unresolved merge conflict markers in
# renderer_d3d11.cpp from bgfx commit 056846eb, fixed by 4d5276c7e
# which is included in this pin). Will be restored to af75c874 once
# the bad commit is identified and fixed/reverted upstream.
GIT_TAG 5a00e8df9e409ec459dcd7ea9e4607c07262c373
EXCLUDE_FROM_ALL)
FetchContent_Declare(CMakeExtensions
GIT_REPOSITORY https://github.com/BabylonJS/CMakeExtensions.git
Expand Down
14 changes: 14 additions & 0 deletions Dependencies/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,20 @@ set_property(TARGET bimg PROPERTY FOLDER Dependencies/bgfx)
set_property(TARGET bx PROPERTY FOLDER Dependencies/bgfx)
target_compile_definitions(bx PRIVATE _CRT_SECURE_NO_WARNINGS)

if(UNIX AND NOT APPLE AND NOT ANDROID AND CMAKE_SYSTEM_PROCESSOR MATCHES "(x86_64|amd64|AMD64)")
# The new bx requires SSE4.2 on x86_64 (see bx scripts/toolchain.lua and
# include/bx/inline/simd128_sse.inl which uses _mm_round_ps / _mm_blendv_ps
# under "SSE4.1 - always available with SSE4.2 minspec"). bgfx.cmake does
# not propagate this flag, so add it explicitly on Linux x86_64 where
# GCC/Clang don't enable SSE4.x by default. MSVC and Apple Silicon are
# unaffected.
foreach(_bx_target IN ITEMS bx bgfx bimg bimg_decode bimg_encode)
if(TARGET ${_bx_target})
target_compile_options(${_bx_target} PRIVATE -msse4.2)
endif()
endforeach()
endif()

if(UNIX AND NOT APPLE AND NOT ANDROID)
# Use GLVND libraries for EGL support in bgfx
set(OpenGL_GL_PREFERENCE GLVND)
Expand Down
5 changes: 3 additions & 2 deletions Polyfills/Canvas/Source/nanovg/nanovg_babylon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@

#include <bx/bx.h>
#include <bx/allocator.h>
#include <bx/math.h>

#include <Babylon/Graphics/DeviceContext.h>
#include <Babylon/Graphics/FrameBuffer.h>
Expand Down Expand Up @@ -961,8 +962,8 @@ namespace

static uint64_t glnvg_convertBlendFuncFactor(int factor)
{
const uint32_t numtz = bx::uint32_cnttz(factor);
const uint32_t idx = bx::uint32_min(numtz, BX_COUNTOF(s_blend)-1);
const uint32_t numtz = bx::countTrailingZeros<uint32_t>(uint32_t(factor));
const uint32_t idx = bx::min<uint32_t>(numtz, BX_COUNTOF(s_blend)-1);
return s_blend[idx];
}

Expand Down
Loading