summaryrefslogtreecommitdiff
path: root/src/tests
diff options
context:
space:
mode:
Diffstat (limited to 'src/tests')
-rw-r--r--src/tests/test_post_process_helper.cc280
1 files changed, 280 insertions, 0 deletions
diff --git a/src/tests/test_post_process_helper.cc b/src/tests/test_post_process_helper.cc
new file mode 100644
index 0000000..c1c5591
--- /dev/null
+++ b/src/tests/test_post_process_helper.cc
@@ -0,0 +1,280 @@
+// 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.
+
+#if !defined(STRIP_ALL) // Test code only - zero size impact on final binary
+
+#include "webgpu_test_fixture.h"
+#include "offscreen_render_target.h"
+#include "gpu/demo_effects.h"
+#include "gpu/gpu.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>;
+
+@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};
+
+ // Test bind group creation
+ WGPUBindGroup bind_group = nullptr;
+ pp_update_bind_group(fixture.device(), pipeline, &bind_group,
+ input_view, uniforms);
+
+ 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);
+ 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};
+
+ // Create initial bind group
+ WGPUBindGroup bind_group = nullptr;
+ pp_update_bind_group(fixture.device(), pipeline, &bind_group,
+ view1, uniforms);
+ 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);
+ 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);
+ 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};
+
+ // Create bind group
+ WGPUBindGroup bind_group = nullptr;
+ pp_update_bind_group(fixture.device(), pipeline, &bind_group,
+ input_view, uniforms);
+ 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);
+ 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;
+}
+
+#endif /* !defined(STRIP_ALL) */