Skip to content

Commit

Permalink
Merge pull request #18 from maniatic0/main
Browse files Browse the repository at this point in the history
Support for WASM with EMSCRIPTEN
  • Loading branch information
jbikker authored Nov 13, 2024
2 parents 7e6feb4 + b9393cb commit e4546b7
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 19 deletions.
122 changes: 105 additions & 17 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,38 +8,126 @@ project(tiny_bvh LANGUAGES CXX)

if (APPLE)
find_library(COCOA_LIBRARY Cocoa)
elseif (UNIX)
elseif (UNIX AND NOT EMSCRIPTEN)
find_package(X11)
elseif (EMSCRIPTEN)
# To auto generate test pages
set(CMAKE_EXECUTABLE_SUFFIX ".html")

# Enable clang's Address Sanitizer for WASM
option(enable_asan "Enable ASAN on the build (it'll slow down the app)" OFF)

# Enable wasm SIMD instructions
set(emcc_simd "fixed" CACHE STRING "SIMD type to use in the build (none, fixed, relaxed)")
set_property(CACHE emcc_simd PROPERTY STRINGS none fixed relaxed)

# Enable thread support for WASM (note that you need special http headers now https://emscripten.org/docs/porting/pthreads.html )
option(emcc_threads "Enable Threads in WASM (note that means you need to change your http headers)" OFF)

# Enable C/C++ main thread as a proxy thread to avoid blocking DOM/UI thread https://emscripten.org/docs/porting/pthreads.html
option(emcc_proxy_main "Enable C/C++ main thread as a proxy thread to avoid blocking DOM/UI thread" ${emcc_threads})
endif()

enable_testing()

add_executable(tiny_bvh_test tiny_bvh_test.cpp)
add_executable(tiny_bvh_renderer tiny_bvh_renderer.cpp)
add_executable(tiny_bvh_speedtest tiny_bvh_speedtest.cpp)
add_executable(tiny_bvh_fenster tiny_bvh_fenster.cpp)
if (NOT EMSCRIPTEN)
# EMSCRIPTEN doesn't render anything by default (you would need WebGL/WebGPU)
add_executable(tiny_bvh_fenster tiny_bvh_fenster.cpp)
endif()

if (NOT MSVC)
# Produce debug symbols and set optimization level
set(common_cxx_flags
-g -march=native
$<$<OR:$<CONFIG:RelWithDebInfo>,$<CONFIG:Debug>>:-g> $<$<OR:$<CONFIG:RelWithDebInfo>,$<CONFIG:Release>>:-O3>
)

set(common_link_flags
$<$<OR:$<CONFIG:RelWithDebInfo>,$<CONFIG:Debug>>:-g>
)

if (NOT EMSCRIPTEN)
# EMSCRIPTEN doesn't know about archs
set(common_cxx_flags
${common_cxx_flags} -march=native
)
else()
# EMSCRIPTEN flags:
# -sALLOW_MEMORY_GROWTH (allow runtime allocations) (for some reason cmake passes this as a linker flag when in theory it's a compiler one)
set(common_link_flags ${common_link_flags} -sALLOW_MEMORY_GROWTH=1)

# -fsanitize=address (enable ASAN)
# More at https://emscripten.org/docs/debugging/Sanitizers.html#address-sanitizer
if (enable_asan)
set(common_cxx_flags ${common_cxx_flags} -fsanitize=address)
set(common_link_flags ${common_link_flags} -fsanitize=address)
endif()

# Enable SIMD instructions and AVX intrinsics
string(TOLOWER "${emcc_simd}" emcc_simd_lower)
if (emcc_simd_lower STREQUAL fixed)
# Supported by all browsers https://webassembly.org/features/
set(common_cxx_flags ${common_cxx_flags} -msimd128 -mavx)
elseif (emcc_simd_lower STREQUAL relaxed)
# Supported some browsers https://webassembly.org/features/
set(common_cxx_flags ${common_cxx_flags} -mrelaxed-simd -mavx)
endif()

endif()

target_compile_options(tiny_bvh_test PRIVATE ${common_cxx_flags})
target_link_options(tiny_bvh_test PRIVATE ${common_link_flags})

target_compile_options(tiny_bvh_renderer PRIVATE ${common_cxx_flags})
if (NOT APPLE)
target_compile_options(tiny_bvh_speedtest PRIVATE ${common_cxx_flags} -fopenmp)
target_link_options(tiny_bvh_speedtest PRIVATE -fopenmp)
else()
# No openmp support in default compiler
target_compile_options(tiny_bvh_speedtest PRIVATE ${common_cxx_flags})
target_link_options(tiny_bvh_speedtest PRIVATE)
target_link_options(tiny_bvh_renderer PRIVATE ${common_link_flags})

# No openmp support in default compiler
set(tiny_bvh_speedtest_cxx_flags ${common_cxx_flags})
set(tiny_bvh_speedtest_link_flags ${common_link_flags})

if (NOT APPLE AND NOT EMSCRIPTEN)
set(tiny_bvh_speedtest_cxx_flags ${tiny_bvh_speedtest_cxx_flags} -fopenmp)
set(tiny_bvh_speedtest_link_flags ${tiny_bvh_speedtest_link_flags} -fopenmp)
elseif (EMSCRIPTEN)
if (emcc_threads)
set(tiny_bvh_speedtest_cxx_flags ${tiny_bvh_speedtest_cxx_flags} -pthread)
set(tiny_bvh_speedtest_link_flags ${tiny_bvh_speedtest_link_flags} -pthread -sUSE_PTHREADS=1 -sPTHREAD_POOL_SIZE=navigator.hardwareConcurrency)

if (FALSE)
# From:
# - https://github.com/llvm/llvm-project/pull/95169
# - https://github.com/llvm/llvm-project/pull/71297
# - https://reviews.llvm.org/D142593?id=492403
# llvm's and clang's OpenMP has been ported to WASM around August 2024
# Sadly emcc's clang doesn't include a copy of the lib
# So you would need to do compile it yourself https://github.com/abrown/wasm-openmp-examples/blob/main/emscripten.sh
# Then link it here
set(tiny_bvh_speedtest_cxx_flags ${tiny_bvh_speedtest_cxx_flags} -fopenmp)
set(tiny_bvh_speedtest_link_flags ${tiny_bvh_speedtest_link_flags} -fopenmp)
endif()

if (emcc_proxy_main)
set(tiny_bvh_speedtest_link_flags ${tiny_bvh_speedtest_link_flags} -sPROXY_TO_PTHREAD=1)
endif()
endif()
endif()
target_compile_options(tiny_bvh_fenster PRIVATE ${common_cxx_flags})
if (WIN32)
target_link_libraries(tiny_bvh_fenster -mwindows)
elseif (APPLE)
target_link_libraries(tiny_bvh_fenster ${COCOA_LIBRARY})
elseif (X11_FOUND)
target_link_libraries(tiny_bvh_fenster ${X11_LIBRARIES})
target_compile_options(tiny_bvh_speedtest PRIVATE ${tiny_bvh_speedtest_cxx_flags})
target_link_options(tiny_bvh_speedtest PRIVATE ${tiny_bvh_speedtest_link_flags})

if (TARGET tiny_bvh_fenster)
# Not all platforms support all targets. E. g.:
# - EMSCRIPTEN doesn't render anything by default (you would need WebGL/WebGPU)
target_compile_options(tiny_bvh_fenster PRIVATE ${common_cxx_flags})
target_link_options(tiny_bvh_fenster PRIVATE ${common_link_flags})
if (WIN32)
target_link_libraries(tiny_bvh_fenster -mwindows)
elseif (APPLE)
target_link_libraries(tiny_bvh_fenster ${COCOA_LIBRARY})
elseif (X11_FOUND)
target_link_libraries(tiny_bvh_fenster ${X11_LIBRARIES})
endif()
endif()
else()
if (WIN32)
Expand Down
22 changes: 21 additions & 1 deletion tiny_bvh.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ THE SOFTWARE.
#define BVHBINS 8

