载入中...
搜索中...
未找到
VulkanUtil.cpp
浏览该文件的文档.
2
3#include "common/Exception.h"
4#include "common/Module.h"
8
9#include <cstdio>
10#include <cstdlib>
11#include <cstring>
12#include <memory>
13#include <vector>
14
15#if defined(_WIN32)
16#include <windows.h>
17#if defined(EVE_HAS_SHADERC)
18#include <shaderc/shaderc.h>
19#endif
20#else
21#include <unistd.h>
22#endif
23
24namespace eve::gpgpu {
25namespace {
26
27std::vector<uint32_t> loadSpirvBytes(const void *data, size_t size) {
28 if (!data || size < 4 || (size % 4) != 0)
29 throw Exception("Gpgpu SPIR-V: invalid size %zu", size);
30 const auto *words = static_cast<const uint32_t *>(data);
31 if (words[0] != 0x07230203)
32 throw Exception("Gpgpu SPIR-V: bad magic (expected 0x07230203)");
33 return std::vector<uint32_t>(words, words + size / 4);
34}
35
36} // namespace
37
39 auto *gfx = eve::ModuleManager::getInstance<eve::graphics::Graphics>("Graphics");
40 if (!gfx) gfx = eve::graphics::Graphics::create();
41 auto *vkg = dynamic_cast<graphics::vulkan::Graphics *>(gfx);
42 if (!vkg) throw Exception("Gpgpu: requires Vulkan Graphics backend");
43 if (!static_cast<VkDevice>(vkg->getDevice().instance))
44 throw Exception("Gpgpu: Graphics device not initialized (create a window first)");
45 return vkg;
46}
47
49 try {
50 auto *gfx = eve::ModuleManager::getInstance<eve::graphics::Graphics>("Graphics");
51 if (!gfx) return false;
52 auto *vkg = dynamic_cast<graphics::vulkan::Graphics *>(gfx);
53 if (!vkg) return false;
54 return static_cast<VkDevice>(vkg->getDevice().instance) != VK_NULL_HANDLE;
55 } catch (...) {
56 return false;
57 }
58}
59
61 // Must match computeCommandPool (uploadPool is graphics-family). Only use a
62 // dedicated compute queue when it shares that family; otherwise submit on graphics.
63 auto &device = vkg->getDevice();
64 const uint32_t graphicsFamily = device.get_queue_index(vkb::QueueType::graphics);
65 vk::Queue compute = device.getQueue(vkb::QueueType::compute);
66 if (compute && device.get_queue_index(vkb::QueueType::compute) == graphicsFamily)
67 return compute;
68 return device.getQueue(vkb::QueueType::graphics);
69}
70
72 return vkg->getUploadPool();
73}
74
75std::vector<uint32_t> loadSpirvFile(const std::string &path) {
76 auto *fs = eve::filesystem::Filesystem::create();
77 std::unique_ptr<eve::filesystem::FileData> fd(fs->read(path));
78 if (!fd) throw Exception("Gpgpu.newShaderFromSpvFile: failed to read '%s'", path.c_str());
79 return loadSpirvBytes(fd->getData(), fd->getSize());
80}
81
82#if defined(_WIN32)
83namespace {
84
85#if defined(EVE_HAS_SHADERC)
87std::vector<uint32_t> compileComputeGlslInProcess(const std::string &glsl) {
88 shaderc_compiler_t compiler = shaderc_compiler_initialize();
89 if (!compiler) throw Exception("Gpgpu.newShader: shaderc_compiler_initialize failed");
90 shaderc_compile_options_t options = shaderc_compile_options_initialize();
91 if (!options) {
92 shaderc_compiler_release(compiler);
93 throw Exception("Gpgpu.newShader: shaderc_compile_options_initialize failed");
94 }
95 shaderc_compile_options_set_target_env(options, shaderc_target_env_vulkan,
96 shaderc_env_version_vulkan_1_0);
97 shaderc_compile_options_set_target_spirv(options, shaderc_spirv_version_1_0);
98 shaderc_compilation_result_t result =
99 shaderc_compile_into_spv(compiler, glsl.data(), glsl.size(),
100 shaderc_glsl_compute_shader, "eve_compute.comp", "main",
101 options);
102 shaderc_compile_options_release(options);
103 shaderc_compiler_release(compiler);
104 if (shaderc_result_get_compilation_status(result) != shaderc_compilation_status_success) {
105 const std::string err = shaderc_result_get_error_message(result);
106 shaderc_result_release(result);
107 throw Exception("Gpgpu.newShader: shaderc failed:\n%s", err.c_str());
108 }
109 const size_t len = shaderc_result_get_length(result);
110 const char *bytes = shaderc_result_get_bytes(result);
111 if (len == 0 || len % 4 != 0) {
112 shaderc_result_release(result);
113 throw Exception("Gpgpu.newShader: shaderc returned invalid SPIR-V");
114 }
115 std::vector<uint32_t> spv(len / 4);
116 std::memcpy(spv.data(), bytes, len);
117 shaderc_result_release(result);
118 return spv;
119}
120#endif // EVE_HAS_SHADERC
121
123std::string findGlslc() {
124 if (const char *sdk = std::getenv("VULKAN_SDK"); sdk && *sdk) {
125 for (const char *sub : {"\\bin\\glslc.exe", "\\Bin\\glslc.exe"}) {
126 std::string p = std::string(sdk) + sub;
127 if (GetFileAttributesA(p.c_str()) != INVALID_FILE_ATTRIBUTES) return p;
128 }
129 }
130 // C:\VulkanSDK<version>\Bin\glslc.exe
131 WIN32_FIND_DATAA fd{};
132 HANDLE h = FindFirstFileA("C:\\VulkanSDK\\*", &fd);
133 if (h != INVALID_HANDLE_VALUE) {
134 do {
135 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
136 std::string p = std::string("C:\\VulkanSDK\\") + fd.cFileName + "\\Bin\\glslc.exe";
137 if (GetFileAttributesA(p.c_str()) != INVALID_FILE_ATTRIBUTES) {
138 FindClose(h);
139 return p;
140 }
141 }
142 } while (FindNextFileA(h, &fd));
143 FindClose(h);
144 }
145 FILE *pipe = _popen("where glslc 2>nul", "r");
146 if (pipe) {
147 char buf[512];
148 std::string result;
149 while (fgets(buf, sizeof(buf), pipe)) result += buf;
150 int status = _pclose(pipe);
151 if (status == 0 && !result.empty()) {
152 while (!result.empty() && (result.back() == '\r' || result.back() == '\n'))
153 result.pop_back();
154 return result;
155 }
156 }
157 return {};
158}
159
160} // namespace
161#endif
162
163std::vector<uint32_t> compileComputeGlsl(const std::string &glsl) {
164 if (glsl.empty()) throw Exception("Gpgpu.newShader: empty GLSL");
165#if defined(_WIN32)
166#if defined(EVE_HAS_SHADERC)
167 try {
168 return compileComputeGlslInProcess(glsl);
169 } catch (...) {
170 // Fall through to spawning glslc (SDK lib present but compile failed).
171 }
172#endif
173 const std::string glslc = findGlslc();
174 if (glslc.empty())
175 throw Exception("Gpgpu.newShader: glslc not found on Windows "
176 "(install the Vulkan SDK or set VULKAN_SDK)");
177
178 char tmpDir[MAX_PATH];
179 if (GetTempPathA(MAX_PATH, tmpDir) == 0)
180 throw Exception("Gpgpu.newShader: GetTempPath failed");
181 char inPath[MAX_PATH];
182 if (GetTempFileNameA(tmpDir, "eve", 0, inPath) == 0)
183 throw Exception("Gpgpu.newShader: GetTempFileName failed");
184 std::string outPath = std::string(inPath) + ".spv";
185
186 {
187 FILE *f = nullptr;
188 if (fopen_s(&f, inPath, "wb") != 0 || !f) {
189 DeleteFileA(inPath);
190 throw Exception("Gpgpu.newShader: failed to write temp GLSL");
191 }
192 fwrite(glsl.data(), 1, glsl.size(), f);
193 fclose(f);
194 }
195
196 // Spawn glslc directly with CreateProcess: _popen routes through cmd.exe
197 // /c whose quote-stripping rules mangle commands starting with a quoted
198 // program path. Capture stderr/stdout into a sidecar file.
199 const std::string errPath = outPath + ".err";
200 std::string cmd = "\"" + glslc + "\" -fshader-stage=comp \"" + inPath + "\" -o \"" +
201 outPath + "\"";
202 STARTUPINFOA si{};
203 si.cb = sizeof(si);
204 PROCESS_INFORMATION pi{};
205 SECURITY_ATTRIBUTES sa{};
206 sa.nLength = sizeof(sa);
207 sa.bInheritHandle = TRUE;
208 HANDLE errFile = CreateFileA(errPath.c_str(), GENERIC_WRITE, FILE_SHARE_READ, &sa,
209 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
210 if (errFile != INVALID_HANDLE_VALUE) {
211 si.dwFlags = STARTF_USESTDHANDLES;
212 si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
213 si.hStdOutput = errFile;
214 si.hStdError = errFile;
215 }
216 char *mutableCmd = _strdup(cmd.c_str());
217 const BOOL spawned =
218 CreateProcessA(glslc.c_str(), mutableCmd, nullptr, nullptr, TRUE, CREATE_NO_WINDOW,
219 nullptr, nullptr, &si, &pi);
220 free(mutableCmd);
221 if (errFile != INVALID_HANDLE_VALUE) CloseHandle(errFile);
222 if (!spawned) {
223 const DWORD errCode = GetLastError();
224 DeleteFileA(inPath);
225 DeleteFileA(outPath.c_str());
226 DeleteFileA(errPath.c_str());
227 throw Exception("Gpgpu.newShader: failed to launch glslc (error %lu)", errCode);
228 }
229 WaitForSingleObject(pi.hProcess, INFINITE);
230 DWORD exitCode = 0;
231 GetExitCodeProcess(pi.hProcess, &exitCode);
232 CloseHandle(pi.hProcess);
233 CloseHandle(pi.hThread);
234 DeleteFileA(inPath);
235 if (exitCode != 0) {
236 std::string err;
237 FILE *ef = nullptr;
238 if (fopen_s(&ef, errPath.c_str(), "rb") == 0 && ef) {
239 char buf[512];
240 size_t n;
241 while ((n = fread(buf, 1, sizeof(buf) - 1, ef)) > 0) {
242 buf[n] = '\0';
243 err += buf;
244 }
245 fclose(ef);
246 }
247 DeleteFileA(outPath.c_str());
248 DeleteFileA(errPath.c_str());
249 throw Exception("Gpgpu.newShader: glslc failed (exit %lu):\n%s", exitCode, err.c_str());
250 }
251 DeleteFileA(errPath.c_str());
252
253 FILE *f = nullptr;
254 if (fopen_s(&f, outPath.c_str(), "rb") != 0 || !f) {
255 DeleteFileA(outPath.c_str());
256 throw Exception("Gpgpu.newShader: failed to open compiled SPIR-V");
257 }
258 fseek(f, 0, SEEK_END);
259 long sz = ftell(f);
260 fseek(f, 0, SEEK_SET);
261 std::vector<uint8_t> bytes(static_cast<size_t>(sz > 0 ? sz : 0));
262 if (sz > 0 && fread(bytes.data(), 1, static_cast<size_t>(sz), f) != static_cast<size_t>(sz)) {
263 fclose(f);
264 DeleteFileA(outPath.c_str());
265 throw Exception("Gpgpu.newShader: failed to read compiled SPIR-V");
266 }
267 fclose(f);
268 DeleteFileA(outPath.c_str());
269 return loadSpirvBytes(bytes.data(), bytes.size());
270#else
271 char inPath[] = "/tmp/eve_comp_XXXXXX";
272 int fd = mkstemp(inPath);
273 if (fd < 0) throw Exception("Gpgpu.newShader: mkstemp failed");
274 std::string outPath = std::string(inPath) + ".spv";
275 {
276 ssize_t n = write(fd, glsl.data(), glsl.size());
277 close(fd);
278 if (n < 0 || size_t(n) != glsl.size()) {
279 unlink(inPath);
280 throw Exception("Gpgpu.newShader: failed to write temp GLSL");
281 }
282 }
283
284 std::string cmd =
285 std::string("glslc -fshader-stage=comp \"") + inPath + "\" -o \"" + outPath + "\" 2>&1";
286 FILE *pipe = popen(cmd.c_str(), "r");
287 std::string err;
288 if (pipe) {
289 char buf[256];
290 while (fgets(buf, sizeof(buf), pipe)) err += buf;
291 int status = pclose(pipe);
292 unlink(inPath);
293 if (status != 0) {
294 unlink(outPath.c_str());
295 throw Exception("Gpgpu.newShader: glslc failed:\n%s", err.c_str());
296 }
297 } else {
298 unlink(inPath);
299 throw Exception("Gpgpu.newShader: glslc not available (popen failed)");
300 }
301
302 FILE *f = fopen(outPath.c_str(), "rb");
303 if (!f) {
304 unlink(outPath.c_str());
305 throw Exception("Gpgpu.newShader: failed to open compiled SPIR-V");
306 }
307 fseek(f, 0, SEEK_END);
308 long sz = ftell(f);
309 fseek(f, 0, SEEK_SET);
310 std::vector<uint8_t> bytes(static_cast<size_t>(sz > 0 ? sz : 0));
311 if (sz > 0 && fread(bytes.data(), 1, static_cast<size_t>(sz), f) != static_cast<size_t>(sz)) {
312 fclose(f);
313 unlink(outPath.c_str());
314 throw Exception("Gpgpu.newShader: failed to read compiled SPIR-V");
315 }
316 fclose(f);
317 unlink(outPath.c_str());
318 return loadSpirvBytes(bytes.data(), bytes.size());
319#endif
320}
321
322} // namespace eve::gpgpu
vkb::Device & device
glm::vec3 n
Definition Grass.cpp:64
int h
JobStatus status
float f
glm::vec4 p[6]
Light2D::Data * data
vk::CommandPool getUploadPool() const
Definition Graphics.h:403
bool vulkanGraphicsReady()
True if Vulkan Graphics device is ready.
std::vector< uint32_t > loadSpirvFile(const std::string &path)
std::vector< uint32_t > compileComputeGlsl(const std::string &glsl)
vk::Queue computeQueue(graphics::vulkan::Graphics *vkg)
graphics::vulkan::Graphics * requireVulkanGraphics()
Resolve live Vulkan Graphics (throws if missing / not initialized).
vk::CommandPool computeCommandPool(graphics::vulkan::Graphics *vkg)