Blender V4.3
vk_shader_compiler.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "BKE_appdir.hh"
10
11#include "BLI_fileops.hh"
12#include "BLI_hash.hh"
13#include "BLI_path_utils.hh"
14#include "BLI_time.h"
15#ifdef _WIN32
16# include "BLI_winstuff.h"
17#endif
18
19#include "vk_shader.hh"
20#include "vk_shader_compiler.hh"
21
22namespace blender::gpu {
27
29{
31 BLI_task_pool_free(task_pool_);
32 task_pool_ = nullptr;
33}
34
35/* -------------------------------------------------------------------- */
43
44static std::optional<std::string> cache_dir_get()
45{
46 static std::optional<std::string> result;
47 if (!result.has_value()) {
48 static char tmp_dir_buffer[FILE_MAX];
49 /* Shader builder doesn't return the correct appdir*/
50 if (!BKE_appdir_folder_caches(tmp_dir_buffer, sizeof(tmp_dir_buffer))) {
51 return std::nullopt;
52 }
53
54 std::string cache_dir = std::string(tmp_dir_buffer) + "vk-spirv-cache" + SEP_STR;
55 BLI_dir_create_recursive(cache_dir.c_str());
56 result = cache_dir;
57 }
58
59 return result;
60}
61
62static bool read_spirv_from_disk(VKShaderModule &shader_module)
63{
64 if (G.debug & G_DEBUG_GPU_RENDERDOC) {
65 /* RenderDoc uses spirv shaders including debug information. */
66 return false;
67 }
68 std::optional<std::string> cache_dir = cache_dir_get();
69 if (!cache_dir.has_value()) {
70 return false;
71 }
72 shader_module.build_sources_hash();
73 std::string spirv_path = (*cache_dir) + SEP_STR + shader_module.sources_hash + ".spv";
74 std::string sidecar_path = (*cache_dir) + SEP_STR + shader_module.sources_hash + ".sidecar.bin";
75
76 if (!BLI_exists(spirv_path.c_str()) || !BLI_exists(sidecar_path.c_str())) {
77 return false;
78 }
79
80 BLI_file_touch(spirv_path.c_str());
81 BLI_file_touch(sidecar_path.c_str());
82
83 /* Read sidecar*/
84 fstream sidecar_file(sidecar_path, std::ios::binary | std::ios::in | std::ios::ate);
85 std::streamsize sidecar_size_on_disk = sidecar_file.tellg();
86 SPIRVSidecar sidecar = {};
87 if (sidecar_size_on_disk != sizeof(sidecar)) {
88 return false;
89 }
90 sidecar_file.seekg(0, std::ios::beg);
91 sidecar_file.read(reinterpret_cast<char *>(&sidecar), sizeof(sidecar));
92
93 /* Read spirv binary */
94 fstream spirv_file(spirv_path, std::ios::binary | std::ios::in | std::ios::ate);
95 std::streamsize size = spirv_file.tellg();
96 if (size != sidecar.spirv_size) {
97 return false;
98 }
99 spirv_file.seekg(0, std::ios::beg);
100 shader_module.spirv_binary.resize(size / 4);
101 spirv_file.read(reinterpret_cast<char *>(shader_module.spirv_binary.data()), size);
102 return true;
103}
104
105static void write_spirv_to_disk(VKShaderModule &shader_module)
106{
107 if (G.debug & G_DEBUG_GPU_RENDERDOC) {
108 return;
109 }
110 std::optional<std::string> cache_dir = cache_dir_get();
111 if (!cache_dir.has_value()) {
112 return;
113 }
114
115 /* Write the spirv binary */
116 std::string spirv_path = (*cache_dir) + SEP_STR + shader_module.sources_hash + ".spv";
117 size_t size = (shader_module.compilation_result.end() -
118 shader_module.compilation_result.begin()) *
119 sizeof(uint32_t);
120 fstream spirv_file(spirv_path, std::ios::binary | std::ios::out);
121 spirv_file.write(reinterpret_cast<const char *>(shader_module.compilation_result.begin()), size);
122
123 /* Write the sidecar */
124 SPIRVSidecar sidecar = {size};
125 std::string sidecar_path = (*cache_dir) + SEP_STR + shader_module.sources_hash + ".sidecar.bin";
126 fstream sidecar_file(sidecar_path, std::ios::binary | std::ios::out);
127 sidecar_file.write(reinterpret_cast<const char *>(&sidecar), sizeof(SPIRVSidecar));
128}
129
131{
132 std::optional<std::string> cache_dir = cache_dir_get();
133 if (!cache_dir.has_value()) {
134 return;
135 }
136
137 direntry *entries = nullptr;
138 uint32_t dir_len = BLI_filelist_dir_contents(cache_dir->c_str(), &entries);
139 for (int i : blender::IndexRange(dir_len)) {
140 direntry entry = entries[i];
141 if (S_ISDIR(entry.s.st_mode)) {
142 continue;
143 }
144 const time_t ts_now = time(nullptr);
145 const time_t delete_threshold = 60 /*seconds*/ * 60 /*minutes*/ * 24 /*hours*/ * 30 /*days*/;
146 if (entry.s.st_mtime + delete_threshold < ts_now) {
147 BLI_delete(entry.path, false, false);
148 }
149 }
150 BLI_filelist_free(entries, dir_len);
151}
152
155/* -------------------------------------------------------------------- */
160{
161 std::scoped_lock lock(mutex_);
162 BatchHandle handle = next_batch_handle_++;
163 VKBatch &batch = batches_.lookup_or_add_default(handle);
164 batch.shaders.reserve(infos.size());
165 for (const shader::ShaderCreateInfo *info : infos) {
166 Shader *shader = compile(*info, true);
167 batch.shaders.append(shader);
168 }
169 for (Shader *shader : batch.shaders) {
170 BLI_task_pool_push(task_pool_, run, shader, false, nullptr);
171 }
172 return handle;
173}
174
175static const std::string to_stage_name(shaderc_shader_kind stage)
176{
177 switch (stage) {
178 case shaderc_vertex_shader:
179 return std::string("vertex");
180 case shaderc_geometry_shader:
181 return std::string("geometry");
182 case shaderc_fragment_shader:
183 return std::string("fragment");
184 case shaderc_compute_shader:
185 return std::string("compute");
186
187 default:
188 BLI_assert_msg(false, "Do not know how to convert shaderc_shader_kind to stage name.");
189 break;
190 }
191 return std::string("unknown stage");
192}
193
194static bool compile_ex(shaderc::Compiler &compiler,
195 VKShader &shader,
196 shaderc_shader_kind stage,
197 VKShaderModule &shader_module)
198{
199 if (read_spirv_from_disk(shader_module)) {
200 return true;
201 }
202
203 shaderc::CompileOptions options;
204 options.SetOptimizationLevel(shaderc_optimization_level_performance);
205 options.SetTargetEnvironment(shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_2);
206 if (G.debug & G_DEBUG_GPU_RENDERDOC) {
207 options.SetOptimizationLevel(shaderc_optimization_level_zero);
208 options.SetGenerateDebugInfo();
209 }
210
211 /* WORKAROUND: Qualcomm driver can crash when handling optimized SPIR-V. */
213 options.SetOptimizationLevel(shaderc_optimization_level_zero);
214 }
215
216 std::string full_name = std::string(shader.name_get()) + "_" + to_stage_name(stage);
217 shader_module.compilation_result = compiler.CompileGlslToSpv(
218 shader_module.combined_sources, stage, full_name.c_str(), options);
219 bool compilation_succeeded = shader_module.compilation_result.GetCompilationStatus() ==
220 shaderc_compilation_status_success;
221 if (compilation_succeeded) {
222 write_spirv_to_disk(shader_module);
223 }
224 return compilation_succeeded;
225}
226
228 shaderc_shader_kind stage,
229 VKShaderModule &shader_module)
230{
231 shaderc::Compiler compiler;
232 return compile_ex(compiler, shader, stage, shader_module);
233}
234
235void VKShaderCompiler::run(TaskPool *__restrict /*pool*/, void *task_data)
236{
237 VKShader &shader = *static_cast<VKShader *>(task_data);
238 shaderc::Compiler compiler;
239
240 bool has_not_succeeded = false;
241 if (!shader.vertex_module.is_ready) {
242 bool compilation_succeeded = compile_ex(
243 compiler, shader, shaderc_vertex_shader, shader.vertex_module);
244 has_not_succeeded |= !compilation_succeeded;
245 shader.vertex_module.is_ready = true;
246 }
247 if (!shader.geometry_module.is_ready) {
248 bool compilation_succeeded = compile_ex(
249 compiler, shader, shaderc_geometry_shader, shader.geometry_module);
250 has_not_succeeded |= !compilation_succeeded;
251 shader.geometry_module.is_ready = true;
252 }
253 if (!shader.fragment_module.is_ready) {
254 bool compilation_succeeded = compile_ex(
255 compiler, shader, shaderc_fragment_shader, shader.fragment_module);
256 has_not_succeeded |= !compilation_succeeded;
257 shader.fragment_module.is_ready = true;
258 }
259 if (!shader.compute_module.is_ready) {
260 bool compilation_succeeded = compile_ex(
261 compiler, shader, shaderc_compute_shader, shader.compute_module);
262 has_not_succeeded |= !compilation_succeeded;
263 shader.compute_module.is_ready = true;
264 }
265 if (has_not_succeeded) {
266 shader.compilation_failed = true;
267 }
268 shader.compilation_finished = true;
269 shader.finalize_post();
270}
271
273{
274 std::scoped_lock lock(mutex_);
275 BLI_assert(batches_.contains(handle));
276 VKBatch &batch = batches_.lookup(handle);
277 for (Shader *shader_ : batch.shaders) {
278 VKShader &shader = *unwrap(shader_);
279 if (!shader.is_ready()) {
280 return false;
281 }
282 }
283 return true;
284}
285
287{
288 while (!batch_is_ready(handle)) {
290 }
291 std::scoped_lock lock(mutex_);
292
293 BLI_assert(batches_.contains(handle));
294 VKBatch batch = batches_.pop(handle);
295 handle = 0;
296 return batch.shaders;
297}
298
301} // namespace blender::gpu
bool BKE_appdir_folder_caches(char *path, size_t path_maxncpy) ATTR_NONNULL(1)
Definition appdir.cc:226
@ G_DEBUG_GPU_RENDERDOC
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
bool BLI_file_touch(const char *filepath) ATTR_NONNULL(1)
Definition fileops_c.cc:316
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:350
bool BLI_dir_create_recursive(const char *dirname) ATTR_NONNULL()
Definition fileops_c.cc:391
unsigned int BLI_filelist_dir_contents(const char *dirname, struct direntry **r_filelist)
int BLI_delete(const char *path, bool dir, bool recursive) ATTR_NONNULL()
void BLI_filelist_free(struct direntry *filelist, unsigned int nrentries)
File and directory operations.
#define FILE_MAX
@ TASK_PRIORITY_LOW
Definition BLI_task.h:56
void BLI_task_pool_work_and_wait(TaskPool *pool)
Definition task_pool.cc:471
TaskPool * BLI_task_pool_create(void *userdata, eTaskPriority priority)
Definition task_pool.cc:394
void BLI_task_pool_free(TaskPool *pool)
Definition task_pool.cc:431
void BLI_task_pool_push(TaskPool *pool, TaskRunFunction run, void *taskdata, bool free_taskdata, TaskFreeFunction freedata)
Definition task_pool.cc:450
Platform independent time functions.
void BLI_time_sleep_ms(int ms)
Definition time.c:85
Compatibility-like things for windows.
#define S_ISDIR(x)
@ GPU_DRIVER_ANY
@ GPU_OS_ANY
@ GPU_DEVICE_QUALCOMM
bool GPU_type_matches(eGPUDeviceType device, eGPUOSType os, eGPUDriverType driver)
int64_t BatchHandle
Definition GPU_shader.hh:68
volatile int lock
Value pop(const Key &key)
Definition BLI_map.hh:378
Value & lookup_or_add_default(const Key &key)
Definition BLI_map.hh:601
const Value & lookup(const Key &key) const
Definition BLI_map.hh:506
bool contains(const Key &key) const
Definition BLI_map.hh:329
constexpr int64_t size() const
Definition BLI_span.hh:253
void resize(const int64_t new_size)
Shader * compile(const shader::ShaderCreateInfo &info, bool is_batch_compilation)
BatchHandle batch_compile(Span< const shader::ShaderCreateInfo * > &infos) override
Vector< Shader * > batch_finalize(BatchHandle &handle) override
static bool compile_module(VKShader &shader, shaderc_shader_kind stage, VKShaderModule &shader_module)
bool batch_is_ready(BatchHandle handle) override
shaderc::SpvCompilationResult compilation_result
CCL_NAMESPACE_BEGIN struct Options options
double time
EvaluationStage stage
Definition deg_eval.cc:83
struct @620::@622 batch
#define G(x, y, z)
static bool compile_ex(shaderc::Compiler &compiler, VKShader &shader, shaderc_shader_kind stage, VKShaderModule &shader_module)
static Context * unwrap(GPUContext *ctx)
static void write_spirv_to_disk(VKShaderModule &shader_module)
static std::optional< std::string > cache_dir_get()
static bool read_spirv_from_disk(VKShaderModule &shader_module)
static const std::string to_stage_name(shaderc_shader_kind stage)
unsigned int uint32_t
Definition stdint.h:80
unsigned __int64 uint64_t
Definition stdint.h:90
Describe inputs & outputs, stage interfaces, resources and sources of a shader. If all data is correc...
struct stat s
const char * path
#define SEP_STR
Definition unit.cc:39