ACMX 2.136.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(
17 text.begin(), text.end(),
18 [](unsigned char character) { return std::isspace(character); });
19 const auto last = std::find_if_not(
20 text.rbegin(), text.rend(),
21 [](unsigned char character) { return std::isspace(character); })
22 .base();
23 if (first >= last) {
24 return {};
25 }
26 return std::string(first, last);
27 }
28 } // namespace
29
31 const std::vector<fs::path> &available_shaders,
32 const fs::path &library_directory, std::string name) {
33 name = trim_text(std::move(name));
34 if (name.empty()) {
35 return {};
36 }
37
38 fs::path requested(name);
39 if (requested.extension() != ".spv") {
40 requested += ".spv";
41 }
42 const auto match = std::find_if(
43 available_shaders.begin(), available_shaders.end(),
44 [&](const fs::path &shader) {
45 return shader.filename() == requested.filename() ||
46 (!library_directory.empty() &&
47 shader.lexically_relative(library_directory) ==
48 requested);
49 });
50 return match == available_shaders.end() ? fs::path{} : *match;
51 }
52
53 std::vector<PlaylistNode>
54 load_playlist(const fs::path &playlist_path,
55 const std::vector<fs::path> &available_shaders,
56 const fs::path &library_directory,
57 std::ostream &warning_output) {
58 input::validate_file_size(playlist_path, "shader playlist");
59 std::ifstream playlist_input(playlist_path);
60 if (!playlist_input) {
61 throw std::runtime_error("unable to open playlist: " +
62 playlist_path.string());
63 }
64
65 std::vector<PlaylistNode> playlist;
66 PlaylistNode *current_node = nullptr;
67 std::vector<fs::path> default_entries;
68 std::string line;
69 std::size_t line_number = 1;
70 std::size_t entry_count = 0;
71 while (input::read_bounded_line(playlist_input, line,
72 "shader playlist", line_number++)) {
73 line = trim_text(std::move(line));
74 if (line.empty() || line.front() == '#') {
75 continue;
76 }
77 if (line.size() >= 2 && line.front() == '[' &&
78 line.back() == ']') {
79 if (playlist.size() >= input::MAX_PLAYLIST_NODES) {
80 throw std::runtime_error(
81 "shader playlist contains too many nodes");
82 }
83 std::string node_name =
84 trim_text(line.substr(1, line.size() - 2));
85 input::validate_string(node_name,
87 "shader playlist node");
88 playlist.push_back({std::move(node_name), {}});
89 current_node = &playlist.back();
90 continue;
91 }
92 if (line.front() == '[' || line.back() == ']') {
93 throw std::runtime_error(
94 "malformed shader playlist node at line " +
95 std::to_string(line_number - 1));
96 }
97 if (++entry_count > input::MAX_PLAYLIST_ENTRIES) {
98 throw std::runtime_error(
99 "shader playlist contains too many entries");
100 }
102 "shader playlist entry");
103
104 const fs::path shader = find_shader_path(
105 available_shaders, library_directory, line);
106 if (shader.empty()) {
107 warning_output << "acmxvk: playlist shader not found: " << line
108 << '\n';
109 continue;
110 }
111 if (current_node != nullptr) {
112 current_node->shaders.push_back(shader);
113 } else {
114 default_entries.push_back(shader);
115 }
116 }
117
118 playlist.erase(std::remove_if(playlist.begin(), playlist.end(),
119 [](const PlaylistNode &node) {
120 return node.shaders.empty();
121 }),
122 playlist.end());
123 if (!default_entries.empty()) {
124 playlist.insert(playlist.begin(),
125 {"Default", std::move(default_entries)});
126 }
127 if (playlist.empty()) {
128 throw std::runtime_error(
129 "playlist contains no shaders available in the SPIR-V library");
130 }
131 return playlist;
132 }
133
134 std::size_t
135 playlist_shader_count(const std::vector<PlaylistNode> &playlist) noexcept {
136 std::size_t count = 0;
137 for (const PlaylistNode &node : playlist) {
138 count += node.shaders.size();
139 }
140 return count;
141 }
142} // 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