diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp index 11fd3491e..e8653da23 100644 --- a/src/android/app/src/main/jni/config.cpp +++ b/src/android/app/src/main/jni/config.cpp @@ -124,7 +124,7 @@ void Config::ReadValues() { Settings::values.resolution_factor = static_cast(sdl2_config->GetInteger("Renderer", "resolution_factor", 1)); Settings::values.use_disk_shader_cache = - sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", true); + sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", true); Settings::values.use_vsync_new = sdl2_config->GetBoolean("Renderer", "use_vsync_new", true); // Work around to map Android setting for enabling the frame limiter to the format Citra expects diff --git a/src/android/app/src/main/jni/id_cache.cpp b/src/android/app/src/main/jni/id_cache.cpp index be69f5874..d060baf1e 100644 --- a/src/android/app/src/main/jni/id_cache.cpp +++ b/src/android/app/src/main/jni/id_cache.cpp @@ -145,10 +145,10 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) { env->NewGlobalRef(env->FindClass("org/citra/citra_emu/NativeLibrary$SavestateInfo"))); s_core_error_class = reinterpret_cast( env->NewGlobalRef(env->FindClass("org/citra/citra_emu/NativeLibrary$CoreError"))); - s_disk_cache_progress_class = reinterpret_cast( - env->NewGlobalRef(env->FindClass("org/citra/citra_emu/disk_shader_cache/DiskShaderCacheProgress"))); - s_load_callback_stage_class = reinterpret_cast( - env->NewGlobalRef(env->FindClass("org/citra/citra_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage"))); + s_disk_cache_progress_class = reinterpret_cast(env->NewGlobalRef( + env->FindClass("org/citra/citra_emu/disk_shader_cache/DiskShaderCacheProgress"))); + s_load_callback_stage_class = reinterpret_cast(env->NewGlobalRef(env->FindClass( + "org/citra/citra_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage"))); s_on_core_error = env->GetStaticMethodID( s_native_library_class, "OnCoreError", @@ -169,8 +169,9 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) { env->GetStaticMethodID(s_native_library_class, "RequestCameraPermission", "()Z"); s_request_mic_permission = env->GetStaticMethodID(s_native_library_class, "RequestMicPermission", "()Z"); - s_disk_cache_load_progress = - env->GetStaticMethodID(s_disk_cache_progress_class, "loadProgress", "(Lorg/citra/citra_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage;II)V"); + s_disk_cache_load_progress = env->GetStaticMethodID( + s_disk_cache_progress_class, "loadProgress", + "(Lorg/citra/citra_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage;II)V"); MiiSelector::InitJNI(env); SoftwareKeyboard::InitJNI(env); diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 0a0484b01..931b704d8 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -121,10 +121,10 @@ static bool HandleCoreError(Core::System::ResultStatus result, const std::string static jobject ToJavaLoadCallbackStage(VideoCore::LoadCallbackStage stage) { static const std::map LoadCallbackStageMap{ - {VideoCore::LoadCallbackStage::Prepare, "Prepare"}, - {VideoCore::LoadCallbackStage::Decompile, "Decompile"}, - {VideoCore::LoadCallbackStage::Build, "Build"}, - {VideoCore::LoadCallbackStage::Complete, "Complete"}, + {VideoCore::LoadCallbackStage::Prepare, "Prepare"}, + {VideoCore::LoadCallbackStage::Decompile, "Decompile"}, + {VideoCore::LoadCallbackStage::Build, "Build"}, + {VideoCore::LoadCallbackStage::Complete, "Complete"}, }; const auto name = LoadCallbackStageMap.at(stage); @@ -133,13 +133,17 @@ static jobject ToJavaLoadCallbackStage(VideoCore::LoadCallbackStage stage) { const jclass load_callback_stage_class = IDCache::GetDiskCacheLoadCallbackStageClass(); return env->GetStaticObjectField( - load_callback_stage_class, env->GetStaticFieldID(load_callback_stage_class, name, - "Lorg/citra/citra_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage;")); + load_callback_stage_class, + env->GetStaticFieldID( + load_callback_stage_class, name, + "Lorg/citra/citra_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage;")); } static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max) { JNIEnv* env = IDCache::GetEnvForThread(); - env->CallStaticVoidMethod(IDCache::GetDiskCacheProgressClass(), IDCache::GetDiskCacheLoadProgress(), ToJavaLoadCallbackStage(stage), (jint) progress, (jint) max); + env->CallStaticVoidMethod(IDCache::GetDiskCacheProgressClass(), + IDCache::GetDiskCacheLoadProgress(), ToJavaLoadCallbackStage(stage), + (jint)progress, (jint)max); } static Camera::NDK::Factory* g_ndk_factory{}; @@ -220,9 +224,7 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) { cpu_context->MakeCurrent(); } - system.Renderer().Rasterizer()->LoadDiskResources( - !is_running, &LoadDiskCacheProgress - ); + system.Renderer().Rasterizer()->LoadDiskResources(!is_running, &LoadDiskCacheProgress); if (Settings::values.use_asynchronous_gpu_emulation) { cpu_context->DoneCurrent(); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 0a8e91e60..f6b8d7f18 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -171,16 +171,17 @@ RasterizerOpenGL::RasterizerOpenGL(Frontend::EmuWindow& emu_window) #ifdef __APPLE__ if (IsVendorIntel()) { - shader_program_manager = std::make_unique(emu_window, + shader_program_manager = std::make_unique( + emu_window, VideoCore::g_separable_shader_enabled ? GLAD_GL_ARB_separate_shader_objects : false, is_amd); } else { - shader_program_manager = - std::make_unique(emu_window, GLAD_GL_ARB_separate_shader_objects, is_amd); + shader_program_manager = std::make_unique( + emu_window, GLAD_GL_ARB_separate_shader_objects, is_amd); } #else - shader_program_manager = - std::make_unique(emu_window, GLAD_GL_ARB_separate_shader_objects, is_amd); + shader_program_manager = std::make_unique( + emu_window, GLAD_GL_ARB_separate_shader_objects, is_amd); #endif glEnable(GL_BLEND); 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 5e06462c8..6091c6bf2 100644 --- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp @@ -106,8 +106,8 @@ ShaderDiskCache::ShaderDiskCache(bool separable) : separable{separable} {} std::optional> ShaderDiskCache::LoadTransferable() { const bool has_title_id = GetProgramID() != 0; - if (!Settings::values.use_hw_shader || - !Settings::values.use_disk_shader_cache || !has_title_id) { + if (!Settings::values.use_hw_shader || !Settings::values.use_disk_shader_cache || + !has_title_id) { return std::nullopt; } tried_to_load = true; @@ -200,7 +200,8 @@ ShaderDiskCache::LoadPrecompiledFile(FileUtil::IOFile& file, bool compressed) { std::vector precompiled_file(file.GetSize()); file.ReadBytes(precompiled_file.data(), precompiled_file.size()); if (compressed) { - const std::vector decompressed = Common::Compression::DecompressDataZSTD(precompiled_file); + const std::vector decompressed = + Common::Compression::DecompressDataZSTD(precompiled_file); SaveArrayToPrecompiled(decompressed.data(), decompressed.size()); } else { SaveArrayToPrecompiled(precompiled_file.data(), precompiled_file.size()); @@ -299,14 +300,13 @@ std::optional ShaderDiskCache::LoadDecompiledEntry() } void ShaderDiskCache::SaveDecompiledToFile(FileUtil::IOFile& file, u64 unique_identifier, - const ShaderDecompiler::ProgramResult& result, - bool sanitize_mul) { + const ShaderDecompiler::ProgramResult& result, + bool sanitize_mul) { if (!IsUsable()) return; if (file.WriteObject(static_cast(PrecompiledEntryKind::Decompiled)) != 1 || - file.WriteObject(unique_identifier) != 1 || - file.WriteObject(sanitize_mul) != 1 || + file.WriteObject(unique_identifier) != 1 || file.WriteObject(sanitize_mul) != 1 || file.WriteObject(static_cast(result.code.size())) != 1 || file.WriteArray(result.code.data(), result.code.size()) != result.code.size()) { LOG_ERROR(Render_OpenGL, "Failed to save decompiled cache entry - removing"); @@ -435,7 +435,7 @@ void ShaderDiskCache::SaveDumpToFile(u64 unique_identifier, GLuint program, bool if (file.WriteObject(static_cast(PrecompiledEntryKind::Dump)) != 1 || file.WriteObject(unique_identifier) != 1 || file.WriteObject(static_cast(binary_format)) != 1 || - file.WriteObject(static_cast(binary_length)) != 1|| + file.WriteObject(static_cast(binary_length)) != 1 || file.WriteArray(binary.data(), binary.size()) != binary.size()) { LOG_ERROR(Render_OpenGL, "Failed to save binary program file in shader={:016x} - removing", unique_identifier); 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 97db6f89b..5a2faeb9a 100644 --- a/src/video_core/renderer_opengl/gl_shader_disk_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.h @@ -131,8 +131,8 @@ private: std::optional LoadDecompiledEntry(); /// Saves a decompiled entry to the passed file. Does not check for collisions. - void SaveDecompiledToFile(FileUtil::IOFile& file, u64 unique_identifier, const ShaderDecompiler::ProgramResult& code, - bool sanitize_mul); + void SaveDecompiledToFile(FileUtil::IOFile& file, u64 unique_identifier, + const ShaderDecompiler::ProgramResult& code, bool sanitize_mul); /// Saves a decompiled entry to the virtual precompiled cache. Does not check for collisions. bool SaveDecompiledToCache(u64 unique_identifier, const ShaderDecompiler::ProgramResult& code, diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp index 128e138fa..6f314d53f 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.cpp +++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp @@ -27,7 +27,8 @@ static u64 GetUniqueIdentifier(const Pica::Regs& regs, const ProgramCode& code) } static OGLProgram GeneratePrecompiledProgram(const ShaderDiskCacheDump& dump, - const std::set& supported_formats, bool separable) { + const std::set& supported_formats, + bool separable) { if (supported_formats.find(dump.binary_format) == supported_formats.end()) { LOG_INFO(Render_OpenGL, "Precompiled cache entry with unsupported format - removing"); @@ -384,7 +385,8 @@ public: ShaderDiskCache disk_cache; }; -ShaderProgramManager::ShaderProgramManager(Frontend::EmuWindow& emu_window_, bool separable, bool is_amd) +ShaderProgramManager::ShaderProgramManager(Frontend::EmuWindow& emu_window_, bool separable, + bool is_amd) : impl(std::make_unique(separable, is_amd)), emu_window{emu_window_} {} ShaderProgramManager::~ShaderProgramManager() = default; @@ -469,9 +471,11 @@ void ShaderProgramManager::ApplyTo(OpenGLState& state) { cached_program = std::move(disk_program); impl->disk_program_cache.erase(unique_identifier); } else { - cached_program.Create(false, {impl->current.vs, impl->current.gs, impl->current.fs}); + cached_program.Create(false, + {impl->current.vs, impl->current.gs, impl->current.fs}); auto& disk_cache = impl->disk_cache; - disk_cache.SaveDumpToFile(unique_identifier, cached_program.handle, VideoCore::g_hw_shader_accurate_mul); + disk_cache.SaveDumpToFile(unique_identifier, cached_program.handle, + VideoCore::g_hw_shader_accurate_mul); } SetShaderUniformBlockBindings(cached_program.handle); SetShaderSamplerBindings(cached_program.handle); @@ -510,106 +514,108 @@ void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading, } std::vector load_raws_index; // Loads both decompiled and precompiled shaders from the cache. If either one is missing for - const auto LoadPrecompiledWorker = - [&](std::size_t begin, std::size_t end, const std::vector& raw_cache, - const ShaderDecompiledMap& decompiled_map, const ShaderDumpsMap& dump_map) { - for (std::size_t i = begin; i < end; ++i) { - if (stop_loading || compilation_failed) { - return; - } - const auto& raw{raw_cache[i]}; - const u64 unique_identifier{raw.GetUniqueIdentifier()}; - - const u64 calculated_hash = - GetUniqueIdentifier(raw.GetRawShaderConfig(), raw.GetProgramCode()); - if (unique_identifier != calculated_hash) { - LOG_ERROR(Render_OpenGL, - "Invalid hash in entry={:016x} (obtained hash={:016x}) - removing " - "shader cache", - raw.GetUniqueIdentifier(), calculated_hash); - disk_cache.InvalidateAll(); - return; - } - - const auto dump{dump_map.find(unique_identifier)}; - const auto decomp{decompiled_map.find(unique_identifier)}; - - OGLProgram shader; - - if (dump != dump_map.end() && decomp != decompiled_map.end()) { - // Only load the vertex shader if its sanitize_mul setting matches - if (raw.GetProgramType() == ProgramType::VS && - decomp->second.sanitize_mul != VideoCore::g_hw_shader_accurate_mul) { - continue; - } - - // If the shader is dumped, attempt to load it - shader = GeneratePrecompiledProgram(dump->second, supported_formats, impl->separable); - if (shader.handle == 0) { - // If any shader failed, stop trying to compile, delete the cache, and start - // loading from raws - compilation_failed = true; - return; - } - // we have both the binary shader and the decompiled, so inject it into the - // cache - if (raw.GetProgramType() == ProgramType::VS) { - auto [conf, setup] = BuildVSConfigFromRaw(raw); - std::scoped_lock lock(mutex); - impl->programmable_vertex_shaders.Inject(conf, decomp->second.result.code, - std::move(shader)); - } else if (raw.GetProgramType() == ProgramType::FS) { - PicaFSConfig conf = PicaFSConfig::BuildFromRegs(raw.GetRawShaderConfig()); - std::scoped_lock lock(mutex); - impl->fragment_shaders.Inject(conf, std::move(shader)); - } else { - // Unsupported shader type got stored somehow so nuke the cache - - LOG_CRITICAL(Frontend, "failed to load raw ProgramType {}", - raw.GetProgramType()); - compilation_failed = true; - return; - } - } else { - // Since precompiled didn't have the dump, we'll load them in the next phase - std::scoped_lock lock(mutex); - load_raws_index.push_back(i); - } - if (callback) { - callback(VideoCore::LoadCallbackStage::Decompile, i, raw_cache.size()); - } + const auto LoadPrecompiledWorker = [&](std::size_t begin, std::size_t end, + const std::vector& raw_cache, + const ShaderDecompiledMap& decompiled_map, + const ShaderDumpsMap& dump_map) { + for (std::size_t i = begin; i < end; ++i) { + if (stop_loading || compilation_failed) { + return; } - }; + const auto& raw{raw_cache[i]}; + const u64 unique_identifier{raw.GetUniqueIdentifier()}; - const auto LoadPrecompiledProgram = - [&](const ShaderDecompiledMap& decompiled_map, const ShaderDumpsMap& dump_map) { - std::size_t i{0}; - for (const auto &dump : dump_map) { - if (stop_loading) { - break; - } - const u64 unique_identifier{dump.first}; - const auto decomp{decompiled_map.find(unique_identifier)}; + const u64 calculated_hash = + GetUniqueIdentifier(raw.GetRawShaderConfig(), raw.GetProgramCode()); + if (unique_identifier != calculated_hash) { + LOG_ERROR(Render_OpenGL, + "Invalid hash in entry={:016x} (obtained hash={:016x}) - removing " + "shader cache", + raw.GetUniqueIdentifier(), calculated_hash); + disk_cache.InvalidateAll(); + return; + } - // Only load the program if its sanitize_mul setting matches - if (decomp->second.sanitize_mul != VideoCore::g_hw_shader_accurate_mul) { + const auto dump{dump_map.find(unique_identifier)}; + const auto decomp{decompiled_map.find(unique_identifier)}; + + OGLProgram shader; + + if (dump != dump_map.end() && decomp != decompiled_map.end()) { + // Only load the vertex shader if its sanitize_mul setting matches + if (raw.GetProgramType() == ProgramType::VS && + decomp->second.sanitize_mul != VideoCore::g_hw_shader_accurate_mul) { continue; } - // If the shader program is dumped, attempt to load it - OGLProgram shader = - GeneratePrecompiledProgram(dump.second, supported_formats, impl->separable); - if (shader.handle != 0) { - impl->disk_program_cache.insert({unique_identifier, std::move(shader)}); - } else { - LOG_ERROR(Frontend, "Failed to link Precompiled program!"); + // If the shader is dumped, attempt to load it + shader = + GeneratePrecompiledProgram(dump->second, supported_formats, impl->separable); + if (shader.handle == 0) { + // If any shader failed, stop trying to compile, delete the cache, and start + // loading from raws compilation_failed = true; - break; + return; } - if (callback) { - callback(VideoCore::LoadCallbackStage::Decompile, ++i, dump_map.size()); + // we have both the binary shader and the decompiled, so inject it into the + // cache + if (raw.GetProgramType() == ProgramType::VS) { + auto [conf, setup] = BuildVSConfigFromRaw(raw); + std::scoped_lock lock(mutex); + impl->programmable_vertex_shaders.Inject(conf, decomp->second.result.code, + std::move(shader)); + } else if (raw.GetProgramType() == ProgramType::FS) { + PicaFSConfig conf = PicaFSConfig::BuildFromRegs(raw.GetRawShaderConfig()); + std::scoped_lock lock(mutex); + impl->fragment_shaders.Inject(conf, std::move(shader)); + } else { + // Unsupported shader type got stored somehow so nuke the cache + + LOG_CRITICAL(Frontend, "failed to load raw ProgramType {}", + raw.GetProgramType()); + compilation_failed = true; + return; } + } else { + // Since precompiled didn't have the dump, we'll load them in the next phase + std::scoped_lock lock(mutex); + load_raws_index.push_back(i); } + if (callback) { + callback(VideoCore::LoadCallbackStage::Decompile, i, raw_cache.size()); + } + } + }; + + const auto LoadPrecompiledProgram = [&](const ShaderDecompiledMap& decompiled_map, + const ShaderDumpsMap& dump_map) { + std::size_t i{0}; + for (const auto& dump : dump_map) { + if (stop_loading) { + break; + } + const u64 unique_identifier{dump.first}; + const auto decomp{decompiled_map.find(unique_identifier)}; + + // Only load the program if its sanitize_mul setting matches + if (decomp->second.sanitize_mul != VideoCore::g_hw_shader_accurate_mul) { + continue; + } + + // If the shader program is dumped, attempt to load it + OGLProgram shader = + GeneratePrecompiledProgram(dump.second, supported_formats, impl->separable); + if (shader.handle != 0) { + impl->disk_program_cache.insert({unique_identifier, std::move(shader)}); + } else { + LOG_ERROR(Frontend, "Failed to link Precompiled program!"); + compilation_failed = true; + break; + } + if (callback) { + callback(VideoCore::LoadCallbackStage::Decompile, ++i, dump_map.size()); + } + } }; if (impl->separable) { @@ -633,7 +639,8 @@ void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading, compilation_failed = false; std::size_t built_shaders = 0; // It doesn't have be atomic since it's used behind a mutex - const auto LoadTransferable = [&](Frontend::GraphicsContext* context, std::size_t begin, std::size_t end) { + const auto LoadTransferable = [&](Frontend::GraphicsContext* context, std::size_t begin, + std::size_t end) { Frontend::ScopeAcquireContext scope(*context); for (std::size_t i = begin; i < end; ++i) { if (stop_loading || compilation_failed) { @@ -655,7 +662,7 @@ void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading, handle = stage.GetHandle(); sanitize_mul = conf.state.sanitize_mul; std::scoped_lock lock(mutex); - impl->programmable_vertex_shaders.Inject(conf, result->code,std::move(stage)); + impl->programmable_vertex_shaders.Inject(conf, result->code, std::move(stage)); } else if (raw.GetProgramType() == ProgramType::FS) { PicaFSConfig conf = PicaFSConfig::BuildFromRegs(raw.GetRawShaderConfig()); result = GenerateFragmentShader(conf, impl->separable);