summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp10
-rw-r--r--src/video_core/renderer_opengl/gl_shader_disk_cache.cpp239
-rw-r--r--src/video_core/renderer_opengl/gl_shader_disk_cache.h54
3 files changed, 170 insertions, 133 deletions
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 2a81b1169..b1c8f7c35 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -363,6 +363,10 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
363 if (stop_loading) 363 if (stop_loading)
364 return; 364 return;
365 365
366 // Track if precompiled cache was altered during loading to know if we have to serialize the
367 // virtual precompiled cache file back to the hard drive
368 bool precompiled_cache_altered = false;
369
366 // Build shaders 370 // Build shaders
367 if (callback) 371 if (callback)
368 callback(VideoCore::LoadCallbackStage::Build, 0, usages.size()); 372 callback(VideoCore::LoadCallbackStage::Build, 0, usages.size());
@@ -384,6 +388,7 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
384 if (!shader) { 388 if (!shader) {
385 // Invalidate the precompiled cache if a shader dumped shader was rejected 389 // Invalidate the precompiled cache if a shader dumped shader was rejected
386 disk_cache.InvalidatePrecompiled(); 390 disk_cache.InvalidatePrecompiled();
391 precompiled_cache_altered = true;
387 dumps.clear(); 392 dumps.clear();
388 } 393 }
389 } 394 }
@@ -405,8 +410,13 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
405 if (dumps.find(usage) == dumps.end()) { 410 if (dumps.find(usage) == dumps.end()) {
406 const auto& program = precompiled_programs.at(usage); 411 const auto& program = precompiled_programs.at(usage);
407 disk_cache.SaveDump(usage, program->handle); 412 disk_cache.SaveDump(usage, program->handle);
413 precompiled_cache_altered = true;
408 } 414 }
409 } 415 }
416
417 if (precompiled_cache_altered) {
418 disk_cache.SaveVirtualPrecompiledFile();
419 }
410} 420}
411 421
412CachedProgram ShaderCacheOpenGL::GeneratePrecompiledProgram( 422CachedProgram ShaderCacheOpenGL::GeneratePrecompiledProgram(
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 53752b38d..ed7afc4a0 100644
--- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
@@ -104,7 +104,8 @@ bool ShaderDiskCacheRaw::Save(FileUtil::IOFile& file) const {
104 return true; 104 return true;
105} 105}
106 106
107ShaderDiskCacheOpenGL::ShaderDiskCacheOpenGL(Core::System& system) : system{system} {} 107ShaderDiskCacheOpenGL::ShaderDiskCacheOpenGL(Core::System& system)
108 : system{system}, precompiled_cache_virtual_file_offset{0} {}
108 109
109std::optional<std::pair<std::vector<ShaderDiskCacheRaw>, std::vector<ShaderDiskCacheUsage>>> 110std::optional<std::pair<std::vector<ShaderDiskCacheRaw>, std::vector<ShaderDiskCacheUsage>>>
110ShaderDiskCacheOpenGL::LoadTransferable() { 111ShaderDiskCacheOpenGL::LoadTransferable() {
@@ -177,6 +178,7 @@ ShaderDiskCacheOpenGL::LoadTransferable() {
177 return {}; 178 return {};
178 } 179 }
179 } 180 }
181
180 return {{raws, usages}}; 182 return {{raws, usages}};
181} 183}
182 184
@@ -208,59 +210,64 @@ ShaderDiskCacheOpenGL::LoadPrecompiled() {
208std::optional<std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>, 210std::optional<std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>,
209 std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>>> 211 std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>>>
210ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) { 212ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) {
213 // Read compressed file from disk and decompress to virtual precompiled cache file
214 std::vector<u8> compressed(file.GetSize());
215 file.ReadBytes(compressed.data(), compressed.size());
216 const std::vector<u8> decompressed = Common::Compression::DecompressDataZSTD(compressed);
217 SaveArrayToPrecompiled(decompressed.data(), decompressed.size());
218 precompiled_cache_virtual_file_offset = 0;
219
211 ShaderCacheVersionHash file_hash{}; 220 ShaderCacheVersionHash file_hash{};
212 if (file.ReadArray(file_hash.data(), file_hash.size()) != file_hash.size()) { 221 if (!LoadArrayFromPrecompiled(file_hash.data(), file_hash.size())) {
222 precompiled_cache_virtual_file_offset = 0;
213 return {}; 223 return {};
214 } 224 }
215 if (GetShaderCacheVersionHash() != file_hash) { 225 if (GetShaderCacheVersionHash() != file_hash) {
216 LOG_INFO(Render_OpenGL, "Precompiled cache is from another version of the emulator"); 226 LOG_INFO(Render_OpenGL, "Precompiled cache is from another version of the emulator");
227 precompiled_cache_virtual_file_offset = 0;
217 return {}; 228 return {};
218 } 229 }
219 230
220 std::unordered_map<u64, ShaderDiskCacheDecompiled> decompiled; 231 std::unordered_map<u64, ShaderDiskCacheDecompiled> decompiled;
221 std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump> dumps; 232 std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump> dumps;
222 while (file.Tell() < file.GetSize()) { 233 while (precompiled_cache_virtual_file_offset < precompiled_cache_virtual_file.GetSize()) {
223 PrecompiledEntryKind kind{}; 234 PrecompiledEntryKind kind{};
224 if (file.ReadBytes(&kind, sizeof(u32)) != sizeof(u32)) { 235 if (!LoadObjectFromPrecompiled(kind)) {
225 return {}; 236 return {};
226 } 237 }
227 238
228 switch (kind) { 239 switch (kind) {
229 case PrecompiledEntryKind::Decompiled: { 240 case PrecompiledEntryKind::Decompiled: {
230 u64 unique_identifier{}; 241 u64 unique_identifier{};
231 if (file.ReadBytes(&unique_identifier, sizeof(u64)) != sizeof(u64)) 242 if (!LoadObjectFromPrecompiled(unique_identifier)) {
232 return {}; 243 return {};
244 }
233 245
234 const auto entry = LoadDecompiledEntry(file); 246 const auto entry = LoadDecompiledEntry();
235 if (!entry) 247 if (!entry) {
236 return {}; 248 return {};
249 }
237 decompiled.insert({unique_identifier, std::move(*entry)}); 250 decompiled.insert({unique_identifier, std::move(*entry)});
238 break; 251 break;
239 } 252 }
240 case PrecompiledEntryKind::Dump: { 253 case PrecompiledEntryKind::Dump: {
241 ShaderDiskCacheUsage usage; 254 ShaderDiskCacheUsage usage;
242 if (file.ReadBytes(&usage, sizeof(usage)) != sizeof(usage)) 255 if (!LoadObjectFromPrecompiled(usage)) {
243 return {}; 256 return {};
257 }
244 258
245 ShaderDiskCacheDump dump; 259 ShaderDiskCacheDump dump;
246 if (file.ReadBytes(&dump.binary_format, sizeof(u32)) != sizeof(u32)) 260 if (!LoadObjectFromPrecompiled(dump.binary_format)) {
247 return {};
248
249 u32 binary_length{};
250 u32 compressed_size{};
251 if (file.ReadBytes(&binary_length, sizeof(u32)) != sizeof(u32) ||
252 file.ReadBytes(&compressed_size, sizeof(u32)) != sizeof(u32)) {
253 return {}; 261 return {};
254 } 262 }
255 263
256 std::vector<u8> compressed_binary(compressed_size); 264 u32 binary_length{};
257 if (file.ReadArray(compressed_binary.data(), compressed_binary.size()) != 265 if (!LoadObjectFromPrecompiled(binary_length)) {
258 compressed_binary.size()) {
259 return {}; 266 return {};
260 } 267 }
261 268
262 dump.binary = Common::Compression::DecompressDataZSTD(compressed_binary); 269 dump.binary.resize(binary_length);
263 if (dump.binary.empty()) { 270 if (!LoadArrayFromPrecompiled(dump.binary.data(), dump.binary.size())) {
264 return {}; 271 return {};
265 } 272 }
266 273
@@ -274,45 +281,41 @@ ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) {
274 return {{decompiled, dumps}}; 281 return {{decompiled, dumps}};
275} 282}
276 283
277std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEntry( 284std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEntry() {
278 FileUtil::IOFile& file) {
279 u32 code_size{}; 285 u32 code_size{};
280 u32 compressed_code_size{}; 286 if (!LoadObjectFromPrecompiled(code_size)) {
281 if (file.ReadBytes(&code_size, sizeof(u32)) != sizeof(u32) ||
282 file.ReadBytes(&compressed_code_size, sizeof(u32)) != sizeof(u32)) {
283 return {}; 287 return {};
284 } 288 }
285 289
286 std::vector<u8> compressed_code(compressed_code_size); 290 std::vector<u8> code(code_size);
287 if (file.ReadArray(compressed_code.data(), compressed_code.size()) != compressed_code.size()) { 291 if (!LoadArrayFromPrecompiled(code.data(), code.size())) {
288 return {}; 292 return {};
289 } 293 }
290 294
291 const std::vector<u8> code = Common::Compression::DecompressDataZSTD(compressed_code);
292 if (code.empty()) {
293 return {};
294 }
295 ShaderDiskCacheDecompiled entry; 295 ShaderDiskCacheDecompiled entry;
296 entry.code = std::string(reinterpret_cast<const char*>(code.data()), code_size); 296 entry.code = std::string(reinterpret_cast<const char*>(code.data()), code_size);
297 297
298 u32 const_buffers_count{}; 298 u32 const_buffers_count{};
299 if (file.ReadBytes(&const_buffers_count, sizeof(u32)) != sizeof(u32)) 299 if (!LoadObjectFromPrecompiled(const_buffers_count)) {
300 return {}; 300 return {};
301 }
302
301 for (u32 i = 0; i < const_buffers_count; ++i) { 303 for (u32 i = 0; i < const_buffers_count; ++i) {
302 u32 max_offset{}; 304 u32 max_offset{};
303 u32 index{}; 305 u32 index{};
304 u8 is_indirect{}; 306 u8 is_indirect{};
305 if (file.ReadBytes(&max_offset, sizeof(u32)) != sizeof(u32) || 307 if (!LoadObjectFromPrecompiled(max_offset) || !LoadObjectFromPrecompiled(index) ||
306 file.ReadBytes(&index, sizeof(u32)) != sizeof(u32) || 308 !LoadObjectFromPrecompiled(is_indirect)) {
307 file.ReadBytes(&is_indirect, sizeof(u8)) != sizeof(u8)) {
308 return {}; 309 return {};
309 } 310 }
310 entry.entries.const_buffers.emplace_back(max_offset, is_indirect != 0, index); 311 entry.entries.const_buffers.emplace_back(max_offset, is_indirect != 0, index);
311 } 312 }
312 313
313 u32 samplers_count{}; 314 u32 samplers_count{};
314 if (file.ReadBytes(&samplers_count, sizeof(u32)) != sizeof(u32)) 315 if (!LoadObjectFromPrecompiled(samplers_count)) {
315 return {}; 316 return {};
317 }
318
316 for (u32 i = 0; i < samplers_count; ++i) { 319 for (u32 i = 0; i < samplers_count; ++i) {
317 u64 offset{}; 320 u64 offset{};
318 u64 index{}; 321 u64 index{};
@@ -320,12 +323,9 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn
320 u8 is_array{}; 323 u8 is_array{};
321 u8 is_shadow{}; 324 u8 is_shadow{};
322 u8 is_bindless{}; 325 u8 is_bindless{};
323 if (file.ReadBytes(&offset, sizeof(u64)) != sizeof(u64) || 326 if (!LoadObjectFromPrecompiled(offset) || !LoadObjectFromPrecompiled(index) ||
324 file.ReadBytes(&index, sizeof(u64)) != sizeof(u64) || 327 !LoadObjectFromPrecompiled(type) || !LoadObjectFromPrecompiled(is_array) ||
325 file.ReadBytes(&type, sizeof(u32)) != sizeof(u32) || 328 !LoadObjectFromPrecompiled(is_shadow) || !LoadObjectFromPrecompiled(is_bindless)) {
326 file.ReadBytes(&is_array, sizeof(u8)) != sizeof(u8) ||
327 file.ReadBytes(&is_shadow, sizeof(u8)) != sizeof(u8) ||
328 file.ReadBytes(&is_bindless, sizeof(u8)) != sizeof(u8)) {
329 return {}; 329 return {};
330 } 330 }
331 entry.entries.samplers.emplace_back(static_cast<std::size_t>(offset), 331 entry.entries.samplers.emplace_back(static_cast<std::size_t>(offset),
@@ -335,17 +335,17 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn
335 } 335 }
336 336
337 u32 global_memory_count{}; 337 u32 global_memory_count{};
338 if (file.ReadBytes(&global_memory_count, sizeof(u32)) != sizeof(u32)) 338 if (!LoadObjectFromPrecompiled(global_memory_count)) {
339 return {}; 339 return {};
340 }
341
340 for (u32 i = 0; i < global_memory_count; ++i) { 342 for (u32 i = 0; i < global_memory_count; ++i) {
341 u32 cbuf_index{}; 343 u32 cbuf_index{};
342 u32 cbuf_offset{}; 344 u32 cbuf_offset{};
343 u8 is_read{}; 345 u8 is_read{};
344 u8 is_written{}; 346 u8 is_written{};
345 if (file.ReadBytes(&cbuf_index, sizeof(u32)) != sizeof(u32) || 347 if (!LoadObjectFromPrecompiled(cbuf_index) || !LoadObjectFromPrecompiled(cbuf_offset) ||
346 file.ReadBytes(&cbuf_offset, sizeof(u32)) != sizeof(u32) || 348 !LoadObjectFromPrecompiled(is_read) || !LoadObjectFromPrecompiled(is_written)) {
347 file.ReadBytes(&is_read, sizeof(u8)) != sizeof(u8) ||
348 file.ReadBytes(&is_written, sizeof(u8)) != sizeof(u8)) {
349 return {}; 349 return {};
350 } 350 }
351 entry.entries.global_memory_entries.emplace_back(cbuf_index, cbuf_offset, is_read != 0, 351 entry.entries.global_memory_entries.emplace_back(cbuf_index, cbuf_offset, is_read != 0,
@@ -354,74 +354,81 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn
354 354
355 for (auto& clip_distance : entry.entries.clip_distances) { 355 for (auto& clip_distance : entry.entries.clip_distances) {
356 u8 clip_distance_raw{}; 356 u8 clip_distance_raw{};
357 if (file.ReadBytes(&clip_distance_raw, sizeof(u8)) != sizeof(u8)) 357 if (!LoadObjectFromPrecompiled(clip_distance_raw))
358 return {}; 358 return {};
359 clip_distance = clip_distance_raw != 0; 359 clip_distance = clip_distance_raw != 0;
360 } 360 }
361 361
362 u64 shader_length{}; 362 u64 shader_length{};
363 if (file.ReadBytes(&shader_length, sizeof(u64)) != sizeof(u64)) 363 if (!LoadObjectFromPrecompiled(shader_length)) {
364 return {}; 364 return {};
365 }
366
365 entry.entries.shader_length = static_cast<std::size_t>(shader_length); 367 entry.entries.shader_length = static_cast<std::size_t>(shader_length);
366 368
367 return entry; 369 return entry;
368} 370}
369 371
370bool ShaderDiskCacheOpenGL::SaveDecompiledFile(FileUtil::IOFile& file, u64 unique_identifier, 372bool ShaderDiskCacheOpenGL::SaveDecompiledFile(u64 unique_identifier, const std::string& code,
371 const std::string& code,
372 const std::vector<u8>& compressed_code,
373 const GLShader::ShaderEntries& entries) { 373 const GLShader::ShaderEntries& entries) {
374 if (file.WriteObject(static_cast<u32>(PrecompiledEntryKind::Decompiled)) != 1 || 374 if (!SaveObjectToPrecompiled(static_cast<u32>(PrecompiledEntryKind::Decompiled)) ||
375 file.WriteObject(unique_identifier) != 1 || 375 !SaveObjectToPrecompiled(unique_identifier) ||
376 file.WriteObject(static_cast<u32>(code.size())) != 1 || 376 !SaveObjectToPrecompiled(static_cast<u32>(code.size())) ||
377 file.WriteObject(static_cast<u32>(compressed_code.size())) != 1 || 377 !SaveArrayToPrecompiled(code.data(), code.size())) {
378 file.WriteArray(compressed_code.data(), compressed_code.size()) != compressed_code.size()) {
379 return false; 378 return false;
380 } 379 }
381 380
382 if (file.WriteObject(static_cast<u32>(entries.const_buffers.size())) != 1) 381 if (!SaveObjectToPrecompiled(static_cast<u32>(entries.const_buffers.size()))) {
383 return false; 382 return false;
383 }
384 for (const auto& cbuf : entries.const_buffers) { 384 for (const auto& cbuf : entries.const_buffers) {
385 if (file.WriteObject(static_cast<u32>(cbuf.GetMaxOffset())) != 1 || 385 if (!SaveObjectToPrecompiled(static_cast<u32>(cbuf.GetMaxOffset())) ||
386 file.WriteObject(static_cast<u32>(cbuf.GetIndex())) != 1 || 386 !SaveObjectToPrecompiled(static_cast<u32>(cbuf.GetIndex())) ||
387 file.WriteObject(static_cast<u8>(cbuf.IsIndirect() ? 1 : 0)) != 1) { 387 !SaveObjectToPrecompiled(static_cast<u8>(cbuf.IsIndirect() ? 1 : 0))) {
388 return false; 388 return false;
389 } 389 }
390 } 390 }
391 391
392 if (file.WriteObject(static_cast<u32>(entries.samplers.size())) != 1) 392 if (!SaveObjectToPrecompiled(static_cast<u32>(entries.samplers.size()))) {
393 return false; 393 return false;
394 }
394 for (const auto& sampler : entries.samplers) { 395 for (const auto& sampler : entries.samplers) {
395 if (file.WriteObject(static_cast<u64>(sampler.GetOffset())) != 1 || 396 if (!SaveObjectToPrecompiled(static_cast<u64>(sampler.GetOffset())) ||
396 file.WriteObject(static_cast<u64>(sampler.GetIndex())) != 1 || 397 !SaveObjectToPrecompiled(static_cast<u64>(sampler.GetIndex())) ||
397 file.WriteObject(static_cast<u32>(sampler.GetType())) != 1 || 398 !SaveObjectToPrecompiled(static_cast<u32>(sampler.GetType())) ||
398 file.WriteObject(static_cast<u8>(sampler.IsArray() ? 1 : 0)) != 1 || 399 !SaveObjectToPrecompiled(static_cast<u8>(sampler.IsArray() ? 1 : 0)) ||
399 file.WriteObject(static_cast<u8>(sampler.IsShadow() ? 1 : 0)) != 1 || 400 !SaveObjectToPrecompiled(static_cast<u8>(sampler.IsShadow() ? 1 : 0)) ||
400 file.WriteObject(static_cast<u8>(sampler.IsBindless() ? 1 : 0)) != 1) { 401 !SaveObjectToPrecompiled(static_cast<u8>(sampler.IsBindless() ? 1 : 0))) {
401 return false; 402 return false;
402 } 403 }
403 } 404 }
404 405
405 if (file.WriteObject(static_cast<u32>(entries.global_memory_entries.size())) != 1) 406 if (!SaveObjectToPrecompiled(static_cast<u32>(entries.global_memory_entries.size()))) {
406 return false; 407 return false;
408 }
407 for (const auto& gmem : entries.global_memory_entries) { 409 for (const auto& gmem : entries.global_memory_entries) {
408 if (file.WriteObject(static_cast<u32>(gmem.GetCbufIndex())) != 1 || 410 if (!SaveObjectToPrecompiled(static_cast<u32>(gmem.GetCbufIndex())) ||
409 file.WriteObject(static_cast<u32>(gmem.GetCbufOffset())) != 1 || 411 !SaveObjectToPrecompiled(static_cast<u32>(gmem.GetCbufOffset())) ||
410 file.WriteObject(static_cast<u8>(gmem.IsRead() ? 1 : 0)) != 1 || 412 !SaveObjectToPrecompiled(static_cast<u8>(gmem.IsRead() ? 1 : 0)) ||
411 file.WriteObject(static_cast<u8>(gmem.IsWritten() ? 1 : 0)) != 1) { 413 !SaveObjectToPrecompiled(static_cast<u8>(gmem.IsWritten() ? 1 : 0))) {
412 return false; 414 return false;
413 } 415 }
414 } 416 }
415 417
416 for (const bool clip_distance : entries.clip_distances) { 418 for (const bool clip_distance : entries.clip_distances) {
417 if (file.WriteObject(static_cast<u8>(clip_distance ? 1 : 0)) != 1) 419 if (!SaveObjectToPrecompiled(static_cast<u8>(clip_distance ? 1 : 0))) {
418 return false; 420 return false;
421 }
419 } 422 }
420 423
421 return file.WriteObject(static_cast<u64>(entries.shader_length)) == 1; 424 if (!SaveObjectToPrecompiled(static_cast<u64>(entries.shader_length))) {
425 return false;
426 }
427
428 return true;
422} 429}
423 430
424void ShaderDiskCacheOpenGL::InvalidateTransferable() const { 431void ShaderDiskCacheOpenGL::InvalidateTransferable() {
425 if (!FileUtil::Delete(GetTransferablePath())) { 432 if (!FileUtil::Delete(GetTransferablePath())) {
426 LOG_ERROR(Render_OpenGL, "Failed to invalidate transferable file={}", 433 LOG_ERROR(Render_OpenGL, "Failed to invalidate transferable file={}",
427 GetTransferablePath()); 434 GetTransferablePath());
@@ -429,7 +436,10 @@ void ShaderDiskCacheOpenGL::InvalidateTransferable() const {
429 InvalidatePrecompiled(); 436 InvalidatePrecompiled();
430} 437}
431 438
432void ShaderDiskCacheOpenGL::InvalidatePrecompiled() const { 439void ShaderDiskCacheOpenGL::InvalidatePrecompiled() {
440 // Clear virtaul precompiled cache file
441 precompiled_cache_virtual_file.Resize(0);
442
433 if (!FileUtil::Delete(GetPrecompiledPath())) { 443 if (!FileUtil::Delete(GetPrecompiledPath())) {
434 LOG_ERROR(Render_OpenGL, "Failed to invalidate precompiled file={}", GetPrecompiledPath()); 444 LOG_ERROR(Render_OpenGL, "Failed to invalidate precompiled file={}", GetPrecompiledPath());
435 } 445 }
@@ -485,22 +495,13 @@ void ShaderDiskCacheOpenGL::SaveDecompiled(u64 unique_identifier, const std::str
485 if (!IsUsable()) 495 if (!IsUsable())
486 return; 496 return;
487 497
488 const std::vector<u8> compressed_code{Common::Compression::CompressDataZSTDDefault( 498 if (precompiled_cache_virtual_file.GetSize() == 0) {
489 reinterpret_cast<const u8*>(code.data()), code.size())}; 499 SavePrecompiledHeaderToVirtualPrecompiledCache();
490 if (compressed_code.empty()) {
491 LOG_ERROR(Render_OpenGL, "Failed to compress GLSL code - skipping shader {:016x}",
492 unique_identifier);
493 return;
494 } 500 }
495 501
496 FileUtil::IOFile file = AppendPrecompiledFile(); 502 if (!SaveDecompiledFile(unique_identifier, code, entries)) {
497 if (!file.IsOpen())
498 return;
499
500 if (!SaveDecompiledFile(file, unique_identifier, code, compressed_code, entries)) {
501 LOG_ERROR(Render_OpenGL, 503 LOG_ERROR(Render_OpenGL,
502 "Failed to save decompiled entry to the precompiled file - removing"); 504 "Failed to save decompiled entry to the precompiled file - removing");
503 file.Close();
504 InvalidatePrecompiled(); 505 InvalidatePrecompiled();
505 } 506 }
506} 507}
@@ -516,28 +517,13 @@ void ShaderDiskCacheOpenGL::SaveDump(const ShaderDiskCacheUsage& usage, GLuint p
516 std::vector<u8> binary(binary_length); 517 std::vector<u8> binary(binary_length);
517 glGetProgramBinary(program, binary_length, nullptr, &binary_format, binary.data()); 518 glGetProgramBinary(program, binary_length, nullptr, &binary_format, binary.data());
518 519
519 const std::vector<u8> compressed_binary = 520 if (!SaveObjectToPrecompiled(static_cast<u32>(PrecompiledEntryKind::Dump)) ||
520 Common::Compression::CompressDataZSTDDefault(binary.data(), binary.size()); 521 !SaveObjectToPrecompiled(usage) ||
521 522 !SaveObjectToPrecompiled(static_cast<u32>(binary_format)) ||
522 if (compressed_binary.empty()) { 523 !SaveObjectToPrecompiled(static_cast<u32>(binary_length)) ||
523 LOG_ERROR(Render_OpenGL, "Failed to compress binary program in shader={:016x}", 524 !SaveArrayToPrecompiled(binary.data(), binary.size())) {
524 usage.unique_identifier);
525 return;
526 }
527
528 FileUtil::IOFile file = AppendPrecompiledFile();
529 if (!file.IsOpen())
530 return;
531
532 if (file.WriteObject(static_cast<u32>(PrecompiledEntryKind::Dump)) != 1 ||
533 file.WriteObject(usage) != 1 || file.WriteObject(static_cast<u32>(binary_format)) != 1 ||
534 file.WriteObject(static_cast<u32>(binary_length)) != 1 ||
535 file.WriteObject(static_cast<u32>(compressed_binary.size())) != 1 ||
536 file.WriteArray(compressed_binary.data(), compressed_binary.size()) !=
537 compressed_binary.size()) {
538 LOG_ERROR(Render_OpenGL, "Failed to save binary program file in shader={:016x} - removing", 525 LOG_ERROR(Render_OpenGL, "Failed to save binary program file in shader={:016x} - removing",
539 usage.unique_identifier); 526 usage.unique_identifier);
540 file.Close();
541 InvalidatePrecompiled(); 527 InvalidatePrecompiled();
542 return; 528 return;
543 } 529 }
@@ -570,28 +556,33 @@ FileUtil::IOFile ShaderDiskCacheOpenGL::AppendTransferableFile() const {
570 return file; 556 return file;
571} 557}
572 558
573FileUtil::IOFile ShaderDiskCacheOpenGL::AppendPrecompiledFile() const { 559void ShaderDiskCacheOpenGL::SavePrecompiledHeaderToVirtualPrecompiledCache() {
574 if (!EnsureDirectories()) 560 const auto hash{GetShaderCacheVersionHash()};
575 return {}; 561 if (!SaveArrayToPrecompiled(hash.data(), hash.size())) {
562 LOG_ERROR(
563 Render_OpenGL,
564 "Failed to write precompiled cache version hash to virtual precompiled cache file");
565 }
566}
567
568void ShaderDiskCacheOpenGL::SaveVirtualPrecompiledFile() {
569 precompiled_cache_virtual_file_offset = 0;
570 const std::vector<u8>& uncompressed = precompiled_cache_virtual_file.ReadAllBytes();
571 const std::vector<u8>& compressed =
572 Common::Compression::CompressDataZSTDDefault(uncompressed.data(), uncompressed.size());
576 573
577 const auto precompiled_path{GetPrecompiledPath()}; 574 const auto precompiled_path{GetPrecompiledPath()};
578 const bool existed = FileUtil::Exists(precompiled_path); 575 FileUtil::IOFile file(precompiled_path, "wb");
579 576
580 FileUtil::IOFile file(precompiled_path, "ab");
581 if (!file.IsOpen()) { 577 if (!file.IsOpen()) {
582 LOG_ERROR(Render_OpenGL, "Failed to open precompiled cache in path={}", precompiled_path); 578 LOG_ERROR(Render_OpenGL, "Failed to open precompiled cache in path={}", precompiled_path);
583 return {}; 579 return;
584 } 580 }
585 581 if (file.WriteBytes(compressed.data(), compressed.size()) != compressed.size()) {
586 if (!existed || file.GetSize() == 0) { 582 LOG_ERROR(Render_OpenGL, "Failed to write precompiled cache version in path={}",
587 const auto hash{GetShaderCacheVersionHash()}; 583 precompiled_path);
588 if (file.WriteArray(hash.data(), hash.size()) != hash.size()) { 584 return;
589 LOG_ERROR(Render_OpenGL, "Failed to write precompiled cache version hash in path={}",
590 precompiled_path);
591 return {};
592 }
593 } 585 }
594 return file;
595} 586}
596 587
597bool ShaderDiskCacheOpenGL::EnsureDirectories() const { 588bool ShaderDiskCacheOpenGL::EnsureDirectories() const {
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 6be0c0547..0142b2e3b 100644
--- a/src/video_core/renderer_opengl/gl_shader_disk_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.h
@@ -16,6 +16,7 @@
16 16
17#include "common/assert.h" 17#include "common/assert.h"
18#include "common/common_types.h" 18#include "common/common_types.h"
19#include "core/file_sys/vfs_vector.h"
19#include "video_core/engines/maxwell_3d.h" 20#include "video_core/engines/maxwell_3d.h"
20#include "video_core/renderer_opengl/gl_shader_gen.h" 21#include "video_core/renderer_opengl/gl_shader_gen.h"
21 22
@@ -172,10 +173,10 @@ public:
172 LoadPrecompiled(); 173 LoadPrecompiled();
173 174
174 /// Removes the transferable (and precompiled) cache file. 175 /// Removes the transferable (and precompiled) cache file.
175 void InvalidateTransferable() const; 176 void InvalidateTransferable();
176 177
177 /// Removes the precompiled cache file. 178 /// Removes the precompiled cache file and clears virtual precompiled cache file.
178 void InvalidatePrecompiled() const; 179 void InvalidatePrecompiled();
179 180
180 /// Saves a raw dump to the transferable file. Checks for collisions. 181 /// Saves a raw dump to the transferable file. Checks for collisions.
181 void SaveRaw(const ShaderDiskCacheRaw& entry); 182 void SaveRaw(const ShaderDiskCacheRaw& entry);
@@ -190,18 +191,21 @@ public:
190 /// Saves a dump entry to the precompiled file. Does not check for collisions. 191 /// Saves a dump entry to the precompiled file. Does not check for collisions.
191 void SaveDump(const ShaderDiskCacheUsage& usage, GLuint program); 192 void SaveDump(const ShaderDiskCacheUsage& usage, GLuint program);
192 193
194 /// Serializes virtual precompiled shader cache file to real file
195 void SaveVirtualPrecompiledFile();
196
193private: 197private:
194 /// Loads the transferable cache. Returns empty on failure. 198 /// Loads the transferable cache. Returns empty on failure.
195 std::optional<std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>, 199 std::optional<std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>,
196 std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>>> 200 std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>>>
197 LoadPrecompiledFile(FileUtil::IOFile& file); 201 LoadPrecompiledFile(FileUtil::IOFile& file);
198 202
199 /// Loads a decompiled cache entry from the passed file. Returns empty on failure. 203 /// Loads a decompiled cache entry from m_precompiled_cache_virtual_file. Returns empty on
200 std::optional<ShaderDiskCacheDecompiled> LoadDecompiledEntry(FileUtil::IOFile& file); 204 /// failure.
205 std::optional<ShaderDiskCacheDecompiled> LoadDecompiledEntry();
201 206
202 /// Saves a decompiled entry to the passed file. Returns true on success. 207 /// Saves a decompiled entry to the passed file. Returns true on success.
203 bool SaveDecompiledFile(FileUtil::IOFile& file, u64 unique_identifier, const std::string& code, 208 bool SaveDecompiledFile(u64 unique_identifier, const std::string& code,
204 const std::vector<u8>& compressed_code,
205 const GLShader::ShaderEntries& entries); 209 const GLShader::ShaderEntries& entries);
206 210
207 /// Returns if the cache can be used 211 /// Returns if the cache can be used
@@ -210,8 +214,8 @@ private:
210 /// Opens current game's transferable file and write it's header if it doesn't exist 214 /// Opens current game's transferable file and write it's header if it doesn't exist
211 FileUtil::IOFile AppendTransferableFile() const; 215 FileUtil::IOFile AppendTransferableFile() const;
212 216
213 /// Opens current game's precompiled file and write it's header if it doesn't exist 217 /// Save precompiled header to precompiled_cache_in_memory
214 FileUtil::IOFile AppendPrecompiledFile() const; 218 void SavePrecompiledHeaderToVirtualPrecompiledCache();
215 219
216 /// Create shader disk cache directories. Returns true on success. 220 /// Create shader disk cache directories. Returns true on success.
217 bool EnsureDirectories() const; 221 bool EnsureDirectories() const;
@@ -234,10 +238,42 @@ private:
234 /// Get current game's title id 238 /// Get current game's title id
235 std::string GetTitleID() const; 239 std::string GetTitleID() const;
236 240
241 template <typename T>
242 bool SaveArrayToPrecompiled(const T* data, std::size_t length) {
243 const std::size_t write_length = precompiled_cache_virtual_file.WriteArray(
244 data, length, precompiled_cache_virtual_file_offset);
245 precompiled_cache_virtual_file_offset += write_length;
246 return write_length == sizeof(T) * length;
247 }
248
249 template <typename T>
250 bool LoadArrayFromPrecompiled(T* data, std::size_t length) {
251 const std::size_t read_length = precompiled_cache_virtual_file.ReadArray(
252 data, length, precompiled_cache_virtual_file_offset);
253 precompiled_cache_virtual_file_offset += read_length;
254 return read_length == sizeof(T) * length;
255 }
256
257 template <typename T>
258 bool SaveObjectToPrecompiled(const T& object) {
259 return SaveArrayToPrecompiled(&object, 1);
260 }
261
262 template <typename T>
263 bool LoadObjectFromPrecompiled(T& object) {
264 return LoadArrayFromPrecompiled(&object, 1);
265 }
266
237 // Copre system 267 // Copre system
238 Core::System& system; 268 Core::System& system;
239 // Stored transferable shaders 269 // Stored transferable shaders
240 std::map<u64, std::unordered_set<ShaderDiskCacheUsage>> transferable; 270 std::map<u64, std::unordered_set<ShaderDiskCacheUsage>> transferable;
271 // Stores whole precompiled cache which will be read from or saved to the precompiled chache
272 // file
273 FileSys::VectorVfsFile precompiled_cache_virtual_file;
274 // Stores the current offset of the precompiled cache file for IO purposes
275 std::size_t precompiled_cache_virtual_file_offset;
276
241 // The cache has been loaded at boot 277 // The cache has been loaded at boot
242 bool tried_to_load{}; 278 bool tried_to_load{};
243}; 279};