# 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