Files
Asuro/doc/planning/webgpu.md
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

12 KiB

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:

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:

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:

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:

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:

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:

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:

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