1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
// This file is part of the 64k demo project.
// It implements the ShaderComposer class.
#include "gpu/shader_composer.h"
#include <cassert>
#include <set>
#include <sstream>
ShaderComposer& ShaderComposer::Get() {
static ShaderComposer instance;
return instance;
}
void ShaderComposer::RegisterSnippet(const std::string& name,
const std::string& code) {
snippets_[name] = code;
}
void ShaderComposer::ResolveRecursive(const std::string& source,
std::stringstream& ss,
std::set<std::string>& included,
const CompositionMap& substitutions) {
std::istringstream stream(source);
std::string line;
while (std::getline(stream, line)) {
// Check for #include "snippet_name"
if (line.compare(0, 9, "#include ") == 0) {
size_t start = line.find('"');
size_t end = line.find('"', start + 1);
if (start != std::string::npos && end != std::string::npos) {
std::string name = line.substr(start + 1, end - start - 1);
// Apply substitution if available
auto sub_it = substitutions.find(name);
if (sub_it != substitutions.end()) {
name = sub_it->second;
}
if (included.find(name) == included.end()) {
included.insert(name);
auto it = snippets_.find(name);
if (it != snippets_.end()) {
ss << "// --- Included: " << name << " ---\n";
ResolveRecursive(it->second, ss, included, substitutions);
ss << "// --- End Include: " << name << " ---\n";
} else {
ss << "// ERROR: Snippet not found: " << name << "\n";
#if !defined(STRIP_ALL)
// Find suggestions: registered snippets whose basename matches.
std::string suggestion;
for (const auto& [sname, _] : snippets_) {
auto slash = sname.rfind('/');
std::string basename =
(slash == std::string::npos) ? sname : sname.substr(slash + 1);
if (basename == name) {
suggestion = sname;
break;
}
}
fprintf(stderr,
"FATAL: #include \"%s\" - snippet not registered.\n",
name.c_str());
if (!suggestion.empty()) {
fprintf(stderr, " Fix: use #include \"%s\"\n",
suggestion.c_str());
}
assert(false && "Unresolved shader #include");
#endif
}
}
}
} else {
ss << line << "\n";
}
}
}
std::string
ShaderComposer::Compose(const std::vector<std::string>& dependencies,
const std::string& main_code,
const CompositionMap& substitutions) {
std::stringstream ss;
ss << "// Generated by ShaderComposer\n\n";
std::set<std::string> included;
// Process explicit dependencies first
for (const auto& dep : dependencies) {
std::string name = dep;
auto sub_it = substitutions.find(name);
if (sub_it != substitutions.end()) {
name = sub_it->second;
}
if (included.find(name) == included.end()) {
included.insert(name);
auto it = snippets_.find(name);
if (it != snippets_.end()) {
ss << "// --- Dependency: " << name << " ---\n";
ResolveRecursive(it->second, ss, included, substitutions);
ss << "\n";
}
}
}
ss << "// --- Main Code ---\n";
ResolveRecursive(main_code, ss, included, substitutions);
return ss.str();
}
void ShaderComposer::VerifyIncludes() const {
#if !defined(STRIP_ALL)
// Known placeholders that get substituted at composition time
std::set<std::string> known_placeholders = {"render/scene_query_mode"};
std::set<std::string> missing;
for (const auto& [name, code] : snippets_) {
std::istringstream stream(code);
std::string line;
while (std::getline(stream, line)) {
if (line.compare(0, 9, "#include ") == 0) {
size_t start = line.find('"');
size_t end = line.find('"', start + 1);
if (start != std::string::npos && end != std::string::npos) {
std::string included = line.substr(start + 1, end - start - 1);
if (snippets_.find(included) == snippets_.end() &&
known_placeholders.find(included) == known_placeholders.end()) {
missing.insert(included);
}
}
}
}
}
if (!missing.empty()) {
fprintf(stderr, "ERROR: Unregistered shader snippets referenced in #include:\n");
for (const auto& name : missing) {
std::string suggestion;
for (const auto& [sname, _] : snippets_) {
auto slash = sname.rfind('/');
std::string basename =
(slash == std::string::npos) ? sname : sname.substr(slash + 1);
if (basename == name) {
suggestion = sname;
break;
}
}
if (!suggestion.empty()) {
fprintf(stderr, " - \"%s\" (did you mean \"%s\"?)\n", name.c_str(),
suggestion.c_str());
} else {
fprintf(stderr, " - \"%s\"\n", name.c_str());
}
}
assert(false && "Unregistered shader snippets - fix #include paths");
}
#endif
}
|