diff options
| author | skal <pascal.massimino@gmail.com> | 2026-02-09 20:27:04 +0100 |
|---|---|---|
| committer | skal <pascal.massimino@gmail.com> | 2026-02-09 20:27:04 +0100 |
| commit | eff8d43479e7704df65fae2a80eefa787213f502 (patch) | |
| tree | 76f2fb8fe8d3db2c15179449df2cf12f7f54e0bf /src/tests/gpu/test_post_process_helper.cc | |
| parent | 12378b1b7e9091ba59895b4360b2fa959180a56a (diff) | |
refactor: Reorganize tests into subsystem subdirectories
Restructured test suite for better organization and targeted testing:
**Structure:**
- src/tests/audio/ - 15 audio system tests
- src/tests/gpu/ - 12 GPU/shader tests
- src/tests/3d/ - 6 3D rendering tests
- src/tests/assets/ - 2 asset system tests
- src/tests/util/ - 3 utility tests
- src/tests/common/ - 3 shared test helpers
- src/tests/scripts/ - 2 bash test scripts (moved conceptually, not physically)
**CMake changes:**
- Updated add_demo_test macro to accept LABEL parameter
- Applied CTest labels to all 36 tests for subsystem filtering
- Updated all test file paths in CMakeLists.txt
- Fixed common helper paths (webgpu_test_fixture, etc.)
- Added custom targets for subsystem testing:
- run_audio_tests, run_gpu_tests, run_3d_tests
- run_assets_tests, run_util_tests, run_all_tests
**Include path updates:**
- Fixed relative includes in GPU tests to reference ../common/
**Documentation:**
- Updated doc/HOWTO.md with subsystem test commands
- Updated doc/CONTRIBUTING.md with new test organization
- Updated scripts/check_all.sh to reflect new structure
**Verification:**
- All 36 tests passing (100%)
- ctest -L <subsystem> filters work correctly
- make run_<subsystem>_tests targets functional
- scripts/check_all.sh passes
Backward compatible: make test and ctest continue to work unchanged.
handoff(Gemini): Test reorganization complete. 36/36 tests passing.
Diffstat (limited to 'src/tests/gpu/test_post_process_helper.cc')
| -rw-r--r-- | src/tests/gpu/test_post_process_helper.cc | 306 |
1 files changed, 306 insertions, 0 deletions
diff --git a/src/tests/gpu/test_post_process_helper.cc b/src/tests/gpu/test_post_process_helper.cc new file mode 100644 index 0000000..868bf26 --- /dev/null +++ b/src/tests/gpu/test_post_process_helper.cc @@ -0,0 +1,306 @@ +// This file is part of the 64k demo project. +// It tests post-processing helper functions (pipeline and bind group creation). +// Validates that helpers can create valid WebGPU resources. + +#include "gpu/demo_effects.h" +#include "gpu/gpu.h" +#include "../common/offscreen_render_target.h" +#include "../common/webgpu_test_fixture.h" +#include <cassert> +#include <cstdio> + +// External helper functions (defined in post_process_helper.cc) +extern WGPURenderPipeline create_post_process_pipeline(WGPUDevice device, + WGPUTextureFormat format, + const char* shader_code); +extern void pp_update_bind_group(WGPUDevice device, WGPURenderPipeline pipeline, + WGPUBindGroup* bind_group, + WGPUTextureView input_view, + GpuBuffer uniforms); + +// Helper: Create a texture suitable for post-processing (both render target and +// texture binding) +static WGPUTexture create_post_process_texture(WGPUDevice device, int width, + int height, + WGPUTextureFormat format) { + const WGPUTextureDescriptor texture_desc = { + .usage = WGPUTextureUsage_RenderAttachment | + WGPUTextureUsage_TextureBinding | WGPUTextureUsage_CopySrc, + .dimension = WGPUTextureDimension_2D, + .size = {static_cast<uint32_t>(width), static_cast<uint32_t>(height), 1}, + .format = format, + .mipLevelCount = 1, + .sampleCount = 1, + }; + return wgpuDeviceCreateTexture(device, &texture_desc); +} + +// Helper: Create texture view +static WGPUTextureView create_texture_view(WGPUTexture texture, + WGPUTextureFormat format) { + const WGPUTextureViewDescriptor view_desc = { + .format = format, + .dimension = WGPUTextureViewDimension_2D, + .baseMipLevel = 0, + .mipLevelCount = 1, + .baseArrayLayer = 0, + .arrayLayerCount = 1, + }; + return wgpuTextureCreateView(texture, &view_desc); +} + +// Minimal valid post-process shader for testing +static const char* test_shader = R"( +@vertex +fn vs_main(@builtin(vertex_index) vid: u32) -> @builtin(position) vec4<f32> { + let x = f32((vid & 1u) << 1u) - 1.0; + let y = f32((vid & 2u) >> 0u) - 1.0; + return vec4<f32>(x, y, 0.0, 1.0); +} + +@group(0) @binding(0) var input_sampler: sampler; +@group(0) @binding(1) var input_texture: texture_2d<f32>; +@group(0) @binding(2) var<uniform> uniforms: vec4<f32>; +@group(0) @binding(3) var<uniform> effect_params: vec4<f32>; // Dummy for testing + +@fragment +fn fs_main(@builtin(position) pos: vec4<f32>) -> @location(0) vec4<f32> { + let uv = pos.xy / vec2<f32>(256.0, 256.0); + return textureSample(input_texture, input_sampler, uv); +} +)"; + +// Test 1: Pipeline creation +static void test_pipeline_creation() { + fprintf(stdout, "Testing post-process pipeline creation...\n"); + + WebGPUTestFixture fixture; + if (!fixture.init()) { + fprintf(stdout, " ⚠ WebGPU unavailable - skipping test\n"); + return; + } + + WGPURenderPipeline pipeline = create_post_process_pipeline( + fixture.device(), fixture.format(), test_shader); + + assert(pipeline != nullptr && "Pipeline should be created successfully"); + fprintf(stdout, " ✓ Pipeline created successfully\n"); + + // Cleanup + wgpuRenderPipelineRelease(pipeline); + fprintf(stdout, " ✓ Pipeline released\n"); +} + +// Test 2: Bind group creation +static void test_bind_group_creation() { + fprintf(stdout, "Testing post-process bind group creation...\n"); + + WebGPUTestFixture fixture; + if (!fixture.init()) { + fprintf(stdout, " ⚠ WebGPU unavailable - skipping test\n"); + return; + } + + // Create pipeline + WGPURenderPipeline pipeline = create_post_process_pipeline( + fixture.device(), fixture.format(), test_shader); + assert(pipeline != nullptr && "Pipeline required for bind group test"); + + // Create input texture with TEXTURE_BINDING usage + WGPUTexture input_texture = + create_post_process_texture(fixture.device(), 256, 256, fixture.format()); + WGPUTextureView input_view = + create_texture_view(input_texture, fixture.format()); + + // Create uniform buffer + const WGPUBufferDescriptor uniform_desc = { + .usage = WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst, + .size = 16, // vec4<f32> + }; + WGPUBuffer uniform_buffer = + wgpuDeviceCreateBuffer(fixture.device(), &uniform_desc); + assert(uniform_buffer != nullptr && "Uniform buffer should be created"); + + GpuBuffer uniforms = {uniform_buffer, 16}; + + // Dummy effect params buffer for testing (matches vec4<f32>) + WGPUBuffer dummy_params_buffer_handle = + wgpuDeviceCreateBuffer(fixture.device(), &uniform_desc); + GpuBuffer dummy_effect_params_buffer = {dummy_params_buffer_handle, 16}; + + // Test bind group creation + WGPUBindGroup bind_group = nullptr; + pp_update_bind_group(fixture.device(), pipeline, &bind_group, input_view, + uniforms, dummy_effect_params_buffer); + + assert(bind_group != nullptr && "Bind group should be created successfully"); + fprintf(stdout, " ✓ Bind group created successfully\n"); + + // Cleanup + wgpuBindGroupRelease(bind_group); + wgpuTextureViewRelease(input_view); + wgpuTextureRelease(input_texture); + wgpuBufferRelease(uniform_buffer); + wgpuBufferRelease(dummy_params_buffer_handle); + wgpuRenderPipelineRelease(pipeline); + fprintf(stdout, " ✓ Resources released\n"); +} + +// Test 3: Bind group update (replacement) +static void test_bind_group_update() { + fprintf(stdout, "Testing post-process bind group update...\n"); + + WebGPUTestFixture fixture; + if (!fixture.init()) { + fprintf(stdout, " ⚠ WebGPU unavailable - skipping test\n"); + return; + } + + WGPURenderPipeline pipeline = create_post_process_pipeline( + fixture.device(), fixture.format(), test_shader); + + WGPUTexture texture1 = + create_post_process_texture(fixture.device(), 256, 256, fixture.format()); + WGPUTextureView view1 = create_texture_view(texture1, fixture.format()); + + WGPUTexture texture2 = + create_post_process_texture(fixture.device(), 512, 512, fixture.format()); + WGPUTextureView view2 = create_texture_view(texture2, fixture.format()); + + const WGPUBufferDescriptor uniform_desc = { + .usage = WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst, + .size = 16, + }; + WGPUBuffer uniform_buffer = + wgpuDeviceCreateBuffer(fixture.device(), &uniform_desc); + GpuBuffer uniforms = {uniform_buffer, 16}; + + // Dummy effect params buffer for testing (matches vec4<f32>) + WGPUBuffer dummy_params_buffer_handle = + wgpuDeviceCreateBuffer(fixture.device(), &uniform_desc); + GpuBuffer dummy_effect_params_buffer = {dummy_params_buffer_handle, 16}; + + // Create initial bind group + WGPUBindGroup bind_group = nullptr; + pp_update_bind_group(fixture.device(), pipeline, &bind_group, view1, uniforms, + dummy_effect_params_buffer); + assert(bind_group != nullptr && "Initial bind group should be created"); + fprintf(stdout, " ✓ Initial bind group created\n"); + + // Update bind group (should release old and create new) + pp_update_bind_group(fixture.device(), pipeline, &bind_group, view2, uniforms, + dummy_effect_params_buffer); + assert(bind_group != nullptr && "Updated bind group should be created"); + fprintf(stdout, " ✓ Bind group updated successfully\n"); + + // Cleanup + wgpuBindGroupRelease(bind_group); + wgpuTextureViewRelease(view1); + wgpuTextureRelease(texture1); + wgpuTextureViewRelease(view2); + wgpuTextureRelease(texture2); + wgpuBufferRelease(uniform_buffer); + wgpuBufferRelease(dummy_params_buffer_handle); + wgpuRenderPipelineRelease(pipeline); + fprintf(stdout, " ✓ Resources released\n"); +} + +// Test 4: Full post-process setup (pipeline + bind group) +static void test_full_setup() { + fprintf(stdout, "Testing full post-process setup...\n"); + + WebGPUTestFixture fixture; + if (!fixture.init()) { + fprintf(stdout, " ⚠ WebGPU unavailable - skipping test\n"); + return; + } + + // Create pipeline + WGPURenderPipeline pipeline = create_post_process_pipeline( + fixture.device(), fixture.format(), test_shader); + assert(pipeline != nullptr && "Pipeline creation failed"); + + // Create input texture (with TEXTURE_BINDING usage) + WGPUTexture input_texture = + create_post_process_texture(fixture.device(), 256, 256, fixture.format()); + WGPUTextureView input_view = + create_texture_view(input_texture, fixture.format()); + + // Create output texture (can use OffscreenRenderTarget for this) + OffscreenRenderTarget output_target(fixture.instance(), fixture.device(), 256, + 256); + + const WGPUBufferDescriptor uniform_desc = { + .usage = WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst, + .size = 16, + }; + WGPUBuffer uniform_buffer = + wgpuDeviceCreateBuffer(fixture.device(), &uniform_desc); + GpuBuffer uniforms = {uniform_buffer, 16}; + + // Dummy effect params buffer for testing (matches vec4<f32>) + WGPUBuffer dummy_params_buffer_handle = + wgpuDeviceCreateBuffer(fixture.device(), &uniform_desc); + GpuBuffer dummy_effect_params_buffer = {dummy_params_buffer_handle, 16}; + + // Create bind group + WGPUBindGroup bind_group = nullptr; + pp_update_bind_group(fixture.device(), pipeline, &bind_group, input_view, + uniforms, dummy_effect_params_buffer); + assert(bind_group != nullptr && "Bind group creation failed"); + + fprintf(stdout, " ✓ Pipeline and bind group ready\n"); + + // Test render pass setup (smoke test - just verify we can create a pass) + const WGPUCommandEncoderDescriptor enc_desc = {}; + WGPUCommandEncoder encoder = + wgpuDeviceCreateCommandEncoder(fixture.device(), &enc_desc); + + WGPURenderPassColorAttachment color_attachment = {}; + gpu_init_color_attachment(color_attachment, output_target.view()); + + WGPURenderPassDescriptor pass_desc = {}; + pass_desc.colorAttachmentCount = 1; + pass_desc.colorAttachments = &color_attachment; + + WGPURenderPassEncoder pass = + wgpuCommandEncoderBeginRenderPass(encoder, &pass_desc); + + // Set pipeline and bind group + wgpuRenderPassEncoderSetPipeline(pass, pipeline); + wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group, 0, nullptr); + + // Draw fullscreen triangle + wgpuRenderPassEncoderDraw(pass, 3, 1, 0, 0); + wgpuRenderPassEncoderEnd(pass); + + WGPUCommandBuffer commands = wgpuCommandEncoderFinish(encoder, nullptr); + wgpuQueueSubmit(wgpuDeviceGetQueue(fixture.device()), 1, &commands); + + fprintf(stdout, " ✓ Render pass executed successfully\n"); + + // Cleanup + wgpuCommandBufferRelease(commands); + wgpuRenderPassEncoderRelease(pass); + wgpuCommandEncoderRelease(encoder); + wgpuBindGroupRelease(bind_group); + wgpuTextureViewRelease(input_view); + wgpuTextureRelease(input_texture); + wgpuBufferRelease(uniform_buffer); + wgpuBufferRelease(dummy_params_buffer_handle); + wgpuRenderPipelineRelease(pipeline); + + fprintf(stdout, " ✓ Full setup test completed\n"); +} + +int main() { + fprintf(stdout, "=== Post-Process Helper Tests ===\n"); + + test_pipeline_creation(); + test_bind_group_creation(); + test_bind_group_update(); + test_full_setup(); + + fprintf(stdout, "=== All Post-Process Helper Tests Passed ===\n"); + return 0; +} |
