summaryrefslogtreecommitdiff
path: root/src/tests
diff options
context:
space:
mode:
Diffstat (limited to 'src/tests')
-rw-r--r--src/tests/gpu/test_effect_base.cc142
1 files changed, 141 insertions, 1 deletions
diff --git a/src/tests/gpu/test_effect_base.cc b/src/tests/gpu/test_effect_base.cc
index e73f4d7..ad7bca3 100644
--- a/src/tests/gpu/test_effect_base.cc
+++ b/src/tests/gpu/test_effect_base.cc
@@ -208,7 +208,145 @@ static void test_sequence_time_params() {
fprintf(stdout, " ✓ Sequence time parameters updated correctly\n");
}
-// Test 7: Pixel validation helpers
+// Minimal Effect subclass for wire_dag / find_downstream_output tests.
+// Exposes the protected helper and records what wire_dag received.
+class WireDagTestEffect : public Effect {
+ public:
+ WireDagTestEffect(const GpuContext& ctx, std::vector<std::string> ins,
+ std::vector<std::string> outs)
+ : Effect(ctx, std::move(ins), std::move(outs), 0.0f, 1000.0f) {}
+
+ void render(WGPUCommandEncoder, const UniformsSequenceParams&,
+ NodeRegistry&) override {}
+
+ std::string call_find_downstream(const std::vector<EffectDAGNode>& dag) const {
+ return find_downstream_output(dag);
+ }
+
+ std::string wired_to;
+ void wire_dag(const std::vector<EffectDAGNode>& dag) override {
+ wired_to = find_downstream_output(dag);
+ }
+};
+
+// Test 7: find_downstream_output DAG query
+static void test_find_downstream_output() {
+ fprintf(stdout, "Testing find_downstream_output...\n");
+
+ WebGPUTestFixture fixture;
+ if (!fixture.init()) {
+ fprintf(stdout, " ⚠ WebGPU unavailable - skipping test\n");
+ return;
+ }
+
+ auto a = std::make_shared<WireDagTestEffect>(
+ fixture.ctx(), std::vector<std::string>{"src"},
+ std::vector<std::string>{"mid"});
+ auto b = std::make_shared<WireDagTestEffect>(
+ fixture.ctx(), std::vector<std::string>{"mid"},
+ std::vector<std::string>{"out"});
+ auto c = std::make_shared<WireDagTestEffect>(
+ fixture.ctx(), std::vector<std::string>{"out"},
+ std::vector<std::string>{"final"});
+
+ // Two-node chain: A→B. A's downstream is B, returns B's output "out".
+ std::vector<EffectDAGNode> dag_ab = {
+ {a, {"src"}, {"mid"}, 0},
+ {b, {"mid"}, {"out"}, 1},
+ };
+ assert(a->call_find_downstream(dag_ab) == "out" &&
+ "A's downstream output should be 'out'");
+ fprintf(stdout, " ✓ two-node chain: correct downstream output\n");
+
+ // Three-node chain: A→B→C. A finds B first (not C).
+ std::vector<EffectDAGNode> dag_abc = {
+ {a, {"src"}, {"mid"}, 0},
+ {b, {"mid"}, {"out"}, 1},
+ {c, {"out"}, {"final"}, 2},
+ };
+ assert(a->call_find_downstream(dag_abc) == "out" &&
+ "A should find first downstream, not transitive");
+ fprintf(stdout, " ✓ three-node chain: first downstream only\n");
+
+ // No downstream: A is the last node.
+ std::vector<EffectDAGNode> dag_a_only = {
+ {a, {"src"}, {"mid"}, 0},
+ };
+ assert(a->call_find_downstream(dag_a_only) == "" &&
+ "No downstream should return empty string");
+ fprintf(stdout, " ✓ no downstream: returns empty string\n");
+
+ // Unrelated node: B does not consume A's output.
+ auto unrelated = std::make_shared<WireDagTestEffect>(
+ fixture.ctx(), std::vector<std::string>{"other"},
+ std::vector<std::string>{"sink"});
+ std::vector<EffectDAGNode> dag_unrelated = {
+ {a, {"src"}, {"mid"}, 0},
+ {unrelated, {"other"}, {"sink"}, 1},
+ };
+ assert(a->call_find_downstream(dag_unrelated) == "" &&
+ "Unrelated node should not match");
+ fprintf(stdout, " ✓ unrelated node: returns empty string\n");
+
+ // Downstream outputs to "sink" (external view, no owned texture).
+ // wire_dag must not wire to it — GBufferEffect skips "sink" outputs.
+ auto to_sink = std::make_shared<WireDagTestEffect>(
+ fixture.ctx(), std::vector<std::string>{"mid"},
+ std::vector<std::string>{"sink"});
+ std::vector<EffectDAGNode> dag_to_sink = {
+ {a, {"src"}, {"mid"}, 0},
+ {to_sink, {"mid"}, {"sink"}, 1},
+ };
+ // find_downstream_output returns "sink" (it's agnostic)
+ assert(a->call_find_downstream(dag_to_sink) == "sink");
+ // but wire_dag on a WireDagTestEffect just stores whatever find returns;
+ // verify GBufferEffect-style guard: "sink" should NOT be wired as prev
+ a->wire_dag(dag_to_sink);
+ assert(a->wired_to == "sink" &&
+ "base helper returns sink — caller must guard");
+ fprintf(stdout, " ✓ sink downstream: find returns 'sink', caller must guard\n");
+}
+
+// Test 8: wire_dag called automatically by init_effect_nodes
+static void test_wire_dag_called_by_sequence() {
+ fprintf(stdout, "Testing wire_dag called by init_effect_nodes...\n");
+
+ WebGPUTestFixture fixture;
+ if (!fixture.init()) {
+ fprintf(stdout, " ⚠ WebGPU unavailable - skipping test\n");
+ return;
+ }
+
+ auto upstream = std::make_shared<WireDagTestEffect>(
+ fixture.ctx(), std::vector<std::string>{"source"},
+ std::vector<std::string>{"mid"});
+ auto downstream = std::make_shared<WireDagTestEffect>(
+ fixture.ctx(), std::vector<std::string>{"mid"},
+ std::vector<std::string>{"sink"});
+
+ class TestSequence : public Sequence {
+ public:
+ TestSequence(const GpuContext& ctx,
+ std::shared_ptr<Effect> up,
+ std::shared_ptr<Effect> down)
+ : Sequence(ctx, 256, 256) {
+ effect_dag_.push_back({up, {"source"}, {"mid"}, 0});
+ effect_dag_.push_back({down, {"mid"}, {"sink"}, 1});
+ init_effect_nodes(); // triggers wire_dag on both effects
+ }
+ };
+
+ TestSequence seq(fixture.ctx(), upstream, downstream);
+
+ assert(upstream->wired_to == "sink" &&
+ "upstream should be wired to downstream's output 'sink'");
+ assert(downstream->wired_to == "" &&
+ "downstream has no consumer, should be empty");
+
+ fprintf(stdout, " ✓ upstream wired_to='sink', downstream wired_to=''\n");
+}
+
+// Test 9: Pixel validation helpers
static void test_pixel_helpers() {
fprintf(stdout, "Testing pixel validation helpers...\n");
@@ -254,6 +392,8 @@ int main() {
test_effect_in_sequence();
test_sequence_render();
test_sequence_time_params();
+ test_find_downstream_output();
+ test_wire_dag_called_by_sequence();
test_pixel_helpers();
fprintf(stdout, "=== All Effect Base Tests Passed ===\n");