diff options
| author | skal <pascal.massimino@gmail.com> | 2026-02-16 15:20:39 +0100 |
|---|---|---|
| committer | skal <pascal.massimino@gmail.com> | 2026-02-16 15:20:39 +0100 |
| commit | 1c9bc4abd5ab90a61e3485fe30ff3c6f9b4b319c (patch) | |
| tree | ae97a9712411eedc1fee98c134fc54e53de8b047 /src | |
| parent | 80d395b8bc0c37778401eb771094c25db7a1b8a4 (diff) | |
refactor(headless): convert nullptr checks to strippable macros
Added HEADLESS_RETURN_IF_NULL/HEADLESS_RETURN_VAL_IF_NULL macros
that compile to no-ops in STRIP_ALL/FINAL_STRIP modes.
Files updated:
- fatal_error.h: New headless check macros
- sequence.cc: NodeRegistry::create_texture
- post_process_helper.cc: Pipeline creation functions
- sampler_cache.h: SamplerCache::get_or_create
- bind_group_builder.h: Layout/group builders
- pipeline_builder.h: Shader and pipeline builders
- All effect constructors (7 files)
Headless tests passing. STRIP_ALL builds will have zero overhead.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Diffstat (limited to 'src')
| -rw-r--r-- | src/effects/gaussian_blur_effect.cc | 7 | ||||
| -rw-r--r-- | src/effects/heptagon_effect.cc | 7 | ||||
| -rw-r--r-- | src/effects/hybrid3_d_effect.cc | 7 | ||||
| -rw-r--r-- | src/effects/particles_effect.cc | 7 | ||||
| -rw-r--r-- | src/effects/passthrough_effect.cc | 7 | ||||
| -rw-r--r-- | src/effects/placeholder_effect.cc | 27 | ||||
| -rw-r--r-- | src/effects/rotating_cube_effect.cc | 7 | ||||
| -rw-r--r-- | src/gpu/bind_group_builder.h | 13 | ||||
| -rw-r--r-- | src/gpu/pipeline_builder.h | 28 | ||||
| -rw-r--r-- | src/gpu/post_process_helper.cc | 20 | ||||
| -rw-r--r-- | src/gpu/sampler_cache.h | 7 | ||||
| -rw-r--r-- | src/gpu/sequence.cc | 10 | ||||
| -rw-r--r-- | src/util/fatal_error.h | 32 |
13 files changed, 98 insertions, 81 deletions
diff --git a/src/effects/gaussian_blur_effect.cc b/src/effects/gaussian_blur_effect.cc index fdf1807..657c283 100644 --- a/src/effects/gaussian_blur_effect.cc +++ b/src/effects/gaussian_blur_effect.cc @@ -1,6 +1,7 @@ // Gaussian blur effect v2 implementation #include "effects/gaussian_blur_effect.h" +#include "util/fatal_error.h" #include "gpu/post_process_helper.h" #include "gpu/shaders.h" @@ -9,10 +10,8 @@ GaussianBlurEffect::GaussianBlurEffect(const GpuContext& ctx, const std::vector<std::string>& outputs) : Effect(ctx, inputs, outputs), pipeline_(nullptr), bind_group_(nullptr), sampler_(nullptr) { - // Headless mode: skip GPU resource creation - if (ctx_.device == nullptr) { - return; - } + // Headless mode: skip GPU resource creation (compiled out in STRIP_ALL) + HEADLESS_RETURN_IF_NULL(ctx_.device); // Create pipeline pipeline_ = create_post_process_pipeline(ctx_.device, WGPUTextureFormat_RGBA8Unorm, diff --git a/src/effects/heptagon_effect.cc b/src/effects/heptagon_effect.cc index 3321517..31e94e7 100644 --- a/src/effects/heptagon_effect.cc +++ b/src/effects/heptagon_effect.cc @@ -1,6 +1,7 @@ // Heptagon effect v2 implementation #include "effects/heptagon_effect.h" +#include "util/fatal_error.h" #include "gpu/gpu.h" #include "gpu/post_process_helper.h" #include "gpu/shaders.h" @@ -9,10 +10,8 @@ HeptagonEffect::HeptagonEffect(const GpuContext& ctx, const std::vector<std::string>& inputs, const std::vector<std::string>& outputs) : Effect(ctx, inputs, outputs), pipeline_(nullptr), bind_group_(nullptr), sampler_(nullptr) { - // Headless mode: skip GPU resource creation - if (ctx_.device == nullptr) { - return; - } + // Headless mode: skip GPU resource creation (compiled out in STRIP_ALL) + HEADLESS_RETURN_IF_NULL(ctx_.device); // Init uniforms uniforms_buffer_.init(ctx_.device); diff --git a/src/effects/hybrid3_d_effect.cc b/src/effects/hybrid3_d_effect.cc index 2a015a0..0e6ea35 100644 --- a/src/effects/hybrid3_d_effect.cc +++ b/src/effects/hybrid3_d_effect.cc @@ -1,6 +1,7 @@ // This file is part of the 64k demo project. // It implements Hybrid3DEffect (simplified v2 port). // TODO: Full Renderer3D integration with texture manager, noise assets +#include "util/fatal_error.h" #include "effects/hybrid3_d_effect.h" #include <cmath> @@ -10,10 +11,8 @@ Hybrid3DEffect::Hybrid3DEffect(const GpuContext& ctx, const std::vector<std::string>& outputs) : Effect(ctx, inputs, outputs), depth_node_(outputs[0] + "_depth"), dummy_texture_(nullptr), dummy_texture_view_(nullptr) { - // Headless mode: skip GPU resource creation - if (ctx_.device == nullptr) { - return; - } + // Headless mode: skip GPU resource creation (compiled out in STRIP_ALL) + HEADLESS_RETURN_IF_NULL(ctx_.device); // Initialize renderer (format is always RGBA8Unorm for v2) renderer_.init(ctx_.device, ctx_.queue, WGPUTextureFormat_RGBA8Unorm); diff --git a/src/effects/particles_effect.cc b/src/effects/particles_effect.cc index 709acbf..a98d7e5 100644 --- a/src/effects/particles_effect.cc +++ b/src/effects/particles_effect.cc @@ -1,6 +1,7 @@ // This file is part of the 64k demo project. // It implements the ParticlesEffect. +#include "util/fatal_error.h" #include "effects/particles_effect.h" #include "gpu/gpu.h" #include "gpu/shaders.h" @@ -10,10 +11,8 @@ ParticlesEffect::ParticlesEffect(const GpuContext& ctx, const std::vector<std::string>& inputs, const std::vector<std::string>& outputs) : Effect(ctx, inputs, outputs) { - // Headless mode: skip GPU resource creation - if (ctx_.device == nullptr) { - return; - } + // Headless mode: skip GPU resource creation (compiled out in STRIP_ALL) + HEADLESS_RETURN_IF_NULL(ctx_.device); // Initialize uniforms uniforms_.init(ctx_.device); diff --git a/src/effects/passthrough_effect.cc b/src/effects/passthrough_effect.cc index c4106b1..8cbd425 100644 --- a/src/effects/passthrough_effect.cc +++ b/src/effects/passthrough_effect.cc @@ -1,6 +1,7 @@ // Passthrough effect v2 implementation #include "effects/passthrough_effect.h" +#include "util/fatal_error.h" #include "gpu/post_process_helper.h" #include "gpu/shaders.h" @@ -9,10 +10,8 @@ PassthroughEffect::PassthroughEffect(const GpuContext& ctx, const std::vector<std::string>& outputs) : Effect(ctx, inputs, outputs), pipeline_(nullptr), bind_group_(nullptr), sampler_(nullptr) { - // Headless mode: skip GPU resource creation - if (ctx_.device == nullptr) { - return; - } + // Headless mode: skip GPU resource creation (compiled out in STRIP_ALL) + HEADLESS_RETURN_IF_NULL(ctx_.device); // Init uniform buffer uniforms_buffer_.init(ctx_.device); diff --git a/src/effects/placeholder_effect.cc b/src/effects/placeholder_effect.cc index a83d922..a014a71 100644 --- a/src/effects/placeholder_effect.cc +++ b/src/effects/placeholder_effect.cc @@ -1,6 +1,7 @@ // Placeholder effect v2 implementation - logs TODO warning once #include "effects/placeholder_effect.h" +#include "util/fatal_error.h" #include "gpu/post_process_helper.h" #include "gpu/shaders.h" #include <cstdio> @@ -13,23 +14,23 @@ PlaceholderEffect::PlaceholderEffect(const GpuContext& ctx, sampler_(nullptr), name_(placeholder_name) { // Log once on construction fprintf(stderr, "TODO: %s not yet ported to v2, using passthrough\n", name_); - + + // Headless mode: skip GPU resource creation (compiled out in STRIP_ALL) + HEADLESS_RETURN_IF_NULL(ctx_.device); + uniforms_buffer_.init(ctx_.device); pipeline_ = create_post_process_pipeline(ctx_.device, WGPUTextureFormat_RGBA8Unorm, passthrough_v2_shader_wgsl); - // Headless mode: skip sampler creation - if (ctx_.device != nullptr) { - WGPUSamplerDescriptor sampler_desc = {}; - sampler_desc.addressModeU = WGPUAddressMode_ClampToEdge; - sampler_desc.addressModeV = WGPUAddressMode_ClampToEdge; - sampler_desc.addressModeW = WGPUAddressMode_ClampToEdge; - sampler_desc.magFilter = WGPUFilterMode_Linear; - sampler_desc.minFilter = WGPUFilterMode_Linear; - sampler_desc.mipmapFilter = WGPUMipmapFilterMode_Nearest; - sampler_desc.maxAnisotropy = 1; - sampler_ = wgpuDeviceCreateSampler(ctx_.device, &sampler_desc); - } + WGPUSamplerDescriptor sampler_desc = {}; + sampler_desc.addressModeU = WGPUAddressMode_ClampToEdge; + sampler_desc.addressModeV = WGPUAddressMode_ClampToEdge; + sampler_desc.addressModeW = WGPUAddressMode_ClampToEdge; + sampler_desc.magFilter = WGPUFilterMode_Linear; + sampler_desc.minFilter = WGPUFilterMode_Linear; + sampler_desc.mipmapFilter = WGPUMipmapFilterMode_Nearest; + sampler_desc.maxAnisotropy = 1; + sampler_ = wgpuDeviceCreateSampler(ctx_.device, &sampler_desc); } void PlaceholderEffect::render(WGPUCommandEncoder encoder, diff --git a/src/effects/rotating_cube_effect.cc b/src/effects/rotating_cube_effect.cc index e7ed52e..4bd79fe 100644 --- a/src/effects/rotating_cube_effect.cc +++ b/src/effects/rotating_cube_effect.cc @@ -1,6 +1,7 @@ // This file is part of the 64k demo project. // It implements RotatingCubeEffect (simplified v2 port). +#include "util/fatal_error.h" #include "effects/rotating_cube_effect.h" #include "gpu/bind_group_builder.h" #include "gpu/gpu.h" @@ -10,10 +11,8 @@ RotatingCubeEffect::RotatingCubeEffect(const GpuContext& ctx, const std::vector<std::string>& inputs, const std::vector<std::string>& outputs) : Effect(ctx, inputs, outputs), depth_node_(outputs[0] + "_depth") { - // Headless mode: skip GPU resource creation - if (ctx_.device == nullptr) { - return; - } + // Headless mode: skip GPU resource creation (compiled out in STRIP_ALL) + HEADLESS_RETURN_IF_NULL(ctx_.device); // Create uniform buffers uniform_buffer_ = diff --git a/src/gpu/bind_group_builder.h b/src/gpu/bind_group_builder.h index 3b25ba9..49b7ebe 100644 --- a/src/gpu/bind_group_builder.h +++ b/src/gpu/bind_group_builder.h @@ -20,6 +20,7 @@ typedef struct WGPUSamplerImpl* WGPUSampler; typedef uint32_t WGPUShaderStageFlags; #include "platform/platform.h" +#include "util/fatal_error.h" class BindGroupLayoutBuilder { std::vector<WGPUBindGroupLayoutEntry> entries_; @@ -93,10 +94,8 @@ class BindGroupLayoutBuilder { } WGPUBindGroupLayout build(WGPUDevice device) { - // Headless mode: skip bind group layout creation - if (device == nullptr) { - return nullptr; - } + // Headless mode: skip bind group layout creation (compiled out in STRIP_ALL) + HEADLESS_RETURN_VAL_IF_NULL(device, nullptr); WGPUBindGroupLayoutDescriptor desc{}; desc.entryCount = entries_.size(); desc.entries = entries_.data(); @@ -134,10 +133,8 @@ class BindGroupBuilder { } WGPUBindGroup build(WGPUDevice device, WGPUBindGroupLayout layout) { - // Headless mode: skip bind group creation - if (device == nullptr) { - return nullptr; - } + // Headless mode: skip bind group creation (compiled out in STRIP_ALL) + HEADLESS_RETURN_VAL_IF_NULL(device, nullptr); WGPUBindGroupDescriptor desc{}; desc.layout = layout; desc.entryCount = entries_.size(); diff --git a/src/gpu/pipeline_builder.h b/src/gpu/pipeline_builder.h index a258b49..937c55f 100644 --- a/src/gpu/pipeline_builder.h +++ b/src/gpu/pipeline_builder.h @@ -15,6 +15,7 @@ typedef struct WGPUShaderModuleImpl* WGPUShaderModule; #include "gpu/shader_composer.h" #include "platform/platform.h" +#include "util/fatal_error.h" class RenderPipelineBuilder { WGPUDevice device_; @@ -38,17 +39,18 @@ class RenderPipelineBuilder { RenderPipelineBuilder& shader(const char* wgsl, bool compose = true) { shader_text_ = compose ? ShaderComposer::Get().Compose({}, wgsl) : wgsl; - // Headless mode: skip shader module creation - if (device_ != nullptr) { - WGPUShaderSourceWGSL wgsl_src{}; - wgsl_src.chain.sType = WGPUSType_ShaderSourceWGSL; - wgsl_src.code = str_view(shader_text_.c_str()); - WGPUShaderModuleDescriptor shader_desc{}; - shader_desc.nextInChain = &wgsl_src.chain; - shader_module_ = wgpuDeviceCreateShaderModule(device_, &shader_desc); - desc_.vertex.module = shader_module_; - desc_.vertex.entryPoint = str_view("vs_main"); + // Headless mode: skip shader module creation (compiled out in STRIP_ALL) + if (device_ == nullptr) { + return *this; } + WGPUShaderSourceWGSL wgsl_src{}; + wgsl_src.chain.sType = WGPUSType_ShaderSourceWGSL; + wgsl_src.code = str_view(shader_text_.c_str()); + WGPUShaderModuleDescriptor shader_desc{}; + shader_desc.nextInChain = &wgsl_src.chain; + shader_module_ = wgpuDeviceCreateShaderModule(device_, &shader_desc); + desc_.vertex.module = shader_module_; + desc_.vertex.entryPoint = str_view("vs_main"); return *this; } @@ -88,10 +90,8 @@ class RenderPipelineBuilder { } WGPURenderPipeline build() { - // Headless mode: skip pipeline creation - if (device_ == nullptr) { - return nullptr; - } + // Headless mode: skip pipeline creation (compiled out in STRIP_ALL) + HEADLESS_RETURN_VAL_IF_NULL(device_, nullptr); color_.writeMask = WGPUColorWriteMask_All; if (has_blend_) diff --git a/src/gpu/post_process_helper.cc b/src/gpu/post_process_helper.cc index 6026f6e..281fde6 100644 --- a/src/gpu/post_process_helper.cc +++ b/src/gpu/post_process_helper.cc @@ -8,16 +8,15 @@ #include "gpu/pipeline_builder.h" #include "gpu/sampler_cache.h" #include "gpu/shader_composer.h" +#include "util/fatal_error.h" #include <cstring> // Helper to create a standard post-processing pipeline WGPURenderPipeline create_post_process_pipeline(WGPUDevice device, WGPUTextureFormat format, const char* shader_code) { - // Headless mode: skip pipeline creation - if (device == nullptr) { - return nullptr; - } + // Headless mode: skip pipeline creation (compiled out in STRIP_ALL) + HEADLESS_RETURN_VAL_IF_NULL(device, nullptr); WGPUBindGroupLayout bgl = BindGroupLayoutBuilder() @@ -45,10 +44,8 @@ WGPURenderPipeline create_post_process_pipeline(WGPUDevice device, WGPURenderPipeline create_post_process_pipeline_simple(WGPUDevice device, WGPUTextureFormat format, const char* shader_code) { - // Headless mode: skip pipeline creation - if (device == nullptr) { - return nullptr; - } + // Headless mode: skip pipeline creation (compiled out in STRIP_ALL) + HEADLESS_RETURN_VAL_IF_NULL(device, nullptr); WGPUBindGroupLayout bgl = BindGroupLayoutBuilder() @@ -77,10 +74,9 @@ static GpuBuffer g_dummy_buffer = {nullptr, 0}; void pp_update_bind_group(WGPUDevice device, WGPURenderPipeline pipeline, WGPUBindGroup* bind_group, WGPUTextureView input_view, GpuBuffer uniforms, GpuBuffer effect_params) { - // Headless mode: skip bind group creation - if (device == nullptr || pipeline == nullptr) { - return; - } + // Headless mode: skip bind group creation (compiled out in STRIP_ALL) + HEADLESS_RETURN_IF_NULL(device); + HEADLESS_RETURN_IF_NULL(pipeline); if (!g_dummy_buffer.buffer) { g_dummy_buffer = gpu_create_buffer(device, 32, WGPUBufferUsage_Uniform); diff --git a/src/gpu/sampler_cache.h b/src/gpu/sampler_cache.h index b3fdb19..7d88109 100644 --- a/src/gpu/sampler_cache.h +++ b/src/gpu/sampler_cache.h @@ -9,6 +9,7 @@ struct WGPUSamplerImpl; typedef struct WGPUSamplerImpl* WGPUSampler; #include "platform/platform.h" +#include "util/fatal_error.h" struct SamplerSpec { WGPUAddressMode u, v; @@ -39,10 +40,8 @@ class SamplerCache { } WGPUSampler get_or_create(WGPUDevice device, const SamplerSpec& spec) { - // Headless mode: skip sampler creation - if (device == nullptr) { - return nullptr; - } + // Headless mode: skip sampler creation (compiled out in STRIP_ALL) + HEADLESS_RETURN_VAL_IF_NULL(device, nullptr); auto it = cache_.find(spec); if (it != cache_.end()) diff --git a/src/gpu/sequence.cc b/src/gpu/sequence.cc index 578fc01..62c2933 100644 --- a/src/gpu/sequence.cc +++ b/src/gpu/sequence.cc @@ -130,12 +130,10 @@ void NodeRegistry::set_external_view(const std::string& name, } void NodeRegistry::create_texture(Node& node) { - // Headless mode: skip texture creation - if (device_ == nullptr) { - node.texture = nullptr; - node.view = nullptr; - return; - } + // Headless mode: skip texture creation (compiled out in STRIP_ALL) + node.texture = nullptr; + node.view = nullptr; + HEADLESS_RETURN_IF_NULL(device_); WGPUTextureFormat format; WGPUTextureUsage usage; diff --git a/src/util/fatal_error.h b/src/util/fatal_error.h index c0f7c65..988ec1d 100644 --- a/src/util/fatal_error.h +++ b/src/util/fatal_error.h @@ -14,6 +14,38 @@ // // FINAL_STRIP implies STRIP_ALL (stricter superset) +#if defined(STRIP_ALL) || defined(FINAL_STRIP) + +// ============================================================================== +// STRIP_ALL / FINAL_STRIP: Headless checks compiled out (production builds) +// ============================================================================== + +// Headless mode nullptr checks - compiles to nothing in production +#define HEADLESS_RETURN_IF_NULL(device) ((void)0) +#define HEADLESS_RETURN_VAL_IF_NULL(device, val) ((void)0) + +#else + +// ============================================================================== +// Debug: Headless mode nullptr checks enabled +// ============================================================================== + +// Early return if device is nullptr (headless mode stub) +#define HEADLESS_RETURN_IF_NULL(device) \ + do { \ + if ((device) == nullptr) \ + return; \ + } while (0) + +// Early return with value if device is nullptr (headless mode stub) +#define HEADLESS_RETURN_VAL_IF_NULL(device, val) \ + do { \ + if ((device) == nullptr) \ + return (val); \ + } while (0) + +#endif /* defined(STRIP_ALL) || defined(FINAL_STRIP) */ + #if defined(FINAL_STRIP) // ============================================================================== |
