diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_cache.cpp | 10 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_disk_cache.cpp | 239 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_disk_cache.h | 54 |
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 | ||
| 412 | CachedProgram ShaderCacheOpenGL::GeneratePrecompiledProgram( | 422 | CachedProgram 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 | ||
| 107 | ShaderDiskCacheOpenGL::ShaderDiskCacheOpenGL(Core::System& system) : system{system} {} | 107 | ShaderDiskCacheOpenGL::ShaderDiskCacheOpenGL(Core::System& system) |
| 108 | : system{system}, precompiled_cache_virtual_file_offset{0} {} | ||
| 108 | 109 | ||
| 109 | std::optional<std::pair<std::vector<ShaderDiskCacheRaw>, std::vector<ShaderDiskCacheUsage>>> | 110 | std::optional<std::pair<std::vector<ShaderDiskCacheRaw>, std::vector<ShaderDiskCacheUsage>>> |
| 110 | ShaderDiskCacheOpenGL::LoadTransferable() { | 111 | ShaderDiskCacheOpenGL::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() { | |||
| 208 | std::optional<std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>, | 210 | std::optional<std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>, |
| 209 | std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>>> | 211 | std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>>> |
| 210 | ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) { | 212 | ShaderDiskCacheOpenGL::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 | ||
| 277 | std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEntry( | 284 | std::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 | ||
| 370 | bool ShaderDiskCacheOpenGL::SaveDecompiledFile(FileUtil::IOFile& file, u64 unique_identifier, | 372 | bool 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 | ||
| 424 | void ShaderDiskCacheOpenGL::InvalidateTransferable() const { | 431 | void 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 | ||
| 432 | void ShaderDiskCacheOpenGL::InvalidatePrecompiled() const { | 439 | void 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 | ||
| 573 | FileUtil::IOFile ShaderDiskCacheOpenGL::AppendPrecompiledFile() const { | 559 | void 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 | |||
| 568 | void 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 | ||
| 597 | bool ShaderDiskCacheOpenGL::EnsureDirectories() const { | 588 | bool 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 | |||
| 193 | private: | 197 | private: |
| 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 | }; |