diff options
| author | 2022-07-16 23:48:45 +0100 | |
|---|---|---|
| committer | 2022-07-22 01:11:32 +0100 | |
| commit | 458da8a94877677f086f06cdeecf959ec4283a33 (patch) | |
| tree | 583166d77602ad90a0d552f37de8729ad80fd6c1 /src/audio_core/renderer/nodes | |
| parent | Merge pull request #8598 from Link4565/recv-dontwait (diff) | |
| download | yuzu-458da8a94877677f086f06cdeecf959ec4283a33.tar.gz yuzu-458da8a94877677f086f06cdeecf959ec4283a33.tar.xz yuzu-458da8a94877677f086f06cdeecf959ec4283a33.zip | |
Project Andio
Diffstat (limited to 'src/audio_core/renderer/nodes')
| -rw-r--r-- | src/audio_core/renderer/nodes/bit_array.h | 25 | ||||
| -rw-r--r-- | src/audio_core/renderer/nodes/edge_matrix.cpp | 38 | ||||
| -rw-r--r-- | src/audio_core/renderer/nodes/edge_matrix.h | 82 | ||||
| -rw-r--r-- | src/audio_core/renderer/nodes/node_states.cpp | 141 | ||||
| -rw-r--r-- | src/audio_core/renderer/nodes/node_states.h | 195 |
5 files changed, 481 insertions, 0 deletions
diff --git a/src/audio_core/renderer/nodes/bit_array.h b/src/audio_core/renderer/nodes/bit_array.h new file mode 100644 index 000000000..b0d53cd51 --- /dev/null +++ b/src/audio_core/renderer/nodes/bit_array.h | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <vector> | ||
| 7 | |||
| 8 | #include "common/common_types.h" | ||
| 9 | |||
| 10 | namespace AudioCore::AudioRenderer { | ||
| 11 | /** | ||
| 12 | * Represents an array of bits used for nodes and edges for the mixing graph. | ||
| 13 | */ | ||
| 14 | struct BitArray { | ||
| 15 | void reset() { | ||
| 16 | buffer.assign(buffer.size(), false); | ||
| 17 | } | ||
| 18 | |||
| 19 | /// Bits | ||
| 20 | std::vector<bool> buffer{}; | ||
| 21 | /// Size of the buffer | ||
| 22 | u32 size{}; | ||
| 23 | }; | ||
| 24 | |||
| 25 | } // namespace AudioCore::AudioRenderer | ||
diff --git a/src/audio_core/renderer/nodes/edge_matrix.cpp b/src/audio_core/renderer/nodes/edge_matrix.cpp new file mode 100644 index 000000000..5573f33b9 --- /dev/null +++ b/src/audio_core/renderer/nodes/edge_matrix.cpp | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "audio_core/renderer/nodes/edge_matrix.h" | ||
| 5 | |||
| 6 | namespace AudioCore::AudioRenderer { | ||
| 7 | |||
| 8 | void EdgeMatrix::Initialize([[maybe_unused]] std::span<u8> buffer, | ||
| 9 | [[maybe_unused]] const u64 node_buffer_size, const u32 count_) { | ||
| 10 | count = count_; | ||
| 11 | edges.buffer.resize(count_ * count_); | ||
| 12 | edges.size = count_ * count_; | ||
| 13 | edges.reset(); | ||
| 14 | } | ||
| 15 | |||
| 16 | bool EdgeMatrix::Connected(const u32 id, const u32 destination_id) const { | ||
| 17 | return edges.buffer[count * id + destination_id]; | ||
| 18 | } | ||
| 19 | |||
| 20 | void EdgeMatrix::Connect(const u32 id, const u32 destination_id) { | ||
| 21 | edges.buffer[count * id + destination_id] = true; | ||
| 22 | } | ||
| 23 | |||
| 24 | void EdgeMatrix::Disconnect(const u32 id, const u32 destination_id) { | ||
| 25 | edges.buffer[count * id + destination_id] = false; | ||
| 26 | } | ||
| 27 | |||
| 28 | void EdgeMatrix::RemoveEdges(const u32 id) { | ||
| 29 | for (u32 dest = 0; dest < count; dest++) { | ||
| 30 | Disconnect(id, dest); | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | u32 EdgeMatrix::GetNodeCount() const { | ||
| 35 | return count; | ||
| 36 | } | ||
| 37 | |||
| 38 | } // namespace AudioCore::AudioRenderer | ||
diff --git a/src/audio_core/renderer/nodes/edge_matrix.h b/src/audio_core/renderer/nodes/edge_matrix.h new file mode 100644 index 000000000..27a20e43e --- /dev/null +++ b/src/audio_core/renderer/nodes/edge_matrix.h | |||
| @@ -0,0 +1,82 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <span> | ||
| 7 | |||
| 8 | #include "audio_core/renderer/nodes/bit_array.h" | ||
| 9 | #include "common/alignment.h" | ||
| 10 | #include "common/common_types.h" | ||
| 11 | |||
| 12 | namespace AudioCore::AudioRenderer { | ||
| 13 | /** | ||
| 14 | * An edge matrix, holding the connections for each node to every other node in the graph. | ||
| 15 | */ | ||
| 16 | class EdgeMatrix { | ||
| 17 | public: | ||
| 18 | /** | ||
| 19 | * Calculate the size required for its workbuffer. | ||
| 20 | * | ||
| 21 | * @param count - The number of nodes in the graph. | ||
| 22 | * @return The required workbuffer size. | ||
| 23 | */ | ||
| 24 | static u64 GetWorkBufferSize(u32 count) { | ||
| 25 | return Common::AlignUp(count * count, 0x40) / sizeof(u64); | ||
| 26 | } | ||
| 27 | |||
| 28 | /** | ||
| 29 | * Initialize this edge matrix. | ||
| 30 | * | ||
| 31 | * @param buffer - The workbuffer to use. Unused. | ||
| 32 | * @param node_buffer_size - The size of the workbuffer. Unused. | ||
| 33 | * @param count - The number of nodes in the graph. | ||
| 34 | */ | ||
| 35 | void Initialize(std::span<u8> buffer, u64 node_buffer_size, u32 count); | ||
| 36 | |||
| 37 | /** | ||
| 38 | * Check if a node is connected to another. | ||
| 39 | * | ||
| 40 | * @param id - The node id to check. | ||
| 41 | * @param destination_id - Node id to check connection with. | ||
| 42 | */ | ||
| 43 | bool Connected(u32 id, u32 destination_id) const; | ||
| 44 | |||
| 45 | /** | ||
| 46 | * Connect a node to another. | ||
| 47 | * | ||
| 48 | * @param id - The node id to connect. | ||
| 49 | * @param destination_id - Destination to connect it to. | ||
| 50 | */ | ||
| 51 | void Connect(u32 id, u32 destination_id); | ||
| 52 | |||
| 53 | /** | ||
| 54 | * Disconnect a node from another. | ||
| 55 | * | ||
| 56 | * @param id - The node id to disconnect. | ||
| 57 | * @param destination_id - Destination to disconnect it from. | ||
| 58 | */ | ||
| 59 | void Disconnect(u32 id, u32 destination_id); | ||
| 60 | |||
| 61 | /** | ||
| 62 | * Remove all connections for a given node. | ||
| 63 | * | ||
| 64 | * @param id - The node id to disconnect. | ||
| 65 | */ | ||
| 66 | void RemoveEdges(u32 id); | ||
| 67 | |||
| 68 | /** | ||
| 69 | * Get the number of nodes in the graph. | ||
| 70 | * | ||
| 71 | * @return Number of nodes. | ||
| 72 | */ | ||
| 73 | u32 GetNodeCount() const; | ||
| 74 | |||
| 75 | private: | ||
| 76 | /// Edges for the current graph | ||
| 77 | BitArray edges; | ||
| 78 | /// Number of nodes (not edges) in the graph | ||
| 79 | u32 count; | ||
| 80 | }; | ||
| 81 | |||
| 82 | } // namespace AudioCore::AudioRenderer | ||
diff --git a/src/audio_core/renderer/nodes/node_states.cpp b/src/audio_core/renderer/nodes/node_states.cpp new file mode 100644 index 000000000..1821a51e6 --- /dev/null +++ b/src/audio_core/renderer/nodes/node_states.cpp | |||
| @@ -0,0 +1,141 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "audio_core/renderer/nodes/node_states.h" | ||
| 5 | #include "common/logging/log.h" | ||
| 6 | |||
| 7 | namespace AudioCore::AudioRenderer { | ||
| 8 | |||
| 9 | void NodeStates::Initialize(std::span<u8> buffer_, [[maybe_unused]] const u64 node_buffer_size, | ||
| 10 | const u32 count) { | ||
| 11 | u64 num_blocks{Common::AlignUp(count, 0x40) / sizeof(u64)}; | ||
| 12 | u64 offset{0}; | ||
| 13 | |||
| 14 | node_count = count; | ||
| 15 | |||
| 16 | nodes_found.buffer.resize(count); | ||
| 17 | nodes_found.size = count; | ||
| 18 | nodes_found.reset(); | ||
| 19 | |||
| 20 | offset += num_blocks; | ||
| 21 | |||
| 22 | nodes_complete.buffer.resize(count); | ||
| 23 | nodes_complete.size = count; | ||
| 24 | nodes_complete.reset(); | ||
| 25 | |||
| 26 | offset += num_blocks; | ||
| 27 | |||
| 28 | results = {reinterpret_cast<u32*>(&buffer_[offset]), count}; | ||
| 29 | |||
| 30 | offset += count * sizeof(u32); | ||
| 31 | |||
| 32 | stack.stack = {reinterpret_cast<u32*>(&buffer_[offset]), count * count}; | ||
| 33 | stack.size = count * count; | ||
| 34 | stack.unk_10 = count * count; | ||
| 35 | |||
| 36 | offset += count * count * sizeof(u32); | ||
| 37 | } | ||
| 38 | |||
| 39 | bool NodeStates::Tsort(const EdgeMatrix& edge_matrix) { | ||
| 40 | return DepthFirstSearch(edge_matrix, stack); | ||
| 41 | } | ||
| 42 | |||
| 43 | bool NodeStates::DepthFirstSearch(const EdgeMatrix& edge_matrix, Stack& stack_) { | ||
| 44 | ResetState(); | ||
| 45 | |||
| 46 | for (u32 node_id = 0; node_id < node_count; node_id++) { | ||
| 47 | if (GetState(node_id) == SearchState::Unknown) { | ||
| 48 | stack_.push(node_id); | ||
| 49 | } | ||
| 50 | |||
| 51 | while (stack_.Count() > 0) { | ||
| 52 | auto current_node{stack_.top()}; | ||
| 53 | switch (GetState(current_node)) { | ||
| 54 | case SearchState::Unknown: | ||
| 55 | SetState(current_node, SearchState::Found); | ||
| 56 | break; | ||
| 57 | case SearchState::Found: | ||
| 58 | SetState(current_node, SearchState::Complete); | ||
| 59 | PushTsortResult(current_node); | ||
| 60 | stack_.pop(); | ||
| 61 | continue; | ||
| 62 | case SearchState::Complete: | ||
| 63 | stack_.pop(); | ||
| 64 | continue; | ||
| 65 | } | ||
| 66 | |||
| 67 | const auto edge_count{edge_matrix.GetNodeCount()}; | ||
| 68 | for (u32 edge_id = 0; edge_id < edge_count; edge_id++) { | ||
| 69 | if (!edge_matrix.Connected(current_node, edge_id)) { | ||
| 70 | continue; | ||
| 71 | } | ||
| 72 | |||
| 73 | switch (GetState(edge_id)) { | ||
| 74 | case SearchState::Unknown: | ||
| 75 | stack_.push(edge_id); | ||
| 76 | break; | ||
| 77 | case SearchState::Found: | ||
| 78 | LOG_ERROR(Service_Audio, | ||
| 79 | "Cycle detected in the node graph, graph is not a DAG! " | ||
| 80 | "Bailing to avoid an infinite loop"); | ||
| 81 | ResetState(); | ||
| 82 | return false; | ||
| 83 | case SearchState::Complete: | ||
| 84 | break; | ||
| 85 | } | ||
| 86 | } | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | return true; | ||
| 91 | } | ||
| 92 | |||
| 93 | NodeStates::SearchState NodeStates::GetState(const u32 id) const { | ||
| 94 | if (nodes_found.buffer[id]) { | ||
| 95 | return SearchState::Found; | ||
| 96 | } else if (nodes_complete.buffer[id]) { | ||
| 97 | return SearchState::Complete; | ||
| 98 | } | ||
| 99 | return SearchState::Unknown; | ||
| 100 | } | ||
| 101 | |||
| 102 | void NodeStates::PushTsortResult(const u32 id) { | ||
| 103 | results[result_pos++] = id; | ||
| 104 | } | ||
| 105 | |||
| 106 | void NodeStates::SetState(const u32 id, const SearchState state) { | ||
| 107 | switch (state) { | ||
| 108 | case SearchState::Complete: | ||
| 109 | nodes_found.buffer[id] = false; | ||
| 110 | nodes_complete.buffer[id] = true; | ||
| 111 | break; | ||
| 112 | case SearchState::Found: | ||
| 113 | nodes_found.buffer[id] = true; | ||
| 114 | nodes_complete.buffer[id] = false; | ||
| 115 | break; | ||
| 116 | case SearchState::Unknown: | ||
| 117 | nodes_found.buffer[id] = false; | ||
| 118 | nodes_complete.buffer[id] = false; | ||
| 119 | break; | ||
| 120 | default: | ||
| 121 | LOG_ERROR(Service_Audio, "Unknown node SearchState {}", static_cast<u32>(state)); | ||
| 122 | break; | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 126 | void NodeStates::ResetState() { | ||
| 127 | nodes_found.reset(); | ||
| 128 | nodes_complete.reset(); | ||
| 129 | std::fill(results.begin(), results.end(), -1); | ||
| 130 | result_pos = 0; | ||
| 131 | } | ||
| 132 | |||
| 133 | u32 NodeStates::GetNodeCount() const { | ||
| 134 | return node_count; | ||
| 135 | } | ||
| 136 | |||
| 137 | std::vector<s32> NodeStates::GetSortedResuls() const { | ||
| 138 | return {results.rbegin(), results.rbegin() + result_pos}; | ||
| 139 | } | ||
| 140 | |||
| 141 | } // namespace AudioCore::AudioRenderer | ||
diff --git a/src/audio_core/renderer/nodes/node_states.h b/src/audio_core/renderer/nodes/node_states.h new file mode 100644 index 000000000..a1e0958a2 --- /dev/null +++ b/src/audio_core/renderer/nodes/node_states.h | |||
| @@ -0,0 +1,195 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <span> | ||
| 7 | #include <vector> | ||
| 8 | |||
| 9 | #include "audio_core/renderer/nodes/edge_matrix.h" | ||
| 10 | #include "common/alignment.h" | ||
| 11 | #include "common/common_types.h" | ||
| 12 | |||
| 13 | namespace AudioCore::AudioRenderer { | ||
| 14 | /** | ||
| 15 | * Graph utility functions for sorting and getting results from the DAG. | ||
| 16 | */ | ||
| 17 | class NodeStates { | ||
| 18 | /** | ||
| 19 | * State of a node in the depth first search. | ||
| 20 | */ | ||
| 21 | enum class SearchState { | ||
| 22 | Unknown, | ||
| 23 | Found, | ||
| 24 | Complete, | ||
| 25 | }; | ||
| 26 | |||
| 27 | /** | ||
| 28 | * Stack used for a depth first search. | ||
| 29 | */ | ||
| 30 | struct Stack { | ||
| 31 | /** | ||
| 32 | * Calculate the workbuffer size required for this stack. | ||
| 33 | * | ||
| 34 | * @param count - Maximum number of nodes for the stack. | ||
| 35 | * @return Required buffer size. | ||
| 36 | */ | ||
| 37 | static u32 CalcBufferSize(u32 count) { | ||
| 38 | return count * sizeof(u32); | ||
| 39 | } | ||
| 40 | |||
| 41 | /** | ||
| 42 | * Reset the stack back to default. | ||
| 43 | * | ||
| 44 | * @param buffer_ - The new buffer to use. | ||
| 45 | * @param size_ - The size of the new buffer. | ||
| 46 | */ | ||
| 47 | void Reset(u32* buffer_, u32 size_) { | ||
| 48 | stack = {buffer_, size_}; | ||
| 49 | size = size_; | ||
| 50 | pos = 0; | ||
| 51 | unk_10 = size_; | ||
| 52 | } | ||
| 53 | |||
| 54 | /** | ||
| 55 | * Get the current stack position. | ||
| 56 | * | ||
| 57 | * @return The current stack position. | ||
| 58 | */ | ||
| 59 | u32 Count() { | ||
| 60 | return pos; | ||
| 61 | } | ||
| 62 | |||
| 63 | /** | ||
| 64 | * Push a new node to the stack. | ||
| 65 | * | ||
| 66 | * @param data - The node to push. | ||
| 67 | */ | ||
| 68 | void push(u32 data) { | ||
| 69 | stack[pos++] = data; | ||
| 70 | } | ||
| 71 | |||
| 72 | /** | ||
| 73 | * Pop a node from the stack. | ||
| 74 | * | ||
| 75 | * @return The node on the top of the stack. | ||
| 76 | */ | ||
| 77 | u32 pop() { | ||
| 78 | return stack[--pos]; | ||
| 79 | } | ||
| 80 | |||
| 81 | /** | ||
| 82 | * Get the top of the stack without popping. | ||
| 83 | * | ||
| 84 | * @return The node on the top of the stack. | ||
| 85 | */ | ||
| 86 | u32 top() { | ||
| 87 | return stack[pos - 1]; | ||
| 88 | } | ||
| 89 | |||
| 90 | /// Buffer for the stack | ||
| 91 | std::span<u32> stack{}; | ||
| 92 | /// Size of the stack buffer | ||
| 93 | u32 size{}; | ||
| 94 | /// Current stack position | ||
| 95 | u32 pos{}; | ||
| 96 | /// Unknown | ||
| 97 | u32 unk_10{}; | ||
| 98 | }; | ||
| 99 | |||
| 100 | public: | ||
| 101 | /** | ||
| 102 | * Calculate the workbuffer size required for the node states. | ||
| 103 | * | ||
| 104 | * @param count - The number of nodes. | ||
| 105 | * @return The required workbuffer size. | ||
| 106 | */ | ||
| 107 | static u64 GetWorkBufferSize(u32 count) { | ||
| 108 | return (Common::AlignUp(count, 0x40) / sizeof(u64)) * 2 + count * sizeof(BitArray) + | ||
| 109 | count * Stack::CalcBufferSize(count); | ||
| 110 | } | ||
| 111 | |||
| 112 | /** | ||
| 113 | * Initialize the node states. | ||
| 114 | * | ||
| 115 | * @param buffer - The workbuffer to use. Unused. | ||
| 116 | * @param node_buffer_size - The size of the workbuffer. Unused. | ||
| 117 | * @param count - The number of nodes in the graph. | ||
| 118 | */ | ||
| 119 | void Initialize(std::span<u8> nodes, u64 node_buffer_size, u32 count); | ||
| 120 | |||
| 121 | /** | ||
| 122 | * Sort the graph. Only calls DepthFirstSearch. | ||
| 123 | * | ||
| 124 | * @param edge_matrix - The edge matrix used to hold the connections between nodes. | ||
| 125 | * @return True if the sort was successful, otherwise false. | ||
| 126 | */ | ||
| 127 | bool Tsort(const EdgeMatrix& edge_matrix); | ||
| 128 | |||
| 129 | /** | ||
| 130 | * Sort the graph via depth first search. | ||
| 131 | * | ||
| 132 | * @param edge_matrix - The edge matrix used to hold the connections between nodes. | ||
| 133 | * @param stack - The stack used for pushing and popping nodes. | ||
| 134 | * @return True if the sort was successful, otherwise false. | ||
| 135 | */ | ||
| 136 | bool DepthFirstSearch(const EdgeMatrix& edge_matrix, Stack& stack); | ||
| 137 | |||
| 138 | /** | ||
| 139 | * Get the search state of a given node. | ||
| 140 | * | ||
| 141 | * @param id - The node id to check. | ||
| 142 | * @return The node's search state. See SearchState | ||
| 143 | */ | ||
| 144 | SearchState GetState(u32 id) const; | ||
| 145 | |||
| 146 | /** | ||
| 147 | * Push a node id to the results buffer when found in the DFS. | ||
| 148 | * | ||
| 149 | * @param id - The node id to push. | ||
| 150 | */ | ||
| 151 | void PushTsortResult(u32 id); | ||
| 152 | |||
| 153 | /** | ||
| 154 | * Set the state of a node. | ||
| 155 | * | ||
| 156 | * @param id - The node id to alter. | ||
| 157 | * @param state - The new search state. | ||
| 158 | */ | ||
| 159 | void SetState(u32 id, SearchState state); | ||
| 160 | |||
| 161 | /** | ||
| 162 | * Reset the nodes found, complete and the results. | ||
| 163 | */ | ||
| 164 | void ResetState(); | ||
| 165 | |||
| 166 | /** | ||
| 167 | * Get the number of nodes in the graph. | ||
| 168 | * | ||
| 169 | * @return The number of nodes. | ||
| 170 | */ | ||
| 171 | u32 GetNodeCount() const; | ||
| 172 | |||
| 173 | /** | ||
| 174 | * Get the sorted results from the DFS. | ||
| 175 | * | ||
| 176 | * @return Vector of nodes in reverse order. | ||
| 177 | */ | ||
| 178 | std::vector<s32> GetSortedResuls() const; | ||
| 179 | |||
| 180 | private: | ||
| 181 | /// Number of nodes in the graph | ||
| 182 | u32 node_count{}; | ||
| 183 | /// Position in results buffer | ||
| 184 | u32 result_pos{}; | ||
| 185 | /// List of nodes found | ||
| 186 | BitArray nodes_found{}; | ||
| 187 | /// List of nodes completed | ||
| 188 | BitArray nodes_complete{}; | ||
| 189 | /// List of results from the depth first search | ||
| 190 | std::span<u32> results{}; | ||
| 191 | /// Stack used during the depth first search | ||
| 192 | Stack stack{}; | ||
| 193 | }; | ||
| 194 | |||
| 195 | } // namespace AudioCore::AudioRenderer | ||