summaryrefslogtreecommitdiff
path: root/src/gpu
diff options
context:
space:
mode:
authorskal <pascal.massimino@gmail.com>2026-02-16 15:15:40 +0100
committerskal <pascal.massimino@gmail.com>2026-02-16 15:15:40 +0100
commit42de5c4e5aeb486b4afb5b73bcb6a9d5ba567cbd (patch)
treedb7febab2d795343cb7b9925b47d78ea911da303 /src/gpu
parentce2f70d30094cad87606f480a317328d60595bdc (diff)
fix(headless): add nullptr checks for GPU resource creation
- NodeRegistry::create_texture: skip texture creation when device is nullptr - Post-process helpers: skip pipeline/bind group creation in headless mode - SamplerCache: return nullptr for headless mode - BindGroupLayoutBuilder/BindGroupBuilder: skip creation with null device - RenderPipelineBuilder: skip shader module and pipeline creation in headless - PlaceholderEffect: skip sampler creation in headless mode - RotatingCubeEffect: early return in constructor for headless mode WIP: Some effects (GaussianBlur, etc.) still need headless checks Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Diffstat (limited to 'src/gpu')
-rw-r--r--src/gpu/bind_group_builder.h8
-rw-r--r--src/gpu/headless_gpu.cc51
-rw-r--r--src/gpu/pipeline_builder.h24
-rw-r--r--src/gpu/post_process_helper.cc15
-rw-r--r--src/gpu/sampler_cache.h5
-rw-r--r--src/gpu/sequence.cc7
6 files changed, 102 insertions, 8 deletions
diff --git a/src/gpu/bind_group_builder.h b/src/gpu/bind_group_builder.h
index 55b7291..3b25ba9 100644
--- a/src/gpu/bind_group_builder.h
+++ b/src/gpu/bind_group_builder.h
@@ -93,6 +93,10 @@ class BindGroupLayoutBuilder {
}
WGPUBindGroupLayout build(WGPUDevice device) {
+ // Headless mode: skip bind group layout creation
+ if (device == nullptr) {
+ return nullptr;
+ }
WGPUBindGroupLayoutDescriptor desc{};
desc.entryCount = entries_.size();
desc.entries = entries_.data();
@@ -130,6 +134,10 @@ class BindGroupBuilder {
}
WGPUBindGroup build(WGPUDevice device, WGPUBindGroupLayout layout) {
+ // Headless mode: skip bind group creation
+ if (device == nullptr) {
+ return nullptr;
+ }
WGPUBindGroupDescriptor desc{};
desc.layout = layout;
desc.entryCount = entries_.size();
diff --git a/src/gpu/headless_gpu.cc b/src/gpu/headless_gpu.cc
index c4de577..e6995b2 100644
--- a/src/gpu/headless_gpu.cc
+++ b/src/gpu/headless_gpu.cc
@@ -78,6 +78,57 @@ WGPUSurface gpu_get_surface() {
return nullptr;
}
+TextureWithView gpu_create_texture_2d(WGPUDevice device, uint32_t width,
+ uint32_t height, WGPUTextureFormat format,
+ WGPUTextureUsage usage,
+ uint32_t mip_levels) {
+ (void)device;
+ (void)width;
+ (void)height;
+ (void)format;
+ (void)usage;
+ (void)mip_levels;
+ return {nullptr, nullptr};
+}
+
+TextureWithView gpu_create_storage_texture_2d(WGPUDevice device, uint32_t width,
+ uint32_t height,
+ WGPUTextureFormat format) {
+ (void)device;
+ (void)width;
+ (void)height;
+ (void)format;
+ return {nullptr, nullptr};
+}
+
+TextureWithView gpu_create_post_process_texture(WGPUDevice device,
+ uint32_t width, uint32_t height,
+ WGPUTextureFormat format) {
+ (void)device;
+ (void)width;
+ (void)height;
+ (void)format;
+ return {nullptr, nullptr};
+}
+
+WGPUTextureView gpu_create_mip_view(WGPUTexture texture,
+ WGPUTextureFormat format,
+ uint32_t mip_level) {
+ (void)texture;
+ (void)format;
+ (void)mip_level;
+ return nullptr;
+}
+
+WGPUTextureView gpu_create_texture_view_2d(WGPUTexture texture,
+ WGPUTextureFormat format,
+ uint32_t mip_levels) {
+ (void)texture;
+ (void)format;
+ (void)mip_levels;
+ return nullptr;
+}
+
#if !defined(STRIP_ALL)
void gpu_simulate_until(float time, float bpm) {
(void)time;
diff --git a/src/gpu/pipeline_builder.h b/src/gpu/pipeline_builder.h
index 87b9190..a258b49 100644
--- a/src/gpu/pipeline_builder.h
+++ b/src/gpu/pipeline_builder.h
@@ -38,14 +38,17 @@ class RenderPipelineBuilder {
RenderPipelineBuilder& shader(const char* wgsl, bool compose = true) {
shader_text_ = compose ? ShaderComposer::Get().Compose({}, wgsl) : wgsl;
- 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
+ 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");
+ }
return *this;
}
@@ -85,6 +88,11 @@ class RenderPipelineBuilder {
}
WGPURenderPipeline build() {
+ // Headless mode: skip pipeline creation
+ if (device_ == nullptr) {
+ return nullptr;
+ }
+
color_.writeMask = WGPUColorWriteMask_All;
if (has_blend_)
color_.blend = &blend_;
diff --git a/src/gpu/post_process_helper.cc b/src/gpu/post_process_helper.cc
index 5f2ff56..6026f6e 100644
--- a/src/gpu/post_process_helper.cc
+++ b/src/gpu/post_process_helper.cc
@@ -14,6 +14,11 @@
WGPURenderPipeline create_post_process_pipeline(WGPUDevice device,
WGPUTextureFormat format,
const char* shader_code) {
+ // Headless mode: skip pipeline creation
+ if (device == nullptr) {
+ return nullptr;
+ }
+
WGPUBindGroupLayout bgl =
BindGroupLayoutBuilder()
.sampler(PP_BINDING_SAMPLER, WGPUShaderStage_Fragment)
@@ -40,6 +45,11 @@ 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;
+ }
+
WGPUBindGroupLayout bgl =
BindGroupLayoutBuilder()
.sampler(PP_BINDING_SAMPLER, WGPUShaderStage_Fragment)
@@ -67,6 +77,11 @@ 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;
+ }
+
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 7149921..b3fdb19 100644
--- a/src/gpu/sampler_cache.h
+++ b/src/gpu/sampler_cache.h
@@ -39,6 +39,11 @@ class SamplerCache {
}
WGPUSampler get_or_create(WGPUDevice device, const SamplerSpec& spec) {
+ // Headless mode: skip sampler creation
+ if (device == nullptr) {
+ return nullptr;
+ }
+
auto it = cache_.find(spec);
if (it != cache_.end())
return it->second;
diff --git a/src/gpu/sequence.cc b/src/gpu/sequence.cc
index 38a6866..578fc01 100644
--- a/src/gpu/sequence.cc
+++ b/src/gpu/sequence.cc
@@ -130,6 +130,13 @@ 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;
+ }
+
WGPUTextureFormat format;
WGPUTextureUsage usage;