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
|
// This file is part of the 64k demo project.
// It defines the Effect interface and Sequence management system.
// Used for choreographing visual effects.
#pragma once
#include <vector>
#include <memory>
#include <algorithm>
#if defined(DEMO_CROSS_COMPILE_WIN32)
#include <webgpu/webgpu.h>
#else
#include <webgpu.h>
#endif
class MainSequence;
// Abstract base class for all visual effects
class Effect {
public:
virtual ~Effect() = default;
// One-time setup (load assets, create buffers).
// Idempotent: safe to call multiple times if effect is shared.
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;
};
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.
// start_time, end_time: Relative to sequence start.
// priority: Drawing order within this 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.
// seq_time: Time relative to sequence start.
void update_active_list(float seq_time);
// Calls compute() on all active effects (sorted by priority).
void dispatch_compute(WGPUCommandEncoder encoder, float seq_time, float beat,
float intensity, float aspect_ratio);
// Calls render() on all active effects (sorted by priority).
void dispatch_render(WGPURenderPassEncoder pass, float seq_time, float beat,
float intensity, float aspect_ratio);
void reset();
private:
std::vector<SequenceItem> items_;
bool is_sorted_ = false;
void sort_items();
};
class MainSequence {
public:
WGPUDevice device;
WGPUQueue queue;
WGPUTextureFormat format;
void init(WGPUDevice device, WGPUQueue queue, WGPUTextureFormat format);
// Add a sequence to the demo.
// start_time: Global time when this sequence starts.
// priority: Layering order (higher = on top).
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();
private:
struct ActiveSequence {
std::shared_ptr<Sequence> seq;
float start_time;
int priority;
};
std::vector<ActiveSequence> sequences_;
};
|