summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/CMakeLists.txt93
-rw-r--r--src/common/common_paths.h1
-rw-r--r--src/common/file_util.cpp1
-rw-r--r--src/common/file_util.h1
-rw-r--r--src/common/scm_rev.cpp.in2
-rw-r--r--src/common/scm_rev.h1
-rw-r--r--src/core/core.cpp3
-rw-r--r--src/core/settings.h1
-rw-r--r--src/core/telemetry_session.cpp2
-rw-r--r--src/video_core/CMakeLists.txt4
-rw-r--r--src/video_core/rasterizer_interface.h5
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp21
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h10
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.cpp5
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.h6
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp478
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h89
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp7
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.h56
-rw-r--r--src/video_core/renderer_opengl/gl_shader_disk_cache.cpp656
-rw-r--r--src/video_core/renderer_opengl/gl_shader_disk_cache.h245
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.h4
-rw-r--r--src/video_core/renderer_opengl/gl_shader_util.h5
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp6
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h8
-rw-r--r--src/video_core/shader/shader_ir.h9
-rw-r--r--src/video_core/video_core.cpp5
-rw-r--r--src/video_core/video_core.h7
-rw-r--r--src/yuzu/bootmanager.cpp9
-rw-r--r--src/yuzu/bootmanager.h8
-rw-r--r--src/yuzu/configuration/config.cpp3
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp2
-rw-r--r--src/yuzu/configuration/configure_graphics.ui7
-rw-r--r--src/yuzu/loading_screen.cpp4
-rw-r--r--src/yuzu/main.cpp3
-rw-r--r--src/yuzu_cmd/config.cpp2
-rw-r--r--src/yuzu_cmd/default_ini.h4
-rw-r--r--src/yuzu_cmd/yuzu.cpp3
38 files changed, 1529 insertions, 247 deletions
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
3set(REPO_NAME "") 3# could affect the result, but much more unlikely than the following files. Keeping a list of files
4set(BUILD_VERSION "0") 4# like this allows for much better caching since it doesn't force the user to recompile binary shaders every update
5if ($ENV{CI}) 5set(VIDEO_CORE "${CMAKE_SOURCE_DIR}/src/video_core")
6 if ($ENV{TRAVIS}) 6if (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()
38endif() 14endif()
39configure_file("${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" "${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp" @ONLY) 15add_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
41add_library(common STATIC 68add_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
15namespace Common { 16namespace Common {
16 17
@@ -21,6 +22,7 @@ const char g_build_name[] = BUILD_NAME;
21const char g_build_date[] = BUILD_DATE; 22const char g_build_date[] = BUILD_DATE;
22const char g_build_fullname[] = BUILD_FULLNAME; 23const char g_build_fullname[] = BUILD_FULLNAME;
23const char g_build_version[] = BUILD_VERSION; 24const char g_build_version[] = BUILD_VERSION;
25const 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[];
13extern const char g_build_date[]; 13extern const char g_build_date[];
14extern const char g_build_fullname[]; 14extern const char g_build_fullname[];
15extern const char g_build_version[]; 15extern const char g_build_version[];
16extern 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
102create_target_directory_groups(video_core) 104create_target_directory_groups(video_core)
103 105
104target_link_libraries(video_core PUBLIC common core) 106target_link_libraries(video_core PUBLIC common core)
105target_link_libraries(video_core PRIVATE glad) 107target_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
102RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo& info) 103RasterizerOpenGL::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
450void RasterizerOpenGL::UpdatePagesCachedCount(VAddr addr, u64 size, int delta) { 452void 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
482void RasterizerOpenGL::LoadDiskResources(const std::atomic_bool& stop_loading,
483 const VideoCore::DiskResourceLoadCallback& callback) {
484 shader_cache.LoadDiskCache(stop_loading, callback);
485}
486
480std::pair<bool, bool> RasterizerOpenGL::ConfigureFramebuffers( 487std::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
37namespace Core {
38class System;
39}
40
36namespace Core::Frontend { 41namespace Core::Frontend {
37class EmuWindow; 42class EmuWindow;
38} 43}
@@ -45,7 +50,8 @@ struct FramebufferCacheKey;
45 50
46class RasterizerOpenGL : public VideoCore::RasterizerInterface { 51class RasterizerOpenGL : public VideoCore::RasterizerInterface {
47public: 52public:
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
73void OGLProgram::CreateFromSource(const char* vert_shader, const char* geo_shader, 73void 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
87void OGLProgram::Release() { 88void 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
20using VideoCommon::Shader::ProgramCode; 21using VideoCommon::Shader::ProgramCode;
21 22
23// One UBO is always reserved for emulation values
24constexpr u32 RESERVED_UBOS = 1;
25
26struct UnspecializedShader {
27 std::string code;
28 GLShader::ShaderEntries entries;
29 Maxwell::ShaderProgram program_type;
30};
31
32namespace {
33
22/// Gets the address for the specified shader stage program 34/// Gets the address for the specified shader stage program
23static VAddr GetShaderAddress(Maxwell::ShaderProgram program) { 35VAddr 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
33static ProgramCode GetShaderCode(VAddr addr) { 45ProgramCode 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
54CachedShader::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)} { 67constexpr 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
75constexpr 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: 98std::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
118u64 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
137GLShader::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
164CachedProgram 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
201std::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
217CachedShader::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
246CachedShader::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
88std::tuple<GLuint, BaseBindings> CachedShader::GetProgramHandle(GLenum primitive_mode, 258std::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
118std::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
139GLuint CachedShader::GetGeometryShader(GLenum primitive_mode, BaseBindings base_bindings) { 287GLuint 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
168GLuint CachedShader::LazyGeometryProgram(OGLProgram& target_program, BaseBindings base_bindings, 313GLuint 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
186static bool IsSchedInstruction(std::size_t offset, std::size_t main_offset) { 331CachedProgram 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
193static std::size_t CalculateProgramSize(const GLShader::ProgramCode& program) { 340ShaderDiskCacheUsage 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]; 345ShaderCacheOpenGL::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; 348void 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
210void CachedShader::CalculateProperties() { 410CachedProgram 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
226ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer) : RasterizerCache{rasterizer} {} 434std::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
228Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { 484Shader 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
24namespace Core {
25class System;
26} // namespace Core
27
21namespace OpenGL { 28namespace OpenGL {
22 29
23class CachedShader; 30class CachedShader;
24class RasterizerOpenGL; 31class RasterizerOpenGL;
32struct UnspecializedShader;
25 33
26using Shader = std::shared_ptr<CachedShader>; 34using Shader = std::shared_ptr<CachedShader>;
35using CachedProgram = std::shared_ptr<OGLProgram>;
27using Maxwell = Tegra::Engines::Maxwell3D::Regs; 36using Maxwell = Tegra::Engines::Maxwell3D::Regs;
28 37using PrecompiledPrograms = std::unordered_map<ShaderDiskCacheUsage, CachedProgram>;
29struct BaseBindings { 38using 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
39class CachedShader final : public RasterizerCacheObject { 40class CachedShader final : public RasterizerCacheObject {
40public: 41public:
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
102class ShaderCacheOpenGL final : public RasterizerCache<Shader> { 113class ShaderCacheOpenGL final : public RasterizerCache<Shader> {
103public: 114public:
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
109private: 124private:
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
19namespace OpenGL::GLShader { 20namespace OpenGL::GLShader {
20 21
22struct ShaderEntries;
23
21using Maxwell = Tegra::Engines::Maxwell3D::Regs; 24using Maxwell = Tegra::Engines::Maxwell3D::Regs;
25using ProgramResult = std::pair<std::string, ShaderEntries>;
26using SamplerEntry = VideoCommon::Shader::Sampler;
22 27
23class ConstBufferEntry : public VideoCommon::Shader::ConstBuffer { 28class ConstBufferEntry : public VideoCommon::Shader::ConstBuffer {
24public: 29public:
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
41private: 37private:
42 std::string name;
43 Maxwell::ShaderStage stage{};
44 u32 index{}; 38 u32 index{};
45}; 39};
46 40
47class SamplerEntry : public VideoCommon::Shader::Sampler {
48public:
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
61private:
62 std::string name;
63 Maxwell::ShaderStage stage{};
64};
65
66class GlobalMemoryEntry { 41class GlobalMemoryEntry {
67public: 42public:
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
88private: 54private:
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
95struct ShaderEntries { 59struct ShaderEntries {
@@ -100,8 +64,6 @@ struct ShaderEntries {
100 std::size_t shader_length{}; 64 std::size_t shader_length{};
101}; 65};
102 66
103using ProgramResult = std::pair<std::string, ShaderEntries>;
104
105std::string GetCommonDeclarations(); 67std::string GetCommonDeclarations();
106 68
107ProgramResult Decompile(const VideoCommon::Shader::ShaderIR& ir, Maxwell::ShaderStage stage, 69ProgramResult 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
25namespace OpenGL {
26
27using ShaderCacheVersionHash = std::array<u8, 64>;
28
29enum class TransferableEntryKind : u32 {
30 Raw,
31 Usage,
32};
33
34enum class PrecompiledEntryKind : u32 {
35 Decompiled,
36 Dump,
37};
38
39constexpr u32 NativeVersion = 1;
40
41// Making sure sizes doesn't change by accident
42static_assert(sizeof(BaseBindings) == 12);
43static_assert(sizeof(ShaderDiskCacheUsage) == 24);
44
45namespace {
46
47ShaderCacheVersionHash 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
54template <typename T>
55std::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
74std::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
89ShaderDiskCacheRaw::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
96ShaderDiskCacheRaw::ShaderDiskCacheRaw() = default;
97
98ShaderDiskCacheRaw::~ShaderDiskCacheRaw() = default;
99
100bool 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
125bool 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
142ShaderDiskCacheOpenGL::ShaderDiskCacheOpenGL(Core::System& system) : system{system} {}
143
144std::optional<std::pair<std::vector<ShaderDiskCacheRaw>, std::vector<ShaderDiskCacheUsage>>>
145ShaderDiskCacheOpenGL::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
218std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>,
219 std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>>
220ShaderDiskCacheOpenGL::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
243std::optional<std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>,
244 std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>>>
245ShaderDiskCacheOpenGL::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
312std::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
397bool 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
448void 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
456void ShaderDiskCacheOpenGL::InvalidatePrecompiled() const {
457 if (!FileUtil::Delete(GetPrecompiledPath())) {
458 LOG_ERROR(Render_OpenGL, "Failed to invalidate precompiled file={}", GetPrecompiledPath());
459 }
460}
461
462void 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
484void 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
507void 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
531void 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
567bool ShaderDiskCacheOpenGL::IsUsable() const {
568 return tried_to_load && Settings::values.use_disk_shader_cache;
569}
570
571FileUtil::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
594FileUtil::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
618bool 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
632std::string ShaderDiskCacheOpenGL::GetTransferablePath() const {
633 return FileUtil::SanitizePath(GetTransferableDir() + DIR_SEP_CHR + GetTitleID() + ".bin");
634}
635
636std::string ShaderDiskCacheOpenGL::GetPrecompiledPath() const {
637 return FileUtil::SanitizePath(GetPrecompiledDir() + DIR_SEP_CHR + GetTitleID() + ".bin");
638}
639
640std::string ShaderDiskCacheOpenGL::GetTransferableDir() const {
641 return GetBaseDir() + DIR_SEP "transferable";
642}
643
644std::string ShaderDiskCacheOpenGL::GetPrecompiledDir() const {
645 return GetBaseDir() + DIR_SEP "precompiled";
646}
647
648std::string ShaderDiskCacheOpenGL::GetBaseDir() const {
649 return FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir) + DIR_SEP "opengl";
650}
651
652std::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
22namespace Core {
23class System;
24}
25
26namespace FileUtil {
27class IOFile;
28}
29
30namespace OpenGL {
31
32using ProgramCode = std::vector<u64>;
33using Maxwell = Tegra::Engines::Maxwell3D::Regs;
34
35/// Allocated bindings used by an OpenGL shader program
36struct 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
51struct 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
68namespace std {
69
70template <>
71struct 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
77template <>
78struct 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
87namespace OpenGL {
88
89/// Describes a shader how it's used by the guest GPU
90class ShaderDiskCacheRaw {
91public:
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
139private:
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
150struct ShaderDiskCacheDecompiled {
151 std::string code;
152 GLShader::ShaderEntries entries;
153};
154
155/// Contains an OpenGL dumped binary program
156struct ShaderDiskCacheDump {
157 GLenum binary_format;
158 std::vector<u8> binary;
159};
160
161class ShaderDiskCacheOpenGL {
162public:
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
193private:
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 */
49template <typename... T> 49template <typename... T>
50GLuint LoadProgram(bool separable_program, T... shaders) { 50GLuint 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
101RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& window) 101RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& window, Core::System& system)
102 : VideoCore::RendererBase{window} {} 102 : VideoCore::RendererBase{window}, system{system} {}
103 103
104RendererOpenGL::~RendererOpenGL() = default; 104RendererOpenGL::~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
256void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, 256void 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
15namespace Core {
16class System;
17}
18
15namespace Core::Frontend { 19namespace Core::Frontend {
16class EmuWindow; 20class EmuWindow;
17} 21}
@@ -41,7 +45,7 @@ struct ScreenInfo {
41 45
42class RendererOpenGL : public VideoCore::RendererBase { 46class RendererOpenGL : public VideoCore::RendererBase {
43public: 47public:
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
237class ConstBuffer { 237class ConstBuffer {
238public: 238public:
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
255private: 264private:
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
12namespace VideoCore { 12namespace VideoCore {
13 13
14std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window) { 14std::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
18u16 GetResolutionScaleFactor(const RendererBase& renderer) { 19u16 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
9namespace Core {
10class System;
11}
12
9namespace Core::Frontend { 13namespace Core::Frontend {
10class EmuWindow; 14class 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 */
23std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window); 27std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window,
28 Core::System& system);
24 29
25u16 GetResolutionScaleFactor(const RendererBase& renderer); 30u16 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;
22class GMainWindow; 22class GMainWindow;
23class GRenderWindow; 23class GRenderWindow;
24 24
25namespace VideoCore {
26enum class LoadCallbackStage;
27}
28
25class EmuThread : public QThread { 29class EmuThread : public QThread {
26 Q_OBJECT 30 Q_OBJECT
27 31
@@ -75,7 +79,7 @@ public:
75private: 79private:
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
106class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow { 112class 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}
44QProgressBar::chunk { 44QProgressBar::chunk {
45 background-color: #0ab9e6; 45 background-color: #0ab9e6;
46 width: 1px;
46})"; 47})";
47 48
48constexpr const char PROGRESSBAR_STYLE_BUILD[] = R"( 49constexpr const char PROGRESSBAR_STYLE_BUILD[] = R"(
@@ -53,7 +54,8 @@ QProgressBar {
53 padding: 2px; 54 padding: 2px;
54} 55}
55QProgressBar::chunk { 56QProgressBar::chunk {
56 background-color: #ff3c28; 57 background-color: #ff3c28;
58 width: 1px;
57})"; 59})";
58 60
59constexpr const char PROGRESSBAR_STYLE_COMPLETE[] = R"( 61constexpr 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)
111frame_limit = 111frame_limit =
112 112
113# Whether to use disk based shader cache
114# 0 (default): Off, 1 : On
115use_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)
115use_accurate_gpu_emulation = 119use_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 }