splitter_context.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616
  1. // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "audio_core/behavior_info.h"
  4. #include "audio_core/splitter_context.h"
  5. #include "common/alignment.h"
  6. #include "common/assert.h"
  7. #include "common/logging/log.h"
  8. namespace AudioCore {
  9. ServerSplitterDestinationData::ServerSplitterDestinationData(s32 id_) : id{id_} {}
  10. ServerSplitterDestinationData::~ServerSplitterDestinationData() = default;
  11. void ServerSplitterDestinationData::Update(SplitterInfo::InDestinationParams& header) {
  12. // Log error as these are not actually failure states
  13. if (header.magic != SplitterMagic::DataHeader) {
  14. LOG_ERROR(Audio, "Splitter destination header is invalid!");
  15. return;
  16. }
  17. // Incorrect splitter id
  18. if (header.splitter_id != id) {
  19. LOG_ERROR(Audio, "Splitter destination ids do not match!");
  20. return;
  21. }
  22. mix_id = header.mix_id;
  23. // Copy our mix volumes
  24. std::copy(header.mix_volumes.begin(), header.mix_volumes.end(), current_mix_volumes.begin());
  25. if (!in_use && header.in_use) {
  26. // Update mix volumes
  27. std::copy(current_mix_volumes.begin(), current_mix_volumes.end(), last_mix_volumes.begin());
  28. needs_update = false;
  29. }
  30. in_use = header.in_use;
  31. }
  32. ServerSplitterDestinationData* ServerSplitterDestinationData::GetNextDestination() {
  33. return next;
  34. }
  35. const ServerSplitterDestinationData* ServerSplitterDestinationData::GetNextDestination() const {
  36. return next;
  37. }
  38. void ServerSplitterDestinationData::SetNextDestination(ServerSplitterDestinationData* dest) {
  39. next = dest;
  40. }
  41. bool ServerSplitterDestinationData::ValidMixId() const {
  42. return GetMixId() != AudioCommon::NO_MIX;
  43. }
  44. s32 ServerSplitterDestinationData::GetMixId() const {
  45. return mix_id;
  46. }
  47. bool ServerSplitterDestinationData::IsConfigured() const {
  48. return in_use && ValidMixId();
  49. }
  50. float ServerSplitterDestinationData::GetMixVolume(std::size_t i) const {
  51. ASSERT(i < AudioCommon::MAX_MIX_BUFFERS);
  52. return current_mix_volumes.at(i);
  53. }
  54. const std::array<float, AudioCommon::MAX_MIX_BUFFERS>&
  55. ServerSplitterDestinationData::CurrentMixVolumes() const {
  56. return current_mix_volumes;
  57. }
  58. const std::array<float, AudioCommon::MAX_MIX_BUFFERS>&
  59. ServerSplitterDestinationData::LastMixVolumes() const {
  60. return last_mix_volumes;
  61. }
  62. void ServerSplitterDestinationData::MarkDirty() {
  63. needs_update = true;
  64. }
  65. void ServerSplitterDestinationData::UpdateInternalState() {
  66. if (in_use && needs_update) {
  67. std::copy(current_mix_volumes.begin(), current_mix_volumes.end(), last_mix_volumes.begin());
  68. }
  69. needs_update = false;
  70. }
  71. ServerSplitterInfo::ServerSplitterInfo(s32 id_) : id(id_) {}
  72. ServerSplitterInfo::~ServerSplitterInfo() = default;
  73. void ServerSplitterInfo::InitializeInfos() {
  74. send_length = 0;
  75. head = nullptr;
  76. new_connection = true;
  77. }
  78. void ServerSplitterInfo::ClearNewConnectionFlag() {
  79. new_connection = false;
  80. }
  81. std::size_t ServerSplitterInfo::Update(SplitterInfo::InInfoPrams& header) {
  82. if (header.send_id != id) {
  83. return 0;
  84. }
  85. sample_rate = header.sample_rate;
  86. new_connection = true;
  87. // We need to update the size here due to the splitter bug being present and providing an
  88. // incorrect size. We're suppose to also update the header here but we just ignore and continue
  89. return (sizeof(s32_le) * (header.length - 1)) + (sizeof(s32_le) * 3);
  90. }
  91. ServerSplitterDestinationData* ServerSplitterInfo::GetHead() {
  92. return head;
  93. }
  94. const ServerSplitterDestinationData* ServerSplitterInfo::GetHead() const {
  95. return head;
  96. }
  97. ServerSplitterDestinationData* ServerSplitterInfo::GetData(std::size_t depth) {
  98. auto* current_head = head;
  99. for (std::size_t i = 0; i < depth; i++) {
  100. if (current_head == nullptr) {
  101. return nullptr;
  102. }
  103. current_head = current_head->GetNextDestination();
  104. }
  105. return current_head;
  106. }
  107. const ServerSplitterDestinationData* ServerSplitterInfo::GetData(std::size_t depth) const {
  108. auto* current_head = head;
  109. for (std::size_t i = 0; i < depth; i++) {
  110. if (current_head == nullptr) {
  111. return nullptr;
  112. }
  113. current_head = current_head->GetNextDestination();
  114. }
  115. return current_head;
  116. }
  117. bool ServerSplitterInfo::HasNewConnection() const {
  118. return new_connection;
  119. }
  120. s32 ServerSplitterInfo::GetLength() const {
  121. return send_length;
  122. }
  123. void ServerSplitterInfo::SetHead(ServerSplitterDestinationData* new_head) {
  124. head = new_head;
  125. }
  126. void ServerSplitterInfo::SetHeadDepth(s32 length) {
  127. send_length = length;
  128. }
  129. SplitterContext::SplitterContext() = default;
  130. SplitterContext::~SplitterContext() = default;
  131. void SplitterContext::Initialize(BehaviorInfo& behavior_info, std::size_t _info_count,
  132. std::size_t _data_count) {
  133. if (!behavior_info.IsSplitterSupported() || _data_count == 0 || _info_count == 0) {
  134. Setup(0, 0, false);
  135. return;
  136. }
  137. // Only initialize if we're using splitters
  138. Setup(_info_count, _data_count, behavior_info.IsSplitterBugFixed());
  139. }
  140. bool SplitterContext::Update(const std::vector<u8>& input, std::size_t& input_offset,
  141. std::size_t& bytes_read) {
  142. const auto UpdateOffsets = [&](std::size_t read) {
  143. input_offset += read;
  144. bytes_read += read;
  145. };
  146. if (info_count == 0 || data_count == 0) {
  147. bytes_read = 0;
  148. return true;
  149. }
  150. if (!AudioCommon::CanConsumeBuffer(input.size(), input_offset,
  151. sizeof(SplitterInfo::InHeader))) {
  152. LOG_ERROR(Audio, "Buffer is an invalid size!");
  153. return false;
  154. }
  155. SplitterInfo::InHeader header{};
  156. std::memcpy(&header, input.data() + input_offset, sizeof(SplitterInfo::InHeader));
  157. UpdateOffsets(sizeof(SplitterInfo::InHeader));
  158. if (header.magic != SplitterMagic::SplitterHeader) {
  159. LOG_ERROR(Audio, "Invalid header magic! Expecting {:X} but got {:X}",
  160. SplitterMagic::SplitterHeader, header.magic);
  161. return false;
  162. }
  163. // Clear all connections
  164. for (auto& info : infos) {
  165. info.ClearNewConnectionFlag();
  166. }
  167. UpdateInfo(input, input_offset, bytes_read, header.info_count);
  168. UpdateData(input, input_offset, bytes_read, header.data_count);
  169. const auto aligned_bytes_read = Common::AlignUp(bytes_read, 16);
  170. input_offset += aligned_bytes_read - bytes_read;
  171. bytes_read = aligned_bytes_read;
  172. return true;
  173. }
  174. bool SplitterContext::UsingSplitter() const {
  175. return info_count > 0 && data_count > 0;
  176. }
  177. ServerSplitterInfo& SplitterContext::GetInfo(std::size_t i) {
  178. ASSERT(i < info_count);
  179. return infos.at(i);
  180. }
  181. const ServerSplitterInfo& SplitterContext::GetInfo(std::size_t i) const {
  182. ASSERT(i < info_count);
  183. return infos.at(i);
  184. }
  185. ServerSplitterDestinationData& SplitterContext::GetData(std::size_t i) {
  186. ASSERT(i < data_count);
  187. return datas.at(i);
  188. }
  189. const ServerSplitterDestinationData& SplitterContext::GetData(std::size_t i) const {
  190. ASSERT(i < data_count);
  191. return datas.at(i);
  192. }
  193. ServerSplitterDestinationData* SplitterContext::GetDestinationData(std::size_t info,
  194. std::size_t data) {
  195. ASSERT(info < info_count);
  196. auto& cur_info = GetInfo(info);
  197. return cur_info.GetData(data);
  198. }
  199. const ServerSplitterDestinationData* SplitterContext::GetDestinationData(std::size_t info,
  200. std::size_t data) const {
  201. ASSERT(info < info_count);
  202. const auto& cur_info = GetInfo(info);
  203. return cur_info.GetData(data);
  204. }
  205. void SplitterContext::UpdateInternalState() {
  206. if (data_count == 0) {
  207. return;
  208. }
  209. for (auto& data : datas) {
  210. data.UpdateInternalState();
  211. }
  212. }
  213. std::size_t SplitterContext::GetInfoCount() const {
  214. return info_count;
  215. }
  216. std::size_t SplitterContext::GetDataCount() const {
  217. return data_count;
  218. }
  219. void SplitterContext::Setup(std::size_t info_count_, std::size_t data_count_,
  220. bool is_splitter_bug_fixed) {
  221. info_count = info_count_;
  222. data_count = data_count_;
  223. for (std::size_t i = 0; i < info_count; i++) {
  224. auto& splitter = infos.emplace_back(static_cast<s32>(i));
  225. splitter.InitializeInfos();
  226. }
  227. for (std::size_t i = 0; i < data_count; i++) {
  228. datas.emplace_back(static_cast<s32>(i));
  229. }
  230. bug_fixed = is_splitter_bug_fixed;
  231. }
  232. bool SplitterContext::UpdateInfo(const std::vector<u8>& input, std::size_t& input_offset,
  233. std::size_t& bytes_read, s32 in_splitter_count) {
  234. const auto UpdateOffsets = [&](std::size_t read) {
  235. input_offset += read;
  236. bytes_read += read;
  237. };
  238. for (s32 i = 0; i < in_splitter_count; i++) {
  239. if (!AudioCommon::CanConsumeBuffer(input.size(), input_offset,
  240. sizeof(SplitterInfo::InInfoPrams))) {
  241. LOG_ERROR(Audio, "Buffer is an invalid size!");
  242. return false;
  243. }
  244. SplitterInfo::InInfoPrams header{};
  245. std::memcpy(&header, input.data() + input_offset, sizeof(SplitterInfo::InInfoPrams));
  246. // Logged as warning as these don't actually cause a bailout for some reason
  247. if (header.magic != SplitterMagic::InfoHeader) {
  248. LOG_ERROR(Audio, "Bad splitter data header");
  249. break;
  250. }
  251. if (header.send_id < 0 || static_cast<std::size_t>(header.send_id) > info_count) {
  252. LOG_ERROR(Audio, "Bad splitter data id");
  253. break;
  254. }
  255. UpdateOffsets(sizeof(SplitterInfo::InInfoPrams));
  256. auto& info = GetInfo(header.send_id);
  257. if (!RecomposeDestination(info, header, input, input_offset)) {
  258. LOG_ERROR(Audio, "Failed to recompose destination for splitter!");
  259. return false;
  260. }
  261. const std::size_t read = info.Update(header);
  262. bytes_read += read;
  263. input_offset += read;
  264. }
  265. return true;
  266. }
  267. bool SplitterContext::UpdateData(const std::vector<u8>& input, std::size_t& input_offset,
  268. std::size_t& bytes_read, s32 in_data_count) {
  269. const auto UpdateOffsets = [&](std::size_t read) {
  270. input_offset += read;
  271. bytes_read += read;
  272. };
  273. for (s32 i = 0; i < in_data_count; i++) {
  274. if (!AudioCommon::CanConsumeBuffer(input.size(), input_offset,
  275. sizeof(SplitterInfo::InDestinationParams))) {
  276. LOG_ERROR(Audio, "Buffer is an invalid size!");
  277. return false;
  278. }
  279. SplitterInfo::InDestinationParams header{};
  280. std::memcpy(&header, input.data() + input_offset,
  281. sizeof(SplitterInfo::InDestinationParams));
  282. UpdateOffsets(sizeof(SplitterInfo::InDestinationParams));
  283. // Logged as warning as these don't actually cause a bailout for some reason
  284. if (header.magic != SplitterMagic::DataHeader) {
  285. LOG_ERROR(Audio, "Bad splitter data header");
  286. break;
  287. }
  288. if (header.splitter_id < 0 || static_cast<std::size_t>(header.splitter_id) > data_count) {
  289. LOG_ERROR(Audio, "Bad splitter data id");
  290. break;
  291. }
  292. GetData(header.splitter_id).Update(header);
  293. }
  294. return true;
  295. }
  296. bool SplitterContext::RecomposeDestination(ServerSplitterInfo& info,
  297. SplitterInfo::InInfoPrams& header,
  298. const std::vector<u8>& input,
  299. const std::size_t& input_offset) {
  300. // Clear our current destinations
  301. auto* current_head = info.GetHead();
  302. while (current_head != nullptr) {
  303. auto* next_head = current_head->GetNextDestination();
  304. current_head->SetNextDestination(nullptr);
  305. current_head = next_head;
  306. }
  307. info.SetHead(nullptr);
  308. s32 size = header.length;
  309. // If the splitter bug is present, calculate fixed size
  310. if (!bug_fixed) {
  311. if (info_count > 0) {
  312. const auto factor = data_count / info_count;
  313. size = std::min(header.length, static_cast<s32>(factor));
  314. } else {
  315. size = 0;
  316. }
  317. }
  318. if (size < 1) {
  319. LOG_ERROR(Audio, "Invalid splitter info size! size={:X}", size);
  320. return true;
  321. }
  322. auto* start_head = &GetData(header.resource_id_base);
  323. current_head = start_head;
  324. std::vector<s32_le> resource_ids(size - 1);
  325. if (!AudioCommon::CanConsumeBuffer(input.size(), input_offset,
  326. resource_ids.size() * sizeof(s32_le))) {
  327. LOG_ERROR(Audio, "Buffer is an invalid size!");
  328. return false;
  329. }
  330. std::memcpy(resource_ids.data(), input.data() + input_offset,
  331. resource_ids.size() * sizeof(s32_le));
  332. for (auto resource_id : resource_ids) {
  333. auto* head = &GetData(resource_id);
  334. current_head->SetNextDestination(head);
  335. current_head = head;
  336. }
  337. info.SetHead(start_head);
  338. info.SetHeadDepth(size);
  339. return true;
  340. }
  341. NodeStates::NodeStates() = default;
  342. NodeStates::~NodeStates() = default;
  343. void NodeStates::Initialize(std::size_t node_count_) {
  344. // Setup our work parameters
  345. node_count = node_count_;
  346. was_node_found.resize(node_count);
  347. was_node_completed.resize(node_count);
  348. index_list.resize(node_count);
  349. index_stack.Reset(node_count * node_count);
  350. }
  351. bool NodeStates::Tsort(EdgeMatrix& edge_matrix) {
  352. return DepthFirstSearch(edge_matrix);
  353. }
  354. std::size_t NodeStates::GetIndexPos() const {
  355. return index_pos;
  356. }
  357. const std::vector<s32>& NodeStates::GetIndexList() const {
  358. return index_list;
  359. }
  360. void NodeStates::PushTsortResult(s32 index) {
  361. ASSERT(index < static_cast<s32>(node_count));
  362. index_list[index_pos++] = index;
  363. }
  364. bool NodeStates::DepthFirstSearch(EdgeMatrix& edge_matrix) {
  365. ResetState();
  366. for (std::size_t i = 0; i < node_count; i++) {
  367. const auto node_id = static_cast<s32>(i);
  368. // If we don't have a state, send to our index stack for work
  369. if (GetState(i) == NodeStates::State::NoState) {
  370. index_stack.push(node_id);
  371. }
  372. // While we have work to do in our stack
  373. while (index_stack.Count() > 0) {
  374. // Get the current node
  375. const auto current_stack_index = index_stack.top();
  376. // Check if we've seen the node yet
  377. const auto index_state = GetState(current_stack_index);
  378. if (index_state == NodeStates::State::NoState) {
  379. // Mark the node as seen
  380. UpdateState(NodeStates::State::InFound, current_stack_index);
  381. } else if (index_state == NodeStates::State::InFound) {
  382. // We've seen this node before, mark it as completed
  383. UpdateState(NodeStates::State::InCompleted, current_stack_index);
  384. // Update our index list
  385. PushTsortResult(current_stack_index);
  386. // Pop the stack
  387. index_stack.pop();
  388. continue;
  389. } else if (index_state == NodeStates::State::InCompleted) {
  390. // If our node is already sorted, clear it
  391. index_stack.pop();
  392. continue;
  393. }
  394. const auto edge_node_count = edge_matrix.GetNodeCount();
  395. for (s32 j = 0; j < static_cast<s32>(edge_node_count); j++) {
  396. // Check if our node is connected to our edge matrix
  397. if (!edge_matrix.Connected(current_stack_index, j)) {
  398. continue;
  399. }
  400. // Check if our node exists
  401. const auto node_state = GetState(j);
  402. if (node_state == NodeStates::State::NoState) {
  403. // Add more work
  404. index_stack.push(j);
  405. } else if (node_state == NodeStates::State::InFound) {
  406. UNREACHABLE_MSG("Node start marked as found");
  407. ResetState();
  408. return false;
  409. }
  410. }
  411. }
  412. }
  413. return true;
  414. }
  415. void NodeStates::ResetState() {
  416. // Reset to the start of our index stack
  417. index_pos = 0;
  418. for (std::size_t i = 0; i < node_count; i++) {
  419. // Mark all nodes as not found
  420. was_node_found[i] = false;
  421. // Mark all nodes as uncompleted
  422. was_node_completed[i] = false;
  423. // Mark all indexes as invalid
  424. index_list[i] = -1;
  425. }
  426. }
  427. void NodeStates::UpdateState(NodeStates::State state, std::size_t i) {
  428. switch (state) {
  429. case NodeStates::State::NoState:
  430. was_node_found[i] = false;
  431. was_node_completed[i] = false;
  432. break;
  433. case NodeStates::State::InFound:
  434. was_node_found[i] = true;
  435. was_node_completed[i] = false;
  436. break;
  437. case NodeStates::State::InCompleted:
  438. was_node_found[i] = false;
  439. was_node_completed[i] = true;
  440. break;
  441. }
  442. }
  443. NodeStates::State NodeStates::GetState(std::size_t i) {
  444. ASSERT(i < node_count);
  445. if (was_node_found[i]) {
  446. // If our node exists in our found list
  447. return NodeStates::State::InFound;
  448. } else if (was_node_completed[i]) {
  449. // If node is in the completed list
  450. return NodeStates::State::InCompleted;
  451. } else {
  452. // If in neither
  453. return NodeStates::State::NoState;
  454. }
  455. }
  456. NodeStates::Stack::Stack() = default;
  457. NodeStates::Stack::~Stack() = default;
  458. void NodeStates::Stack::Reset(std::size_t size) {
  459. // Mark our stack as empty
  460. stack.resize(size);
  461. stack_size = size;
  462. stack_pos = 0;
  463. std::fill(stack.begin(), stack.end(), 0);
  464. }
  465. void NodeStates::Stack::push(s32 val) {
  466. ASSERT(stack_pos < stack_size);
  467. stack[stack_pos++] = val;
  468. }
  469. std::size_t NodeStates::Stack::Count() const {
  470. return stack_pos;
  471. }
  472. s32 NodeStates::Stack::top() const {
  473. ASSERT(stack_pos > 0);
  474. return stack[stack_pos - 1];
  475. }
  476. s32 NodeStates::Stack::pop() {
  477. ASSERT(stack_pos > 0);
  478. stack_pos--;
  479. return stack[stack_pos];
  480. }
  481. EdgeMatrix::EdgeMatrix() = default;
  482. EdgeMatrix::~EdgeMatrix() = default;
  483. void EdgeMatrix::Initialize(std::size_t _node_count) {
  484. node_count = _node_count;
  485. edge_matrix.resize(node_count * node_count);
  486. }
  487. bool EdgeMatrix::Connected(s32 a, s32 b) {
  488. return GetState(a, b);
  489. }
  490. void EdgeMatrix::Connect(s32 a, s32 b) {
  491. SetState(a, b, true);
  492. }
  493. void EdgeMatrix::Disconnect(s32 a, s32 b) {
  494. SetState(a, b, false);
  495. }
  496. void EdgeMatrix::RemoveEdges(s32 edge) {
  497. for (std::size_t i = 0; i < node_count; i++) {
  498. SetState(edge, static_cast<s32>(i), false);
  499. }
  500. }
  501. std::size_t EdgeMatrix::GetNodeCount() const {
  502. return node_count;
  503. }
  504. void EdgeMatrix::SetState(s32 a, s32 b, bool state) {
  505. ASSERT(InRange(a, b));
  506. edge_matrix.at(a * node_count + b) = state;
  507. }
  508. bool EdgeMatrix::GetState(s32 a, s32 b) {
  509. ASSERT(InRange(a, b));
  510. return edge_matrix.at(a * node_count + b);
  511. }
  512. bool EdgeMatrix::InRange(s32 a, s32 b) const {
  513. const std::size_t pos = a * node_count + b;
  514. return pos < (node_count * node_count);
  515. }
  516. } // namespace AudioCore