summaryrefslogtreecommitdiff
path: root/src/gpu
diff options
context:
space:
mode:
Diffstat (limited to 'src/gpu')
-rw-r--r--src/gpu/demo_effects.h55
-rw-r--r--src/gpu/effect.h5
-rw-r--r--src/gpu/effects/chroma_aberration_effect.cc1
-rw-r--r--src/gpu/effects/circle_mask_effect.cc24
-rw-r--r--src/gpu/effects/circle_mask_effect.h8
-rw-r--r--src/gpu/effects/distort_effect.cc26
-rw-r--r--src/gpu/effects/fade_effect.cc19
-rw-r--r--src/gpu/effects/fade_effect.h3
-rw-r--r--src/gpu/effects/flash_cube_effect.h2
-rw-r--r--src/gpu/effects/gaussian_blur_effect.cc1
-rw-r--r--src/gpu/effects/heptagon_effect.cc24
-rw-r--r--src/gpu/effects/moving_ellipse_effect.cc9
-rw-r--r--src/gpu/effects/particle_spray_effect.cc1
-rw-r--r--src/gpu/effects/particles_effect.cc1
-rw-r--r--src/gpu/effects/passthrough_effect.cc1
-rw-r--r--src/gpu/effects/post_process_helper.cc8
-rw-r--r--src/gpu/effects/post_process_helper.h8
-rw-r--r--src/gpu/effects/shaders.cc22
-rw-r--r--src/gpu/effects/shaders.h7
-rw-r--r--src/gpu/effects/solarize_effect.cc5
-rw-r--r--src/gpu/effects/theme_modulation_effect.cc19
-rw-r--r--src/gpu/effects/theme_modulation_effect.h3
-rw-r--r--src/gpu/effects/vignette_effect.cc5
-rw-r--r--src/gpu/gpu.cc11
-rw-r--r--src/gpu/stub_gpu.cc83
-rw-r--r--src/gpu/texture_manager.cc586
-rw-r--r--src/gpu/texture_manager.h68
-rw-r--r--src/gpu/uniform_helper.h1
28 files changed, 879 insertions, 127 deletions
diff --git a/src/gpu/demo_effects.h b/src/gpu/demo_effects.h
index 54bf657..ff7e017 100644
--- a/src/gpu/demo_effects.h
+++ b/src/gpu/demo_effects.h
@@ -7,12 +7,14 @@
#include "3d/scene.h"
#include "effect.h"
#include "gpu/effects/circle_mask_effect.h"
-#include "gpu/effects/fade_effect.h" // FadeEffect with full definition
+#include "gpu/effects/fade_effect.h" // FadeEffect with full definition
#include "gpu/effects/flash_effect.h" // FlashEffect with params support
#include "gpu/effects/post_process_helper.h"
#include "gpu/effects/rotating_cube_effect.h"
#include "gpu/effects/shaders.h"
#include "gpu/effects/theme_modulation_effect.h" // ThemeModulationEffect with full definition
+#include "gpu/effects/hybrid_3d_effect.h"
+#include "gpu/effects/flash_cube_effect.h"
#include "gpu/gpu.h"
#include "gpu/texture_manager.h"
#include "gpu/uniform_helper.h"
@@ -49,7 +51,6 @@ class ParticlesEffect : public Effect {
ComputePass compute_pass_;
RenderPass render_pass_;
GpuBuffer particles_buffer_;
- UniformBuffer<CommonPostProcessUniforms> uniforms_;
};
class PassthroughEffect : public PostProcessEffect {
@@ -58,7 +59,6 @@ class PassthroughEffect : public PostProcessEffect {
void update_bind_group(WGPUTextureView input_view) override;
private:
- UniformBuffer<CommonPostProcessUniforms> uniforms_;
};
class MovingEllipseEffect : public Effect {
@@ -83,7 +83,6 @@ class ParticleSprayEffect : public Effect {
ComputePass compute_pass_;
RenderPass render_pass_;
GpuBuffer particles_buffer_;
- UniformBuffer<CommonPostProcessUniforms> uniforms_;
};
// Parameters for GaussianBlurEffect (set at construction time)
@@ -106,7 +105,6 @@ class GaussianBlurEffect : public PostProcessEffect {
private:
GaussianBlurParams params_;
- UniformBuffer<CommonPostProcessUniforms> uniforms_;
UniformBuffer<GaussianBlurParams> params_buffer_;
};
@@ -118,7 +116,6 @@ class SolarizeEffect : public PostProcessEffect {
void update_bind_group(WGPUTextureView input_view) override;
private:
- UniformBuffer<CommonPostProcessUniforms> uniforms_;
};
// Parameters for VignetteEffect
@@ -137,7 +134,6 @@ class VignetteEffect : public PostProcessEffect {
private:
VignetteParams params_;
- UniformBuffer<CommonPostProcessUniforms> uniforms_;
UniformBuffer<VignetteParams> params_buffer_;
};
@@ -160,48 +156,33 @@ class ChromaAberrationEffect : public PostProcessEffect {
private:
ChromaAberrationParams params_;
- UniformBuffer<CommonPostProcessUniforms> uniforms_;
UniformBuffer<ChromaAberrationParams> params_buffer_;
};
-class Hybrid3DEffect : public Effect {
- public:
- Hybrid3DEffect(const GpuContext& ctx);
- void init(MainSequence* demo) override;
- void render(WGPURenderPassEncoder pass, float time, float beat,
- float intensity, float aspect_ratio) override;
-
- private:
- Renderer3D renderer_;
- TextureManager texture_manager_;
- Scene scene_;
- Camera camera_;
- int width_ = 1280;
- int height_ = 720;
+// Parameters for DistortEffect
+struct DistortParams {
+ float strength = 0.01f; // Default distortion strength
+ float speed = 1.0f; // Default distortion speed
};
+static_assert(sizeof(DistortParams) == 8, "DistortParams must be 8 bytes for WGSL alignment");
-class FlashCubeEffect : public Effect {
+class DistortEffect : public PostProcessEffect {
public:
- FlashCubeEffect(const GpuContext& ctx);
- void init(MainSequence* demo) override;
- void resize(int width, int height) override;
+ DistortEffect(const GpuContext& ctx);
+ DistortEffect(const GpuContext& ctx, const DistortParams& params);
void render(WGPURenderPassEncoder pass, float time, float beat,
float intensity, float aspect_ratio) override;
+ void update_bind_group(WGPUTextureView input_view) override;
private:
- Renderer3D renderer_;
- TextureManager texture_manager_;
- Scene scene_;
- Camera camera_;
- int width_ = 1280;
- int height_ = 720;
- float last_beat_;
- float flash_intensity_;
+ DistortParams params_;
+ UniformBuffer<DistortParams> params_buffer_;
};
-// ThemeModulationEffect now defined in gpu/effects/theme_modulation_effect.h (included above)
-// FadeEffect now defined in gpu/effects/fade_effect.h (included above)
-// FlashEffect now defined in gpu/effects/flash_effect.h (included above)
+// ThemeModulationEffect now defined in gpu/effects/theme_modulation_effect.h
+// (included above) FadeEffect now defined in gpu/effects/fade_effect.h
+// (included above) FlashEffect now defined in gpu/effects/flash_effect.h
+// (included above)
// Auto-generated functions
void LoadTimeline(MainSequence& main_seq, const GpuContext& ctx);
diff --git a/src/gpu/effect.h b/src/gpu/effect.h
index 6fdb0f4..8f35f3c 100644
--- a/src/gpu/effect.h
+++ b/src/gpu/effect.h
@@ -1,5 +1,7 @@
#pragma once
#include "gpu/gpu.h"
+#include "gpu/effects/post_process_helper.h"
+#include "gpu/uniform_helper.h"
#include <algorithm>
#include <map>
#include <memory>
@@ -12,6 +14,7 @@ class PostProcessEffect;
class Effect {
public:
Effect(const GpuContext& ctx) : ctx_(ctx) {
+ uniforms_.init(ctx.device);
}
virtual ~Effect() = default;
virtual void init(MainSequence* demo) {
@@ -43,7 +46,7 @@ class Effect {
protected:
const GpuContext& ctx_;
- GpuBuffer uniforms_;
+ UniformBuffer<CommonPostProcessUniforms> uniforms_;
int width_ = 1280;
int height_ = 720;
};
diff --git a/src/gpu/effects/chroma_aberration_effect.cc b/src/gpu/effects/chroma_aberration_effect.cc
index 7f41153..af3acc5 100644
--- a/src/gpu/effects/chroma_aberration_effect.cc
+++ b/src/gpu/effects/chroma_aberration_effect.cc
@@ -18,7 +18,6 @@ ChromaAberrationEffect::ChromaAberrationEffect(
: PostProcessEffect(ctx), params_(params) {
pipeline_ = create_post_process_pipeline(ctx_.device, ctx_.format,
chroma_aberration_shader_wgsl);
- uniforms_.init(ctx_.device);
params_buffer_.init(ctx_.device);
}
diff --git a/src/gpu/effects/circle_mask_effect.cc b/src/gpu/effects/circle_mask_effect.cc
index 5b71086..ca80cf9 100644
--- a/src/gpu/effects/circle_mask_effect.cc
+++ b/src/gpu/effects/circle_mask_effect.cc
@@ -3,6 +3,7 @@
// Generates circular mask and renders green background outside circle.
#include "gpu/effects/circle_mask_effect.h"
+#include "gpu/effects/shader_composer.h"
#include "generated/assets.h"
CircleMaskEffect::CircleMaskEffect(const GpuContext& ctx, float radius)
@@ -30,9 +31,7 @@ void CircleMaskEffect::init(MainSequence* demo) {
demo_->register_auxiliary_texture("circle_mask", width, height);
- compute_uniforms_.init(ctx_.device);
compute_params_.init(ctx_.device);
- render_uniforms_.init(ctx_.device);
WGPUSamplerDescriptor sampler_desc = {};
sampler_desc.addressModeU = WGPUAddressMode_ClampToEdge;
@@ -49,9 +48,12 @@ void CircleMaskEffect::init(MainSequence* demo) {
const char* render_shader = (const char*)GetAsset(
AssetId::ASSET_CIRCLE_MASK_RENDER_SHADER, &render_size);
+ // Compose shaders to resolve #include directives
+ std::string composed_compute = ShaderComposer::Get().Compose({}, compute_shader);
+
WGPUShaderSourceWGSL compute_wgsl = {};
compute_wgsl.chain.sType = WGPUSType_ShaderSourceWGSL;
- compute_wgsl.code = str_view(compute_shader);
+ compute_wgsl.code = str_view(composed_compute.c_str());
WGPUShaderModuleDescriptor compute_desc = {};
compute_desc.nextInChain = &compute_wgsl.chain;
@@ -82,11 +84,11 @@ void CircleMaskEffect::init(MainSequence* demo) {
const WGPUBindGroupEntry compute_entries[] = {
{.binding = 0,
- .buffer = compute_uniforms_.get().buffer,
+ .buffer = uniforms_.get().buffer,
.size = sizeof(CommonPostProcessUniforms)},
{.binding = 1,
.buffer = compute_params_.get().buffer,
- .size = sizeof(EffectParams)},
+ .size = sizeof(CircleMaskParams)},
};
const WGPUBindGroupDescriptor compute_bg_desc = {
.layout = wgpuRenderPipelineGetBindGroupLayout(compute_pipeline_, 0),
@@ -96,9 +98,11 @@ void CircleMaskEffect::init(MainSequence* demo) {
compute_bind_group_ =
wgpuDeviceCreateBindGroup(ctx_.device, &compute_bg_desc);
+ std::string composed_render = ShaderComposer::Get().Compose({}, render_shader);
+
WGPUShaderSourceWGSL render_wgsl = {};
render_wgsl.chain.sType = WGPUSType_ShaderSourceWGSL;
- render_wgsl.code = str_view(render_shader);
+ render_wgsl.code = str_view(composed_render.c_str());
WGPUShaderModuleDescriptor render_desc = {};
render_desc.nextInChain = &render_wgsl.chain;
@@ -139,7 +143,7 @@ void CircleMaskEffect::init(MainSequence* demo) {
{.binding = 0, .textureView = mask_view},
{.binding = 1, .sampler = mask_sampler_},
{.binding = 2,
- .buffer = render_uniforms_.get().buffer,
+ .buffer = uniforms_.get().buffer,
.size = sizeof(CommonPostProcessUniforms)},
};
const WGPUBindGroupDescriptor render_bg_desc = {
@@ -160,9 +164,9 @@ void CircleMaskEffect::compute(WGPUCommandEncoder encoder, float time,
.beat = beat,
.audio_intensity = intensity,
};
- compute_uniforms_.update(ctx_.queue, uniforms);
+ uniforms_.update(ctx_.queue, uniforms);
- const EffectParams params = {
+ const CircleMaskParams params = {
.radius = radius_,
};
compute_params_.update(ctx_.queue, params);
@@ -199,7 +203,7 @@ void CircleMaskEffect::render(WGPURenderPassEncoder pass, float time,
.beat = beat,
.audio_intensity = intensity,
};
- render_uniforms_.update(ctx_.queue, uniforms);
+ uniforms_.update(ctx_.queue, uniforms);
wgpuRenderPassEncoderSetPipeline(pass, render_pipeline_);
wgpuRenderPassEncoderSetBindGroup(pass, 0, render_bind_group_, 0, nullptr);
diff --git a/src/gpu/effects/circle_mask_effect.h b/src/gpu/effects/circle_mask_effect.h
index ac44210..2ddbb11 100644
--- a/src/gpu/effects/circle_mask_effect.h
+++ b/src/gpu/effects/circle_mask_effect.h
@@ -21,23 +21,23 @@ class CircleMaskEffect : public Effect {
float intensity, float aspect_ratio) override;
private:
- struct EffectParams {
+ struct CircleMaskParams {
float radius;
float _pad[3];
};
+ static_assert(sizeof(CircleMaskParams) == 16,
+ "CircleMaskParams must be 16 bytes for WGSL alignment");
MainSequence* demo_ = nullptr;
float radius_;
WGPURenderPipeline compute_pipeline_ = nullptr;
WGPUBindGroup compute_bind_group_ = nullptr;
- UniformBuffer<CommonPostProcessUniforms> compute_uniforms_;
- UniformBuffer<EffectParams> compute_params_;
+ UniformBuffer<CircleMaskParams> compute_params_;
WGPURenderPipeline render_pipeline_ = nullptr;
WGPUBindGroup render_bind_group_ = nullptr;
WGPUSampler mask_sampler_ = nullptr;
- UniformBuffer<CommonPostProcessUniforms> render_uniforms_;
};
#endif /* CIRCLE_MASK_EFFECT_H_ */
diff --git a/src/gpu/effects/distort_effect.cc b/src/gpu/effects/distort_effect.cc
index d11dfd7..52a8ec7 100644
--- a/src/gpu/effects/distort_effect.cc
+++ b/src/gpu/effects/distort_effect.cc
@@ -9,31 +9,35 @@ DistortEffect::DistortEffect(const GpuContext& ctx)
: DistortEffect(ctx, DistortParams()) {
}
-DistortEffect::DistEffect(const GpuContext& ctx, const DistortParams& params)
+DistortEffect::DistortEffect(const GpuContext& ctx, const DistortParams& params)
: PostProcessEffect(ctx), params_(params) {
- uniforms_ =
- gpu_create_buffer(ctx_.device, sizeof(DistortUniforms),
- WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst);
+ params_buffer_.init(ctx_.device);
pipeline_ = create_post_process_pipeline(ctx_.device, ctx_.format,
distort_shader_wgsl);
}
void DistortEffect::render(WGPURenderPassEncoder pass, float t, float b,
float i, float a) {
- DistortUniforms u = {
+ // Populate CommonPostProcessUniforms
+ const CommonPostProcessUniforms common_u = {
+ .resolution = {(float)width_, (float)height_},
+ .aspect_ratio = a,
.time = t,
.beat = b,
- .intensity = i,
- .aspect_ratio = a,
- .width = (float)width_,
- .height = (float)height_,
+ .audio_intensity = i,
+ };
+ uniforms_.update(ctx_.queue, common_u);
+
+ // Populate DistortParams
+ const DistortParams distort_p = {
.strength = params_.strength,
.speed = params_.speed,
};
- wgpuQueueWriteBuffer(ctx_.queue, uniforms_.buffer, 0, &u, sizeof(u));
+ params_buffer_.update(ctx_.queue, distort_p);
+
PostProcessEffect::render(pass, t, b, i, a);
}
void DistortEffect::update_bind_group(WGPUTextureView v) {
- pp_update_bind_group(ctx_.device, pipeline_, &bind_group_, v, {}, uniforms_);
+ pp_update_bind_group(ctx_.device, pipeline_, &bind_group_, v, uniforms_.get(), params_buffer_);
} \ No newline at end of file
diff --git a/src/gpu/effects/fade_effect.cc b/src/gpu/effects/fade_effect.cc
index 3efc583..39b54e0 100644
--- a/src/gpu/effects/fade_effect.cc
+++ b/src/gpu/effects/fade_effect.cc
@@ -5,6 +5,12 @@
#include "gpu/effects/post_process_helper.h"
#include <cmath>
+struct FadeParams {
+ float fade_amount;
+ float _pad[3];
+};
+static_assert(sizeof(FadeParams) == 16, "FadeParams must be 16 bytes for WGSL alignment");
+
FadeEffect::FadeEffect(const GpuContext& ctx) : PostProcessEffect(ctx) {
const char* shader_code = R"(
struct VertexOutput {
@@ -22,7 +28,7 @@ FadeEffect::FadeEffect(const GpuContext& ctx) : PostProcessEffect(ctx) {
audio_intensity: f32,
};
- struct EffectParams {
+ struct FadeParams {
fade_amount: f32,
_pad0: f32,
_pad1: f32,
@@ -32,7 +38,7 @@ FadeEffect::FadeEffect(const GpuContext& ctx) : PostProcessEffect(ctx) {
@group(0) @binding(0) var inputSampler: sampler;
@group(0) @binding(1) var inputTexture: texture_2d<f32>;
@group(0) @binding(2) var<uniform> uniforms: CommonUniforms;
- @group(0) @binding(3) var<uniform> params: EffectParams;
+ @group(0) @binding(3) var<uniform> params: FadeParams;
@vertex
fn vs_main(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput {
@@ -57,14 +63,13 @@ FadeEffect::FadeEffect(const GpuContext& ctx) : PostProcessEffect(ctx) {
pipeline_ =
create_post_process_pipeline(ctx_.device, ctx_.format, shader_code);
- common_uniforms_.init(ctx_.device);
params_buffer_ = gpu_create_buffer(
ctx_.device, 16, WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst);
}
void FadeEffect::update_bind_group(WGPUTextureView input_view) {
pp_update_bind_group(ctx_.device, pipeline_, &bind_group_, input_view,
- common_uniforms_.get(), params_buffer_);
+ uniforms_.get(), params_buffer_);
}
void FadeEffect::render(WGPURenderPassEncoder pass, float time, float beat,
@@ -76,7 +81,7 @@ void FadeEffect::render(WGPURenderPassEncoder pass, float time, float beat,
.beat = beat,
.audio_intensity = intensity,
};
- common_uniforms_.update(ctx_.queue, u);
+ uniforms_.update(ctx_.queue, u);
// Example fade pattern: fade in at start, fade out at end
// Customize this based on your needs
@@ -90,8 +95,8 @@ void FadeEffect::render(WGPURenderPassEncoder pass, float time, float beat,
fade_amount = fmaxf(fade_amount, 0.0f);
}
- float params[4] = {fade_amount, 0.0f, 0.0f, 0.0f};
- wgpuQueueWriteBuffer(ctx_.queue, params_buffer_.buffer, 0, params,
+ FadeParams params = {fade_amount, {0.0f, 0.0f, 0.0f}};
+ wgpuQueueWriteBuffer(ctx_.queue, params_buffer_.buffer, 0, &params,
sizeof(params));
wgpuRenderPassEncoderSetPipeline(pass, pipeline_);
diff --git a/src/gpu/effects/fade_effect.h b/src/gpu/effects/fade_effect.h
index 22b8f76..178c360 100644
--- a/src/gpu/effects/fade_effect.h
+++ b/src/gpu/effects/fade_effect.h
@@ -4,9 +4,9 @@
#pragma once
#include "gpu/effect.h"
+#include "gpu/effects/post_process_helper.h"
#include "gpu/gpu.h"
#include "gpu/uniform_helper.h"
-#include "gpu/effects/post_process_helper.h"
class FadeEffect : public PostProcessEffect {
public:
@@ -16,6 +16,5 @@ class FadeEffect : public PostProcessEffect {
void update_bind_group(WGPUTextureView input_view) override;
private:
- UniformBuffer<CommonPostProcessUniforms> common_uniforms_;
GpuBuffer params_buffer_;
};
diff --git a/src/gpu/effects/flash_cube_effect.h b/src/gpu/effects/flash_cube_effect.h
index 7089af2..5faeb00 100644
--- a/src/gpu/effects/flash_cube_effect.h
+++ b/src/gpu/effects/flash_cube_effect.h
@@ -22,8 +22,6 @@ class FlashCubeEffect : public Effect {
TextureManager texture_manager_;
Scene scene_;
Camera camera_;
- int width_ = 1280;
- int height_ = 720;
float last_beat_ = 0.0f;
float flash_intensity_ = 0.0f;
};
diff --git a/src/gpu/effects/gaussian_blur_effect.cc b/src/gpu/effects/gaussian_blur_effect.cc
index 0cc4821..697be88 100644
--- a/src/gpu/effects/gaussian_blur_effect.cc
+++ b/src/gpu/effects/gaussian_blur_effect.cc
@@ -18,7 +18,6 @@ GaussianBlurEffect::GaussianBlurEffect(const GpuContext& ctx,
: PostProcessEffect(ctx), params_(params) {
pipeline_ = create_post_process_pipeline(ctx_.device, ctx_.format,
gaussian_blur_shader_wgsl);
- uniforms_.init(ctx_.device);
params_buffer_.init(ctx_.device);
}
diff --git a/src/gpu/effects/heptagon_effect.cc b/src/gpu/effects/heptagon_effect.cc
index b77ec53..7b0702d 100644
--- a/src/gpu/effects/heptagon_effect.cc
+++ b/src/gpu/effects/heptagon_effect.cc
@@ -5,39 +5,25 @@
#include "gpu/gpu.h"
#include "util/mini_math.h"
-// Match CommonUniforms struct from main_shader.wgsl.
-// Padded to 32 bytes for WGSL alignment rules.
-struct HeptagonUniforms {
- vec2 resolution; // 8 bytes
- float _pad0[2]; // 8 bytes padding to align next float
- float aspect_ratio; // 4 bytes
- float time; // 4 bytes
- float beat; // 4 bytes
- float audio_intensity; // 4 bytes
-};
-static_assert(sizeof(HeptagonUniforms) == 32,
- "HeptagonUniforms must be 32 bytes for WGSL alignment");
-
// --- HeptagonEffect ---
HeptagonEffect::HeptagonEffect(const GpuContext& ctx) : Effect(ctx) {
- uniforms_ =
- gpu_create_buffer(ctx_.device, sizeof(HeptagonUniforms),
- WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst);
- ResourceBinding bindings[] = {{uniforms_, WGPUBufferBindingType_Uniform}};
+ // uniforms_ is initialized by Effect base class
+ ResourceBinding bindings[] = {{uniforms_.get(), WGPUBufferBindingType_Uniform}};
pass_ = gpu_create_render_pass(ctx_.device, ctx_.format, main_shader_wgsl,
bindings, 1);
pass_.vertex_count = 21;
}
void HeptagonEffect::render(WGPURenderPassEncoder pass, float t, float b,
float i, float a) {
- HeptagonUniforms u = {
+ CommonPostProcessUniforms u = {
.resolution = {(float)width_, (float)height_},
+ ._pad = {0.0f, 0.0f},
.aspect_ratio = a,
.time = t,
.beat = b,
.audio_intensity = i,
};
- wgpuQueueWriteBuffer(ctx_.queue, uniforms_.buffer, 0, &u, sizeof(u));
+ uniforms_.update(ctx_.queue, u);
wgpuRenderPassEncoderSetPipeline(pass, pass_.pipeline);
wgpuRenderPassEncoderSetBindGroup(pass, 0, pass_.bind_group, 0, nullptr);
wgpuRenderPassEncoderDraw(pass, pass_.vertex_count, 1, 0, 0);
diff --git a/src/gpu/effects/moving_ellipse_effect.cc b/src/gpu/effects/moving_ellipse_effect.cc
index 945f807..9866f20 100644
--- a/src/gpu/effects/moving_ellipse_effect.cc
+++ b/src/gpu/effects/moving_ellipse_effect.cc
@@ -7,10 +7,8 @@
// --- MovingEllipseEffect ---
MovingEllipseEffect::MovingEllipseEffect(const GpuContext& ctx) : Effect(ctx) {
- uniforms_ =
- gpu_create_buffer(ctx_.device, sizeof(CommonPostProcessUniforms),
- WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst);
- ResourceBinding bindings[] = {{uniforms_, WGPUBufferBindingType_Uniform}};
+ // uniforms_ is initialized by Effect base class
+ ResourceBinding bindings[] = {{uniforms_.get(), WGPUBufferBindingType_Uniform}};
pass_ = gpu_create_render_pass(ctx_.device, ctx_.format, ellipse_shader_wgsl,
bindings, 1);
pass_.vertex_count = 3;
@@ -19,12 +17,13 @@ void MovingEllipseEffect::render(WGPURenderPassEncoder pass, float t, float b,
float i, float a) {
const CommonPostProcessUniforms u = {
.resolution = {(float)width_, (float)height_},
+ ._pad = {0.0f, 0.0f},
.aspect_ratio = a,
.time = t,
.beat = b,
.audio_intensity = i,
};
- wgpuQueueWriteBuffer(ctx_.queue, uniforms_.buffer, 0, &u, sizeof(u));
+ uniforms_.update(ctx_.queue, u);
wgpuRenderPassEncoderSetPipeline(pass, pass_.pipeline);
wgpuRenderPassEncoderSetBindGroup(pass, 0, pass_.bind_group, 0, nullptr);
wgpuRenderPassEncoderDraw(pass, 3, 1, 0, 0);
diff --git a/src/gpu/effects/particle_spray_effect.cc b/src/gpu/effects/particle_spray_effect.cc
index 3fd2590..a435884 100644
--- a/src/gpu/effects/particle_spray_effect.cc
+++ b/src/gpu/effects/particle_spray_effect.cc
@@ -8,7 +8,6 @@
// --- ParticleSprayEffect ---
ParticleSprayEffect::ParticleSprayEffect(const GpuContext& ctx) : Effect(ctx) {
- uniforms_.init(ctx_.device);
std::vector<Particle> init_p(NUM_PARTICLES);
for (Particle& p : init_p)
p.pos[3] = 0.0f;
diff --git a/src/gpu/effects/particles_effect.cc b/src/gpu/effects/particles_effect.cc
index 01f90a5..cd0df74 100644
--- a/src/gpu/effects/particles_effect.cc
+++ b/src/gpu/effects/particles_effect.cc
@@ -8,7 +8,6 @@
// --- ParticlesEffect ---
ParticlesEffect::ParticlesEffect(const GpuContext& ctx) : Effect(ctx) {
- uniforms_.init(ctx_.device);
std::vector<Particle> init_p(NUM_PARTICLES);
particles_buffer_ = gpu_create_buffer(
ctx_.device, sizeof(Particle) * NUM_PARTICLES,
diff --git a/src/gpu/effects/passthrough_effect.cc b/src/gpu/effects/passthrough_effect.cc
index 93cf948..01d557a 100644
--- a/src/gpu/effects/passthrough_effect.cc
+++ b/src/gpu/effects/passthrough_effect.cc
@@ -7,7 +7,6 @@
// --- PassthroughEffect ---
PassthroughEffect::PassthroughEffect(const GpuContext& ctx)
: PostProcessEffect(ctx) {
- uniforms_.init(ctx_.device);
pipeline_ = create_post_process_pipeline(ctx_.device, ctx_.format,
passthrough_shader_wgsl);
}
diff --git a/src/gpu/effects/post_process_helper.cc b/src/gpu/effects/post_process_helper.cc
index 74e052d..e99467f 100644
--- a/src/gpu/effects/post_process_helper.cc
+++ b/src/gpu/effects/post_process_helper.cc
@@ -4,16 +4,19 @@
#include "post_process_helper.h"
#include "../demo_effects.h"
#include "gpu/gpu.h"
+#include "gpu/effects/shader_composer.h"
#include <cstring>
// Helper to create a standard post-processing pipeline
WGPURenderPipeline create_post_process_pipeline(WGPUDevice device,
WGPUTextureFormat format,
const char* shader_code) {
+ std::string composed_shader = ShaderComposer::Get().Compose({}, shader_code);
+
WGPUShaderModuleDescriptor shader_desc = {};
WGPUShaderSourceWGSL wgsl_src = {};
wgsl_src.chain.sType = WGPUSType_ShaderSourceWGSL;
- wgsl_src.code = str_view(shader_code);
+ wgsl_src.code = str_view(composed_shader.c_str());
shader_desc.nextInChain = &wgsl_src.chain;
WGPUShaderModule shader_module =
wgpuDeviceCreateShaderModule(device, &shader_desc);
@@ -94,7 +97,8 @@ void pp_update_bind_group(WGPUDevice device, WGPURenderPipeline pipeline,
bge[2].buffer = uniforms.buffer;
bge[2].size = uniforms.size;
bge[3].binding = PP_BINDING_EFFECT_PARAMS;
- bge[3].buffer = effect_params.buffer ? effect_params.buffer : g_dummy_buffer.buffer;
+ bge[3].buffer =
+ effect_params.buffer ? effect_params.buffer : g_dummy_buffer.buffer;
bge[3].size = effect_params.buffer ? effect_params.size : g_dummy_buffer.size;
WGPUBindGroupDescriptor bgd = {
.layout = bgl, .entryCount = 4, .entries = bge};
diff --git a/src/gpu/effects/post_process_helper.h b/src/gpu/effects/post_process_helper.h
index 77b184f..23cde0e 100644
--- a/src/gpu/effects/post_process_helper.h
+++ b/src/gpu/effects/post_process_helper.h
@@ -19,10 +19,10 @@ static_assert(sizeof(CommonPostProcessUniforms) == 32,
"CommonPostProcessUniforms must be 32 bytes for WGSL alignment");
// Standard post-process bind group layout (group 0):
-#define PP_BINDING_SAMPLER 0 // Sampler for input texture
-#define PP_BINDING_TEXTURE 1 // Input texture (previous render pass)
-#define PP_BINDING_UNIFORMS 2 // Custom uniforms buffer
-#define PP_BINDING_EFFECT_PARAMS 3 // Effect-specific parameters
+#define PP_BINDING_SAMPLER 0 // Sampler for input texture
+#define PP_BINDING_TEXTURE 1 // Input texture (previous render pass)
+#define PP_BINDING_UNIFORMS 2 // Custom uniforms buffer
+#define PP_BINDING_EFFECT_PARAMS 3 // Effect-specific parameters
// Helper to create a standard post-processing pipeline
// Uniforms are accessible to both vertex and fragment shaders
diff --git a/src/gpu/effects/shaders.cc b/src/gpu/effects/shaders.cc
index 2e1cfe5..625c5b6 100644
--- a/src/gpu/effects/shaders.cc
+++ b/src/gpu/effects/shaders.cc
@@ -99,6 +99,28 @@ const char* chroma_aberration_shader_wgsl =
SafeGetAsset(AssetId::ASSET_SHADER_CHROMA_ABERRATION);
+const char* gen_noise_compute_wgsl =
+
+ SafeGetAsset(AssetId::ASSET_SHADER_COMPUTE_GEN_NOISE);
+
+const char* gen_perlin_compute_wgsl =
+
+ SafeGetAsset(AssetId::ASSET_SHADER_COMPUTE_GEN_PERLIN);
+
+const char* gen_grid_compute_wgsl =
+
+ SafeGetAsset(AssetId::ASSET_SHADER_COMPUTE_GEN_GRID);
+
+#if !defined(STRIP_GPU_COMPOSITE)
+const char* gen_blend_compute_wgsl =
+
+ SafeGetAsset(AssetId::ASSET_SHADER_COMPUTE_GEN_BLEND);
+
+const char* gen_mask_compute_wgsl =
+
+ SafeGetAsset(AssetId::ASSET_SHADER_COMPUTE_GEN_MASK);
+#endif
+
const char* vignette_shader_wgsl =
SafeGetAsset(AssetId::ASSET_SHADER_VIGNETTE);
diff --git a/src/gpu/effects/shaders.h b/src/gpu/effects/shaders.h
index 50b4f32..68b8834 100644
--- a/src/gpu/effects/shaders.h
+++ b/src/gpu/effects/shaders.h
@@ -18,3 +18,10 @@ extern const char* solarize_shader_wgsl;
extern const char* distort_shader_wgsl;
extern const char* chroma_aberration_shader_wgsl;
extern const char* vignette_shader_wgsl;
+extern const char* gen_noise_compute_wgsl;
+extern const char* gen_perlin_compute_wgsl;
+extern const char* gen_grid_compute_wgsl;
+#if !defined(STRIP_GPU_COMPOSITE)
+extern const char* gen_blend_compute_wgsl;
+extern const char* gen_mask_compute_wgsl;
+#endif
diff --git a/src/gpu/effects/solarize_effect.cc b/src/gpu/effects/solarize_effect.cc
index d74d708..4f47218 100644
--- a/src/gpu/effects/solarize_effect.cc
+++ b/src/gpu/effects/solarize_effect.cc
@@ -6,7 +6,6 @@
// --- SolarizeEffect ---
SolarizeEffect::SolarizeEffect(const GpuContext& ctx) : PostProcessEffect(ctx) {
- uniforms_.init(ctx.device);
pipeline_ = create_post_process_pipeline(ctx_.device, ctx_.format,
solarize_shader_wgsl);
}
@@ -23,6 +22,6 @@ void SolarizeEffect::render(WGPURenderPassEncoder pass, float t, float b,
PostProcessEffect::render(pass, t, b, i, a);
}
void SolarizeEffect::update_bind_group(WGPUTextureView v) {
- pp_update_bind_group(ctx_.device, pipeline_, &bind_group_, v,
- uniforms_.get(), {});
+ pp_update_bind_group(ctx_.device, pipeline_, &bind_group_, v, uniforms_.get(),
+ {});
}
diff --git a/src/gpu/effects/theme_modulation_effect.cc b/src/gpu/effects/theme_modulation_effect.cc
index f9ae636..b1eff90 100644
--- a/src/gpu/effects/theme_modulation_effect.cc
+++ b/src/gpu/effects/theme_modulation_effect.cc
@@ -6,6 +6,12 @@
#include "gpu/effects/shaders.h"
#include <cmath>
+struct ThemeModulationParams {
+ float theme_brightness;
+ float _pad[3];
+};
+static_assert(sizeof(ThemeModulationParams) == 16, "ThemeModulationParams must be 16 bytes for WGSL alignment");
+
ThemeModulationEffect::ThemeModulationEffect(const GpuContext& ctx)
: PostProcessEffect(ctx) {
const char* shader_code = R"(
@@ -24,7 +30,7 @@ ThemeModulationEffect::ThemeModulationEffect(const GpuContext& ctx)
audio_intensity: f32,
};
- struct EffectParams {
+ struct ThemeModulationParams {
theme_brightness: f32,
_pad0: f32,
_pad1: f32,
@@ -34,7 +40,7 @@ ThemeModulationEffect::ThemeModulationEffect(const GpuContext& ctx)
@group(0) @binding(0) var inputSampler: sampler;
@group(0) @binding(1) var inputTexture: texture_2d<f32>;
@group(0) @binding(2) var<uniform> uniforms: CommonUniforms;
- @group(0) @binding(3) var<uniform> params: EffectParams;
+ @group(0) @binding(3) var<uniform> params: ThemeModulationParams;
@vertex
fn vs_main(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput {
@@ -61,14 +67,13 @@ ThemeModulationEffect::ThemeModulationEffect(const GpuContext& ctx)
pipeline_ =
create_post_process_pipeline(ctx_.device, ctx_.format, shader_code);
- common_uniforms_.init(ctx_.device);
params_buffer_ = gpu_create_buffer(
ctx_.device, 16, WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst);
}
void ThemeModulationEffect::update_bind_group(WGPUTextureView input_view) {
pp_update_bind_group(ctx_.device, pipeline_, &bind_group_, input_view,
- common_uniforms_.get(), params_buffer_);
+ uniforms_.get(), params_buffer_);
}
void ThemeModulationEffect::render(WGPURenderPassEncoder pass, float time,
@@ -81,7 +86,7 @@ void ThemeModulationEffect::render(WGPURenderPassEncoder pass, float time,
.beat = beat,
.audio_intensity = intensity,
};
- common_uniforms_.update(ctx_.queue, u);
+ uniforms_.update(ctx_.queue, u);
// Alternate between bright and dark every 4 seconds (2 pattern changes)
// Music patterns change every 2 seconds at 120 BPM
@@ -97,8 +102,8 @@ void ThemeModulationEffect::render(WGPURenderPassEncoder pass, float time,
bright_value + (dark_value - bright_value) * transition;
// Update params buffer
- float params[4] = {theme_brightness, 0.0f, 0.0f, 0.0f};
- wgpuQueueWriteBuffer(ctx_.queue, params_buffer_.buffer, 0, params,
+ ThemeModulationParams params = {theme_brightness, {0.0f, 0.0f, 0.0f}};
+ wgpuQueueWriteBuffer(ctx_.queue, params_buffer_.buffer, 0, &params,
sizeof(params));
// Render
diff --git a/src/gpu/effects/theme_modulation_effect.h b/src/gpu/effects/theme_modulation_effect.h
index 107529b..713347b 100644
--- a/src/gpu/effects/theme_modulation_effect.h
+++ b/src/gpu/effects/theme_modulation_effect.h
@@ -5,8 +5,8 @@
#pragma once
#include "gpu/effect.h"
-#include "gpu/uniform_helper.h"
#include "gpu/effects/post_process_helper.h"
+#include "gpu/uniform_helper.h"
class ThemeModulationEffect : public PostProcessEffect {
public:
@@ -16,6 +16,5 @@ class ThemeModulationEffect : public PostProcessEffect {
void update_bind_group(WGPUTextureView input_view) override;
private:
- UniformBuffer<CommonPostProcessUniforms> common_uniforms_;
GpuBuffer params_buffer_;
};
diff --git a/src/gpu/effects/vignette_effect.cc b/src/gpu/effects/vignette_effect.cc
index a4967dd..bba0372 100644
--- a/src/gpu/effects/vignette_effect.cc
+++ b/src/gpu/effects/vignette_effect.cc
@@ -12,7 +12,6 @@ VignetteEffect::VignetteEffect(const GpuContext& ctx)
VignetteEffect::VignetteEffect(const GpuContext& ctx,
const VignetteParams& params)
: PostProcessEffect(ctx), params_(params) {
- uniforms_.init(ctx_.device);
params_buffer_.init(ctx_.device);
pipeline_ = create_post_process_pipeline(ctx_.device, ctx_.format,
vignette_shader_wgsl);
@@ -33,6 +32,6 @@ void VignetteEffect::render(WGPURenderPassEncoder pass, float t, float b,
}
void VignetteEffect::update_bind_group(WGPUTextureView v) {
- pp_update_bind_group(ctx_.device, pipeline_, &bind_group_, v,
- uniforms_.get(), params_buffer_.get());
+ pp_update_bind_group(ctx_.device, pipeline_, &bind_group_, v, uniforms_.get(),
+ params_buffer_.get());
}
diff --git a/src/gpu/gpu.cc b/src/gpu/gpu.cc
index fde241d..e89a2f0 100644
--- a/src/gpu/gpu.cc
+++ b/src/gpu/gpu.cc
@@ -5,6 +5,7 @@
#include "gpu.h"
#include "effect.h"
#include "gpu/effects/shaders.h"
+#include "gpu/effects/shader_composer.h"
#include "platform/platform.h"
#include <cassert>
@@ -55,10 +56,13 @@ RenderPass gpu_create_render_pass(WGPUDevice device, WGPUTextureFormat format,
ResourceBinding* bindings, int num_bindings) {
RenderPass pass = {};
+ // Compose shader to resolve #include directives
+ std::string composed_shader = ShaderComposer::Get().Compose({}, shader_code);
+
// Create Shader Module
WGPUShaderSourceWGSL wgsl_src = {};
wgsl_src.chain.sType = WGPUSType_ShaderSourceWGSL;
- wgsl_src.code = str_view(shader_code);
+ wgsl_src.code = str_view(composed_shader.c_str());
WGPUShaderModuleDescriptor shader_desc = {};
shader_desc.nextInChain = &wgsl_src.chain;
WGPUShaderModule shader_module =
@@ -156,9 +160,12 @@ ComputePass gpu_create_compute_pass(WGPUDevice device, const char* shader_code,
int num_bindings) {
ComputePass pass = {};
+ // Compose shader to resolve #include directives
+ std::string composed_shader = ShaderComposer::Get().Compose({}, shader_code);
+
WGPUShaderSourceWGSL wgsl_src = {};
wgsl_src.chain.sType = WGPUSType_ShaderSourceWGSL;
- wgsl_src.code = str_view(shader_code);
+ wgsl_src.code = str_view(composed_shader.c_str());
WGPUShaderModuleDescriptor shader_desc = {};
shader_desc.nextInChain = &wgsl_src.chain;
WGPUShaderModule shader_module =
diff --git a/src/gpu/stub_gpu.cc b/src/gpu/stub_gpu.cc
new file mode 100644
index 0000000..0b4185c
--- /dev/null
+++ b/src/gpu/stub_gpu.cc
@@ -0,0 +1,83 @@
+// Stub GPU implementation for size measurement builds.
+// All functions are no-ops. Binary compiles but does NOT run.
+// This file is only compiled when STRIP_EXTERNAL_LIBS is defined.
+
+#if defined(STRIP_EXTERNAL_LIBS)
+
+#include "gpu.h"
+#include "platform/stub_types.h"
+
+GpuBuffer gpu_create_buffer(WGPUDevice device, size_t size, uint32_t usage,
+ const void* data) {
+ (void)device;
+ (void)size;
+ (void)usage;
+ (void)data;
+ return {nullptr, 0};
+}
+
+RenderPass gpu_create_render_pass(WGPUDevice device, WGPUTextureFormat format,
+ const char* shader_code,
+ ResourceBinding* bindings, int num_bindings) {
+ (void)device;
+ (void)format;
+ (void)shader_code;
+ (void)bindings;
+ (void)num_bindings;
+ return {nullptr, nullptr, 0, 0};
+}
+
+ComputePass gpu_create_compute_pass(WGPUDevice device, const char* shader_code,
+ ResourceBinding* bindings,
+ int num_bindings) {
+ (void)device;
+ (void)shader_code;
+ (void)bindings;
+ (void)num_bindings;
+ return {nullptr, nullptr, 0, 0, 0};
+}
+
+void gpu_init(PlatformState* platform_state) {
+ (void)platform_state;
+}
+
+void gpu_draw(float audio_peak, float aspect_ratio, float time, float beat) {
+ (void)audio_peak;
+ (void)aspect_ratio;
+ (void)time;
+ (void)beat;
+}
+
+void gpu_resize(int width, int height) {
+ (void)width;
+ (void)height;
+}
+
+void gpu_shutdown() {
+}
+
+const GpuContext* gpu_get_context() {
+ static GpuContext ctx = {nullptr, nullptr, WGPUTextureFormat_BGRA8Unorm};
+ return &ctx;
+}
+
+MainSequence* gpu_get_main_sequence() {
+ return nullptr;
+}
+
+#if !defined(STRIP_ALL)
+void gpu_simulate_until(float time, float bpm) {
+ (void)time;
+ (void)bpm;
+}
+
+void gpu_add_custom_effect(Effect* effect, float start_time, float end_time,
+ int priority) {
+ (void)effect;
+ (void)start_time;
+ (void)end_time;
+ (void)priority;
+}
+#endif
+
+#endif // STRIP_EXTERNAL_LIBS
diff --git a/src/gpu/texture_manager.cc b/src/gpu/texture_manager.cc
index 0c30c94..dfa6315 100644
--- a/src/gpu/texture_manager.cc
+++ b/src/gpu/texture_manager.cc
@@ -2,7 +2,10 @@
// It implements the TextureManager.
#include "gpu/texture_manager.h"
+#include "gpu/effects/shader_composer.h"
+#include "platform/platform.h"
#include <cstdio>
+#include <cstring>
#include <vector>
#if defined(DEMO_CROSS_COMPILE_WIN32)
@@ -26,6 +29,22 @@ void TextureManager::shutdown() {
wgpuTextureRelease(pair.second.texture);
}
textures_.clear();
+
+ for (auto& pair : compute_pipelines_) {
+ if (pair.second.pipeline) {
+ wgpuComputePipelineRelease(pair.second.pipeline);
+ }
+ }
+ compute_pipelines_.clear();
+
+#if !defined(STRIP_GPU_COMPOSITE)
+ for (auto& pair : samplers_) {
+ if (pair.second) {
+ wgpuSamplerRelease(pair.second);
+ }
+ }
+ samplers_.clear();
+#endif
}
void TextureManager::create_procedural_texture(
@@ -112,3 +131,570 @@ WGPUTextureView TextureManager::get_texture_view(const std::string& name) {
}
return nullptr;
}
+
+WGPUComputePipeline TextureManager::get_or_create_compute_pipeline(
+ const std::string& func_name, const char* shader_code,
+ size_t uniform_size, int num_input_textures) {
+ auto it = compute_pipelines_.find(func_name);
+ if (it != compute_pipelines_.end()) {
+ return it->second.pipeline;
+ }
+
+ // Create new pipeline
+ ShaderComposer& composer = ShaderComposer::Get();
+ std::string resolved_shader = composer.Compose({}, shader_code);
+
+ WGPUShaderSourceWGSL wgsl_src = {};
+ wgsl_src.chain.sType = WGPUSType_ShaderSourceWGSL;
+ wgsl_src.code = str_view(resolved_shader.c_str());
+ WGPUShaderModuleDescriptor shader_desc = {};
+ shader_desc.nextInChain = &wgsl_src.chain;
+ WGPUShaderModule shader_module =
+ wgpuDeviceCreateShaderModule(device_, &shader_desc);
+
+ // Dynamic bind group layout
+ // Binding 0: output storage texture
+ // Binding 1: uniform buffer
+ // Binding 2 to (2 + num_input_textures - 1): input textures
+ // Binding (2 + num_input_textures): sampler (if inputs > 0)
+ const int max_entries = 2 + num_input_textures + (num_input_textures > 0 ? 1 : 0);
+ std::vector<WGPUBindGroupLayoutEntry> bgl_entries(max_entries);
+
+ // Binding 0: Output storage texture
+ bgl_entries[0].binding = 0;
+ bgl_entries[0].visibility = WGPUShaderStage_Compute;
+ bgl_entries[0].storageTexture.access = WGPUStorageTextureAccess_WriteOnly;
+ bgl_entries[0].storageTexture.format = WGPUTextureFormat_RGBA8Unorm;
+ bgl_entries[0].storageTexture.viewDimension = WGPUTextureViewDimension_2D;
+
+ // Binding 1: Uniform buffer
+ bgl_entries[1].binding = 1;
+ bgl_entries[1].visibility = WGPUShaderStage_Compute;
+ bgl_entries[1].buffer.type = WGPUBufferBindingType_Uniform;
+ bgl_entries[1].buffer.minBindingSize = uniform_size;
+
+ // Binding 2+: Input textures
+ for (int i = 0; i < num_input_textures; ++i) {
+ bgl_entries[2 + i].binding = 2 + i;
+ bgl_entries[2 + i].visibility = WGPUShaderStage_Compute;
+ bgl_entries[2 + i].texture.sampleType = WGPUTextureSampleType_Float;
+ bgl_entries[2 + i].texture.viewDimension = WGPUTextureViewDimension_2D;
+ }
+
+ // Binding N: Sampler (if inputs exist)
+ if (num_input_textures > 0) {
+ bgl_entries[2 + num_input_textures].binding = 2 + num_input_textures;
+ bgl_entries[2 + num_input_textures].visibility = WGPUShaderStage_Compute;
+ bgl_entries[2 + num_input_textures].sampler.type = WGPUSamplerBindingType_Filtering;
+ }
+
+ WGPUBindGroupLayoutDescriptor bgl_desc = {};
+ bgl_desc.entryCount = max_entries;
+ bgl_desc.entries = bgl_entries.data();
+ WGPUBindGroupLayout bind_group_layout =
+ wgpuDeviceCreateBindGroupLayout(device_, &bgl_desc);
+
+ WGPUPipelineLayoutDescriptor pl_desc = {};
+ pl_desc.bindGroupLayoutCount = 1;
+ pl_desc.bindGroupLayouts = &bind_group_layout;
+ WGPUPipelineLayout pipeline_layout =
+ wgpuDeviceCreatePipelineLayout(device_, &pl_desc);
+
+ WGPUComputePipelineDescriptor pipeline_desc = {};
+ pipeline_desc.layout = pipeline_layout;
+ pipeline_desc.compute.module = shader_module;
+ pipeline_desc.compute.entryPoint = str_view("main");
+
+ WGPUComputePipeline pipeline =
+ wgpuDeviceCreateComputePipeline(device_, &pipeline_desc);
+
+ wgpuPipelineLayoutRelease(pipeline_layout);
+ wgpuBindGroupLayoutRelease(bind_group_layout);
+ wgpuShaderModuleRelease(shader_module);
+
+ // Cache pipeline
+ ComputePipelineInfo info = {pipeline, shader_code, uniform_size, num_input_textures};
+ compute_pipelines_[func_name] = info;
+
+ return pipeline;
+}
+
+void TextureManager::dispatch_compute(const std::string& func_name,
+ WGPUTexture target,
+ const GpuProceduralParams& params,
+ const void* uniform_data,
+ size_t uniform_size) {
+ auto it = compute_pipelines_.find(func_name);
+ if (it == compute_pipelines_.end()) {
+ return; // Pipeline not created yet
+ }
+
+ WGPUComputePipeline pipeline = it->second.pipeline;
+
+ // Create uniform buffer
+ WGPUBufferDescriptor buf_desc = {};
+ buf_desc.size = uniform_size;
+ buf_desc.usage = WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst;
+ buf_desc.mappedAtCreation = WGPUOptionalBool_True;
+ WGPUBuffer uniform_buf = wgpuDeviceCreateBuffer(device_, &buf_desc);
+ void* mapped = wgpuBufferGetMappedRange(uniform_buf, 0, uniform_size);
+ memcpy(mapped, uniform_data, uniform_size);
+ wgpuBufferUnmap(uniform_buf);
+
+ // Create storage texture view
+ WGPUTextureViewDescriptor view_desc = {};
+ view_desc.format = WGPUTextureFormat_RGBA8Unorm;
+ view_desc.dimension = WGPUTextureViewDimension_2D;
+ view_desc.mipLevelCount = 1;
+ view_desc.arrayLayerCount = 1;
+ WGPUTextureView target_view = wgpuTextureCreateView(target, &view_desc);
+
+ // Create bind group layout entries (must match pipeline)
+ WGPUBindGroupLayoutEntry bgl_entries[2] = {};
+ bgl_entries[0].binding = 0;
+ bgl_entries[0].visibility = WGPUShaderStage_Compute;
+ bgl_entries[0].storageTexture.access = WGPUStorageTextureAccess_WriteOnly;
+ bgl_entries[0].storageTexture.format = WGPUTextureFormat_RGBA8Unorm;
+ bgl_entries[0].storageTexture.viewDimension = WGPUTextureViewDimension_2D;
+ bgl_entries[1].binding = 1;
+ bgl_entries[1].visibility = WGPUShaderStage_Compute;
+ bgl_entries[1].buffer.type = WGPUBufferBindingType_Uniform;
+ bgl_entries[1].buffer.minBindingSize = uniform_size;
+
+ WGPUBindGroupLayoutDescriptor bgl_desc = {};
+ bgl_desc.entryCount = 2;
+ bgl_desc.entries = bgl_entries;
+ WGPUBindGroupLayout bind_group_layout =
+ wgpuDeviceCreateBindGroupLayout(device_, &bgl_desc);
+
+ // Create bind group
+ WGPUBindGroupEntry bg_entries[2] = {};
+ bg_entries[0].binding = 0;
+ bg_entries[0].textureView = target_view;
+ bg_entries[1].binding = 1;
+ bg_entries[1].buffer = uniform_buf;
+ bg_entries[1].size = uniform_size;
+
+ WGPUBindGroupDescriptor bg_desc = {};
+ bg_desc.layout = bind_group_layout;
+ bg_desc.entryCount = 2;
+ bg_desc.entries = bg_entries;
+ WGPUBindGroup bind_group = wgpuDeviceCreateBindGroup(device_, &bg_desc);
+
+ // Dispatch compute
+ WGPUCommandEncoderDescriptor enc_desc = {};
+ WGPUCommandEncoder encoder =
+ wgpuDeviceCreateCommandEncoder(device_, &enc_desc);
+ WGPUComputePassEncoder pass =
+ wgpuCommandEncoderBeginComputePass(encoder, nullptr);
+ wgpuComputePassEncoderSetPipeline(pass, pipeline);
+ wgpuComputePassEncoderSetBindGroup(pass, 0, bind_group, 0, nullptr);
+ wgpuComputePassEncoderDispatchWorkgroups(pass, (params.width + 7) / 8,
+ (params.height + 7) / 8, 1);
+ wgpuComputePassEncoderEnd(pass);
+
+ WGPUCommandBufferDescriptor cmd_desc = {};
+ WGPUCommandBuffer cmd = wgpuCommandEncoderFinish(encoder, &cmd_desc);
+ wgpuQueueSubmit(queue_, 1, &cmd);
+
+ // Cleanup
+ wgpuCommandBufferRelease(cmd);
+ wgpuCommandEncoderRelease(encoder);
+ wgpuComputePassEncoderRelease(pass);
+ wgpuBindGroupRelease(bind_group);
+ wgpuBindGroupLayoutRelease(bind_group_layout);
+ wgpuBufferRelease(uniform_buf);
+ wgpuTextureViewRelease(target_view);
+}
+
+void TextureManager::create_gpu_noise_texture(
+ const std::string& name, const GpuProceduralParams& params) {
+ extern const char* gen_noise_compute_wgsl;
+ get_or_create_compute_pipeline("gen_noise", gen_noise_compute_wgsl, 16);
+
+ WGPUTextureDescriptor tex_desc = {};
+ tex_desc.usage =
+ WGPUTextureUsage_StorageBinding | WGPUTextureUsage_TextureBinding;
+ tex_desc.dimension = WGPUTextureDimension_2D;
+ tex_desc.size = {(uint32_t)params.width, (uint32_t)params.height, 1};
+ tex_desc.format = WGPUTextureFormat_RGBA8Unorm;
+ tex_desc.mipLevelCount = 1;
+ tex_desc.sampleCount = 1;
+ WGPUTexture texture = wgpuDeviceCreateTexture(device_, &tex_desc);
+
+ struct NoiseParams {
+ uint32_t width;
+ uint32_t height;
+ float seed;
+ float frequency;
+ };
+ NoiseParams uniforms = {(uint32_t)params.width, (uint32_t)params.height,
+ params.params[0], params.params[1]};
+ dispatch_compute("gen_noise", texture, params, &uniforms, sizeof(NoiseParams));
+
+ WGPUTextureViewDescriptor view_desc = {};
+ view_desc.format = WGPUTextureFormat_RGBA8Unorm;
+ view_desc.dimension = WGPUTextureViewDimension_2D;
+ view_desc.mipLevelCount = 1;
+ view_desc.arrayLayerCount = 1;
+ WGPUTextureView view = wgpuTextureCreateView(texture, &view_desc);
+
+ GpuTexture gpu_tex;
+ gpu_tex.texture = texture;
+ gpu_tex.view = view;
+ gpu_tex.width = params.width;
+ gpu_tex.height = params.height;
+ textures_[name] = gpu_tex;
+
+#if !defined(STRIP_ALL)
+ printf("Generated GPU noise texture: %s (%dx%d)\n", name.c_str(),
+ params.width, params.height);
+#endif
+}
+
+void TextureManager::create_gpu_perlin_texture(
+ const std::string& name, const GpuProceduralParams& params) {
+ extern const char* gen_perlin_compute_wgsl;
+ get_or_create_compute_pipeline("gen_perlin", gen_perlin_compute_wgsl, 32);
+
+ WGPUTextureDescriptor tex_desc = {};
+ tex_desc.usage =
+ WGPUTextureUsage_StorageBinding | WGPUTextureUsage_TextureBinding;
+ tex_desc.dimension = WGPUTextureDimension_2D;
+ tex_desc.size = {(uint32_t)params.width, (uint32_t)params.height, 1};
+ tex_desc.format = WGPUTextureFormat_RGBA8Unorm;
+ tex_desc.mipLevelCount = 1;
+ tex_desc.sampleCount = 1;
+ WGPUTexture texture = wgpuDeviceCreateTexture(device_, &tex_desc);
+
+ struct PerlinParams {
+ uint32_t width;
+ uint32_t height;
+ float seed;
+ float frequency;
+ float amplitude;
+ float amplitude_decay;
+ uint32_t octaves;
+ float _pad0;
+ };
+ PerlinParams uniforms = {
+ (uint32_t)params.width,
+ (uint32_t)params.height,
+ params.params[0],
+ params.params[1],
+ params.num_params > 2 ? params.params[2] : 1.0f,
+ params.num_params > 3 ? params.params[3] : 0.5f,
+ params.num_params > 4 ? (uint32_t)params.params[4] : 4u,
+ 0.0f};
+ dispatch_compute("gen_perlin", texture, params, &uniforms,
+ sizeof(PerlinParams));
+
+ WGPUTextureViewDescriptor view_desc = {};
+ view_desc.format = WGPUTextureFormat_RGBA8Unorm;
+ view_desc.dimension = WGPUTextureViewDimension_2D;
+ view_desc.mipLevelCount = 1;
+ view_desc.arrayLayerCount = 1;
+ WGPUTextureView view = wgpuTextureCreateView(texture, &view_desc);
+
+ GpuTexture gpu_tex;
+ gpu_tex.texture = texture;
+ gpu_tex.view = view;
+ gpu_tex.width = params.width;
+ gpu_tex.height = params.height;
+ textures_[name] = gpu_tex;
+
+#if !defined(STRIP_ALL)
+ printf("Generated GPU perlin texture: %s (%dx%d)\n", name.c_str(),
+ params.width, params.height);
+#endif
+}
+
+void TextureManager::create_gpu_grid_texture(
+ const std::string& name, const GpuProceduralParams& params) {
+ extern const char* gen_grid_compute_wgsl;
+ get_or_create_compute_pipeline("gen_grid", gen_grid_compute_wgsl, 16);
+
+ WGPUTextureDescriptor tex_desc = {};
+ tex_desc.usage =
+ WGPUTextureUsage_StorageBinding | WGPUTextureUsage_TextureBinding;
+ tex_desc.dimension = WGPUTextureDimension_2D;
+ tex_desc.size = {(uint32_t)params.width, (uint32_t)params.height, 1};
+ tex_desc.format = WGPUTextureFormat_RGBA8Unorm;
+ tex_desc.mipLevelCount = 1;
+ tex_desc.sampleCount = 1;
+ WGPUTexture texture = wgpuDeviceCreateTexture(device_, &tex_desc);
+
+ struct GridParams {
+ uint32_t width;
+ uint32_t height;
+ uint32_t grid_size;
+ uint32_t thickness;
+ };
+ GridParams uniforms = {
+ (uint32_t)params.width, (uint32_t)params.height,
+ params.num_params > 0 ? (uint32_t)params.params[0] : 32u,
+ params.num_params > 1 ? (uint32_t)params.params[1] : 2u};
+ dispatch_compute("gen_grid", texture, params, &uniforms, sizeof(GridParams));
+
+ WGPUTextureViewDescriptor view_desc = {};
+ view_desc.format = WGPUTextureFormat_RGBA8Unorm;
+ view_desc.dimension = WGPUTextureViewDimension_2D;
+ view_desc.mipLevelCount = 1;
+ view_desc.arrayLayerCount = 1;
+ WGPUTextureView view = wgpuTextureCreateView(texture, &view_desc);
+
+ GpuTexture gpu_tex;
+ gpu_tex.texture = texture;
+ gpu_tex.view = view;
+ gpu_tex.width = params.width;
+ gpu_tex.height = params.height;
+ textures_[name] = gpu_tex;
+
+#if !defined(STRIP_ALL)
+ printf("Generated GPU grid texture: %s (%dx%d)\n", name.c_str(),
+ params.width, params.height);
+#endif
+}
+
+#if !defined(STRIP_GPU_COMPOSITE)
+WGPUSampler TextureManager::get_or_create_sampler(SamplerType type) {
+ auto it = samplers_.find(type);
+ if (it != samplers_.end()) {
+ return it->second;
+ }
+
+ WGPUSamplerDescriptor desc = {};
+ desc.lodMinClamp = 0.0f;
+ desc.lodMaxClamp = 1.0f;
+ desc.maxAnisotropy = 1;
+
+ switch (type) {
+ case SamplerType::LinearClamp:
+ desc.addressModeU = WGPUAddressMode_ClampToEdge;
+ desc.addressModeV = WGPUAddressMode_ClampToEdge;
+ desc.magFilter = WGPUFilterMode_Linear;
+ desc.minFilter = WGPUFilterMode_Linear;
+ desc.mipmapFilter = WGPUMipmapFilterMode_Linear;
+ break;
+ case SamplerType::LinearRepeat:
+ desc.addressModeU = WGPUAddressMode_Repeat;
+ desc.addressModeV = WGPUAddressMode_Repeat;
+ desc.magFilter = WGPUFilterMode_Linear;
+ desc.minFilter = WGPUFilterMode_Linear;
+ desc.mipmapFilter = WGPUMipmapFilterMode_Linear;
+ break;
+ case SamplerType::NearestClamp:
+ desc.addressModeU = WGPUAddressMode_ClampToEdge;
+ desc.addressModeV = WGPUAddressMode_ClampToEdge;
+ desc.magFilter = WGPUFilterMode_Nearest;
+ desc.minFilter = WGPUFilterMode_Nearest;
+ desc.mipmapFilter = WGPUMipmapFilterMode_Nearest;
+ break;
+ case SamplerType::NearestRepeat:
+ desc.addressModeU = WGPUAddressMode_Repeat;
+ desc.addressModeV = WGPUAddressMode_Repeat;
+ desc.magFilter = WGPUFilterMode_Nearest;
+ desc.minFilter = WGPUFilterMode_Nearest;
+ desc.mipmapFilter = WGPUMipmapFilterMode_Nearest;
+ break;
+ }
+
+ WGPUSampler sampler = wgpuDeviceCreateSampler(device_, &desc);
+ samplers_[type] = sampler;
+ return sampler;
+}
+
+void TextureManager::dispatch_composite(
+ const std::string& func_name, WGPUTexture target,
+ const GpuProceduralParams& params, const void* uniform_data,
+ size_t uniform_size, const std::vector<WGPUTextureView>& input_views,
+ SamplerType sampler_type) {
+ auto it = compute_pipelines_.find(func_name);
+ if (it == compute_pipelines_.end()) {
+ return; // Pipeline not created yet
+ }
+
+ WGPUComputePipeline pipeline = it->second.pipeline;
+ int num_inputs = (int)input_views.size();
+
+ // Create uniform buffer
+ WGPUBufferDescriptor buf_desc = {};
+ buf_desc.size = uniform_size;
+ buf_desc.usage = WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst;
+ buf_desc.mappedAtCreation = WGPUOptionalBool_True;
+ WGPUBuffer uniform_buf = wgpuDeviceCreateBuffer(device_, &buf_desc);
+ void* mapped = wgpuBufferGetMappedRange(uniform_buf, 0, uniform_size);
+ memcpy(mapped, uniform_data, uniform_size);
+ wgpuBufferUnmap(uniform_buf);
+
+ // Create storage texture view
+ WGPUTextureViewDescriptor view_desc = {};
+ view_desc.format = WGPUTextureFormat_RGBA8Unorm;
+ view_desc.dimension = WGPUTextureViewDimension_2D;
+ view_desc.mipLevelCount = 1;
+ view_desc.arrayLayerCount = 1;
+ WGPUTextureView target_view = wgpuTextureCreateView(target, &view_desc);
+
+ // Dynamic bind group
+ const int max_entries = 2 + num_inputs + (num_inputs > 0 ? 1 : 0);
+ std::vector<WGPUBindGroupEntry> bg_entries(max_entries);
+
+ // Binding 0: Output texture
+ bg_entries[0].binding = 0;
+ bg_entries[0].textureView = target_view;
+
+ // Binding 1: Uniform buffer
+ bg_entries[1].binding = 1;
+ bg_entries[1].buffer = uniform_buf;
+ bg_entries[1].size = uniform_size;
+
+ // Binding 2+: Input textures
+ for (int i = 0; i < num_inputs; ++i) {
+ bg_entries[2 + i].binding = 2 + i;
+ bg_entries[2 + i].textureView = input_views[i];
+ }
+
+ // Binding N: Sampler
+ if (num_inputs > 0) {
+ bg_entries[2 + num_inputs].binding = 2 + num_inputs;
+ bg_entries[2 + num_inputs].sampler = get_or_create_sampler(sampler_type);
+ }
+
+ // Create bind group layout (must match pipeline)
+ const int layout_entries_count = 2 + num_inputs + (num_inputs > 0 ? 1 : 0);
+ std::vector<WGPUBindGroupLayoutEntry> bgl_entries(layout_entries_count);
+
+ bgl_entries[0].binding = 0;
+ bgl_entries[0].visibility = WGPUShaderStage_Compute;
+ bgl_entries[0].storageTexture.access = WGPUStorageTextureAccess_WriteOnly;
+ bgl_entries[0].storageTexture.format = WGPUTextureFormat_RGBA8Unorm;
+ bgl_entries[0].storageTexture.viewDimension = WGPUTextureViewDimension_2D;
+
+ bgl_entries[1].binding = 1;
+ bgl_entries[1].visibility = WGPUShaderStage_Compute;
+ bgl_entries[1].buffer.type = WGPUBufferBindingType_Uniform;
+ bgl_entries[1].buffer.minBindingSize = uniform_size;
+
+ for (int i = 0; i < num_inputs; ++i) {
+ bgl_entries[2 + i].binding = 2 + i;
+ bgl_entries[2 + i].visibility = WGPUShaderStage_Compute;
+ bgl_entries[2 + i].texture.sampleType = WGPUTextureSampleType_Float;
+ bgl_entries[2 + i].texture.viewDimension = WGPUTextureViewDimension_2D;
+ }
+
+ if (num_inputs > 0) {
+ bgl_entries[2 + num_inputs].binding = 2 + num_inputs;
+ bgl_entries[2 + num_inputs].visibility = WGPUShaderStage_Compute;
+ bgl_entries[2 + num_inputs].sampler.type = WGPUSamplerBindingType_Filtering;
+ }
+
+ WGPUBindGroupLayoutDescriptor bgl_desc = {};
+ bgl_desc.entryCount = layout_entries_count;
+ bgl_desc.entries = bgl_entries.data();
+ WGPUBindGroupLayout bind_group_layout =
+ wgpuDeviceCreateBindGroupLayout(device_, &bgl_desc);
+
+ WGPUBindGroupDescriptor bg_desc = {};
+ bg_desc.layout = bind_group_layout;
+ bg_desc.entryCount = max_entries;
+ bg_desc.entries = bg_entries.data();
+ WGPUBindGroup bind_group = wgpuDeviceCreateBindGroup(device_, &bg_desc);
+
+ // Dispatch compute
+ WGPUCommandEncoderDescriptor enc_desc = {};
+ WGPUCommandEncoder encoder =
+ wgpuDeviceCreateCommandEncoder(device_, &enc_desc);
+ WGPUComputePassEncoder pass =
+ wgpuCommandEncoderBeginComputePass(encoder, nullptr);
+ wgpuComputePassEncoderSetPipeline(pass, pipeline);
+ wgpuComputePassEncoderSetBindGroup(pass, 0, bind_group, 0, nullptr);
+ wgpuComputePassEncoderDispatchWorkgroups(pass, (params.width + 7) / 8,
+ (params.height + 7) / 8, 1);
+ wgpuComputePassEncoderEnd(pass);
+
+ WGPUCommandBufferDescriptor cmd_desc = {};
+ WGPUCommandBuffer cmd = wgpuCommandEncoderFinish(encoder, &cmd_desc);
+ wgpuQueueSubmit(queue_, 1, &cmd);
+
+ // Cleanup
+ wgpuCommandBufferRelease(cmd);
+ wgpuCommandEncoderRelease(encoder);
+ wgpuComputePassEncoderRelease(pass);
+ wgpuBindGroupRelease(bind_group);
+ wgpuBindGroupLayoutRelease(bind_group_layout);
+ wgpuBufferRelease(uniform_buf);
+ wgpuTextureViewRelease(target_view);
+}
+
+void TextureManager::create_gpu_composite_texture(
+ const std::string& name, const std::string& shader_func,
+ const char* shader_code, const void* uniform_data, size_t uniform_size,
+ int width, int height, const std::vector<std::string>& input_names,
+ SamplerType sampler) {
+ // Create pipeline if needed
+ get_or_create_compute_pipeline(shader_func, shader_code, uniform_size,
+ (int)input_names.size());
+
+ // Resolve input texture views
+ std::vector<WGPUTextureView> input_views;
+ input_views.reserve(input_names.size());
+ for (const auto& input_name : input_names) {
+ WGPUTextureView view = get_texture_view(input_name);
+ if (!view) {
+ fprintf(stderr, "Error: Input texture not found: %s\n",
+ input_name.c_str());
+ return;
+ }
+ input_views.push_back(view);
+ }
+
+ // Create output texture
+ WGPUTextureDescriptor tex_desc = {};
+ tex_desc.usage =
+ WGPUTextureUsage_StorageBinding | WGPUTextureUsage_TextureBinding;
+ tex_desc.dimension = WGPUTextureDimension_2D;
+ tex_desc.size = {(uint32_t)width, (uint32_t)height, 1};
+ tex_desc.format = WGPUTextureFormat_RGBA8Unorm;
+ tex_desc.mipLevelCount = 1;
+ tex_desc.sampleCount = 1;
+ WGPUTexture texture = wgpuDeviceCreateTexture(device_, &tex_desc);
+
+ // Dispatch composite shader
+ GpuProceduralParams params = {width, height, nullptr, 0};
+ dispatch_composite(shader_func, texture, params, uniform_data, uniform_size,
+ input_views, sampler);
+
+ // Create view
+ WGPUTextureViewDescriptor view_desc = {};
+ view_desc.format = WGPUTextureFormat_RGBA8Unorm;
+ view_desc.dimension = WGPUTextureViewDimension_2D;
+ view_desc.mipLevelCount = 1;
+ view_desc.arrayLayerCount = 1;
+ WGPUTextureView view = wgpuTextureCreateView(texture, &view_desc);
+
+ // Store
+ GpuTexture gpu_tex;
+ gpu_tex.texture = texture;
+ gpu_tex.view = view;
+ gpu_tex.width = width;
+ gpu_tex.height = height;
+ textures_[name] = gpu_tex;
+
+#if !defined(STRIP_ALL)
+ printf("Generated GPU composite texture: %s (%dx%d, %zu inputs)\n",
+ name.c_str(), width, height, input_names.size());
+#endif
+}
+#endif // !defined(STRIP_GPU_COMPOSITE)
+
+#if !defined(STRIP_ALL)
+WGPUTextureView TextureManager::get_or_generate_gpu_texture(
+ const std::string& name, const GpuProceduralParams& params) {
+ auto it = textures_.find(name);
+ if (it != textures_.end()) {
+ return it->second.view;
+ }
+ create_gpu_noise_texture(name, params);
+ return textures_[name].view;
+}
+#endif
diff --git a/src/gpu/texture_manager.h b/src/gpu/texture_manager.h
index 23fdbe8..5a2b9f8 100644
--- a/src/gpu/texture_manager.h
+++ b/src/gpu/texture_manager.h
@@ -23,6 +23,13 @@ struct GpuTexture {
int height;
};
+struct GpuProceduralParams {
+ int width;
+ int height;
+ const float* params;
+ int num_params;
+};
+
class TextureManager {
public:
void init(WGPUDevice device, WGPUQueue queue);
@@ -36,11 +43,72 @@ class TextureManager {
void create_texture(const std::string& name, int width, int height,
const uint8_t* data);
+ // GPU procedural generation
+ void create_gpu_noise_texture(const std::string& name,
+ const GpuProceduralParams& params);
+ void create_gpu_perlin_texture(const std::string& name,
+ const GpuProceduralParams& params);
+ void create_gpu_grid_texture(const std::string& name,
+ const GpuProceduralParams& params);
+
+#if !defined(STRIP_GPU_COMPOSITE)
+ enum class SamplerType {
+ LinearClamp,
+ LinearRepeat,
+ NearestClamp,
+ NearestRepeat
+ };
+
+ // GPU composite generation (multi-input textures)
+ void create_gpu_composite_texture(const std::string& name,
+ const std::string& shader_func,
+ const char* shader_code,
+ const void* uniform_data,
+ size_t uniform_size,
+ int width, int height,
+ const std::vector<std::string>& input_names,
+ SamplerType sampler = SamplerType::LinearClamp);
+#endif
+
+#if !defined(STRIP_ALL)
+ // On-demand lazy generation (stripped in final builds)
+ WGPUTextureView get_or_generate_gpu_texture(const std::string& name,
+ const GpuProceduralParams& params);
+#endif
+
// Retrieves a texture view by name (returns nullptr if not found)
WGPUTextureView get_texture_view(const std::string& name);
private:
+ struct ComputePipelineInfo {
+ WGPUComputePipeline pipeline;
+ const char* shader_code;
+ size_t uniform_size;
+ int num_input_textures;
+ };
+
+ WGPUComputePipeline get_or_create_compute_pipeline(const std::string& func_name,
+ const char* shader_code,
+ size_t uniform_size,
+ int num_input_textures = 0);
+ void dispatch_compute(const std::string& func_name, WGPUTexture target,
+ const GpuProceduralParams& params, const void* uniform_data,
+ size_t uniform_size);
+
+#if !defined(STRIP_GPU_COMPOSITE)
+ void dispatch_composite(const std::string& func_name, WGPUTexture target,
+ const GpuProceduralParams& params,
+ const void* uniform_data, size_t uniform_size,
+ const std::vector<WGPUTextureView>& input_views,
+ SamplerType sampler_type);
+#endif
+
WGPUDevice device_;
WGPUQueue queue_;
std::map<std::string, GpuTexture> textures_;
+ std::map<std::string, ComputePipelineInfo> compute_pipelines_;
+#if !defined(STRIP_GPU_COMPOSITE)
+ WGPUSampler get_or_create_sampler(SamplerType type);
+ std::map<SamplerType, WGPUSampler> samplers_;
+#endif
};
diff --git a/src/gpu/uniform_helper.h b/src/gpu/uniform_helper.h
index 151153f..8556c98 100644
--- a/src/gpu/uniform_helper.h
+++ b/src/gpu/uniform_helper.h
@@ -5,7 +5,6 @@
#pragma once
#include "gpu/gpu.h"
-#include <cstring>
// Generic uniform buffer helper
// Usage: