-
Notifications
You must be signed in to change notification settings - Fork 0
UserBulbSandbox DevPlan
Companion pages: Technical Index · User Bulb 3D Dev Plan · Fractal Equation Design Guide · User-facing User Bulb Guide · Resources & Bibliography
Sandbox-Bulb DSL on the feature/gpu-compute branch. Stages 3A + 3B shipped 2026-06-10. Stage 3C (interpreter perf) remains, plus chain-mode GPU.
| Path | ms | Hits | Notes |
|---|---|---|---|
Roslyn z²+c
|
33–36 | 4799 | baseline |
Sandbox triplex(z,8)+c
|
34 | 4756 | analytic — at Roslyn parity |
Sandbox z^8+c
|
39 | 4756 | operator-form detect (#16) |
Sandbox explicit vec(...)+c
|
209–337 | 4799 | Square pattern detect (#9) |
Sandbox chain abs→triplex
|
96 | 4764 | analytic via chain detect (#21) |
Sandbox chain sin→triplex
|
None | — | Lipschitz guard rejects (#21) |
Sandbox-Quat qmul(z,z)+c
|
102–149 | 4800 | Quat support (#10) |
| Emitter→Roslyn parity | exact | 4756 | E2E test passes (#14) |
-
Compiler axis orthogonal to algebra axis.
UserBulbCompilerKind(Roslyn / Sandbox) ×UserBulbAxisModeKind(Vec3 / Quat). Both compilers cover both algebras. -
SbxVal3is a tagged union {Real | Vec | Quat} with a W field always present. Vec3 ops leave W=0. -
Adapter delegate. Both compilers expose
Func<Vec3,Vec3,int,double[],Vec3>(or Quat form). Raymarch is compiler-agnostic. -
Per-thread env scratch.
ThreadLocal<SbxVal3[]>sized byEnvSize. No allocation on hot path. -
AnalyticDE pattern detection on the AST, not source text. Source-text regex stays for Roslyn; Sandbox uses
DetectSandbox(Sbx3Node)/DetectSandboxChain(SandboxBulbChain).
- Parser + AST (
Sbx3Nodehierarchy) for vec/triplex/fold ops/let/ternary/comparisons. - Interpreter with per-thread env array.
- Chain support (
SandboxBulbChain) with shared scope across steps. - AnalyticDE pattern detection for
triplex(z, K) + c. - UI compiler toggle (Roslyn ↔ Sandbox).
- Self-tests for parity, chain, analytic.
Files: Models/SandboxBulbExpression.cs, Models/SandboxBulbChain.cs, Calculators/UserBulbAnalyticDE.cs, Calculators/UserBulbCalculator.cs.
| # | Item | Files |
|---|---|---|
| #9 | Square pattern detect on AST | UserBulbAnalyticDE.cs |
| #10 | Quat support — qmul, qconj, qpow, qvec, .w, Quat eval path |
SandboxBulbExpression.cs, Quat.cs, UserBulbCalculator.cs |
| #11 | User guide Sandbox chapter | UserBulb-Guide.md §19 |
| #12 | UI badge + tooltips + compiler-aware gating | UserBulbView.axaml, UserBulbViewModel.cs |
| #13 | ILGPU emitter foundation — Sbx3Node → C# walker with per-subtree kind inference |
UserBulbSandboxEmitter.cs |
| #14 | Emitter→Roslyn E2E parity test | UserBulbSelfTest.cs |
| #15 |
qpow emit — literal int unfolded, runtime Quat.Pow fallback |
UserBulbSandboxEmitter.cs, Quat.cs |
| #16 |
z^N + c operator-form AnalyticDE detect |
UserBulbAnalyticDE.cs |
| #17 | Inline let emit (no IIFE — no delegate dispatch) | UserBulbSandboxEmitter.cs |
| #18 | Reject sin/cos/... on Quat (interpreter + emitter) |
SandboxBulbExpression.cs, UserBulbSandboxEmitter.cs |
| #19 | Parser error spans → UI highlight with focus guard | SandboxBulbExpression.cs, UserBulbCalculator.cs, UserBulbViewModel.cs, UserBulbView.axaml.cs |
| #20 | Translator cache (ConcurrentDictionary, cap 32) |
UserBulbIlgpuTranslator.cs |
| #21 | Chain AnalyticDE detect — last-step pattern + Lipschitz-≤1 fold prefix | UserBulbAnalyticDE.cs, SandboxBulbChain.cs |
| #22 | Help anchor jump to Sandbox section when compiler=Sandbox | UserBulbViewModel.cs |
Stage 3A end-to-end runtime path: Sandbox DSL → AST → emitter (gpuTarget: true) → Roslyn in-memory asm → ILGPU LoadAutoGroupedStreamKernel. Wired into UserBulbCalculator.cs GPU gate ahead of the legacy UserBulbGpuCalculator.cs power-N path.
| File | Role |
|---|---|
| Models/Vec3GpuOps.cs | Device-safe mirrors of Vec3.Pow/Rot/BoxFold/SphereFold/Mod/Normalized using scalar if-clamps (no Math.Clamp Throw). |
| Calculators/UserBulbSandboxEmitter.cs | New Emit(..., gpuTarget: bool) overload routes Vec3.* → Vec3GpuOps.* and Math.Clamp → Vec3GpuOps.Clamp when true. (3B: Quat axis also supported — runtime qpow → QuatGpuOps.Pow.) |
| Calculators/UserBulbSandboxGpuCompiler.cs | Bridge. Parses, emits, wraps in kernel source (mirrors BulbKernel shape), Roslyn-compiles, JITs via ILGPU. Caches kernel by (source, paramNames, axisMode). fp64 fallback: catches CapabilityNotSupportedException and re-JITs on CPU accelerator. |
| Calculators/UserBulbCalculator.cs | GPU gate now routes to UserBulbSandboxGpuCompiler when Compiler=Sandbox and chain-less; falls through to UserBulbGpuCalculator on any failure. |
Smoke (dotnet run -- --ubspike): T1/T2/T3/T4 all pass on this box (Intel UHD OpenCL → CPU-accel fp64 fallback). T5 added in 3B (see below).
| Tier | What | Result |
|---|---|---|
| T1 | Minimal kernel in byte[]-loaded asm, no external types | SUCCESS |
| T2 | Kernel + cross-asm Vec3 reference |
SUCCESS |
| T3 |
triplex(z, 8) + c emitter body in DE-loop kernel + spike-inlined TriplexPowSafe
|
SUCCESS (parity vs CPU Vec3.Pow matches) |
| T4 | Full UserBulbSandboxGpuCompiler.TryCompile + Render on triplex(z, 8) + c
|
SUCCESS (hit=326, bg=698 on 32×32 with the fp64 fallback) |
Limitations carried forward (after 3B):
- Chain mode (multi-step DSL) not yet GPU-compiled — chain path stays CPU.
- Quat-mode Julia + numerical-Jacobian DE not yet on GPU — current quat GPU path is analytic-DE only (matches the CPU
qpow(z, K) + cshape). Julia + 5-trajectory DE stays CPU. - fp64 fallback to CPU accelerator works but is slower than CUDA/OpenCL; on fp64-capable devices the preferred accelerator is used directly.
Extends 3A's vec-mode kernel with a Quat-mode variant. Emitter routes Quat constants, Hamilton *, .Conjugate, .Length, and qpow through device-safe paths. QuatGpuOps.Pow is the throw-free mirror of Quat.Pow (runtime exponent — literal int still inlines to chained * from Stage 2). Kernel branches on quatMode in BuildKernelSource: Step takes Quat z/c, SandboxDE projects via GpuRenderParams.QuatSliceW.
| File | Role |
|---|---|
| Models/QuatGpuOps.cs |
Pow(Quat, double) mirror — rounds + clamps to [0, MaxIter], loops Hamilton-multiply. No throw on non-integer/negative/non-finite. |
| Calculators/UserBulbSandboxEmitter.cs | Drops gpuTarget && quatMode early-return. Runtime-exponent qpow routes to QuatGpuOps.Pow on GPU. |
| Calculators/UserBulbSandboxGpuCompiler.cs |
BuildKernelSource(stepBody, paramNames, quatMode). Quat branch builds Quat Step(Quat z, Quat c, …) + Quat-typed analytic DE loop using p.QuatSliceW as c.W. |
| Calculators/UserBulbGpuCalculator.cs |
GpuRenderParams.QuatSliceW added. |
| Calculators/UserBulbCalculator.cs | GPU gate drops !quatMode guard at top, gates only the legacy Roslyn-translator branch on !quatMode. Populates gp.QuatSliceW. |
T5 smoke: qpow(z, 2) + c in Quat mode, 32×32, DEIter=12 → hit=284, bg=740 on the fp64-fallback CPU accelerator.
Three tiered probes run via dotnet run -- --ubspike (UserBulbSandboxGpuSpike.cs) against the CPU accelerator (the OpenCL/Intel UHD device on this box has no fp64).
| Tier | What | Result |
|---|---|---|
| T1 | Minimal kernel in byte[]-loaded asm, no external types | SUCCESS |
| T2 | Kernel + cross-asm Vec3 reference |
SUCCESS |
| T3 |
triplex(z, 8) + c emitter body wrapped in a DE-loop kernel + device-safe TriplexPow
|
SUCCESS (1024/1024 finite, 719 inSet / 305 outSet, center pixel parity vs CPU Vec3.Pow = 0/0) |
Confirmed. Runtime Assembly.Load(byte[]) outputs are JIT-acceptable to ILGPU. Cross-asm type refs resolve. No need for on-disk staging or for sinking Vec3 into the runtime asm.
Blockers found and fixed in-spike.
-
Vec3.PowcallsMath.Clamp, which lowers to aThrowopcode ILGPU rejects (Not supported IL instruction of type 'Throw'). Workaround in spike: hand-rolledTriplexPowSafethat clamps via two scalar compares. Real fix: addModels/Vec3GpuOps.cswith device-safe mirrors ofVec3.Pow,Vec3.Rot,Vec3.SphereFold,Vec3.BoxFold, etc., and route the emitter to call those when targeting GPU. - Intel UHD OpenCL has no fp64. Existing
UserBulbGpuCalculatorshares this risk; out of scope for 3A. Pivot if it bites: float32 kernel variant.
CPU Sandbox interpreter is currently ~10–15× slower than Roslyn for non-analytic sources. Options:
- Opcode-flat dispatch table (replace virtual
Evaldispatch with switch on a packed opcode array). - Struct-based ops (avoid
Sbx3Binary/Sbx3Callheap allocation cost on tight loops — already small butEvalis virtual). - IL emit (compile AST to
DynamicMethod).
Lower priority once 3A lands — GPU wins dominate for big renders.
- Removing the Roslyn compile path.
- DSL extensions beyond the existing function table.
- Cross-compiling the DSL to WebGL / CUDA-direct / SPIR-V (ILGPU is the only backend).
- Standalone
Docs/UserBulbSandbox-DSL-Reference.md(section 19 of UserBulb-Guide.md covers the grammar fully — split only if the section grows).
- Build clean.
-
dotnet run -- --ubtestself-test passes, all existing scenarios green. - New scenario added under UserBulbSelfTest.cs for the new path.
- Manual smoke render in Avalonia shell (
dotnet run) — confirm no visual regression on a saved preset. - Performance: no item slows
triplex(z, 8) + cSandbox path below the 34 ms baseline.