ACMX 2.139.0
Dual-Backend Real-Time GPU Video Synthesis
Loading...
Searching...
No Matches
ACMXVK/app/playlist.cpp
Go to the documentation of this file.
1#include "playlist.hpp"
2
4
5#include <algorithm>
6#include <cctype>
7#include <fstream>
8#include <ostream>
9#include <stdexcept>
10#include <string_view>
11#include <utility>
12
13namespace acmxvk {
14 namespace {
15 [[nodiscard]] std::string trim_text(std::string text) {
16 const auto first = std::find_if_not(text.begin(), text.end(), [](unsigned char character) { return std::isspace(character); });
17 const auto last = std::find_if_not(text.rbegin(), text.rend(), [](unsigned char character) { return std::isspace(character); }).base();
18 if (first >= last) {
19 return {};
20 }
21 return std::string(first, last);
22 }
23 } // namespace
24
25 fs::path find_shader_path(const std::vector<fs::path> &available_shaders, const fs::path &library_directory, std::string name) {
26 name = trim_text(std::move(name));
27 if (name.empty()) {
28 return {};
29 }
30
31 fs::path requested(name);
32 if (requested.extension() != ".spv") {
33 requested += ".spv";
34 }
35 const auto match = std::find_if(available_shaders.begin(), available_shaders.end(), [&](const fs::path &shader) { return shader.filename() == requested.filename() || (!library_directory.empty() && shader.lexically_relative(library_directory) == requested); });
36 return match == available_shaders.end() ? fs::path{} : *match;
37 }
38
39 std::vector<PlaylistNode> load_playlist(const fs::path &playlist_path, const std::vector<fs::path> &available_shaders, const fs::path &library_directory, std::ostream &warning_output) {
40 input::validate_file_size(playlist_path, "shader playlist");
41 std::ifstream playlist_input(playlist_path);
42 if (!playlist_input) {
43 throw std::runtime_error("unable to open playlist: " + playlist_path.string());
44 }
45
46 std::vector<PlaylistNode> playlist;
47 PlaylistNode *current_node = nullptr;
48 std::vector<fs::path> default_entries;
49 std::string line;
50 std::size_t line_number = 1;
51 std::size_t entry_count = 0;
52 while (input::read_bounded_line(playlist_input, line, "shader playlist", line_number++)) {
53 line = trim_text(std::move(line));
54 if (line.empty() || line.front() == '#') {
55 continue;
56 }
57 if (line.size() >= 2 && line.front() == '[' && line.back() == ']') {
58 if (playlist.size() >= input::MAX_PLAYLIST_NODES) {
59 throw std::runtime_error("shader playlist contains too many nodes");
60 }
61 std::string node_name = trim_text(line.substr(1, line.size() - 2));
62 input::validate_string(node_name, input::StringKind::DisplayText, "shader playlist node");
63 playlist.push_back({std::move(node_name), {}});
64 current_node = &playlist.back();
65 continue;
66 }
67 if (line.front() == '[' || line.back() == ']') {
68 throw std::runtime_error("malformed shader playlist node at line " + std::to_string(line_number - 1));
69 }
70 if (++entry_count > input::MAX_PLAYLIST_ENTRIES) {
71 throw std::runtime_error("shader playlist contains too many entries");
72 }
73 input::validate_string(line, input::StringKind::Path, "shader playlist entry");
74
75 const fs::path shader = find_shader_path(available_shaders, library_directory, line);
76 if (shader.empty()) {
77 warning_output << "acmxvk: playlist shader not found: " << line << '\n';
78 continue;
79 }
80 if (current_node != nullptr) {
81 current_node->shaders.push_back(shader);
82 } else {
83 default_entries.push_back(shader);
84 }
85 }
86
87 playlist.erase(std::remove_if(playlist.begin(), playlist.end(), [](const PlaylistNode &node) { return node.shaders.empty(); }), playlist.end());
88 if (!default_entries.empty()) {
89 playlist.insert(playlist.begin(), {"Default", std::move(default_entries)});
90 }
91 if (playlist.empty()) {
92 throw std::runtime_error("playlist contains no shaders available in the SPIR-V library");
93 }
94 return playlist;
95 }
96
97 std::size_t playlist_shader_count(const std::vector<PlaylistNode> &playlist) noexcept {
98 std::size_t count = 0;
99 for (const PlaylistNode &node : playlist) {
100 count += node.shaders.size();
101 }
102 return count;
103 }
104} // namespace acmxvk
bool read_bounded_line(std::istream &input, std::string &line, std::string_view context, std::size_t line_number, std::size_t maximum_bytes)
constexpr std::size_t MAX_PLAYLIST_NODES
void validate_file_size(const std::filesystem::path &path, std::string_view context, std::uintmax_t maximum_bytes)
constexpr std::size_t MAX_PLAYLIST_ENTRIES
void validate_string(std::string_view value, StringKind kind, std::string_view context, bool allow_empty)
std::vector< PlaylistNode > load_playlist(const fs::path &playlist_path, const std::vector< fs::path > &available_shaders, const fs::path &library_directory, std::ostream &warning_output)
std::size_t playlist_shader_count(const std::vector< PlaylistNode > &playlist) noexcept
fs::path find_shader_path(const std::vector< fs::path > &available_shaders, const fs::path &library_directory, std::string name)
std::vector< fs::path > shaders