Blender V4.5
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 bool compile_ex(shaderc::Compiler &compiler,
167 shaderc_shader_kind stage,
168 VKShaderModule &shader_module)
169{
170 if (read_spirv_from_disk(shader_module)) {
171 return true;
172 }
173
174 shaderc::CompileOptions options;
175 bool do_optimize = true;
176 options.SetTargetEnvironment(shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_2);
177 if (G.debug & G_DEBUG_GPU_RENDERDOC) {
178 do_optimize = false;
179 options.SetGenerateDebugInfo();
180 }
181
182 /* WORKAROUND: Qualcomm driver can crash when handling optimized SPIR-V. */
184 do_optimize = false;
185 }
186 /* Do not optimize large shaders. They can overflow internal buffers that during optimizations
187 * that cannot be adjusted via the ShaderC API. ShaderC in the past had this API
188 * (PassId::kCompactIds) but is unused.
189 *
190 * The shaders in #144614 and #143516 are larger than 512Kb so using this as a limit to disable
191 * optimizations.
192 */
193 constexpr int64_t optimization_source_size_limit = 512 * 1024;
194 if (shader_module.combined_sources.size() > optimization_source_size_limit) {
195 do_optimize = false;
196 }
197 options.SetOptimizationLevel(do_optimize ? shaderc_optimization_level_performance :
198 shaderc_optimization_level_zero);
199
200 std::string full_name = shader.name_get() + "_" + to_stage_name(stage);
201 shader_module.compilation_result = compiler.CompileGlslToSpv(
202 shader_module.combined_sources, stage, full_name.c_str(), options);
203 bool compilation_succeeded = shader_module.compilation_result.GetCompilationStatus() ==
204 shaderc_compilation_status_success;
205 if (compilation_succeeded) {
206 write_spirv_to_disk(shader_module);
207 }
208 return compilation_succeeded;
209}
210
212 shaderc_shader_kind stage,
213 VKShaderModule &shader_module)
214{
215 shaderc::Compiler compiler;
216 return compile_ex(compiler, shader, stage, shader_module);
217}
218
220
221} // namespace blender::gpu
bool BKE_appdir_folder_caches(char *path, size_t path_maxncpy) ATTR_NONNULL(1)
Definition appdir.cc:202
@ 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:373
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
Platform independent time functions.
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)
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)
struct stat s
const char * path
i
Definition text_draw.cc:230
#define SEP_STR
Definition unit.cc:39