Skip to content

Add clean pure-C11 port of the lightrt_c BVH binding#3

Merged
syoyo merged 3 commits into
mainfrom
feat/c11-port
May 26, 2026
Merged

Add clean pure-C11 port of the lightrt_c BVH binding#3
syoyo merged 3 commits into
mainfrom
feat/c11-port

Conversation

@syoyo

@syoyo syoyo commented May 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds lightrt_c.c — a standalone pure-C11 reimplementation of the lightrt_c.h API with no dependency on the C++ library (lightrt.hh). The existing lightrt_c.cc is a thin C++ wrapper over lightrt::MMapGenericBVH; this new file builds and traverses its own BVH entirely in C.

Both expose the same lightrt_c.h API, so exactly one is linked into the library (selectable via a build option).

What the port does

  • Build: binned SAH (16 bins/axis), faithfully ported from MMapGenericBVH::buildRecursive — same cost function, axis selection, and median-split fallback. Nodes are preallocated to 2·N, so the array never reallocates mid-build.
  • Traverse: stack-based with front-to-back child ordering by split axis; slab test ported from AABB::intersect.
  • Precision: preserves the design intent — fp32 BVH for broad phase, fp64 ray + best-hit kept in double so the user's intersection callback can solve analytic surfaces at full precision.

Build integration

  • CMake: new LIGHTRT_PURE_C_BINDING option (default OFF) selects lightrt_c.c over lightrt_c.cc. Project now enables the C language at C11. New lightrt_c_test CTest target compiles lightrt_c.c directly (no C++).
  • meson: new pure_c_binding option (default false) mirroring CMake, plus lightrt_c_test wired into meson test. Also fixes several pre-existing bugs that prevented meson.build from configuring under modern meson ('c++''cpp', flags-as-list, link_with:, invalid bare install(), C language/standard).

