graph/graph.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // SPDX-License-Identifier: BSD-3-Clause | ||
| 2 | #include "chrono_batch_render/graph/graph.h" | ||
| 3 | |||
| 4 | #include <algorithm> | ||
| 5 | #include <cstring> | ||
| 6 | |||
| 7 | #include "chrono_batch_render/util/error.h" | ||
| 8 | #include "chrono_batch_render/util/text.h" | ||
| 9 | |||
| 10 | namespace chrono::batch_render::graph { | ||
| 11 | |||
| 12 | 442 | uint32_t Graph::Add(uint32_t kind) { | |
| 13 | 442 | m_nodes.push_back({.kind = kind}); | |
| 14 | 442 | return uint32_t(m_nodes.size() - 1); | |
| 15 | } | ||
| 16 | |||
| 17 | 320 | void Graph::Connect(uint32_t node, uint32_t input, uint32_t source, uint32_t output) { | |
| 18 | 320 | if (node >= m_nodes.size() || source >= m_nodes.size()) { | |
| 19 | 1 | throw Error("Connecting node {} to node {} of a graph of {}", node, source, m_nodes.size()); | |
| 20 | } | ||
| 21 | 319 | std::vector<Edge>& inputs = m_nodes[node].inputs; | |
| 22 | 319 | if (std::ranges::find(inputs, input, &Edge::input) != inputs.end()) { | |
| 23 | 1 | throw Error("Node {}'s input {} is already connected", node, input); | |
| 24 | } | ||
| 25 | 318 | inputs.push_back({input, source, output}); | |
| 26 | 318 | } | |
| 27 | |||
| 28 | 122 | Ordering Graph::Order() const { | |
| 29 | 122 | enum class Mark : uint8_t { None, Open, Done }; | |
| 30 | 122 | std::vector<Mark> marks(m_nodes.size(), Mark::None); | |
| 31 | 122 | std::vector<uint32_t> path; | |
| 32 | 122 | Ordering ordering; | |
| 33 | |||
| 34 | 122 | struct Frame { | |
| 35 | uint32_t node; | ||
| 36 | uint32_t next; | ||
| 37 | }; | ||
| 38 | |||
| 39 | 122 | std::vector<Frame> stack; | |
| 40 | 543 | for (uint32_t root = 0; root < m_nodes.size(); ++root) { | |
| 41 | 424 | if (marks[root] != Mark::None) { | |
| 42 | 43 | continue; | |
| 43 | } | ||
| 44 | 381 | stack.push_back({root, 0}); | |
| 45 | 381 | marks[root] = Mark::Open; | |
| 46 | 381 | path.push_back(root); | |
| 47 | 1111 | while (!stack.empty()) { | |
| 48 | 733 | Frame& frame = stack.back(); | |
| 49 | 733 | const std::vector<Edge>& inputs = m_nodes[frame.node].inputs; | |
| 50 | 733 | if (frame.next == inputs.size()) { | |
| 51 | 421 | marks[frame.node] = Mark::Done; | |
| 52 | 421 | ordering.order.push_back(frame.node); | |
| 53 | 421 | path.pop_back(); | |
| 54 | 421 | stack.pop_back(); | |
| 55 | 421 | continue; | |
| 56 | } | ||
| 57 | 312 | const uint32_t source = inputs[frame.next++].source; | |
| 58 | 312 | if (marks[source] == Mark::Open) { | |
| 59 | 3 | ordering.cycle.assign(std::ranges::find(path, source), path.end()); | |
| 60 | 3 | ordering.order.clear(); | |
| 61 | 3 | return ordering; | |
| 62 | } | ||
| 63 | 309 | if (marks[source] == Mark::None) { | |
| 64 | 48 | marks[source] = Mark::Open; | |
| 65 | 48 | path.push_back(source); | |
| 66 | 48 | stack.push_back({source, 0}); | |
| 67 | } | ||
| 68 | } | ||
| 69 | } | ||
| 70 | 119 | return ordering; | |
| 71 | 122 | } | |
| 72 | |||
| 73 | 116 | uint64_t ShapeKey::Hash() const { | |
| 74 | 116 | return Fnv1a(m_bytes); | |
| 75 | } | ||
| 76 | |||
| 77 | 2834 | void ShapeWriter::Add(uint32_t value) { | |
| 78 | 2834 | char bytes[sizeof(value)]; | |
| 79 | 2834 | std::memcpy(bytes, &value, sizeof(value)); | |
| 80 | 2834 | m_key.m_bytes.append(bytes, sizeof(bytes)); | |
| 81 | 2834 | } | |
| 82 | |||
| 83 | 514 | void ShapeWriter::Add(std::string_view text) { | |
| 84 | 514 | Add(uint32_t(text.size())); | |
| 85 | 514 | m_key.m_bytes.append(text); | |
| 86 | 514 | } | |
| 87 | |||
| 88 | ✗ | void ShapeWriter::Add(std::span<const std::byte> bytes) { | |
| 89 | ✗ | Add(uint32_t(bytes.size())); | |
| 90 | ✗ | m_key.m_bytes.append(reinterpret_cast<const char*>(bytes.data()), bytes.size()); | |
| 91 | ✗ | } | |
| 92 | |||
| 93 | 117 | ShapeKey ShapeWriter::Finish() && { | |
| 94 | 117 | return std::move(m_key); | |
| 95 | } | ||
| 96 | |||
| 97 | 102 | ShapeKey GraphShape(const Graph& graph, std::span<const uint32_t> order, | |
| 98 | const NodeUniforms& uniforms) { | ||
| 99 | 102 | std::vector<uint32_t> positions(graph.Nodes().size(), kNoNode); | |
| 100 | 496 | for (uint32_t position = 0; position < order.size(); ++position) { | |
| 101 | 394 | positions[order[position]] = position; | |
| 102 | } | ||
| 103 | 102 | ShapeWriter writer; | |
| 104 | 102 | writer.Add(uint32_t(order.size())); | |
| 105 | 496 | for (const uint32_t node : order) { | |
| 106 | 394 | const Node& each = graph[node]; | |
| 107 | 394 | writer.Add(each.kind); | |
| 108 | 394 | std::vector<Edge> inputs = each.inputs; | |
| 109 | 394 | std::ranges::sort(inputs, {}, &Edge::input); | |
| 110 | 394 | writer.Add(uint32_t(inputs.size())); | |
| 111 | 688 | for (const Edge& edge : inputs) { | |
| 112 | 294 | if (positions[edge.source] == kNoNode) { | |
| 113 | ✗ | throw Error("Node {} reads node {}, which the order leaves out", node, edge.source); | |
| 114 | } | ||
| 115 | 294 | writer.Add(edge.input); | |
| 116 | 294 | writer.Add(positions[edge.source]); | |
| 117 | 294 | writer.Add(edge.output); | |
| 118 | } | ||
| 119 | 394 | if (uniforms) { | |
| 120 | 394 | uniforms(node, writer); | |
| 121 | } | ||
| 122 | 394 | } | |
| 123 | 204 | return std::move(writer).Finish(); | |
| 124 | 102 | } | |
| 125 | |||
| 126 | } | ||
| 127 |