Skip to content

Conversation

@jserv
Copy link
Contributor

@jserv jserv commented Oct 19, 2025

This pull request adds comprehensive cross-compilation and WebAssembly support. The core achievement is a unified build infrastructure that automatically detects compiler type (GCC/Clang/Emscripten) via mk/toolchain.mk and
configures appropriate flags, backend restrictions, and feature availability through Kconfig. For WebAssembly specifically, the branch introduces complete browser-based testing workflow with assets/web/index.html as the runtime interface, scripts/serve-wasm.py as development server with correct MIME types, and automatic post-build artifact deployment via the wasm-install Makefile target.

The backend architecture underwent major refactoring to establish a consistent interface where all four backends (SDL, VNC, fbdev, headless) now implement a standardized .start method and use twin_dispatch_once() for their main loops, ensuring work queue and timeout mechanisms function correctly across platforms. This architectural unification enabled significant API simplification where the new twin_run() function completely abstracts platform differences.


Summary by cubic

Adds cross-compilation and WebAssembly support with unified toolchain detection and a new twin_run API for consistent backend loops. Enables browser testing and improves SDL performance.

  • New Features

    • Automatic compiler detection (GCC/Clang/Emscripten) and cross-compile prefix via toolchain.mk, Kconfig, and scripts/detect-compiler.py.
    • WebAssembly build path: SDL-only backend, Emscripten flags, wasm-install target, assets/web/index.html, and scripts/serve-wasm.py for local testing.
    • Unified app startup: twin_run(init) and twin_dispatch_once(), with .start implemented in SDL, VNC, fbdev, and headless.
    • apps/main.c now uses twin_run(init_demo_apps) to support both native and WASM.
    • Cross-platform guards for endianness and 64-bit fixed-point math fallback on 32-bit targets.
  • Refactors

    • Standardized backend main loops with twin_dispatch_once() to ensure work queue and timeouts behave uniformly.
    • SDL: robust init/cleanup, reduced idle CPU with smart delay, and proper Emscripten main loop integration.
    • fbdev/headless/vnc: added start functions and fixed error paths and resource leaks; POSIX guards and stubs for headless on non-POSIX.
    • Build updates: use Emscripten ports for SDL/libpng/libjpeg, avoid building tools for WASM, and add macOS linker flag detection.

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

7 issues found across 18 files

Prompt for AI agents (all 7 issues)

Understand the root cause of the following 7 issues and fix them.


<file name="scripts/detect-compiler.py">

<violation number="1" location="scripts/detect-compiler.py:49">
CROSS_COMPILE should not override an explicit CC value; otherwise clang-based cross builds try to run a non-existent &lt;prefix&gt;gcc and detection fails.</violation>

<violation number="2" location="scripts/detect-compiler.py:69">
Split the compiler command into argv tokens before invoking subprocess so wrapped compilers (e.g., CC=&quot;ccache clang&quot;) are detected correctly.</violation>
</file>

<file name="src/twin_private.h">

<violation number="1" location="src/twin_private.h:190">
`_twin_xfixed_mul_32bit` loses carry bits when accumulating the 64-bit partial products, so the software fallback produces incorrect results for valid operands.</violation>

<violation number="2" location="src/twin_private.h:196">
The 64-bit fixed-point division fallback `_twin_xfixed_div_32bit` for 32-bit platforms is mathematically incorrect, implementing standard integer division instead of fixed-point division. This breaks path rendering in `src/path.c` on 32-bit cross-compilation targets, defeating a key objective of the PR.</violation>
</file>

<file name="backend/sdl.c">

<violation number="1" location="backend/sdl.c:263">
twin_sdl_exit now destroys SDL objects that were already freed in _twin_sdl_destroy (invoked on SDL_QUIT), leading to double-destroy crashes during shutdown.</violation>
</file>

<file name="assets/web/index.html">

<violation number="1" location="assets/web/index.html:98">
Only add the error styling when the message type is actually an error so informational logs aren&#39;t misrepresented.</violation>
</file>

<file name="mk/toolchain.mk">

<violation number="1" location="mk/toolchain.mk:162">
This detection invokes $(CC) with -Wl,-no_warn_duplicate_libraries but no input file, so clang/gcc exit with “no input files” before ld ever runs. As a result LD_SUPPORTS_NO_WARN is set to 1 even when ld rejects the flag, and the final link then fails on toolchains that lack -no_warn_duplicate_libraries.</violation>
</file>

React with 👍 or 👎 to teach cubic. Mention @cubic-dev-ai to give feedback, ask questions, or re-run the review.

return neg ? -(int64_t) result : (int64_t) result;
}