Also included

  • A small preceding commit adds Emscripten/WASM build support for TaskSystem (guards <thread>/<mutex>/etc. behind #ifndef __EMSCRIPTEN__ with an inline single-threaded stub). No behavior change for native builds.

Testing

tests/test_lightrt_c.c builds a scene of analytic spheres and cross-checks closest-hit results against an O(N) brute-force reference.

  • 2000 spheres / 20000 rays → 0 mismatches, exact t match
  • Clean under -Wall -Wextra -Wpedantic and ASan + UBSan
  • Edge cases pass: single-prim leaf-only tree, axis-aligned/zero-direction rays, misses, rebuild (no leak), empty scene, NULL callbacks
  • Both CMake and meson option states verified; static library archives correctly with the pure-C binding; ctest/meson test pass

🤖 Generated with Claude Code

syoyo and others added 3 commits May 21, 2026 15:19
Guard <thread>/<mutex>/<condition_variable>/<queue> includes and the
thread-pool TaskSystem behind #ifndef __EMSCRIPTEN__. Under WASM, provide
an inline single-threaded TaskSystem stub (submit/parallelFor run the body
directly) and route traverseBatch* / buildRecursive work-stealing through a
single-threaded path. No behavior change for native builds.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
lightrt_c.c is a standalone C11 reimplementation of the lightrt_c.h API with
no dependency on the C++ library (lightrt.hh). It builds its own generic BVH
(binned SAH, 16 bins, ported from MMapGenericBVH::buildRecursive) and traverses
it with a stack-based front-to-back walk, calling the user's fp64 intersection
callback per candidate — preserving the fp32-broadphase / fp64-callback design
of the existing C++ wrapper (lightrt_c.cc).

Build integration:
- CMake gains LIGHTRT_PURE_C_BINDING (default OFF) to select lightrt_c.c over
  lightrt_c.cc in the library; both expose the same API, so exactly one links.
- Project now enables the C language (C11) for the pure-C sources.
- New lightrt_c_test target (CTest) compiles lightrt_c.c directly and validates
  closest-hit results against an O(N) brute-force reference over random spheres.

Verified: test passes (0 mismatches, exact t-match), clean under
-Wall -Wextra -Wpedantic and ASan+UBSan; edge cases (N=1 leaf-only tree,
axis-aligned/zero-dir rays, misses, rebuild, empty scene, NULL callbacks) pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add a `pure_c_binding` boolean option (default false) that swaps lightrt_c.c
for lightrt_c.cc in the library, matching CMake's LIGHTRT_PURE_C_BINDING, plus
a lightrt_c_test target wired into `meson test`.

This also fixes several pre-existing bugs that prevented meson.build from
configuring under modern meson (1.10):
- enable the C language and set c_std=c11
- 'c++' -> 'cpp' (meson's C++ language id) in add_project_arguments, and pass
  the flags as a list rather than one combined string
- link executables with `link_with:` instead of a positional arg after the
  `sources:` keyword (positional-after-keyword is a syntax error)
- replace the invalid bare `install()` call with install_headers()

Verified: both option states configure; the static library archives in the
pure-C config (lightrt.cc.o + lightrt_c.c.o); `meson test` passes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 21, 2026 22:19

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a standalone pure-C11 implementation of the lightrt_c.h API (lightrt_c.c) and wires build-system switches (CMake + Meson) to choose between the existing C++ wrapper (lightrt_c.cc) and the new pure-C port. It also adds a C-based correctness test for the binding and includes Emscripten/WASM guards/stubs around the thread-pool TaskSystem.

Changes:

  • Add lightrt_c.c: a self-contained C11 BVH build/traversal + callback intersection backend implementing lightrt_c.h.
  • Add tests/test_lightrt_c.c and register it in both CMake and Meson.
  • Add build options to select the pure-C binding (LIGHTRT_PURE_C_BINDING / pure_c_binding) and add Emscripten stubs/guards for threading.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
lightrt_c.c New pure-C11 implementation of the lightrt_c.h BVH binding.
tests/test_lightrt_c.c New correctness test comparing BVH results vs brute-force analytic sphere hits.
CMakeLists.txt Enables C11, adds option to select binding implementation, adds CTest target for the C binding.
meson.build Adds C language, adds option-controlled binding source selection, fixes Meson configuration issues, adds test executable.
meson_options.txt Adds pure_c_binding Meson option.
lightrt.hh Adds Emscripten guards around thread/queue includes and introduces a single-threaded TaskSystem stub for WASM.
lightrt.cc Adds Emscripten guards around TaskSystem globals and adjusts batch traversal threading logic.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lightrt.cc
if (num_rays == 0) return;

#ifdef __EMSCRIPTEN__
if (num_threads == 0) num_threads = 1;
Comment thread lightrt.cc
if (num_rays == 0) return;

#ifdef __EMSCRIPTEN__
if (num_threads == 0) num_threads = 1;
Comment thread lightrt.cc
if (num_rays == 0) return;

#ifdef __EMSCRIPTEN__
if (num_threads == 0) num_threads = 1;
Comment thread lightrt.cc
Comment on lines +9808 to 9812
#ifdef __EMSCRIPTEN__
if (num_threads == 0) num_threads = 1;
#else
if (num_threads == 0) num_threads = std::thread::hardware_concurrency();
if (num_threads < 1) num_threads = 1;
Comment thread lightrt_c.c
uint32_t left = n->u.inner.left;
uint32_t right = n->u.inner.right;
uint32_t axis = (n->flags >> 1) & 0x3u;
if (sp + 2 > LRT_STACK_SIZE) continue; /* guard (very deep tree) */
Comment thread tests/test_lightrt_c.c
Comment on lines +79 to +83
static unsigned long g_seed = 0x12345678ul;
static double frand(void) {
g_seed = g_seed * 6364136223846793005ul + 1442695040888963407ul;
return (double)((g_seed >> 33) & 0x7fffffff) / (double)0x7fffffff;
}
Comment thread tests/test_lightrt_c.c
Comment on lines +91 to +94
sphere *spheres = (sphere *)malloc(N * sizeof(sphere));
for (unsigned i = 0; i < N; i++) {
spheres[i].c[0] = frange(-WORLD, WORLD);
spheres[i].c[1] = frange(-WORLD, WORLD);
@syoyo syoyo merged commit dcb71eb into main May 26, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants