Blender V5.0
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
8
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 {
23
24static std::optional<std::string> cache_dir_get()
25{
26 static std::optional<std::string> result = []() -> std::optional<std::string> {
27 static char tmp_dir_buffer[FILE_MAX];
28 /* Shader builder doesn't return the correct appdir. */
29 if (!BKE_appdir_folder_caches(tmp_dir_buffer, sizeof(tmp_dir_buffer))) {
30 return std::nullopt;
31 }
32
33 std::string cache_dir = std::string(tmp_dir_buffer) + "vk-spirv-cache" + SEP_STR;
34 BLI_dir_create_recursive(cache_dir.c_str());
35 return cache_dir;
36 }();
37
38 return result;
39}
40
41/* -------------------------------------------------------------------- */
44
49
50static bool read_spirv_from_disk(VKShaderModule &shader_module)
51{
52 if (G.debug & G_DEBUG_GPU_RENDERDOC) {
53 /* RenderDoc uses spirv shaders including debug information. */
54 return false;
55 }
56 if (!cache_dir_get().has_value()) {
57 return false;
58 }
59 shader_module.build_sources_hash();
60 std::string spirv_path = (*cache_dir_get()) + SEP_STR + shader_module.sources_hash + ".spv";
61 std::string sidecar_path = (*cache_dir_get()) + SEP_STR + shader_module.sources_hash +
62 ".sidecar.bin";
63
64 if (!BLI_exists(spirv_path.c_str()) || !BLI_exists(sidecar_path.c_str())) {
65 return false;
66 }
67
68 BLI_file_touch(spirv_path.c_str());
69 BLI_file_touch(sidecar_path.c_str());
70
71 /* Read sidecar. */
72 fstream sidecar_file(sidecar_path, std::ios::binary | std::ios::in | std::ios::ate);
73 std::streamsize sidecar_size_on_disk = sidecar_file.tellg();
74 SPIRVSidecar sidecar = {};
75 if (sidecar_size_on_disk != sizeof(sidecar)) {
76 return false;
77 }
78 sidecar_file.seekg(0, std::ios::beg);
79 sidecar_file.read(reinterpret_cast<char *>(&sidecar), sizeof(sidecar));
80
81 /* Read spirv binary. */
82 fstream spirv_file(spirv_path, std::ios::binary | std::ios::in | std::ios::ate);
83 std::streamsize size = spirv_file.tellg();
84 if (size != sidecar.spirv_size) {
85 return false;
86 }
87 spirv_file.seekg(0, std::ios::beg);
88 shader_module.spirv_binary.resize(size / 4);
89 spirv_file.read(reinterpret_cast<char *>(shader_module.spirv_binary.data()), size);
90 return true;
91}
92
93static void write_spirv_to_disk(VKShaderModule &shader_module)
94{
95 if (G.debug & G_DEBUG_GPU_RENDERDOC) {
96 return;
97 }
98 if (!cache_dir_get().has_value()) {
99 return;
100 }
101
102 /* Write the spirv binary */
103 std::string spirv_path = (*cache_dir_get()) + SEP_STR + shader_module.sources_hash + ".spv";
104 size_t size = (shader_module.compilation_result.end() -
105 shader_module.compilation_result.begin()) *
106 sizeof(uint32_t);
107 fstream spirv_file(spirv_path, std::ios::binary | std::ios::out);
108 spirv_file.write(reinterpret_cast<const char *>(shader_module.compilation_result.begin()), size);
109
110 /* Write the sidecar */
111 SPIRVSidecar sidecar = {size};
112 std::string sidecar_path = (*cache_dir_get()) + SEP_STR + shader_module.sources_hash +
113 ".sidecar.bin";
114 fstream sidecar_file(sidecar_path, std::ios::binary | std::ios::out);
115 sidecar_file.write(reinterpret_cast<const char *>(&sidecar), sizeof(SPIRVSidecar));
116}
117
119{
120 if (!cache_dir_get().has_value()) {
121 return;
122 }
123
124 direntry *entries = nullptr;
125 uint32_t dir_len = BLI_filelist_dir_contents(cache_dir_get()->c_str(), &entries);
126 for (int i : blender::IndexRange(dir_len)) {
127 direntry entry = entries[i];
128 if (S_ISDIR(entry.s.st_mode)) {
129 continue;
130 }
131 const time_t ts_now = time(nullptr);
132 const time_t delete_threshold = 60 /*seconds*/ * 60 /*minutes*/ * 24 /*hours*/ * 30 /*days*/;
133 if (entry.s.st_mtime + delete_threshold < ts_now) {
134 BLI_delete(entry.path, false, false);
135 }
136 }
137 BLI_filelist_free(entries, dir_len);
138}
139
141
142/* -------------------------------------------------------------------- */
145
146static StringRef to_stage_name(shaderc_shader_kind stage)
147{
148 switch (stage) {
149 case shaderc_vertex_shader:
150 return "vertex";
151 case shaderc_geometry_shader:
152 return "geometry";
153 case shaderc_fragment_shader:
154 return "fragment";
155 case shaderc_compute_shader:
156 return "compute";
157
158 default:
159 BLI_assert_msg(false, "Do not know how to convert shaderc_shader_kind to stage name.");
160 break;
161 }
162 return "unknown stage";
163}
164
165static std::string patch_line_directives(std::string source)
166{
167 /* Patch line directives so that we can make error reporting consistent. */
168 size_t start_pos = 0;
169 while ((start_pos = source.find("#line ", start_pos)) != std::string::npos) {
170 source[start_pos] = '/';
171 source[start_pos + 1] = '/';
172 }
173 return source;
174}
175
176static bool compile_ex(shaderc::Compiler &compiler,
178 shaderc_shader_kind stage,
179 VKShaderModule &shader_module)
180{
181 if (read_spirv_from_disk(shader_module)) {
182 return true;
183 }
184
185 shaderc::CompileOptions options;
186 bool do_optimize = true;
187 options.SetTargetEnvironment(shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_2);
188 if (G.debug & G_DEBUG_GPU_RENDERDOC) {
189 do_optimize = false;
190 }
191 /* WORKAROUND: Qualcomm driver can crash when handling optimized SPIR-V. */
193 do_optimize = false;
194 }
195 /* Do not optimize large shaders. They can overflow internal buffers that during optimizations
196 * that cannot be adjusted via the ShaderC API. ShaderC in the past had this API
197 * (PassId::kCompactIds) but is unused.
198 *
199 * The shaders in #144614 and #143516 are larger than 512Kb so using this as a limit to disable
200 * optimizations.
201 */
202 constexpr int64_t optimization_source_size_limit = 512 * 1024;
203 if (shader_module.combined_sources.size() > optimization_source_size_limit) {
204 do_optimize = false;
205 }
206 options.SetOptimizationLevel(do_optimize ? shaderc_optimization_level_performance :
207 shaderc_optimization_level_zero);
208
209 /* Should always be called after setting the optimization level. Setting optimization level
210 * resets all previous passes. */
211 if (G.debug & G_DEBUG_GPU_SHADER_DEBUG_INFO) {
212 options.SetGenerateDebugInfo();
213 }
214
215 /* Removes line directive. */
216 std::string sources = patch_line_directives(shader_module.combined_sources);
217
218 std::string full_name = shader.name_get() + "_" + to_stage_name(stage);
219 shader_module.compilation_result = compiler.CompileGlslToSpv(
220 sources, stage, full_name.c_str(), options);
221 bool compilation_succeeded = shader_module.compilation_result.GetCompilationStatus() ==
222 shaderc_compilation_status_success;
223 if (compilation_succeeded) {
224 write_spirv_to_disk(shader_module);
225 }
226 return compilation_succeeded;
227}
228
230 shaderc_shader_kind stage,
231 VKShaderModule &shader_module)
232{
233 shaderc::Compiler compiler;
234 return compile_ex(compiler, shader, stage, shader_module);
235}
236
238
239} // namespace blender::gpu
bool BKE_appdir_folder_caches(char *path, size_t path_maxncpy) ATTR_NONNULL(1)
Definition appdir.cc:203
@ G_DEBUG_GPU_SHADER_DEBUG_INFO
@ G_DEBUG_GPU_RENDERDOC
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
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:360
bool BLI_dir_create_recursive(const char *dirname) ATTR_NONNULL()
Definition fileops_c.cc:414
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
Platform independent time functions.
Compatibility-like things for windows.
#define S_ISDIR(x)
@ GPU_DEVICE_QUALCOMM
bool GPU_type_matches(GPUDeviceType device, GPUOSType os, GPUDriverType driver)
@ GPU_DRIVER_ANY
@ GPU_OS_ANY
long long int int64_t
unsigned long long int uint64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
void resize(const int64_t new_size)
T * data()
static bool compile_module(VKShader &shader, shaderc_shader_kind stage, VKShaderModule &shader_module)
shaderc::SpvCompilationResult compilation_result
CCL_NAMESPACE_BEGIN struct Options options
#define G(x, y, z)
static bool compile_ex(shaderc::Compiler &compiler, VKShader &shader, shaderc_shader_kind stage, VKShaderModule &shader_module)
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 StringRef to_stage_name(shaderc_shader_kind stage)
static std::string patch_line_directives(std::string source)
struct stat s
const char * path
i
Definition text_draw.cc:230
#define SEP_STR
Definition unit.cc:39