Vector Engine

WebAssembly i workery

Build C++23 do WASM, publiczne API instancji, Dedicated Worker z zero-copy transferami i anulowaniem przez AbortSignal.

Build#

Kompilacja natywnego rdzenia C++23 do zoptymalizowanego -O3 WASM:

bash
npm run wasm:build
npm run verify:wasm

scripts/build-wasm.mjs kompiluje wszystkie jednostki translacji z flagami:

text
-std=c++23 -O3 -ffunction-sections -fdata-sections
-Wl,--gc-sections -Wl,--strip-all

Target: wasm32-wasi reactor mode. Artefakty:

  • apps/web/public/wasm/vector_engine.wasm
  • apps/web/public/wasm/vector_engine.js (loader zgodny z CSP)

Publiczne API instancji#

FunkcjaCo robi
validateJson(source)Walidacja schemy, unikalnych ID, gradientów, warstw, workspace'ów i limitów
measureBounds(source, optionsJson)Dokładne analityczne bounding boxy przez złożone transformacje i stroke
renderScopedSvg(source, optionsJson)Ekstrakcja scope, viewport/margin/clip/fit, zwraca SVG + metadane
exportScopedMeta(source, optionsJson) + takeLastBytes()Rasteryzacja i enkodowanie; bajty kopiowane raz przez pamięć liniową
renderSvg(...), exportBinary(...)Legacy convenience entrypoints

Dedicated Worker#

WorkerBackend uruchamia exportWorker.ts poza głównym wątkiem UI:

text
UI thread                    Worker thread
    │  export(source, opts)      │
    │ ─────────────────────────▶ │  WASM render + encode
    │                            │
    │  Transferable ArrayBuffer  │
    │ ◀───────────────────────── │  zero-copy transfer
    │                            │
    │  abort()                   │
    │ ─────────────────────────▶ │  terminate() natychmiast
    │  nowy worker przy next op  │
  • Zakodowane bufory wracają przez zero-copy Transferable ArrayBuffer — bez kopiowania megabajtów PNG między wątkami.
  • AbortSignal.abort() terminuje workera natychmiast, bez blokowania UI.
  • Następna operacja transparentnie inicjuje czystego workera.

Przykład: adapter#

ts
import { WorkerBackend } from '@/engine/workerBackend'

const backend = new WorkerBackend()

const validation = await backend.validateJson(source)
if (!validation.valid) throw new Error(validation.errors[0].message)

const meta = await backend.exportScopedMeta(source, JSON.stringify({
  format: 'png',
  width: 2400,
  height: 1600,
  background: 'transparent'
}))

const bytes = backend.takeLastBytes()
const blob = new Blob([bytes], { type: 'image/png' })

Anulowanie w locie#

ts
const controller = new AbortController()

const promise = backend.exportScopedMeta(source, options, controller.signal)

setTimeout(() => controller.abort(), 5000)

try {
  await promise
} catch (e) {
  if (e.name === 'AbortError') {
    status.value = 'Eksport anulowany'
  }
}

Fallback przeglądarkowy#

Gdy artefaktów WASM nie ma (świeży checkout bez builda), development używa BrowserBackend — implementacji zgodnej API w TypeScripcie. UI działa, ale produkcyjnie zawsze weryfikuj rdzeń:

bash
npm run verify:wasm

Weryfikacja sprawdza fabrykę JavaScript i magic header WebAssembly przed buildem Vite.

Dostarczanie WASM#

Deployment serwuje:

text
/wasm/vector_engine.js
/wasm/vector_engine.wasm

z Content-Type: application/wasm, same-origin resource policy i revalidation caching dla stabilnych nazw plików. Szczegóły: Wdrożenie na Vercel.

Jeden rdzeń, trzy wyjścia

Ta sama implementacja C++23 zasila natywną bibliotekę, CLI i WASM. Nie ma osobnego „renderera przeglądarkowego” do utrzymania — fallback dev to jedyny wyjątek i jest jawnie oznaczony.