载入中...
搜索中...
未找到
GlslCompiler.cpp
浏览该文件的文档.
2
3#include <cstdio>
4#include <cstring>
5#include <stdexcept>
6
7#if defined(EVE_HAS_SHADERC)
8#include <shaderc/shaderc.h>
9#endif
10
11#if defined(_WIN32)
12#include <windows.h>
13#else
14#include <unistd.h>
15#endif
16
17namespace eve::graphics {
18namespace {
19
21constexpr std::uint32_t kSpirvMagic = 0x07230203u;
22
23std::runtime_error compileFailure(const std::string &message) { return std::runtime_error(message); }
24
26std::vector<std::uint32_t> spirvWords(const void *data, std::size_t size) {
27 if (!data || size < 4 || (size % 4) != 0)
28 throw compileFailure("compiled SPIR-V has an invalid size");
29 const auto *words = static_cast<const std::uint32_t *>(data);
30 if (words[0] != kSpirvMagic) throw compileFailure("compiled SPIR-V has a bad magic number");
31 return std::vector<std::uint32_t>(words, words + size / 4);
32}
33
35const char *stageName(GlslStage stage) {
36 switch (stage) {
37 case GlslStage::eVertex: return "vertex";
38 case GlslStage::eFragment: return "fragment";
39 case GlslStage::eCompute: return "compute";
40 }
41 return "shader";
42}
43
44#if !defined(EVE_HAS_SHADERC)
45
47const char *stageArgument(GlslStage stage) {
48 switch (stage) {
49 case GlslStage::eVertex: return "vert";
50 case GlslStage::eFragment: return "frag";
51 case GlslStage::eCompute: return "comp";
52 }
53 return "frag";
54}
55
57std::vector<std::uint32_t> readCompiledFile(const std::string &path, bool remove) {
58 FILE *file = nullptr;
59#if defined(_WIN32)
60 if (fopen_s(&file, path.c_str(), "rb") != 0) file = nullptr;
61#else
62 file = fopen(path.c_str(), "rb");
63#endif
64 if (!file) {
65 if (remove) std::remove(path.c_str());
66 throw compileFailure("failed to open compiled SPIR-V");
67 }
68 std::fseek(file, 0, SEEK_END);
69 const long size = std::ftell(file);
70 std::fseek(file, 0, SEEK_SET);
71 std::vector<std::uint8_t> bytes(static_cast<std::size_t>(size > 0 ? size : 0));
72 const bool shortRead =
73 size > 0 && std::fread(bytes.data(), 1, static_cast<std::size_t>(size), file) != static_cast<std::size_t>(size);
74 std::fclose(file);
75 if (remove) std::remove(path.c_str());
76 if (shortRead) throw compileFailure("failed to read compiled SPIR-V");
77 return spirvWords(bytes.data(), bytes.size());
78}
79
80#if defined(_WIN32)
81
83std::string findGlslc() {
84 if (const char *sdk = std::getenv("VULKAN_SDK"); sdk && *sdk) {
85 for (const char *sub : {"\\bin\\glslc.exe", "\\Bin\\glslc.exe"}) {
86 std::string path = std::string(sdk) + sub;
87 if (GetFileAttributesA(path.c_str()) != INVALID_FILE_ATTRIBUTES) return path;
88 }
89 }
90 // C:\VulkanSDK<version>\Bin\glslc.exe
91 WIN32_FIND_DATAA entry{};
92 HANDLE find = FindFirstFileA("C:\\VulkanSDK\\*", &entry);
93 if (find != INVALID_HANDLE_VALUE) {
94 do {
95 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
96 std::string path = std::string("C:\\VulkanSDK\\") + entry.cFileName + "\\Bin\\glslc.exe";
97 if (GetFileAttributesA(path.c_str()) != INVALID_FILE_ATTRIBUTES) {
98 FindClose(find);
99 return path;
100 }
101 }
102 } while (FindNextFileA(find, &entry));
103 FindClose(find);
104 }
105 FILE *pipe = _popen("where glslc 2>nul", "r");
106 if (pipe) {
107 char buffer[512];
108 std::string first;
109 // `where` lists every match on its own line and only the first path is usable as
110 // an application name, so stop at the first non-empty line instead of
111 // concatenating the remaining matches into a value with embedded newlines.
112 while (first.empty() && fgets(buffer, sizeof(buffer), pipe)) {
113 first = buffer;
114 while (!first.empty() && (first.back() == '\r' || first.back() == '\n')) first.pop_back();
115 }
116 const int status = _pclose(pipe);
117 if (status == 0 && !first.empty()) return first;
118 }
119 return {};
120}
121
129std::vector<std::uint32_t> compileWithExternalGlslc(const std::string &source, GlslStage stage,
130 const std::string &debugName) {
131 const std::string glslc = findGlslc();
132 if (glslc.empty())
133 throw compileFailure(
134 "no GLSL compiler available: this build has no in-process shaderc and no "
135 "glslc.exe was found (install the Vulkan SDK or set VULKAN_SDK)");
136
137 char tempDir[MAX_PATH];
138 if (GetTempPathA(MAX_PATH, tempDir) == 0) throw compileFailure("GetTempPath failed");
139 char inputPath[MAX_PATH];
140 if (GetTempFileNameA(tempDir, "eve", 0, inputPath) == 0) throw compileFailure("GetTempFileName failed");
141 const std::string outputPath = std::string(inputPath) + ".spv";
142 const std::string errorPath = outputPath + ".err";
143 struct TemporaryFiles {
144 const char *input;
145 const std::string &output;
146 const std::string &error;
147 ~TemporaryFiles() {
148 DeleteFileA(error.c_str());
149 DeleteFileA(output.c_str());
150 // Reserve the unique basename until both sidecars are consumed.
151 DeleteFileA(input);
152 }
153 } temporary{inputPath, outputPath, errorPath};
154
155 {
156 FILE *file = nullptr;
157 if (fopen_s(&file, inputPath, "wb") != 0 || !file) throw compileFailure("failed to write temp GLSL");
158 // A short write (disk full, for instance) must not reach the compiler as a
159 // truncated shader; the POSIX branch fails on the same condition.
160 const size_t written = fwrite(source.data(), 1, source.size(), file);
161 const bool flushed = fflush(file) == 0;
162 const bool closed = fclose(file) == 0;
163 if (written != source.size() || !flushed || !closed)
164 throw compileFailure("failed to write the complete temp GLSL");
165 }
166
167 std::string command = "\"" + glslc + "\" -fshader-stage=" + stageArgument(stage) + " \"" + inputPath +
168 "\" -o \"" + outputPath + "\"";
169 STARTUPINFOEXA startup{};
170 startup.StartupInfo.cb = sizeof(startup);
171 PROCESS_INFORMATION process{};
172 SECURITY_ATTRIBUTES security{};
173 security.nLength = sizeof(security);
174 security.bInheritHandle = TRUE;
175 HANDLE diagnostics =
176 CreateFileA(errorPath.c_str(), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, &security, CREATE_ALWAYS,
177 FILE_ATTRIBUTE_NORMAL, nullptr);
178 HANDLE input =
179 CreateFileA("NUL", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, &security, OPEN_EXISTING,
180 FILE_ATTRIBUTE_NORMAL, nullptr);
181 if (diagnostics == INVALID_HANDLE_VALUE || input == INVALID_HANDLE_VALUE) {
182 if (diagnostics != INVALID_HANDLE_VALUE) CloseHandle(diagnostics);
183 if (input != INVALID_HANDLE_VALUE) CloseHandle(input);
184 throw compileFailure("failed to create compiler standard handles");
185 }
186 struct StandardHandles {
187 HANDLE output, input;
188 ~StandardHandles() {
189 CloseHandle(output);
190 CloseHandle(input);
191 }
192 } handles{diagnostics, input};
193 // Without an explicit list, concurrent CreateProcess calls inherit other
194 // jobs' temporary-file handles and can prevent glslc from opening its input.
195 SIZE_T attributeBytes = 0;
196 InitializeProcThreadAttributeList(nullptr, 1, 0, &attributeBytes);
197 std::vector<std::uint8_t> attributes(attributeBytes);
198 startup.lpAttributeList = reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(attributes.data());
199 if (!InitializeProcThreadAttributeList(startup.lpAttributeList, 1, 0, &attributeBytes))
200 throw compileFailure("failed to initialize process attributes");
201 struct AttributeList {
202 LPPROC_THREAD_ATTRIBUTE_LIST value;
203 ~AttributeList() { DeleteProcThreadAttributeList(value); }
204 } attributeList{startup.lpAttributeList};
205 HANDLE inherited[] = {diagnostics, input};
206 if (!UpdateProcThreadAttribute(startup.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST, inherited,
207 sizeof(inherited), nullptr, nullptr))
208 throw compileFailure("failed to restrict compiler handle inheritance");
209 startup.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
210 startup.StartupInfo.hStdInput = input;
211 startup.StartupInfo.hStdOutput = diagnostics;
212 startup.StartupInfo.hStdError = diagnostics;
213 if (!CreateProcessA(glslc.c_str(), command.data(), nullptr, nullptr, TRUE,
214 CREATE_NO_WINDOW | EXTENDED_STARTUPINFO_PRESENT, nullptr, nullptr, &startup.StartupInfo,
215 &process)) {
216 throw compileFailure("failed to launch glslc (error " + std::to_string(GetLastError()) + ")");
217 }
218 WaitForSingleObject(process.hProcess, INFINITE);
219 DWORD exitCode = 0;
220 GetExitCodeProcess(process.hProcess, &exitCode);
221 CloseHandle(process.hProcess);
222 CloseHandle(process.hThread);
223 if (exitCode != 0) {
224 std::string errors;
225 HANDLE reader =
226 CreateFileA(errorPath.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
227 nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
228 if (reader != INVALID_HANDLE_VALUE) {
229 char buffer[512];
230 DWORD count = 0;
231 while (ReadFile(reader, buffer, sizeof(buffer), &count, nullptr) && count)
232 errors.append(buffer, count);
233 CloseHandle(reader);
234 }
235 throw compileFailure("glslc failed for " + debugName + " (exit " + std::to_string(exitCode) +
236 "):\n" + errors);
237 }
238 return readCompiledFile(outputPath, false);
239}
240
241#else // !_WIN32
242
244std::vector<std::uint32_t> compileWithExternalGlslc(const std::string &source, GlslStage stage,
245 const std::string &debugName) {
246 char inputPath[] = "/tmp/eve_shader_XXXXXX";
247 const int descriptor = mkstemp(inputPath);
248 if (descriptor < 0) throw compileFailure("mkstemp failed");
249 const std::string outputPath = std::string(inputPath) + ".spv";
250 {
251 const ssize_t written = write(descriptor, source.data(), source.size());
252 close(descriptor);
253 if (written < 0 || static_cast<std::size_t>(written) != source.size()) {
254 unlink(inputPath);
255 throw compileFailure("failed to write temp GLSL");
256 }
257 }
258
259 const std::string command = std::string("glslc -fshader-stage=") + stageArgument(stage) + " \"" + inputPath +
260 "\" -o \"" + outputPath + "\" 2>&1";
261 FILE *pipe = popen(command.c_str(), "r");
262 if (!pipe) {
263 unlink(inputPath);
264 throw compileFailure("glslc not available (popen failed)");
265 }
266 char buffer[256];
267 std::string errors;
268 while (fgets(buffer, sizeof(buffer), pipe)) errors += buffer;
269 const int status = pclose(pipe);
270 unlink(inputPath);
271 if (status != 0) {
272 unlink(outputPath.c_str());
273 throw compileFailure("glslc failed for " + debugName + ":\n" + errors);
274 }
275 return readCompiledFile(outputPath, true);
276}
277
279bool externalGlslcAvailable() {
280 FILE *pipe = popen("command -v glslc >/dev/null 2>&1", "r");
281 if (!pipe) return false;
282 return pclose(pipe) == 0;
283}
284
285#endif // _WIN32
286
287#endif // !EVE_HAS_SHADERC
288
289#if defined(EVE_HAS_SHADERC)
290std::vector<std::uint32_t> compileInProcess(const std::string &source, GlslStage stage, const std::string &debugName) {
291 struct CompilerContext {
292 shaderc_compiler_t compiler = shaderc_compiler_initialize();
293 shaderc_compile_options_t options = nullptr;
294 CompilerContext() {
295 if (!compiler) throw compileFailure("shaderc_compiler_initialize failed");
296 options = shaderc_compile_options_initialize();
297 if (!options) {
298 shaderc_compiler_release(compiler);
299 throw compileFailure("shaderc_compile_options_initialize failed");
300 }
301 shaderc_compile_options_set_target_env(options, shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_0);
302 shaderc_compile_options_set_target_spirv(options, shaderc_spirv_version_1_0);
303 }
304 ~CompilerContext() {
305 shaderc_compile_options_release(options);
306 shaderc_compiler_release(compiler);
307 }
308 };
309 // Keep glslang's compiler/built-in state alive across jobs and graph segments.
310 // Each CPU thread owns its context; no device references survive here.
311 thread_local CompilerContext context;
312 shaderc_shader_kind kind = shaderc_glsl_vertex_shader;
313 switch (stage) {
314 case GlslStage::eVertex: kind = shaderc_glsl_vertex_shader; break;
315 case GlslStage::eFragment: kind = shaderc_glsl_fragment_shader; break;
316 case GlslStage::eCompute: kind = shaderc_glsl_compute_shader; break;
317 }
318 shaderc_compilation_result_t result = shaderc_compile_into_spv(
319 context.compiler, source.data(), source.size(), kind, debugName.c_str(), "main", context.options);
320 if (!result) throw compileFailure("shaderc returned no compilation result");
321 struct CompilationResult {
322 shaderc_compilation_result_t value;
323 ~CompilationResult() { shaderc_result_release(value); }
324 } ownedResult{result};
325 if (shaderc_result_get_compilation_status(result) != shaderc_compilation_status_success)
326 throw compileFailure("shaderc failed:\n" + std::string(shaderc_result_get_error_message(result)));
327 const std::size_t length = shaderc_result_get_length(result);
328 return spirvWords(shaderc_result_get_bytes(result), length);
329}
330#endif
331
332} // namespace
333
335#if defined(EVE_HAS_SHADERC)
336 return true;
337#elif defined(_WIN32)
338 // findGlslc() walks the SDK directories and may spawn `where`, so probe once.
339 static const bool available = !findGlslc().empty();
340 return available;
341#else
342 static const bool available = externalGlslcAvailable();
343 return available;
344#endif
345}
346
347std::vector<std::uint32_t> compileGlslToSpirv(const std::string &source, GlslStage stage,
348 const std::string &debugName) {
349 if (source.empty()) throw compileFailure(std::string("empty ") + stageName(stage) + " GLSL source");
350#if defined(EVE_HAS_SHADERC)
351 return compileInProcess(source, stage, debugName);
352#else
353 return compileWithExternalGlslc(source, stage, debugName);
354#endif
355}
356
357} // namespace eve::graphics
double value
std::string output
std::string descriptor
float length
Definition CaveMesh.cpp:94
filesystem::File * file
EvpackChunkInput input
Definition Evpack.cpp:170
std::string message
wgpu::PopErrorScopeStatus status
std::int32_t first
TokenKind kind
std::uint64_t bytes
onnx_detail::CompilerQueue compiler
Definition OnnxGpgpu.cpp:45
std::unique_ptr< gpgpu::GpuBuffer > buffer
Definition OnnxGpgpu.cpp:26
std::string error
Definition Package.cpp:60
std::string path
Definition PlayHost.cpp:110
std::uint32_t count
const SquirrelValueOptions & options
float size
Definition TreeMesh.cpp:156
const UnitySourceAsset & source
const VegetationPresetContext & context
卡牌游戏 UI 工具模块:工厂 + 脚本绑定入口。 功能参考 ycarowr/UiCard:扇形手牌布局、抽牌/洗牌、悬浮放大、拖拽到落牌区、 敌方手牌(背面/偷看)、费用不足置灰,以及可实时调节的布局...
Definition Animation.h:25
bool glslRuntimeCompilationAvailable()
Whether this build can compile GLSL text to SPIR-V at runtime.
std::vector< std::uint32_t > compileGlslToSpirv(const std::string &source, GlslStage stage, const std::string &debugName)
Compile GLSL text to SPIR-V words.
GlslStage
GLSL stage accepted by compileGlslToSpirv.