diff options
40 files changed, 1623 insertions, 260 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 871e0ca1a..97d888762 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
| @@ -419,19 +419,6 @@ function(create_target_directory_groups target_name) | |||
| 419 | endforeach() | 419 | endforeach() |
| 420 | endfunction() | 420 | endfunction() |
| 421 | 421 | ||
| 422 | # Gets a UTC timstamp and sets the provided variable to it | ||
| 423 | function(get_timestamp _var) | ||
| 424 | string(TIMESTAMP timestamp UTC) | ||
| 425 | set(${_var} "${timestamp}" PARENT_SCOPE) | ||
| 426 | endfunction() | ||
| 427 | |||
| 428 | # generate git/build information | ||
| 429 | include(GetGitRevisionDescription) | ||
| 430 | get_git_head_revision(GIT_REF_SPEC GIT_REV) | ||
| 431 | git_describe(GIT_DESC --always --long --dirty) | ||
| 432 | git_branch_name(GIT_BRANCH) | ||
| 433 | get_timestamp(BUILD_DATE) | ||
| 434 | |||
| 435 | enable_testing() | 422 | enable_testing() |
| 436 | add_subdirectory(externals) | 423 | add_subdirectory(externals) |
| 437 | add_subdirectory(src) | 424 | add_subdirectory(src) |
diff --git a/CMakeModules/GenerateSCMRev.cmake b/CMakeModules/GenerateSCMRev.cmake new file mode 100644 index 000000000..78728e08b --- /dev/null +++ b/CMakeModules/GenerateSCMRev.cmake | |||
| @@ -0,0 +1,94 @@ | |||
| 1 | # Gets a UTC timstamp and sets the provided variable to it | ||
| 2 | function(get_timestamp _var) | ||
| 3 | string(TIMESTAMP timestamp UTC) | ||
| 4 | set(${_var} "${timestamp}" PARENT_SCOPE) | ||
| 5 | endfunction() | ||
| 6 | |||
| 7 | list(APPEND CMAKE_MODULE_PATH "${SRC_DIR}/externals/cmake-modules") | ||
| 8 | # generate git/build information | ||
| 9 | include(GetGitRevisionDescription) | ||
| 10 | get_git_head_revision(GIT_REF_SPEC GIT_REV) | ||
| 11 | git_describe(GIT_DESC --always --long --dirty) | ||
| 12 | git_branch_name(GIT_BRANCH) | ||
| 13 | get_timestamp(BUILD_DATE) | ||
| 14 | |||
| 15 | # Generate cpp with Git revision from template | ||
| 16 | # Also if this is a CI build, add the build name (ie: Nightly, Canary) to the scm_rev file as well | ||
| 17 | set(REPO_NAME "") | ||
| 18 | set(BUILD_VERSION "0") | ||
| 19 | if (BUILD_REPOSITORY) | ||
| 20 | # regex capture the string nightly or canary into CMAKE_MATCH_1 | ||
| 21 | string(REGEX MATCH "yuzu-emu/yuzu-?(.*)" OUTVAR ${BUILD_REPOSITORY}) | ||
| 22 | if (${CMAKE_MATCH_COUNT} GREATER 0) | ||
| 23 | # capitalize the first letter of each word in the repo name. | ||
| 24 | string(REPLACE "-" ";" REPO_NAME_LIST ${CMAKE_MATCH_1}) | ||
| 25 | foreach(WORD ${REPO_NAME_LIST}) | ||
| 26 | string(SUBSTRING ${WORD} 0 1 FIRST_LETTER) | ||
| 27 | string(SUBSTRING ${WORD} 1 -1 REMAINDER) | ||
| 28 | string(TOUPPER ${FIRST_LETTER} FIRST_LETTER) | ||
| 29 | set(REPO_NAME "${REPO_NAME}${FIRST_LETTER}${REMAINDER}") | ||
| 30 | endforeach() | ||
| 31 | if (BUILD_TAG) | ||
| 32 | string(REGEX MATCH "${CMAKE_MATCH_1}-([0-9]+)" OUTVAR ${BUILD_TAG}) | ||
| 33 | if (${CMAKE_MATCH_COUNT} GREATER 0) | ||
| 34 | set(BUILD_VERSION ${CMAKE_MATCH_1}) | ||
| 35 | endif() | ||
| 36 | if (BUILD_VERSION) | ||
| 37 | # This leaves a trailing space on the last word, but we actually want that | ||
| 38 | # because of how it's styled in the title bar. | ||
| 39 | set(BUILD_FULLNAME "${REPO_NAME} ${BUILD_VERSION} ") | ||
| 40 | else() | ||
| 41 | set(BUILD_FULLNAME "") | ||
| 42 | endif() | ||
| 43 | endif() | ||
| 44 | endif() | ||
| 45 | endif() | ||
| 46 | |||
| 47 | # The variable SRC_DIR must be passed into the script (since it uses the current build directory for all values of CMAKE_*_DIR) | ||
| 48 | set(VIDEO_CORE "${SRC_DIR}/src/video_core") | ||
| 49 | set(HASH_FILES | ||
| 50 | "${VIDEO_CORE}/renderer_opengl/gl_shader_cache.cpp" | ||
| 51 | "${VIDEO_CORE}/renderer_opengl/gl_shader_cache.h" | ||
| 52 | "${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.cpp" | ||
| 53 | "${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.h" | ||
| 54 | "${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.cpp" | ||
| 55 | "${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.h" | ||
| 56 | "${VIDEO_CORE}/renderer_opengl/gl_shader_gen.cpp" | ||
| 57 | "${VIDEO_CORE}/renderer_opengl/gl_shader_gen.h" | ||
| 58 | "${VIDEO_CORE}/shader/decode/arithmetic.cpp" | ||
| 59 | "${VIDEO_CORE}/shader/decode/arithmetic_half.cpp" | ||
| 60 | "${VIDEO_CORE}/shader/decode/arithmetic_half_immediate.cpp" | ||
| 61 | "${VIDEO_CORE}/shader/decode/arithmetic_immediate.cpp" | ||
| 62 | "${VIDEO_CORE}/shader/decode/arithmetic_integer.cpp" | ||
| 63 | "${VIDEO_CORE}/shader/decode/arithmetic_integer_immediate.cpp" | ||
| 64 | "${VIDEO_CORE}/shader/decode/bfe.cpp" | ||
| 65 | "${VIDEO_CORE}/shader/decode/bfi.cpp" | ||
| 66 | "${VIDEO_CORE}/shader/decode/conversion.cpp" | ||
| 67 | "${VIDEO_CORE}/shader/decode/ffma.cpp" | ||
| 68 | "${VIDEO_CORE}/shader/decode/float_set.cpp" | ||
| 69 | "${VIDEO_CORE}/shader/decode/float_set_predicate.cpp" | ||
| 70 | "${VIDEO_CORE}/shader/decode/half_set.cpp" | ||
| 71 | "${VIDEO_CORE}/shader/decode/half_set_predicate.cpp" | ||
| 72 | "${VIDEO_CORE}/shader/decode/hfma2.cpp" | ||
| 73 | "${VIDEO_CORE}/shader/decode/integer_set.cpp" | ||
| 74 | "${VIDEO_CORE}/shader/decode/integer_set_predicate.cpp" | ||
| 75 | "${VIDEO_CORE}/shader/decode/memory.cpp" | ||
| 76 | "${VIDEO_CORE}/shader/decode/other.cpp" | ||
| 77 | "${VIDEO_CORE}/shader/decode/predicate_set_predicate.cpp" | ||
| 78 | "${VIDEO_CORE}/shader/decode/predicate_set_register.cpp" | ||
| 79 | "${VIDEO_CORE}/shader/decode/register_set_predicate.cpp" | ||
| 80 | "${VIDEO_CORE}/shader/decode/shift.cpp" | ||
| 81 | "${VIDEO_CORE}/shader/decode/video.cpp" | ||
| 82 | "${VIDEO_CORE}/shader/decode/xmad.cpp" | ||
| 83 | "${VIDEO_CORE}/shader/decode.cpp" | ||
| 84 | "${VIDEO_CORE}/shader/shader_ir.cpp" | ||
| 85 | "${VIDEO_CORE}/shader/shader_ir.h" | ||
| 86 | "${VIDEO_CORE}/shader/track.cpp" | ||
| 87 | ) | ||
| 88 | set(COMBINED "") | ||
| 89 | foreach (F IN LISTS HASH_FILES) | ||
| 90 | file(READ ${F} TMP) | ||
| 91 | set(COMBINED "${COMBINED}${TMP}") | ||
| 92 | endforeach() | ||
| 93 | string(MD5 SHADER_CACHE_VERSION "${COMBINED}") | ||
| 94 | configure_file("${SRC_DIR}/src/common/scm_rev.cpp.in" "scm_rev.cpp" @ONLY) | ||
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 845626fc5..bdd885273 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -1,42 +1,69 @@ | |||
| 1 | # Generate cpp with Git revision from template | 1 | # Add a custom command to generate a new shader_cache_version hash when any of the following files change |
| 2 | # Also if this is a CI build, add the build name (ie: Nightly, Canary) to the scm_rev file as well | 2 | # NOTE: This is an approximation of what files affect shader generation, its possible something else |
| 3 | set(REPO_NAME "") | 3 | # could affect the result, but much more unlikely than the following files. Keeping a list of files |
| 4 | set(BUILD_VERSION "0") | 4 | # like this allows for much better caching since it doesn't force the user to recompile binary shaders every update |
| 5 | if ($ENV{CI}) | 5 | set(VIDEO_CORE "${CMAKE_SOURCE_DIR}/src/video_core") |
| 6 | if ($ENV{TRAVIS}) | 6 | if (DEFINED ENV{CI}) |
| 7 | if (DEFINED ENV{TRAVIS}) | ||
| 7 | set(BUILD_REPOSITORY $ENV{TRAVIS_REPO_SLUG}) | 8 | set(BUILD_REPOSITORY $ENV{TRAVIS_REPO_SLUG}) |
| 8 | set(BUILD_TAG $ENV{TRAVIS_TAG}) | 9 | set(BUILD_TAG $ENV{TRAVIS_TAG}) |
| 9 | elseif($ENV{APPVEYOR}) | 10 | elseif(DEFINED ENV{APPVEYOR}) |
| 10 | set(BUILD_REPOSITORY $ENV{APPVEYOR_REPO_NAME}) | 11 | set(BUILD_REPOSITORY $ENV{APPVEYOR_REPO_NAME}) |
| 11 | set(BUILD_TAG $ENV{APPVEYOR_REPO_TAG_NAME}) | 12 | set(BUILD_TAG $ENV{APPVEYOR_REPO_TAG_NAME}) |
| 12 | endif() | 13 | endif() |
| 13 | # regex capture the string nightly or canary into CMAKE_MATCH_1 | ||
| 14 | string(REGEX MATCH "yuzu-emu/yuzu-?(.*)" OUTVAR ${BUILD_REPOSITORY}) | ||
| 15 | if (${CMAKE_MATCH_COUNT} GREATER 0) | ||
| 16 | # capitalize the first letter of each word in the repo name. | ||
| 17 | string(REPLACE "-" ";" REPO_NAME_LIST ${CMAKE_MATCH_1}) | ||
| 18 | foreach(WORD ${REPO_NAME_LIST}) | ||
| 19 | string(SUBSTRING ${WORD} 0 1 FIRST_LETTER) | ||
| 20 | string(SUBSTRING ${WORD} 1 -1 REMAINDER) | ||
| 21 | string(TOUPPER ${FIRST_LETTER} FIRST_LETTER) | ||
| 22 | set(REPO_NAME "${REPO_NAME}${FIRST_LETTER}${REMAINDER}") | ||
| 23 | endforeach() | ||
| 24 | if (BUILD_TAG) | ||
| 25 | string(REGEX MATCH "${CMAKE_MATCH_1}-([0-9]+)" OUTVAR ${BUILD_TAG}) | ||
| 26 | if (${CMAKE_MATCH_COUNT} GREATER 0) | ||
| 27 | set(BUILD_VERSION ${CMAKE_MATCH_1}) | ||
| 28 | endif() | ||
| 29 | if (BUILD_VERSION) | ||
| 30 | # This leaves a trailing space on the last word, but we actually want that | ||
| 31 | # because of how it's styled in the title bar. | ||
| 32 | set(BUILD_FULLNAME "${REPO_NAME} ${BUILD_VERSION} ") | ||
| 33 | else() | ||
| 34 | set(BUILD_FULLNAME "") | ||
| 35 | endif() | ||
| 36 | endif() | ||
| 37 | endif() | ||
| 38 | endif() | 14 | endif() |
| 39 | configure_file("${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" "${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp" @ONLY) | 15 | add_custom_command(OUTPUT scm_rev.cpp |
| 16 | COMMAND ${CMAKE_COMMAND} | ||
| 17 | -DSRC_DIR="${CMAKE_SOURCE_DIR}" | ||
| 18 | -DBUILD_REPOSITORY="${BUILD_REPOSITORY}" | ||
| 19 | -DBUILD_TAG="${BUILD_TAG}" | ||
| 20 | -P "${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake" | ||
| 21 | DEPENDS | ||
| 22 | # WARNING! It was too much work to try and make a common location for this list, | ||
| 23 | # so if you need to change it, please update CMakeModules/GenerateSCMRev.cmake as well | ||
| 24 | "${VIDEO_CORE}/renderer_opengl/gl_shader_cache.cpp" | ||
| 25 | "${VIDEO_CORE}/renderer_opengl/gl_shader_cache.h" | ||
| 26 | "${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.cpp" | ||
| 27 | "${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.h" | ||
| 28 | "${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.cpp" | ||
| 29 | "${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.h" | ||
| 30 | "${VIDEO_CORE}/renderer_opengl/gl_shader_gen.cpp" | ||
| 31 | "${VIDEO_CORE}/renderer_opengl/gl_shader_gen.h" | ||
| 32 | "${VIDEO_CORE}/shader/decode/arithmetic.cpp" | ||
| 33 | "${VIDEO_CORE}/shader/decode/arithmetic_half.cpp" | ||
| 34 | "${VIDEO_CORE}/shader/decode/arithmetic_half_immediate.cpp" | ||
| 35 | "${VIDEO_CORE}/shader/decode/arithmetic_immediate.cpp" | ||
| 36 | "${VIDEO_CORE}/shader/decode/arithmetic_integer.cpp" | ||
| 37 | "${VIDEO_CORE}/shader/decode/arithmetic_integer_immediate.cpp" | ||
| 38 | "${VIDEO_CORE}/shader/decode/bfe.cpp" | ||
| 39 | "${VIDEO_CORE}/shader/decode/bfi.cpp" | ||
| 40 | "${VIDEO_CORE}/shader/decode/conversion.cpp" | ||
| 41 | "${VIDEO_CORE}/shader/decode/ffma.cpp" | ||
| 42 | "${VIDEO_CORE}/shader/decode/float_set.cpp" | ||
| 43 | "${VIDEO_CORE}/shader/decode/float_set_predicate.cpp" | ||
| 44 | "${VIDEO_CORE}/shader/decode/half_set.cpp" | ||
| 45 | "${VIDEO_CORE}/shader/decode/half_set_predicate.cpp" | ||
| 46 | "${VIDEO_CORE}/shader/decode/hfma2.cpp" | ||
| 47 | "${VIDEO_CORE}/shader/decode/integer_set.cpp" | ||
| 48 | "${VIDEO_CORE}/shader/decode/integer_set_predicate.cpp" | ||
| 49 | "${VIDEO_CORE}/shader/decode/memory.cpp" | ||
| 50 | "${VIDEO_CORE}/shader/decode/other.cpp" | ||
| 51 | "${VIDEO_CORE}/shader/decode/predicate_set_predicate.cpp" | ||
| 52 | "${VIDEO_CORE}/shader/decode/predicate_set_register.cpp" | ||
| 53 | "${VIDEO_CORE}/shader/decode/register_set_predicate.cpp" | ||
| 54 | "${VIDEO_CORE}/shader/decode/shift.cpp" | ||
| 55 | "${VIDEO_CORE}/shader/decode/video.cpp" | ||
| 56 | "${VIDEO_CORE}/shader/decode/xmad.cpp" | ||
| 57 | "${VIDEO_CORE}/shader/decode.cpp" | ||
| 58 | "${VIDEO_CORE}/shader/shader_ir.cpp" | ||
| 59 | "${VIDEO_CORE}/shader/shader_ir.h" | ||
| 60 | "${VIDEO_CORE}/shader/track.cpp" | ||
| 61 | # and also check that the scm_rev files haven't changed | ||
| 62 | "${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" | ||
| 63 | "${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.h" | ||
| 64 | # technically we should regenerate if the git version changed, but its not worth the effort imo | ||
| 65 | "${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake" | ||
| 66 | ) | ||
| 40 | 67 | ||
| 41 | add_library(common STATIC | 68 | add_library(common STATIC |
| 42 | alignment.h | 69 | alignment.h |
diff --git a/src/common/common_paths.h b/src/common/common_paths.h index 4f88de768..076752d3b 100644 --- a/src/common/common_paths.h +++ b/src/common/common_paths.h | |||
| @@ -35,6 +35,7 @@ | |||
| 35 | #define KEYS_DIR "keys" | 35 | #define KEYS_DIR "keys" |
| 36 | #define LOAD_DIR "load" | 36 | #define LOAD_DIR "load" |
| 37 | #define DUMP_DIR "dump" | 37 | #define DUMP_DIR "dump" |
| 38 | #define SHADER_DIR "shader" | ||
| 38 | #define LOG_DIR "log" | 39 | #define LOG_DIR "log" |
| 39 | 40 | ||
| 40 | // Filenames | 41 | // Filenames |
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index b52492da6..aecb66c32 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp | |||
| @@ -710,6 +710,7 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) { | |||
| 710 | paths.emplace(UserPath::NANDDir, user_path + NAND_DIR DIR_SEP); | 710 | paths.emplace(UserPath::NANDDir, user_path + NAND_DIR DIR_SEP); |
| 711 | paths.emplace(UserPath::LoadDir, user_path + LOAD_DIR DIR_SEP); | 711 | paths.emplace(UserPath::LoadDir, user_path + LOAD_DIR DIR_SEP); |
| 712 | paths.emplace(UserPath::DumpDir, user_path + DUMP_DIR DIR_SEP); | 712 | paths.emplace(UserPath::DumpDir, user_path + DUMP_DIR DIR_SEP); |
| 713 | paths.emplace(UserPath::ShaderDir, user_path + SHADER_DIR DIR_SEP); | ||
| 713 | paths.emplace(UserPath::SysDataDir, user_path + SYSDATA_DIR DIR_SEP); | 714 | paths.emplace(UserPath::SysDataDir, user_path + SYSDATA_DIR DIR_SEP); |
| 714 | paths.emplace(UserPath::KeysDir, user_path + KEYS_DIR DIR_SEP); | 715 | paths.emplace(UserPath::KeysDir, user_path + KEYS_DIR DIR_SEP); |
| 715 | // TODO: Put the logs in a better location for each OS | 716 | // TODO: Put the logs in a better location for each OS |
diff --git a/src/common/file_util.h b/src/common/file_util.h index 571503d2a..38cc7f059 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h | |||
| @@ -31,6 +31,7 @@ enum class UserPath { | |||
| 31 | SDMCDir, | 31 | SDMCDir, |
| 32 | LoadDir, | 32 | LoadDir, |
| 33 | DumpDir, | 33 | DumpDir, |
| 34 | ShaderDir, | ||
| 34 | SysDataDir, | 35 | SysDataDir, |
| 35 | UserDir, | 36 | UserDir, |
| 36 | }; | 37 | }; |
diff --git a/src/common/scm_rev.cpp.in b/src/common/scm_rev.cpp.in index 2b1727769..d69038f65 100644 --- a/src/common/scm_rev.cpp.in +++ b/src/common/scm_rev.cpp.in | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | #define BUILD_DATE "@BUILD_DATE@" | 11 | #define BUILD_DATE "@BUILD_DATE@" |
| 12 | #define BUILD_FULLNAME "@BUILD_FULLNAME@" | 12 | #define BUILD_FULLNAME "@BUILD_FULLNAME@" |
| 13 | #define BUILD_VERSION "@BUILD_VERSION@" | 13 | #define BUILD_VERSION "@BUILD_VERSION@" |
| 14 | #define SHADER_CACHE_VERSION "@SHADER_CACHE_VERSION@" | ||
| 14 | 15 | ||
| 15 | namespace Common { | 16 | namespace Common { |
| 16 | 17 | ||
| @@ -21,6 +22,7 @@ const char g_build_name[] = BUILD_NAME; | |||
| 21 | const char g_build_date[] = BUILD_DATE; | 22 | const char g_build_date[] = BUILD_DATE; |
| 22 | const char g_build_fullname[] = BUILD_FULLNAME; | 23 | const char g_build_fullname[] = BUILD_FULLNAME; |
| 23 | const char g_build_version[] = BUILD_VERSION; | 24 | const char g_build_version[] = BUILD_VERSION; |
| 25 | const char g_shader_cache_version[] = SHADER_CACHE_VERSION; | ||
| 24 | 26 | ||
| 25 | } // namespace | 27 | } // namespace |
| 26 | 28 | ||
diff --git a/src/common/scm_rev.h b/src/common/scm_rev.h index af9a9daed..666bf0367 100644 --- a/src/common/scm_rev.h +++ b/src/common/scm_rev.h | |||
| @@ -13,5 +13,6 @@ extern const char g_build_name[]; | |||
| 13 | extern const char g_build_date[]; | 13 | extern const char g_build_date[]; |
| 14 | extern const char g_build_fullname[]; | 14 | extern const char g_build_fullname[]; |
| 15 | extern const char g_build_version[]; | 15 | extern const char g_build_version[]; |
| 16 | extern const char g_shader_cache_version[]; | ||
| 16 | 17 | ||
| 17 | } // namespace Common | 18 | } // namespace Common |
diff --git a/src/core/core.cpp b/src/core/core.cpp index 572814e4b..1dd576c26 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -123,7 +123,7 @@ struct System::Impl { | |||
| 123 | Service::Init(service_manager, *virtual_filesystem); | 123 | Service::Init(service_manager, *virtual_filesystem); |
| 124 | GDBStub::Init(); | 124 | GDBStub::Init(); |
| 125 | 125 | ||
| 126 | renderer = VideoCore::CreateRenderer(emu_window); | 126 | renderer = VideoCore::CreateRenderer(emu_window, system); |
| 127 | if (!renderer->Init()) { | 127 | if (!renderer->Init()) { |
| 128 | return ResultStatus::ErrorVideoCore; | 128 | return ResultStatus::ErrorVideoCore; |
| 129 | } | 129 | } |
| @@ -175,6 +175,7 @@ struct System::Impl { | |||
| 175 | return static_cast<ResultStatus>(static_cast<u32>(ResultStatus::ErrorLoader) + | 175 | return static_cast<ResultStatus>(static_cast<u32>(ResultStatus::ErrorLoader) + |
| 176 | static_cast<u32>(load_result)); | 176 | static_cast<u32>(load_result)); |
| 177 | } | 177 | } |
| 178 | |||
| 178 | status = ResultStatus::Success; | 179 | status = ResultStatus::Success; |
| 179 | return status; | 180 | return status; |
| 180 | } | 181 | } |
diff --git a/src/core/settings.h b/src/core/settings.h index c97387fc7..7e76e0466 100644 --- a/src/core/settings.h +++ b/src/core/settings.h | |||
| @@ -391,6 +391,7 @@ struct Values { | |||
| 391 | float resolution_factor; | 391 | float resolution_factor; |
| 392 | bool use_frame_limit; | 392 | bool use_frame_limit; |
| 393 | u16 frame_limit; | 393 | u16 frame_limit; |
| 394 | bool use_disk_shader_cache; | ||
| 394 | bool use_accurate_gpu_emulation; | 395 | bool use_accurate_gpu_emulation; |
| 395 | 396 | ||
| 396 | float bg_red; | 397 | float bg_red; |
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index 09ed74d78..58dfcc4df 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp | |||
| @@ -158,6 +158,8 @@ TelemetrySession::TelemetrySession() { | |||
| 158 | AddField(Telemetry::FieldType::UserConfig, "Renderer_UseFrameLimit", | 158 | AddField(Telemetry::FieldType::UserConfig, "Renderer_UseFrameLimit", |
| 159 | Settings::values.use_frame_limit); | 159 | Settings::values.use_frame_limit); |
| 160 | AddField(Telemetry::FieldType::UserConfig, "Renderer_FrameLimit", Settings::values.frame_limit); | 160 | AddField(Telemetry::FieldType::UserConfig, "Renderer_FrameLimit", Settings::values.frame_limit); |
| 161 | AddField(Telemetry::FieldType::UserConfig, "Renderer_UseDiskShaderCache", | ||
| 162 | Settings::values.use_disk_shader_cache); | ||
| 161 | AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAccurateGpuEmulation", | 163 | AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAccurateGpuEmulation", |
| 162 | Settings::values.use_accurate_gpu_emulation); | 164 | Settings::values.use_accurate_gpu_emulation); |
| 163 | AddField(Telemetry::FieldType::UserConfig, "System_UseDockedMode", | 165 | AddField(Telemetry::FieldType::UserConfig, "System_UseDockedMode", |
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 6113e17ff..33e507e69 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt | |||
| @@ -44,6 +44,8 @@ add_library(video_core STATIC | |||
| 44 | renderer_opengl/gl_shader_cache.h | 44 | renderer_opengl/gl_shader_cache.h |
| 45 | renderer_opengl/gl_shader_decompiler.cpp | 45 | renderer_opengl/gl_shader_decompiler.cpp |
| 46 | renderer_opengl/gl_shader_decompiler.h | 46 | renderer_opengl/gl_shader_decompiler.h |
| 47 | renderer_opengl/gl_shader_disk_cache.cpp | ||
| 48 | renderer_opengl/gl_shader_disk_cache.h | ||
| 47 | renderer_opengl/gl_shader_gen.cpp | 49 | renderer_opengl/gl_shader_gen.cpp |
| 48 | renderer_opengl/gl_shader_gen.h | 50 | renderer_opengl/gl_shader_gen.h |
| 49 | renderer_opengl/gl_shader_manager.cpp | 51 | renderer_opengl/gl_shader_manager.cpp |
| @@ -102,4 +104,4 @@ add_library(video_core STATIC | |||
| 102 | create_target_directory_groups(video_core) | 104 | create_target_directory_groups(video_core) |
| 103 | 105 | ||
| 104 | target_link_libraries(video_core PUBLIC common core) | 106 | target_link_libraries(video_core PUBLIC common core) |
| 105 | target_link_libraries(video_core PRIVATE glad) | 107 | target_link_libraries(video_core PRIVATE glad lz4_static) |
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index 4c08bb148..77da135a0 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <atomic> | ||
| 7 | #include <functional> | 8 | #include <functional> |
| 8 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 9 | #include "video_core/engines/fermi_2d.h" | 10 | #include "video_core/engines/fermi_2d.h" |
| @@ -61,5 +62,9 @@ public: | |||
| 61 | 62 | ||
| 62 | /// Increase/decrease the number of object in pages touching the specified region | 63 | /// Increase/decrease the number of object in pages touching the specified region |
| 63 | virtual void UpdatePagesCachedCount(Tegra::GPUVAddr addr, u64 size, int delta) {} | 64 | virtual void UpdatePagesCachedCount(Tegra::GPUVAddr addr, u64 size, int delta) {} |
| 65 | |||
| 66 | /// Initialize disk cached resources for the game being emulated | ||
| 67 | virtual void LoadDiskResources(const std::atomic_bool& stop_loading = false, | ||
| 68 | const DiskResourceLoadCallback& callback = {}) {} | ||
| 64 | }; | 69 | }; |
| 65 | } // namespace VideoCore | 70 | } // namespace VideoCore |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index c806b7da7..974ca6a20 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -22,6 +22,7 @@ | |||
| 22 | #include "core/settings.h" | 22 | #include "core/settings.h" |
| 23 | #include "video_core/engines/maxwell_3d.h" | 23 | #include "video_core/engines/maxwell_3d.h" |
| 24 | #include "video_core/renderer_opengl/gl_rasterizer.h" | 24 | #include "video_core/renderer_opengl/gl_rasterizer.h" |
| 25 | #include "video_core/renderer_opengl/gl_shader_cache.h" | ||
| 25 | #include "video_core/renderer_opengl/gl_shader_gen.h" | 26 | #include "video_core/renderer_opengl/gl_shader_gen.h" |
| 26 | #include "video_core/renderer_opengl/maxwell_to_gl.h" | 27 | #include "video_core/renderer_opengl/maxwell_to_gl.h" |
| 27 | #include "video_core/renderer_opengl/renderer_opengl.h" | 28 | #include "video_core/renderer_opengl/renderer_opengl.h" |
| @@ -99,8 +100,9 @@ struct FramebufferCacheKey { | |||
| 99 | } | 100 | } |
| 100 | }; | 101 | }; |
| 101 | 102 | ||
| 102 | RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo& info) | 103 | RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, Core::System& system, |
| 103 | : res_cache{*this}, shader_cache{*this}, emu_window{window}, screen_info{info}, | 104 | ScreenInfo& info) |
| 105 | : res_cache{*this}, shader_cache{*this, system}, emu_window{window}, screen_info{info}, | ||
| 104 | buffer_cache(*this, STREAM_BUFFER_SIZE), global_cache{*this} { | 106 | buffer_cache(*this, STREAM_BUFFER_SIZE), global_cache{*this} { |
| 105 | // Create sampler objects | 107 | // Create sampler objects |
| 106 | for (std::size_t i = 0; i < texture_samplers.size(); ++i) { | 108 | for (std::size_t i = 0; i < texture_samplers.size(); ++i) { |
| @@ -447,7 +449,7 @@ static constexpr auto RangeFromInterval(Map& map, const Interval& interval) { | |||
| 447 | return boost::make_iterator_range(map.equal_range(interval)); | 449 | return boost::make_iterator_range(map.equal_range(interval)); |
| 448 | } | 450 | } |
| 449 | 451 | ||
| 450 | void RasterizerOpenGL::UpdatePagesCachedCount(VAddr addr, u64 size, int delta) { | 452 | void RasterizerOpenGL::UpdatePagesCachedCount(Tegra::GPUVAddr addr, u64 size, int delta) { |
| 451 | const u64 page_start{addr >> Memory::PAGE_BITS}; | 453 | const u64 page_start{addr >> Memory::PAGE_BITS}; |
| 452 | const u64 page_end{(addr + size + Memory::PAGE_SIZE - 1) >> Memory::PAGE_BITS}; | 454 | const u64 page_end{(addr + size + Memory::PAGE_SIZE - 1) >> Memory::PAGE_BITS}; |
| 453 | 455 | ||
| @@ -477,6 +479,11 @@ void RasterizerOpenGL::UpdatePagesCachedCount(VAddr addr, u64 size, int delta) { | |||
| 477 | cached_pages.add({pages_interval, delta}); | 479 | cached_pages.add({pages_interval, delta}); |
| 478 | } | 480 | } |
| 479 | 481 | ||
| 482 | void RasterizerOpenGL::LoadDiskResources(const std::atomic_bool& stop_loading, | ||
| 483 | const VideoCore::DiskResourceLoadCallback& callback) { | ||
| 484 | shader_cache.LoadDiskCache(stop_loading, callback); | ||
| 485 | } | ||
| 486 | |||
| 480 | std::pair<bool, bool> RasterizerOpenGL::ConfigureFramebuffers( | 487 | std::pair<bool, bool> RasterizerOpenGL::ConfigureFramebuffers( |
| 481 | OpenGLState& current_state, bool using_color_fb, bool using_depth_fb, bool preserve_contents, | 488 | OpenGLState& current_state, bool using_color_fb, bool using_depth_fb, bool preserve_contents, |
| 482 | std::optional<std::size_t> single_color_target) { | 489 | std::optional<std::size_t> single_color_target) { |
| @@ -1004,22 +1011,20 @@ void RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, const Shader& s | |||
| 1004 | 1011 | ||
| 1005 | for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) { | 1012 | for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) { |
| 1006 | const auto& entry = entries[bindpoint]; | 1013 | const auto& entry = entries[bindpoint]; |
| 1014 | const auto texture = maxwell3d.GetStageTexture(stage, entry.GetOffset()); | ||
| 1007 | const u32 current_bindpoint = base_bindings.sampler + bindpoint; | 1015 | const u32 current_bindpoint = base_bindings.sampler + bindpoint; |
| 1008 | auto& unit = state.texture_units[current_bindpoint]; | ||
| 1009 | |||
| 1010 | const auto texture = maxwell3d.GetStageTexture(entry.GetStage(), entry.GetOffset()); | ||
| 1011 | 1016 | ||
| 1012 | texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc); | 1017 | texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc); |
| 1013 | 1018 | ||
| 1014 | Surface surface = res_cache.GetTextureSurface(texture, entry); | 1019 | Surface surface = res_cache.GetTextureSurface(texture, entry); |
| 1015 | if (surface != nullptr) { | 1020 | if (surface != nullptr) { |
| 1016 | unit.texture = | 1021 | state.texture_units[current_bindpoint].texture = |
| 1017 | entry.IsArray() ? surface->TextureLayer().handle : surface->Texture().handle; | 1022 | entry.IsArray() ? surface->TextureLayer().handle : surface->Texture().handle; |
| 1018 | surface->UpdateSwizzle(texture.tic.x_source, texture.tic.y_source, texture.tic.z_source, | 1023 | surface->UpdateSwizzle(texture.tic.x_source, texture.tic.y_source, texture.tic.z_source, |
| 1019 | texture.tic.w_source); | 1024 | texture.tic.w_source); |
| 1020 | } else { | 1025 | } else { |
| 1021 | // Can occur when texture addr is null or its memory is unmapped/invalid | 1026 | // Can occur when texture addr is null or its memory is unmapped/invalid |
| 1022 | unit.texture = 0; | 1027 | state.texture_units[current_bindpoint].texture = 0; |
| 1023 | } | 1028 | } |
| 1024 | } | 1029 | } |
| 1025 | } | 1030 | } |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 7f2bf0f8b..f3b607f4d 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <atomic> | ||
| 8 | #include <cstddef> | 9 | #include <cstddef> |
| 9 | #include <map> | 10 | #include <map> |
| 10 | #include <memory> | 11 | #include <memory> |
| @@ -33,6 +34,10 @@ | |||
| 33 | #include "video_core/renderer_opengl/gl_state.h" | 34 | #include "video_core/renderer_opengl/gl_state.h" |
| 34 | #include "video_core/renderer_opengl/gl_stream_buffer.h" | 35 | #include "video_core/renderer_opengl/gl_stream_buffer.h" |
| 35 | 36 | ||
| 37 | namespace Core { | ||
| 38 | class System; | ||
| 39 | } | ||
| 40 | |||
| 36 | namespace Core::Frontend { | 41 | namespace Core::Frontend { |
| 37 | class EmuWindow; | 42 | class EmuWindow; |
| 38 | } | 43 | } |
| @@ -45,7 +50,8 @@ struct FramebufferCacheKey; | |||
| 45 | 50 | ||
| 46 | class RasterizerOpenGL : public VideoCore::RasterizerInterface { | 51 | class RasterizerOpenGL : public VideoCore::RasterizerInterface { |
| 47 | public: | 52 | public: |
| 48 | explicit RasterizerOpenGL(Core::Frontend::EmuWindow& renderer, ScreenInfo& info); | 53 | explicit RasterizerOpenGL(Core::Frontend::EmuWindow& window, Core::System& system, |
| 54 | ScreenInfo& info); | ||
| 49 | ~RasterizerOpenGL() override; | 55 | ~RasterizerOpenGL() override; |
| 50 | 56 | ||
| 51 | void DrawArrays() override; | 57 | void DrawArrays() override; |
| @@ -60,6 +66,8 @@ public: | |||
| 60 | u32 pixel_stride) override; | 66 | u32 pixel_stride) override; |
| 61 | bool AccelerateDrawBatch(bool is_indexed) override; | 67 | bool AccelerateDrawBatch(bool is_indexed) override; |
| 62 | void UpdatePagesCachedCount(Tegra::GPUVAddr addr, u64 size, int delta) override; | 68 | void UpdatePagesCachedCount(Tegra::GPUVAddr addr, u64 size, int delta) override; |
| 69 | void LoadDiskResources(const std::atomic_bool& stop_loading, | ||
| 70 | const VideoCore::DiskResourceLoadCallback& callback) override; | ||
| 63 | 71 | ||
| 64 | /// Maximum supported size that a constbuffer can have in bytes. | 72 | /// Maximum supported size that a constbuffer can have in bytes. |
| 65 | static constexpr std::size_t MaxConstbufferSize = 0x10000; | 73 | static constexpr std::size_t MaxConstbufferSize = 0x10000; |
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.cpp b/src/video_core/renderer_opengl/gl_resource_manager.cpp index 4170cbd3c..bfe666a73 100644 --- a/src/video_core/renderer_opengl/gl_resource_manager.cpp +++ b/src/video_core/renderer_opengl/gl_resource_manager.cpp | |||
| @@ -71,7 +71,8 @@ void OGLShader::Release() { | |||
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | void OGLProgram::CreateFromSource(const char* vert_shader, const char* geo_shader, | 73 | void OGLProgram::CreateFromSource(const char* vert_shader, const char* geo_shader, |
| 74 | const char* frag_shader, bool separable_program) { | 74 | const char* frag_shader, bool separable_program, |
| 75 | bool hint_retrievable) { | ||
| 75 | OGLShader vert, geo, frag; | 76 | OGLShader vert, geo, frag; |
| 76 | if (vert_shader) | 77 | if (vert_shader) |
| 77 | vert.Create(vert_shader, GL_VERTEX_SHADER); | 78 | vert.Create(vert_shader, GL_VERTEX_SHADER); |
| @@ -81,7 +82,7 @@ void OGLProgram::CreateFromSource(const char* vert_shader, const char* geo_shade | |||
| 81 | frag.Create(frag_shader, GL_FRAGMENT_SHADER); | 82 | frag.Create(frag_shader, GL_FRAGMENT_SHADER); |
| 82 | 83 | ||
| 83 | MICROPROFILE_SCOPE(OpenGL_ResourceCreation); | 84 | MICROPROFILE_SCOPE(OpenGL_ResourceCreation); |
| 84 | Create(separable_program, vert.handle, geo.handle, frag.handle); | 85 | Create(separable_program, hint_retrievable, vert.handle, geo.handle, frag.handle); |
| 85 | } | 86 | } |
| 86 | 87 | ||
| 87 | void OGLProgram::Release() { | 88 | void OGLProgram::Release() { |
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h index df76cbc4b..fbb93ee49 100644 --- a/src/video_core/renderer_opengl/gl_resource_manager.h +++ b/src/video_core/renderer_opengl/gl_resource_manager.h | |||
| @@ -101,15 +101,15 @@ public: | |||
| 101 | } | 101 | } |
| 102 | 102 | ||
| 103 | template <typename... T> | 103 | template <typename... T> |
| 104 | void Create(bool separable_program, T... shaders) { | 104 | void Create(bool separable_program, bool hint_retrievable, T... shaders) { |
| 105 | if (handle != 0) | 105 | if (handle != 0) |
| 106 | return; | 106 | return; |
| 107 | handle = GLShader::LoadProgram(separable_program, shaders...); | 107 | handle = GLShader::LoadProgram(separable_program, hint_retrievable, shaders...); |
| 108 | } | 108 | } |
| 109 | 109 | ||
| 110 | /// Creates a new internal OpenGL resource and stores the handle | 110 | /// Creates a new internal OpenGL resource and stores the handle |
| 111 | void CreateFromSource(const char* vert_shader, const char* geo_shader, const char* frag_shader, | 111 | void CreateFromSource(const char* vert_shader, const char* geo_shader, const char* frag_shader, |
| 112 | bool separable_program = false); | 112 | bool separable_program = false, bool hint_retrievable = false); |
| 113 | 113 | ||
| 114 | /// Deletes the internal OpenGL resource | 114 | /// Deletes the internal OpenGL resource |
| 115 | void Release(); | 115 | void Release(); |
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 6174f7074..4883e4f62 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | #include "video_core/renderer_opengl/gl_rasterizer.h" | 11 | #include "video_core/renderer_opengl/gl_rasterizer.h" |
| 12 | #include "video_core/renderer_opengl/gl_shader_cache.h" | 12 | #include "video_core/renderer_opengl/gl_shader_cache.h" |
| 13 | #include "video_core/renderer_opengl/gl_shader_decompiler.h" | 13 | #include "video_core/renderer_opengl/gl_shader_decompiler.h" |
| 14 | #include "video_core/renderer_opengl/gl_shader_disk_cache.h" | ||
| 14 | #include "video_core/renderer_opengl/gl_shader_manager.h" | 15 | #include "video_core/renderer_opengl/gl_shader_manager.h" |
| 15 | #include "video_core/renderer_opengl/utils.h" | 16 | #include "video_core/renderer_opengl/utils.h" |
| 16 | #include "video_core/shader/shader_ir.h" | 17 | #include "video_core/shader/shader_ir.h" |
| @@ -19,8 +20,19 @@ namespace OpenGL { | |||
| 19 | 20 | ||
| 20 | using VideoCommon::Shader::ProgramCode; | 21 | using VideoCommon::Shader::ProgramCode; |
| 21 | 22 | ||
| 23 | // One UBO is always reserved for emulation values | ||
| 24 | constexpr u32 RESERVED_UBOS = 1; | ||
| 25 | |||
| 26 | struct UnspecializedShader { | ||
| 27 | std::string code; | ||
| 28 | GLShader::ShaderEntries entries; | ||
| 29 | Maxwell::ShaderProgram program_type; | ||
| 30 | }; | ||
| 31 | |||
| 32 | namespace { | ||
| 33 | |||
| 22 | /// Gets the address for the specified shader stage program | 34 | /// Gets the address for the specified shader stage program |
| 23 | static VAddr GetShaderAddress(Maxwell::ShaderProgram program) { | 35 | VAddr GetShaderAddress(Maxwell::ShaderProgram program) { |
| 24 | const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); | 36 | const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); |
| 25 | const auto& shader_config = gpu.regs.shader_config[static_cast<std::size_t>(program)]; | 37 | const auto& shader_config = gpu.regs.shader_config[static_cast<std::size_t>(program)]; |
| 26 | const auto address = gpu.memory_manager.GpuToCpuAddress(gpu.regs.code_address.CodeAddress() + | 38 | const auto address = gpu.memory_manager.GpuToCpuAddress(gpu.regs.code_address.CodeAddress() + |
| @@ -30,7 +42,7 @@ static VAddr GetShaderAddress(Maxwell::ShaderProgram program) { | |||
| 30 | } | 42 | } |
| 31 | 43 | ||
| 32 | /// Gets the shader program code from memory for the specified address | 44 | /// Gets the shader program code from memory for the specified address |
| 33 | static ProgramCode GetShaderCode(VAddr addr) { | 45 | ProgramCode GetShaderCode(VAddr addr) { |
| 34 | ProgramCode program_code(VideoCommon::Shader::MAX_PROGRAM_LENGTH); | 46 | ProgramCode program_code(VideoCommon::Shader::MAX_PROGRAM_LENGTH); |
| 35 | Memory::ReadBlock(addr, program_code.data(), program_code.size() * sizeof(u64)); | 47 | Memory::ReadBlock(addr, program_code.data(), program_code.size() * sizeof(u64)); |
| 36 | return program_code; | 48 | return program_code; |
| @@ -51,38 +63,196 @@ constexpr GLenum GetShaderType(Maxwell::ShaderProgram program_type) { | |||
| 51 | } | 63 | } |
| 52 | } | 64 | } |
| 53 | 65 | ||
| 54 | CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type) | 66 | /// Gets if the current instruction offset is a scheduler instruction |
| 55 | : addr{addr}, program_type{program_type}, setup{GetShaderCode(addr)} { | 67 | constexpr bool IsSchedInstruction(std::size_t offset, std::size_t main_offset) { |
| 68 | // Sched instructions appear once every 4 instructions. | ||
| 69 | constexpr std::size_t SchedPeriod = 4; | ||
| 70 | const std::size_t absolute_offset = offset - main_offset; | ||
| 71 | return (absolute_offset % SchedPeriod) == 0; | ||
| 72 | } | ||
| 56 | 73 | ||
| 57 | GLShader::ProgramResult program_result; | 74 | /// Describes primitive behavior on geometry shaders |
| 75 | constexpr std::tuple<const char*, const char*, u32> GetPrimitiveDescription(GLenum primitive_mode) { | ||
| 76 | switch (primitive_mode) { | ||
| 77 | case GL_POINTS: | ||
| 78 | return {"points", "Points", 1}; | ||
| 79 | case GL_LINES: | ||
| 80 | case GL_LINE_STRIP: | ||
| 81 | return {"lines", "Lines", 2}; | ||
| 82 | case GL_LINES_ADJACENCY: | ||
| 83 | case GL_LINE_STRIP_ADJACENCY: | ||
| 84 | return {"lines_adjacency", "LinesAdj", 4}; | ||
| 85 | case GL_TRIANGLES: | ||
| 86 | case GL_TRIANGLE_STRIP: | ||
| 87 | case GL_TRIANGLE_FAN: | ||
| 88 | return {"triangles", "Triangles", 3}; | ||
| 89 | case GL_TRIANGLES_ADJACENCY: | ||
| 90 | case GL_TRIANGLE_STRIP_ADJACENCY: | ||
| 91 | return {"triangles_adjacency", "TrianglesAdj", 6}; | ||
| 92 | default: | ||
| 93 | return {"points", "Invalid", 1}; | ||
| 94 | } | ||
| 95 | } | ||
| 58 | 96 | ||
| 59 | switch (program_type) { | 97 | /// Calculates the size of a program stream |
| 60 | case Maxwell::ShaderProgram::VertexA: | 98 | std::size_t CalculateProgramSize(const GLShader::ProgramCode& program) { |
| 99 | constexpr std::size_t start_offset = 10; | ||
| 100 | std::size_t offset = start_offset; | ||
| 101 | std::size_t size = start_offset * sizeof(u64); | ||
| 102 | while (offset < program.size()) { | ||
| 103 | const u64 instruction = program[offset]; | ||
| 104 | if (!IsSchedInstruction(offset, start_offset)) { | ||
| 105 | if (instruction == 0 || (instruction >> 52) == 0x50b) { | ||
| 106 | // End on Maxwell's "nop" instruction | ||
| 107 | break; | ||
| 108 | } | ||
| 109 | } | ||
| 110 | size += sizeof(u64); | ||
| 111 | offset++; | ||
| 112 | } | ||
| 113 | // The last instruction is included in the program size | ||
| 114 | return std::min(size + sizeof(u64), program.size() * sizeof(u64)); | ||
| 115 | } | ||
| 116 | |||
| 117 | /// Hashes one (or two) program streams | ||
| 118 | u64 GetUniqueIdentifier(Maxwell::ShaderProgram program_type, const ProgramCode& code, | ||
| 119 | const ProgramCode& code_b) { | ||
| 120 | u64 unique_identifier = | ||
| 121 | Common::CityHash64(reinterpret_cast<const char*>(code.data()), CalculateProgramSize(code)); | ||
| 122 | if (program_type != Maxwell::ShaderProgram::VertexA) { | ||
| 123 | return unique_identifier; | ||
| 124 | } | ||
| 125 | // VertexA programs include two programs | ||
| 126 | |||
| 127 | std::size_t seed = 0; | ||
| 128 | boost::hash_combine(seed, unique_identifier); | ||
| 129 | |||
| 130 | const u64 identifier_b = Common::CityHash64(reinterpret_cast<const char*>(code_b.data()), | ||
| 131 | CalculateProgramSize(code_b)); | ||
| 132 | boost::hash_combine(seed, identifier_b); | ||
| 133 | return static_cast<u64>(seed); | ||
| 134 | } | ||
| 135 | |||
| 136 | /// Creates an unspecialized program from code streams | ||
| 137 | GLShader::ProgramResult CreateProgram(Maxwell::ShaderProgram program_type, ProgramCode program_code, | ||
| 138 | ProgramCode program_code_b) { | ||
| 139 | GLShader::ShaderSetup setup(program_code); | ||
| 140 | if (program_type == Maxwell::ShaderProgram::VertexA) { | ||
| 61 | // VertexB is always enabled, so when VertexA is enabled, we have two vertex shaders. | 141 | // VertexB is always enabled, so when VertexA is enabled, we have two vertex shaders. |
| 62 | // Conventional HW does not support this, so we combine VertexA and VertexB into one | 142 | // Conventional HW does not support this, so we combine VertexA and VertexB into one |
| 63 | // stage here. | 143 | // stage here. |
| 64 | setup.SetProgramB(GetShaderCode(GetShaderAddress(Maxwell::ShaderProgram::VertexB))); | 144 | setup.SetProgramB(program_code_b); |
| 145 | } | ||
| 146 | setup.program.unique_identifier = | ||
| 147 | GetUniqueIdentifier(program_type, program_code, program_code_b); | ||
| 148 | |||
| 149 | switch (program_type) { | ||
| 150 | case Maxwell::ShaderProgram::VertexA: | ||
| 65 | case Maxwell::ShaderProgram::VertexB: | 151 | case Maxwell::ShaderProgram::VertexB: |
| 66 | CalculateProperties(); | 152 | return GLShader::GenerateVertexShader(setup); |
| 67 | program_result = GLShader::GenerateVertexShader(setup); | ||
| 68 | break; | ||
| 69 | case Maxwell::ShaderProgram::Geometry: | 153 | case Maxwell::ShaderProgram::Geometry: |
| 70 | CalculateProperties(); | 154 | return GLShader::GenerateGeometryShader(setup); |
| 71 | program_result = GLShader::GenerateGeometryShader(setup); | ||
| 72 | break; | ||
| 73 | case Maxwell::ShaderProgram::Fragment: | 155 | case Maxwell::ShaderProgram::Fragment: |
| 74 | CalculateProperties(); | 156 | return GLShader::GenerateFragmentShader(setup); |
| 75 | program_result = GLShader::GenerateFragmentShader(setup); | ||
| 76 | break; | ||
| 77 | default: | 157 | default: |
| 78 | LOG_CRITICAL(HW_GPU, "Unimplemented program_type={}", static_cast<u32>(program_type)); | 158 | LOG_CRITICAL(HW_GPU, "Unimplemented program_type={}", static_cast<u32>(program_type)); |
| 79 | UNREACHABLE(); | 159 | UNREACHABLE(); |
| 160 | return {}; | ||
| 161 | } | ||
| 162 | } | ||
| 163 | |||
| 164 | CachedProgram SpecializeShader(const std::string& code, const GLShader::ShaderEntries& entries, | ||
| 165 | Maxwell::ShaderProgram program_type, BaseBindings base_bindings, | ||
| 166 | GLenum primitive_mode, bool hint_retrievable = false) { | ||
| 167 | std::string source = "#version 430 core\n"; | ||
| 168 | source += fmt::format("#define EMULATION_UBO_BINDING {}\n", base_bindings.cbuf++); | ||
| 169 | |||
| 170 | for (const auto& cbuf : entries.const_buffers) { | ||
| 171 | source += | ||
| 172 | fmt::format("#define CBUF_BINDING_{} {}\n", cbuf.GetIndex(), base_bindings.cbuf++); | ||
| 173 | } | ||
| 174 | for (const auto& gmem : entries.global_memory_entries) { | ||
| 175 | source += fmt::format("#define GMEM_BINDING_{}_{} {}\n", gmem.GetCbufIndex(), | ||
| 176 | gmem.GetCbufOffset(), base_bindings.gmem++); | ||
| 177 | } | ||
| 178 | for (const auto& sampler : entries.samplers) { | ||
| 179 | source += fmt::format("#define SAMPLER_BINDING_{} {}\n", sampler.GetIndex(), | ||
| 180 | base_bindings.sampler++); | ||
| 181 | } | ||
| 182 | |||
| 183 | if (program_type == Maxwell::ShaderProgram::Geometry) { | ||
| 184 | const auto [glsl_topology, debug_name, max_vertices] = | ||
| 185 | GetPrimitiveDescription(primitive_mode); | ||
| 186 | |||
| 187 | source += "layout (" + std::string(glsl_topology) + ") in;\n"; | ||
| 188 | source += "#define MAX_VERTEX_INPUT " + std::to_string(max_vertices) + '\n'; | ||
| 189 | } | ||
| 190 | |||
| 191 | source += code; | ||
| 192 | |||
| 193 | OGLShader shader; | ||
| 194 | shader.Create(source.c_str(), GetShaderType(program_type)); | ||
| 195 | |||
| 196 | auto program = std::make_shared<OGLProgram>(); | ||
| 197 | program->Create(true, hint_retrievable, shader.handle); | ||
| 198 | return program; | ||
| 199 | } | ||
| 200 | |||
| 201 | std::set<GLenum> GetSupportedFormats() { | ||
| 202 | std::set<GLenum> supported_formats; | ||
| 203 | |||
| 204 | GLint num_formats{}; | ||
| 205 | glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &num_formats); | ||
| 206 | |||
| 207 | std::vector<GLint> formats(num_formats); | ||
| 208 | glGetIntegerv(GL_PROGRAM_BINARY_FORMATS, formats.data()); | ||
| 209 | |||
| 210 | for (const GLint format : formats) | ||
| 211 | supported_formats.insert(static_cast<GLenum>(format)); | ||
| 212 | return supported_formats; | ||
| 213 | } | ||
| 214 | |||
| 215 | } // namespace | ||
| 216 | |||
| 217 | CachedShader::CachedShader(VAddr addr, u64 unique_identifier, Maxwell::ShaderProgram program_type, | ||
| 218 | ShaderDiskCacheOpenGL& disk_cache, | ||
| 219 | const PrecompiledPrograms& precompiled_programs, | ||
| 220 | ProgramCode&& program_code, ProgramCode&& program_code_b) | ||
| 221 | : addr{addr}, unique_identifier{unique_identifier}, program_type{program_type}, | ||
| 222 | disk_cache{disk_cache}, precompiled_programs{precompiled_programs} { | ||
| 223 | |||
| 224 | const std::size_t code_size = CalculateProgramSize(program_code); | ||
| 225 | const std::size_t code_size_b = | ||
| 226 | program_code_b.empty() ? 0 : CalculateProgramSize(program_code_b); | ||
| 227 | |||
| 228 | GLShader::ProgramResult program_result = | ||
| 229 | CreateProgram(program_type, program_code, program_code_b); | ||
| 230 | if (program_result.first.empty()) { | ||
| 231 | // TODO(Rodrigo): Unimplemented shader stages hit here, avoid using these for now | ||
| 80 | return; | 232 | return; |
| 81 | } | 233 | } |
| 82 | 234 | ||
| 83 | code = program_result.first; | 235 | code = program_result.first; |
| 84 | entries = program_result.second; | 236 | entries = program_result.second; |
| 85 | shader_length = entries.shader_length; | 237 | shader_length = entries.shader_length; |
| 238 | |||
| 239 | const ShaderDiskCacheRaw raw(unique_identifier, program_type, | ||
| 240 | static_cast<u32>(code_size / sizeof(u64)), | ||
| 241 | static_cast<u32>(code_size_b / sizeof(u64)), | ||
| 242 | std::move(program_code), std::move(program_code_b)); | ||
| 243 | disk_cache.SaveRaw(raw); | ||
| 244 | } | ||
| 245 | |||
| 246 | CachedShader::CachedShader(VAddr addr, u64 unique_identifier, Maxwell::ShaderProgram program_type, | ||
| 247 | ShaderDiskCacheOpenGL& disk_cache, | ||
| 248 | const PrecompiledPrograms& precompiled_programs, | ||
| 249 | GLShader::ProgramResult result) | ||
| 250 | : addr{addr}, unique_identifier{unique_identifier}, program_type{program_type}, | ||
| 251 | disk_cache{disk_cache}, precompiled_programs{precompiled_programs} { | ||
| 252 | |||
| 253 | code = std::move(result.first); | ||
| 254 | entries = result.second; | ||
| 255 | shader_length = entries.shader_length; | ||
| 86 | } | 256 | } |
| 87 | 257 | ||
| 88 | std::tuple<GLuint, BaseBindings> CachedShader::GetProgramHandle(GLenum primitive_mode, | 258 | std::tuple<GLuint, BaseBindings> CachedShader::GetProgramHandle(GLenum primitive_mode, |
| @@ -94,136 +264,222 @@ std::tuple<GLuint, BaseBindings> CachedShader::GetProgramHandle(GLenum primitive | |||
| 94 | const auto [entry, is_cache_miss] = programs.try_emplace(base_bindings); | 264 | const auto [entry, is_cache_miss] = programs.try_emplace(base_bindings); |
| 95 | auto& program = entry->second; | 265 | auto& program = entry->second; |
| 96 | if (is_cache_miss) { | 266 | if (is_cache_miss) { |
| 97 | std::string source = AllocateBindings(base_bindings); | 267 | program = TryLoadProgram(primitive_mode, base_bindings); |
| 98 | source += code; | 268 | if (!program) { |
| 269 | program = | ||
| 270 | SpecializeShader(code, entries, program_type, base_bindings, primitive_mode); | ||
| 271 | disk_cache.SaveUsage(GetUsage(primitive_mode, base_bindings)); | ||
| 272 | } | ||
| 99 | 273 | ||
| 100 | OGLShader shader; | 274 | LabelGLObject(GL_PROGRAM, program->handle, addr); |
| 101 | shader.Create(source.c_str(), GetShaderType(program_type)); | ||
| 102 | program.Create(true, shader.handle); | ||
| 103 | LabelGLObject(GL_PROGRAM, program.handle, addr); | ||
| 104 | } | 275 | } |
| 105 | 276 | ||
| 106 | handle = program.handle; | 277 | handle = program->handle; |
| 107 | } | 278 | } |
| 108 | 279 | ||
| 109 | // Add const buffer and samplers offset reserved by this shader. One UBO binding is reserved for | 280 | base_bindings.cbuf += static_cast<u32>(entries.const_buffers.size()) + RESERVED_UBOS; |
| 110 | // emulation values | ||
| 111 | base_bindings.cbuf += static_cast<u32>(entries.const_buffers.size()) + 1; | ||
| 112 | base_bindings.gmem += static_cast<u32>(entries.global_memory_entries.size()); | 281 | base_bindings.gmem += static_cast<u32>(entries.global_memory_entries.size()); |
| 113 | base_bindings.sampler += static_cast<u32>(entries.samplers.size()); | 282 | base_bindings.sampler += static_cast<u32>(entries.samplers.size()); |
| 114 | 283 | ||
| 115 | return {handle, base_bindings}; | 284 | return {handle, base_bindings}; |
| 116 | } | 285 | } |
| 117 | 286 | ||
| 118 | std::string CachedShader::AllocateBindings(BaseBindings base_bindings) { | ||
| 119 | std::string code = "#version 430 core\n"; | ||
| 120 | code += fmt::format("#define EMULATION_UBO_BINDING {}\n", base_bindings.cbuf++); | ||
| 121 | |||
| 122 | for (const auto& cbuf : entries.const_buffers) { | ||
| 123 | code += fmt::format("#define CBUF_BINDING_{} {}\n", cbuf.GetIndex(), base_bindings.cbuf++); | ||
| 124 | } | ||
| 125 | |||
| 126 | for (const auto& gmem : entries.global_memory_entries) { | ||
| 127 | code += fmt::format("#define GMEM_BINDING_{}_{} {}\n", gmem.GetCbufIndex(), | ||
| 128 | gmem.GetCbufOffset(), base_bindings.gmem++); | ||
| 129 | } | ||
| 130 | |||
| 131 | for (const auto& sampler : entries.samplers) { | ||
| 132 | code += fmt::format("#define SAMPLER_BINDING_{} {}\n", sampler.GetIndex(), | ||
| 133 | base_bindings.sampler++); | ||
| 134 | } | ||
| 135 | |||
| 136 | return code; | ||
| 137 | } | ||
| 138 | |||
| 139 | GLuint CachedShader::GetGeometryShader(GLenum primitive_mode, BaseBindings base_bindings) { | 287 | GLuint CachedShader::GetGeometryShader(GLenum primitive_mode, BaseBindings base_bindings) { |
| 140 | const auto [entry, is_cache_miss] = geometry_programs.try_emplace(base_bindings); | 288 | const auto [entry, is_cache_miss] = geometry_programs.try_emplace(base_bindings); |
| 141 | auto& programs = entry->second; | 289 | auto& programs = entry->second; |
| 142 | 290 | ||
| 143 | switch (primitive_mode) { | 291 | switch (primitive_mode) { |
| 144 | case GL_POINTS: | 292 | case GL_POINTS: |
| 145 | return LazyGeometryProgram(programs.points, base_bindings, "points", 1, "ShaderPoints"); | 293 | return LazyGeometryProgram(programs.points, base_bindings, primitive_mode); |
| 146 | case GL_LINES: | 294 | case GL_LINES: |
| 147 | case GL_LINE_STRIP: | 295 | case GL_LINE_STRIP: |
| 148 | return LazyGeometryProgram(programs.lines, base_bindings, "lines", 2, "ShaderLines"); | 296 | return LazyGeometryProgram(programs.lines, base_bindings, primitive_mode); |
| 149 | case GL_LINES_ADJACENCY: | 297 | case GL_LINES_ADJACENCY: |
| 150 | case GL_LINE_STRIP_ADJACENCY: | 298 | case GL_LINE_STRIP_ADJACENCY: |
| 151 | return LazyGeometryProgram(programs.lines_adjacency, base_bindings, "lines_adjacency", 4, | 299 | return LazyGeometryProgram(programs.lines_adjacency, base_bindings, primitive_mode); |
| 152 | "ShaderLinesAdjacency"); | ||
| 153 | case GL_TRIANGLES: | 300 | case GL_TRIANGLES: |
| 154 | case GL_TRIANGLE_STRIP: | 301 | case GL_TRIANGLE_STRIP: |
| 155 | case GL_TRIANGLE_FAN: | 302 | case GL_TRIANGLE_FAN: |
| 156 | return LazyGeometryProgram(programs.triangles, base_bindings, "triangles", 3, | 303 | return LazyGeometryProgram(programs.triangles, base_bindings, primitive_mode); |
| 157 | "ShaderTriangles"); | ||
| 158 | case GL_TRIANGLES_ADJACENCY: | 304 | case GL_TRIANGLES_ADJACENCY: |
| 159 | case GL_TRIANGLE_STRIP_ADJACENCY: | 305 | case GL_TRIANGLE_STRIP_ADJACENCY: |
| 160 | return LazyGeometryProgram(programs.triangles_adjacency, base_bindings, | 306 | return LazyGeometryProgram(programs.triangles_adjacency, base_bindings, primitive_mode); |
| 161 | "triangles_adjacency", 6, "ShaderTrianglesAdjacency"); | ||
| 162 | default: | 307 | default: |
| 163 | UNREACHABLE_MSG("Unknown primitive mode."); | 308 | UNREACHABLE_MSG("Unknown primitive mode."); |
| 164 | return LazyGeometryProgram(programs.points, base_bindings, "points", 1, "ShaderPoints"); | 309 | return LazyGeometryProgram(programs.points, base_bindings, primitive_mode); |
| 165 | } | 310 | } |
| 166 | } | 311 | } |
| 167 | 312 | ||
| 168 | GLuint CachedShader::LazyGeometryProgram(OGLProgram& target_program, BaseBindings base_bindings, | 313 | GLuint CachedShader::LazyGeometryProgram(CachedProgram& target_program, BaseBindings base_bindings, |
| 169 | const std::string& glsl_topology, u32 max_vertices, | 314 | GLenum primitive_mode) { |
| 170 | const std::string& debug_name) { | 315 | if (target_program) { |
| 171 | if (target_program.handle != 0) { | 316 | return target_program->handle; |
| 172 | return target_program.handle; | 317 | } |
| 318 | const auto [glsl_name, debug_name, vertices] = GetPrimitiveDescription(primitive_mode); | ||
| 319 | target_program = TryLoadProgram(primitive_mode, base_bindings); | ||
| 320 | if (!target_program) { | ||
| 321 | target_program = | ||
| 322 | SpecializeShader(code, entries, program_type, base_bindings, primitive_mode); | ||
| 323 | disk_cache.SaveUsage(GetUsage(primitive_mode, base_bindings)); | ||
| 173 | } | 324 | } |
| 174 | std::string source = AllocateBindings(base_bindings); | ||
| 175 | source += "layout (" + glsl_topology + ") in;\n"; | ||
| 176 | source += "#define MAX_VERTEX_INPUT " + std::to_string(max_vertices) + '\n'; | ||
| 177 | source += code; | ||
| 178 | 325 | ||
| 179 | OGLShader shader; | 326 | LabelGLObject(GL_PROGRAM, target_program->handle, addr, debug_name); |
| 180 | shader.Create(source.c_str(), GL_GEOMETRY_SHADER); | 327 | |
| 181 | target_program.Create(true, shader.handle); | 328 | return target_program->handle; |
| 182 | LabelGLObject(GL_PROGRAM, target_program.handle, addr, debug_name); | ||
| 183 | return target_program.handle; | ||
| 184 | }; | 329 | }; |
| 185 | 330 | ||
| 186 | static bool IsSchedInstruction(std::size_t offset, std::size_t main_offset) { | 331 | CachedProgram CachedShader::TryLoadProgram(GLenum primitive_mode, |
| 187 | // sched instructions appear once every 4 instructions. | 332 | BaseBindings base_bindings) const { |
| 188 | static constexpr std::size_t SchedPeriod = 4; | 333 | const auto found = precompiled_programs.find(GetUsage(primitive_mode, base_bindings)); |
| 189 | const std::size_t absolute_offset = offset - main_offset; | 334 | if (found == precompiled_programs.end()) { |
| 190 | return (absolute_offset % SchedPeriod) == 0; | 335 | return {}; |
| 336 | } | ||
| 337 | return found->second; | ||
| 191 | } | 338 | } |
| 192 | 339 | ||
| 193 | static std::size_t CalculateProgramSize(const GLShader::ProgramCode& program) { | 340 | ShaderDiskCacheUsage CachedShader::GetUsage(GLenum primitive_mode, |
| 194 | constexpr std::size_t start_offset = 10; | 341 | BaseBindings base_bindings) const { |
| 195 | std::size_t offset = start_offset; | 342 | return {unique_identifier, base_bindings, primitive_mode}; |
| 196 | std::size_t size = start_offset * sizeof(u64); | 343 | } |
| 197 | while (offset < program.size()) { | 344 | |
| 198 | const u64 inst = program[offset]; | 345 | ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system) |
| 199 | if (!IsSchedInstruction(offset, start_offset)) { | 346 | : RasterizerCache{rasterizer}, disk_cache{system} {} |
| 200 | if (inst == 0 || (inst >> 52) == 0x50b) { | 347 | |
| 201 | break; | 348 | void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading, |
| 349 | const VideoCore::DiskResourceLoadCallback& callback) { | ||
| 350 | const auto transferable = disk_cache.LoadTransferable(); | ||
| 351 | if (!transferable) { | ||
| 352 | return; | ||
| 353 | } | ||
| 354 | const auto [raws, usages] = *transferable; | ||
| 355 | |||
| 356 | auto [decompiled, dumps] = disk_cache.LoadPrecompiled(); | ||
| 357 | |||
| 358 | const auto supported_formats{GetSupportedFormats()}; | ||
| 359 | const auto unspecialized{ | ||
| 360 | GenerateUnspecializedShaders(stop_loading, callback, raws, decompiled)}; | ||
| 361 | if (stop_loading) | ||
| 362 | return; | ||
| 363 | |||
| 364 | // Build shaders | ||
| 365 | if (callback) | ||
| 366 | callback(VideoCore::LoadCallbackStage::Build, 0, usages.size()); | ||
| 367 | for (std::size_t i = 0; i < usages.size(); ++i) { | ||
| 368 | if (stop_loading) | ||
| 369 | return; | ||
| 370 | |||
| 371 | const auto& usage{usages[i]}; | ||
| 372 | LOG_INFO(Render_OpenGL, "Building shader {:016x} ({} of {})", usage.unique_identifier, | ||
| 373 | i + 1, usages.size()); | ||
| 374 | |||
| 375 | const auto& unspec{unspecialized.at(usage.unique_identifier)}; | ||
| 376 | const auto dump_it = dumps.find(usage); | ||
| 377 | |||
| 378 | CachedProgram shader; | ||
| 379 | if (dump_it != dumps.end()) { | ||
| 380 | // If the shader is dumped, attempt to load it with | ||
| 381 | shader = GeneratePrecompiledProgram(dump_it->second, supported_formats); | ||
| 382 | if (!shader) { | ||
| 383 | // Invalidate the precompiled cache if a shader dumped shader was rejected | ||
| 384 | disk_cache.InvalidatePrecompiled(); | ||
| 385 | dumps.clear(); | ||
| 202 | } | 386 | } |
| 203 | } | 387 | } |
| 204 | size += sizeof(inst); | 388 | if (!shader) { |
| 205 | offset++; | 389 | shader = SpecializeShader(unspec.code, unspec.entries, unspec.program_type, |
| 390 | usage.bindings, usage.primitive, true); | ||
| 391 | } | ||
| 392 | precompiled_programs.insert({usage, std::move(shader)}); | ||
| 393 | |||
| 394 | if (callback) | ||
| 395 | callback(VideoCore::LoadCallbackStage::Build, i + 1, usages.size()); | ||
| 396 | } | ||
| 397 | |||
| 398 | // TODO(Rodrigo): Do state tracking for transferable shaders and do a dummy draw before | ||
| 399 | // precompiling them | ||
| 400 | |||
| 401 | for (std::size_t i = 0; i < usages.size(); ++i) { | ||
| 402 | const auto& usage{usages[i]}; | ||
| 403 | if (dumps.find(usage) == dumps.end()) { | ||
| 404 | const auto& program = precompiled_programs.at(usage); | ||
| 405 | disk_cache.SaveDump(usage, program->handle); | ||
| 406 | } | ||
| 206 | } | 407 | } |
| 207 | return size; | ||
| 208 | } | 408 | } |
| 209 | 409 | ||
| 210 | void CachedShader::CalculateProperties() { | 410 | CachedProgram ShaderCacheOpenGL::GeneratePrecompiledProgram( |
| 211 | setup.program.real_size = CalculateProgramSize(setup.program.code); | 411 | const ShaderDiskCacheDump& dump, const std::set<GLenum>& supported_formats) { |
| 212 | setup.program.real_size_b = 0; | 412 | |
| 213 | setup.program.unique_identifier = Common::CityHash64( | 413 | if (supported_formats.find(dump.binary_format) == supported_formats.end()) { |
| 214 | reinterpret_cast<const char*>(setup.program.code.data()), setup.program.real_size); | 414 | LOG_INFO(Render_OpenGL, "Precompiled cache entry with unsupported format - removing"); |
| 215 | if (program_type == Maxwell::ShaderProgram::VertexA) { | 415 | return {}; |
| 216 | std::size_t seed = 0; | ||
| 217 | boost::hash_combine(seed, setup.program.unique_identifier); | ||
| 218 | setup.program.real_size_b = CalculateProgramSize(setup.program.code_b); | ||
| 219 | const u64 identifier_b = Common::CityHash64( | ||
| 220 | reinterpret_cast<const char*>(setup.program.code_b.data()), setup.program.real_size_b); | ||
| 221 | boost::hash_combine(seed, identifier_b); | ||
| 222 | setup.program.unique_identifier = static_cast<u64>(seed); | ||
| 223 | } | 416 | } |
| 417 | |||
| 418 | CachedProgram shader = std::make_shared<OGLProgram>(); | ||
| 419 | shader->handle = glCreateProgram(); | ||
| 420 | glProgramParameteri(shader->handle, GL_PROGRAM_SEPARABLE, GL_TRUE); | ||
| 421 | glProgramBinary(shader->handle, dump.binary_format, dump.binary.data(), | ||
| 422 | static_cast<GLsizei>(dump.binary.size())); | ||
| 423 | |||
| 424 | GLint link_status{}; | ||
| 425 | glGetProgramiv(shader->handle, GL_LINK_STATUS, &link_status); | ||
| 426 | if (link_status == GL_FALSE) { | ||
| 427 | LOG_INFO(Render_OpenGL, "Precompiled cache rejected by the driver - removing"); | ||
| 428 | return {}; | ||
| 429 | } | ||
| 430 | |||
| 431 | return shader; | ||
| 224 | } | 432 | } |
| 225 | 433 | ||
| 226 | ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer) : RasterizerCache{rasterizer} {} | 434 | std::unordered_map<u64, UnspecializedShader> ShaderCacheOpenGL::GenerateUnspecializedShaders( |
| 435 | const std::atomic_bool& stop_loading, const VideoCore::DiskResourceLoadCallback& callback, | ||
| 436 | const std::vector<ShaderDiskCacheRaw>& raws, | ||
| 437 | const std::unordered_map<u64, ShaderDiskCacheDecompiled>& decompiled) { | ||
| 438 | std::unordered_map<u64, UnspecializedShader> unspecialized; | ||
| 439 | |||
| 440 | if (callback) | ||
| 441 | callback(VideoCore::LoadCallbackStage::Decompile, 0, raws.size()); | ||
| 442 | |||
| 443 | for (std::size_t i = 0; i < raws.size(); ++i) { | ||
| 444 | if (stop_loading) | ||
| 445 | return {}; | ||
| 446 | |||
| 447 | const auto& raw{raws[i]}; | ||
| 448 | const u64 unique_identifier = raw.GetUniqueIdentifier(); | ||
| 449 | const u64 calculated_hash = | ||
| 450 | GetUniqueIdentifier(raw.GetProgramType(), raw.GetProgramCode(), raw.GetProgramCodeB()); | ||
| 451 | if (unique_identifier != calculated_hash) { | ||
| 452 | LOG_ERROR( | ||
| 453 | Render_OpenGL, | ||
| 454 | "Invalid hash in entry={:016x} (obtained hash={:016x}) - removing shader cache", | ||
| 455 | raw.GetUniqueIdentifier(), calculated_hash); | ||
| 456 | disk_cache.InvalidateTransferable(); | ||
| 457 | return {}; | ||
| 458 | } | ||
| 459 | |||
| 460 | GLShader::ProgramResult result; | ||
| 461 | if (const auto it = decompiled.find(unique_identifier); it != decompiled.end()) { | ||
| 462 | // If it's stored in the precompiled file, avoid decompiling it here | ||
| 463 | const auto& stored_decompiled{it->second}; | ||
| 464 | result = {stored_decompiled.code, stored_decompiled.entries}; | ||
| 465 | } else { | ||
| 466 | // Otherwise decompile the shader at boot and save the result to the decompiled file | ||
| 467 | result = | ||
| 468 | CreateProgram(raw.GetProgramType(), raw.GetProgramCode(), raw.GetProgramCodeB()); | ||
| 469 | disk_cache.SaveDecompiled(unique_identifier, result.first, result.second); | ||
| 470 | } | ||
| 471 | |||
| 472 | precompiled_shaders.insert({unique_identifier, result}); | ||
| 473 | |||
| 474 | unspecialized.insert( | ||
| 475 | {raw.GetUniqueIdentifier(), | ||
| 476 | {std::move(result.first), std::move(result.second), raw.GetProgramType()}}); | ||
| 477 | |||
| 478 | if (callback) | ||
| 479 | callback(VideoCore::LoadCallbackStage::Decompile, i, raws.size()); | ||
| 480 | } | ||
| 481 | return unspecialized; | ||
| 482 | } | ||
| 227 | 483 | ||
| 228 | Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { | 484 | Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { |
| 229 | if (!Core::System::GetInstance().GPU().Maxwell3D().dirty_flags.shaders) { | 485 | if (!Core::System::GetInstance().GPU().Maxwell3D().dirty_flags.shaders) { |
| @@ -237,7 +493,23 @@ Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { | |||
| 237 | 493 | ||
| 238 | if (!shader) { | 494 | if (!shader) { |
| 239 | // No shader found - create a new one | 495 | // No shader found - create a new one |
| 240 | shader = std::make_shared<CachedShader>(program_addr, program); | 496 | ProgramCode program_code = GetShaderCode(program_addr); |
| 497 | ProgramCode program_code_b; | ||
| 498 | if (program == Maxwell::ShaderProgram::VertexA) { | ||
| 499 | program_code_b = GetShaderCode(GetShaderAddress(Maxwell::ShaderProgram::VertexB)); | ||
| 500 | } | ||
| 501 | const u64 unique_identifier = GetUniqueIdentifier(program, program_code, program_code_b); | ||
| 502 | |||
| 503 | const auto found = precompiled_shaders.find(unique_identifier); | ||
| 504 | if (found != precompiled_shaders.end()) { | ||
| 505 | shader = | ||
| 506 | std::make_shared<CachedShader>(program_addr, unique_identifier, program, disk_cache, | ||
| 507 | precompiled_programs, found->second); | ||
| 508 | } else { | ||
| 509 | shader = std::make_shared<CachedShader>( | ||
| 510 | program_addr, unique_identifier, program, disk_cache, precompiled_programs, | ||
| 511 | std::move(program_code), std::move(program_code_b)); | ||
| 512 | } | ||
| 241 | Register(shader); | 513 | Register(shader); |
| 242 | } | 514 | } |
| 243 | 515 | ||
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h index 904d15dd0..97eed192f 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_cache.h | |||
| @@ -5,40 +5,49 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <map> | ||
| 9 | #include <memory> | 8 | #include <memory> |
| 9 | #include <set> | ||
| 10 | #include <tuple> | 10 | #include <tuple> |
| 11 | #include <unordered_map> | ||
| 11 | 12 | ||
| 12 | #include <glad/glad.h> | 13 | #include <glad/glad.h> |
| 13 | 14 | ||
| 14 | #include "common/assert.h" | 15 | #include "common/assert.h" |
| 15 | #include "common/common_types.h" | 16 | #include "common/common_types.h" |
| 16 | #include "video_core/rasterizer_cache.h" | 17 | #include "video_core/rasterizer_cache.h" |
| 18 | #include "video_core/renderer_base.h" | ||
| 17 | #include "video_core/renderer_opengl/gl_resource_manager.h" | 19 | #include "video_core/renderer_opengl/gl_resource_manager.h" |
| 18 | #include "video_core/renderer_opengl/gl_shader_decompiler.h" | 20 | #include "video_core/renderer_opengl/gl_shader_decompiler.h" |
| 21 | #include "video_core/renderer_opengl/gl_shader_disk_cache.h" | ||
| 19 | #include "video_core/renderer_opengl/gl_shader_gen.h" | 22 | #include "video_core/renderer_opengl/gl_shader_gen.h" |
| 20 | 23 | ||
| 24 | namespace Core { | ||
| 25 | class System; | ||
| 26 | } // namespace Core | ||
| 27 | |||
| 21 | namespace OpenGL { | 28 | namespace OpenGL { |
| 22 | 29 | ||
| 23 | class CachedShader; | 30 | class CachedShader; |
| 24 | class RasterizerOpenGL; | 31 | class RasterizerOpenGL; |
| 32 | struct UnspecializedShader; | ||
| 25 | 33 | ||
| 26 | using Shader = std::shared_ptr<CachedShader>; | 34 | using Shader = std::shared_ptr<CachedShader>; |
| 35 | using CachedProgram = std::shared_ptr<OGLProgram>; | ||
| 27 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; | 36 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; |
| 28 | 37 | using PrecompiledPrograms = std::unordered_map<ShaderDiskCacheUsage, CachedProgram>; | |
| 29 | struct BaseBindings { | 38 | using PrecompiledShaders = std::unordered_map<u64, GLShader::ProgramResult>; |
| 30 | u32 cbuf{}; | ||
| 31 | u32 gmem{}; | ||
| 32 | u32 sampler{}; | ||
| 33 | |||
| 34 | bool operator<(const BaseBindings& rhs) const { | ||
| 35 | return std::tie(cbuf, gmem, sampler) < std::tie(rhs.cbuf, rhs.gmem, rhs.sampler); | ||
| 36 | } | ||
| 37 | }; | ||
| 38 | 39 | ||
| 39 | class CachedShader final : public RasterizerCacheObject { | 40 | class CachedShader final : public RasterizerCacheObject { |
| 40 | public: | 41 | public: |
| 41 | CachedShader(VAddr addr, Maxwell::ShaderProgram program_type); | 42 | explicit CachedShader(VAddr addr, u64 unique_identifier, Maxwell::ShaderProgram program_type, |
| 43 | ShaderDiskCacheOpenGL& disk_cache, | ||
| 44 | const PrecompiledPrograms& precompiled_programs, | ||
| 45 | ProgramCode&& program_code, ProgramCode&& program_code_b); | ||
| 46 | |||
| 47 | explicit CachedShader(VAddr addr, u64 unique_identifier, Maxwell::ShaderProgram program_type, | ||
| 48 | ShaderDiskCacheOpenGL& disk_cache, | ||
| 49 | const PrecompiledPrograms& precompiled_programs, | ||
| 50 | GLShader::ProgramResult result); | ||
| 42 | 51 | ||
| 43 | VAddr GetAddr() const override { | 52 | VAddr GetAddr() const override { |
| 44 | return addr; | 53 | return addr; |
| @@ -65,49 +74,67 @@ private: | |||
| 65 | // declared by the hardware. Workaround this issue by generating a different shader per input | 74 | // declared by the hardware. Workaround this issue by generating a different shader per input |
| 66 | // topology class. | 75 | // topology class. |
| 67 | struct GeometryPrograms { | 76 | struct GeometryPrograms { |
| 68 | OGLProgram points; | 77 | CachedProgram points; |
| 69 | OGLProgram lines; | 78 | CachedProgram lines; |
| 70 | OGLProgram lines_adjacency; | 79 | CachedProgram lines_adjacency; |
| 71 | OGLProgram triangles; | 80 | CachedProgram triangles; |
| 72 | OGLProgram triangles_adjacency; | 81 | CachedProgram triangles_adjacency; |
| 73 | }; | 82 | }; |
| 74 | 83 | ||
| 75 | std::string AllocateBindings(BaseBindings base_bindings); | ||
| 76 | |||
| 77 | GLuint GetGeometryShader(GLenum primitive_mode, BaseBindings base_bindings); | 84 | GLuint GetGeometryShader(GLenum primitive_mode, BaseBindings base_bindings); |
| 78 | 85 | ||
| 79 | /// Generates a geometry shader or returns one that already exists. | 86 | /// Generates a geometry shader or returns one that already exists. |
| 80 | GLuint LazyGeometryProgram(OGLProgram& target_program, BaseBindings base_bindings, | 87 | GLuint LazyGeometryProgram(CachedProgram& target_program, BaseBindings base_bindings, |
| 81 | const std::string& glsl_topology, u32 max_vertices, | 88 | GLenum primitive_mode); |
| 82 | const std::string& debug_name); | 89 | |
| 90 | CachedProgram TryLoadProgram(GLenum primitive_mode, BaseBindings base_bindings) const; | ||
| 83 | 91 | ||
| 84 | void CalculateProperties(); | 92 | ShaderDiskCacheUsage GetUsage(GLenum primitive_mode, BaseBindings base_bindings) const; |
| 85 | 93 | ||
| 86 | VAddr addr{}; | 94 | VAddr addr{}; |
| 87 | std::size_t shader_length{}; | 95 | u64 unique_identifier{}; |
| 88 | Maxwell::ShaderProgram program_type{}; | 96 | Maxwell::ShaderProgram program_type{}; |
| 89 | GLShader::ShaderSetup setup; | 97 | ShaderDiskCacheOpenGL& disk_cache; |
| 98 | const PrecompiledPrograms& precompiled_programs; | ||
| 99 | |||
| 100 | std::size_t shader_length{}; | ||
| 90 | GLShader::ShaderEntries entries; | 101 | GLShader::ShaderEntries entries; |
| 91 | 102 | ||
| 92 | std::string code; | 103 | std::string code; |
| 93 | 104 | ||
| 94 | std::map<BaseBindings, OGLProgram> programs; | 105 | std::unordered_map<BaseBindings, CachedProgram> programs; |
| 95 | std::map<BaseBindings, GeometryPrograms> geometry_programs; | 106 | std::unordered_map<BaseBindings, GeometryPrograms> geometry_programs; |
| 96 | 107 | ||
| 97 | std::map<u32, GLuint> cbuf_resource_cache; | 108 | std::unordered_map<u32, GLuint> cbuf_resource_cache; |
| 98 | std::map<u32, GLuint> gmem_resource_cache; | 109 | std::unordered_map<u32, GLuint> gmem_resource_cache; |
| 99 | std::map<u32, GLint> uniform_cache; | 110 | std::unordered_map<u32, GLint> uniform_cache; |
| 100 | }; | 111 | }; |
| 101 | 112 | ||
| 102 | class ShaderCacheOpenGL final : public RasterizerCache<Shader> { | 113 | class ShaderCacheOpenGL final : public RasterizerCache<Shader> { |
| 103 | public: | 114 | public: |
| 104 | explicit ShaderCacheOpenGL(RasterizerOpenGL& rasterizer); | 115 | explicit ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system); |
| 116 | |||
| 117 | /// Loads disk cache for the current game | ||
| 118 | void LoadDiskCache(const std::atomic_bool& stop_loading, | ||
| 119 | const VideoCore::DiskResourceLoadCallback& callback); | ||
| 105 | 120 | ||
| 106 | /// Gets the current specified shader stage program | 121 | /// Gets the current specified shader stage program |
| 107 | Shader GetStageProgram(Maxwell::ShaderProgram program); | 122 | Shader GetStageProgram(Maxwell::ShaderProgram program); |
| 108 | 123 | ||
| 109 | private: | 124 | private: |
| 125 | std::unordered_map<u64, UnspecializedShader> GenerateUnspecializedShaders( | ||
| 126 | const std::atomic_bool& stop_loading, const VideoCore::DiskResourceLoadCallback& callback, | ||
| 127 | const std::vector<ShaderDiskCacheRaw>& raws, | ||
| 128 | const std::unordered_map<u64, ShaderDiskCacheDecompiled>& decompiled); | ||
| 129 | |||
| 130 | CachedProgram GeneratePrecompiledProgram(const ShaderDiskCacheDump& dump, | ||
| 131 | const std::set<GLenum>& supported_formats); | ||
| 132 | |||
| 110 | std::array<Shader, Maxwell::MaxShaderProgram> last_shaders; | 133 | std::array<Shader, Maxwell::MaxShaderProgram> last_shaders; |
| 134 | |||
| 135 | ShaderDiskCacheOpenGL disk_cache; | ||
| 136 | PrecompiledShaders precompiled_shaders; | ||
| 137 | PrecompiledPrograms precompiled_programs; | ||
| 111 | }; | 138 | }; |
| 112 | 139 | ||
| 113 | } // namespace OpenGL | 140 | } // namespace OpenGL |
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 36035d0d2..d84caa6db 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp | |||
| @@ -193,15 +193,14 @@ public: | |||
| 193 | ShaderEntries GetShaderEntries() const { | 193 | ShaderEntries GetShaderEntries() const { |
| 194 | ShaderEntries entries; | 194 | ShaderEntries entries; |
| 195 | for (const auto& cbuf : ir.GetConstantBuffers()) { | 195 | for (const auto& cbuf : ir.GetConstantBuffers()) { |
| 196 | entries.const_buffers.emplace_back(cbuf.second, stage, GetConstBufferBlock(cbuf.first), | 196 | entries.const_buffers.emplace_back(cbuf.second.GetMaxOffset(), cbuf.second.IsIndirect(), |
| 197 | cbuf.first); | 197 | cbuf.first); |
| 198 | } | 198 | } |
| 199 | for (const auto& sampler : ir.GetSamplers()) { | 199 | for (const auto& sampler : ir.GetSamplers()) { |
| 200 | entries.samplers.emplace_back(sampler, stage, GetSampler(sampler)); | 200 | entries.samplers.emplace_back(sampler); |
| 201 | } | 201 | } |
| 202 | for (const auto& gmem : ir.GetGlobalMemoryBases()) { | 202 | for (const auto& gmem : ir.GetGlobalMemoryBases()) { |
| 203 | entries.global_memory_entries.emplace_back(gmem.cbuf_index, gmem.cbuf_offset, stage, | 203 | entries.global_memory_entries.emplace_back(gmem.cbuf_index, gmem.cbuf_offset); |
| 204 | GetGlobalMemoryBlock(gmem)); | ||
| 205 | } | 204 | } |
| 206 | entries.clip_distances = ir.GetClipDistances(); | 205 | entries.clip_distances = ir.GetClipDistances(); |
| 207 | entries.shader_length = ir.GetLength(); | 206 | entries.shader_length = ir.GetLength(); |
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.h b/src/video_core/renderer_opengl/gl_shader_decompiler.h index 0856a1361..72aca4938 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.h +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <set> | ||
| 8 | #include <string> | 9 | #include <string> |
| 9 | #include <utility> | 10 | #include <utility> |
| 10 | #include <vector> | 11 | #include <vector> |
| @@ -18,56 +19,29 @@ class ShaderIR; | |||
| 18 | 19 | ||
| 19 | namespace OpenGL::GLShader { | 20 | namespace OpenGL::GLShader { |
| 20 | 21 | ||
| 22 | struct ShaderEntries; | ||
| 23 | |||
| 21 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; | 24 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; |
| 25 | using ProgramResult = std::pair<std::string, ShaderEntries>; | ||
| 26 | using SamplerEntry = VideoCommon::Shader::Sampler; | ||
| 22 | 27 | ||
| 23 | class ConstBufferEntry : public VideoCommon::Shader::ConstBuffer { | 28 | class ConstBufferEntry : public VideoCommon::Shader::ConstBuffer { |
| 24 | public: | 29 | public: |
| 25 | explicit ConstBufferEntry(const VideoCommon::Shader::ConstBuffer& entry, | 30 | explicit ConstBufferEntry(u32 max_offset, bool is_indirect, u32 index) |
| 26 | Maxwell::ShaderStage stage, const std::string& name, u32 index) | 31 | : VideoCommon::Shader::ConstBuffer{max_offset, is_indirect}, index{index} {} |
| 27 | : VideoCommon::Shader::ConstBuffer{entry}, stage{stage}, name{name}, index{index} {} | ||
| 28 | |||
| 29 | const std::string& GetName() const { | ||
| 30 | return name; | ||
| 31 | } | ||
| 32 | |||
| 33 | Maxwell::ShaderStage GetStage() const { | ||
| 34 | return stage; | ||
| 35 | } | ||
| 36 | 32 | ||
| 37 | u32 GetIndex() const { | 33 | u32 GetIndex() const { |
| 38 | return index; | 34 | return index; |
| 39 | } | 35 | } |
| 40 | 36 | ||
| 41 | private: | 37 | private: |
| 42 | std::string name; | ||
| 43 | Maxwell::ShaderStage stage{}; | ||
| 44 | u32 index{}; | 38 | u32 index{}; |
| 45 | }; | 39 | }; |
| 46 | 40 | ||
| 47 | class SamplerEntry : public VideoCommon::Shader::Sampler { | ||
| 48 | public: | ||
| 49 | explicit SamplerEntry(const VideoCommon::Shader::Sampler& entry, Maxwell::ShaderStage stage, | ||
| 50 | const std::string& name) | ||
| 51 | : VideoCommon::Shader::Sampler{entry}, stage{stage}, name{name} {} | ||
| 52 | |||
| 53 | const std::string& GetName() const { | ||
| 54 | return name; | ||
| 55 | } | ||
| 56 | |||
| 57 | Maxwell::ShaderStage GetStage() const { | ||
| 58 | return stage; | ||
| 59 | } | ||
| 60 | |||
| 61 | private: | ||
| 62 | std::string name; | ||
| 63 | Maxwell::ShaderStage stage{}; | ||
| 64 | }; | ||
| 65 | |||
| 66 | class GlobalMemoryEntry { | 41 | class GlobalMemoryEntry { |
| 67 | public: | 42 | public: |
| 68 | explicit GlobalMemoryEntry(u32 cbuf_index, u32 cbuf_offset, Maxwell::ShaderStage stage, | 43 | explicit GlobalMemoryEntry(u32 cbuf_index, u32 cbuf_offset) |
| 69 | std::string name) | 44 | : cbuf_index{cbuf_index}, cbuf_offset{cbuf_offset} {} |
| 70 | : cbuf_index{cbuf_index}, cbuf_offset{cbuf_offset}, stage{stage}, name{std::move(name)} {} | ||
| 71 | 45 | ||
| 72 | u32 GetCbufIndex() const { | 46 | u32 GetCbufIndex() const { |
| 73 | return cbuf_index; | 47 | return cbuf_index; |
| @@ -77,19 +51,9 @@ public: | |||
| 77 | return cbuf_offset; | 51 | return cbuf_offset; |
| 78 | } | 52 | } |
| 79 | 53 | ||
| 80 | const std::string& GetName() const { | ||
| 81 | return name; | ||
| 82 | } | ||
| 83 | |||
| 84 | Maxwell::ShaderStage GetStage() const { | ||
| 85 | return stage; | ||
| 86 | } | ||
| 87 | |||
| 88 | private: | 54 | private: |
| 89 | u32 cbuf_index{}; | 55 | u32 cbuf_index{}; |
| 90 | u32 cbuf_offset{}; | 56 | u32 cbuf_offset{}; |
| 91 | Maxwell::ShaderStage stage{}; | ||
| 92 | std::string name; | ||
| 93 | }; | 57 | }; |
| 94 | 58 | ||
| 95 | struct ShaderEntries { | 59 | struct ShaderEntries { |
| @@ -100,8 +64,6 @@ struct ShaderEntries { | |||
| 100 | std::size_t shader_length{}; | 64 | std::size_t shader_length{}; |
| 101 | }; | 65 | }; |
| 102 | 66 | ||
| 103 | using ProgramResult = std::pair<std::string, ShaderEntries>; | ||
| 104 | |||
| 105 | std::string GetCommonDeclarations(); | 67 | std::string GetCommonDeclarations(); |
| 106 | 68 | ||
| 107 | ProgramResult Decompile(const VideoCommon::Shader::ShaderIR& ir, Maxwell::ShaderStage stage, | 69 | ProgramResult Decompile(const VideoCommon::Shader::ShaderIR& ir, Maxwell::ShaderStage stage, |
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp new file mode 100644 index 000000000..81882822b --- /dev/null +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp | |||
| @@ -0,0 +1,656 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <cstring> | ||
| 8 | #include <fmt/format.h> | ||
| 9 | #include <lz4.h> | ||
| 10 | |||
| 11 | #include "common/assert.h" | ||
| 12 | #include "common/common_paths.h" | ||
| 13 | #include "common/common_types.h" | ||
| 14 | #include "common/file_util.h" | ||
| 15 | #include "common/logging/log.h" | ||
| 16 | #include "common/scm_rev.h" | ||
| 17 | |||
| 18 | #include "core/core.h" | ||
| 19 | #include "core/hle/kernel/process.h" | ||
| 20 | #include "core/settings.h" | ||
| 21 | |||
| 22 | #include "video_core/renderer_opengl/gl_shader_cache.h" | ||
| 23 | #include "video_core/renderer_opengl/gl_shader_disk_cache.h" | ||
| 24 | |||
| 25 | namespace OpenGL { | ||
| 26 | |||
| 27 | using ShaderCacheVersionHash = std::array<u8, 64>; | ||
| 28 | |||
| 29 | enum class TransferableEntryKind : u32 { | ||
| 30 | Raw, | ||
| 31 | Usage, | ||
| 32 | }; | ||
| 33 | |||
| 34 | enum class PrecompiledEntryKind : u32 { | ||
| 35 | Decompiled, | ||
| 36 | Dump, | ||
| 37 | }; | ||
| 38 | |||
| 39 | constexpr u32 NativeVersion = 1; | ||
| 40 | |||
| 41 | // Making sure sizes doesn't change by accident | ||
| 42 | static_assert(sizeof(BaseBindings) == 12); | ||
| 43 | static_assert(sizeof(ShaderDiskCacheUsage) == 24); | ||
| 44 | |||
| 45 | namespace { | ||
| 46 | |||
| 47 | ShaderCacheVersionHash GetShaderCacheVersionHash() { | ||
| 48 | ShaderCacheVersionHash hash{}; | ||
| 49 | const std::size_t length = std::min(std::strlen(Common::g_shader_cache_version), hash.size()); | ||
| 50 | std::memcpy(hash.data(), Common::g_shader_cache_version, length); | ||
| 51 | return hash; | ||
| 52 | } | ||
| 53 | |||
| 54 | template <typename T> | ||
| 55 | std::vector<u8> CompressData(const T* source, std::size_t source_size) { | ||
| 56 | if (source_size > LZ4_MAX_INPUT_SIZE) { | ||
| 57 | // Source size exceeds LZ4 maximum input size | ||
| 58 | return {}; | ||
| 59 | } | ||
| 60 | const auto source_size_int = static_cast<int>(source_size); | ||
| 61 | const int max_compressed_size = LZ4_compressBound(source_size_int); | ||
| 62 | std::vector<u8> compressed(max_compressed_size); | ||
| 63 | const int compressed_size = LZ4_compress_default(reinterpret_cast<const char*>(source), | ||
| 64 | reinterpret_cast<char*>(compressed.data()), | ||
| 65 | source_size_int, max_compressed_size); | ||
| 66 | if (compressed_size <= 0) { | ||
| 67 | // Compression failed | ||
| 68 | return {}; | ||
| 69 | } | ||
| 70 | compressed.resize(compressed_size); | ||
| 71 | return compressed; | ||
| 72 | } | ||
| 73 | |||
| 74 | std::vector<u8> DecompressData(const std::vector<u8>& compressed, std::size_t uncompressed_size) { | ||
| 75 | std::vector<u8> uncompressed(uncompressed_size); | ||
| 76 | const int size_check = LZ4_decompress_safe(reinterpret_cast<const char*>(compressed.data()), | ||
| 77 | reinterpret_cast<char*>(uncompressed.data()), | ||
| 78 | static_cast<int>(compressed.size()), | ||
| 79 | static_cast<int>(uncompressed.size())); | ||
| 80 | if (static_cast<int>(uncompressed_size) != size_check) { | ||
| 81 | // Decompression failed | ||
| 82 | return {}; | ||
| 83 | } | ||
| 84 | return uncompressed; | ||
| 85 | } | ||
| 86 | |||
| 87 | } // namespace | ||
| 88 | |||
| 89 | ShaderDiskCacheRaw::ShaderDiskCacheRaw(u64 unique_identifier, Maxwell::ShaderProgram program_type, | ||
| 90 | u32 program_code_size, u32 program_code_size_b, | ||
| 91 | ProgramCode program_code, ProgramCode program_code_b) | ||
| 92 | : unique_identifier{unique_identifier}, program_type{program_type}, | ||
| 93 | program_code_size{program_code_size}, program_code_size_b{program_code_size_b}, | ||
| 94 | program_code{std::move(program_code)}, program_code_b{std::move(program_code_b)} {} | ||
| 95 | |||
| 96 | ShaderDiskCacheRaw::ShaderDiskCacheRaw() = default; | ||
| 97 | |||
| 98 | ShaderDiskCacheRaw::~ShaderDiskCacheRaw() = default; | ||
| 99 | |||
| 100 | bool ShaderDiskCacheRaw::Load(FileUtil::IOFile& file) { | ||
| 101 | if (file.ReadBytes(&unique_identifier, sizeof(u64)) != sizeof(u64) || | ||
| 102 | file.ReadBytes(&program_type, sizeof(u32)) != sizeof(u32)) { | ||
| 103 | return false; | ||
| 104 | } | ||
| 105 | u32 program_code_size{}; | ||
| 106 | u32 program_code_size_b{}; | ||
| 107 | if (file.ReadBytes(&program_code_size, sizeof(u32)) != sizeof(u32) || | ||
| 108 | file.ReadBytes(&program_code_size_b, sizeof(u32)) != sizeof(u32)) { | ||
| 109 | return false; | ||
| 110 | } | ||
| 111 | |||
| 112 | program_code.resize(program_code_size); | ||
| 113 | program_code_b.resize(program_code_size_b); | ||
| 114 | |||
| 115 | if (file.ReadArray(program_code.data(), program_code_size) != program_code_size) | ||
| 116 | return false; | ||
| 117 | |||
| 118 | if (HasProgramA() && | ||
| 119 | file.ReadArray(program_code_b.data(), program_code_size_b) != program_code_size_b) { | ||
| 120 | return false; | ||
| 121 | } | ||
| 122 | return true; | ||
| 123 | } | ||
| 124 | |||
| 125 | bool ShaderDiskCacheRaw::Save(FileUtil::IOFile& file) const { | ||
| 126 | if (file.WriteObject(unique_identifier) != 1 || | ||
| 127 | file.WriteObject(static_cast<u32>(program_type)) != 1 || | ||
| 128 | file.WriteObject(program_code_size) != 1 || file.WriteObject(program_code_size_b) != 1) { | ||
| 129 | return false; | ||
| 130 | } | ||
| 131 | |||
| 132 | if (file.WriteArray(program_code.data(), program_code_size) != program_code_size) | ||
| 133 | return false; | ||
| 134 | |||
| 135 | if (HasProgramA() && | ||
| 136 | file.WriteArray(program_code_b.data(), program_code_size_b) != program_code_size_b) { | ||
| 137 | return false; | ||
| 138 | } | ||
| 139 | return true; | ||
| 140 | } | ||
| 141 | |||
| 142 | ShaderDiskCacheOpenGL::ShaderDiskCacheOpenGL(Core::System& system) : system{system} {} | ||
| 143 | |||
| 144 | std::optional<std::pair<std::vector<ShaderDiskCacheRaw>, std::vector<ShaderDiskCacheUsage>>> | ||
| 145 | ShaderDiskCacheOpenGL::LoadTransferable() { | ||
| 146 | // Skip games without title id | ||
| 147 | const bool has_title_id = system.CurrentProcess()->GetTitleID() != 0; | ||
| 148 | if (!Settings::values.use_disk_shader_cache || !has_title_id) | ||
| 149 | return {}; | ||
| 150 | tried_to_load = true; | ||
| 151 | |||
| 152 | FileUtil::IOFile file(GetTransferablePath(), "rb"); | ||
| 153 | if (!file.IsOpen()) { | ||
| 154 | LOG_INFO(Render_OpenGL, "No transferable shader cache found for game with title id={}", | ||
| 155 | GetTitleID()); | ||
| 156 | return {}; | ||
| 157 | } | ||
| 158 | |||
| 159 | u32 version{}; | ||
| 160 | if (file.ReadBytes(&version, sizeof(version)) != sizeof(version)) { | ||
| 161 | LOG_ERROR(Render_OpenGL, | ||
| 162 | "Failed to get transferable cache version for title id={} - skipping", | ||
| 163 | GetTitleID()); | ||
| 164 | return {}; | ||
| 165 | } | ||
| 166 | |||
| 167 | if (version < NativeVersion) { | ||
| 168 | LOG_INFO(Render_OpenGL, "Transferable shader cache is old - removing"); | ||
| 169 | file.Close(); | ||
| 170 | InvalidateTransferable(); | ||
| 171 | return {}; | ||
| 172 | } | ||
| 173 | if (version > NativeVersion) { | ||
| 174 | LOG_WARNING(Render_OpenGL, "Transferable shader cache was generated with a newer version " | ||
| 175 | "of the emulator - skipping"); | ||
| 176 | return {}; | ||
| 177 | } | ||
| 178 | |||
| 179 | // Version is valid, load the shaders | ||
| 180 | std::vector<ShaderDiskCacheRaw> raws; | ||
| 181 | std::vector<ShaderDiskCacheUsage> usages; | ||
| 182 | while (file.Tell() < file.GetSize()) { | ||
| 183 | TransferableEntryKind kind{}; | ||
| 184 | if (file.ReadBytes(&kind, sizeof(u32)) != sizeof(u32)) { | ||
| 185 | LOG_ERROR(Render_OpenGL, "Failed to read transferable file - skipping"); | ||
| 186 | return {}; | ||
| 187 | } | ||
| 188 | |||
| 189 | switch (kind) { | ||
| 190 | case TransferableEntryKind::Raw: { | ||
| 191 | ShaderDiskCacheRaw entry; | ||
| 192 | if (!entry.Load(file)) { | ||
| 193 | LOG_ERROR(Render_OpenGL, "Failed to load transferable raw entry - skipping"); | ||
| 194 | return {}; | ||
| 195 | } | ||
| 196 | transferable.insert({entry.GetUniqueIdentifier(), {}}); | ||
| 197 | raws.push_back(std::move(entry)); | ||
| 198 | break; | ||
| 199 | } | ||
| 200 | case TransferableEntryKind::Usage: { | ||
| 201 | ShaderDiskCacheUsage usage{}; | ||
| 202 | if (file.ReadBytes(&usage, sizeof(usage)) != sizeof(usage)) { | ||
| 203 | LOG_ERROR(Render_OpenGL, "Failed to load transferable usage entry - skipping"); | ||
| 204 | return {}; | ||
| 205 | } | ||
| 206 | usages.push_back(std::move(usage)); | ||
| 207 | break; | ||
| 208 | } | ||
| 209 | default: | ||
| 210 | LOG_ERROR(Render_OpenGL, "Unknown transferable shader cache entry kind={} - skipping", | ||
| 211 | static_cast<u32>(kind)); | ||
| 212 | return {}; | ||
| 213 | } | ||
| 214 | } | ||
| 215 | return {{raws, usages}}; | ||
| 216 | } | ||
| 217 | |||
| 218 | std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>, | ||
| 219 | std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>> | ||
| 220 | ShaderDiskCacheOpenGL::LoadPrecompiled() { | ||
| 221 | if (!IsUsable()) | ||
| 222 | return {}; | ||
| 223 | |||
| 224 | FileUtil::IOFile file(GetPrecompiledPath(), "rb"); | ||
| 225 | if (!file.IsOpen()) { | ||
| 226 | LOG_INFO(Render_OpenGL, "No precompiled shader cache found for game with title id={}", | ||
| 227 | GetTitleID()); | ||
| 228 | return {}; | ||
| 229 | } | ||
| 230 | |||
| 231 | const auto result = LoadPrecompiledFile(file); | ||
| 232 | if (!result) { | ||
| 233 | LOG_INFO(Render_OpenGL, | ||
| 234 | "Failed to load precompiled cache for game with title id={} - removing", | ||
| 235 | GetTitleID()); | ||
| 236 | file.Close(); | ||
| 237 | InvalidatePrecompiled(); | ||
| 238 | return {}; | ||
| 239 | } | ||
| 240 | return *result; | ||
| 241 | } | ||
| 242 | |||
| 243 | std::optional<std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>, | ||
| 244 | std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>>> | ||
| 245 | ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) { | ||
| 246 | ShaderCacheVersionHash file_hash{}; | ||
| 247 | if (file.ReadArray(file_hash.data(), file_hash.size()) != file_hash.size()) { | ||
| 248 | return {}; | ||
| 249 | } | ||
| 250 | if (GetShaderCacheVersionHash() != file_hash) { | ||
| 251 | LOG_INFO(Render_OpenGL, "Precompiled cache is from another version of the emulator"); | ||
| 252 | return {}; | ||
| 253 | } | ||
| 254 | |||
| 255 | std::unordered_map<u64, ShaderDiskCacheDecompiled> decompiled; | ||
| 256 | std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump> dumps; | ||
| 257 | while (file.Tell() < file.GetSize()) { | ||
| 258 | PrecompiledEntryKind kind{}; | ||
| 259 | if (file.ReadBytes(&kind, sizeof(u32)) != sizeof(u32)) { | ||
| 260 | return {}; | ||
| 261 | } | ||
| 262 | |||
| 263 | switch (kind) { | ||
| 264 | case PrecompiledEntryKind::Decompiled: { | ||
| 265 | u64 unique_identifier{}; | ||
| 266 | if (file.ReadBytes(&unique_identifier, sizeof(u64)) != sizeof(u64)) | ||
| 267 | return {}; | ||
| 268 | |||
| 269 | const auto entry = LoadDecompiledEntry(file); | ||
| 270 | if (!entry) | ||
| 271 | return {}; | ||
| 272 | decompiled.insert({unique_identifier, std::move(*entry)}); | ||
| 273 | break; | ||
| 274 | } | ||
| 275 | case PrecompiledEntryKind::Dump: { | ||
| 276 | ShaderDiskCacheUsage usage; | ||
| 277 | if (file.ReadBytes(&usage, sizeof(usage)) != sizeof(usage)) | ||
| 278 | return {}; | ||
| 279 | |||
| 280 | ShaderDiskCacheDump dump; | ||
| 281 | if (file.ReadBytes(&dump.binary_format, sizeof(u32)) != sizeof(u32)) | ||
| 282 | return {}; | ||
| 283 | |||
| 284 | u32 binary_length{}; | ||
| 285 | u32 compressed_size{}; | ||
| 286 | if (file.ReadBytes(&binary_length, sizeof(u32)) != sizeof(u32) || | ||
| 287 | file.ReadBytes(&compressed_size, sizeof(u32)) != sizeof(u32)) { | ||
| 288 | return {}; | ||
| 289 | } | ||
| 290 | |||
| 291 | std::vector<u8> compressed_binary(compressed_size); | ||
| 292 | if (file.ReadArray(compressed_binary.data(), compressed_binary.size()) != | ||
| 293 | compressed_binary.size()) { | ||
| 294 | return {}; | ||
| 295 | } | ||
| 296 | |||
| 297 | dump.binary = DecompressData(compressed_binary, binary_length); | ||
| 298 | if (dump.binary.empty()) { | ||
| 299 | return {}; | ||
| 300 | } | ||
| 301 | |||
| 302 | dumps.insert({usage, dump}); | ||
| 303 | break; | ||
| 304 | } | ||
| 305 | default: | ||
| 306 | return {}; | ||
| 307 | } | ||
| 308 | } | ||
| 309 | return {{decompiled, dumps}}; | ||
| 310 | } | ||
| 311 | |||
| 312 | std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEntry( | ||
| 313 | FileUtil::IOFile& file) { | ||
| 314 | u32 code_size{}; | ||
| 315 | u32 compressed_code_size{}; | ||
| 316 | if (file.ReadBytes(&code_size, sizeof(u32)) != sizeof(u32) || | ||
| 317 | file.ReadBytes(&compressed_code_size, sizeof(u32)) != sizeof(u32)) { | ||
| 318 | return {}; | ||
| 319 | } | ||
| 320 | |||
| 321 | std::vector<u8> compressed_code(compressed_code_size); | ||
| 322 | if (file.ReadArray(compressed_code.data(), compressed_code.size()) != compressed_code.size()) { | ||
| 323 | return {}; | ||
| 324 | } | ||
| 325 | |||
| 326 | const std::vector<u8> code = DecompressData(compressed_code, code_size); | ||
| 327 | if (code.empty()) { | ||
| 328 | return {}; | ||
| 329 | } | ||
| 330 | ShaderDiskCacheDecompiled entry; | ||
| 331 | entry.code = std::string(reinterpret_cast<const char*>(code.data()), code_size); | ||
| 332 | |||
| 333 | u32 const_buffers_count{}; | ||
| 334 | if (file.ReadBytes(&const_buffers_count, sizeof(u32)) != sizeof(u32)) | ||
| 335 | return {}; | ||
| 336 | for (u32 i = 0; i < const_buffers_count; ++i) { | ||
| 337 | u32 max_offset{}; | ||
| 338 | u32 index{}; | ||
| 339 | u8 is_indirect{}; | ||
| 340 | if (file.ReadBytes(&max_offset, sizeof(u32)) != sizeof(u32) || | ||
| 341 | file.ReadBytes(&index, sizeof(u32)) != sizeof(u32) || | ||
| 342 | file.ReadBytes(&is_indirect, sizeof(u8)) != sizeof(u8)) { | ||
| 343 | return {}; | ||
| 344 | } | ||
| 345 | entry.entries.const_buffers.emplace_back(max_offset, is_indirect != 0, index); | ||
| 346 | } | ||
| 347 | |||
| 348 | u32 samplers_count{}; | ||
| 349 | if (file.ReadBytes(&samplers_count, sizeof(u32)) != sizeof(u32)) | ||
| 350 | return {}; | ||
| 351 | for (u32 i = 0; i < samplers_count; ++i) { | ||
| 352 | u64 offset{}; | ||
| 353 | u64 index{}; | ||
| 354 | u32 type{}; | ||
| 355 | u8 is_array{}; | ||
| 356 | u8 is_shadow{}; | ||
| 357 | if (file.ReadBytes(&offset, sizeof(u64)) != sizeof(u64) || | ||
| 358 | file.ReadBytes(&index, sizeof(u64)) != sizeof(u64) || | ||
| 359 | file.ReadBytes(&type, sizeof(u32)) != sizeof(u32) || | ||
| 360 | file.ReadBytes(&is_array, sizeof(u8)) != sizeof(u8) || | ||
| 361 | file.ReadBytes(&is_shadow, sizeof(u8)) != sizeof(u8)) { | ||
| 362 | return {}; | ||
| 363 | } | ||
| 364 | entry.entries.samplers.emplace_back( | ||
| 365 | static_cast<std::size_t>(offset), static_cast<std::size_t>(index), | ||
| 366 | static_cast<Tegra::Shader::TextureType>(type), is_array != 0, is_shadow != 0); | ||
| 367 | } | ||
| 368 | |||
| 369 | u32 global_memory_count{}; | ||
| 370 | if (file.ReadBytes(&global_memory_count, sizeof(u32)) != sizeof(u32)) | ||
| 371 | return {}; | ||
| 372 | for (u32 i = 0; i < global_memory_count; ++i) { | ||
| 373 | u32 cbuf_index{}; | ||
| 374 | u32 cbuf_offset{}; | ||
| 375 | if (file.ReadBytes(&cbuf_index, sizeof(u32)) != sizeof(u32) || | ||
| 376 | file.ReadBytes(&cbuf_offset, sizeof(u32)) != sizeof(u32)) { | ||
| 377 | return {}; | ||
| 378 | } | ||
| 379 | entry.entries.global_memory_entries.emplace_back(cbuf_index, cbuf_offset); | ||
| 380 | } | ||
| 381 | |||
| 382 | for (auto& clip_distance : entry.entries.clip_distances) { | ||
| 383 | u8 clip_distance_raw{}; | ||
| 384 | if (file.ReadBytes(&clip_distance_raw, sizeof(u8)) != sizeof(u8)) | ||
| 385 | return {}; | ||
| 386 | clip_distance = clip_distance_raw != 0; | ||
| 387 | } | ||
| 388 | |||
| 389 | u64 shader_length{}; | ||
| 390 | if (file.ReadBytes(&shader_length, sizeof(u64)) != sizeof(u64)) | ||
| 391 | return {}; | ||
| 392 | entry.entries.shader_length = static_cast<std::size_t>(shader_length); | ||
| 393 | |||
| 394 | return entry; | ||
| 395 | } | ||
| 396 | |||
| 397 | bool ShaderDiskCacheOpenGL::SaveDecompiledFile(FileUtil::IOFile& file, u64 unique_identifier, | ||
| 398 | const std::string& code, | ||
| 399 | const std::vector<u8>& compressed_code, | ||
| 400 | const GLShader::ShaderEntries& entries) { | ||
| 401 | if (file.WriteObject(static_cast<u32>(PrecompiledEntryKind::Decompiled)) != 1 || | ||
| 402 | file.WriteObject(unique_identifier) != 1 || | ||
| 403 | file.WriteObject(static_cast<u32>(code.size())) != 1 || | ||
| 404 | file.WriteObject(static_cast<u32>(compressed_code.size())) != 1 || | ||
| 405 | file.WriteArray(compressed_code.data(), compressed_code.size()) != compressed_code.size()) { | ||
| 406 | return false; | ||
| 407 | } | ||
| 408 | |||
| 409 | if (file.WriteObject(static_cast<u32>(entries.const_buffers.size())) != 1) | ||
| 410 | return false; | ||
| 411 | for (const auto& cbuf : entries.const_buffers) { | ||
| 412 | if (file.WriteObject(static_cast<u32>(cbuf.GetMaxOffset())) != 1 || | ||
| 413 | file.WriteObject(static_cast<u32>(cbuf.GetIndex())) != 1 || | ||
| 414 | file.WriteObject(static_cast<u8>(cbuf.IsIndirect() ? 1 : 0)) != 1) { | ||
| 415 | return false; | ||
| 416 | } | ||
| 417 | } | ||
| 418 | |||
| 419 | if (file.WriteObject(static_cast<u32>(entries.samplers.size())) != 1) | ||
| 420 | return false; | ||
| 421 | for (const auto& sampler : entries.samplers) { | ||
| 422 | if (file.WriteObject(static_cast<u64>(sampler.GetOffset())) != 1 || | ||
| 423 | file.WriteObject(static_cast<u64>(sampler.GetIndex())) != 1 || | ||
| 424 | file.WriteObject(static_cast<u32>(sampler.GetType())) != 1 || | ||
| 425 | file.WriteObject(static_cast<u8>(sampler.IsArray() ? 1 : 0)) != 1 || | ||
| 426 | file.WriteObject(static_cast<u8>(sampler.IsShadow() ? 1 : 0)) != 1) { | ||
| 427 | return false; | ||
| 428 | } | ||
| 429 | } | ||
| 430 | |||
| 431 | if (file.WriteObject(static_cast<u32>(entries.global_memory_entries.size())) != 1) | ||
| 432 | return false; | ||
| 433 | for (const auto& gmem : entries.global_memory_entries) { | ||
| 434 | if (file.WriteObject(static_cast<u32>(gmem.GetCbufIndex())) != 1 || | ||
| 435 | file.WriteObject(static_cast<u32>(gmem.GetCbufOffset())) != 1) { | ||
| 436 | return false; | ||
| 437 | } | ||
| 438 | } | ||
| 439 | |||
| 440 | for (const bool clip_distance : entries.clip_distances) { | ||
| 441 | if (file.WriteObject(static_cast<u8>(clip_distance ? 1 : 0)) != 1) | ||
| 442 | return false; | ||
| 443 | } | ||
| 444 | |||
| 445 | return file.WriteObject(static_cast<u64>(entries.shader_length)) == 1; | ||
| 446 | } | ||
| 447 | |||
| 448 | void ShaderDiskCacheOpenGL::InvalidateTransferable() const { | ||
| 449 | if (!FileUtil::Delete(GetTransferablePath())) { | ||
| 450 | LOG_ERROR(Render_OpenGL, "Failed to invalidate transferable file={}", | ||
| 451 | GetTransferablePath()); | ||
| 452 | } | ||
| 453 | InvalidatePrecompiled(); | ||
| 454 | } | ||
| 455 | |||
| 456 | void ShaderDiskCacheOpenGL::InvalidatePrecompiled() const { | ||
| 457 | if (!FileUtil::Delete(GetPrecompiledPath())) { | ||
| 458 | LOG_ERROR(Render_OpenGL, "Failed to invalidate precompiled file={}", GetPrecompiledPath()); | ||
| 459 | } | ||
| 460 | } | ||
| 461 | |||
| 462 | void ShaderDiskCacheOpenGL::SaveRaw(const ShaderDiskCacheRaw& entry) { | ||
| 463 | if (!IsUsable()) | ||
| 464 | return; | ||
| 465 | |||
| 466 | const u64 id = entry.GetUniqueIdentifier(); | ||
| 467 | if (transferable.find(id) != transferable.end()) { | ||
| 468 | // The shader already exists | ||
| 469 | return; | ||
| 470 | } | ||
| 471 | |||
| 472 | FileUtil::IOFile file = AppendTransferableFile(); | ||
| 473 | if (!file.IsOpen()) | ||
| 474 | return; | ||
| 475 | if (file.WriteObject(TransferableEntryKind::Raw) != 1 || !entry.Save(file)) { | ||
| 476 | LOG_ERROR(Render_OpenGL, "Failed to save raw transferable cache entry - removing"); | ||
| 477 | file.Close(); | ||
| 478 | InvalidateTransferable(); | ||
| 479 | return; | ||
| 480 | } | ||
| 481 | transferable.insert({id, {}}); | ||
| 482 | } | ||
| 483 | |||
| 484 | void ShaderDiskCacheOpenGL::SaveUsage(const ShaderDiskCacheUsage& usage) { | ||
| 485 | if (!IsUsable()) | ||
| 486 | return; | ||
| 487 | |||
| 488 | const auto it = transferable.find(usage.unique_identifier); | ||
| 489 | ASSERT_MSG(it != transferable.end(), "Saving shader usage without storing raw previously"); | ||
| 490 | |||
| 491 | auto& usages{it->second}; | ||
| 492 | ASSERT(usages.find(usage) == usages.end()); | ||
| 493 | usages.insert(usage); | ||
| 494 | |||
| 495 | FileUtil::IOFile file = AppendTransferableFile(); | ||
| 496 | if (!file.IsOpen()) | ||
| 497 | return; | ||
| 498 | |||
| 499 | if (file.WriteObject(TransferableEntryKind::Usage) != 1 || file.WriteObject(usage) != 1) { | ||
| 500 | LOG_ERROR(Render_OpenGL, "Failed to save usage transferable cache entry - removing"); | ||
| 501 | file.Close(); | ||
| 502 | InvalidateTransferable(); | ||
| 503 | return; | ||
| 504 | } | ||
| 505 | } | ||
| 506 | |||
| 507 | void ShaderDiskCacheOpenGL::SaveDecompiled(u64 unique_identifier, const std::string& code, | ||
| 508 | const GLShader::ShaderEntries& entries) { | ||
| 509 | if (!IsUsable()) | ||
| 510 | return; | ||
| 511 | |||
| 512 | const std::vector<u8> compressed_code{CompressData(code.data(), code.size())}; | ||
| 513 | if (compressed_code.empty()) { | ||
| 514 | LOG_ERROR(Render_OpenGL, "Failed to compress GLSL code - skipping shader {:016x}", | ||
| 515 | unique_identifier); | ||
| 516 | return; | ||
| 517 | } | ||
| 518 | |||
| 519 | FileUtil::IOFile file = AppendPrecompiledFile(); | ||
| 520 | if (!file.IsOpen()) | ||
| 521 | return; | ||
| 522 | |||
| 523 | if (!SaveDecompiledFile(file, unique_identifier, code, compressed_code, entries)) { | ||
| 524 | LOG_ERROR(Render_OpenGL, | ||
| 525 | "Failed to save decompiled entry to the precompiled file - removing"); | ||
| 526 | file.Close(); | ||
| 527 | InvalidatePrecompiled(); | ||
| 528 | } | ||
| 529 | } | ||
| 530 | |||
| 531 | void ShaderDiskCacheOpenGL::SaveDump(const ShaderDiskCacheUsage& usage, GLuint program) { | ||
| 532 | if (!IsUsable()) | ||
| 533 | return; | ||
| 534 | |||
| 535 | GLint binary_length{}; | ||
| 536 | glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &binary_length); | ||
| 537 | |||
| 538 | GLenum binary_format{}; | ||
| 539 | std::vector<u8> binary(binary_length); | ||
| 540 | glGetProgramBinary(program, binary_length, nullptr, &binary_format, binary.data()); | ||
| 541 | |||
| 542 | const std::vector<u8> compressed_binary = CompressData(binary.data(), binary.size()); | ||
| 543 | if (compressed_binary.empty()) { | ||
| 544 | LOG_ERROR(Render_OpenGL, "Failed to compress binary program in shader={:016x}", | ||
| 545 | usage.unique_identifier); | ||
| 546 | return; | ||
| 547 | } | ||
| 548 | |||
| 549 | FileUtil::IOFile file = AppendPrecompiledFile(); | ||
| 550 | if (!file.IsOpen()) | ||
| 551 | return; | ||
| 552 | |||
| 553 | if (file.WriteObject(static_cast<u32>(PrecompiledEntryKind::Dump)) != 1 || | ||
| 554 | file.WriteObject(usage) != 1 || file.WriteObject(static_cast<u32>(binary_format)) != 1 || | ||
| 555 | file.WriteObject(static_cast<u32>(binary_length)) != 1 || | ||
| 556 | file.WriteObject(static_cast<u32>(compressed_binary.size())) != 1 || | ||
| 557 | file.WriteArray(compressed_binary.data(), compressed_binary.size()) != | ||
| 558 | compressed_binary.size()) { | ||
| 559 | LOG_ERROR(Render_OpenGL, "Failed to save binary program file in shader={:016x} - removing", | ||
| 560 | usage.unique_identifier); | ||
| 561 | file.Close(); | ||
| 562 | InvalidatePrecompiled(); | ||
| 563 | return; | ||
| 564 | } | ||
| 565 | } | ||
| 566 | |||
| 567 | bool ShaderDiskCacheOpenGL::IsUsable() const { | ||
| 568 | return tried_to_load && Settings::values.use_disk_shader_cache; | ||
| 569 | } | ||
| 570 | |||
| 571 | FileUtil::IOFile ShaderDiskCacheOpenGL::AppendTransferableFile() const { | ||
| 572 | if (!EnsureDirectories()) | ||
| 573 | return {}; | ||
| 574 | |||
| 575 | const auto transferable_path{GetTransferablePath()}; | ||
| 576 | const bool existed = FileUtil::Exists(transferable_path); | ||
| 577 | |||
| 578 | FileUtil::IOFile file(transferable_path, "ab"); | ||
| 579 | if (!file.IsOpen()) { | ||
| 580 | LOG_ERROR(Render_OpenGL, "Failed to open transferable cache in path={}", transferable_path); | ||
| 581 | return {}; | ||
| 582 | } | ||
| 583 | if (!existed || file.GetSize() == 0) { | ||
| 584 | // If the file didn't exist, write its version | ||
| 585 | if (file.WriteObject(NativeVersion) != 1) { | ||
| 586 | LOG_ERROR(Render_OpenGL, "Failed to write transferable cache version in path={}", | ||
| 587 | transferable_path); | ||
| 588 | return {}; | ||
| 589 | } | ||
| 590 | } | ||
| 591 | return file; | ||
| 592 | } | ||
| 593 | |||
| 594 | FileUtil::IOFile ShaderDiskCacheOpenGL::AppendPrecompiledFile() const { | ||
| 595 | if (!EnsureDirectories()) | ||
| 596 | return {}; | ||
| 597 | |||
| 598 | const auto precompiled_path{GetPrecompiledPath()}; | ||
| 599 | const bool existed = FileUtil::Exists(precompiled_path); | ||
| 600 | |||
| 601 | FileUtil::IOFile file(precompiled_path, "ab"); | ||
| 602 | if (!file.IsOpen()) { | ||
| 603 | LOG_ERROR(Render_OpenGL, "Failed to open precompiled cache in path={}", precompiled_path); | ||
| 604 | return {}; | ||
| 605 | } | ||
| 606 | |||
| 607 | if (!existed || file.GetSize() == 0) { | ||
| 608 | const auto hash{GetShaderCacheVersionHash()}; | ||
| 609 | if (file.WriteArray(hash.data(), hash.size()) != hash.size()) { | ||
| 610 | LOG_ERROR(Render_OpenGL, "Failed to write precompiled cache version hash in path={}", | ||
| 611 | precompiled_path); | ||
| 612 | return {}; | ||
| 613 | } | ||
| 614 | } | ||
| 615 | return file; | ||
| 616 | } | ||
| 617 | |||
| 618 | bool ShaderDiskCacheOpenGL::EnsureDirectories() const { | ||
| 619 | const auto CreateDir = [](const std::string& dir) { | ||
| 620 | if (!FileUtil::CreateDir(dir)) { | ||
| 621 | LOG_ERROR(Render_OpenGL, "Failed to create directory={}", dir); | ||
| 622 | return false; | ||
| 623 | } | ||
| 624 | return true; | ||
| 625 | }; | ||
| 626 | |||
| 627 | return CreateDir(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)) && | ||
| 628 | CreateDir(GetBaseDir()) && CreateDir(GetTransferableDir()) && | ||
| 629 | CreateDir(GetPrecompiledDir()); | ||
| 630 | } | ||
| 631 | |||
| 632 | std::string ShaderDiskCacheOpenGL::GetTransferablePath() const { | ||
| 633 | return FileUtil::SanitizePath(GetTransferableDir() + DIR_SEP_CHR + GetTitleID() + ".bin"); | ||
| 634 | } | ||
| 635 | |||
| 636 | std::string ShaderDiskCacheOpenGL::GetPrecompiledPath() const { | ||
| 637 | return FileUtil::SanitizePath(GetPrecompiledDir() + DIR_SEP_CHR + GetTitleID() + ".bin"); | ||
| 638 | } | ||
| 639 | |||
| 640 | std::string ShaderDiskCacheOpenGL::GetTransferableDir() const { | ||
| 641 | return GetBaseDir() + DIR_SEP "transferable"; | ||
| 642 | } | ||
| 643 | |||
| 644 | std::string ShaderDiskCacheOpenGL::GetPrecompiledDir() const { | ||
| 645 | return GetBaseDir() + DIR_SEP "precompiled"; | ||
| 646 | } | ||
| 647 | |||
| 648 | std::string ShaderDiskCacheOpenGL::GetBaseDir() const { | ||
| 649 | return FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir) + DIR_SEP "opengl"; | ||
| 650 | } | ||
| 651 | |||
| 652 | std::string ShaderDiskCacheOpenGL::GetTitleID() const { | ||
| 653 | return fmt::format("{:016X}", system.CurrentProcess()->GetTitleID()); | ||
| 654 | } | ||
| 655 | |||
| 656 | } // namespace OpenGL | ||
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.h b/src/video_core/renderer_opengl/gl_shader_disk_cache.h new file mode 100644 index 000000000..6be0c0547 --- /dev/null +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.h | |||
| @@ -0,0 +1,245 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <optional> | ||
| 8 | #include <string> | ||
| 9 | #include <tuple> | ||
| 10 | #include <unordered_map> | ||
| 11 | #include <unordered_set> | ||
| 12 | #include <utility> | ||
| 13 | #include <vector> | ||
| 14 | |||
| 15 | #include <glad/glad.h> | ||
| 16 | |||
| 17 | #include "common/assert.h" | ||
| 18 | #include "common/common_types.h" | ||
| 19 | #include "video_core/engines/maxwell_3d.h" | ||
| 20 | #include "video_core/renderer_opengl/gl_shader_gen.h" | ||
| 21 | |||
| 22 | namespace Core { | ||
| 23 | class System; | ||
| 24 | } | ||
| 25 | |||
| 26 | namespace FileUtil { | ||
| 27 | class IOFile; | ||
| 28 | } | ||
| 29 | |||
| 30 | namespace OpenGL { | ||
| 31 | |||
| 32 | using ProgramCode = std::vector<u64>; | ||
| 33 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; | ||
| 34 | |||
| 35 | /// Allocated bindings used by an OpenGL shader program | ||
| 36 | struct BaseBindings { | ||
| 37 | u32 cbuf{}; | ||
| 38 | u32 gmem{}; | ||
| 39 | u32 sampler{}; | ||
| 40 | |||
| 41 | bool operator==(const BaseBindings& rhs) const { | ||
| 42 | return std::tie(cbuf, gmem, sampler) == std::tie(rhs.cbuf, rhs.gmem, rhs.sampler); | ||
| 43 | } | ||
| 44 | |||
| 45 | bool operator!=(const BaseBindings& rhs) const { | ||
| 46 | return !operator==(rhs); | ||
| 47 | } | ||
| 48 | }; | ||
| 49 | |||
| 50 | /// Describes how a shader is used | ||
| 51 | struct ShaderDiskCacheUsage { | ||
| 52 | u64 unique_identifier{}; | ||
| 53 | BaseBindings bindings; | ||
| 54 | GLenum primitive{}; | ||
| 55 | |||
| 56 | bool operator==(const ShaderDiskCacheUsage& rhs) const { | ||
| 57 | return std::tie(unique_identifier, bindings, primitive) == | ||
| 58 | std::tie(rhs.unique_identifier, rhs.bindings, rhs.primitive); | ||
| 59 | } | ||
| 60 | |||
| 61 | bool operator!=(const ShaderDiskCacheUsage& rhs) const { | ||
| 62 | return !operator==(rhs); | ||
| 63 | } | ||
| 64 | }; | ||
| 65 | |||
| 66 | } // namespace OpenGL | ||
| 67 | |||
| 68 | namespace std { | ||
| 69 | |||
| 70 | template <> | ||
| 71 | struct hash<OpenGL::BaseBindings> { | ||
| 72 | std::size_t operator()(const OpenGL::BaseBindings& bindings) const { | ||
| 73 | return bindings.cbuf | bindings.gmem << 8 | bindings.sampler << 16; | ||
| 74 | } | ||
| 75 | }; | ||
| 76 | |||
| 77 | template <> | ||
| 78 | struct hash<OpenGL::ShaderDiskCacheUsage> { | ||
| 79 | std::size_t operator()(const OpenGL::ShaderDiskCacheUsage& usage) const { | ||
| 80 | return static_cast<std::size_t>(usage.unique_identifier) ^ | ||
| 81 | std::hash<OpenGL::BaseBindings>()(usage.bindings) ^ usage.primitive << 16; | ||
| 82 | } | ||
| 83 | }; | ||
| 84 | |||
| 85 | } // namespace std | ||
| 86 | |||
| 87 | namespace OpenGL { | ||
| 88 | |||
| 89 | /// Describes a shader how it's used by the guest GPU | ||
| 90 | class ShaderDiskCacheRaw { | ||
| 91 | public: | ||
| 92 | explicit ShaderDiskCacheRaw(u64 unique_identifier, Maxwell::ShaderProgram program_type, | ||
| 93 | u32 program_code_size, u32 program_code_size_b, | ||
| 94 | ProgramCode program_code, ProgramCode program_code_b); | ||
| 95 | ShaderDiskCacheRaw(); | ||
| 96 | ~ShaderDiskCacheRaw(); | ||
| 97 | |||
| 98 | bool Load(FileUtil::IOFile& file); | ||
| 99 | |||
| 100 | bool Save(FileUtil::IOFile& file) const; | ||
| 101 | |||
| 102 | u64 GetUniqueIdentifier() const { | ||
| 103 | return unique_identifier; | ||
| 104 | } | ||
| 105 | |||
| 106 | bool HasProgramA() const { | ||
| 107 | return program_type == Maxwell::ShaderProgram::VertexA; | ||
| 108 | } | ||
| 109 | |||
| 110 | Maxwell::ShaderProgram GetProgramType() const { | ||
| 111 | return program_type; | ||
| 112 | } | ||
| 113 | |||
| 114 | Maxwell::ShaderStage GetProgramStage() const { | ||
| 115 | switch (program_type) { | ||
| 116 | case Maxwell::ShaderProgram::VertexA: | ||
| 117 | case Maxwell::ShaderProgram::VertexB: | ||
| 118 | return Maxwell::ShaderStage::Vertex; | ||
| 119 | case Maxwell::ShaderProgram::TesselationControl: | ||
| 120 | return Maxwell::ShaderStage::TesselationControl; | ||
| 121 | case Maxwell::ShaderProgram::TesselationEval: | ||
| 122 | return Maxwell::ShaderStage::TesselationEval; | ||
| 123 | case Maxwell::ShaderProgram::Geometry: | ||
| 124 | return Maxwell::ShaderStage::Geometry; | ||
| 125 | case Maxwell::ShaderProgram::Fragment: | ||
| 126 | return Maxwell::ShaderStage::Fragment; | ||
| 127 | } | ||
| 128 | UNREACHABLE(); | ||
| 129 | } | ||
| 130 | |||
| 131 | const ProgramCode& GetProgramCode() const { | ||
| 132 | return program_code; | ||
| 133 | } | ||
| 134 | |||
| 135 | const ProgramCode& GetProgramCodeB() const { | ||
| 136 | return program_code_b; | ||
| 137 | } | ||
| 138 | |||
| 139 | private: | ||
| 140 | u64 unique_identifier{}; | ||
| 141 | Maxwell::ShaderProgram program_type{}; | ||
| 142 | u32 program_code_size{}; | ||
| 143 | u32 program_code_size_b{}; | ||
| 144 | |||
| 145 | ProgramCode program_code; | ||
| 146 | ProgramCode program_code_b; | ||
| 147 | }; | ||
| 148 | |||
| 149 | /// Contains decompiled data from a shader | ||
| 150 | struct ShaderDiskCacheDecompiled { | ||
| 151 | std::string code; | ||
| 152 | GLShader::ShaderEntries entries; | ||
| 153 | }; | ||
| 154 | |||
| 155 | /// Contains an OpenGL dumped binary program | ||
| 156 | struct ShaderDiskCacheDump { | ||
| 157 | GLenum binary_format; | ||
| 158 | std::vector<u8> binary; | ||
| 159 | }; | ||
| 160 | |||
| 161 | class ShaderDiskCacheOpenGL { | ||
| 162 | public: | ||
| 163 | explicit ShaderDiskCacheOpenGL(Core::System& system); | ||
| 164 | |||
| 165 | /// Loads transferable cache. If file has a old version or on failure, it deletes the file. | ||
| 166 | std::optional<std::pair<std::vector<ShaderDiskCacheRaw>, std::vector<ShaderDiskCacheUsage>>> | ||
| 167 | LoadTransferable(); | ||
| 168 | |||
| 169 | /// Loads current game's precompiled cache. Invalidates on failure. | ||
| 170 | std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>, | ||
| 171 | std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>> | ||
| 172 | LoadPrecompiled(); | ||
| 173 | |||
| 174 | /// Removes the transferable (and precompiled) cache file. | ||
| 175 | void InvalidateTransferable() const; | ||
| 176 | |||
| 177 | /// Removes the precompiled cache file. | ||
| 178 | void InvalidatePrecompiled() const; | ||
| 179 | |||
| 180 | /// Saves a raw dump to the transferable file. Checks for collisions. | ||
| 181 | void SaveRaw(const ShaderDiskCacheRaw& entry); | ||
| 182 | |||
| 183 | /// Saves shader usage to the transferable file. Does not check for collisions. | ||
| 184 | void SaveUsage(const ShaderDiskCacheUsage& usage); | ||
| 185 | |||
| 186 | /// Saves a decompiled entry to the precompiled file. Does not check for collisions. | ||
| 187 | void SaveDecompiled(u64 unique_identifier, const std::string& code, | ||
| 188 | const GLShader::ShaderEntries& entries); | ||
| 189 | |||
| 190 | /// Saves a dump entry to the precompiled file. Does not check for collisions. | ||
| 191 | void SaveDump(const ShaderDiskCacheUsage& usage, GLuint program); | ||
| 192 | |||
| 193 | private: | ||
| 194 | /// Loads the transferable cache. Returns empty on failure. | ||
| 195 | std::optional<std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>, | ||
| 196 | std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>>> | ||
| 197 | LoadPrecompiledFile(FileUtil::IOFile& file); | ||
| 198 | |||
| 199 | /// Loads a decompiled cache entry from the passed file. Returns empty on failure. | ||
| 200 | std::optional<ShaderDiskCacheDecompiled> LoadDecompiledEntry(FileUtil::IOFile& file); | ||
| 201 | |||
| 202 | /// 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, | ||
| 204 | const std::vector<u8>& compressed_code, | ||
| 205 | const GLShader::ShaderEntries& entries); | ||
| 206 | |||
| 207 | /// Returns if the cache can be used | ||
| 208 | bool IsUsable() const; | ||
| 209 | |||
| 210 | /// Opens current game's transferable file and write it's header if it doesn't exist | ||
| 211 | FileUtil::IOFile AppendTransferableFile() const; | ||
| 212 | |||
| 213 | /// Opens current game's precompiled file and write it's header if it doesn't exist | ||
| 214 | FileUtil::IOFile AppendPrecompiledFile() const; | ||
| 215 | |||
| 216 | /// Create shader disk cache directories. Returns true on success. | ||
| 217 | bool EnsureDirectories() const; | ||
| 218 | |||
| 219 | /// Gets current game's transferable file path | ||
| 220 | std::string GetTransferablePath() const; | ||
| 221 | |||
| 222 | /// Gets current game's precompiled file path | ||
| 223 | std::string GetPrecompiledPath() const; | ||
| 224 | |||
| 225 | /// Get user's transferable directory path | ||
| 226 | std::string GetTransferableDir() const; | ||
| 227 | |||
| 228 | /// Get user's precompiled directory path | ||
| 229 | std::string GetPrecompiledDir() const; | ||
| 230 | |||
| 231 | /// Get user's shader directory path | ||
| 232 | std::string GetBaseDir() const; | ||
| 233 | |||
| 234 | /// Get current game's title id | ||
| 235 | std::string GetTitleID() const; | ||
| 236 | |||
| 237 | // Copre system | ||
| 238 | Core::System& system; | ||
| 239 | // Stored transferable shaders | ||
| 240 | std::map<u64, std::unordered_set<ShaderDiskCacheUsage>> transferable; | ||
| 241 | // The cache has been loaded at boot | ||
| 242 | bool tried_to_load{}; | ||
| 243 | }; | ||
| 244 | |||
| 245 | } // namespace OpenGL \ No newline at end of file | ||
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h index ac5e6917b..fba8e681b 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.h +++ b/src/video_core/renderer_opengl/gl_shader_gen.h | |||
| @@ -26,12 +26,10 @@ struct ShaderSetup { | |||
| 26 | ProgramCode code; | 26 | ProgramCode code; |
| 27 | ProgramCode code_b; // Used for dual vertex shaders | 27 | ProgramCode code_b; // Used for dual vertex shaders |
| 28 | u64 unique_identifier; | 28 | u64 unique_identifier; |
| 29 | std::size_t real_size; | ||
| 30 | std::size_t real_size_b; | ||
| 31 | } program; | 29 | } program; |
| 32 | 30 | ||
| 33 | /// Used in scenarios where we have a dual vertex shaders | 31 | /// Used in scenarios where we have a dual vertex shaders |
| 34 | void SetProgramB(ProgramCode&& program_b) { | 32 | void SetProgramB(ProgramCode program_b) { |
| 35 | program.code_b = std::move(program_b); | 33 | program.code_b = std::move(program_b); |
| 36 | has_program_b = true; | 34 | has_program_b = true; |
| 37 | } | 35 | } |
diff --git a/src/video_core/renderer_opengl/gl_shader_util.h b/src/video_core/renderer_opengl/gl_shader_util.h index 285594f50..03b7548c2 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.h +++ b/src/video_core/renderer_opengl/gl_shader_util.h | |||
| @@ -47,7 +47,7 @@ GLuint LoadShader(const char* source, GLenum type); | |||
| 47 | * @returns Handle of the newly created OpenGL program object | 47 | * @returns Handle of the newly created OpenGL program object |
| 48 | */ | 48 | */ |
| 49 | template <typename... T> | 49 | template <typename... T> |
| 50 | GLuint LoadProgram(bool separable_program, T... shaders) { | 50 | GLuint LoadProgram(bool separable_program, bool hint_retrievable, T... shaders) { |
| 51 | // Link the program | 51 | // Link the program |
| 52 | LOG_DEBUG(Render_OpenGL, "Linking program..."); | 52 | LOG_DEBUG(Render_OpenGL, "Linking program..."); |
| 53 | 53 | ||
| @@ -58,6 +58,9 @@ GLuint LoadProgram(bool separable_program, T... shaders) { | |||
| 58 | if (separable_program) { | 58 | if (separable_program) { |
| 59 | glProgramParameteri(program_id, GL_PROGRAM_SEPARABLE, GL_TRUE); | 59 | glProgramParameteri(program_id, GL_PROGRAM_SEPARABLE, GL_TRUE); |
| 60 | } | 60 | } |
| 61 | if (hint_retrievable) { | ||
| 62 | glProgramParameteri(program_id, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE); | ||
| 63 | } | ||
| 61 | 64 | ||
| 62 | glLinkProgram(program_id); | 65 | glLinkProgram(program_id); |
| 63 | 66 | ||
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 5b09c38ea..6476a9e1a 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp | |||
| @@ -98,8 +98,8 @@ static std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(const float width, cons | |||
| 98 | return matrix; | 98 | return matrix; |
| 99 | } | 99 | } |
| 100 | 100 | ||
| 101 | RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& window) | 101 | RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& window, Core::System& system) |
| 102 | : VideoCore::RendererBase{window} {} | 102 | : VideoCore::RendererBase{window}, system{system} {} |
| 103 | 103 | ||
| 104 | RendererOpenGL::~RendererOpenGL() = default; | 104 | RendererOpenGL::~RendererOpenGL() = default; |
| 105 | 105 | ||
| @@ -250,7 +250,7 @@ void RendererOpenGL::CreateRasterizer() { | |||
| 250 | } | 250 | } |
| 251 | // Initialize sRGB Usage | 251 | // Initialize sRGB Usage |
| 252 | OpenGLState::ClearsRGBUsed(); | 252 | OpenGLState::ClearsRGBUsed(); |
| 253 | rasterizer = std::make_unique<RasterizerOpenGL>(render_window, screen_info); | 253 | rasterizer = std::make_unique<RasterizerOpenGL>(render_window, system, screen_info); |
| 254 | } | 254 | } |
| 255 | 255 | ||
| 256 | void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, | 256 | void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, |
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index 1665018db..7e13e566b 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h | |||
| @@ -12,6 +12,10 @@ | |||
| 12 | #include "video_core/renderer_opengl/gl_resource_manager.h" | 12 | #include "video_core/renderer_opengl/gl_resource_manager.h" |
| 13 | #include "video_core/renderer_opengl/gl_state.h" | 13 | #include "video_core/renderer_opengl/gl_state.h" |
| 14 | 14 | ||
| 15 | namespace Core { | ||
| 16 | class System; | ||
| 17 | } | ||
| 18 | |||
| 15 | namespace Core::Frontend { | 19 | namespace Core::Frontend { |
| 16 | class EmuWindow; | 20 | class EmuWindow; |
| 17 | } | 21 | } |
| @@ -41,7 +45,7 @@ struct ScreenInfo { | |||
| 41 | 45 | ||
| 42 | class RendererOpenGL : public VideoCore::RendererBase { | 46 | class RendererOpenGL : public VideoCore::RendererBase { |
| 43 | public: | 47 | public: |
| 44 | explicit RendererOpenGL(Core::Frontend::EmuWindow& window); | 48 | explicit RendererOpenGL(Core::Frontend::EmuWindow& window, Core::System& system); |
| 45 | ~RendererOpenGL() override; | 49 | ~RendererOpenGL() override; |
| 46 | 50 | ||
| 47 | /// Swap buffers (render frame) | 51 | /// Swap buffers (render frame) |
| @@ -72,6 +76,8 @@ private: | |||
| 72 | void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a, | 76 | void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a, |
| 73 | const TextureInfo& texture); | 77 | const TextureInfo& texture); |
| 74 | 78 | ||
| 79 | Core::System& system; | ||
| 80 | |||
| 75 | OpenGLState state; | 81 | OpenGLState state; |
| 76 | 82 | ||
| 77 | // OpenGL object IDs | 83 | // OpenGL object IDs |
diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h index 6e42e3dfb..ef0f3a106 100644 --- a/src/video_core/shader/shader_ir.h +++ b/src/video_core/shader/shader_ir.h | |||
| @@ -236,6 +236,11 @@ private: | |||
| 236 | 236 | ||
| 237 | class ConstBuffer { | 237 | class ConstBuffer { |
| 238 | public: | 238 | public: |
| 239 | explicit ConstBuffer(u32 max_offset, bool is_indirect) | ||
| 240 | : max_offset{max_offset}, is_indirect{is_indirect} {} | ||
| 241 | |||
| 242 | ConstBuffer() = default; | ||
| 243 | |||
| 239 | void MarkAsUsed(u64 offset) { | 244 | void MarkAsUsed(u64 offset) { |
| 240 | max_offset = std::max(max_offset, static_cast<u32>(offset)); | 245 | max_offset = std::max(max_offset, static_cast<u32>(offset)); |
| 241 | } | 246 | } |
| @@ -252,6 +257,10 @@ public: | |||
| 252 | return max_offset + sizeof(float); | 257 | return max_offset + sizeof(float); |
| 253 | } | 258 | } |
| 254 | 259 | ||
| 260 | u32 GetMaxOffset() const { | ||
| 261 | return max_offset; | ||
| 262 | } | ||
| 263 | |||
| 255 | private: | 264 | private: |
| 256 | u32 max_offset{}; | 265 | u32 max_offset{}; |
| 257 | bool is_indirect{}; | 266 | bool is_indirect{}; |
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index 0b8ccdd44..cb82ecf3f 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp | |||
| @@ -11,8 +11,9 @@ | |||
| 11 | 11 | ||
| 12 | namespace VideoCore { | 12 | namespace VideoCore { |
| 13 | 13 | ||
| 14 | std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window) { | 14 | std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window, |
| 15 | return std::make_unique<OpenGL::RendererOpenGL>(emu_window); | 15 | Core::System& system) { |
| 16 | return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system); | ||
| 16 | } | 17 | } |
| 17 | 18 | ||
| 18 | u16 GetResolutionScaleFactor(const RendererBase& renderer) { | 19 | u16 GetResolutionScaleFactor(const RendererBase& renderer) { |
diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h index 5b373bcb1..3c583f195 100644 --- a/src/video_core/video_core.h +++ b/src/video_core/video_core.h | |||
| @@ -6,6 +6,10 @@ | |||
| 6 | 6 | ||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | 8 | ||
| 9 | namespace Core { | ||
| 10 | class System; | ||
| 11 | } | ||
| 12 | |||
| 9 | namespace Core::Frontend { | 13 | namespace Core::Frontend { |
| 10 | class EmuWindow; | 14 | class EmuWindow; |
| 11 | } | 15 | } |
| @@ -20,7 +24,8 @@ class RendererBase; | |||
| 20 | * @note The returned renderer instance is simply allocated. Its Init() | 24 | * @note The returned renderer instance is simply allocated. Its Init() |
| 21 | * function still needs to be called to fully complete its setup. | 25 | * function still needs to be called to fully complete its setup. |
| 22 | */ | 26 | */ |
| 23 | std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window); | 27 | std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window, |
| 28 | Core::System& system); | ||
| 24 | 29 | ||
| 25 | u16 GetResolutionScaleFactor(const RendererBase& renderer); | 30 | u16 GetResolutionScaleFactor(const RendererBase& renderer); |
| 26 | 31 | ||
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index f74cb693a..73b04b749 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp | |||
| @@ -29,6 +29,15 @@ void EmuThread::run() { | |||
| 29 | 29 | ||
| 30 | stop_run = false; | 30 | stop_run = false; |
| 31 | 31 | ||
| 32 | emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); | ||
| 33 | |||
| 34 | Core::System::GetInstance().Renderer().Rasterizer().LoadDiskResources( | ||
| 35 | stop_run, [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) { | ||
| 36 | emit LoadProgress(stage, value, total); | ||
| 37 | }); | ||
| 38 | |||
| 39 | emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); | ||
| 40 | |||
| 32 | // holds whether the cpu was running during the last iteration, | 41 | // holds whether the cpu was running during the last iteration, |
| 33 | // so that the DebugModeLeft signal can be emitted before the | 42 | // so that the DebugModeLeft signal can be emitted before the |
| 34 | // next execution step | 43 | // next execution step |
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index d1f37e503..7226e690e 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h | |||
| @@ -22,6 +22,10 @@ class GGLWidgetInternal; | |||
| 22 | class GMainWindow; | 22 | class GMainWindow; |
| 23 | class GRenderWindow; | 23 | class GRenderWindow; |
| 24 | 24 | ||
| 25 | namespace VideoCore { | ||
| 26 | enum class LoadCallbackStage; | ||
| 27 | } | ||
| 28 | |||
| 25 | class EmuThread : public QThread { | 29 | class EmuThread : public QThread { |
| 26 | Q_OBJECT | 30 | Q_OBJECT |
| 27 | 31 | ||
| @@ -75,7 +79,7 @@ public: | |||
| 75 | private: | 79 | private: |
| 76 | bool exec_step = false; | 80 | bool exec_step = false; |
| 77 | bool running = false; | 81 | bool running = false; |
| 78 | std::atomic<bool> stop_run{false}; | 82 | std::atomic_bool stop_run{false}; |
| 79 | std::mutex running_mutex; | 83 | std::mutex running_mutex; |
| 80 | std::condition_variable running_cv; | 84 | std::condition_variable running_cv; |
| 81 | 85 | ||
| @@ -101,6 +105,8 @@ signals: | |||
| 101 | void DebugModeLeft(); | 105 | void DebugModeLeft(); |
| 102 | 106 | ||
| 103 | void ErrorThrown(Core::System::ResultStatus, std::string); | 107 | void ErrorThrown(Core::System::ResultStatus, std::string); |
| 108 | |||
| 109 | void LoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total); | ||
| 104 | }; | 110 | }; |
| 105 | 111 | ||
| 106 | class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow { | 112 | class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow { |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index ddf4cf552..e9546dadf 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -370,6 +370,8 @@ void Config::ReadValues() { | |||
| 370 | Settings::values.resolution_factor = qt_config->value("resolution_factor", 1.0).toFloat(); | 370 | Settings::values.resolution_factor = qt_config->value("resolution_factor", 1.0).toFloat(); |
| 371 | Settings::values.use_frame_limit = qt_config->value("use_frame_limit", true).toBool(); | 371 | Settings::values.use_frame_limit = qt_config->value("use_frame_limit", true).toBool(); |
| 372 | Settings::values.frame_limit = qt_config->value("frame_limit", 100).toInt(); | 372 | Settings::values.frame_limit = qt_config->value("frame_limit", 100).toInt(); |
| 373 | Settings::values.use_disk_shader_cache = | ||
| 374 | qt_config->value("use_disk_shader_cache", false).toBool(); | ||
| 373 | Settings::values.use_accurate_gpu_emulation = | 375 | Settings::values.use_accurate_gpu_emulation = |
| 374 | qt_config->value("use_accurate_gpu_emulation", false).toBool(); | 376 | qt_config->value("use_accurate_gpu_emulation", false).toBool(); |
| 375 | 377 | ||
| @@ -629,6 +631,7 @@ void Config::SaveValues() { | |||
| 629 | qt_config->setValue("resolution_factor", (double)Settings::values.resolution_factor); | 631 | qt_config->setValue("resolution_factor", (double)Settings::values.resolution_factor); |
| 630 | qt_config->setValue("use_frame_limit", Settings::values.use_frame_limit); | 632 | qt_config->setValue("use_frame_limit", Settings::values.use_frame_limit); |
| 631 | qt_config->setValue("frame_limit", Settings::values.frame_limit); | 633 | qt_config->setValue("frame_limit", Settings::values.frame_limit); |
| 634 | qt_config->setValue("use_disk_shader_cache", Settings::values.use_disk_shader_cache); | ||
| 632 | qt_config->setValue("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation); | 635 | qt_config->setValue("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation); |
| 633 | 636 | ||
| 634 | // Cast to double because Qt's written float values are not human-readable | 637 | // Cast to double because Qt's written float values are not human-readable |
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index d21f95469..0f5dd534b 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp | |||
| @@ -73,6 +73,7 @@ void ConfigureGraphics::setConfiguration() { | |||
| 73 | static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor))); | 73 | static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor))); |
| 74 | ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit); | 74 | ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit); |
| 75 | ui->frame_limit->setValue(Settings::values.frame_limit); | 75 | ui->frame_limit->setValue(Settings::values.frame_limit); |
| 76 | ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache); | ||
| 76 | ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation); | 77 | ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation); |
| 77 | UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, | 78 | UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, |
| 78 | Settings::values.bg_blue)); | 79 | Settings::values.bg_blue)); |
| @@ -83,6 +84,7 @@ void ConfigureGraphics::applyConfiguration() { | |||
| 83 | ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex())); | 84 | ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex())); |
| 84 | Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked(); | 85 | Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked(); |
| 85 | Settings::values.frame_limit = ui->frame_limit->value(); | 86 | Settings::values.frame_limit = ui->frame_limit->value(); |
| 87 | Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked(); | ||
| 86 | Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked(); | 88 | Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked(); |
| 87 | Settings::values.bg_red = static_cast<float>(bg_color.redF()); | 89 | Settings::values.bg_red = static_cast<float>(bg_color.redF()); |
| 88 | Settings::values.bg_green = static_cast<float>(bg_color.greenF()); | 90 | Settings::values.bg_green = static_cast<float>(bg_color.greenF()); |
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index e278cdd05..824f5810a 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui | |||
| @@ -50,6 +50,13 @@ | |||
| 50 | </layout> | 50 | </layout> |
| 51 | </item> | 51 | </item> |
| 52 | <item> | 52 | <item> |
| 53 | <widget class="QCheckBox" name="use_disk_shader_cache"> | ||
| 54 | <property name="text"> | ||
| 55 | <string>Use disk shader cache</string> | ||
| 56 | </property> | ||
| 57 | </widget> | ||
| 58 | </item> | ||
| 59 | <item> | ||
| 53 | <widget class="QCheckBox" name="use_accurate_gpu_emulation"> | 60 | <widget class="QCheckBox" name="use_accurate_gpu_emulation"> |
| 54 | <property name="text"> | 61 | <property name="text"> |
| 55 | <string>Use accurate GPU emulation (slow)</string> | 62 | <string>Use accurate GPU emulation (slow)</string> |
diff --git a/src/yuzu/loading_screen.cpp b/src/yuzu/loading_screen.cpp index 907aac4f1..86f6d0165 100644 --- a/src/yuzu/loading_screen.cpp +++ b/src/yuzu/loading_screen.cpp | |||
| @@ -43,6 +43,7 @@ QProgressBar { | |||
| 43 | } | 43 | } |
| 44 | QProgressBar::chunk { | 44 | QProgressBar::chunk { |
| 45 | background-color: #0ab9e6; | 45 | background-color: #0ab9e6; |
| 46 | width: 1px; | ||
| 46 | })"; | 47 | })"; |
| 47 | 48 | ||
| 48 | constexpr const char PROGRESSBAR_STYLE_BUILD[] = R"( | 49 | constexpr const char PROGRESSBAR_STYLE_BUILD[] = R"( |
| @@ -53,7 +54,8 @@ QProgressBar { | |||
| 53 | padding: 2px; | 54 | padding: 2px; |
| 54 | } | 55 | } |
| 55 | QProgressBar::chunk { | 56 | QProgressBar::chunk { |
| 56 | background-color: #ff3c28; | 57 | background-color: #ff3c28; |
| 58 | width: 1px; | ||
| 57 | })"; | 59 | })"; |
| 58 | 60 | ||
| 59 | constexpr const char PROGRESSBAR_STYLE_COMPLETE[] = R"( | 61 | constexpr const char PROGRESSBAR_STYLE_COMPLETE[] = R"( |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 485e29de2..1d460c189 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -887,6 +887,9 @@ void GMainWindow::BootGame(const QString& filename) { | |||
| 887 | connect(emu_thread.get(), &EmuThread::DebugModeLeft, waitTreeWidget, | 887 | connect(emu_thread.get(), &EmuThread::DebugModeLeft, waitTreeWidget, |
| 888 | &WaitTreeWidget::OnDebugModeLeft, Qt::BlockingQueuedConnection); | 888 | &WaitTreeWidget::OnDebugModeLeft, Qt::BlockingQueuedConnection); |
| 889 | 889 | ||
| 890 | connect(emu_thread.get(), &EmuThread::LoadProgress, loading_screen, | ||
| 891 | &LoadingScreen::OnLoadProgress, Qt::QueuedConnection); | ||
| 892 | |||
| 890 | // Update the GUI | 893 | // Update the GUI |
| 891 | if (ui.action_Single_Window_Mode->isChecked()) { | 894 | if (ui.action_Single_Window_Mode->isChecked()) { |
| 892 | game_list->hide(); | 895 | game_list->hide(); |
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 7a77f76e8..ff05b3179 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp | |||
| @@ -350,6 +350,8 @@ void Config::ReadValues() { | |||
| 350 | Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true); | 350 | Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true); |
| 351 | Settings::values.frame_limit = | 351 | Settings::values.frame_limit = |
| 352 | static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100)); | 352 | static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100)); |
| 353 | Settings::values.use_disk_shader_cache = | ||
| 354 | sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false); | ||
| 353 | Settings::values.use_accurate_gpu_emulation = | 355 | Settings::values.use_accurate_gpu_emulation = |
| 354 | sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false); | 356 | sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false); |
| 355 | 357 | ||
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index ba51a4a51..a81986f8e 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h | |||
| @@ -110,6 +110,10 @@ use_frame_limit = | |||
| 110 | # 1 - 9999: Speed limit as a percentage of target game speed. 100 (default) | 110 | # 1 - 9999: Speed limit as a percentage of target game speed. 100 (default) |
| 111 | frame_limit = | 111 | frame_limit = |
| 112 | 112 | ||
| 113 | # Whether to use disk based shader cache | ||
| 114 | # 0 (default): Off, 1 : On | ||
| 115 | use_disk_shader_cache = | ||
| 116 | |||
| 113 | # Whether to use accurate GPU emulation | 117 | # Whether to use accurate GPU emulation |
| 114 | # 0 (default): Off (fast), 1 : On (slow) | 118 | # 0 (default): Off (fast), 1 : On (slow) |
| 115 | use_accurate_gpu_emulation = | 119 | use_accurate_gpu_emulation = |
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 806127b12..c34b5467f 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp | |||
| @@ -28,6 +28,7 @@ | |||
| 28 | #include "core/loader/loader.h" | 28 | #include "core/loader/loader.h" |
| 29 | #include "core/settings.h" | 29 | #include "core/settings.h" |
| 30 | #include "core/telemetry_session.h" | 30 | #include "core/telemetry_session.h" |
| 31 | #include "video_core/renderer_base.h" | ||
| 31 | #include "yuzu_cmd/config.h" | 32 | #include "yuzu_cmd/config.h" |
| 32 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" | 33 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" |
| 33 | 34 | ||
| @@ -217,6 +218,8 @@ int main(int argc, char** argv) { | |||
| 217 | 218 | ||
| 218 | Core::Telemetry().AddField(Telemetry::FieldType::App, "Frontend", "SDL"); | 219 | Core::Telemetry().AddField(Telemetry::FieldType::App, "Frontend", "SDL"); |
| 219 | 220 | ||
| 221 | system.Renderer().Rasterizer().LoadDiskResources(); | ||
| 222 | |||
| 220 | while (emu_window->IsOpen()) { | 223 | while (emu_window->IsOpen()) { |
| 221 | system.RunLoop(); | 224 | system.RunLoop(); |
| 222 | } | 225 | } |