Skip to content

Commit

Permalink
Enable emscripten build with parallelization (#1045)
Browse files Browse the repository at this point in the history
* port tbb changes

* format
  • Loading branch information
pca006132 authored Nov 27, 2024
1 parent 3ea3e7e commit f8c9dc9
Show file tree
Hide file tree
Showing 11 changed files with 90 additions and 7 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/manifold.yml
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ jobs:
timeout-minutes: 30
strategy:
matrix:
variant: [manifold-none, manifold-tbb, manifold-js, manifold3d]
variant: [manifold-none, manifold-tbb, manifold-js, manifold-js-tbb, manifold3d]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand Down
4 changes: 0 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,6 @@ if(EMSCRIPTEN)
if(MANIFOLD_PAR)
set(CMAKE_THREAD_LIBS_INIT "-pthread")
add_compile_options(-pthread)
# PTHREAD_POOL_SIZE is set to 4 for OK-ish performance, in general we are
# not getting too much speedup for very large number of cores, and a lot of
# CPUs now have 4 cores...
add_link_options(-sPTHREAD_POOL_SIZE=4)
# mimalloc is needed for good performance
add_link_options(-sMALLOC=mimalloc)
# The default stack size apparently causes problem when parallelization is
Expand Down
4 changes: 4 additions & 0 deletions bindings/wasm/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ add_custom_target(
add_custom_command(
TARGET js_deps
POST_BUILD
# fix js file
COMMAND
${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/fixup.py
${CMAKE_CURRENT_BINARY_DIR}/manifold.js
# copy WASM build back here for publishing to npm
COMMAND
${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/manifold.*
Expand Down
26 changes: 26 additions & 0 deletions bindings/wasm/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,30 @@
#include "manifold/manifold.h"
#include "manifold/polygon.h"

#if (MANIFOLD_PAR == 1)
#include <tbb/parallel_for.h>

#include <atomic>
#endif

// https://github.com/oneapi-src/oneTBB/blob/master/WASM_Support.md#limitations
void initTBB() {
#if (MANIFOLD_PAR == 1)
int num_threads = tbb::this_task_arena::max_concurrency();
std::atomic<int> barrier{num_threads};
tbb::parallel_for(
0, num_threads,
[&barrier](int) {
barrier--;
while (barrier > 0) {
// Send browser thread to event loop
std::this_thread::yield();
}
},
tbb::static_partitioner{});
#endif
}

using namespace emscripten;
using namespace manifold;

Expand Down Expand Up @@ -195,4 +219,6 @@ EMSCRIPTEN_BINDINGS(whatever) {
function("setCircularSegments", &Quality::SetCircularSegments);
function("getCircularSegments", &Quality::GetCircularSegments);
function("resetToCircularDefaults", &Quality::ResetToDefaults);

function("initTBB", &initTBB);
}
3 changes: 3 additions & 0 deletions bindings/wasm/bindings.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ Module.setup = function() {
if (_ManifoldInitialized) return;
_ManifoldInitialized = true;

// warmup tbb for emscripten, according to
// https://github.com/oneapi-src/oneTBB/blob/master/WASM_Support.md#limitations
Module.initTBB();
// conversion utilities

function toVec(vec, list, f = x => x) {
Expand Down
6 changes: 6 additions & 0 deletions bindings/wasm/examples/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ export default defineConfig({
worker: {
format: 'es',
},
server: {
headers: {
'Cross-Origin-Embedder-Policy': 'require-corp',
'Cross-Origin-Opener-Policy': 'same-origin',
},
},
build: {
target: 'esnext',
sourcemap: true,
Expand Down
19 changes: 19 additions & 0 deletions bindings/wasm/fixup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env python3
import sys

if len(sys.argv) != 2:
print("Usage: python fixup.py <SOURCE_DIR>")

filename = sys.argv[1]
with open(filename, "r") as file:
data = file.read()

data = data.replace(
'var workerOptions={type:"module",workerData:"em-pthread",name:"em-pthread"};', ""
)
data = data.replace(
"workerOptions", '{type:"module",workerData:"em-pthread",name:"em-pthread"}'
)

with open(filename, "w") as file:
file.write(data)
2 changes: 1 addition & 1 deletion bindings/wasm/helpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ Manifold SetProperties(Manifold& manifold, int numProp, uintptr_t funcPtr) {
Manifold LevelSet(uintptr_t funcPtr, Box bounds, double edgeLength,
double level, double tolerance) {
double (*f)(const vec3&) = reinterpret_cast<double (*)(const vec3&)>(funcPtr);
return Manifold::LevelSet(f, bounds, edgeLength, level, tolerance);
return Manifold::LevelSet(f, bounds, edgeLength, level, tolerance, false);
}

std::vector<Manifold> Split(Manifold& a, Manifold& b) {
Expand Down
4 changes: 4 additions & 0 deletions cmake/manifoldDeps.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,10 @@ if(MANIFOLD_TEST)
endif()
endif()

if(EMSCRIPTEN)
find_package(Python REQUIRED)
endif()

if(MANIFOLD_FUZZ)
logmissingdep("fuzztest" , "MANIFOLD_FUZZ")
FetchContent_Declare(
Expand Down
6 changes: 5 additions & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@
cd ../
'';
};
manifold-emscripten = { doCheck ? true }: pkgs.buildEmscriptenPackage {
manifold-emscripten = { doCheck ? true, parallel ? false }: pkgs.buildEmscriptenPackage {
name = "manifold-js";
version = manifold-version;
src = self;
Expand All @@ -126,7 +126,9 @@
mkdir build
cd build
emcmake cmake -DCMAKE_BUILD_TYPE=Release \
-DMANIFOLD_PAR=${if parallel then "ON" else "OFF"} \
-DFETCHCONTENT_SOURCE_DIR_GOOGLETEST=${gtest-src} \
-DFETCHCONTENT_SOURCE_DIR_TBB=${onetbb-src} \
-DFETCHCONTENT_SOURCE_DIR_CLIPPER2=../clipper2 ..
'';
buildPhase = ''
Expand All @@ -148,6 +150,7 @@
manifold-tbb = manifold { };
manifold-none = manifold { parallel = false; };
manifold-js = manifold-emscripten { };
manifold-js-tbb = manifold-emscripten { parallel = true; };
# but how should we make it work with other python versions?
manifold3d = with pkgs.python3Packages; buildPythonPackage {
pname = "manifold3d";
Expand Down Expand Up @@ -197,6 +200,7 @@
# useful tools
clang-tools_18
clang_18
llvmPackages_18.bintools
tracy
];
};
Expand Down
21 changes: 21 additions & 0 deletions test/test_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
#include "manifold/polygon.h"
#include "test.h"

#if (MANIFOLD_PAR == 1)
#include <tbb/parallel_for.h>
#endif

// we need to call some tracy API to establish the connection
#if __has_include(<tracy/Tracy.hpp>)
#include <tracy/Tracy.hpp>
Expand Down Expand Up @@ -45,6 +49,23 @@ int main(int argc, char** argv) {
const char* name = "test setup";
FrameMarkStart(name);

// warmup tbb for emscripten, according to
// https://github.com/oneapi-src/oneTBB/blob/master/WASM_Support.md#limitations
#if defined(__EMSCRIPTEN__) && (MANIFOLD_PAR == 1)
int num_threads = tbb::this_task_arena::max_concurrency();
std::atomic<int> barrier{num_threads};
tbb::parallel_for(
0, num_threads,
[&barrier](int) {
barrier--;
while (barrier > 0) {
// Send browser thread to event loop
std::this_thread::yield();
}
},
tbb::static_partitioner{});
#endif

for (int i = 1; i < argc; i++) {
if (argv[i][0] != '-') {
fprintf(stderr, "Unknown option: %s\n", argv[i]);
Expand Down

0 comments on commit f8c9dc9

Please sign in to comment.