summaryrefslogtreecommitdiff
path: root/src/gpu/effect.h
blob: 50b2d72c594b1309a0fef73d658074d18a622b97 (plain)
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_;
};