summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/video_core/renderer_opengl/gl_shader_disk_cache.cpp525
-rw-r--r--src/video_core/renderer_opengl/gl_shader_disk_cache.h30
2 files changed, 339 insertions, 216 deletions
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
index 5157e319a..f8bdb7779 100644
--- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
@@ -24,6 +24,8 @@
24 24
25namespace OpenGL { 25namespace OpenGL {
26 26
27using ShaderCacheVersionHash = std::array<u8, 64>;
28
27enum class TransferableEntryKind : u32 { 29enum class TransferableEntryKind : u32 {
28 Raw, 30 Raw,
29 Usage, 31 Usage,
@@ -35,7 +37,6 @@ enum class PrecompiledEntryKind : u32 {
35}; 37};
36 38
37constexpr u32 NativeVersion = 1; 39constexpr u32 NativeVersion = 1;
38constexpr u32 ShaderHashSize = 64;
39 40
40// Making sure sizes doesn't change by accident 41// Making sure sizes doesn't change by accident
41static_assert(sizeof(BaseBindings) == 12); 42static_assert(sizeof(BaseBindings) == 12);
@@ -46,10 +47,11 @@ std::string GetTitleID() {
46 return fmt::format("{:016X}", Core::CurrentProcess()->GetTitleID()); 47 return fmt::format("{:016X}", Core::CurrentProcess()->GetTitleID());
47} 48}
48 49
49std::string GetShaderHash() { 50ShaderCacheVersionHash GetShaderCacheVersionHash() {
50 std::array<char, ShaderHashSize> hash{}; 51 ShaderCacheVersionHash hash{};
51 std::strncpy(hash.data(), Common::g_shader_cache_version, ShaderHashSize); 52 const std::size_t length = std::min(std::strlen(Common::g_shader_cache_version), hash.size());
52 return std::string(hash.data(), hash.size()); 53 std::memcpy(hash.data(), Common::g_shader_cache_version, length);
54 return hash;
53} 55}
54 56
55template <typename T> 57template <typename T>
@@ -82,50 +84,64 @@ std::vector<u8> DecompressData(const std::vector<u8>& compressed, std::size_t un
82} 84}
83} // namespace 85} // namespace
84 86
85ShaderDiskCacheRaw::ShaderDiskCacheRaw(FileUtil::IOFile& file) { 87ShaderDiskCacheRaw::ShaderDiskCacheRaw(u64 unique_identifier, Maxwell::ShaderProgram program_type,
86 file.ReadBytes(&unique_identifier, sizeof(u64)); 88 u32 program_code_size, u32 program_code_size_b,
87 file.ReadBytes(&program_type, sizeof(u32)); 89 ProgramCode program_code, ProgramCode program_code_b)
90 : unique_identifier{unique_identifier}, program_type{program_type},
91 program_code_size{program_code_size}, program_code_size_b{program_code_size_b},
92 program_code{std::move(program_code)}, program_code_b{std::move(program_code_b)} {}
93
94ShaderDiskCacheRaw::ShaderDiskCacheRaw() = default;
88 95
96ShaderDiskCacheRaw::~ShaderDiskCacheRaw() = default;
97
98bool ShaderDiskCacheRaw::Load(FileUtil::IOFile& file) {
99 if (file.ReadBytes(&unique_identifier, sizeof(u64)) != sizeof(u64) ||
100 file.ReadBytes(&program_type, sizeof(u32)) != sizeof(u32)) {
101 return false;
102 }
89 u32 program_code_size{}; 103 u32 program_code_size{};
90 u32 program_code_size_b{}; 104 u32 program_code_size_b{};
91 file.ReadBytes(&program_code_size, sizeof(u32)); 105 if (file.ReadBytes(&program_code_size, sizeof(u32)) != sizeof(u32) ||
92 file.ReadBytes(&program_code_size_b, sizeof(u32)); 106 file.ReadBytes(&program_code_size_b, sizeof(u32)) != sizeof(u32)) {
107 return false;
108 }
93 109
94 program_code.resize(program_code_size); 110 program_code.resize(program_code_size);
95 program_code_b.resize(program_code_size_b); 111 program_code_b.resize(program_code_size_b);
96 112
97 file.ReadArray(program_code.data(), program_code_size); 113 if (file.ReadArray(program_code.data(), program_code_size) != program_code_size)
98 if (HasProgramA()) { 114 return false;
99 file.ReadArray(program_code_b.data(), program_code_size_b); 115
116 if (HasProgramA() &&
117 file.ReadArray(program_code_b.data(), program_code_size_b) != program_code_size_b) {
118 return false;
100 } 119 }
120 return true;
101} 121}
102 122
103ShaderDiskCacheRaw::ShaderDiskCacheRaw(u64 unique_identifier, Maxwell::ShaderProgram program_type, 123bool ShaderDiskCacheRaw::Save(FileUtil::IOFile& file) const {
104 u32 program_code_size, u32 program_code_size_b, 124 if (file.WriteObject(unique_identifier) != 1 ||
105 ProgramCode program_code, ProgramCode program_code_b) 125 file.WriteObject(static_cast<u32>(program_type)) != 1 ||
106 : unique_identifier{unique_identifier}, program_type{program_type}, 126 file.WriteObject(program_code_size) != 1 || file.WriteObject(program_code_size_b) != 1) {
107 program_code_size{program_code_size}, program_code_size_b{program_code_size_b}, 127 return false;
108 program_code{std::move(program_code)}, program_code_b{std::move(program_code_b)} {} 128 }
109
110ShaderDiskCacheRaw::~ShaderDiskCacheRaw() = default;
111 129
112void ShaderDiskCacheRaw::Save(FileUtil::IOFile& file) const { 130 if (file.WriteArray(program_code.data(), program_code_size) != program_code_size)
113 file.WriteObject(unique_identifier); 131 return false;
114 file.WriteObject(static_cast<u32>(program_type));
115 file.WriteObject(program_code_size);
116 file.WriteObject(program_code_size_b);
117 132
118 file.WriteArray(program_code.data(), program_code_size); 133 if (HasProgramA() &&
119 if (HasProgramA()) { 134 file.WriteArray(program_code_b.data(), program_code_size_b) != program_code_size_b) {
120 file.WriteArray(program_code_b.data(), program_code_size_b); 135 return false;
121 } 136 }
137 return true;
122} 138}
123 139
124std::optional<std::pair<std::vector<ShaderDiskCacheRaw>, std::vector<ShaderDiskCacheUsage>>> 140std::optional<std::pair<std::vector<ShaderDiskCacheRaw>, std::vector<ShaderDiskCacheUsage>>>
125ShaderDiskCacheOpenGL::LoadTransferable() { 141ShaderDiskCacheOpenGL::LoadTransferable() {
126 if (!Settings::values.use_disk_shader_cache) { 142 if (!Settings::values.use_disk_shader_cache)
127 return {}; 143 return {};
128 } 144 tried_to_load = true;
129 145
130 FileUtil::IOFile file(GetTransferablePath(), "rb"); 146 FileUtil::IOFile file(GetTransferablePath(), "rb");
131 if (!file.IsOpen()) { 147 if (!file.IsOpen()) {
@@ -133,15 +149,19 @@ ShaderDiskCacheOpenGL::LoadTransferable() {
133 GetTitleID()); 149 GetTitleID());
134 return {}; 150 return {};
135 } 151 }
136 const u64 file_size = file.GetSize();
137 152
138 u32 version{}; 153 u32 version{};
139 file.ReadBytes(&version, sizeof(version)); 154 if (file.ReadBytes(&version, sizeof(version)) != sizeof(version)) {
155 LOG_ERROR(Render_OpenGL,
156 "Failed to get transferable cache version for title id={} - skipping",
157 GetTitleID());
158 return {};
159 }
140 160
141 if (version < NativeVersion) { 161 if (version < NativeVersion) {
142 LOG_INFO(Render_OpenGL, "Transferable shader cache is old - removing"); 162 LOG_INFO(Render_OpenGL, "Transferable shader cache is old - removing");
143 file.Close(); 163 file.Close();
144 FileUtil::Delete(GetTransferablePath()); 164 InvalidateTransferable();
145 return {}; 165 return {};
146 } 166 }
147 if (version > NativeVersion) { 167 if (version > NativeVersion) {
@@ -153,25 +173,35 @@ ShaderDiskCacheOpenGL::LoadTransferable() {
153 // Version is valid, load the shaders 173 // Version is valid, load the shaders
154 std::vector<ShaderDiskCacheRaw> raws; 174 std::vector<ShaderDiskCacheRaw> raws;
155 std::vector<ShaderDiskCacheUsage> usages; 175 std::vector<ShaderDiskCacheUsage> usages;
156 while (file.Tell() < file_size) { 176 while (file.Tell() < file.GetSize()) {
157 TransferableEntryKind kind{}; 177 TransferableEntryKind kind{};
158 file.ReadBytes(&kind, sizeof(u32)); 178 if (file.ReadBytes(&kind, sizeof(u32)) != sizeof(u32)) {
179 LOG_ERROR(Render_OpenGL, "Failed to read transferable file - skipping");
180 return {};
181 }
159 182
160 switch (kind) { 183 switch (kind) {
161 case TransferableEntryKind::Raw: { 184 case TransferableEntryKind::Raw: {
162 ShaderDiskCacheRaw entry{file}; 185 ShaderDiskCacheRaw entry;
186 if (!entry.Load(file)) {
187 LOG_ERROR(Render_OpenGL, "Failed to load transferable raw entry - skipping");
188 return {};
189 }
163 transferable.insert({entry.GetUniqueIdentifier(), {}}); 190 transferable.insert({entry.GetUniqueIdentifier(), {}});
164 raws.push_back(std::move(entry)); 191 raws.push_back(std::move(entry));
165 break; 192 break;
166 } 193 }
167 case TransferableEntryKind::Usage: { 194 case TransferableEntryKind::Usage: {
168 ShaderDiskCacheUsage usage{}; 195 ShaderDiskCacheUsage usage{};
169 file.ReadBytes(&usage, sizeof(usage)); 196 if (file.ReadBytes(&usage, sizeof(usage)) != sizeof(usage)) {
197 LOG_ERROR(Render_OpenGL, "Failed to load transferable usage entry - skipping");
198 return {};
199 }
170 usages.push_back(std::move(usage)); 200 usages.push_back(std::move(usage));
171 break; 201 break;
172 } 202 }
173 default: 203 default:
174 LOG_ERROR(Render_OpenGL, "Unknown transferable shader cache entry kind={} - aborting", 204 LOG_ERROR(Render_OpenGL, "Unknown transferable shader cache entry kind={} - skipping",
175 static_cast<u32>(kind)); 205 static_cast<u32>(kind));
176 return {}; 206 return {};
177 } 207 }
@@ -182,9 +212,8 @@ ShaderDiskCacheOpenGL::LoadTransferable() {
182std::pair<std::map<u64, ShaderDiskCacheDecompiled>, 212std::pair<std::map<u64, ShaderDiskCacheDecompiled>,
183 std::map<ShaderDiskCacheUsage, ShaderDiskCacheDump>> 213 std::map<ShaderDiskCacheUsage, ShaderDiskCacheDump>>
184ShaderDiskCacheOpenGL::LoadPrecompiled() { 214ShaderDiskCacheOpenGL::LoadPrecompiled() {
185 if (!Settings::values.use_disk_shader_cache) { 215 if (!IsUsable())
186 return {}; 216 return {};
187 }
188 217
189 FileUtil::IOFile file(GetPrecompiledPath(), "rb"); 218 FileUtil::IOFile file(GetPrecompiledPath(), "rb");
190 if (!file.IsOpen()) { 219 if (!file.IsOpen()) {
@@ -192,119 +221,75 @@ ShaderDiskCacheOpenGL::LoadPrecompiled() {
192 GetTitleID()); 221 GetTitleID());
193 return {}; 222 return {};
194 } 223 }
195 const u64 file_size = file.GetSize();
196 224
197 char precompiled_hash[ShaderHashSize]; 225 const auto result = LoadPrecompiledFile(file);
198 file.ReadBytes(&precompiled_hash, ShaderHashSize); 226 if (!result) {
199 if (precompiled_hash != GetShaderHash()) { 227 LOG_INFO(Render_OpenGL,
200 LOG_INFO(Render_OpenGL, "Precompiled cache is from another version of yuzu - removing"); 228 "Failed to load precompiled cache for game with title id={} - removing",
229 GetTitleID());
201 file.Close(); 230 file.Close();
202 InvalidatePrecompiled(); 231 InvalidatePrecompiled();
203 return {}; 232 return {};
204 } 233 }
234 return *result;
235}
236
237std::optional<std::pair<std::map<u64, ShaderDiskCacheDecompiled>,
238 std::map<ShaderDiskCacheUsage, ShaderDiskCacheDump>>>
239ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) {
240 ShaderCacheVersionHash file_hash{};
241 if (file.ReadArray(file_hash.data(), file_hash.size()) != file_hash.size()) {
242 return {};
243 }
244 if (GetShaderCacheVersionHash() != file_hash) {
245 LOG_INFO(Render_OpenGL, "Precompiled cache is from another version of the emulator");
246 return {};
247 }
205 248
206 std::map<u64, ShaderDiskCacheDecompiled> decompiled; 249 std::map<u64, ShaderDiskCacheDecompiled> decompiled;
207 std::map<ShaderDiskCacheUsage, ShaderDiskCacheDump> dumps; 250 std::map<ShaderDiskCacheUsage, ShaderDiskCacheDump> dumps;
208 while (file.Tell() < file_size) { 251 while (file.Tell() < file.GetSize()) {
209 PrecompiledEntryKind kind{}; 252 PrecompiledEntryKind kind{};
210 file.ReadBytes(&kind, sizeof(u32)); 253 if (file.ReadBytes(&kind, sizeof(u32)) != sizeof(u32)) {
254 return {};
255 }
211 256
212 switch (kind) { 257 switch (kind) {
213 case PrecompiledEntryKind::Decompiled: { 258 case PrecompiledEntryKind::Decompiled: {
214 ShaderDiskCacheDecompiled entry;
215
216 u64 unique_identifier{}; 259 u64 unique_identifier{};
217 file.ReadBytes(&unique_identifier, sizeof(u64)); 260 if (file.ReadBytes(&unique_identifier, sizeof(u64)) != sizeof(u64))
218
219 u32 code_size{};
220 u32 compressed_code_size{};
221 file.ReadBytes(&code_size, sizeof(u32));
222 file.ReadBytes(&compressed_code_size, sizeof(u32));
223
224 std::vector<u8> compressed_code(compressed_code_size);
225 file.ReadArray(compressed_code.data(), compressed_code.size());
226
227 const std::vector<u8> code = DecompressData(compressed_code, code_size);
228 if (code.empty()) {
229 LOG_ERROR(Render_OpenGL,
230 "Failed to decompress GLSL code in precompiled shader={:016x} - removing",
231 unique_identifier);
232 InvalidatePrecompiled();
233 return {}; 261 return {};
234 }
235 entry.code = std::string(reinterpret_cast<const char*>(code.data()), code_size);
236
237 u32 const_buffers_count{};
238 file.ReadBytes(&const_buffers_count, sizeof(u32));
239 for (u32 i = 0; i < const_buffers_count; ++i) {
240 u32 max_offset{}, index{};
241 u8 is_indirect{};
242 file.ReadBytes(&max_offset, sizeof(u32));
243 file.ReadBytes(&index, sizeof(u32));
244 file.ReadBytes(&is_indirect, sizeof(u8));
245
246 entry.entries.const_buffers.emplace_back(max_offset, is_indirect != 0, index);
247 }
248
249 u32 samplers_count{};
250 file.ReadBytes(&samplers_count, sizeof(u32));
251 for (u32 i = 0; i < samplers_count; ++i) {
252 u64 offset{}, index{};
253 u32 type{};
254 u8 is_array{}, is_shadow{};
255 file.ReadBytes(&offset, sizeof(u64));
256 file.ReadBytes(&index, sizeof(u64));
257 file.ReadBytes(&type, sizeof(u32));
258 file.ReadBytes(&is_array, sizeof(u8));
259 file.ReadBytes(&is_shadow, sizeof(u8));
260
261 entry.entries.samplers.emplace_back(
262 static_cast<std::size_t>(offset), static_cast<std::size_t>(index),
263 static_cast<Tegra::Shader::TextureType>(type), is_array != 0, is_shadow != 0);
264 }
265 262
266 u32 global_memory_count{}; 263 const auto entry = LoadDecompiledEntry(file);
267 file.ReadBytes(&global_memory_count, sizeof(u32)); 264 if (!entry)
268 for (u32 i = 0; i < global_memory_count; ++i) { 265 return {};
269 u32 cbuf_index{}, cbuf_offset{}; 266 decompiled.insert({unique_identifier, std::move(*entry)});
270 file.ReadBytes(&cbuf_index, sizeof(u32));
271 file.ReadBytes(&cbuf_offset, sizeof(u32));
272 entry.entries.global_memory_entries.emplace_back(cbuf_index, cbuf_offset);
273 }
274
275 for (auto& clip_distance : entry.entries.clip_distances) {
276 u8 clip_distance_raw{};
277 file.ReadBytes(&clip_distance_raw, sizeof(u8));
278 clip_distance = clip_distance_raw != 0;
279 }
280
281 u64 shader_length{};
282 file.ReadBytes(&shader_length, sizeof(u64));
283 entry.entries.shader_length = static_cast<std::size_t>(shader_length);
284
285 decompiled.insert({unique_identifier, std::move(entry)});
286 break; 267 break;
287 } 268 }
288 case PrecompiledEntryKind::Dump: { 269 case PrecompiledEntryKind::Dump: {
289 ShaderDiskCacheUsage usage; 270 ShaderDiskCacheUsage usage;
290 file.ReadBytes(&usage, sizeof(usage)); 271 if (file.ReadBytes(&usage, sizeof(usage)) != sizeof(usage))
272 return {};
291 273
292 ShaderDiskCacheDump dump; 274 ShaderDiskCacheDump dump;
293 file.ReadBytes(&dump.binary_format, sizeof(u32)); 275 if (file.ReadBytes(&dump.binary_format, sizeof(u32)) != sizeof(u32))
276 return {};
294 277
295 u32 binary_length{}; 278 u32 binary_length{};
296 u32 compressed_size{}; 279 u32 compressed_size{};
297 file.ReadBytes(&binary_length, sizeof(u32)); 280 if (file.ReadBytes(&binary_length, sizeof(u32)) != sizeof(u32) ||
298 file.ReadBytes(&compressed_size, sizeof(u32)); 281 file.ReadBytes(&compressed_size, sizeof(u32)) != sizeof(u32)) {
282 return {};
283 }
299 284
300 std::vector<u8> compressed_binary(compressed_size); 285 std::vector<u8> compressed_binary(compressed_size);
301 file.ReadArray(compressed_binary.data(), compressed_binary.size()); 286 if (file.ReadArray(compressed_binary.data(), compressed_binary.size()) !=
287 compressed_binary.size()) {
288 return {};
289 }
302 290
303 dump.binary = DecompressData(compressed_binary, binary_length); 291 dump.binary = DecompressData(compressed_binary, binary_length);
304 if (dump.binary.empty()) { 292 if (dump.binary.empty()) {
305 LOG_ERROR(Render_OpenGL,
306 "Failed to decompress precompiled binary program - removing");
307 InvalidatePrecompiled();
308 return {}; 293 return {};
309 } 294 }
310 295
@@ -312,28 +297,165 @@ ShaderDiskCacheOpenGL::LoadPrecompiled() {
312 break; 297 break;
313 } 298 }
314 default: 299 default:
315 LOG_ERROR(Render_OpenGL, "Unknown precompiled shader cache entry kind={} - removing",
316 static_cast<u32>(kind));
317 InvalidatePrecompiled();
318 return {}; 300 return {};
319 } 301 }
320 } 302 }
321 return {decompiled, dumps}; 303 return {{decompiled, dumps}};
304}
305
306std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEntry(
307 FileUtil::IOFile& file) {
308 u32 code_size{};
309 u32 compressed_code_size{};
310 if (file.ReadBytes(&code_size, sizeof(u32)) != sizeof(u32) ||
311 file.ReadBytes(&compressed_code_size, sizeof(u32)) != sizeof(u32)) {
312 return {};
313 }
314
315 std::vector<u8> compressed_code(compressed_code_size);
316 if (file.ReadArray(compressed_code.data(), compressed_code.size()) != compressed_code.size()) {
317 return {};
318 }
319
320 const std::vector<u8> code = DecompressData(compressed_code, code_size);
321 if (code.empty()) {
322 return {};
323 }
324 ShaderDiskCacheDecompiled entry;
325 entry.code = std::string(reinterpret_cast<const char*>(code.data()), code_size);
326
327 u32 const_buffers_count{};
328 if (file.ReadBytes(&const_buffers_count, sizeof(u32)) != sizeof(u32))
329 return {};
330 for (u32 i = 0; i < const_buffers_count; ++i) {
331 u32 max_offset{};
332 u32 index{};
333 u8 is_indirect{};
334 if (file.ReadBytes(&max_offset, sizeof(u32)) != sizeof(u32) ||
335 file.ReadBytes(&index, sizeof(u32)) != sizeof(u32) ||
336 file.ReadBytes(&is_indirect, sizeof(u8)) != sizeof(u8)) {
337 return {};
338 }
339 entry.entries.const_buffers.emplace_back(max_offset, is_indirect != 0, index);
340 }
341
342 u32 samplers_count{};
343 if (file.ReadBytes(&samplers_count, sizeof(u32)) != sizeof(u32))
344 return {};
345 for (u32 i = 0; i < samplers_count; ++i) {
346 u64 offset{};
347 u64 index{};
348 u32 type{};
349 u8 is_array{};
350 u8 is_shadow{};
351 if (file.ReadBytes(&offset, sizeof(u64)) != sizeof(u64) ||
352 file.ReadBytes(&index, sizeof(u64)) != sizeof(u64) ||
353 file.ReadBytes(&type, sizeof(u32)) != sizeof(u32) ||
354 file.ReadBytes(&is_array, sizeof(u8)) != sizeof(u8) ||
355 file.ReadBytes(&is_shadow, sizeof(u8)) != sizeof(u8)) {
356 return {};
357 }
358 entry.entries.samplers.emplace_back(
359 static_cast<std::size_t>(offset), static_cast<std::size_t>(index),
360 static_cast<Tegra::Shader::TextureType>(type), is_array != 0, is_shadow != 0);
361 }
362
363 u32 global_memory_count{};
364 if (file.ReadBytes(&global_memory_count, sizeof(u32)) != sizeof(u32))
365 return {};
366 for (u32 i = 0; i < global_memory_count; ++i) {
367 u32 cbuf_index{};
368 u32 cbuf_offset{};
369 if (file.ReadBytes(&cbuf_index, sizeof(u32)) != sizeof(u32) ||
370 file.ReadBytes(&cbuf_offset, sizeof(u32)) != sizeof(u32)) {
371 return {};
372 }
373 entry.entries.global_memory_entries.emplace_back(cbuf_index, cbuf_offset);
374 }
375
376 for (auto& clip_distance : entry.entries.clip_distances) {
377 u8 clip_distance_raw{};
378 if (file.ReadBytes(&clip_distance_raw, sizeof(u8)) != sizeof(u8))
379 return {};
380 clip_distance = clip_distance_raw != 0;
381 }
382
383 u64 shader_length{};
384 if (file.ReadBytes(&shader_length, sizeof(u64)) != sizeof(u64))
385 return {};
386 entry.entries.shader_length = static_cast<std::size_t>(shader_length);
387
388 return entry;
389}
390
391bool ShaderDiskCacheOpenGL::SaveDecompiledFile(FileUtil::IOFile& file, u64 unique_identifier,
392 const std::string& code,
393 const std::vector<u8>& compressed_code,
394 const GLShader::ShaderEntries& entries) {
395 if (file.WriteObject(static_cast<u32>(PrecompiledEntryKind::Decompiled)) != 1 ||
396 file.WriteObject(unique_identifier) != 1 ||
397 file.WriteObject(static_cast<u32>(code.size())) != 1 ||
398 file.WriteObject(static_cast<u32>(compressed_code.size())) != 1 ||
399 file.WriteArray(compressed_code.data(), compressed_code.size()) != compressed_code.size()) {
400 return false;
401 }
402
403 if (file.WriteObject(static_cast<u32>(entries.const_buffers.size())) != 1)
404 return false;
405 for (const auto& cbuf : entries.const_buffers) {
406 if (file.WriteObject(static_cast<u32>(cbuf.GetMaxOffset())) != 1 ||
407 file.WriteObject(static_cast<u32>(cbuf.GetIndex())) != 1 ||
408 file.WriteObject(static_cast<u8>(cbuf.IsIndirect() ? 1 : 0)) != 1) {
409 return false;
410 }
411 }
412
413 if (file.WriteObject(static_cast<u32>(entries.samplers.size())) != 1)
414 return false;
415 for (const auto& sampler : entries.samplers) {
416 if (file.WriteObject(static_cast<u64>(sampler.GetOffset())) != 1 ||
417 file.WriteObject(static_cast<u64>(sampler.GetIndex())) != 1 ||
418 file.WriteObject(static_cast<u32>(sampler.GetType())) != 1 ||
419 file.WriteObject(static_cast<u8>(sampler.IsArray() ? 1 : 0)) != 1 ||
420 file.WriteObject(static_cast<u8>(sampler.IsShadow() ? 1 : 0)) != 1) {
421 return false;
422 }
423 }
424
425 if (file.WriteObject(static_cast<u32>(entries.global_memory_entries.size())) != 1)
426 return false;
427 for (const auto& gmem : entries.global_memory_entries) {
428 if (file.WriteObject(static_cast<u32>(gmem.GetCbufIndex())) != 1 ||
429 file.WriteObject(static_cast<u32>(gmem.GetCbufOffset())) != 1) {
430 return false;
431 }
432 }
433
434 for (const bool clip_distance : entries.clip_distances) {
435 if (file.WriteObject(static_cast<u8>(clip_distance ? 1 : 0)) != 1)
436 return false;
437 }
438
439 return file.WriteObject(static_cast<u64>(entries.shader_length)) == 1;
322} 440}
323 441
324bool ShaderDiskCacheOpenGL::InvalidateTransferable() const { 442void ShaderDiskCacheOpenGL::InvalidateTransferable() const {
325 const bool success = FileUtil::Delete(GetTransferablePath()); 443 if (!FileUtil::Delete(GetTransferablePath())) {
326 return InvalidatePrecompiled() && success; 444 LOG_ERROR(Render_OpenGL, "Failed to invalidate transferable file={}",
445 GetTransferablePath());
446 }
447 InvalidatePrecompiled();
327} 448}
328 449
329bool ShaderDiskCacheOpenGL::InvalidatePrecompiled() const { 450void ShaderDiskCacheOpenGL::InvalidatePrecompiled() const {
330 return FileUtil::Delete(GetPrecompiledPath()); 451 if (!FileUtil::Delete(GetPrecompiledPath())) {
452 LOG_ERROR(Render_OpenGL, "Failed to invalidate precompiled file={}", GetPrecompiledPath());
453 }
331} 454}
332 455
333void ShaderDiskCacheOpenGL::SaveRaw(const ShaderDiskCacheRaw& entry) { 456void ShaderDiskCacheOpenGL::SaveRaw(const ShaderDiskCacheRaw& entry) {
334 if (!Settings::values.use_disk_shader_cache) { 457 if (!IsUsable())
335 return; 458 return;
336 }
337 459
338 const u64 id = entry.GetUniqueIdentifier(); 460 const u64 id = entry.GetUniqueIdentifier();
339 if (transferable.find(id) != transferable.end()) { 461 if (transferable.find(id) != transferable.end()) {
@@ -342,47 +464,44 @@ void ShaderDiskCacheOpenGL::SaveRaw(const ShaderDiskCacheRaw& entry) {
342 } 464 }
343 465
344 FileUtil::IOFile file = AppendTransferableFile(); 466 FileUtil::IOFile file = AppendTransferableFile();
345 if (!file.IsOpen()) { 467 if (!file.IsOpen())
468 return;
469 if (file.WriteObject(TransferableEntryKind::Raw) != 1 || !entry.Save(file)) {
470 LOG_ERROR(Render_OpenGL, "Failed to save raw transferable cache entry - removing");
471 file.Close();
472 InvalidateTransferable();
346 return; 473 return;
347 } 474 }
348 file.WriteObject(TransferableEntryKind::Raw);
349 entry.Save(file);
350
351 transferable.insert({id, {}}); 475 transferable.insert({id, {}});
352} 476}
353 477
354void ShaderDiskCacheOpenGL::SaveUsage(const ShaderDiskCacheUsage& usage) { 478void ShaderDiskCacheOpenGL::SaveUsage(const ShaderDiskCacheUsage& usage) {
355 if (!Settings::values.use_disk_shader_cache) { 479 if (!IsUsable())
356 return; 480 return;
357 }
358 481
359 const auto it = transferable.find(usage.unique_identifier); 482 const auto it = transferable.find(usage.unique_identifier);
360 if (it == transferable.end()) { 483 ASSERT_MSG(it != transferable.end(), "Saving shader usage without storing raw previously");
361 LOG_CRITICAL(Render_OpenGL, "Saving shader usage without storing raw previously"); 484
362 UNREACHABLE();
363 }
364 auto& usages{it->second}; 485 auto& usages{it->second};
365 ASSERT(usages.find(usage) == usages.end()); 486 ASSERT(usages.find(usage) == usages.end());
366 usages.insert(usage); 487 usages.insert(usage);
367 488
368 FileUtil::IOFile file = AppendTransferableFile(); 489 FileUtil::IOFile file = AppendTransferableFile();
369 if (!file.IsOpen()) { 490 if (!file.IsOpen())
491 return;
492
493 if (file.WriteObject(TransferableEntryKind::Usage) != 1 || file.WriteObject(usage) != 1) {
494 LOG_ERROR(Render_OpenGL, "Failed to save usage transferable cache entry - removing");
495 file.Close();
496 InvalidateTransferable();
370 return; 497 return;
371 } 498 }
372 file.WriteObject(TransferableEntryKind::Usage);
373 file.WriteObject(usage);
374} 499}
375 500
376void ShaderDiskCacheOpenGL::SaveDecompiled(u64 unique_identifier, const std::string& code, 501void ShaderDiskCacheOpenGL::SaveDecompiled(u64 unique_identifier, const std::string& code,
377 const GLShader::ShaderEntries& entries) { 502 const GLShader::ShaderEntries& entries) {
378 if (!Settings::values.use_disk_shader_cache) { 503 if (!IsUsable())
379 return; 504 return;
380 }
381
382 FileUtil::IOFile file = AppendPrecompiledFile();
383 if (!file.IsOpen()) {
384 return;
385 }
386 505
387 const std::vector<u8> compressed_code{CompressData(code.data(), code.size())}; 506 const std::vector<u8> compressed_code{CompressData(code.data(), code.size())};
388 if (compressed_code.empty()) { 507 if (compressed_code.empty()) {
@@ -391,52 +510,21 @@ void ShaderDiskCacheOpenGL::SaveDecompiled(u64 unique_identifier, const std::str
391 return; 510 return;
392 } 511 }
393 512
394 file.WriteObject(static_cast<u32>(PrecompiledEntryKind::Decompiled)); 513 FileUtil::IOFile file = AppendPrecompiledFile();
395 514 if (!file.IsOpen())
396 file.WriteObject(unique_identifier); 515 return;
397
398 file.WriteObject(static_cast<u32>(code.size()));
399 file.WriteObject(static_cast<u32>(compressed_code.size()));
400 file.WriteArray(compressed_code.data(), compressed_code.size());
401
402 file.WriteObject(static_cast<u32>(entries.const_buffers.size()));
403 for (const auto& cbuf : entries.const_buffers) {
404 file.WriteObject(static_cast<u32>(cbuf.GetMaxOffset()));
405 file.WriteObject(static_cast<u32>(cbuf.GetIndex()));
406 file.WriteObject(static_cast<u8>(cbuf.IsIndirect() ? 1 : 0));
407 }
408
409 file.WriteObject(static_cast<u32>(entries.samplers.size()));
410 for (const auto& sampler : entries.samplers) {
411 file.WriteObject(static_cast<u64>(sampler.GetOffset()));
412 file.WriteObject(static_cast<u64>(sampler.GetIndex()));
413 file.WriteObject(static_cast<u32>(sampler.GetType()));
414 file.WriteObject(static_cast<u8>(sampler.IsArray() ? 1 : 0));
415 file.WriteObject(static_cast<u8>(sampler.IsShadow() ? 1 : 0));
416 }
417
418 file.WriteObject(static_cast<u32>(entries.global_memory_entries.size()));
419 for (const auto& gmem : entries.global_memory_entries) {
420 file.WriteObject(static_cast<u32>(gmem.GetCbufIndex()));
421 file.WriteObject(static_cast<u32>(gmem.GetCbufOffset()));
422 }
423 516
424 for (const bool clip_distance : entries.clip_distances) { 517 if (!SaveDecompiledFile(file, unique_identifier, code, compressed_code, entries)) {
425 file.WriteObject(static_cast<u8>(clip_distance ? 1 : 0)); 518 LOG_ERROR(Render_OpenGL,
519 "Failed to save decompiled entry to the precompiled file - removing");
520 file.Close();
521 InvalidatePrecompiled();
426 } 522 }
427
428 file.WriteObject(static_cast<u64>(entries.shader_length));
429} 523}
430 524
431void ShaderDiskCacheOpenGL::SaveDump(const ShaderDiskCacheUsage& usage, GLuint program) { 525void ShaderDiskCacheOpenGL::SaveDump(const ShaderDiskCacheUsage& usage, GLuint program) {
432 if (!Settings::values.use_disk_shader_cache) { 526 if (!IsUsable())
433 return; 527 return;
434 }
435
436 FileUtil::IOFile file = AppendPrecompiledFile();
437 if (!file.IsOpen()) {
438 return;
439 }
440 528
441 GLint binary_length{}; 529 GLint binary_length{};
442 glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &binary_length); 530 glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &binary_length);
@@ -452,20 +540,31 @@ void ShaderDiskCacheOpenGL::SaveDump(const ShaderDiskCacheUsage& usage, GLuint p
452 return; 540 return;
453 } 541 }
454 542
455 file.WriteObject(static_cast<u32>(PrecompiledEntryKind::Dump)); 543 FileUtil::IOFile file = AppendPrecompiledFile();
544 if (!file.IsOpen())
545 return;
456 546
457 file.WriteObject(usage); 547 if (file.WriteObject(static_cast<u32>(PrecompiledEntryKind::Dump)) != 1 ||
548 file.WriteObject(usage) != 1 || file.WriteObject(static_cast<u32>(binary_format)) != 1 ||
549 file.WriteObject(static_cast<u32>(binary_length)) != 1 ||
550 file.WriteObject(static_cast<u32>(compressed_binary.size())) != 1 ||
551 file.WriteArray(compressed_binary.data(), compressed_binary.size()) !=
552 compressed_binary.size()) {
553 LOG_ERROR(Render_OpenGL, "Failed to save binary program file in shader={:016x} - removing",
554 usage.unique_identifier);
555 file.Close();
556 InvalidatePrecompiled();
557 return;
558 }
559}
458 560
459 file.WriteObject(static_cast<u32>(binary_format)); 561bool ShaderDiskCacheOpenGL::IsUsable() const {
460 file.WriteObject(static_cast<u32>(binary_length)); 562 return tried_to_load && Settings::values.use_disk_shader_cache;
461 file.WriteObject(static_cast<u32>(compressed_binary.size()));
462 file.WriteArray(compressed_binary.data(), compressed_binary.size());
463} 563}
464 564
465FileUtil::IOFile ShaderDiskCacheOpenGL::AppendTransferableFile() const { 565FileUtil::IOFile ShaderDiskCacheOpenGL::AppendTransferableFile() const {
466 if (!EnsureDirectories()) { 566 if (!EnsureDirectories())
467 return {}; 567 return {};
468 }
469 568
470 const auto transferable_path{GetTransferablePath()}; 569 const auto transferable_path{GetTransferablePath()};
471 const bool existed = FileUtil::Exists(transferable_path); 570 const bool existed = FileUtil::Exists(transferable_path);
@@ -477,15 +576,18 @@ FileUtil::IOFile ShaderDiskCacheOpenGL::AppendTransferableFile() const {
477 } 576 }
478 if (!existed || file.GetSize() == 0) { 577 if (!existed || file.GetSize() == 0) {
479 // If the file didn't exist, write its version 578 // If the file didn't exist, write its version
480 file.WriteObject(NativeVersion); 579 if (file.WriteObject(NativeVersion) != 1) {
580 LOG_ERROR(Render_OpenGL, "Failed to write transferable cache version in path={}",
581 transferable_path);
582 return {};
583 }
481 } 584 }
482 return file; 585 return file;
483} 586}
484 587
485FileUtil::IOFile ShaderDiskCacheOpenGL::AppendPrecompiledFile() const { 588FileUtil::IOFile ShaderDiskCacheOpenGL::AppendPrecompiledFile() const {
486 if (!EnsureDirectories()) { 589 if (!EnsureDirectories())
487 return {}; 590 return {};
488 }
489 591
490 const auto precompiled_path{GetPrecompiledPath()}; 592 const auto precompiled_path{GetPrecompiledPath()};
491 const bool existed = FileUtil::Exists(precompiled_path); 593 const bool existed = FileUtil::Exists(precompiled_path);
@@ -497,9 +599,12 @@ FileUtil::IOFile ShaderDiskCacheOpenGL::AppendPrecompiledFile() const {
497 } 599 }
498 600
499 if (!existed || file.GetSize() == 0) { 601 if (!existed || file.GetSize() == 0) {
500 std::array<char, ShaderHashSize> hash{}; 602 const auto hash{GetShaderCacheVersionHash()};
501 std::strcpy(hash.data(), GetShaderHash().c_str()); 603 if (file.WriteArray(hash.data(), hash.size()) != hash.size()) {
502 file.WriteArray(hash.data(), hash.size()); 604 LOG_ERROR(Render_OpenGL, "Failed to write precompiled cache version hash in path={}",
605 precompiled_path);
606 return {};
607 }
503 } 608 }
504 return file; 609 return file;
505} 610}
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.h b/src/video_core/renderer_opengl/gl_shader_disk_cache.h
index 4bffe4307..ddcd4cf51 100644
--- a/src/video_core/renderer_opengl/gl_shader_disk_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.h
@@ -53,15 +53,15 @@ struct BaseBindings {
53/// Describes a shader how it's used by the guest GPU 53/// Describes a shader how it's used by the guest GPU
54class ShaderDiskCacheRaw { 54class ShaderDiskCacheRaw {
55public: 55public:
56 explicit ShaderDiskCacheRaw(FileUtil::IOFile& file);
57
58 explicit ShaderDiskCacheRaw(u64 unique_identifier, Maxwell::ShaderProgram program_type, 56 explicit ShaderDiskCacheRaw(u64 unique_identifier, Maxwell::ShaderProgram program_type,
59 u32 program_code_size, u32 program_code_size_b, 57 u32 program_code_size, u32 program_code_size_b,
60 ProgramCode program_code, ProgramCode program_code_b); 58 ProgramCode program_code, ProgramCode program_code_b);
61 59 ShaderDiskCacheRaw();
62 ~ShaderDiskCacheRaw(); 60 ~ShaderDiskCacheRaw();
63 61
64 void Save(FileUtil::IOFile& file) const; 62 bool Load(FileUtil::IOFile& file);
63
64 bool Save(FileUtil::IOFile& file) const;
65 65
66 u64 GetUniqueIdentifier() const { 66 u64 GetUniqueIdentifier() const {
67 return unique_identifier; 67 return unique_identifier;
@@ -158,10 +158,10 @@ public:
158 LoadPrecompiled(); 158 LoadPrecompiled();
159 159
160 /// Removes the transferable (and precompiled) cache file. 160 /// Removes the transferable (and precompiled) cache file.
161 bool InvalidateTransferable() const; 161 void InvalidateTransferable() const;
162 162
163 /// Removes the precompiled cache file. 163 /// Removes the precompiled cache file.
164 bool InvalidatePrecompiled() const; 164 void InvalidatePrecompiled() const;
165 165
166 /// Saves a raw dump to the transferable file. Checks for collisions. 166 /// Saves a raw dump to the transferable file. Checks for collisions.
167 void SaveRaw(const ShaderDiskCacheRaw& entry); 167 void SaveRaw(const ShaderDiskCacheRaw& entry);
@@ -177,6 +177,22 @@ public:
177 void SaveDump(const ShaderDiskCacheUsage& usage, GLuint program); 177 void SaveDump(const ShaderDiskCacheUsage& usage, GLuint program);
178 178
179private: 179private:
180 /// Loads the transferable cache. Returns empty on failure.
181 std::optional<std::pair<std::map<u64, ShaderDiskCacheDecompiled>,
182 std::map<ShaderDiskCacheUsage, ShaderDiskCacheDump>>>
183 LoadPrecompiledFile(FileUtil::IOFile& file);
184
185 /// Loads a decompiled cache entry from the passed file. Returns empty on failure.
186 std::optional<ShaderDiskCacheDecompiled> LoadDecompiledEntry(FileUtil::IOFile& file);
187
188 /// Saves a decompiled entry to the passed file. Returns true on success.
189 bool SaveDecompiledFile(FileUtil::IOFile& file, u64 unique_identifier, const std::string& code,
190 const std::vector<u8>& compressed_code,
191 const GLShader::ShaderEntries& entries);
192
193 /// Returns if the cache can be used
194 bool IsUsable() const;
195
180 /// Opens current game's transferable file and write it's header if it doesn't exist 196 /// Opens current game's transferable file and write it's header if it doesn't exist
181 FileUtil::IOFile AppendTransferableFile() const; 197 FileUtil::IOFile AppendTransferableFile() const;
182 198
@@ -203,6 +219,8 @@ private:
203 219
204 // Stored transferable shaders 220 // Stored transferable shaders
205 std::map<u64, std::set<ShaderDiskCacheUsage>> transferable; 221 std::map<u64, std::set<ShaderDiskCacheUsage>> transferable;
222 // The cache has been loaded at boot
223 bool tried_to_load{};
206}; 224};
207 225
208} // namespace OpenGL \ No newline at end of file 226} // namespace OpenGL \ No newline at end of file