Threading research.
This commit is contained in:
parent
f71136b50a
commit
dd85f280ba
2 changed files with 571 additions and 0 deletions
301
docs/PTHREADS.md
Normal file
301
docs/PTHREADS.md
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
# Emscripten pthreads for KiCad WASM
|
||||
|
||||
## Overview
|
||||
|
||||
Emscripten can compile C++ threading code to WebAssembly using Web Workers and SharedArrayBuffer. This allows KiCad's existing `BS::thread_pool` code to work **unchanged** in the browser.
|
||||
|
||||
## How It Works
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ Browser Environment │
|
||||
│ │
|
||||
│ ┌────────────────────────────────────────────────────┐ │
|
||||
│ │ SharedArrayBuffer (Shared Memory) │ │
|
||||
│ │ │ │
|
||||
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
|
||||
│ │ │ Main │ │ Worker │ │ Worker │ ... │ │
|
||||
│ │ │ Thread │ │ Thread │ │ Thread │ │ │
|
||||
│ │ │ │ │ │ │ │ │ │
|
||||
│ │ │ BOARD* │ │ BOARD* │ │ BOARD* │ │ │
|
||||
│ │ │ (same!) │ │ (same!) │ │ (same!) │ │ │
|
||||
│ │ └─────────┘ └─────────┘ └─────────┘ │ │
|
||||
│ └────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ All threads share memory - C++ pointers work normally │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
Emscripten:
|
||||
1. Creates Web Workers for each pthread
|
||||
2. Uses SharedArrayBuffer for shared memory between workers
|
||||
3. Implements pthread synchronization primitives (mutex, condition variables)
|
||||
4. Existing C++ thread code compiles without modification
|
||||
|
||||
## Build Configuration
|
||||
|
||||
### Minimal Flags
|
||||
|
||||
```bash
|
||||
emcc main.cpp -o app.js \
|
||||
-pthread \
|
||||
-sPROXY_TO_PTHREAD=1
|
||||
```
|
||||
|
||||
### Recommended Full Configuration
|
||||
|
||||
```bash
|
||||
emcc main.cpp -o app.js \
|
||||
-pthread \
|
||||
-sPROXY_TO_PTHREAD=1 \
|
||||
-sPTHREAD_POOL_SIZE="Math.min(navigator.hardwareConcurrency, 8)" \
|
||||
-sALLOW_BLOCKING_ON_MAIN_THREAD=0 \
|
||||
-sOFFSCREENCANVAS_SUPPORT=1 \
|
||||
-sOFFSCREEN_FRAMEBUFFER=1 \
|
||||
-sMALLOC=mimalloc \
|
||||
-sINITIAL_MEMORY=512MB \
|
||||
-sSTACK_SIZE=5MB \
|
||||
-sPTHREAD_POOL_SIZE_STRICT=0
|
||||
```
|
||||
|
||||
### Flag Explanations
|
||||
|
||||
| Flag | Purpose |
|
||||
|------|---------|
|
||||
| `-pthread` | Enable pthread support (required) |
|
||||
| `-sPROXY_TO_PTHREAD=1` | Move main() to worker thread, keeps browser responsive |
|
||||
| `-sPTHREAD_POOL_SIZE=N` | Pre-create N worker threads (avoids async creation delay) |
|
||||
| `-sALLOW_BLOCKING_ON_MAIN_THREAD=0` | Error if main thread blocks (catches bugs) |
|
||||
| `-sOFFSCREENCANVAS_SUPPORT=1` | WebGL rendering in worker threads |
|
||||
| `-sOFFSCREEN_FRAMEBUFFER=1` | Fallback if OffscreenCanvas unavailable |
|
||||
| `-sMALLOC=mimalloc` | Better allocator for multithreaded code |
|
||||
| `-sPTHREAD_POOL_SIZE_STRICT=0` | Allow pool to grow beyond initial size |
|
||||
|
||||
### Dynamic Thread Pool Size
|
||||
|
||||
Use JavaScript expression for dynamic sizing:
|
||||
```bash
|
||||
-sPTHREAD_POOL_SIZE="Math.min(navigator.hardwareConcurrency, 8)"
|
||||
```
|
||||
|
||||
This creates threads based on available CPU cores (max 8).
|
||||
|
||||
## Required HTTP Headers
|
||||
|
||||
**CRITICAL**: SharedArrayBuffer requires these headers or it won't be available.
|
||||
|
||||
```http
|
||||
Cross-Origin-Embedder-Policy: require-corp
|
||||
Cross-Origin-Opener-Policy: same-origin
|
||||
```
|
||||
|
||||
### Server Configuration Examples
|
||||
|
||||
**Nginx:**
|
||||
```nginx
|
||||
location / {
|
||||
add_header Cross-Origin-Embedder-Policy "require-corp" always;
|
||||
add_header Cross-Origin-Opener-Policy "same-origin" always;
|
||||
}
|
||||
```
|
||||
|
||||
**Apache (.htaccess):**
|
||||
```apache
|
||||
Header set Cross-Origin-Embedder-Policy "require-corp"
|
||||
Header set Cross-Origin-Opener-Policy "same-origin"
|
||||
```
|
||||
|
||||
**Express.js:**
|
||||
```javascript
|
||||
app.use((req, res, next) => {
|
||||
res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
|
||||
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
|
||||
next();
|
||||
});
|
||||
```
|
||||
|
||||
**Vercel (vercel.json):**
|
||||
```json
|
||||
{
|
||||
"headers": [
|
||||
{
|
||||
"source": "/(.*)",
|
||||
"headers": [
|
||||
{ "key": "Cross-Origin-Embedder-Policy", "value": "require-corp" },
|
||||
{ "key": "Cross-Origin-Opener-Policy", "value": "same-origin" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**CloudFlare Pages (_headers):**
|
||||
```
|
||||
/*
|
||||
Cross-Origin-Embedder-Policy: require-corp
|
||||
Cross-Origin-Opener-Policy: same-origin
|
||||
```
|
||||
|
||||
## Browser Support
|
||||
|
||||
| Browser | Version | Status |
|
||||
|---------|---------|--------|
|
||||
| Chrome | 92+ | ✅ Full support |
|
||||
| Firefox | 79+ | ✅ Full support |
|
||||
| Safari | 16.4+ | ✅ Full support |
|
||||
| Edge | 92+ | ✅ Full support (Chromium) |
|
||||
|
||||
All modern browsers support SharedArrayBuffer with proper headers.
|
||||
|
||||
## Feature Detection
|
||||
|
||||
Check if threading is available at runtime:
|
||||
|
||||
```javascript
|
||||
function checkThreadingSupport() {
|
||||
const hasSharedArrayBuffer = typeof SharedArrayBuffer !== 'undefined';
|
||||
const isCrossOriginIsolated = crossOriginIsolated;
|
||||
|
||||
console.log('SharedArrayBuffer available:', hasSharedArrayBuffer);
|
||||
console.log('Cross-origin isolated:', isCrossOriginIsolated);
|
||||
|
||||
if (!hasSharedArrayBuffer) {
|
||||
console.error('SharedArrayBuffer not available - check HTTPS');
|
||||
}
|
||||
if (!isCrossOriginIsolated) {
|
||||
console.error('Not cross-origin isolated - check COOP/COEP headers');
|
||||
}
|
||||
|
||||
return hasSharedArrayBuffer && isCrossOriginIsolated;
|
||||
}
|
||||
```
|
||||
|
||||
## PROXY_TO_PTHREAD Explained
|
||||
|
||||
Without `PROXY_TO_PTHREAD`:
|
||||
- main() runs on browser's main thread
|
||||
- Any blocking call (mutex, sleep, join) freezes the browser
|
||||
- User can't interact while computation runs
|
||||
|
||||
With `PROXY_TO_PTHREAD`:
|
||||
- Emscripten creates a new main() that spawns a worker
|
||||
- Your original main() runs entirely in that worker
|
||||
- Browser main thread stays responsive
|
||||
- Blocking calls work normally
|
||||
|
||||
**Always use PROXY_TO_PTHREAD for applications with UI.**
|
||||
|
||||
## Memory Considerations
|
||||
|
||||
Each worker thread loads:
|
||||
- Complete JavaScript glue code
|
||||
- Complete WASM module
|
||||
|
||||
Memory usage scales with thread count:
|
||||
- 2 threads: ~200MB typical
|
||||
- 8 threads: ~400MB typical
|
||||
- 20 threads: ~600MB+ typical
|
||||
|
||||
Keep thread pool size reasonable (4-8 threads recommended).
|
||||
|
||||
## Debugging
|
||||
|
||||
### Check if pthreads are working
|
||||
|
||||
```cpp
|
||||
#include <emscripten/threading.h>
|
||||
#include <thread>
|
||||
|
||||
int main() {
|
||||
printf("Main thread: %d\n", emscripten_is_main_runtime_thread());
|
||||
printf("Hardware concurrency: %d\n", std::thread::hardware_concurrency());
|
||||
|
||||
std::thread t([]() {
|
||||
printf("Worker thread: %d\n", emscripten_is_main_runtime_thread());
|
||||
});
|
||||
t.join();
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
### Common Issues
|
||||
|
||||
**"SharedArrayBuffer is not defined"**
|
||||
- Missing COOP/COEP headers
|
||||
- Not served over HTTPS (except localhost)
|
||||
|
||||
**"pthread_create failed"**
|
||||
- Thread pool exhausted, increase PTHREAD_POOL_SIZE
|
||||
- Or use PTHREAD_POOL_SIZE_STRICT=0 to allow growth
|
||||
|
||||
**Browser hangs/freezes**
|
||||
- Missing PROXY_TO_PTHREAD flag
|
||||
- Blocking on main thread
|
||||
|
||||
**Memory errors**
|
||||
- Increase INITIAL_MEMORY
|
||||
- Increase STACK_SIZE (each thread needs stack space)
|
||||
|
||||
## Comparison with Alternatives
|
||||
|
||||
| Approach | KiCad Changes | Deployment | Performance |
|
||||
|----------|---------------|------------|-------------|
|
||||
| **Emscripten pthreads** | None | Needs headers | Best |
|
||||
| Synchronous stub | None (stub only) | Works everywhere | Slowest |
|
||||
| Manual Web Workers | Significant | Works everywhere | Good |
|
||||
|
||||
## KiCad-Specific Notes
|
||||
|
||||
KiCad uses `BS::thread_pool` (Barak Shoshany's thread pool):
|
||||
- Location: `kicad/thirdparty/thread-pool/bs_thread_pool.hpp`
|
||||
- Uses standard C++ threads internally
|
||||
- Works unchanged with Emscripten pthreads
|
||||
|
||||
Heavy threading usage in:
|
||||
- DRC (Design Rule Check) - `pcbnew/drc/`
|
||||
- Zone filling - `pcbnew/zone_filler.cpp`
|
||||
- 3D raytracing - `3d-viewer/3d_rendering/raytracing/`
|
||||
- Library loading - background tasks
|
||||
|
||||
With pthreads enabled, all these run in parallel as they do natively.
|
||||
|
||||
## Hosting Compatibility
|
||||
|
||||
| Host | Headers Configurable | pthreads Work |
|
||||
|------|---------------------|---------------|
|
||||
| GitHub Pages | ❌ No | ❌ No |
|
||||
| Netlify Free | ❌ No | ❌ No |
|
||||
| Netlify Pro | ✅ Yes | ✅ Yes |
|
||||
| Vercel | ✅ Yes | ✅ Yes |
|
||||
| CloudFlare Pages | ✅ Yes | ✅ Yes |
|
||||
| AWS S3 + CloudFront | ✅ Yes | ✅ Yes |
|
||||
| Your own server | ✅ Yes | ✅ Yes |
|
||||
| localhost | ✅ N/A | ✅ Yes |
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. **Add build flags:**
|
||||
```bash
|
||||
-pthread -sPROXY_TO_PTHREAD=1 -sPTHREAD_POOL_SIZE=4
|
||||
```
|
||||
|
||||
2. **Configure server headers:**
|
||||
```
|
||||
Cross-Origin-Embedder-Policy: require-corp
|
||||
Cross-Origin-Opener-Policy: same-origin
|
||||
```
|
||||
|
||||
3. **Test in browser:**
|
||||
```javascript
|
||||
console.log('Threading:', crossOriginIsolated && typeof SharedArrayBuffer !== 'undefined');
|
||||
```
|
||||
|
||||
4. **Done.** Existing C++ threading code works.
|
||||
|
||||
## References
|
||||
|
||||
- [Emscripten pthreads documentation](https://emscripten.org/docs/porting/pthreads.html)
|
||||
- [MDN SharedArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer)
|
||||
- [Chrome SharedArrayBuffer update](https://developer.chrome.com/blog/enabling-shared-array-buffer)
|
||||
- [web.dev WebAssembly threads guide](https://web.dev/articles/webassembly-threads)
|
||||
270
docs/THREADING_RESEARCH.md
Normal file
270
docs/THREADING_RESEARCH.md
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
# KiCad Threading Analysis for WebAssembly Migration
|
||||
|
||||
> **See also:** [PTHREADS.md](./PTHREADS.md) for Emscripten pthreads implementation details.
|
||||
|
||||
## Executive Summary
|
||||
|
||||
KiCad relies **heavily** on multithreading for performance and UI responsiveness. Moving to single-threaded WASM will cause **significant performance degradation** in specific features, but the application will remain **functional**. The wxWidgets WASM layer already stubs out threading APIs, proving the approach is viable.
|
||||
|
||||
---
|
||||
|
||||
## 1. Threading Infrastructure Overview
|
||||
|
||||
### Core Thread Pool System
|
||||
- **Implementation**: `BS::priority_thread_pool` (Barak Shoshany's library)
|
||||
- **Location**: `kicad/include/thread_pool.h`, `kicad/thirdparty/thread-pool/bs_thread_pool.hpp`
|
||||
- **Access**: Global `GetKiCadThreadPool()` function
|
||||
- **Configuration**: Thread count via `ADVANCED_CFG::GetCfg().m_MaximumThreads`
|
||||
|
||||
### Synchronization Primitives Used
|
||||
| Primitive | Usage Count | Purpose |
|
||||
|-----------|-------------|---------|
|
||||
| `std::mutex` | ~50+ files | Shared data protection |
|
||||
| `std::atomic<T>` | 192 files | Lock-free counters/flags |
|
||||
| `KISPINLOCK` | Core connectivity | Low-contention locking |
|
||||
| `SYNC_QUEUE<T>` | PCM, tasks | Thread-safe queues |
|
||||
| `std::condition_variable` | ~10 files | Thread signaling |
|
||||
|
||||
---
|
||||
|
||||
## 2. Features Using Threading (Impact Analysis)
|
||||
|
||||
### CRITICAL IMPACT (Will be noticeably slower)
|
||||
|
||||
#### DRC (Design Rule Check)
|
||||
- **Files**: `pcbnew/drc/drc_test_provider_*.cpp` (10+ test providers)
|
||||
- **Pattern**: `tp.submit_loop(0, items.size(), check_lambda)`
|
||||
- **Why threads?**: Thousands of items, O(n²) clearance checks
|
||||
- **Without threads**:
|
||||
- 4-8x slower (proportional to CPU cores)
|
||||
- UI freeze for 30 seconds to several minutes on large boards
|
||||
- **Workaround**: Must add progress reporting + time-slicing
|
||||
|
||||
#### Zone Filling
|
||||
- **File**: `pcbnew/zone_filler.cpp` (lines 609-697)
|
||||
- **Pattern**: Two-stage parallel queue (fill → tessellate)
|
||||
- **Why threads?**: Complex polygon operations (Clipper library), GPU tessellation
|
||||
- **Without threads**:
|
||||
- 5-30 second freeze per zone refill
|
||||
- **Workaround**: Chunked processing with progress callbacks
|
||||
|
||||
#### 3D Viewer Raytracing
|
||||
- **File**: `3d-viewer/3d_rendering/raytracing/render_3d_raytrace_base.cpp`
|
||||
- **Pattern**: Work-stealing with `atomic<size_t>` counter, screen divided into blocks
|
||||
- **Why threads?**: Embarrassingly parallel (each pixel independent)
|
||||
- **Without threads**:
|
||||
- 10-40 seconds per frame (unusable for interaction)
|
||||
- **Workaround**: Progressive rendering, lower resolution, skip raytracing in WASM
|
||||
|
||||
### MEDIUM IMPACT (Noticeable but manageable)
|
||||
|
||||
#### Library Loading (Symbols/Footprints)
|
||||
- **Files**: `eeschema/libraries/symbol_library_adapter.cpp`, `pcbnew/footprint_library_adapter.cpp`
|
||||
- **Pattern**: Low-priority background tasks (`BS::pr::lowest`)
|
||||
- **Why threads?**: Hundreds of libraries, file I/O blocking
|
||||
- **Without threads**:
|
||||
- 10+ second startup hang
|
||||
- **Workaround**: Progressive loading with Asyncify, show loading indicator
|
||||
|
||||
#### Connectivity Graph
|
||||
- **Files**: `eeschema/connection_graph.cpp`, `pcbnew/connectivity/connectivity_algo.cpp`
|
||||
- **Pattern**: `submit_loop()` for parallel subgraph updates
|
||||
- **Why threads?**: Large schematics have thousands of connections
|
||||
- **Without threads**:
|
||||
- 2-10 second lag on large schematics after edits
|
||||
- **Workaround**: Incremental updates, defer full recalculation
|
||||
|
||||
#### 3D Layer Creation
|
||||
- **File**: `3d-viewer/3d_canvas/create_layer_items.cpp`
|
||||
- **Pattern**: Per-layer mutexes, parallel zone processing
|
||||
- **Without threads**: Slower initial 3D view load
|
||||
|
||||
### LOW IMPACT (Acceptable degradation)
|
||||
|
||||
| Feature | File | Impact |
|
||||
|---------|------|--------|
|
||||
| Font polling | `common/widgets/font_choice.cpp` | 30s poll just runs on main thread |
|
||||
| Git status | `kicad/project_tree_pane.cpp` | Slower git operations |
|
||||
| Update checker | `kicad/update_manager.cpp` | Blocks during check |
|
||||
| PCM downloads | `kicad/pcm/pcm_task_manager.cpp` | Sequential downloads |
|
||||
| Python plugins | `scripting/python_manager.cpp` | Slower plugin load |
|
||||
| STEP export | `pcbnew/dialogs/dialog_export_step_process.cpp` | Blocking export |
|
||||
|
||||
---
|
||||
|
||||
## 3. Current WASM Threading Status
|
||||
|
||||
### wxWidgets WASM Layer (Already Implemented)
|
||||
|
||||
```
|
||||
wxUSE_THREADS = 1 (API available, but stubbed)
|
||||
```
|
||||
|
||||
**Pthread Shim** (`wxwidgets/include/wx/wasm/pthread.h`):
|
||||
- `pthread_attr_getschedpolicy()` → no-op
|
||||
- `pthread_attr_setschedparam()` → no-op
|
||||
- `pthread_setschedparam()` → no-op
|
||||
- `sched_get_priority_*()` → returns 0
|
||||
|
||||
**Event Loop** (`wxwidgets/src/wasm/evtloop.cpp`):
|
||||
- Uses `emscripten_set_main_loop()` - browser's requestAnimationFrame
|
||||
- Single-threaded cooperative scheduler
|
||||
- ~16ms per frame at 60fps
|
||||
|
||||
### Proven Async Patterns
|
||||
|
||||
| Pattern | File | Status |
|
||||
|---------|------|--------|
|
||||
| Modal dialogs | `wxwidgets/src/wasm/dialog.cpp` | Working via Asyncify |
|
||||
| Clipboard | `wxwidgets/src/wasm/clipbrd.cpp` | Working via Asyncify |
|
||||
| Timers | `wxwidgets/src/wasm/timer.cpp` | Working via `emscripten_async_call` |
|
||||
|
||||
**Asyncify Configuration** (`tests/wasm-app/Makefile.wasm`):
|
||||
```makefile
|
||||
-sASYNCIFY=1
|
||||
-sASYNCIFY_STACK_SIZE=8192
|
||||
-sASYNCIFY_IMPORTS=['startModal','js_writeTextToClipboard',...]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Migration Strategies
|
||||
|
||||
### Option A: Sequential with Progress Feedback (Recommended for MVP)
|
||||
|
||||
**Approach**: Let all operations run sequentially, add progress UI
|
||||
|
||||
```cpp
|
||||
#ifdef __EMSCRIPTEN__
|
||||
// Run sequentially with progress updates
|
||||
for (size_t i = 0; i < items.size(); i++) {
|
||||
checkItem(items[i]);
|
||||
if (i % 100 == 0) {
|
||||
reporter.SetCurrentProgress(i, items.size());
|
||||
emscripten_sleep(0); // Yield to browser
|
||||
}
|
||||
}
|
||||
#else
|
||||
// Original threaded code
|
||||
tp.submit_loop(0, items.size(), check_lambda);
|
||||
#endif
|
||||
```
|
||||
|
||||
**Pros**: Simple, predictable, works everywhere
|
||||
**Cons**: Slower, UI shows "working" frequently
|
||||
|
||||
### Option B: Asyncify Time-Slicing
|
||||
|
||||
**Approach**: Break operations into 10-50ms chunks, yield between
|
||||
|
||||
```cpp
|
||||
class TimeSlicedDRC {
|
||||
void RunChunk() {
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
while (m_currentItem < m_items.size()) {
|
||||
checkItem(m_items[m_currentItem++]);
|
||||
if (elapsed(start) > 16ms) { // One frame
|
||||
emscripten_async_call(RunChunk, this, 0);
|
||||
return; // Yield to browser
|
||||
}
|
||||
}
|
||||
OnComplete();
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**Pros**: Responsive UI during long operations
|
||||
**Cons**: More complex, requires state machine refactoring
|
||||
|
||||
### Option C: Web Workers (Heavy Operations Only)
|
||||
|
||||
**Approach**: Run DRC/Router in separate WASM instance
|
||||
|
||||
```javascript
|
||||
// Main thread
|
||||
const drcWorker = new Worker('kicad-drc-worker.js');
|
||||
drcWorker.postMessage({ type: 'runDRC', boardData: serializedBoard });
|
||||
drcWorker.onmessage = (e) => updateDRCResults(e.data);
|
||||
```
|
||||
|
||||
**Pros**: True parallelism, responsive UI
|
||||
**Cons**:
|
||||
- Requires `SharedArrayBuffer` (security headers)
|
||||
- Memory duplication (can't share pointers)
|
||||
- Complex serialization/deserialization
|
||||
- Separate WASM binary for worker
|
||||
|
||||
---
|
||||
|
||||
## 5. Severity Assessment
|
||||
|
||||
| Severity | Features | User Experience |
|
||||
|----------|----------|-----------------|
|
||||
| **Blocking** | None | KiCad will run |
|
||||
| **Degraded** | DRC, Zone Fill, 3D Raytrace | Slower, may need UI changes |
|
||||
| **Minor** | Library loading, connectivity | Slight delays |
|
||||
| **Unaffected** | Most editing, viewing | Normal operation |
|
||||
|
||||
### What Users Will Notice
|
||||
|
||||
1. **DRC takes 4-8x longer** - Was 5 seconds, now 20-40 seconds
|
||||
2. **Zone fills freeze UI** - Need progress indicator
|
||||
3. **3D raytracing unusable** - Should default to OpenGL mode
|
||||
4. **Startup slightly slower** - Library preloading blocks
|
||||
|
||||
### What Won't Change
|
||||
|
||||
- Schematic editing responsiveness
|
||||
- PCB editing (non-DRC operations)
|
||||
- File save/load (already uses Asyncify patterns)
|
||||
- Symbol/footprint placement
|
||||
- Most dialogs and UI interactions
|
||||
|
||||
---
|
||||
|
||||
## 6. Recommended Approach
|
||||
|
||||
### Phase 1: Stub Threading (Immediate)
|
||||
- Ensure `GetKiCadThreadPool()` returns a functional single-threaded shim
|
||||
- All `submit_loop()` calls execute sequentially
|
||||
- All `submit_task()` calls execute immediately
|
||||
- Remove mutex locks (single-threaded guarantee)
|
||||
|
||||
### Phase 2: Add Progress Reporting (Short-term)
|
||||
- DRC: Report progress every N items, add cancel button
|
||||
- Zone fill: Report per-zone progress
|
||||
- Library loading: Show loading indicator
|
||||
|
||||
### Phase 3: Time-Slicing (If Needed)
|
||||
- Only if Phase 2 results in unacceptable UI freezes
|
||||
- Prioritize DRC and zone filling
|
||||
- Use Asyncify + `emscripten_sleep(0)` pattern
|
||||
|
||||
### Phase 4: Web Workers (Future/Optional)
|
||||
- Only if WASM threading becomes critical
|
||||
- Would require significant architecture changes
|
||||
- Consider for heavy features like autorouter
|
||||
|
||||
---
|
||||
|
||||
## 7. Key Files to Modify
|
||||
|
||||
| File | Change Required |
|
||||
|------|-----------------|
|
||||
| `kicad/include/thread_pool.h` | Add WASM single-threaded shim |
|
||||
| `kicad/common/thread_pool.cpp` | Implement sequential fallback |
|
||||
| `kicad/pcbnew/drc/drc_engine.cpp` | Add progress reporting for WASM |
|
||||
| `kicad/pcbnew/zone_filler.cpp` | Add chunked processing option |
|
||||
| `kicad/3d-viewer/3d_rendering/raytracing/render_3d_raytrace_base.cpp` | Disable raytracing or make progressive |
|
||||
|
||||
---
|
||||
|
||||
## 8. Conclusion
|
||||
|
||||
**Is threading a blocker for WASM?** No.
|
||||
|
||||
**Will it impact performance?** Yes, significantly for DRC, zone filling, and 3D raytracing.
|
||||
|
||||
**Is it manageable?** Yes, with proper progress reporting and potentially disabling raytracing.
|
||||
|
||||
The wxWidgets WASM port already proves that threading can be stubbed out. KiCad will run slower for certain operations but remain fully functional. The recommended approach is to start simple (sequential with progress feedback) and only add complexity (time-slicing, Web Workers) if user experience demands it.
|
||||
Loading…
Reference in a new issue