summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt176
-rw-r--r--doc/SIZE_MEASUREMENT.md383
-rwxr-xr-xscripts/measure_size.sh46
-rw-r--r--src/gpu/stub_gpu.cc83
-rw-r--r--src/platform/platform.h5
-rw-r--r--src/platform/stub_platform.cc53
-rw-r--r--src/platform/stub_types.h159
-rw-r--r--src/stub_main.cc13
8 files changed, 591 insertions, 327 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1892775..acae668 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -8,6 +8,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
option(DEMO_SIZE_OPT "Enable size optimization flags" OFF)
option(DEMO_STRIP_ALL "Strip all unnecessary code for final build" OFF)
option(DEMO_FINAL_STRIP "Strip ALL error checking for final-final build" OFF)
+option(DEMO_STRIP_EXTERNAL_LIBS "Stub external libs for size measurement (binary won't run)" OFF)
option(DEMO_BUILD_TESTS "Build tests" OFF)
option(DEMO_BUILD_TOOLS "Build tools" OFF)
option(DEMO_ENABLE_COVERAGE "Enable code coverage generation (macOS only)" OFF)
@@ -35,6 +36,13 @@ if (DEMO_STRIP_ALL)
set(DEMO_SIZE_OPT ON)
endif()
+if (DEMO_STRIP_EXTERNAL_LIBS)
+ add_definitions(-DSTRIP_EXTERNAL_LIBS)
+ # Audio: Use miniaudio null backend
+ add_definitions(-DMA_ENABLE_ONLY_SPECIFIC_BACKENDS -DMA_ENABLE_NULL)
+ message(STATUS "STRIP_EXTERNAL_LIBS enabled - binary will compile but NOT run (size measurement only)")
+endif()
+
if (DEMO_ENABLE_COVERAGE AND APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage")
@@ -95,44 +103,60 @@ endif()
#-- - Source Groups -- -
set(AUDIO_SOURCES src/audio/audio.cc src/audio/ring_buffer.cc src/audio/backend/miniaudio_backend.cc src/audio/backend/wav_dump_backend.cc src/audio/backend/silent_backend.cc src/audio/gen.cc src/audio/fdct.cc src/audio/idct.cc src/audio/fft.cc src/audio/window.cc src/audio/synth.cc src/audio/tracker.cc src/audio/spectrogram_resource_manager.cc src/audio/audio_engine.cc src/audio/spectral_brush.cc)
set(PROCEDURAL_SOURCES src/procedural/generator.cc)
-set(GPU_SOURCES
- src/gpu/gpu.cc
- src/gpu/effect.cc
- src/gpu/effects/heptagon_effect.cc
- src/gpu/effects/particles_effect.cc
- src/gpu/effects/passthrough_effect.cc
- src/gpu/effects/moving_ellipse_effect.cc
- src/gpu/effects/particle_spray_effect.cc
- src/gpu/effects/gaussian_blur_effect.cc
- src/gpu/effects/solarize_effect.cc
- src/gpu/effects/chroma_aberration_effect.cc
- src/gpu/effects/vignette_effect.cc
- src/gpu/effects/post_process_helper.cc
- src/gpu/effects/shaders.cc
- src/gpu/effects/hybrid_3d_effect.cc
- src/gpu/effects/flash_cube_effect.cc
- src/gpu/effects/theme_modulation_effect.cc
- src/gpu/effects/fade_effect.cc
- src/gpu/effects/flash_effect.cc
- src/gpu/effects/shader_composer.cc
- src/gpu/effects/circle_mask_effect.cc
- src/gpu/effects/rotating_cube_effect.cc
- src/gpu/texture_manager.cc
-)
-set(3D_SOURCES
- src/3d/renderer.cc
- src/3d/renderer_draw.cc
- src/3d/renderer_pipelines.cc
- src/3d/renderer_resources.cc
- src/3d/visual_debug.cc
- src/3d/bvh.cc
- src/3d/physics.cc
- src/3d/scene_loader.cc
-)
-set(PLATFORM_SOURCES src/platform/platform.cc third_party/glfw3webgpu/glfw3webgpu.c)
+if (DEMO_STRIP_EXTERNAL_LIBS)
+ # Size measurement mode: Minimal GPU stubs only
+ set(GPU_SOURCES src/gpu/stub_gpu.cc)
+else()
+ # Normal mode: Full GPU implementation
+ set(GPU_SOURCES
+ src/gpu/gpu.cc
+ src/gpu/effect.cc
+ src/gpu/effects/heptagon_effect.cc
+ src/gpu/effects/particles_effect.cc
+ src/gpu/effects/passthrough_effect.cc
+ src/gpu/effects/moving_ellipse_effect.cc
+ src/gpu/effects/particle_spray_effect.cc
+ src/gpu/effects/gaussian_blur_effect.cc
+ src/gpu/effects/solarize_effect.cc
+ src/gpu/effects/chroma_aberration_effect.cc
+ src/gpu/effects/vignette_effect.cc
+ src/gpu/effects/post_process_helper.cc
+ src/gpu/effects/shaders.cc
+ src/gpu/effects/hybrid_3d_effect.cc
+ src/gpu/effects/flash_cube_effect.cc
+ src/gpu/effects/theme_modulation_effect.cc
+ src/gpu/effects/fade_effect.cc
+ src/gpu/effects/flash_effect.cc
+ src/gpu/effects/shader_composer.cc
+ src/gpu/effects/circle_mask_effect.cc
+ src/gpu/effects/rotating_cube_effect.cc
+ src/gpu/texture_manager.cc
+ )
+endif()
+if (DEMO_STRIP_EXTERNAL_LIBS)
+ # Size measurement mode: Stub 3D (it depends on WebGPU)
+ set(3D_SOURCES src/3d/bvh.cc src/3d/physics.cc src/3d/scene_loader.cc)
+else()
+ # Normal mode: Full 3D implementation
+ set(3D_SOURCES
+ src/3d/renderer.cc
+ src/3d/renderer_draw.cc
+ src/3d/renderer_pipelines.cc
+ src/3d/renderer_resources.cc
+ src/3d/visual_debug.cc
+ src/3d/bvh.cc
+ src/3d/physics.cc
+ src/3d/scene_loader.cc
+ )
+endif()
+if (DEMO_STRIP_EXTERNAL_LIBS)
+ set(PLATFORM_SOURCES src/platform/stub_platform.cc)
+else()
+ set(PLATFORM_SOURCES src/platform/platform.cc third_party/glfw3webgpu/glfw3webgpu.c)
+endif()
set(UTIL_SOURCES src/util/asset_manager.cc src/util/file_watcher.cc)
-#-- - Subsystem Libraries -- -
+#-- - Subsystem Libraries -- -
add_library(util STATIC ${UTIL_SOURCES})
add_dependencies(util generate_demo_assets generate_test_assets)
add_library(procedural STATIC ${PROCEDURAL_SOURCES})
@@ -307,14 +331,30 @@ set_source_files_properties(${GENERATED_TIMELINE_CC} PROPERTIES GENERATED TRUE)
set_source_files_properties(${GENERATED_MUSIC_DATA_CC} PROPERTIES GENERATED TRUE)
#-- - Main Demo -- -
-add_demo_executable(demo64k src/main.cc ${PLATFORM_SOURCES} ${GEN_DEMO_CC} ${GENERATED_TIMELINE_CC} ${GENERATED_MUSIC_DATA_CC})
-
-add_dependencies(demo64k generate_demo_assets generate_timeline generate_tracker_music)# Link order: Internal libs first, then external libs (DEMO_LIBS).
+if (DEMO_STRIP_EXTERNAL_LIBS)
+ # Size measurement mode: Stub main, no timeline/music
+ add_demo_executable(demo64k src/stub_main.cc ${PLATFORM_SOURCES} ${GEN_DEMO_CC})
+ add_dependencies(demo64k generate_demo_assets)
+else()
+ # Normal mode: Full main with timeline and music
+ add_demo_executable(demo64k src/main.cc ${PLATFORM_SOURCES} ${GEN_DEMO_CC} ${GENERATED_TIMELINE_CC} ${GENERATED_MUSIC_DATA_CC})
+ add_dependencies(demo64k generate_demo_assets generate_timeline generate_tracker_music)
+endif()# Link order: Internal libs first, then external libs (DEMO_LIBS).
# gpu and 3d depend on WGPU (in DEMO_LIBS).
-if (APPLE)
- target_link_libraries(demo64k PRIVATE 3d gpu audio procedural util ${DEMO_LIBS})
+if (DEMO_STRIP_EXTERNAL_LIBS)
+ # Size measurement mode: No external libraries (only math/pthread)
+ if (APPLE)
+ target_link_libraries(demo64k PRIVATE 3d gpu audio procedural util)
+ else()
+ target_link_libraries(demo64k PRIVATE -Wl,--start-group 3d gpu audio procedural util -Wl,--end-group pthread m)
+ endif()
else()
- target_link_libraries(demo64k PRIVATE -Wl,--start-group 3d gpu audio procedural util -Wl,--end-group ${DEMO_LIBS})
+ # Normal mode: Link external libraries
+ if (APPLE)
+ target_link_libraries(demo64k PRIVATE 3d gpu audio procedural util ${DEMO_LIBS})
+ else()
+ target_link_libraries(demo64k PRIVATE -Wl,--start-group 3d gpu audio procedural util -Wl,--end-group ${DEMO_LIBS})
+ endif()
endif()
#Size optimizations
@@ -386,34 +426,36 @@ set_source_files_properties(${GENERATED_TEST_DEMO_TIMELINE_CC} PROPERTIES GENERA
set_source_files_properties(${GENERATED_TEST_DEMO_MUSIC_CC} PROPERTIES GENERATED TRUE)
# Build executable (uses main demo assets)
-add_demo_executable(
- test_demo
- src/test_demo.cc
- ${PLATFORM_SOURCES}
- ${GEN_DEMO_CC}
- ${GENERATED_TEST_DEMO_TIMELINE_CC}
- ${GENERATED_TEST_DEMO_MUSIC_CC}
-)
+if (NOT DEMO_STRIP_EXTERNAL_LIBS)
+ add_demo_executable(
+ test_demo
+ src/test_demo.cc
+ ${PLATFORM_SOURCES}
+ ${GEN_DEMO_CC}
+ ${GENERATED_TEST_DEMO_TIMELINE_CC}
+ ${GENERATED_TEST_DEMO_MUSIC_CC}
+ )
-add_dependencies(test_demo generate_demo_assets generate_test_demo_timeline generate_test_demo_music)
+ add_dependencies(test_demo generate_demo_assets generate_test_demo_timeline generate_test_demo_music)
-if (APPLE)
- target_link_libraries(test_demo PRIVATE 3d gpu audio procedural util ${DEMO_LIBS})
-else()
- target_link_libraries(test_demo PRIVATE -Wl,--start-group 3d gpu audio procedural util -Wl,--end-group ${DEMO_LIBS})
-endif()
+ if (APPLE)
+ target_link_libraries(test_demo PRIVATE 3d gpu audio procedural util ${DEMO_LIBS})
+ else()
+ target_link_libraries(test_demo PRIVATE -Wl,--start-group 3d gpu audio procedural util -Wl,--end-group ${DEMO_LIBS})
+ endif()
-# Size optimizations
-if (DEMO_SIZE_OPT)
- if (MSVC)
- target_compile_options(test_demo PRIVATE /Os /GS-)
- target_link_options(test_demo PRIVATE /OPT:REF /OPT:ICF /INCREMENTAL:NO)
- elseif (APPLE)
- target_compile_options(test_demo PRIVATE -Os)
- target_link_options(test_demo PRIVATE -Wl,-dead_strip)
- else ()
- target_compile_options(test_demo PRIVATE -Os -ffunction-sections -fdata-sections)
- target_link_options(test_demo PRIVATE -Wl,--gc-sections -s)
+ # Size optimizations
+ if (DEMO_SIZE_OPT)
+ if (MSVC)
+ target_compile_options(test_demo PRIVATE /Os /GS-)
+ target_link_options(test_demo PRIVATE /OPT:REF /OPT:ICF /INCREMENTAL:NO)
+ elseif (APPLE)
+ target_compile_options(test_demo PRIVATE -Os)
+ target_link_options(test_demo PRIVATE -Wl,-dead_strip)
+ else ()
+ target_compile_options(test_demo PRIVATE -Os -ffunction-sections -fdata-sections)
+ target_link_options(test_demo PRIVATE -Wl,--gc-sections -s)
+ endif()
endif()
endif()
diff --git a/doc/SIZE_MEASUREMENT.md b/doc/SIZE_MEASUREMENT.md
index b997ea5..96c8e6c 100644
--- a/doc/SIZE_MEASUREMENT.md
+++ b/doc/SIZE_MEASUREMENT.md
@@ -2,340 +2,205 @@
## Goal
-Measure the true binary size of demo code vs external library overhead by stubbing out all system dependencies with minimal implementations.
+Measure true demo code size by stubbing external library dependencies.
-**Motivation:** Current STRIP_ALL builds include wgpu_native (~3-4MB), GLFW, and system libraries. We need to isolate core demo size to:
-- Track size budget accurately
-- Identify optimization targets
-- Measure progress toward 64KB goal
+**Motivation:** STRIP_ALL builds (~5MB) include wgpu_native (~3.5MB), GLFW (~500KB), miniaudio. Need to isolate demo code size for 64KB target tracking.
-## Problem Statement
+## Strategy
-Current size breakdown is opaque:
-```
-STRIP_ALL build: ~5.1MB total
-├── wgpu_native: ~3-4MB (estimate)
-├── GLFW: ~500KB (estimate)
-├── System libs: ~500KB (estimate)
-└── Demo code: ??? KB (unknown)
-```
-
-**Goal:** Measure demo code size precisely by replacing external dependencies with stubs.
-
-## Approach
-
-Create `STRIP_EXTERNAL_LIBS` build mode:
-- Replaces real libraries with minimal stub implementations
-- Functions compile but do nothing (return nullptr, no-ops)
-- Binary compiles successfully but **does not run**
-- Purpose: Size measurement only
+Two-part approach:
-## Architecture
-
-### Stub Implementations
-
-#### 1. wgpu_native Stubs (`third_party/stubs/wgpu_native.c`)
-
-Minimal WebGPU API implementation:
-```c
-// All functions return nullptr or void
-WGPUInstance wgpuCreateInstance(const WGPUInstanceDescriptor* desc) {
- (void)desc;
- return (WGPUInstance)0;
-}
+### 1. Audio: Use miniaudio's Null Backend
-WGPUAdapter wgpuInstanceRequestAdapter(...) {
- return (WGPUAdapter)0;
-}
+miniaudio has built-in `ma_backend_null` that excludes platform audio drivers:
-// ~200 functions to stub
+```cmake
+target_compile_definitions(demo64k PRIVATE
+ MA_ENABLE_ONLY_SPECIFIC_BACKENDS
+ MA_ENABLE_NULL
+)
```
-**Strategy:**
-- Parse `webgpu.h` to generate stubs automatically
-- All pointers return nullptr
-- All numeric returns = 0
-- All void functions are empty
+**Savings:** ~100-200KB (excludes CoreAudio/WASAPI/ALSA/etc.)
+**Benefit:** Audio synthesis still runs, but no driver overhead
-#### 2. GLFW Stubs (`third_party/stubs/glfw3.c`)
+### 2. GPU/Platform: Stub Our Own Abstractions
-Window/input API stubs:
-```c
-int glfwInit(void) { return 1; }
-void glfwTerminate(void) {}
-GLFWwindow* glfwCreateWindow(...) { return (GLFWwindow*)0; }
-void glfwPollEvents(void) {}
-int glfwWindowShouldClose(GLFWwindow* w) { (void)w; return 0; }
-// ~100 functions
-```
+Instead of stubbing ~300 external functions, stub our ~10 platform/gpu wrappers:
-#### 3. miniaudio Stubs (`third_party/stubs/miniaudio.c`)
+**`src/platform/stub_types.h` (STRIP_EXTERNAL_LIBS only):**
+```cpp
+// Minimal WebGPU opaque types
+typedef void* WGPUInstance;
+typedef void* WGPUAdapter;
+typedef void* WGPUDevice;
+typedef void* WGPUBuffer;
+typedef void* WGPUTexture;
+// ... all types as void*
-Audio backend stubs:
-```c
-ma_result ma_context_init(...) { return MA_SUCCESS; }
-ma_result ma_device_init(...) { return MA_SUCCESS; }
-ma_result ma_device_start(...) { return MA_SUCCESS; }
-void ma_device_stop(...) {}
-void ma_device_uninit(...) {}
-// ~50 functions used by demo
+struct WGPUBufferDescriptor {};
+struct WGPUTextureDescriptor {};
+// ... all descriptor structs empty
```
-#### 4. System Library Stubs
+**`src/platform/stub_platform.cc`:**
+```cpp
+#if defined(STRIP_EXTERNAL_LIBS)
+#include "stub_types.h"
-**pthread:**
-```c
-int pthread_create(...) { return 0; }
-int pthread_join(...) { return 0; }
-int pthread_mutex_init(...) { return 0; }
-// ~20 functions
-```
+PlatformState platform_init(bool, int w, int h) {
+ return {w, h, nullptr, nullptr};
+}
-**Math (libm):**
-```c
-double sin(double x) { (void)x; return 0.0; }
-double cos(double x) { (void)x; return 0.0; }
-float sinf(float x) { (void)x; return 0.0f; }
-// ~30 functions
+void platform_poll_events() {}
+bool platform_should_close() { return false; }
+void platform_shutdown() {}
+void platform_present() {}
+double platform_get_time() { return 0.0; }
+#endif
```
-**Platform-specific (macOS):**
-```c
-// Stub Metal/Foundation/Cocoa frameworks
-// Minimal Objective-C stubs if needed
+**`src/gpu/stub_gpu.cc`:**
+```cpp
+#if defined(STRIP_EXTERNAL_LIBS)
+WGPUDevice gpu_create_device() { return nullptr; }
+WGPUBuffer gpu_create_buffer(const WGPUBufferDescriptor*) { return nullptr; }
+// ... stub ~20 gpu wrapper functions
+#endif
```
-### Build System Integration
+**Benefits:**
+- Stub our API (~30 functions) vs external APIs (~300 functions)
+- Opaque pointers only (no real struct definitions needed)
+- Don't link wgpu_native or GLFW at all
+- Actually measures demo code, not stub code
+
+## Build System Integration
**CMakeLists.txt:**
```cmake
if(DEMO_STRIP_EXTERNAL_LIBS)
- # Build stub libraries
- add_library(wgpu_stub STATIC third_party/stubs/wgpu_native.c)
- add_library(glfw_stub STATIC third_party/stubs/glfw3.c)
- add_library(miniaudio_stub STATIC third_party/stubs/miniaudio.c)
- add_library(system_stub STATIC third_party/stubs/system.c)
+ # Audio: Use null backend
+ target_compile_definitions(demo64k PRIVATE
+ MA_ENABLE_ONLY_SPECIFIC_BACKENDS
+ MA_ENABLE_NULL
+ )
- # Override library targets
- set(DEMO_LIBS wgpu_stub glfw_stub miniaudio_stub system_stub)
+ # GPU/Platform: Use stubs
+ target_compile_definitions(demo64k PRIVATE STRIP_EXTERNAL_LIBS)
+ target_sources(demo64k PRIVATE
+ src/platform/stub_platform.cc
+ src/gpu/stub_gpu.cc
+ )
- # Minimal flags (no LTO to see unoptimized size)
+ # Don't link external libs
+ # (only math/pthread from system)
+
+ # Size measurement flags
set(CMAKE_C_FLAGS "-Os")
set(CMAKE_CXX_FLAGS "-Os")
endif()
```
-**Build command:**
+**Build:**
```bash
cmake -S . -B build_size -DDEMO_STRIP_EXTERNAL_LIBS=ON
cmake --build build_size -j4
+strip build_size/demo64k
ls -lh build_size/demo64k # True demo size
```
-## Implementation Plan
-
-### Phase 1: Stub Generation Tool
+## Expected Results
-**Script: `scripts/generate_stubs.py`**
+```
+STRIP_ALL build: 5.1 MB
+├── wgpu_native: 3.5 MB
+├── GLFW: 0.5 MB
+├── miniaudio (full): 0.3 MB
+├── System libs: 0.3 MB
+└── Demo code: 0.5 MB
-Parses header files and generates stub implementations:
-```python
-# Parse webgpu.h, glfw3.h, miniaudio.h
-# Extract function signatures
-# Generate minimal implementations
-# Output to third_party/stubs/
+STRIP_EXTERNAL_LIBS: 0.5 MB
+└── Demo code only: 0.5 MB (100%)
```
-**Input:** Header files with annotations
-**Output:** Complete stub .c files
+Binary compiles but does NOT run (all I/O stubbed).
-**Heuristics:**
-- Pointer returns → nullptr
-- Numeric returns → 0
-- Boolean returns → true/1
-- Void functions → empty body
-- Enum returns → first enum value
+## Implementation Plan
-### Phase 2: Build System Integration
+### Phase 1: Stub Type Definitions (1 hour)
-1. Add `DEMO_STRIP_EXTERNAL_LIBS` option
-2. Create stub library targets
-3. Override `DEMO_LIBS` when flag enabled
-4. Disable features requiring runtime (audio playback, window creation)
+Create `src/platform/stub_types.h`:
+- Define all WebGPU types as `typedef void*`
+- Define all descriptor structs as empty `struct {}`
+- Include guards for `STRIP_EXTERNAL_LIBS`
-### Phase 3: Compatibility Layer
+### Phase 2: Platform Stubs (1 hour)
-Some demo code expects valid objects. Add thin shims:
+Create `src/platform/stub_platform.cc`:
+- Implement ~10 platform functions as no-ops
+- Return dummy PlatformState with reasonable dimensions
+- Compile only when `STRIP_EXTERNAL_LIBS` defined
-**`src/platform/stub_platform.cc`:**
-```cpp
-#if defined(STRIP_EXTERNAL_LIBS)
-// Override platform_init to skip GLFW
-PlatformState platform_init(bool, int, int) {
- PlatformState state = {};
- state.width = 1280;
- state.height = 720;
- return state;
-}
-#endif
-```
+### Phase 3: GPU Stubs (1 hour)
-**`src/audio/stub_audio.cc`:**
-```cpp
-#if defined(STRIP_EXTERNAL_LIBS)
-// Override audio_init to skip miniaudio
-void audio_init() {}
-void audio_start() {}
-#endif
-```
+Create `src/gpu/stub_gpu.cc`:
+- Implement ~20 gpu wrapper functions as no-ops
+- All pointer returns = nullptr
+- All void functions = empty body
+
+### Phase 4: Build Integration (1 hour)
-### Phase 4: Validation
+Update `CMakeLists.txt`:
+- Add `DEMO_STRIP_EXTERNAL_LIBS` option
+- Enable ma_backend_null defines
+- Add stub source files conditionally
+- Remove external library linking
+
+### Phase 5: Validation (1 hour)
-Ensure binary compiles and links:
```bash
-# Should compile successfully
cmake -S . -B build_size -DDEMO_STRIP_EXTERNAL_LIBS=ON
cmake --build build_size -j4
-
-# Size measurement
-ls -lh build_size/demo64k
-
-# Strip debug symbols
strip build_size/demo64k
-ls -lh build_size/demo64k
-
-# Compare with real build
-ls -lh build_strip/demo64k
+size build_size/demo64k
```
-## Expected Results
-
-**Size Breakdown (Projected):**
-```
-Real STRIP_ALL build: 5.1 MB
-├── wgpu_native: 3.5 MB (68%)
-├── GLFW: 0.5 MB (10%)
-├── System libs: 0.6 MB (12%)
-└── Demo code: 0.5 MB (10%)
-
-STRIP_EXTERNAL_LIBS build: 0.5 MB
-└── Demo code only: 0.5 MB (100%)
-```
-
-**Validation:**
-- Binary compiles without errors
-- All symbols resolve
-- Size is significantly smaller (< 1MB)
-- Binary does NOT run (expected)
+**Estimated time: 5 hours**
## Use Cases
-### 1. Size Budget Tracking
+**Weekly size tracking:**
```bash
-# Weekly measurement
-./scripts/measure_size.sh
-# Outputs: Demo=512KB, External=4.5MB
+./scripts/measure_size.sh # Demo=512KB, External=4.5MB
```
-### 2. Optimization Targeting
-Identify which subsystems contribute most to demo size:
+**Subsystem attribution:**
- GPU effects: 150KB
- 3D rendering: 120KB
- Audio synthesis: 100KB
- Asset system: 80KB
-### 3. CI Integration
-Track size growth over time:
+**CI size monitoring:**
```yaml
-# .github/workflows/size_check.yml
-- name: Measure demo size
- run: |
- cmake -B build_size -DDEMO_STRIP_EXTERNAL_LIBS=ON
- size=$(stat -f%z build_size/demo64k)
- echo "Demo size: $size bytes"
+- run: cmake -B build_size -DDEMO_STRIP_EXTERNAL_LIBS=ON
+- run: size build_size/demo64k | tee size_report.txt
```
-## Trade-offs
-
-### Pros
-- Accurate size measurement
-- Identifies optimization targets
-- Simple implementation (stubs)
-- No runtime required
-
-### Cons
-- Maintenance burden (stubs must match real APIs)
-- Binary doesn't run (testing impossible)
-- Platform-specific stubbing (macOS frameworks complex)
-- Stub generation tool needs maintenance
-
-### Alternative Approaches
-
-**1. Link-time size analysis:**
-```bash
-# Use linker map to attribute size
--Wl,-map,output.map
-# Parse map file to see per-symbol sizes
-```
-**Pro:** No stubs needed
-**Con:** Complex parsing, less accurate
-
-**2. Binary diff analysis:**
-```bash
-# Build with/without each library
-# Diff binary sizes
-```
-**Pro:** Simpler
-**Con:** Doesn't isolate demo code cleanly
-
-**3. Compiler size reports:**
-```bash
--ffunction-sections -fdata-sections
--Wl,--print-gc-sections
-```
-**Pro:** Built-in tooling
-**Con:** Still includes external library overhead
-
-**Chosen:** Stub approach (most accurate, clear results)
-
-## Implementation Effort
-
-**Estimated time: 8-12 hours**
-
-- Phase 1: Stub generation (3-4 hours)
-- Phase 2: Build integration (2-3 hours)
-- Phase 3: Compatibility layer (2-3 hours)
-- Phase 4: Validation & documentation (1-2 hours)
-
-**Priority:** Low (measurement tool, not critical for demo)
-
## Success Criteria
-1. Binary compiles successfully with `STRIP_EXTERNAL_LIBS=ON`
-2. Size < 1MB (proves external lib overhead is measured)
-3. Repeatable builds produce consistent sizes
-4. Can track size changes over time
-5. Documentation clear for future use
+1. Binary compiles with `STRIP_EXTERNAL_LIBS=ON`
+2. Size < 1MB (confirms external lib isolation)
+3. Repeatable builds
+4. Tracks size changes over time
## Related Files
-**New files:**
-- `third_party/stubs/wgpu_native.c` - WebGPU stubs (~200 functions)
-- `third_party/stubs/glfw3.c` - GLFW stubs (~100 functions)
-- `third_party/stubs/miniaudio.c` - Audio stubs (~50 functions)
-- `third_party/stubs/system.c` - System library stubs (pthread, math)
-- `scripts/generate_stubs.py` - Stub generation tool
+**New:**
+- `src/platform/stub_types.h` - WebGPU opaque types
+- `src/platform/stub_platform.cc` - Platform stubs (~10 functions)
+- `src/gpu/stub_gpu.cc` - GPU stubs (~20 functions)
- `scripts/measure_size.sh` - Size measurement script
-**Modified files:**
+**Modified:**
- `CMakeLists.txt` - Add STRIP_EXTERNAL_LIBS mode
-- `src/platform/platform.cc` - Conditional stub overrides
-- `src/audio/audio.cc` - Conditional stub overrides
-
-## Notes
-
-- This is a **measurement tool**, not a runtime mode
-- Binary will not execute (all APIs stubbed)
-- Useful for tracking optimization progress
-- Can reveal surprising size contributors
-- Platform-specific (stubs may differ on Windows/Linux/macOS)
diff --git a/scripts/measure_size.sh b/scripts/measure_size.sh
new file mode 100755
index 0000000..51c773f
--- /dev/null
+++ b/scripts/measure_size.sh
@@ -0,0 +1,46 @@
+#!/bin/bash
+# Measures core demo size by building with STRIP_EXTERNAL_LIBS
+# Binary compiles but does NOT run (all I/O stubbed)
+
+set -e
+
+echo "=== Demo Size Measurement ==="
+echo ""
+
+# Clean previous build
+rm -rf build_size
+
+# Build with STRIP_EXTERNAL_LIBS
+echo "Building with STRIP_EXTERNAL_LIBS..."
+cmake -S . -B build_size -DDEMO_STRIP_EXTERNAL_LIBS=ON
+cmake --build build_size -j4
+
+# Strip debug symbols
+strip build_size/demo64k
+
+# Measure sizes
+echo ""
+echo "=== Size Results ==="
+echo ""
+
+DEMO_SIZE=$(stat -f%z build_size/demo64k 2>/dev/null || stat -c%s build_size/demo64k)
+DEMO_SIZE_KB=$((DEMO_SIZE / 1024))
+
+echo "Demo code only: ${DEMO_SIZE_KB} KB (${DEMO_SIZE} bytes)"
+
+# Compare with normal build if it exists
+if [ -f build/demo64k ]; then
+ NORMAL_SIZE=$(stat -f%z build/demo64k 2>/dev/null || stat -c%s build/demo64k)
+ NORMAL_SIZE_KB=$((NORMAL_SIZE / 1024))
+ EXTERNAL_SIZE=$((NORMAL_SIZE - DEMO_SIZE))
+ EXTERNAL_SIZE_KB=$((EXTERNAL_SIZE / 1024))
+
+ echo ""
+ echo "Normal build: ${NORMAL_SIZE_KB} KB (${NORMAL_SIZE} bytes)"
+ echo "External libs: ${EXTERNAL_SIZE_KB} KB (${EXTERNAL_SIZE} bytes)"
+ echo ""
+ echo "Demo is $(echo "scale=1; $DEMO_SIZE * 100 / $NORMAL_SIZE" | bc)% of total size"
+fi
+
+echo ""
+echo "Note: Size measurement binary does NOT run (all I/O stubbed)"
diff --git a/src/gpu/stub_gpu.cc b/src/gpu/stub_gpu.cc
new file mode 100644
index 0000000..0b4185c
--- /dev/null
+++ b/src/gpu/stub_gpu.cc
@@ -0,0 +1,83 @@
+// Stub GPU implementation for size measurement builds.
+// All functions are no-ops. Binary compiles but does NOT run.
+// This file is only compiled when STRIP_EXTERNAL_LIBS is defined.
+
+#if defined(STRIP_EXTERNAL_LIBS)
+
+#include "gpu.h"
+#include "platform/stub_types.h"
+
+GpuBuffer gpu_create_buffer(WGPUDevice device, size_t size, uint32_t usage,
+ const void* data) {
+ (void)device;
+ (void)size;
+ (void)usage;
+ (void)data;
+ return {nullptr, 0};
+}
+
+RenderPass gpu_create_render_pass(WGPUDevice device, WGPUTextureFormat format,
+ const char* shader_code,
+ ResourceBinding* bindings, int num_bindings) {
+ (void)device;
+ (void)format;
+ (void)shader_code;
+ (void)bindings;
+ (void)num_bindings;
+ return {nullptr, nullptr, 0, 0};
+}
+
+ComputePass gpu_create_compute_pass(WGPUDevice device, const char* shader_code,
+ ResourceBinding* bindings,
+ int num_bindings) {
+ (void)device;
+ (void)shader_code;
+ (void)bindings;
+ (void)num_bindings;
+ return {nullptr, nullptr, 0, 0, 0};
+}
+
+void gpu_init(PlatformState* platform_state) {
+ (void)platform_state;
+}
+
+void gpu_draw(float audio_peak, float aspect_ratio, float time, float beat) {
+ (void)audio_peak;
+ (void)aspect_ratio;
+ (void)time;
+ (void)beat;
+}
+
+void gpu_resize(int width, int height) {
+ (void)width;
+ (void)height;
+}
+
+void gpu_shutdown() {
+}
+
+const GpuContext* gpu_get_context() {
+ static GpuContext ctx = {nullptr, nullptr, WGPUTextureFormat_BGRA8Unorm};
+ return &ctx;
+}
+
+MainSequence* gpu_get_main_sequence() {
+ return nullptr;
+}
+
+#if !defined(STRIP_ALL)
+void gpu_simulate_until(float time, float bpm) {
+ (void)time;
+ (void)bpm;
+}
+
+void gpu_add_custom_effect(Effect* effect, float start_time, float end_time,
+ int priority) {
+ (void)effect;
+ (void)start_time;
+ (void)end_time;
+ (void)priority;
+}
+#endif
+
+#endif // STRIP_EXTERNAL_LIBS
diff --git a/src/platform/platform.h b/src/platform/platform.h
index 0a98850..7bcee9d 100644
--- a/src/platform/platform.h
+++ b/src/platform/platform.h
@@ -7,7 +7,10 @@
#include <cstring>
// WebGPU specific headers and shims
-#if defined(DEMO_CROSS_COMPILE_WIN32)
+#if defined(STRIP_EXTERNAL_LIBS)
+#include "stub_types.h"
+
+#elif defined(DEMO_CROSS_COMPILE_WIN32)
#include <webgpu/webgpu.h>
#include <webgpu/wgpu.h>
diff --git a/src/platform/stub_platform.cc b/src/platform/stub_platform.cc
new file mode 100644
index 0000000..61473a0
--- /dev/null
+++ b/src/platform/stub_platform.cc
@@ -0,0 +1,53 @@
+// Stub platform implementation for size measurement builds.
+// All functions are no-ops. Binary compiles but does NOT run.
+// This file is only compiled when STRIP_EXTERNAL_LIBS is defined.
+
+#if defined(STRIP_EXTERNAL_LIBS)
+
+#include "platform.h"
+#include "stub_types.h"
+
+// Forward declare GLFWwindow stub
+struct GLFWwindow {};
+
+PlatformState platform_init(bool fullscreen, int width, int height) {
+ (void)fullscreen;
+ PlatformState state = {};
+ state.width = width;
+ state.height = height;
+ state.aspect_ratio = (float)width / (float)height;
+ state.window = nullptr;
+ state.time = 0.0;
+ state.is_fullscreen = false;
+ return state;
+}
+
+void platform_shutdown(PlatformState* state) {
+ (void)state;
+}
+
+void platform_poll(PlatformState* state) {
+ (void)state;
+}
+
+bool platform_should_close(PlatformState* state) {
+ (void)state;
+ return false;
+}
+
+void platform_toggle_fullscreen(PlatformState* state) {
+ (void)state;
+}
+
+WGPUSurface platform_create_wgpu_surface(WGPUInstance instance,
+ PlatformState* state) {
+ (void)instance;
+ (void)state;
+ return nullptr;
+}
+
+double platform_get_time() {
+ return 0.0;
+}
+
+#endif // STRIP_EXTERNAL_LIBS
diff --git a/src/platform/stub_types.h b/src/platform/stub_types.h
new file mode 100644
index 0000000..f532e04
--- /dev/null
+++ b/src/platform/stub_types.h
@@ -0,0 +1,159 @@
+// Minimal WebGPU type definitions for size measurement builds.
+// All types are opaque pointers, all descriptor structs are empty.
+// This file is only used when STRIP_EXTERNAL_LIBS is defined.
+
+#pragma once
+
+#if defined(STRIP_EXTERNAL_LIBS)
+
+#include <cstdint>
+#include <cstring>
+
+// Opaque handle types
+typedef void* WGPUInstance;
+typedef void* WGPUAdapter;
+typedef void* WGPUSurface;
+typedef void* WGPUDevice;
+typedef void* WGPUQueue;
+typedef void* WGPUBuffer;
+typedef void* WGPUTexture;
+typedef void* WGPUTextureView;
+typedef void* WGPUSampler;
+typedef void* WGPUShaderModule;
+typedef void* WGPUBindGroupLayout;
+typedef void* WGPUPipelineLayout;
+typedef void* WGPUBindGroup;
+typedef void* WGPURenderPipeline;
+typedef void* WGPUComputePipeline;
+typedef void* WGPUCommandEncoder;
+typedef void* WGPURenderPassEncoder;
+typedef void* WGPUComputePassEncoder;
+typedef void* WGPUCommandBuffer;
+typedef void* WGPUQuerySet;
+typedef void* WGPURenderBundle;
+typedef void* WGPURenderBundleEncoder;
+
+// Enums (minimal values)
+typedef enum {
+ WGPUTextureFormat_Undefined = 0,
+ WGPUTextureFormat_BGRA8Unorm = 1,
+ WGPUTextureFormat_RGBA8Unorm = 2,
+} WGPUTextureFormat;
+
+typedef enum {
+ WGPUBufferBindingType_Uniform = 0,
+ WGPUBufferBindingType_Storage = 1,
+ WGPUBufferBindingType_ReadOnlyStorage = 2,
+} WGPUBufferBindingType;
+
+typedef enum {
+ WGPULoadOp_Clear = 0,
+ WGPULoadOp_Load = 1,
+} WGPULoadOp;
+
+typedef enum {
+ WGPUStoreOp_Store = 0,
+ WGPUStoreOp_Discard = 1,
+} WGPUStoreOp;
+
+typedef enum {
+ WGPUSurfaceGetCurrentTextureStatus_Success = 0,
+ WGPUSurfaceGetCurrentTextureStatus_Timeout = 1,
+ WGPUSurfaceGetCurrentTextureStatus_Outdated = 2,
+ WGPUSurfaceGetCurrentTextureStatus_Lost = 3,
+} WGPUSurfaceGetCurrentTextureStatus;
+
+// Buffer usage flags
+#define WGPUBufferUsage_MapRead 0x00000001
+#define WGPUBufferUsage_MapWrite 0x00000002
+#define WGPUBufferUsage_CopySrc 0x00000004
+#define WGPUBufferUsage_CopyDst 0x00000008
+#define WGPUBufferUsage_Index 0x00000010
+#define WGPUBufferUsage_Vertex 0x00000020
+#define WGPUBufferUsage_Uniform 0x00000040
+#define WGPUBufferUsage_Storage 0x00000080
+#define WGPUBufferUsage_Indirect 0x00000100
+#define WGPUBufferUsage_QueryResolve 0x00000200
+
+// Descriptor structs (all empty)
+struct WGPUInstanceDescriptor {};
+struct WGPUAdapterInfo {};
+struct WGPUSurfaceConfiguration {};
+struct WGPUDeviceDescriptor {};
+struct WGPUBufferDescriptor {};
+struct WGPUTextureDescriptor {};
+struct WGPUTextureViewDescriptor {};
+struct WGPUSamplerDescriptor {};
+struct WGPUShaderModuleDescriptor {};
+struct WGPUShaderSourceWGSL {};
+struct WGPUShaderModuleWGSLDescriptor {};
+struct WGPUBindGroupLayoutDescriptor {};
+struct WGPUBindGroupLayoutEntry {};
+struct WGPUPipelineLayoutDescriptor {};
+struct WGPUBindGroupDescriptor {};
+struct WGPUBindGroupEntry {};
+struct WGPURenderPipelineDescriptor {};
+struct WGPUComputePipelineDescriptor {};
+struct WGPUVertexState {};
+struct WGPUFragmentState {};
+struct WGPUColorTargetState {};
+struct WGPUBlendState {};
+struct WGPUPrimitiveState {};
+struct WGPUMultisampleState {};
+struct WGPUDepthStencilState {};
+struct WGPURenderPassDescriptor {};
+struct WGPURenderPassColorAttachment {
+ WGPUTextureView view;
+ WGPULoadOp loadOp;
+ WGPUStoreOp storeOp;
+ struct { float r, g, b, a; } clearValue;
+ uint32_t depthSlice;
+};
+struct WGPUComputePassDescriptor {};
+struct WGPUCommandEncoderDescriptor {};
+struct WGPUCommandBufferDescriptor {};
+struct WGPUSurfaceTexture {
+ WGPUTexture texture;
+ WGPUSurfaceGetCurrentTextureStatus status;
+};
+struct WGPUColor { float r, g, b, a; };
+struct WGPUExtent3D { uint32_t width, height, depthOrArrayLayers; };
+struct WGPUOrigin3D { uint32_t x, y, z; };
+struct WGPUImageCopyTexture {};
+struct WGPUImageCopyBuffer {};
+struct WGPUTextureDataLayout {};
+struct WGPUBufferBindingLayout {};
+struct WGPUSamplerBindingLayout {};
+struct WGPUTextureBindingLayout {};
+struct WGPUStorageTextureBindingLayout {};
+
+// String view helper (for compatibility)
+struct WGPUStringView {
+ const char* data;
+ size_t length;
+};
+
+static inline WGPUStringView str_view(const char* str) {
+ if (!str) return {nullptr, 0};
+ return {str, strlen(str)};
+}
+
+static inline WGPUStringView label_view(const char* str) {
+ (void)str;
+ return {nullptr, 0};
+}
+
+// Platform shims (no-ops)
+static inline void platform_wgpu_wait_any(WGPUInstance) {}
+static inline void platform_wgpu_set_error_callback(WGPUDevice, void*) {}
+
+// Constants
+#define WGPU_DEPTH_SLICE_UNDEFINED 0xffffffff
+#define WGPUOptionalBool_True true
+#define WGPUOptionalBool_False false
+
+// Callback types (empty)
+typedef void (*WGPUErrorCallback)(void*, void*, const char*);
+typedef void (*WGPUUncapturedErrorCallback)(void*, void*, const char*);
+
+#endif // STRIP_EXTERNAL_LIBS
diff --git a/src/stub_main.cc b/src/stub_main.cc
new file mode 100644
index 0000000..8540fcd
--- /dev/null
+++ b/src/stub_main.cc
@@ -0,0 +1,13 @@
+// Stub main for size measurement builds.
+// Binary compiles but does NOT run.
+// This file is only compiled when STRIP_EXTERNAL_LIBS is defined.
+
+#if defined(STRIP_EXTERNAL_LIBS)
+
+int main(int argc, char** argv) {
+ (void)argc;
+ (void)argv;
+ return 0;
+}
+
+#endif // STRIP_EXTERNAL_LIBS