1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
|
// This file is part of the 64k demo project.
// It defines the Effect interface and Sequence management system.
#pragma once
#include <algorithm>
#include <memory>
#include <vector>
#if defined(DEMO_CROSS_COMPILE_WIN32)
#include <webgpu/webgpu.h>
#else
#include <webgpu.h>
#endif
class MainSequence;
class PostProcessEffect;
// Abstract base class for all visual effects
class Effect {
public:
virtual ~Effect() = default;
// One-time setup (load assets, create buffers).
virtual void init(MainSequence *demo) { (void)demo; }
// Called when the effect starts playing in a sequence segment.
virtual void start() {}
// Dispatch compute shaders.
virtual void compute(WGPUCommandEncoder encoder, float time, float beat,
float intensity, float aspect_ratio) {
(void)encoder;
(void)time;
(void)beat;
(void)intensity;
(void)aspect_ratio;
}
// Record render commands.
virtual void render(WGPURenderPassEncoder pass, float time, float beat,
float intensity, float aspect_ratio) = 0;
// Called when the effect finishes in a sequence segment.
virtual void end() {}
bool is_initialized = false;
virtual bool is_post_process() const { return false; }
};
// Base class for all post-processing effects
class PostProcessEffect : public Effect {
public:
bool is_post_process() const override { return true; }
// Post-process effects don't have a compute phase by default
void compute(WGPUCommandEncoder, float, float, float, float) override {}
// Fullscreen quad render
void render(WGPURenderPassEncoder pass, float time, float beat,
float intensity, float aspect_ratio) override;
// Called by MainSequence to update which texture this effect reads from
virtual void update_bind_group(WGPUTextureView input_view) = 0;
protected:
WGPURenderPipeline pipeline_ = nullptr;
WGPUBindGroup bind_group_ = nullptr;
};
struct SequenceItem {
std::shared_ptr<Effect> effect;
float start_time; // Relative to Sequence start
float end_time; // Relative to Sequence start
int priority; // Render order within sequence (higher = later/top)
bool active;
};
class Sequence {
public:
int priority = 0; // Render order of this sequence (higher = later/top)
void init(MainSequence *demo);
// Add an effect to the sequence.
void add_effect(std::shared_ptr<Effect> effect, float start_time,
float end_time, int priority = 0);
// Updates active state of effects based on sequence-local time.
void update_active_list(float seq_time);
// Gathers active effects into lists for processing.
void collect_active_effects(std::vector<SequenceItem *> &scene_effects,
std::vector<SequenceItem *> &post_effects);
void reset();
private:
std::vector<SequenceItem> items_;
bool is_sorted_ = false;
void sort_items();
};
class MainSequence {
public:
MainSequence();
~MainSequence(); // Defined in .cc to handle unique_ptr to incomplete type
WGPUDevice device;
WGPUQueue queue;
WGPUTextureFormat format;
void init(WGPUDevice device, WGPUQueue queue, WGPUTextureFormat format,
int width, int height);
// Add a sequence to the demo.
void add_sequence(std::shared_ptr<Sequence> seq, float start_time,
int priority = 0);
// Renders the full frame: updates sequences, runs compute, runs render pass.
void render_frame(float global_time, float beat, float peak,
float aspect_ratio, WGPUSurface surface);
void shutdown();
#ifndef STRIP_ALL
// Fast-forwards the simulation (updates & compute) without rendering.
void simulate_until(float target_time, float step_rate);
#endif
private:
struct ActiveSequence {
std::shared_ptr<Sequence> seq;
float start_time;
int priority;
};
std::vector<ActiveSequence> sequences_;
// Framebuffers for post-processing
WGPUTexture framebuffer_a_ = nullptr;
WGPUTextureView framebuffer_view_a_ = nullptr;
WGPUTexture framebuffer_b_ = nullptr;
WGPUTextureView framebuffer_view_b_ = nullptr;
// Default passthrough effect for blitting
std::unique_ptr<PostProcessEffect> passthrough_effect_;
void create_framebuffers(int width, int height);
};
|