From 197a03c24baba3acc35327e0e126ec49754f9945 Mon Sep 17 00:00:00 2001 From: skal Date: Sat, 14 Feb 2026 12:52:42 +0100 Subject: Docs: WGPU helper functions reference Comprehensive documentation for project-specific WGPU helpers (not wgpu-native wrappers). **Covers:** - Buffer management (gpu_create_buffer) - Texture creation (gpu_create_texture_2d, storage textures, mip views) - Pipeline helpers (compute/render pass creation) - Cross-platform utilities (gpu_init_color_attachment) - Test helpers (init_wgpu_with_surface) **Usage patterns:** - Effect uniforms setup - Compute shader dispatch - Multi-pass rendering Co-Authored-By: Claude Sonnet 4.5 --- doc/WGPU_HELPERS.md | 408 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 408 insertions(+) create mode 100644 doc/WGPU_HELPERS.md diff --git a/doc/WGPU_HELPERS.md b/doc/WGPU_HELPERS.md new file mode 100644 index 0000000..5642eef --- /dev/null +++ b/doc/WGPU_HELPERS.md @@ -0,0 +1,408 @@ +# WebGPU Helper Functions + +Project-specific WGPU helper functions (not wgpu-native wrapper calls). + +**Scope:** High-level convenience functions built on top of wgpu-native API. + +--- + +## Core Helpers (`src/gpu/gpu.h`) + +### Buffer Management + +#### `gpu_create_buffer` +```cpp +GpuBuffer gpu_create_buffer(WGPUDevice device, size_t size, + uint32_t usage, const void* data = nullptr); +``` + +**Purpose:** Create and optionally initialize a WGPU buffer. + +**Parameters:** +- `device` - WGPU device handle +- `size` - Buffer size in bytes +- `usage` - Usage flags (e.g., `WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst`) +- `data` - Optional initial data (if `nullptr`, buffer is uninitialized) + +**Returns:** `GpuBuffer` struct containing: +- `buffer` - WGPU buffer handle +- `size` - Buffer size (for validation/debugging) + +**Common Usage Flags:** +- `WGPUBufferUsage_Uniform` - Uniform buffer (read-only in shaders) +- `WGPUBufferUsage_Storage` - Storage buffer (read/write in compute shaders) +- `WGPUBufferUsage_Vertex` - Vertex buffer +- `WGPUBufferUsage_Index` - Index buffer +- `WGPUBufferUsage_CopyDst` - Can be written to via queue +- `WGPUBufferUsage_CopySrc` - Can be read from (for readback) + +**Example:** +```cpp +// Create uniform buffer with initial data +CommonPostProcessUniforms uniforms = {...}; +GpuBuffer uniform_buf = gpu_create_buffer( + device, sizeof(uniforms), + WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst, + &uniforms); + +// Create empty storage buffer +GpuBuffer storage_buf = gpu_create_buffer( + device, 1024 * sizeof(float), + WGPUBufferUsage_Storage | WGPUBufferUsage_CopyDst); +``` + +**Implementation:** `src/gpu/gpu.cc` + +--- + +### Texture Management + +#### `gpu_create_texture_2d` +```cpp +TextureWithView gpu_create_texture_2d( + WGPUDevice device, uint32_t width, uint32_t height, + WGPUTextureFormat format, WGPUTextureUsage usage, + uint32_t mip_levels = 1); +``` + +**Purpose:** Create 2D texture with view. + +**Parameters:** +- `device` - WGPU device handle +- `width`, `height` - Texture dimensions +- `format` - Pixel format (e.g., `WGPUTextureFormat_RGBA8Unorm`) +- `usage` - Usage flags (see below) +- `mip_levels` - Mipmap count (default: 1) + +**Returns:** `TextureWithView` struct: +- `texture` - WGPU texture handle +- `view` - Default texture view (all mip levels) + +**Common Usage Flags:** +- `WGPUTextureUsage_TextureBinding` - Readable in shaders +- `WGPUTextureUsage_RenderAttachment` - Render target +- `WGPUTextureUsage_CopyDst` - Can be written via queue +- `WGPUTextureUsage_CopySrc` - Can be read (for download) + +**Example:** +```cpp +// Create render target texture +TextureWithView target = gpu_create_texture_2d( + device, 1920, 1080, + WGPUTextureFormat_RGBA8Unorm, + WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_TextureBinding); + +// Create mipmapped texture +TextureWithView mipmapped = gpu_create_texture_2d( + device, 256, 256, + WGPUTextureFormat_RGBA8Unorm, + WGPUTextureUsage_TextureBinding | WGPUTextureUsage_RenderAttachment, + 4); // 256→128→64→32 mip chain +``` + +--- + +#### `gpu_create_storage_texture_2d` +```cpp +TextureWithView gpu_create_storage_texture_2d( + WGPUDevice device, uint32_t width, uint32_t height, + WGPUTextureFormat format); +``` + +**Purpose:** Create read/write storage texture (for compute shaders). + +**Parameters:** +- `device` - WGPU device handle +- `width`, `height` - Texture dimensions +- `format` - Pixel format (must support storage usage) + +**Returns:** `TextureWithView` with storage usage flags. + +**Valid Formats:** +- `WGPUTextureFormat_RGBA8Unorm` +- `WGPUTextureFormat_RGBA16Float` +- `WGPUTextureFormat_RGBA32Float` +- `WGPUTextureFormat_R32Float`, `WGPUTextureFormat_RG32Float` + +**Example:** +```cpp +// Compute shader output texture +TextureWithView output = gpu_create_storage_texture_2d( + device, 512, 512, WGPUTextureFormat_RGBA8Unorm); +``` + +--- + +#### `gpu_create_mip_view` +```cpp +WGPUTextureView gpu_create_mip_view(WGPUTexture texture, + WGPUTextureFormat format, + uint32_t mip_level); +``` + +**Purpose:** Create view for specific mipmap level (for rendering to individual mips). + +**Example:** +```cpp +// Render to mip level 2 (quarter resolution) +WGPUTextureView mip2_view = gpu_create_mip_view( + texture, WGPUTextureFormat_RGBA8Unorm, 2); +``` + +--- + +### Pipeline Helpers + +#### `gpu_create_compute_pass` +```cpp +ComputePass gpu_create_compute_pass( + WGPUDevice device, const char* shader_code, + ResourceBinding* bindings, int num_bindings); +``` + +**Purpose:** Create compute pipeline + bind group from WGSL shader. + +**Parameters:** +- `device` - WGPU device handle +- `shader_code` - WGSL compute shader source +- `bindings` - Array of resource bindings (buffers/textures) +- `num_bindings` - Binding count + +**Returns:** `ComputePass` struct: +- `pipeline` - Compute pipeline +- `bind_group` - Bind group (pre-bound resources) +- `workgroup_size_{x,y,z}` - Workgroup dimensions + +**ResourceBinding Structure:** +```cpp +struct ResourceBinding { + GpuBuffer buffer; + WGPUBufferBindingType type; // Uniform, Storage, etc. +}; +``` + +**Example:** +```cpp +// Setup bindings +ResourceBinding bindings[2] = { + {uniform_buf, WGPUBufferBindingType_Uniform}, + {storage_buf, WGPUBufferBindingType_Storage} +}; + +// Create compute pass +ComputePass pass = gpu_create_compute_pass( + device, shader_wgsl, bindings, 2); + +// Dispatch +WGPUComputePassEncoder encoder = ...; +wgpuComputePassEncoderSetPipeline(encoder, pass.pipeline); +wgpuComputePassEncoderSetBindGroup(encoder, 0, pass.bind_group, 0, nullptr); +wgpuComputePassEncoderDispatchWorkgroups(encoder, + workgroups_x, workgroups_y, 1); +``` + +--- + +#### `gpu_create_render_pass` +```cpp +RenderPass gpu_create_render_pass( + WGPUDevice device, WGPUTextureFormat format, + const char* shader_code, + ResourceBinding* bindings, int num_bindings); +``` + +**Purpose:** Create render pipeline + bind group from WGSL shader. + +**Parameters:** +- `device` - WGPU device handle +- `format` - Target texture format (for pipeline compatibility) +- `shader_code` - WGSL shader with vertex + fragment stages +- `bindings` - Resource bindings array +- `num_bindings` - Binding count + +**Returns:** `RenderPass` struct: +- `pipeline` - Render pipeline +- `bind_group` - Pre-bound resources +- `vertex_count`, `instance_count` - Draw parameters + +**Example:** +```cpp +RenderPass pass = gpu_create_render_pass( + device, WGPUTextureFormat_BGRA8Unorm, + fullscreen_quad_wgsl, bindings, 1); + +WGPURenderPassEncoder encoder = ...; +wgpuRenderPassEncoderSetPipeline(encoder, pass.pipeline); +wgpuRenderPassEncoderSetBindGroup(encoder, 0, pass.bind_group, 0, nullptr); +wgpuRenderPassEncoderDraw(encoder, pass.vertex_count, pass.instance_count, 0, 0); +``` + +--- + +### Render Pass Setup + +#### `gpu_init_color_attachment` +```cpp +inline void gpu_init_color_attachment( + WGPURenderPassColorAttachment& attachment, + WGPUTextureView view); +``` + +**Purpose:** Initialize color attachment with cross-platform defaults. + +**Sets:** +- `view` - Target texture view +- `loadOp` = `Clear` (clear to black) +- `storeOp` = `Store` (preserve results) +- `clearValue` = `{0, 0, 0, 1}` (opaque black) +- `depthSlice` = `UNDEFINED` (native only, handles Win32 compat) + +**Example:** +```cpp +WGPURenderPassColorAttachment color_attach = {}; +gpu_init_color_attachment(color_attach, target_view); + +WGPURenderPassDescriptor pass_desc = {}; +pass_desc.colorAttachmentCount = 1; +pass_desc.colorAttachments = &color_attach; +``` + +**Cross-Platform Notes:** +- Handles `depthSlice` field differences between native/Win32 builds +- Always clears to black; modify `clearValue` if different color needed + +--- + +## Test Helpers (`src/tests/common/test_3d_helpers.h`) + +### `init_wgpu_with_surface` +```cpp +WgpuSurfaceContext init_wgpu_with_surface(PlatformState* platform_state); +``` + +**Purpose:** Initialize WGPU device/queue/surface for visual tests (test_3d_render, test_3d_physics, test_mesh). + +**Parameters:** +- `platform_state` - Platform window state (from `platform_init`) + +**Returns:** `WgpuSurfaceContext` struct: +- `device` - WGPU device +- `queue` - Command queue +- `surface` - Window surface (pre-configured) +- `adapter` - WGPU adapter +- `format` - Surface texture format + +**Example:** +```cpp +PlatformState platform_state = platform_init(false, 1280, 720); +WgpuSurfaceContext ctx = init_wgpu_with_surface(&platform_state); + +g_device = ctx.device; +g_queue = ctx.queue; +g_surface = ctx.surface; +g_format = ctx.format; +``` + +**Implementation Details:** +- Handles cross-platform adapter/device request callbacks (Win32 vs native) +- Configures surface for `WGPUPresentMode_Fifo` (vsync) +- Chooses high-performance adapter +- Blocks until adapter/device ready + +**Usage:** Test executables only (not production code). + +--- + +## Usage Patterns + +### Creating Effect Uniforms +```cpp +// 1. Define uniform struct (must match WGSL) +struct MyEffectUniforms { + float time; + float param; + float _pad[2]; +}; +static_assert(sizeof(MyEffectUniforms) == 16); + +// 2. Create buffer +MyEffectUniforms data = {time, 1.0f}; +uniform_buf_ = gpu_create_buffer( + device, sizeof(data), + WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst, + &data); + +// 3. Update each frame +wgpuQueueWriteBuffer(queue, uniform_buf_.buffer, 0, &data, sizeof(data)); +``` + +### Compute Shader Dispatch +```cpp +// 1. Create storage buffers +GpuBuffer input = gpu_create_buffer(..., WGPUBufferUsage_Storage, input_data); +GpuBuffer output = gpu_create_buffer(..., WGPUBufferUsage_Storage); + +// 2. Setup bindings +ResourceBinding bindings[] = { + {input, WGPUBufferBindingType_ReadOnlyStorage}, + {output, WGPUBufferBindingType_Storage} +}; + +// 3. Create compute pass +ComputePass pass = gpu_create_compute_pass(device, shader, bindings, 2); + +// 4. Dispatch +WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(device, nullptr); +WGPUComputePassEncoder compute = wgpuCommandEncoderBeginComputePass(encoder, nullptr); +wgpuComputePassEncoderSetPipeline(compute, pass.pipeline); +wgpuComputePassEncoderSetBindGroup(compute, 0, pass.bind_group, 0, nullptr); +wgpuComputePassEncoderDispatchWorkgroups(compute, num_workgroups, 1, 1); +wgpuComputePassEncoderEnd(compute); +``` + +### Multi-Pass Rendering +```cpp +// Pass 1: Render to texture +TextureWithView intermediate = gpu_create_texture_2d(...); +WGPURenderPassColorAttachment attach1 = {}; +gpu_init_color_attachment(attach1, intermediate.view); +// ... render pass 1 ... + +// Pass 2: Post-process +RenderPass post_process = gpu_create_render_pass(...); +WGPURenderPassColorAttachment attach2 = {}; +gpu_init_color_attachment(attach2, final_view); +// ... render pass 2 using intermediate as input ... +``` + +--- + +## Related Documentation + +- **WGSL Shaders:** `doc/SEQUENCE.md` - Shader parameterization +- **Effect Creation:** `doc/EFFECT_WORKFLOW.md` - Visual effect setup +- **Uniforms:** `doc/UNIFORM_BUFFER_GUIDELINES.md` - Alignment rules +- **3D Rendering:** `doc/3D.md` - Renderer3D architecture +- **CNN Post-Process:** `doc/CNN_V2.md` - Storage buffer weights + +--- + +## Implementation Files + +- `src/gpu/gpu.h` - Header with declarations +- `src/gpu/gpu.cc` - Core implementations +- `src/tests/common/test_3d_helpers.{h,cc}` - Test-specific helpers + +--- + +## Size Considerations + +**Production Builds:** +- Helpers are **not** stripped in final build (needed for effect init) +- Validation checks controlled by `STRIP_ALL` / `FINAL_STRIP` +- Total overhead: ~2-3 KB (amortized across all effects) + +**Design Tradeoffs:** +- **Convenience vs Size:** Trade ~1 KB for 10× less boilerplate per effect +- **Type Safety:** GpuBuffer struct prevents size mismatch bugs +- **Cross-Platform:** Single helper handles Win32/native callback differences -- cgit v1.2.3