diff options
| author | skal <pascal.massimino@gmail.com> | 2026-01-31 20:44:23 +0100 |
|---|---|---|
| committer | skal <pascal.massimino@gmail.com> | 2026-01-31 20:44:23 +0100 |
| commit | c6b33c5e9b2325ca472dab8c4b64d1dab7b2885a (patch) | |
| tree | bdd7ad7a253562a5860fb9db20a9179931401672 /src/gpu | |
| parent | 4a55151d1a30f03ce1aa67e4c952ff6759c480c9 (diff) | |
fix(gpu): resolve multiple WebGPU validation and runtime errors
- Fixed 'Invalid sample count 0' and 'Invalid anisotropic clamp: 0' by ensuring explicit pipeline and sampler states.
- Resolved WGSL parsing errors by replacing swizzle assignments in compute shaders.
- Fixed 'Texture destroyed' error in render_frame by reordering command submission and resource presentation/release.
- Added WGPU_DEPTH_SLICE_UNDEFINED for Windows compatibility and ensured consistent resolveTarget initialization.
- Cleaned up PassthroughEffect bind group layout mismatch and redundant string helper definitions.
- Verified all tests pass and applied consistent formatting.
Diffstat (limited to 'src/gpu')
| -rw-r--r-- | src/gpu/demo_effects.cc | 77 | ||||
| -rw-r--r-- | src/gpu/demo_effects.h | 1 | ||||
| -rw-r--r-- | src/gpu/effect.cc | 63 | ||||
| -rw-r--r-- | src/gpu/gpu.h | 56 |
4 files changed, 87 insertions, 110 deletions
diff --git a/src/gpu/demo_effects.cc b/src/gpu/demo_effects.cc index 71f1284..27712d3 100644 --- a/src/gpu/demo_effects.cc +++ b/src/gpu/demo_effects.cc @@ -58,6 +58,8 @@ create_post_process_pipeline(WGPUDevice device, WGPUTextureFormat format, pipeline_desc.vertex.entryPoint = str_view("vs_main"); pipeline_desc.fragment = &fragment_state; pipeline_desc.primitive.topology = WGPUPrimitiveTopology_TriangleList; + pipeline_desc.multisample.count = 1; + pipeline_desc.multisample.mask = 0xFFFFFFFF; return wgpuDeviceCreateRenderPipeline(device, &pipeline_desc); } @@ -91,7 +93,8 @@ struct Uniforms { audio_peak: f32, aspect_ratio: f32, time: f32, beat: f32, }; fn main(@builtin(global_invocation_id) id: vec3<u32>) { let i = id.x; if (i >= arrayLength(&particles)) { return; } var p = particles[i]; - p.pos.xyz = p.pos.xyz + p.vel.xyz * 0.016; + let new_pos = p.pos.xyz + p.vel.xyz * 0.016; + p.pos = vec4<f32>(new_pos, p.pos.w); p.vel.y = p.vel.y - 0.01 * (1.0 + uniforms.audio_peak * 5.0); p.rot.x = p.rot.x + p.rot.y * 0.016; if (p.pos.y < -1.5) { @@ -171,7 +174,9 @@ fn main(@builtin(global_invocation_id) id: vec3<u32>) { p.vel = vec4<f32>(cos(angle), sin(angle), 0.0, 0.0) * (0.5 + hash(r)*0.5) * (1.0 + uniforms.intensity * 2.0); p.color = vec4<f32>(hash(r+0.1), hash(r+0.2), 1.0, 1.0); } - p.pos.xyz = p.pos.xyz + p.vel.xyz * 0.016; p.vel.y = p.vel.y - 0.01; p.pos.w = p.pos.w - 0.01 * (1.0 + uniforms.beat); + let new_pos = p.pos.xyz + p.vel.xyz * 0.016; + p.pos = vec4<f32>(new_pos, p.pos.w - 0.01 * (1.0 + uniforms.beat)); + p.vel.y = p.vel.y - 0.01; particles[i] = p; })"; @@ -310,27 +315,44 @@ void ParticlesEffect::render(WGPURenderPassEncoder pass, float t, float b, render_pass_.instance_count, 0, 0); } +// --- PostProcess Implementation Helper --- +static void pp_update_bind_group(WGPUDevice device, WGPURenderPipeline pipeline, + WGPUBindGroup *bind_group, + WGPUTextureView input_view, + GpuBuffer uniforms) { + if (*bind_group) + wgpuBindGroupRelease(*bind_group); + WGPUBindGroupLayout bgl = wgpuRenderPipelineGetBindGroupLayout(pipeline, 0); + WGPUSamplerDescriptor sd = {}; + sd.magFilter = WGPUFilterMode_Linear; + sd.minFilter = WGPUFilterMode_Linear; + sd.maxAnisotropy = 1; + WGPUSampler sampler = wgpuDeviceCreateSampler(device, &sd); + WGPUBindGroupEntry bge[3] = {}; + bge[0].binding = 0; + bge[0].sampler = sampler; + bge[1].binding = 1; + bge[1].textureView = input_view; + bge[2].binding = 2; + bge[2].buffer = uniforms.buffer; + bge[2].size = uniforms.size; + WGPUBindGroupDescriptor bgd = { + .layout = bgl, .entryCount = 3, .entries = bge}; + *bind_group = wgpuDeviceCreateBindGroup(device, &bgd); +} + // --- PassthroughEffect --- PassthroughEffect::PassthroughEffect(WGPUDevice device, WGPUTextureFormat format) : device_(device) { + uniforms_ = + gpu_create_buffer(device, sizeof(float) * 4, + WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst); pipeline_ = create_post_process_pipeline(device, format, passthrough_shader_wgsl); } void PassthroughEffect::update_bind_group(WGPUTextureView input_view) { - if (bind_group_) - wgpuBindGroupRelease(bind_group_); - WGPUBindGroupLayout bgl = wgpuRenderPipelineGetBindGroupLayout(pipeline_, 0); - WGPUSamplerDescriptor sd = {}; - WGPUSampler sampler = wgpuDeviceCreateSampler(device_, &sd); - WGPUBindGroupEntry bge[2] = {}; - bge[0].binding = 0; - bge[0].sampler = sampler; - bge[1].binding = 1; - bge[1].textureView = input_view; - WGPUBindGroupDescriptor bgd = { - .layout = bgl, .entryCount = 2, .entries = bge}; - bind_group_ = wgpuDeviceCreateBindGroup(device_, &bgd); + pp_update_bind_group(device_, pipeline_, &bind_group_, input_view, uniforms_); } // --- MovingEllipseEffect --- @@ -404,31 +426,6 @@ void ParticleSprayEffect::render(WGPURenderPassEncoder pass, float t, float b, wgpuRenderPassEncoderDraw(pass, 6, NUM_PARTICLES, 0, 0); } -// --- PostProcess Implementation Helper --- -static void pp_update_bind_group(WGPUDevice device, WGPURenderPipeline pipeline, - WGPUBindGroup *bind_group, - WGPUTextureView input_view, - GpuBuffer uniforms) { - if (*bind_group) - wgpuBindGroupRelease(*bind_group); - WGPUBindGroupLayout bgl = wgpuRenderPipelineGetBindGroupLayout(pipeline, 0); - WGPUSamplerDescriptor sd = {}; - sd.magFilter = WGPUFilterMode_Linear; - sd.minFilter = WGPUFilterMode_Linear; - WGPUSampler sampler = wgpuDeviceCreateSampler(device, &sd); - WGPUBindGroupEntry bge[3] = {}; - bge[0].binding = 0; - bge[0].sampler = sampler; - bge[1].binding = 1; - bge[1].textureView = input_view; - bge[2].binding = 2; - bge[2].buffer = uniforms.buffer; - bge[2].size = uniforms.size; - WGPUBindGroupDescriptor bgd = { - .layout = bgl, .entryCount = 3, .entries = bge}; - *bind_group = wgpuDeviceCreateBindGroup(device, &bgd); -} - // --- GaussianBlurEffect --- GaussianBlurEffect::GaussianBlurEffect(WGPUDevice device, WGPUQueue queue, WGPUTextureFormat format) diff --git a/src/gpu/demo_effects.h b/src/gpu/demo_effects.h index 603c32e..5db710e 100644 --- a/src/gpu/demo_effects.h +++ b/src/gpu/demo_effects.h @@ -50,6 +50,7 @@ public: private: WGPUDevice device_; + GpuBuffer uniforms_; }; class MovingEllipseEffect : public Effect { diff --git a/src/gpu/effect.cc b/src/gpu/effect.cc index 0ac9063..f9ebf0c 100644 --- a/src/gpu/effect.cc +++ b/src/gpu/effect.cc @@ -175,12 +175,11 @@ void MainSequence::render_frame(float global_time, float beat, float peak, // 2. Scene Pass (to A) WGPURenderPassColorAttachment scene_attachment = {}; scene_attachment.view = framebuffer_view_a_; + scene_attachment.resolveTarget = nullptr; scene_attachment.loadOp = WGPULoadOp_Clear; scene_attachment.storeOp = WGPUStoreOp_Store; scene_attachment.clearValue = {0, 0, 0, 1}; -#if !defined(DEMO_CROSS_COMPILE_WIN32) scene_attachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED; -#endif WGPURenderPassDescriptor scene_desc = {.colorAttachmentCount = 1, .colorAttachments = &scene_attachment}; WGPURenderPassEncoder scene_pass = @@ -192,48 +191,52 @@ void MainSequence::render_frame(float global_time, float beat, float peak, wgpuRenderPassEncoderEnd(scene_pass); // 3. Post Chain + WGPUSurfaceTexture st = {}; + WGPUTextureView final_view = nullptr; + if (post_effects.empty()) { - WGPUSurfaceTexture st; wgpuSurfaceGetCurrentTexture(surface, &st); - WGPUTextureView final_view = wgpuTextureCreateView(st.texture, nullptr); + final_view = wgpuTextureCreateView(st.texture, nullptr); passthrough_effect_->update_bind_group(framebuffer_view_a_); - WGPURenderPassColorAttachment final_attachment = {.view = final_view, - .loadOp = WGPULoadOp_Load, - .storeOp = - WGPUStoreOp_Store}; + WGPURenderPassColorAttachment final_attachment = {}; + final_attachment.view = final_view; + final_attachment.resolveTarget = nullptr; + final_attachment.loadOp = WGPULoadOp_Load; + final_attachment.storeOp = WGPUStoreOp_Store; + final_attachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED; WGPURenderPassDescriptor final_desc = { .colorAttachmentCount = 1, .colorAttachments = &final_attachment}; WGPURenderPassEncoder final_pass = wgpuCommandEncoderBeginRenderPass(encoder, &final_desc); passthrough_effect_->render(final_pass, 0, 0, 0, aspect_ratio); wgpuRenderPassEncoderEnd(final_pass); - - wgpuTextureViewRelease(final_view); - wgpuSurfacePresent(surface); - wgpuTextureRelease(st.texture); } else { WGPUTextureView current_input = framebuffer_view_a_; for (size_t i = 0; i < post_effects.size(); ++i) { bool is_last = (i == post_effects.size() - 1); - WGPUSurfaceTexture st; - if (is_last) - wgpuSurfaceGetCurrentTexture(surface, &st); + WGPUTextureView current_output = nullptr; - WGPUTextureView current_output = - is_last - ? wgpuTextureCreateView(st.texture, nullptr) - : (current_input == framebuffer_view_a_ ? framebuffer_view_b_ - : framebuffer_view_a_); + if (is_last) { + wgpuSurfaceGetCurrentTexture(surface, &st); + final_view = wgpuTextureCreateView(st.texture, nullptr); + current_output = final_view; + } else { + current_output = + (current_input == framebuffer_view_a_ ? framebuffer_view_b_ + : framebuffer_view_a_); + } PostProcessEffect *pp = static_cast<PostProcessEffect *>(post_effects[i]->effect.get()); pp->update_bind_group(current_input); - WGPURenderPassColorAttachment pp_attachment = {.view = current_output, - .loadOp = WGPULoadOp_Load, - .storeOp = - WGPUStoreOp_Store}; + WGPURenderPassColorAttachment pp_attachment = {}; + pp_attachment.view = current_output; + pp_attachment.resolveTarget = nullptr; + pp_attachment.loadOp = WGPULoadOp_Load; + pp_attachment.storeOp = WGPUStoreOp_Store; + pp_attachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED; WGPURenderPassDescriptor pp_desc = {.colorAttachmentCount = 1, .colorAttachments = &pp_attachment}; WGPURenderPassEncoder pp_pass = @@ -241,18 +244,18 @@ void MainSequence::render_frame(float global_time, float beat, float peak, pp->render(pp_pass, global_time - post_effects[i]->start_time, beat, peak, aspect_ratio); wgpuRenderPassEncoderEnd(pp_pass); - - if (is_last) { - wgpuTextureViewRelease(current_output); - wgpuSurfacePresent(surface); - wgpuTextureRelease(st.texture); - } current_input = current_output; } } WGPUCommandBuffer commands = wgpuCommandEncoderFinish(encoder, nullptr); wgpuQueueSubmit(queue, 1, &commands); + + if (st.texture) { + wgpuTextureViewRelease(final_view); + wgpuSurfacePresent(surface); + wgpuTextureRelease(st.texture); + } } void MainSequence::shutdown() { diff --git a/src/gpu/gpu.h b/src/gpu/gpu.h index 0bc20b7..c2c0670 100644 --- a/src/gpu/gpu.h +++ b/src/gpu/gpu.h @@ -8,8 +8,6 @@ #include <cstring> // For strlen - - #if defined(DEMO_CROSS_COMPILE_WIN32) // Windows (MinGW) using wgpu-native v0.19.4.1 @@ -18,69 +16,52 @@ #include <webgpu/wgpu.h> +static inline const char *str_view(const char *str) { + return str; +} - -static inline const char *str_view(const char *str) { return str; } - - - -static inline const char *label_view(const char *str) { return str; } - - - - - - +static inline const char *label_view(const char *str) { + return str; +} #define WGPUSType_ShaderSourceWGSL WGPUSType_ShaderModuleWGSLDescriptor - - using WGPUShaderSourceWGSL = WGPUShaderModuleWGSLDescriptor; +#define WGPUSurfaceGetCurrentTextureStatus_SuccessOptimal \ + WGPUSurfaceGetCurrentTextureStatus_Success - -#define WGPUSurfaceGetCurrentTextureStatus_SuccessOptimal WGPUSurfaceGetCurrentTextureStatus_Success - - - -#define WGPUSurfaceGetCurrentTextureStatus_SuccessSuboptimal WGPUSurfaceGetCurrentTextureStatus_Success - - +#define WGPUSurfaceGetCurrentTextureStatus_SuccessSuboptimal \ + WGPUSurfaceGetCurrentTextureStatus_Success #define WGPUCallbackMode_WaitAnyOnly 0 - - - +#define WGPU_DEPTH_SLICE_UNDEFINED (0xffffffff) #else -// Native (macOS/Linux) using newer wgpu-native +// Native (macOS/Linux) using newer wgpu-native #include <webgpu.h> #include <wgpu.h> - - static inline WGPUStringView str_view(const char *str) { - if (!str) return {nullptr, 0}; + if (!str) + return {nullptr, 0}; return {str, strlen(str)}; - } - - static inline WGPUStringView label_view(const char *str) { #ifndef STRIP_ALL - if (!str) return {nullptr, 0}; + if (!str) + return {nullptr, 0}; return {str, strlen(str)}; @@ -91,15 +72,10 @@ static inline WGPUStringView label_view(const char *str) { return {nullptr, 0}; #endif - } - - #endif - - struct GLFWwindow; // Basic wrapper for WebGPU buffers |
