Files
t3hn3rd 63f6c5ecc1
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
feature: add RFC 8259 JSON parser/serializer and string utility extensions
Add core.fmt.json — a complete RFC 8259 JSON library with recursive-descent
parser, serializer, builders, dot/bracket path accessors, and full cleanup.
Supports all 6 JSON value types plus JSON_ERROR for parse error reporting.
Numbers stored as Double via x87 FPU. Unicode \uXXXX escapes handled
(ASCII range emitted, non-ASCII replaced with '?').

Supporting changes:
- core.strings: add int64ToString, doubleToStr (scientific notation for
  very large/small values, fixed-point for normal range, trailing zero
  trimming), plus unit tests for both
- asuro.pas: wire core.fmt.json.UnitTest into the boot test suite
- mkdocs.yml: add JSON nav entry under Formats
- doc/src/core/fmt/core.fmt.json.md: full API documentation
- doc/src/core/core.strings.md: document int64ToString and doubleToStr
- doc/planning/json.md: implementation plan and design spec
- doc/planning/webgpu.md: WebGPU-over-WASM design plan (software
  rasterizer backend for WASM apps importing webgpu.h)
2026-03-10 11:51:48 +00:00

334 lines
12 KiB
Markdown

# WebGPU Support for WASM Applications
## Overview
Asuro provides WebGPU API compatibility for WASM applications. Apps compiled
against the standard `webgpu.h` C API can create GPU resources, build render
pipelines, and issue draw calls. The host implements these as WASM host
functions backed by a software rasterizer.
## Architecture
```
WASM App (imports webgpu.h functions)
│
▼
WASURO Host Functions (module: "webgpu")
│ Pop params from operand stack
│ Read structs from WASM linear memory
│ Dispatch to backend
▼
AsuroGPU Software Backend (Pascal)
│ Handle table (devices, buffers, textures, pipelines)
│ Command encoder → command buffer recording
│ Software triangle rasterizer
▼
Host Framebuffer (PRGB32)
│
▼
LVGL lv_image widget in Window Manager
│
▼
Double-buffered display (SSE memcpy)
```
WASM applications import `webgpu.h` functions. WASURO resolves these to
Pascal host functions registered under the `"webgpu"` module (and `"env"`
for Emscripten compatibility). Each function pops parameters from the WASM
operand stack, reads descriptor structs from WASM linear memory, and
dispatches to the AsuroGPU software backend.
All WebGPU objects are opaque handles managed in a per-process handle table.
Surfaces bind to LVGL windows via `lv_image` widgets displaying host-side
BGRA framebuffers.
## Handle System
All WebGPU objects are opaque integer handles. Each WASM process gets its
own handle table stored on `TProcessWASMShim.GpuState`:
```pascal
TGPUHandleKind = (hkNone, hkInstance, hkAdapter, hkDevice, hkQueue,
hkSurface, hkBuffer, hkTexture, hkTextureView,
hkSampler, hkShaderModule, hkBindGroupLayout,
hkBindGroup, hkPipelineLayout, hkRenderPipeline,
hkCommandEncoder, hkRenderPassEncoder, hkCommandBuffer);
TGPUHandle = record
Kind : TGPUHandleKind;
Ptr : pointer;
InUse : boolean;
end;
TGPUHandleTable = record
Handles : array[0..255] of TGPUHandle;
Count : uint32;
end;
```
## Rendering Pipeline
The software rasterizer executes recorded command buffers:
1. Vertex fetch from host-side GPU buffers
2. Vertex transform (4x4 matrix from uniform buffer)
3. Perspective divide and viewport transform
4. Triangle assembly per primitive topology
5. Backface culling
6. Scanline rasterization with edge walking
7. Texture sampling (nearest-neighbor) or vertex color interpolation
8. Depth test (16-bit Z-buffer)
9. Alpha/additive blending
10. Pixel write to surface framebuffer (BGRA)
## Shader Support
Initially, shaders are stored but not interpreted. The rasterizer uses a
fixed-function pipeline: vertex transform via MVP matrix (bind group 0,
binding 0), fragment color from texture sample or vertex color.
A future phase adds a WGSL subset interpreter for basic vertex/fragment
shaders.
## Implementation Phases
### Phase 1: Foundation
Handle system, instance/adapter/device creation, surface/window binding.
**WebGPU functions:**
- `wgpuCreateInstance` — Allocate instance record, return handle
- `wgpuInstanceRequestAdapter` — Allocate adapter, invoke callback synchronously
- `wgpuAdapterRequestDevice` — Allocate device + queue, invoke callback
- `wgpuDeviceGetQueue` — Return queue handle from device record
- `wgpuInstanceCreateSurface` — Create window via window manager, create LVGL
`lv_image` widget, allocate host framebuffer, return surface handle
- `wgpuSurfaceConfigure` — Read configuration from WASM memory, resize FB
- `wgpuSurfaceGetCurrentTexture` — Write surface texture handle to WASM memory
- `wgpuSurfacePresent` — Copy host FB to LVGL image, invalidate widget
- `wgpuTextureCreateView` — Return texture view handle
- `wgpu*Release` — Free resource, mark handle slot unused
**Surface record:**
```pascal
TGPUSurface = record
WinID : uint32; // Window manager slot
FBWidth : uint32;
FBHeight : uint32;
HostFB : PRGB32; // Host-side BGRA pixel buffer
DepthBuf : puint16; // 16-bit Z-buffer
ImgWidget : Plv_obj; // LVGL image widget
ImgDsc : PLVImageDsc; // lv_image_dsc_t (reuse boot.splash.pas pattern)
SurfaceTex : uint32; // Handle to surface texture
end;
```
### Phase 2: Buffers, Textures, Shader Modules
Create GPU resources that feed into the render pipeline.
**WebGPU functions:**
- `wgpuDeviceCreateBuffer` — Read descriptor, kalloc host buffer
- `wgpuQueueWriteBuffer` — Copy from WASM linear memory to host buffer
- `wgpuBufferGetMappedRange` — Return WASM-accessible pointer
- `wgpuBufferUnmap` — Sync mapped data back to host
- `wgpuDeviceCreateTexture` — Allocate host-side pixel buffer
- `wgpuQueueWriteTexture` — Copy pixel data from WASM to host texture
- `wgpuDeviceCreateSampler` — Store sampling parameters
- `wgpuDeviceCreateShaderModule` — Store WGSL source as opaque blob
**Buffer record:**
```pascal
TGPUBuffer = record
Data : pointer; // kalloc'd host buffer
Size : uint32;
Usage : uint32; // WGPUBufferUsage flags
Mapped : boolean;
MapPtr : uint32; // WASM-side address if mapped
end;
```
**Texture record:**
```pascal
TGPUTexture = record
Pixels : pointer; // kalloc'd pixel data
Width : uint32;
Height : uint32;
Format : uint32; // WGPUTextureFormat enum
BPP : uint32; // Bytes per pixel
Usage : uint32;
end;
```
### Phase 3: Render Pipeline & Bind Groups
Create render pipelines and bind resource groups.
**WebGPU functions:**
- `wgpuDeviceCreateBindGroupLayout` — Parse entry descriptors
- `wgpuDeviceCreateBindGroup` — Bind concrete resources to layout slots
- `wgpuDeviceCreatePipelineLayout` — Group bind group layouts
- `wgpuDeviceCreateRenderPipeline` — Parse `WGPURenderPipelineDescriptor`
from WASM memory: vertex layout, primitive topology, depth/stencil state,
blend state, multisample
**Pipeline record:**
```pascal
TGPURenderPipeline = record
VertexShader : uint32;
FragmentShader : uint32;
VertexLayout : TVertexLayout;
Topology : uint32; // triangle-list, triangle-strip, etc.
CullMode : uint32; // none, front, back
FrontFace : uint32; // ccw, cw
DepthTest : boolean;
DepthWrite : boolean;
DepthCompare : uint32;
BlendEnabled : boolean;
BlendSrc : uint32;
BlendDst : uint32;
PipelineLayout : uint32;
end;
TVertexAttribute = record
Format : uint32; // float32x2, float32x3, float32x4, etc.
Offset : uint32;
ShaderLoc : uint32;
end;
TVertexLayout = record
Stride : uint32;
StepMode : uint32; // vertex, instance
AttrCount : uint32;
Attrs : array[0..15] of TVertexAttribute;
end;
```
**Parsing WASM structs:** WebGPU descriptors are deeply nested C structs.
The host reads them field-by-field from WASM linear memory using
`read_uint32`/`read_uint64`. Pointer fields are WASM addresses that must
be dereferenced via `wasm.types.heap`. Each struct must be documented with
exact field offsets for the 32-bit ABI.
### Phase 4: Command Encoding & Draw Calls
Record and execute render commands.
**WebGPU functions:**
- `wgpuDeviceCreateCommandEncoder` — Allocate command list
- `wgpuCommandEncoderBeginRenderPass` — Read render pass descriptor, begin
recording
- `wgpuRenderPassEncoderSetPipeline` — Record pipeline bind
- `wgpuRenderPassEncoderSetVertexBuffer` — Record VB bind
- `wgpuRenderPassEncoderSetIndexBuffer` — Record IB bind
- `wgpuRenderPassEncoderSetBindGroup` — Record bind group
- `wgpuRenderPassEncoderDraw` — Record draw command
- `wgpuRenderPassEncoderDrawIndexed` — Record indexed draw
- `wgpuRenderPassEncoderSetViewport` — Record viewport
- `wgpuRenderPassEncoderSetScissorRect` — Record scissor
- `wgpuRenderPassEncoderEnd` — Finish recording
- `wgpuCommandEncoderFinish` — Produce command buffer
- `wgpuQueueSubmit` — Execute all recorded commands via software rasterizer
**Command buffer:**
```pascal
TGPUCommandKind = (cmdSetPipeline, cmdSetVertexBuffer, cmdSetIndexBuffer,
cmdSetBindGroup, cmdDraw, cmdDrawIndexed,
cmdSetViewport, cmdSetScissor, cmdClearColor,
cmdClearDepth);
TGPUCommand = record
Kind : TGPUCommandKind;
// Variant fields per command kind
end;
TGPUCommandBuffer = record
Commands : array[0..1023] of TGPUCommand;
Count : uint32;
end;
```
### Phase 5: Software Rasterizer
Execute draw commands by software-rendering triangles to the surface
framebuffer.
**Rasterizer stages:**
1. **Vertex fetch** — Read raw bytes from host buffer per vertex attribute
format (float32x2, float32x3, float32x4, unorm8x4)
2. **Vertex transform** — Multiply by 4x4 MVP matrix from uniform buffer
(bind group 0, binding 0). Identity if no bind group bound.
3. **Viewport transform** — NDC to screen coordinates
4. **Triangle assembly** — Group vertices per primitive topology
5. **Backface culling** — Per pipeline cull mode setting
6. **Scanline rasterization** — Sort vertices by Y, split into flat-top /
flat-bottom, walk scanlines interpolating X, depth, UV, color
7. **Texture sampling** — Nearest-neighbor (bilinear as stretch goal)
8. **Depth test** — 16-bit Z-buffer per pipeline depth settings
9. **Blending** — Alpha/additive per pipeline blend state
10. **Pixel write** — Store BGRA to surface HostFB
**Performance target:** ~1000 triangles at 160x120 at ~10-15 FPS. All math
runs as native Pascal, not interpreted WASM.
### Phase 6 (Stretch): WGSL Shader Interpreter
Parse and interpret a common WGSL subset instead of fixed-function fallback.
**Supported subset:**
- Vertex shaders: matrix multiply, passthrough varyings
- Fragment shaders: solid color, texture sample, vertex color passthrough
- Types: `vec2f`, `vec3f`, `vec4f`, `mat4x4f`, `f32`, `u32`
- Built-ins: `textureSample`, `normalize`, `dot`, `cross`, `clamp`
- Decorators: `@vertex`, `@fragment`, `@binding`, `@group`, `@location`,
`@builtin`
**Approach:** Tokenize → simple AST → interpret per-vertex/per-fragment
during rasterization.
## Source Layout
All GPU-related code lives under `src/app/wasm/gpu/`:
| File | Phase | Purpose |
|------|-------|---------|
| `app.wasm.gpu.pas` | 1 | Main unit, handle table, init/cleanup |
| `app.wasm.gpu.types.pas` | 1 | Records, enums, constants matching webgpu.h |
| `app.wasm.gpu.instance.pas` | 1 | Instance/adapter/device host functions |
| `app.wasm.gpu.surface.pas` | 1 | Surface/window/present host functions |
| `app.wasm.gpu.buffer.pas` | 2 | Buffer creation, mapping, writes |
| `app.wasm.gpu.texture.pas` | 2 | Texture/sampler/view creation |
| `app.wasm.gpu.shader.pas` | 2 | Shader module storage (stub parser) |
| `app.wasm.gpu.pipeline.pas` | 3 | Pipeline, bind group, layout creation |
| `app.wasm.gpu.structs.pas` | 3 | WASM struct readers (descriptor parsing) |
| `app.wasm.gpu.encoder.pas` | 4 | Command encoder, render pass encoder |
| `app.wasm.gpu.submit.pas` | 4 | Command dispatch to rasterizer |
| `app.wasm.gpu.raster.pas` | 5 | Scanline triangle rasterizer |
| `app.wasm.gpu.math.pas` | 5 | Vec3/Vec4/Mat4 math utilities |
| `shader/app.wasm.gpu.shader.lexer.pas` | 6 | WGSL tokenizer |
| `shader/app.wasm.gpu.shader.parser.pas` | 6 | WGSL AST parser |
| `shader/app.wasm.gpu.shader.interp.pas` | 6 | WGSL per-vertex/fragment interpreter |
Modified existing files:
- `src/app/wasm/app.wasm.shim.pas` — Add `GpuState` field
- `src/app/wasm/app.wasm.runner.pas` — Register `webgpu` module host functions
- `src/app/wasm/app.wasm.cleanup.pas` — Free GPU resources on process exit
## WASM Module Naming
Host functions are registered under both `"webgpu"` and `"env"` modules for
Emscripten compatibility:
```pascal
wasm_register_host_func(ctx, 'webgpu', 'wgpuCreateInstance', @gpu_create_instance);
wasm_register_host_func(ctx, 'env', 'wgpuCreateInstance', @gpu_create_instance);
```
## Reused Patterns
- `boot.splash.pas` `buildImageDsc` — TLVImageDsc for raw pixel → lv_image
- `app.wasm.io.pas` ActiveShimPtr — safe host function context access
- `app.wasm.io.pas` `asuro_wasi_args_get` — TWASMHostFunc stack pop/push
- `driver.video.windows.pas` `createWindow` / `setWindowOwner` — window lifecycle
- `wasm.types.heap` `read_uint32` / `write_uint32` — page-aware memory access