static inline twin_xfixed_t _twin_xfixed_div_32bit(twin_xfixed_t a,
Copy link

@cubic-dev-ai cubic-dev-ai bot Oct 19, 2025

Choose a reason for hiding this comment

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

The 64-bit fixed-point division fallback _twin_xfixed_div_32bit for 32-bit platforms is mathematically incorrect, implementing standard integer division instead of fixed-point division. This breaks path rendering in src/path.c on 32-bit cross-compilation targets, defeating a key objective of the PR.

Prompt for AI agents
Address the following comment on src/twin_private.h at line 196:

<comment>The 64-bit fixed-point division fallback `_twin_xfixed_div_32bit` for 32-bit platforms is mathematically incorrect, implementing standard integer division instead of fixed-point division. This breaks path rendering in `src/path.c` on 32-bit cross-compilation targets, defeating a key objective of the PR.</comment>

<file context>
@@ -140,10 +140,96 @@ typedef int64_t twin_xfixed_t;
+    return neg ? -(int64_t) result : (int64_t) result;
+}
+
+static inline twin_xfixed_t _twin_xfixed_div_32bit(twin_xfixed_t a,
+                                                   twin_xfixed_t b)
+{
</file context>
Fix with Cubic

twin_sdl_t *tx = PRIV(ctx);

/* Clean up SDL resources */
if (tx->texture)
Copy link

@cubic-dev-ai cubic-dev-ai bot Oct 19, 2025

Choose a reason for hiding this comment

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

twin_sdl_exit now destroys SDL objects that were already freed in _twin_sdl_destroy (invoked on SDL_QUIT), leading to double-destroy crashes during shutdown.

Prompt for AI agents
Address the following comment on backend/sdl.c at line 263:

<comment>twin_sdl_exit now destroys SDL objects that were already freed in _twin_sdl_destroy (invoked on SDL_QUIT), leading to double-destroy crashes during shutdown.</comment>

<file context>
@@ -188,23 +241,88 @@ static bool twin_sdl_poll(twin_context_t *ctx)
+    twin_sdl_t *tx = PRIV(ctx);
+
+    /* Clean up SDL resources */
+    if (tx-&gt;texture)
+        SDL_DestroyTexture(tx-&gt;texture);
+    if (tx-&gt;render)
</file context>

✅ Addressed in a330284

jserv added 5 commits October 19, 2025 13:21
This supports bare-metal toolchains (e.g., riscv-none-elf-) by adding
conditional compilation for POSIX-specific APIs and fixing toolchain
selection in the build system.
This commit enhances cross-compilation support with key improvements:
1. Kconfig Toolchain Configuration Menu (configs/Kconfig):
   - Automatic compiler detection using Python scripts
   - Cross-compile prefix detection and validation
   - Supports GCC, Clang, Emscripten, and bare-metal toolchains
2. Cross-platform Endianness Detection
   - Priority-based header inclusion:
     * Bare-metal: Compiler macros (__BYTE_ORDER__)
     * Fallback: Assume little-endian
   - Ensures correct byte order handling across diverse target platforms
3. 128-bit Arithmetic Fallback:
   - Software implementation for 64-bit fixed-point math on 32-bit
   - Targets: RISC-V 32, Arm32, i386 (without __int128_t support)
This commit
1. fixes macOS linker flag detection
2. adds Emscripten memory configuration:
   - Configured -sINITIAL_MEMORY=33554432 (32MB)
   - Enabled -sALLOW_MEMORY_GROWTH=1
   - Set -sSTACK_SIZE=1048576 (1MB)

Known Issue - Emscripten 4.0.17 Compatibility:
The "wasm-ld: error: animation.c.o: section too large" error persists
even with memory configuration and -O2 optimization. This is a known
limitation of wasm-ld when linking multiple large object files.
Backend improvements:
- SDL: Add comprehensive error handling with proper resource cleanup
- SDL: Implement smart conditional delay to prevent CPU busy-wait
- SDL: Fix resource leaks in initialization and exit paths
- fbdev: Fix critical memory leaks and invalid return statements
- fbdev: Add NULL checks for screen creation
- All backends: Standardize main loop using twin_dispatch_once()

API refactoring:
- Add twin_run() as unified application entry point
- Hide platform-specific details from public API

Performance:
- Reduce idle CPU usage from 100% to <1% in SDL backend
- Maintain zero-latency event handling and screen updates
This enables Emscripten/WebAssembly compilation with complete toolchain
integration and browser-based testing workflow.

The build system now automatically detects Emscripten and configures
appropriate compiler flags, backend selection, and post-build artifact
management. Run './scripts/serve-wasm.py --open' to test in web browser.
@jserv jserv merged commit 3ec1f5e into main Oct 19, 2025
8 checks passed
@jserv jserv deleted the cross-compile branch October 19, 2025 05:29
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