From eff8d43479e7704df65fae2a80eefa787213f502 Mon Sep 17 00:00:00 2001 From: skal Date: Mon, 9 Feb 2026 20:27:04 +0100 Subject: 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 filters work correctly - make run__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. --- src/tests/gpu/test_post_process_helper.cc | 306 ++++++++++++++++++++++++++++++ 1 file changed, 306 insertions(+) create mode 100644 src/tests/gpu/test_post_process_helper.cc (limited to 'src/tests/gpu/test_post_process_helper.cc') 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 +#include + +// 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(width), static_cast(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 { + let x = f32((vid & 1u) << 1u) - 1.0; + let y = f32((vid & 2u) >> 0u) - 1.0; + return vec4(x, y, 0.0, 1.0); +} + +@group(0) @binding(0) var input_sampler: sampler; +@group(0) @binding(1) var input_texture: texture_2d; +@group(0) @binding(2) var uniforms: vec4; +@group(0) @binding(3) var effect_params: vec4; // Dummy for testing + +@fragment +fn fs_main(@builtin(position) pos: vec4) -> @location(0) vec4 { + let uv = pos.xy / vec2(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 + }; + 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) + 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) + 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) + 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; +} -- cgit v1.2.3