// include fast AVX BVH builder
#if defined(__x86_64__) || defined(_M_X64)
#if defined(__x86_64__) || defined(_M_X64) || defined(__wasm_simd128__) || defined(__wasm_relaxed_simd__)
#define BVH_USEAVX
#endif

Expand All @@ -95,6 +95,24 @@ THE SOFTWARE.
#define ALIGNED( x ) __declspec( align( x ) )
#define ALIGNED_MALLOC( x ) ( ( x ) == 0 ? 0 : _aligned_malloc( ( x ), 64 ) )
#define ALIGNED_FREE( x ) _aligned_free( x )
#elif defined(__EMSCRIPTEN__)
// EMSCRIPTEN (needs to be before gcc and clang to avoid misdetection)
#include <cstdlib>
#include <cmath>
#include <cstring>
#define ALIGNED( x ) __attribute__( ( aligned( x ) ) )
#if defined(__wasm_simd128__) || defined(__wasm_relaxed_simd__)
// https://emscripten.org/docs/porting/simd.html
#include <xmmintrin.h>
#define ALIGNED_MALLOC( x ) ( ( x ) == 0 ? 0 : _mm_malloc( ( x ), 64 ) )
#define ALIGNED_FREE( x ) _mm_free( x )
#else
// Size needs to be a multiple of Alignment in the standard (in EMSCRIPTEN this is enforced)
// See https://en.cppreference.com/w/c/memory/aligned_alloc
#define MAKE_MULIPLE_64( x ) ( ( ( x ) + 63 ) & ( ~0x3f ) )
#define ALIGNED_MALLOC( x ) ( ( x ) == 0 ? 0 : aligned_alloc( 64, MAKE_MULIPLE_64( x ) ) )
#define ALIGNED_FREE( x ) free( x )
#endif
#else
// gcc / clang
#include <cstdlib>
Expand Down Expand Up @@ -2578,6 +2596,8 @@ static unsigned __bfind( unsigned x ) // https://github.com/mackron/refcode/blob
{
#if defined(_MSC_VER) && !defined(__clang__)
return 31 - __lzcnt( x );
#elif defined(__EMSCRIPTEN__)
return 31 - __builtin_clz(x);
#elif defined(__GNUC__) || defined(__clang__)
unsigned r;
__asm__ __volatile__( "lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" );
Expand Down
8 changes: 7 additions & 1 deletion tiny_bvh_speedtest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
#ifdef _WIN32
#include <intrin.h> // for __cpuidex
#endif
#ifdef __EMSCRIPTEN__
#include <emscripten/version.h> // for __EMSCRIPTEN_major__, __EMSCRIPTEN_minor__
#endif

// 'screen resolution': see tiny_bvh_fenster.cpp; this program traces the
// same rays, but without visualization - just performance statistics.
Expand Down Expand Up @@ -122,6 +125,9 @@ int main()
// determine compiler
#ifdef _MSC_VER
printf( "(MSVC %i build)\n", _MSC_VER );
#elif defined __EMSCRIPTEN__
// EMSCRIPTEN needs to be before clang or gcc
printf("(emcc %i.%i build)\n", __EMSCRIPTEN_major__, __EMSCRIPTEN_minor__);
#elif defined __clang__
printf( "(clang %i.%i build)\n", __clang_major__, __clang_minor__ );
#elif defined __GNUC__
Expand Down Expand Up @@ -405,6 +411,6 @@ int main()

#endif

// all done.
printf( "all done." );
return 0;
}

0 comments on commit e4546b7

Please sign in to comment.