summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x.ci/scripts/linux/docker.sh3
-rw-r--r--CMakeLists.txt43
-rw-r--r--dist/qt_themes/default/style.qss4
-rw-r--r--externals/libusb/CMakeLists.txt2
-rw-r--r--src/common/assert.h8
-rw-r--r--src/common/hex_util.h2
-rw-r--r--src/common/settings.h1
-rw-r--r--src/common/uuid.cpp54
-rw-r--r--src/common/uuid.h30
-rw-r--r--src/core/hle/api_version.h14
-rw-r--r--src/core/hle/service/am/applets/applet_software_keyboard.cpp6
-rw-r--r--src/core/hle/service/ns/language.cpp1
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp58
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h45
-rw-r--r--src/core/hle/service/set/set.cpp23
-rw-r--r--src/core/hle/service/set/set.h1
-rw-r--r--src/core/memory.cpp552
-rw-r--r--src/core/memory.h102
-rw-r--r--src/core/network/network.cpp2
-rw-r--r--src/input_common/main.cpp8
-rw-r--r--src/input_common/mouse/mouse_poller.cpp1
-rw-r--r--src/input_common/sdl/sdl_impl.cpp87
-rw-r--r--src/video_core/CMakeLists.txt10
-rw-r--r--src/video_core/command_classes/codecs/codec.cpp144
-rw-r--r--src/video_core/command_classes/codecs/codec.h4
-rw-r--r--src/video_core/command_classes/codecs/vp9.cpp136
-rw-r--r--src/video_core/command_classes/codecs/vp9.h14
-rw-r--r--src/video_core/command_classes/codecs/vp9_types.h8
-rw-r--r--src/video_core/command_classes/nvdec.cpp2
-rw-r--r--src/video_core/command_classes/vic.cpp87
-rw-r--r--src/video_core/command_classes/vic.h7
-rw-r--r--src/video_core/host_shaders/astc_decoder.comp271
-rw-r--r--src/video_core/renderer_base.cpp2
-rw-r--r--src/video_core/renderer_base.h4
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp4
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h2
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache_base.cpp10
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp2
-rw-r--r--src/video_core/renderer_opengl/util_shaders.cpp31
-rw-r--r--src/video_core/renderer_opengl/util_shaders.h1
-rw-r--r--src/video_core/renderer_vulkan/pipeline_statistics.cpp100
-rw-r--r--src/video_core/renderer_vulkan/pipeline_statistics.h40
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp152
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp143
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.h13
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pass.cpp97
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pass.h5
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.cpp13
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp16
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.h6
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp30
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.h7
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp11
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache_base.cpp10
-rw-r--r--src/video_core/texture_cache/image_view_info.cpp4
-rw-r--r--src/video_core/texture_cache/texture_cache.h419
-rw-r--r--src/video_core/texture_cache/texture_cache_base.h385
-rw-r--r--src/video_core/textures/astc.cpp156
-rw-r--r--src/video_core/textures/astc.h111
-rw-r--r--src/video_core/textures/decoders.cpp52
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp29
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h6
-rw-r--r--src/video_core/vulkan_common/vulkan_memory_allocator.cpp8
-rw-r--r--src/video_core/vulkan_common/vulkan_memory_allocator.h2
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.cpp38
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.h8
-rw-r--r--src/yuzu/bootmanager.cpp4
-rw-r--r--src/yuzu/configuration/config.cpp19
-rw-r--r--src/yuzu/configuration/configure_debug.cpp3
-rw-r--r--src/yuzu/configuration/configure_debug.ui23
-rw-r--r--src/yuzu/configuration/configure_general.ui60
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.ui7
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp13
-rw-r--r--src/yuzu/configuration/configure_input_player_widget.cpp61
-rw-r--r--src/yuzu/configuration/configure_input_player_widget.h6
-rw-r--r--src/yuzu/configuration/configure_system.ui5
-rw-r--r--src/yuzu/configuration/configure_ui.cpp50
-rw-r--r--src/yuzu/configuration/configure_ui.ui22
-rw-r--r--src/yuzu/game_list.cpp4
-rw-r--r--src/yuzu/game_list_p.h13
-rw-r--r--src/yuzu/main.cpp7
-rw-r--r--src/yuzu/uisettings.h3
-rw-r--r--src/yuzu_cmd/CMakeLists.txt1
-rw-r--r--src/yuzu_cmd/config.cpp4
-rw-r--r--src/yuzu_cmd/default_ini.h6
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp11
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.h8
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp6
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h7
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp6
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h3
-rw-r--r--src/yuzu_cmd/yuzu.cpp4
98 files changed, 2116 insertions, 1899 deletions
diff --git a/.ci/scripts/linux/docker.sh b/.ci/scripts/linux/docker.sh
index 9b451d3ab..5070b92d1 100755
--- a/.ci/scripts/linux/docker.sh
+++ b/.ci/scripts/linux/docker.sh
@@ -18,7 +18,8 @@ cmake .. \
18 -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \ 18 -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \
19 -DENABLE_QT_TRANSLATION=ON \ 19 -DENABLE_QT_TRANSLATION=ON \
20 -DUSE_DISCORD_PRESENCE=ON \ 20 -DUSE_DISCORD_PRESENCE=ON \
21 -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} 21 -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} \
22 -DYUZU_USE_BUNDLED_FFMPEG=ON
22 23
23make -j$(nproc) 24make -j$(nproc)
24 25
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d98ba7767..de2413843 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -25,7 +25,7 @@ option(YUZU_USE_BUNDLED_BOOST "Download bundled Boost" OFF)
25 25
26option(YUZU_USE_BUNDLED_LIBUSB "Compile bundled libusb" OFF) 26option(YUZU_USE_BUNDLED_LIBUSB "Compile bundled libusb" OFF)
27 27
28CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_FFMPEG "Download/Build bundled FFmpeg" ON "WIN32" OFF) 28option(YUZU_USE_BUNDLED_FFMPEG "Download/Build bundled FFmpeg" "${WIN32}")
29 29
30option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF) 30option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF)
31 31
@@ -583,8 +583,32 @@ if (YUZU_USE_BUNDLED_FFMPEG)
583 "${FFmpeg_PREFIX};${FFmpeg_BUILD_DIR}" 583 "${FFmpeg_PREFIX};${FFmpeg_BUILD_DIR}"
584 CACHE PATH "Path to FFmpeg headers" FORCE) 584 CACHE PATH "Path to FFmpeg headers" FORCE)
585 585
586 if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
587 Include(FindPkgConfig REQUIRED)
588 pkg_check_modules(LIBVA libva)
589 endif()
590 if(LIBVA_FOUND)
591 pkg_check_modules(LIBDRM libdrm REQUIRED)
592 find_package(X11 REQUIRED)
593 pkg_check_modules(LIBVA-DRM libva-drm REQUIRED)
594 pkg_check_modules(LIBVA-X11 libva-x11 REQUIRED)
595 set(FFmpeg_LIBVA_LIBRARIES
596 ${LIBDRM_LIBRARIES}
597 ${X11_LIBRARIES}
598 ${LIBVA-DRM_LIBRARIES}
599 ${LIBVA-X11_LIBRARIES}
600 ${LIBVA_LIBRARIES})
601 set(FFmpeg_HWACCEL_FLAGS
602 --enable-hwaccel=h264_vaapi
603 --enable-hwaccel=vp9_vaapi
604 --enable-libdrm)
605 message(STATUS "VA-API found")
606 else()
607 set(FFmpeg_HWACCEL_FLAGS --disable-vaapi)
608 endif()
609
586 # `configure` parameters builds only exactly what yuzu needs from FFmpeg 610 # `configure` parameters builds only exactly what yuzu needs from FFmpeg
587 # `--disable-{vaapi,vdpau}` is needed to avoid linking issues 611 # `--disable-vdpau` is needed to avoid linking issues
588 add_custom_command( 612 add_custom_command(
589 OUTPUT 613 OUTPUT
590 ${FFmpeg_MAKEFILE} 614 ${FFmpeg_MAKEFILE}
@@ -600,13 +624,16 @@ if (YUZU_USE_BUNDLED_FFMPEG)
600 --disable-network 624 --disable-network
601 --disable-postproc 625 --disable-postproc
602 --disable-swresample 626 --disable-swresample
603 --disable-vaapi
604 --disable-vdpau 627 --disable-vdpau
605 --enable-decoder=h264 628 --enable-decoder=h264
606 --enable-decoder=vp9 629 --enable-decoder=vp9
630 --cc="${CMAKE_C_COMPILER}"
631 --cxx="${CMAKE_CXX_COMPILER}"
632 ${FFmpeg_HWACCEL_FLAGS}
607 WORKING_DIRECTORY 633 WORKING_DIRECTORY
608 ${FFmpeg_BUILD_DIR} 634 ${FFmpeg_BUILD_DIR}
609 ) 635 )
636 unset(FFmpeg_HWACCEL_FLAGS)
610 637
611 # Workaround for Ubuntu 18.04's older version of make not being able to call make as a child 638 # Workaround for Ubuntu 18.04's older version of make not being able to call make as a child
612 # with context of the jobserver. Also helps ninja users. 639 # with context of the jobserver. Also helps ninja users.
@@ -616,9 +643,10 @@ if (YUZU_USE_BUNDLED_FFMPEG)
616 OUTPUT_VARIABLE 643 OUTPUT_VARIABLE
617 SYSTEM_THREADS) 644 SYSTEM_THREADS)
618 645
646 set(FFmpeg_BUILD_LIBRARIES ${FFmpeg_LIBRARIES})
619 add_custom_command( 647 add_custom_command(
620 OUTPUT 648 OUTPUT
621 ${FFmpeg_LIBRARIES} 649 ${FFmpeg_BUILD_LIBRARIES}
622 COMMAND 650 COMMAND
623 make -j${SYSTEM_THREADS} 651 make -j${SYSTEM_THREADS}
624 WORKING_DIRECTORY 652 WORKING_DIRECTORY
@@ -628,7 +656,12 @@ if (YUZU_USE_BUNDLED_FFMPEG)
628 # ALL makes this custom target build every time 656 # ALL makes this custom target build every time
629 # but it won't actually build if the DEPENDS parameter is up to date 657 # but it won't actually build if the DEPENDS parameter is up to date
630 add_custom_target(ffmpeg-configure ALL DEPENDS ${FFmpeg_MAKEFILE}) 658 add_custom_target(ffmpeg-configure ALL DEPENDS ${FFmpeg_MAKEFILE})
631 add_custom_target(ffmpeg-build ALL DEPENDS ${FFmpeg_LIBRARIES} ffmpeg-configure) 659 add_custom_target(ffmpeg-build ALL DEPENDS ${FFmpeg_BUILD_LIBRARIES} ffmpeg-configure)
660 link_libraries(${FFmpeg_LIBVA_LIBRARIES})
661 set(FFmpeg_LIBRARIES ${FFmpeg_LIBVA_LIBRARIES} ${FFmpeg_BUILD_LIBRARIES}
662 CACHE PATH "Paths to FFmpeg libraries" FORCE)
663 unset(FFmpeg_BUILD_LIBRARIES)
664 unset(FFmpeg_LIBVA_LIBRARIES)
632 665
633 if (FFmpeg_FOUND) 666 if (FFmpeg_FOUND)
634 message(STATUS "Found FFmpeg version ${FFmpeg_VERSION}") 667 message(STATUS "Found FFmpeg version ${FFmpeg_VERSION}")
diff --git a/dist/qt_themes/default/style.qss b/dist/qt_themes/default/style.qss
index 9915a40ba..f0908a7f1 100644
--- a/dist/qt_themes/default/style.qss
+++ b/dist/qt_themes/default/style.qss
@@ -51,11 +51,11 @@ QPushButton#GPUStatusBarButton:hover {
51} 51}
52 52
53QPushButton#GPUStatusBarButton:checked { 53QPushButton#GPUStatusBarButton:checked {
54 color: #ff8040; 54 color: #b06020;
55} 55}
56 56
57QPushButton#GPUStatusBarButton:!checked { 57QPushButton#GPUStatusBarButton:!checked {
58 color: #40dd40; 58 color: #109010;
59} 59}
60 60
61QPushButton#buttonRefreshDevices { 61QPushButton#buttonRefreshDevices {
diff --git a/externals/libusb/CMakeLists.txt b/externals/libusb/CMakeLists.txt
index 151ddc462..12bdc097a 100644
--- a/externals/libusb/CMakeLists.txt
+++ b/externals/libusb/CMakeLists.txt
@@ -67,6 +67,8 @@ if (MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux") OR APPLE)
67 "${LIBUSB_MAKEFILE}" 67 "${LIBUSB_MAKEFILE}"
68 COMMAND 68 COMMAND
69 env 69 env
70 CC="${CMAKE_C_COMPILER}"
71 CXX="${CMAKE_CXX_COMPILER}"
70 CFLAGS="${LIBUSB_CFLAGS}" 72 CFLAGS="${LIBUSB_CFLAGS}"
71 sh "${LIBUSB_CONFIGURE}" 73 sh "${LIBUSB_CONFIGURE}"
72 ${LIBUSB_CONFIGURE_ARGS} 74 ${LIBUSB_CONFIGURE_ARGS}
diff --git a/src/common/assert.h b/src/common/assert.h
index b3ba35c0f..33060d865 100644
--- a/src/common/assert.h
+++ b/src/common/assert.h
@@ -52,8 +52,12 @@ assert_noinline_call(const Fn& fn) {
52#define DEBUG_ASSERT(_a_) ASSERT(_a_) 52#define DEBUG_ASSERT(_a_) ASSERT(_a_)
53#define DEBUG_ASSERT_MSG(_a_, ...) ASSERT_MSG(_a_, __VA_ARGS__) 53#define DEBUG_ASSERT_MSG(_a_, ...) ASSERT_MSG(_a_, __VA_ARGS__)
54#else // not debug 54#else // not debug
55#define DEBUG_ASSERT(_a_) 55#define DEBUG_ASSERT(_a_) \
56#define DEBUG_ASSERT_MSG(_a_, _desc_, ...) 56 do { \
57 } while (0)
58#define DEBUG_ASSERT_MSG(_a_, _desc_, ...) \
59 do { \
60 } while (0)
57#endif 61#endif
58 62
59#define UNIMPLEMENTED() ASSERT_MSG(false, "Unimplemented code!") 63#define UNIMPLEMENTED() ASSERT_MSG(false, "Unimplemented code!")
diff --git a/src/common/hex_util.h b/src/common/hex_util.h
index f5f9e4507..5e9b6ef8b 100644
--- a/src/common/hex_util.h
+++ b/src/common/hex_util.h
@@ -61,7 +61,7 @@ template <typename ContiguousContainer>
61 return out; 61 return out;
62} 62}
63 63
64[[nodiscard]] constexpr std::array<u8, 16> AsArray(const char (&data)[17]) { 64[[nodiscard]] constexpr std::array<u8, 16> AsArray(const char (&data)[33]) {
65 return HexStringToArray<16>(data); 65 return HexStringToArray<16>(data);
66} 66}
67 67
diff --git a/src/common/settings.h b/src/common/settings.h
index 69f4adaeb..c4afff50b 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -427,6 +427,7 @@ struct Values {
427 RangedSetting<RendererBackend> renderer_backend{ 427 RangedSetting<RendererBackend> renderer_backend{
428 RendererBackend::OpenGL, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"}; 428 RendererBackend::OpenGL, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"};
429 BasicSetting<bool> renderer_debug{false, "debug"}; 429 BasicSetting<bool> renderer_debug{false, "debug"};
430 BasicSetting<bool> renderer_shader_feedback{false, "shader_feedback"};
430 BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"}; 431 BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"};
431 BasicSetting<bool> disable_shader_loop_safety_checks{false, 432 BasicSetting<bool> disable_shader_loop_safety_checks{false,
432 "disable_shader_loop_safety_checks"}; 433 "disable_shader_loop_safety_checks"};
diff --git a/src/common/uuid.cpp b/src/common/uuid.cpp
index 18303a1e3..d7435a6e9 100644
--- a/src/common/uuid.cpp
+++ b/src/common/uuid.cpp
@@ -6,10 +6,64 @@
6 6
7#include <fmt/format.h> 7#include <fmt/format.h>
8 8
9#include "common/assert.h"
9#include "common/uuid.h" 10#include "common/uuid.h"
10 11
11namespace Common { 12namespace Common {
12 13
14namespace {
15
16bool IsHexDigit(char c) {
17 return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
18}
19
20u8 HexCharToByte(char c) {
21 if (c >= '0' && c <= '9') {
22 return static_cast<u8>(c - '0');
23 }
24 if (c >= 'a' && c <= 'f') {
25 return static_cast<u8>(c - 'a' + 10);
26 }
27 if (c >= 'A' && c <= 'F') {
28 return static_cast<u8>(c - 'A' + 10);
29 }
30 ASSERT_MSG(false, "{} is not a hexadecimal digit!", c);
31 return u8{0};
32}
33
34} // Anonymous namespace
35
36u128 HexStringToU128(std::string_view hex_string) {
37 const size_t length = hex_string.length();
38
39 // Detect "0x" prefix.
40 const bool has_0x_prefix = length > 2 && hex_string[0] == '0' && hex_string[1] == 'x';
41 const size_t offset = has_0x_prefix ? 2 : 0;
42
43 // Check length.
44 if (length > 32 + offset) {
45 ASSERT_MSG(false, "hex_string has more than 32 hexadecimal characters!");
46 return INVALID_UUID;
47 }
48
49 u64 lo = 0;
50 u64 hi = 0;
51 for (size_t i = 0; i < length - offset; ++i) {
52 const char c = hex_string[length - 1 - i];
53 if (!IsHexDigit(c)) {
54 ASSERT_MSG(false, "{} is not a hexadecimal digit!", c);
55 return INVALID_UUID;
56 }
57 if (i < 16) {
58 lo |= u64{HexCharToByte(c)} << (i * 4);
59 }
60 if (i >= 16) {
61 hi |= u64{HexCharToByte(c)} << ((i - 16) * 4);
62 }
63 }
64 return u128{lo, hi};
65}
66
13UUID UUID::Generate() { 67UUID UUID::Generate() {
14 std::random_device device; 68 std::random_device device;
15 std::mt19937 gen(device()); 69 std::mt19937 gen(device());
diff --git a/src/common/uuid.h b/src/common/uuid.h
index 0ffa37e7c..2353179d8 100644
--- a/src/common/uuid.h
+++ b/src/common/uuid.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <string> 7#include <string>
8#include <string_view>
8 9
9#include "common/common_types.h" 10#include "common/common_types.h"
10 11
@@ -12,12 +13,30 @@ namespace Common {
12 13
13constexpr u128 INVALID_UUID{{0, 0}}; 14constexpr u128 INVALID_UUID{{0, 0}};
14 15
16/**
17 * Converts a hex string to a 128-bit unsigned integer.
18 *
19 * The hex string can be formatted in lowercase or uppercase, with or without the "0x" prefix.
20 *
21 * This function will assert and return INVALID_UUID under the following conditions:
22 * - If the hex string is more than 32 characters long
23 * - If the hex string contains non-hexadecimal characters
24 *
25 * @param hex_string Hexadecimal string
26 *
27 * @returns A 128-bit unsigned integer if successfully converted, INVALID_UUID otherwise.
28 */
29[[nodiscard]] u128 HexStringToU128(std::string_view hex_string);
30
15struct UUID { 31struct UUID {
16 // UUIDs which are 0 are considered invalid! 32 // UUIDs which are 0 are considered invalid!
17 u128 uuid; 33 u128 uuid;
18 UUID() = default; 34 UUID() = default;
19 constexpr explicit UUID(const u128& id) : uuid{id} {} 35 constexpr explicit UUID(const u128& id) : uuid{id} {}
20 constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {} 36 constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {}
37 explicit UUID(std::string_view hex_string) {
38 uuid = HexStringToU128(hex_string);
39 }
21 40
22 [[nodiscard]] constexpr explicit operator bool() const { 41 [[nodiscard]] constexpr explicit operator bool() const {
23 return uuid != INVALID_UUID; 42 return uuid != INVALID_UUID;
@@ -50,3 +69,14 @@ struct UUID {
50static_assert(sizeof(UUID) == 16, "UUID is an invalid size!"); 69static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
51 70
52} // namespace Common 71} // namespace Common
72
73namespace std {
74
75template <>
76struct hash<Common::UUID> {
77 size_t operator()(const Common::UUID& uuid) const noexcept {
78 return uuid.uuid[1] ^ uuid.uuid[0];
79 }
80};
81
82} // namespace std
diff --git a/src/core/hle/api_version.h b/src/core/hle/api_version.h
index 5e10a7ad9..43d5670a9 100644
--- a/src/core/hle/api_version.h
+++ b/src/core/hle/api_version.h
@@ -12,9 +12,9 @@ namespace HLE::ApiVersion {
12 12
13// Horizon OS version constants. 13// Horizon OS version constants.
14 14
15constexpr u8 HOS_VERSION_MAJOR = 11; 15constexpr u8 HOS_VERSION_MAJOR = 12;
16constexpr u8 HOS_VERSION_MINOR = 0; 16constexpr u8 HOS_VERSION_MINOR = 1;
17constexpr u8 HOS_VERSION_MICRO = 1; 17constexpr u8 HOS_VERSION_MICRO = 0;
18 18
19// NintendoSDK version constants. 19// NintendoSDK version constants.
20 20
@@ -22,15 +22,15 @@ constexpr u8 SDK_REVISION_MAJOR = 1;
22constexpr u8 SDK_REVISION_MINOR = 0; 22constexpr u8 SDK_REVISION_MINOR = 0;
23 23
24constexpr char PLATFORM_STRING[] = "NX"; 24constexpr char PLATFORM_STRING[] = "NX";
25constexpr char VERSION_HASH[] = "69103fcb2004dace877094c2f8c29e6113be5dbf"; 25constexpr char VERSION_HASH[] = "76b10c2dab7d3aa73fc162f8dff1655e6a21caf4";
26constexpr char DISPLAY_VERSION[] = "11.0.1"; 26constexpr char DISPLAY_VERSION[] = "12.1.0";
27constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 11.0.1-1.0"; 27constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 12.1.0-1.0";
28 28
29// Atmosphere version constants. 29// Atmosphere version constants.
30 30
31constexpr u8 ATMOSPHERE_RELEASE_VERSION_MAJOR = 0; 31constexpr u8 ATMOSPHERE_RELEASE_VERSION_MAJOR = 0;
32constexpr u8 ATMOSPHERE_RELEASE_VERSION_MINOR = 19; 32constexpr u8 ATMOSPHERE_RELEASE_VERSION_MINOR = 19;
33constexpr u8 ATMOSPHERE_RELEASE_VERSION_MICRO = 4; 33constexpr u8 ATMOSPHERE_RELEASE_VERSION_MICRO = 5;
34 34
35constexpr u32 GetTargetFirmware() { 35constexpr u32 GetTargetFirmware() {
36 return u32{HOS_VERSION_MAJOR} << 24 | u32{HOS_VERSION_MINOR} << 16 | 36 return u32{HOS_VERSION_MAJOR} << 24 | u32{HOS_VERSION_MINOR} << 16 |
diff --git a/src/core/hle/service/am/applets/applet_software_keyboard.cpp b/src/core/hle/service/am/applets/applet_software_keyboard.cpp
index 7cae90609..c89aa1bbf 100644
--- a/src/core/hle/service/am/applets/applet_software_keyboard.cpp
+++ b/src/core/hle/service/am/applets/applet_software_keyboard.cpp
@@ -377,7 +377,8 @@ void SoftwareKeyboard::SubmitForTextCheck(std::u16string submitted_text) {
377 377
378 if (swkbd_config_common.use_utf8) { 378 if (swkbd_config_common.use_utf8) {
379 std::string utf8_submitted_text = Common::UTF16ToUTF8(current_text); 379 std::string utf8_submitted_text = Common::UTF16ToUTF8(current_text);
380 const u64 buffer_size = sizeof(u64) + utf8_submitted_text.size(); 380 // Include the null terminator in the buffer size.
381 const u64 buffer_size = utf8_submitted_text.size() + 1;
381 382
382 LOG_DEBUG(Service_AM, "\nBuffer Size: {}\nUTF-8 Submitted Text: {}", buffer_size, 383 LOG_DEBUG(Service_AM, "\nBuffer Size: {}\nUTF-8 Submitted Text: {}", buffer_size,
383 utf8_submitted_text); 384 utf8_submitted_text);
@@ -386,7 +387,8 @@ void SoftwareKeyboard::SubmitForTextCheck(std::u16string submitted_text) {
386 std::memcpy(out_data.data() + sizeof(u64), utf8_submitted_text.data(), 387 std::memcpy(out_data.data() + sizeof(u64), utf8_submitted_text.data(),
387 utf8_submitted_text.size()); 388 utf8_submitted_text.size());
388 } else { 389 } else {
389 const u64 buffer_size = sizeof(u64) + current_text.size() * sizeof(char16_t); 390 // Include the null terminator in the buffer size.
391 const u64 buffer_size = (current_text.size() + 1) * sizeof(char16_t);
390 392
391 LOG_DEBUG(Service_AM, "\nBuffer Size: {}\nUTF-16 Submitted Text: {}", buffer_size, 393 LOG_DEBUG(Service_AM, "\nBuffer Size: {}\nUTF-16 Submitted Text: {}", buffer_size,
392 Common::UTF16ToUTF8(current_text)); 394 Common::UTF16ToUTF8(current_text));
diff --git a/src/core/hle/service/ns/language.cpp b/src/core/hle/service/ns/language.cpp
index 54b644830..7d9e4a20b 100644
--- a/src/core/hle/service/ns/language.cpp
+++ b/src/core/hle/service/ns/language.cpp
@@ -339,6 +339,7 @@ std::optional<ApplicationLanguage> ConvertToApplicationLanguage(
339 case Set::LanguageCode::FR_CA: 339 case Set::LanguageCode::FR_CA:
340 return ApplicationLanguage::CanadianFrench; 340 return ApplicationLanguage::CanadianFrench;
341 case Set::LanguageCode::PT: 341 case Set::LanguageCode::PT:
342 case Set::LanguageCode::PT_BR:
342 return ApplicationLanguage::Portuguese; 343 return ApplicationLanguage::Portuguese;
343 case Set::LanguageCode::RU: 344 case Set::LanguageCode::RU:
344 return ApplicationLanguage::Russian; 345 return ApplicationLanguage::Russian;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
index 1403a39d0..845de724d 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
@@ -166,8 +166,6 @@ NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vecto
166 LOG_ERROR(Service_NVDRV, "failed to map size={}", object->size); 166 LOG_ERROR(Service_NVDRV, "failed to map size={}", object->size);
167 } else { 167 } else {
168 cmd_buffer.map_address = object->dma_map_addr; 168 cmd_buffer.map_address = object->dma_map_addr;
169 AddBufferMap(object->dma_map_addr, object->size, object->addr,
170 object->status == nvmap::Object::Status::Allocated);
171 } 169 }
172 } 170 }
173 std::memcpy(output.data(), &params, sizeof(IoctlMapBuffer)); 171 std::memcpy(output.data(), &params, sizeof(IoctlMapBuffer));
@@ -178,30 +176,11 @@ NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vecto
178} 176}
179 177
180NvResult nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { 178NvResult nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
181 IoctlMapBuffer params{}; 179 // This is intntionally stubbed.
182 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer)); 180 // Skip unmapping buffers here, as to not break the continuity of the VP9 reference frame
183 std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries); 181 // addresses, and risk invalidating data before the async GPU thread is done with it
184 SliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer));
185
186 auto& gpu = system.GPU();
187
188 for (auto& cmd_buffer : cmd_buffer_handles) {
189 const auto object{nvmap_dev->GetObject(cmd_buffer.map_handle)};
190 if (!object) {
191 LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmd_buffer.map_handle);
192 std::memcpy(output.data(), &params, output.size());
193 return NvResult::InvalidState;
194 }
195 if (const auto size{RemoveBufferMap(object->dma_map_addr)}; size) {
196 gpu.MemoryManager().Unmap(object->dma_map_addr, *size);
197 } else {
198 // This occurs quite frequently, however does not seem to impact functionality
199 LOG_DEBUG(Service_NVDRV, "invalid offset=0x{:X} dma=0x{:X}", object->addr,
200 object->dma_map_addr);
201 }
202 object->dma_map_addr = 0;
203 }
204 std::memset(output.data(), 0, output.size()); 182 std::memset(output.data(), 0, output.size());
183 LOG_DEBUG(Service_NVDRV, "(STUBBED) called");
205 return NvResult::Success; 184 return NvResult::Success;
206} 185}
207 186
@@ -212,33 +191,4 @@ NvResult nvhost_nvdec_common::SetSubmitTimeout(const std::vector<u8>& input,
212 return NvResult::Success; 191 return NvResult::Success;
213} 192}
214 193
215std::optional<nvhost_nvdec_common::BufferMap> nvhost_nvdec_common::FindBufferMap(
216 GPUVAddr gpu_addr) const {
217 const auto it = std::find_if(
218 buffer_mappings.begin(), buffer_mappings.upper_bound(gpu_addr), [&](const auto& entry) {
219 return (gpu_addr >= entry.second.StartAddr() && gpu_addr < entry.second.EndAddr());
220 });
221
222 ASSERT(it != buffer_mappings.end());
223 return it->second;
224}
225
226void nvhost_nvdec_common::AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr,
227 bool is_allocated) {
228 buffer_mappings.insert_or_assign(gpu_addr, BufferMap{gpu_addr, size, cpu_addr, is_allocated});
229}
230
231std::optional<std::size_t> nvhost_nvdec_common::RemoveBufferMap(GPUVAddr gpu_addr) {
232 const auto iter{buffer_mappings.find(gpu_addr)};
233 if (iter == buffer_mappings.end()) {
234 return std::nullopt;
235 }
236 std::size_t size = 0;
237 if (iter->second.IsAllocated()) {
238 size = iter->second.Size();
239 }
240 buffer_mappings.erase(iter);
241 return size;
242}
243
244} // namespace Service::Nvidia::Devices 194} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
index da10f5f41..af59f00d2 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
@@ -23,45 +23,6 @@ public:
23 ~nvhost_nvdec_common() override; 23 ~nvhost_nvdec_common() override;
24 24
25protected: 25protected:
26 class BufferMap final {
27 public:
28 constexpr BufferMap() = default;
29
30 constexpr BufferMap(GPUVAddr start_addr_, std::size_t size_)
31 : start_addr{start_addr_}, end_addr{start_addr_ + size_} {}
32
33 constexpr BufferMap(GPUVAddr start_addr_, std::size_t size_, VAddr cpu_addr_,
34 bool is_allocated_)
35 : start_addr{start_addr_}, end_addr{start_addr_ + size_}, cpu_addr{cpu_addr_},
36 is_allocated{is_allocated_} {}
37
38 constexpr VAddr StartAddr() const {
39 return start_addr;
40 }
41
42 constexpr VAddr EndAddr() const {
43 return end_addr;
44 }
45
46 constexpr std::size_t Size() const {
47 return end_addr - start_addr;
48 }
49
50 constexpr VAddr CpuAddr() const {
51 return cpu_addr;
52 }
53
54 constexpr bool IsAllocated() const {
55 return is_allocated;
56 }
57
58 private:
59 GPUVAddr start_addr{};
60 GPUVAddr end_addr{};
61 VAddr cpu_addr{};
62 bool is_allocated{};
63 };
64
65 struct IoctlSetNvmapFD { 26 struct IoctlSetNvmapFD {
66 s32_le nvmap_fd{}; 27 s32_le nvmap_fd{};
67 }; 28 };
@@ -154,17 +115,11 @@ protected:
154 NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output); 115 NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
155 NvResult SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output); 116 NvResult SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output);
156 117
157 std::optional<BufferMap> FindBufferMap(GPUVAddr gpu_addr) const;
158 void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated);
159 std::optional<std::size_t> RemoveBufferMap(GPUVAddr gpu_addr);
160
161 s32_le nvmap_fd{}; 118 s32_le nvmap_fd{};
162 u32_le submit_timeout{}; 119 u32_le submit_timeout{};
163 std::shared_ptr<nvmap> nvmap_dev; 120 std::shared_ptr<nvmap> nvmap_dev;
164 SyncpointManager& syncpoint_manager; 121 SyncpointManager& syncpoint_manager;
165 std::array<u32, MaxSyncPoints> device_syncpoints{}; 122 std::array<u32, MaxSyncPoints> device_syncpoints{};
166 // This is expected to be ordered, therefore we must use a map, not unordered_map
167 std::map<GPUVAddr, BufferMap> buffer_mappings;
168}; 123};
169}; // namespace Devices 124}; // namespace Devices
170} // namespace Service::Nvidia 125} // namespace Service::Nvidia
diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp
index 522a604a5..8795eb6b7 100644
--- a/src/core/hle/service/set/set.cpp
+++ b/src/core/hle/service/set/set.cpp
@@ -12,7 +12,7 @@
12 12
13namespace Service::Set { 13namespace Service::Set {
14namespace { 14namespace {
15constexpr std::array<LanguageCode, 17> available_language_codes = {{ 15constexpr std::array<LanguageCode, 18> available_language_codes = {{
16 LanguageCode::JA, 16 LanguageCode::JA,
17 LanguageCode::EN_US, 17 LanguageCode::EN_US,
18 LanguageCode::FR, 18 LanguageCode::FR,
@@ -30,6 +30,7 @@ constexpr std::array<LanguageCode, 17> available_language_codes = {{
30 LanguageCode::ES_419, 30 LanguageCode::ES_419,
31 LanguageCode::ZH_HANS, 31 LanguageCode::ZH_HANS,
32 LanguageCode::ZH_HANT, 32 LanguageCode::ZH_HANT,
33 LanguageCode::PT_BR,
33}}; 34}};
34 35
35enum class KeyboardLayout : u64 { 36enum class KeyboardLayout : u64 {
@@ -50,7 +51,7 @@ enum class KeyboardLayout : u64 {
50 ChineseTraditional = 14, 51 ChineseTraditional = 14,
51}; 52};
52 53
53constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 17> language_to_layout{{ 54constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 18> language_to_layout{{
54 {LanguageCode::JA, KeyboardLayout::Japanese}, 55 {LanguageCode::JA, KeyboardLayout::Japanese},
55 {LanguageCode::EN_US, KeyboardLayout::EnglishUs}, 56 {LanguageCode::EN_US, KeyboardLayout::EnglishUs},
56 {LanguageCode::FR, KeyboardLayout::French}, 57 {LanguageCode::FR, KeyboardLayout::French},
@@ -68,10 +69,11 @@ constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 17> language_to_la
68 {LanguageCode::ES_419, KeyboardLayout::SpanishLatin}, 69 {LanguageCode::ES_419, KeyboardLayout::SpanishLatin},
69 {LanguageCode::ZH_HANS, KeyboardLayout::ChineseSimplified}, 70 {LanguageCode::ZH_HANS, KeyboardLayout::ChineseSimplified},
70 {LanguageCode::ZH_HANT, KeyboardLayout::ChineseTraditional}, 71 {LanguageCode::ZH_HANT, KeyboardLayout::ChineseTraditional},
72 {LanguageCode::PT_BR, KeyboardLayout::Portuguese},
71}}; 73}};
72 74
73constexpr std::size_t pre4_0_0_max_entries = 15; 75constexpr std::size_t PRE_4_0_0_MAX_ENTRIES = 0xF;
74constexpr std::size_t post4_0_0_max_entries = 17; 76constexpr std::size_t POST_4_0_0_MAX_ENTRIES = 0x40;
75 77
76constexpr ResultCode ERR_INVALID_LANGUAGE{ErrorModule::Settings, 625}; 78constexpr ResultCode ERR_INVALID_LANGUAGE{ErrorModule::Settings, 625};
77 79
@@ -81,9 +83,10 @@ void PushResponseLanguageCode(Kernel::HLERequestContext& ctx, std::size_t num_la
81 rb.Push(static_cast<u32>(num_language_codes)); 83 rb.Push(static_cast<u32>(num_language_codes));
82} 84}
83 85
84void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t max_size) { 86void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t max_entries) {
85 const std::size_t requested_amount = ctx.GetWriteBufferSize() / sizeof(LanguageCode); 87 const std::size_t requested_amount = ctx.GetWriteBufferSize() / sizeof(LanguageCode);
86 const std::size_t copy_amount = std::min(requested_amount, max_size); 88 const std::size_t max_amount = std::min(requested_amount, max_entries);
89 const std::size_t copy_amount = std::min(available_language_codes.size(), max_amount);
87 const std::size_t copy_size = copy_amount * sizeof(LanguageCode); 90 const std::size_t copy_size = copy_amount * sizeof(LanguageCode);
88 91
89 ctx.WriteBuffer(available_language_codes.data(), copy_size); 92 ctx.WriteBuffer(available_language_codes.data(), copy_size);
@@ -118,7 +121,7 @@ LanguageCode GetLanguageCodeFromIndex(std::size_t index) {
118void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) { 121void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) {
119 LOG_DEBUG(Service_SET, "called"); 122 LOG_DEBUG(Service_SET, "called");
120 123
121 GetAvailableLanguageCodesImpl(ctx, pre4_0_0_max_entries); 124 GetAvailableLanguageCodesImpl(ctx, PRE_4_0_0_MAX_ENTRIES);
122} 125}
123 126
124void SET::MakeLanguageCode(Kernel::HLERequestContext& ctx) { 127void SET::MakeLanguageCode(Kernel::HLERequestContext& ctx) {
@@ -140,19 +143,19 @@ void SET::MakeLanguageCode(Kernel::HLERequestContext& ctx) {
140void SET::GetAvailableLanguageCodes2(Kernel::HLERequestContext& ctx) { 143void SET::GetAvailableLanguageCodes2(Kernel::HLERequestContext& ctx) {
141 LOG_DEBUG(Service_SET, "called"); 144 LOG_DEBUG(Service_SET, "called");
142 145
143 GetAvailableLanguageCodesImpl(ctx, post4_0_0_max_entries); 146 GetAvailableLanguageCodesImpl(ctx, POST_4_0_0_MAX_ENTRIES);
144} 147}
145 148
146void SET::GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx) { 149void SET::GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx) {
147 LOG_DEBUG(Service_SET, "called"); 150 LOG_DEBUG(Service_SET, "called");
148 151
149 PushResponseLanguageCode(ctx, pre4_0_0_max_entries); 152 PushResponseLanguageCode(ctx, PRE_4_0_0_MAX_ENTRIES);
150} 153}
151 154
152void SET::GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx) { 155void SET::GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx) {
153 LOG_DEBUG(Service_SET, "called"); 156 LOG_DEBUG(Service_SET, "called");
154 157
155 PushResponseLanguageCode(ctx, post4_0_0_max_entries); 158 PushResponseLanguageCode(ctx, POST_4_0_0_MAX_ENTRIES);
156} 159}
157 160
158void SET::GetQuestFlag(Kernel::HLERequestContext& ctx) { 161void SET::GetQuestFlag(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h
index d5bd7828d..acabebeaa 100644
--- a/src/core/hle/service/set/set.h
+++ b/src/core/hle/service/set/set.h
@@ -31,6 +31,7 @@ enum class LanguageCode : u64 {
31 ES_419 = 0x00003931342D7365, 31 ES_419 = 0x00003931342D7365,
32 ZH_HANS = 0x00736E61482D687A, 32 ZH_HANS = 0x00736E61482D687A,
33 ZH_HANT = 0x00746E61482D687A, 33 ZH_HANT = 0x00746E61482D687A,
34 PT_BR = 0x00000052422D7470,
34}; 35};
35LanguageCode GetLanguageCodeFromIndex(std::size_t idx); 36LanguageCode GetLanguageCodeFromIndex(std::size_t idx);
36 37
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index f285c6f63..51c4dea26 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -4,8 +4,6 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <cstring> 6#include <cstring>
7#include <optional>
8#include <utility>
9 7
10#include "common/assert.h" 8#include "common/assert.h"
11#include "common/atomic_ops.h" 9#include "common/atomic_ops.h"
@@ -14,12 +12,10 @@
14#include "common/page_table.h" 12#include "common/page_table.h"
15#include "common/settings.h" 13#include "common/settings.h"
16#include "common/swap.h" 14#include "common/swap.h"
17#include "core/arm/arm_interface.h"
18#include "core/core.h" 15#include "core/core.h"
19#include "core/device_memory.h" 16#include "core/device_memory.h"
20#include "core/hle/kernel/k_page_table.h" 17#include "core/hle/kernel/k_page_table.h"
21#include "core/hle/kernel/k_process.h" 18#include "core/hle/kernel/k_process.h"
22#include "core/hle/kernel/physical_memory.h"
23#include "core/memory.h" 19#include "core/memory.h"
24#include "video_core/gpu.h" 20#include "video_core/gpu.h"
25 21
@@ -62,17 +58,7 @@ struct Memory::Impl {
62 } 58 }
63 } 59 }
64 60
65 bool IsValidVirtualAddress(const Kernel::KProcess& process, const VAddr vaddr) const { 61 [[nodiscard]] u8* GetPointerFromRasterizerCachedMemory(VAddr vaddr) const {
66 const auto& page_table = process.PageTable().PageTableImpl();
67 const auto [pointer, type] = page_table.pointers[vaddr >> PAGE_BITS].PointerType();
68 return pointer != nullptr || type == Common::PageType::RasterizerCachedMemory;
69 }
70
71 bool IsValidVirtualAddress(VAddr vaddr) const {
72 return IsValidVirtualAddress(*system.CurrentProcess(), vaddr);
73 }
74
75 u8* GetPointerFromRasterizerCachedMemory(VAddr vaddr) const {
76 const PAddr paddr{current_page_table->backing_addr[vaddr >> PAGE_BITS]}; 62 const PAddr paddr{current_page_table->backing_addr[vaddr >> PAGE_BITS]};
77 63
78 if (!paddr) { 64 if (!paddr) {
@@ -82,18 +68,6 @@ struct Memory::Impl {
82 return system.DeviceMemory().GetPointer(paddr) + vaddr; 68 return system.DeviceMemory().GetPointer(paddr) + vaddr;
83 } 69 }
84 70
85 u8* GetPointer(const VAddr vaddr) const {
86 const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
87 if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
88 return pointer + vaddr;
89 }
90 const auto type = Common::PageTable::PageInfo::ExtractType(raw_pointer);
91 if (type == Common::PageType::RasterizerCachedMemory) {
92 return GetPointerFromRasterizerCachedMemory(vaddr);
93 }
94 return nullptr;
95 }
96
97 u8 Read8(const VAddr addr) { 71 u8 Read8(const VAddr addr) {
98 return Read<u8>(addr); 72 return Read<u8>(addr);
99 } 73 }
@@ -179,7 +153,7 @@ struct Memory::Impl {
179 std::string string; 153 std::string string;
180 string.reserve(max_length); 154 string.reserve(max_length);
181 for (std::size_t i = 0; i < max_length; ++i) { 155 for (std::size_t i = 0; i < max_length; ++i) {
182 const char c = Read8(vaddr); 156 const char c = Read<s8>(vaddr);
183 if (c == '\0') { 157 if (c == '\0') {
184 break; 158 break;
185 } 159 }
@@ -190,15 +164,14 @@ struct Memory::Impl {
190 return string; 164 return string;
191 } 165 }
192 166
193 void ReadBlock(const Kernel::KProcess& process, const VAddr src_addr, void* dest_buffer, 167 void WalkBlock(const Kernel::KProcess& process, const VAddr addr, const std::size_t size,
194 const std::size_t size) { 168 auto on_unmapped, auto on_memory, auto on_rasterizer, auto increment) {
195 const auto& page_table = process.PageTable().PageTableImpl(); 169 const auto& page_table = process.PageTable().PageTableImpl();
196
197 std::size_t remaining_size = size; 170 std::size_t remaining_size = size;
198 std::size_t page_index = src_addr >> PAGE_BITS; 171 std::size_t page_index = addr >> PAGE_BITS;
199 std::size_t page_offset = src_addr & PAGE_MASK; 172 std::size_t page_offset = addr & PAGE_MASK;
200 173
201 while (remaining_size > 0) { 174 while (remaining_size) {
202 const std::size_t copy_amount = 175 const std::size_t copy_amount =
203 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); 176 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
204 const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); 177 const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
@@ -206,22 +179,18 @@ struct Memory::Impl {
206 const auto [pointer, type] = page_table.pointers[page_index].PointerType(); 179 const auto [pointer, type] = page_table.pointers[page_index].PointerType();
207 switch (type) { 180 switch (type) {
208 case Common::PageType::Unmapped: { 181 case Common::PageType::Unmapped: {
209 LOG_ERROR(HW_Memory, 182 on_unmapped(copy_amount, current_vaddr);
210 "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
211 current_vaddr, src_addr, size);
212 std::memset(dest_buffer, 0, copy_amount);
213 break; 183 break;
214 } 184 }
215 case Common::PageType::Memory: { 185 case Common::PageType::Memory: {
216 DEBUG_ASSERT(pointer); 186 DEBUG_ASSERT(pointer);
217 const u8* const src_ptr = pointer + page_offset + (page_index << PAGE_BITS); 187 u8* mem_ptr = pointer + page_offset + (page_index << PAGE_BITS);
218 std::memcpy(dest_buffer, src_ptr, copy_amount); 188 on_memory(copy_amount, mem_ptr);
219 break; 189 break;
220 } 190 }
221 case Common::PageType::RasterizerCachedMemory: { 191 case Common::PageType::RasterizerCachedMemory: {
222 const u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)}; 192 u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)};
223 system.GPU().FlushRegion(current_vaddr, copy_amount); 193 on_rasterizer(current_vaddr, copy_amount, host_ptr);
224 std::memcpy(dest_buffer, host_ptr, copy_amount);
225 break; 194 break;
226 } 195 }
227 default: 196 default:
@@ -230,248 +199,122 @@ struct Memory::Impl {
230 199
231 page_index++; 200 page_index++;
232 page_offset = 0; 201 page_offset = 0;
233 dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount; 202 increment(copy_amount);
234 remaining_size -= copy_amount; 203 remaining_size -= copy_amount;
235 } 204 }
236 } 205 }
237 206
238 void ReadBlockUnsafe(const Kernel::KProcess& process, const VAddr src_addr, void* dest_buffer, 207 template <bool UNSAFE>
239 const std::size_t size) { 208 void ReadBlockImpl(const Kernel::KProcess& process, const VAddr src_addr, void* dest_buffer,
240 const auto& page_table = process.PageTable().PageTableImpl(); 209 const std::size_t size) {
241 210 WalkBlock(
242 std::size_t remaining_size = size; 211 process, src_addr, size,
243 std::size_t page_index = src_addr >> PAGE_BITS; 212 [src_addr, size, &dest_buffer](const std::size_t copy_amount,
244 std::size_t page_offset = src_addr & PAGE_MASK; 213 const VAddr current_vaddr) {
245
246 while (remaining_size > 0) {
247 const std::size_t copy_amount =
248 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
249 const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
250
251 const auto [pointer, type] = page_table.pointers[page_index].PointerType();
252 switch (type) {
253 case Common::PageType::Unmapped: {
254 LOG_ERROR(HW_Memory, 214 LOG_ERROR(HW_Memory,
255 "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", 215 "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
256 current_vaddr, src_addr, size); 216 current_vaddr, src_addr, size);
257 std::memset(dest_buffer, 0, copy_amount); 217 std::memset(dest_buffer, 0, copy_amount);
258 break; 218 },
259 } 219 [&dest_buffer](const std::size_t copy_amount, const u8* const src_ptr) {
260 case Common::PageType::Memory: {
261 DEBUG_ASSERT(pointer);
262 const u8* const src_ptr = pointer + page_offset + (page_index << PAGE_BITS);
263 std::memcpy(dest_buffer, src_ptr, copy_amount); 220 std::memcpy(dest_buffer, src_ptr, copy_amount);
264 break; 221 },
265 } 222 [&system = system, &dest_buffer](const VAddr current_vaddr,
266 case Common::PageType::RasterizerCachedMemory: { 223 const std::size_t copy_amount,
267 const u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)}; 224 const u8* const host_ptr) {
225 if constexpr (!UNSAFE) {
226 system.GPU().FlushRegion(current_vaddr, copy_amount);
227 }
268 std::memcpy(dest_buffer, host_ptr, copy_amount); 228 std::memcpy(dest_buffer, host_ptr, copy_amount);
269 break; 229 },
270 } 230 [&dest_buffer](const std::size_t copy_amount) {
271 default: 231 dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
272 UNREACHABLE(); 232 });
273 }
274
275 page_index++;
276 page_offset = 0;
277 dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
278 remaining_size -= copy_amount;
279 }
280 } 233 }
281 234
282 void ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t size) { 235 void ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t size) {
283 ReadBlock(*system.CurrentProcess(), src_addr, dest_buffer, size); 236 ReadBlockImpl<false>(*system.CurrentProcess(), src_addr, dest_buffer, size);
284 } 237 }
285 238
286 void ReadBlockUnsafe(const VAddr src_addr, void* dest_buffer, const std::size_t size) { 239 void ReadBlockUnsafe(const VAddr src_addr, void* dest_buffer, const std::size_t size) {
287 ReadBlockUnsafe(*system.CurrentProcess(), src_addr, dest_buffer, size); 240 ReadBlockImpl<true>(*system.CurrentProcess(), src_addr, dest_buffer, size);
288 } 241 }
289 242
290 void WriteBlock(const Kernel::KProcess& process, const VAddr dest_addr, const void* src_buffer, 243 template <bool UNSAFE>
291 const std::size_t size) { 244 void WriteBlockImpl(const Kernel::KProcess& process, const VAddr dest_addr,
292 const auto& page_table = process.PageTable().PageTableImpl(); 245 const void* src_buffer, const std::size_t size) {
293 std::size_t remaining_size = size; 246 WalkBlock(
294 std::size_t page_index = dest_addr >> PAGE_BITS; 247 process, dest_addr, size,
295 std::size_t page_offset = dest_addr & PAGE_MASK; 248 [dest_addr, size](const std::size_t copy_amount, const VAddr current_vaddr) {
296
297 while (remaining_size > 0) {
298 const std::size_t copy_amount =
299 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
300 const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
301
302 const auto [pointer, type] = page_table.pointers[page_index].PointerType();
303 switch (type) {
304 case Common::PageType::Unmapped: {
305 LOG_ERROR(HW_Memory, 249 LOG_ERROR(HW_Memory,
306 "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", 250 "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
307 current_vaddr, dest_addr, size); 251 current_vaddr, dest_addr, size);
308 break; 252 },
309 } 253 [&src_buffer](const std::size_t copy_amount, u8* const dest_ptr) {
310 case Common::PageType::Memory: {
311 DEBUG_ASSERT(pointer);
312 u8* const dest_ptr = pointer + page_offset + (page_index << PAGE_BITS);
313 std::memcpy(dest_ptr, src_buffer, copy_amount); 254 std::memcpy(dest_ptr, src_buffer, copy_amount);
314 break; 255 },
315 } 256 [&system = system, &src_buffer](const VAddr current_vaddr,
316 case Common::PageType::RasterizerCachedMemory: { 257 const std::size_t copy_amount, u8* const host_ptr) {
317 u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)}; 258 if constexpr (!UNSAFE) {
318 system.GPU().InvalidateRegion(current_vaddr, copy_amount); 259 system.GPU().InvalidateRegion(current_vaddr, copy_amount);
319 std::memcpy(host_ptr, src_buffer, copy_amount); 260 }
320 break;
321 }
322 default:
323 UNREACHABLE();
324 }
325
326 page_index++;
327 page_offset = 0;
328 src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
329 remaining_size -= copy_amount;
330 }
331 }
332
333 void WriteBlockUnsafe(const Kernel::KProcess& process, const VAddr dest_addr,
334 const void* src_buffer, const std::size_t size) {
335 const auto& page_table = process.PageTable().PageTableImpl();
336 std::size_t remaining_size = size;
337 std::size_t page_index = dest_addr >> PAGE_BITS;
338 std::size_t page_offset = dest_addr & PAGE_MASK;
339
340 while (remaining_size > 0) {
341 const std::size_t copy_amount =
342 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
343 const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
344
345 const auto [pointer, type] = page_table.pointers[page_index].PointerType();
346 switch (type) {
347 case Common::PageType::Unmapped: {
348 LOG_ERROR(HW_Memory,
349 "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
350 current_vaddr, dest_addr, size);
351 break;
352 }
353 case Common::PageType::Memory: {
354 DEBUG_ASSERT(pointer);
355 u8* const dest_ptr = pointer + page_offset + (page_index << PAGE_BITS);
356 std::memcpy(dest_ptr, src_buffer, copy_amount);
357 break;
358 }
359 case Common::PageType::RasterizerCachedMemory: {
360 u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)};
361 std::memcpy(host_ptr, src_buffer, copy_amount); 261 std::memcpy(host_ptr, src_buffer, copy_amount);
362 break; 262 },
363 } 263 [&src_buffer](const std::size_t copy_amount) {
364 default: 264 src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
365 UNREACHABLE(); 265 });
366 }
367
368 page_index++;
369 page_offset = 0;
370 src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
371 remaining_size -= copy_amount;
372 }
373 } 266 }
374 267
375 void WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) { 268 void WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) {
376 WriteBlock(*system.CurrentProcess(), dest_addr, src_buffer, size); 269 WriteBlockImpl<false>(*system.CurrentProcess(), dest_addr, src_buffer, size);
377 } 270 }
378 271
379 void WriteBlockUnsafe(const VAddr dest_addr, const void* src_buffer, const std::size_t size) { 272 void WriteBlockUnsafe(const VAddr dest_addr, const void* src_buffer, const std::size_t size) {
380 WriteBlockUnsafe(*system.CurrentProcess(), dest_addr, src_buffer, size); 273 WriteBlockImpl<true>(*system.CurrentProcess(), dest_addr, src_buffer, size);
381 } 274 }
382 275
383 void ZeroBlock(const Kernel::KProcess& process, const VAddr dest_addr, const std::size_t size) { 276 void ZeroBlock(const Kernel::KProcess& process, const VAddr dest_addr, const std::size_t size) {
384 const auto& page_table = process.PageTable().PageTableImpl(); 277 WalkBlock(
385 std::size_t remaining_size = size; 278 process, dest_addr, size,
386 std::size_t page_index = dest_addr >> PAGE_BITS; 279 [dest_addr, size](const std::size_t copy_amount, const VAddr current_vaddr) {
387 std::size_t page_offset = dest_addr & PAGE_MASK;
388
389 while (remaining_size > 0) {
390 const std::size_t copy_amount =
391 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
392 const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
393
394 const auto [pointer, type] = page_table.pointers[page_index].PointerType();
395 switch (type) {
396 case Common::PageType::Unmapped: {
397 LOG_ERROR(HW_Memory, 280 LOG_ERROR(HW_Memory,
398 "Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", 281 "Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
399 current_vaddr, dest_addr, size); 282 current_vaddr, dest_addr, size);
400 break; 283 },
401 } 284 [](const std::size_t copy_amount, u8* const dest_ptr) {
402 case Common::PageType::Memory: {
403 DEBUG_ASSERT(pointer);
404 u8* const dest_ptr = pointer + page_offset + (page_index << PAGE_BITS);
405 std::memset(dest_ptr, 0, copy_amount); 285 std::memset(dest_ptr, 0, copy_amount);
406 break; 286 },
407 } 287 [&system = system](const VAddr current_vaddr, const std::size_t copy_amount,
408 case Common::PageType::RasterizerCachedMemory: { 288 u8* const host_ptr) {
409 u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)};
410 system.GPU().InvalidateRegion(current_vaddr, copy_amount); 289 system.GPU().InvalidateRegion(current_vaddr, copy_amount);
411 std::memset(host_ptr, 0, copy_amount); 290 std::memset(host_ptr, 0, copy_amount);
412 break; 291 },
413 } 292 [](const std::size_t copy_amount) {});
414 default:
415 UNREACHABLE();
416 }
417
418 page_index++;
419 page_offset = 0;
420 remaining_size -= copy_amount;
421 }
422 }
423
424 void ZeroBlock(const VAddr dest_addr, const std::size_t size) {
425 ZeroBlock(*system.CurrentProcess(), dest_addr, size);
426 } 293 }
427 294
428 void CopyBlock(const Kernel::KProcess& process, VAddr dest_addr, VAddr src_addr, 295 void CopyBlock(const Kernel::KProcess& process, VAddr dest_addr, VAddr src_addr,
429 const std::size_t size) { 296 const std::size_t size) {
430 const auto& page_table = process.PageTable().PageTableImpl(); 297 WalkBlock(
431 std::size_t remaining_size = size; 298 process, dest_addr, size,
432 std::size_t page_index = src_addr >> PAGE_BITS; 299 [this, &process, &dest_addr, &src_addr, size](const std::size_t copy_amount,
433 std::size_t page_offset = src_addr & PAGE_MASK; 300 const VAddr current_vaddr) {
434
435 while (remaining_size > 0) {
436 const std::size_t copy_amount =
437 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
438 const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
439
440 const auto [pointer, type] = page_table.pointers[page_index].PointerType();
441 switch (type) {
442 case Common::PageType::Unmapped: {
443 LOG_ERROR(HW_Memory, 301 LOG_ERROR(HW_Memory,
444 "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", 302 "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
445 current_vaddr, src_addr, size); 303 current_vaddr, src_addr, size);
446 ZeroBlock(process, dest_addr, copy_amount); 304 ZeroBlock(process, dest_addr, copy_amount);
447 break; 305 },
448 } 306 [this, &process, &dest_addr](const std::size_t copy_amount, const u8* const src_ptr) {
449 case Common::PageType::Memory: { 307 WriteBlockImpl<false>(process, dest_addr, src_ptr, copy_amount);
450 DEBUG_ASSERT(pointer); 308 },
451 const u8* src_ptr = pointer + page_offset + (page_index << PAGE_BITS); 309 [this, &system = system, &process, &dest_addr](
452 WriteBlock(process, dest_addr, src_ptr, copy_amount); 310 const VAddr current_vaddr, const std::size_t copy_amount, u8* const host_ptr) {
453 break;
454 }
455 case Common::PageType::RasterizerCachedMemory: {
456 const u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)};
457 system.GPU().FlushRegion(current_vaddr, copy_amount); 311 system.GPU().FlushRegion(current_vaddr, copy_amount);
458 WriteBlock(process, dest_addr, host_ptr, copy_amount); 312 WriteBlockImpl<false>(process, dest_addr, host_ptr, copy_amount);
459 break; 313 },
460 } 314 [&dest_addr, &src_addr](const std::size_t copy_amount) {
461 default: 315 dest_addr += static_cast<VAddr>(copy_amount);
462 UNREACHABLE(); 316 src_addr += static_cast<VAddr>(copy_amount);
463 } 317 });
464
465 page_index++;
466 page_offset = 0;
467 dest_addr += static_cast<VAddr>(copy_amount);
468 src_addr += static_cast<VAddr>(copy_amount);
469 remaining_size -= copy_amount;
470 }
471 }
472
473 void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size) {
474 return CopyBlock(*system.CurrentProcess(), dest_addr, src_addr, size);
475 } 318 }
476 319
477 void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) { 320 void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
@@ -514,7 +357,7 @@ struct Memory::Impl {
514 } else { 357 } else {
515 // Switch page type to uncached if now uncached 358 // Switch page type to uncached if now uncached
516 switch (page_type) { 359 switch (page_type) {
517 case Common::PageType::Unmapped: 360 case Common::PageType::Unmapped: // NOLINT(bugprone-branch-clone)
518 // It is not necessary for a process to have this region mapped into its address 361 // It is not necessary for a process to have this region mapped into its address
519 // space, for example, a system module need not have a VRAM mapping. 362 // space, for example, a system module need not have a VRAM mapping.
520 break; 363 break;
@@ -597,52 +440,68 @@ struct Memory::Impl {
597 } 440 }
598 } 441 }
599 442
600 /** 443 [[nodiscard]] u8* GetPointerImpl(VAddr vaddr, auto on_unmapped, auto on_rasterizer) const {
601 * Reads a particular data type out of memory at the given virtual address.
602 *
603 * @param vaddr The virtual address to read the data type from.
604 *
605 * @tparam T The data type to read out of memory. This type *must* be
606 * trivially copyable, otherwise the behavior of this function
607 * is undefined.
608 *
609 * @returns The instance of T read from the specified virtual address.
610 */
611 template <typename T>
612 T Read(VAddr vaddr) {
613 // AARCH64 masks the upper 16 bit of all memory accesses 444 // AARCH64 masks the upper 16 bit of all memory accesses
614 vaddr &= 0xffffffffffffLL; 445 vaddr &= 0xffffffffffffLL;
615 446
616 if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) { 447 if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) {
617 LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr); 448 on_unmapped();
618 return 0; 449 return nullptr;
619 } 450 }
620 451
621 // Avoid adding any extra logic to this fast-path block 452 // Avoid adding any extra logic to this fast-path block
622 const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw(); 453 const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
623 if (const u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) { 454 if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
624 T value; 455 return &pointer[vaddr];
625 std::memcpy(&value, &pointer[vaddr], sizeof(T));
626 return value;
627 } 456 }
628 switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) { 457 switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
629 case Common::PageType::Unmapped: 458 case Common::PageType::Unmapped:
630 LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr); 459 on_unmapped();
631 return 0; 460 return nullptr;
632 case Common::PageType::Memory: 461 case Common::PageType::Memory:
633 ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); 462 ASSERT_MSG(false, "Mapped memory page without a pointer @ 0x{:016X}", vaddr);
634 break; 463 return nullptr;
635 case Common::PageType::RasterizerCachedMemory: { 464 case Common::PageType::RasterizerCachedMemory: {
636 const u8* const host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)}; 465 u8* const host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};
637 system.GPU().FlushRegion(vaddr, sizeof(T)); 466 on_rasterizer();
638 T value; 467 return host_ptr;
639 std::memcpy(&value, host_ptr, sizeof(T));
640 return value;
641 } 468 }
642 default: 469 default:
643 UNREACHABLE(); 470 UNREACHABLE();
644 } 471 }
645 return {}; 472 return nullptr;
473 }
474
475 [[nodiscard]] u8* GetPointer(const VAddr vaddr) const {
476 return GetPointerImpl(
477 vaddr, [vaddr]() { LOG_ERROR(HW_Memory, "Unmapped GetPointer @ 0x{:016X}", vaddr); },
478 []() {});
479 }
480
481 /**
482 * Reads a particular data type out of memory at the given virtual address.
483 *
484 * @param vaddr The virtual address to read the data type from.
485 *
486 * @tparam T The data type to read out of memory. This type *must* be
487 * trivially copyable, otherwise the behavior of this function
488 * is undefined.
489 *
490 * @returns The instance of T read from the specified virtual address.
491 */
492 template <typename T>
493 T Read(VAddr vaddr) {
494 T result = 0;
495 const u8* const ptr = GetPointerImpl(
496 vaddr,
497 [vaddr]() {
498 LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:016X}", sizeof(T) * 8, vaddr);
499 },
500 [&system = system, vaddr]() { system.GPU().FlushRegion(vaddr, sizeof(T)); });
501 if (ptr) {
502 std::memcpy(&result, ptr, sizeof(T));
503 }
504 return result;
646 } 505 }
647 506
648 /** 507 /**
@@ -656,110 +515,46 @@ struct Memory::Impl {
656 */ 515 */
657 template <typename T> 516 template <typename T>
658 void Write(VAddr vaddr, const T data) { 517 void Write(VAddr vaddr, const T data) {
659 // AARCH64 masks the upper 16 bit of all memory accesses 518 u8* const ptr = GetPointerImpl(
660 vaddr &= 0xffffffffffffLL; 519 vaddr,
661 520 [vaddr, data]() {
662 if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) { 521 LOG_ERROR(HW_Memory, "Unmapped Write{} @ 0x{:016X} = 0x{:016X}", sizeof(T) * 8,
663 LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, 522 vaddr, static_cast<u64>(data));
664 static_cast<u32>(data), vaddr); 523 },
665 return; 524 [&system = system, vaddr]() { system.GPU().InvalidateRegion(vaddr, sizeof(T)); });
666 } 525 if (ptr) {
667 526 std::memcpy(ptr, &data, sizeof(T));
668 // Avoid adding any extra logic to this fast-path block
669 const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
670 if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
671 std::memcpy(&pointer[vaddr], &data, sizeof(T));
672 return;
673 }
674 switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
675 case Common::PageType::Unmapped:
676 LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
677 static_cast<u32>(data), vaddr);
678 return;
679 case Common::PageType::Memory:
680 ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
681 break;
682 case Common::PageType::RasterizerCachedMemory: {
683 u8* const host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};
684 system.GPU().InvalidateRegion(vaddr, sizeof(T));
685 std::memcpy(host_ptr, &data, sizeof(T));
686 break;
687 }
688 default:
689 UNREACHABLE();
690 } 527 }
691 } 528 }
692 529
693 template <typename T> 530 template <typename T>
694 bool WriteExclusive(VAddr vaddr, const T data, const T expected) { 531 bool WriteExclusive(VAddr vaddr, const T data, const T expected) {
695 // AARCH64 masks the upper 16 bit of all memory accesses 532 u8* const ptr = GetPointerImpl(
696 vaddr &= 0xffffffffffffLL; 533 vaddr,
697 534 [vaddr, data]() {
698 if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) { 535 LOG_ERROR(HW_Memory, "Unmapped WriteExclusive{} @ 0x{:016X} = 0x{:016X}",
699 LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, 536 sizeof(T) * 8, vaddr, static_cast<u64>(data));
700 static_cast<u32>(data), vaddr); 537 },
701 return true; 538 [&system = system, vaddr]() { system.GPU().InvalidateRegion(vaddr, sizeof(T)); });
702 } 539 if (ptr) {
703 540 const auto volatile_pointer = reinterpret_cast<volatile T*>(ptr);
704 const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
705 if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
706 // NOTE: Avoid adding any extra logic to this fast-path block
707 const auto volatile_pointer = reinterpret_cast<volatile T*>(&pointer[vaddr]);
708 return Common::AtomicCompareAndSwap(volatile_pointer, data, expected); 541 return Common::AtomicCompareAndSwap(volatile_pointer, data, expected);
709 } 542 }
710 switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
711 case Common::PageType::Unmapped:
712 LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
713 static_cast<u32>(data), vaddr);
714 return true;
715 case Common::PageType::Memory:
716 ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
717 break;
718 case Common::PageType::RasterizerCachedMemory: {
719 u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};
720 system.GPU().InvalidateRegion(vaddr, sizeof(T));
721 auto* pointer = reinterpret_cast<volatile T*>(&host_ptr);
722 return Common::AtomicCompareAndSwap(pointer, data, expected);
723 }
724 default:
725 UNREACHABLE();
726 }
727 return true; 543 return true;
728 } 544 }
729 545
730 bool WriteExclusive128(VAddr vaddr, const u128 data, const u128 expected) { 546 bool WriteExclusive128(VAddr vaddr, const u128 data, const u128 expected) {
731 // AARCH64 masks the upper 16 bit of all memory accesses 547 u8* const ptr = GetPointerImpl(
732 vaddr &= 0xffffffffffffLL; 548 vaddr,
733 549 [vaddr, data]() {
734 if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) { 550 LOG_ERROR(HW_Memory, "Unmapped WriteExclusive128 @ 0x{:016X} = 0x{:016X}{:016X}",
735 LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, 551 vaddr, static_cast<u64>(data[1]), static_cast<u64>(data[0]));
736 static_cast<u32>(data[0]), vaddr); 552 },
737 return true; 553 [&system = system, vaddr]() { system.GPU().InvalidateRegion(vaddr, sizeof(u128)); });
738 } 554 if (ptr) {
739 555 const auto volatile_pointer = reinterpret_cast<volatile u64*>(ptr);
740 const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
741 if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
742 // NOTE: Avoid adding any extra logic to this fast-path block
743 const auto volatile_pointer = reinterpret_cast<volatile u64*>(&pointer[vaddr]);
744 return Common::AtomicCompareAndSwap(volatile_pointer, data, expected); 556 return Common::AtomicCompareAndSwap(volatile_pointer, data, expected);
745 } 557 }
746 switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
747 case Common::PageType::Unmapped:
748 LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}{:016X}", sizeof(data) * 8,
749 static_cast<u64>(data[1]), static_cast<u64>(data[0]), vaddr);
750 return true;
751 case Common::PageType::Memory:
752 ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
753 break;
754 case Common::PageType::RasterizerCachedMemory: {
755 u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};
756 system.GPU().InvalidateRegion(vaddr, sizeof(u128));
757 auto* pointer = reinterpret_cast<volatile u64*>(&host_ptr);
758 return Common::AtomicCompareAndSwap(pointer, data, expected);
759 }
760 default:
761 UNREACHABLE();
762 }
763 return true; 558 return true;
764 } 559 }
765 560
@@ -789,12 +584,11 @@ void Memory::UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) {
789 impl->UnmapRegion(page_table, base, size); 584 impl->UnmapRegion(page_table, base, size);
790} 585}
791 586
792bool Memory::IsValidVirtualAddress(const Kernel::KProcess& process, const VAddr vaddr) const {
793 return impl->IsValidVirtualAddress(process, vaddr);
794}
795
796bool Memory::IsValidVirtualAddress(const VAddr vaddr) const { 587bool Memory::IsValidVirtualAddress(const VAddr vaddr) const {
797 return impl->IsValidVirtualAddress(vaddr); 588 const Kernel::KProcess& process = *system.CurrentProcess();
589 const auto& page_table = process.PageTable().PageTableImpl();
590 const auto [pointer, type] = page_table.pointers[vaddr >> PAGE_BITS].PointerType();
591 return pointer != nullptr || type == Common::PageType::RasterizerCachedMemory;
798} 592}
799 593
800u8* Memory::GetPointer(VAddr vaddr) { 594u8* Memory::GetPointer(VAddr vaddr) {
@@ -863,64 +657,38 @@ std::string Memory::ReadCString(VAddr vaddr, std::size_t max_length) {
863 657
864void Memory::ReadBlock(const Kernel::KProcess& process, const VAddr src_addr, void* dest_buffer, 658void Memory::ReadBlock(const Kernel::KProcess& process, const VAddr src_addr, void* dest_buffer,
865 const std::size_t size) { 659 const std::size_t size) {
866 impl->ReadBlock(process, src_addr, dest_buffer, size); 660 impl->ReadBlockImpl<false>(process, src_addr, dest_buffer, size);
867} 661}
868 662
869void Memory::ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t size) { 663void Memory::ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t size) {
870 impl->ReadBlock(src_addr, dest_buffer, size); 664 impl->ReadBlock(src_addr, dest_buffer, size);
871} 665}
872 666
873void Memory::ReadBlockUnsafe(const Kernel::KProcess& process, const VAddr src_addr,
874 void* dest_buffer, const std::size_t size) {
875 impl->ReadBlockUnsafe(process, src_addr, dest_buffer, size);
876}
877
878void Memory::ReadBlockUnsafe(const VAddr src_addr, void* dest_buffer, const std::size_t size) { 667void Memory::ReadBlockUnsafe(const VAddr src_addr, void* dest_buffer, const std::size_t size) {
879 impl->ReadBlockUnsafe(src_addr, dest_buffer, size); 668 impl->ReadBlockUnsafe(src_addr, dest_buffer, size);
880} 669}
881 670
882void Memory::WriteBlock(const Kernel::KProcess& process, VAddr dest_addr, const void* src_buffer, 671void Memory::WriteBlock(const Kernel::KProcess& process, VAddr dest_addr, const void* src_buffer,
883 std::size_t size) { 672 std::size_t size) {
884 impl->WriteBlock(process, dest_addr, src_buffer, size); 673 impl->WriteBlockImpl<false>(process, dest_addr, src_buffer, size);
885} 674}
886 675
887void Memory::WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) { 676void Memory::WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) {
888 impl->WriteBlock(dest_addr, src_buffer, size); 677 impl->WriteBlock(dest_addr, src_buffer, size);
889} 678}
890 679
891void Memory::WriteBlockUnsafe(const Kernel::KProcess& process, VAddr dest_addr,
892 const void* src_buffer, std::size_t size) {
893 impl->WriteBlockUnsafe(process, dest_addr, src_buffer, size);
894}
895
896void Memory::WriteBlockUnsafe(const VAddr dest_addr, const void* src_buffer, 680void Memory::WriteBlockUnsafe(const VAddr dest_addr, const void* src_buffer,
897 const std::size_t size) { 681 const std::size_t size) {
898 impl->WriteBlockUnsafe(dest_addr, src_buffer, size); 682 impl->WriteBlockUnsafe(dest_addr, src_buffer, size);
899} 683}
900 684
901void Memory::ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size) {
902 impl->ZeroBlock(process, dest_addr, size);
903}
904
905void Memory::ZeroBlock(VAddr dest_addr, std::size_t size) {
906 impl->ZeroBlock(dest_addr, size);
907}
908
909void Memory::CopyBlock(const Kernel::KProcess& process, VAddr dest_addr, VAddr src_addr, 685void Memory::CopyBlock(const Kernel::KProcess& process, VAddr dest_addr, VAddr src_addr,
910 const std::size_t size) { 686 const std::size_t size) {
911 impl->CopyBlock(process, dest_addr, src_addr, size); 687 impl->CopyBlock(process, dest_addr, src_addr, size);
912} 688}
913 689
914void Memory::CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size) {
915 impl->CopyBlock(dest_addr, src_addr, size);
916}
917
918void Memory::RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) { 690void Memory::RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
919 impl->RasterizerMarkRegionCached(vaddr, size, cached); 691 impl->RasterizerMarkRegionCached(vaddr, size, cached);
920} 692}
921 693
922bool IsKernelVirtualAddress(const VAddr vaddr) {
923 return KERNEL_REGION_VADDR <= vaddr && vaddr < KERNEL_REGION_END;
924}
925
926} // namespace Core::Memory 694} // namespace Core::Memory
diff --git a/src/core/memory.h b/src/core/memory.h
index c91eeced9..b5721b740 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -39,11 +39,6 @@ enum : VAddr {
39 39
40 /// Application stack 40 /// Application stack
41 DEFAULT_STACK_SIZE = 0x100000, 41 DEFAULT_STACK_SIZE = 0x100000,
42
43 /// Kernel Virtual Address Range
44 KERNEL_REGION_VADDR = 0xFFFFFF8000000000,
45 KERNEL_REGION_SIZE = 0x7FFFE00000,
46 KERNEL_REGION_END = KERNEL_REGION_VADDR + KERNEL_REGION_SIZE,
47}; 42};
48 43
49/// Central class that handles all memory operations and state. 44/// Central class that handles all memory operations and state.
@@ -56,7 +51,7 @@ public:
56 Memory& operator=(const Memory&) = delete; 51 Memory& operator=(const Memory&) = delete;
57 52
58 Memory(Memory&&) = default; 53 Memory(Memory&&) = default;
59 Memory& operator=(Memory&&) = default; 54 Memory& operator=(Memory&&) = delete;
60 55
61 /** 56 /**
62 * Resets the state of the Memory system. 57 * Resets the state of the Memory system.
@@ -92,24 +87,13 @@ public:
92 87
93 /** 88 /**
94 * Checks whether or not the supplied address is a valid virtual 89 * Checks whether or not the supplied address is a valid virtual
95 * address for the given process.
96 *
97 * @param process The emulated process to check the address against.
98 * @param vaddr The virtual address to check the validity of.
99 *
100 * @returns True if the given virtual address is valid, false otherwise.
101 */
102 bool IsValidVirtualAddress(const Kernel::KProcess& process, VAddr vaddr) const;
103
104 /**
105 * Checks whether or not the supplied address is a valid virtual
106 * address for the current process. 90 * address for the current process.
107 * 91 *
108 * @param vaddr The virtual address to check the validity of. 92 * @param vaddr The virtual address to check the validity of.
109 * 93 *
110 * @returns True if the given virtual address is valid, false otherwise. 94 * @returns True if the given virtual address is valid, false otherwise.
111 */ 95 */
112 bool IsValidVirtualAddress(VAddr vaddr) const; 96 [[nodiscard]] bool IsValidVirtualAddress(VAddr vaddr) const;
113 97
114 /** 98 /**
115 * Gets a pointer to the given address. 99 * Gets a pointer to the given address.
@@ -134,7 +118,7 @@ public:
134 * @returns The pointer to the given address, if the address is valid. 118 * @returns The pointer to the given address, if the address is valid.
135 * If the address is not valid, nullptr will be returned. 119 * If the address is not valid, nullptr will be returned.
136 */ 120 */
137 const u8* GetPointer(VAddr vaddr) const; 121 [[nodiscard]] const u8* GetPointer(VAddr vaddr) const;
138 122
139 template <typename T> 123 template <typename T>
140 const T* GetPointer(VAddr vaddr) const { 124 const T* GetPointer(VAddr vaddr) const {
@@ -328,27 +312,6 @@ public:
328 std::size_t size); 312 std::size_t size);
329 313
330 /** 314 /**
331 * Reads a contiguous block of bytes from a specified process' address space.
332 * This unsafe version does not trigger GPU flushing.
333 *
334 * @param process The process to read the data from.
335 * @param src_addr The virtual address to begin reading from.
336 * @param dest_buffer The buffer to place the read bytes into.
337 * @param size The amount of data to read, in bytes.
338 *
339 * @note If a size of 0 is specified, then this function reads nothing and
340 * no attempts to access memory are made at all.
341 *
342 * @pre dest_buffer must be at least size bytes in length, otherwise a
343 * buffer overrun will occur.
344 *
345 * @post The range [dest_buffer, size) contains the read bytes from the
346 * process' address space.
347 */
348 void ReadBlockUnsafe(const Kernel::KProcess& process, VAddr src_addr, void* dest_buffer,
349 std::size_t size);
350
351 /**
352 * Reads a contiguous block of bytes from the current process' address space. 315 * Reads a contiguous block of bytes from the current process' address space.
353 * 316 *
354 * @param src_addr The virtual address to begin reading from. 317 * @param src_addr The virtual address to begin reading from.
@@ -409,26 +372,6 @@ public:
409 std::size_t size); 372 std::size_t size);
410 373
411 /** 374 /**
412 * Writes a range of bytes into a given process' address space at the specified
413 * virtual address.
414 * This unsafe version does not invalidate GPU Memory.
415 *
416 * @param process The process to write data into the address space of.
417 * @param dest_addr The destination virtual address to begin writing the data at.
418 * @param src_buffer The data to write into the process' address space.
419 * @param size The size of the data to write, in bytes.
420 *
421 * @post The address range [dest_addr, size) in the process' address space
422 * contains the data that was within src_buffer.
423 *
424 * @post If an attempt is made to write into an unmapped region of memory, the writes
425 * will be ignored and an error will be logged.
426 *
427 */
428 void WriteBlockUnsafe(const Kernel::KProcess& process, VAddr dest_addr, const void* src_buffer,
429 std::size_t size);
430
431 /**
432 * Writes a range of bytes into the current process' address space at the specified 375 * Writes a range of bytes into the current process' address space at the specified
433 * virtual address. 376 * virtual address.
434 * 377 *
@@ -468,29 +411,6 @@ public:
468 void WriteBlockUnsafe(VAddr dest_addr, const void* src_buffer, std::size_t size); 411 void WriteBlockUnsafe(VAddr dest_addr, const void* src_buffer, std::size_t size);
469 412
470 /** 413 /**
471 * Fills the specified address range within a process' address space with zeroes.
472 *
473 * @param process The process that will have a portion of its memory zeroed out.
474 * @param dest_addr The starting virtual address of the range to zero out.
475 * @param size The size of the address range to zero out, in bytes.
476 *
477 * @post The range [dest_addr, size) within the process' address space is
478 * filled with zeroes.
479 */
480 void ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size);
481
482 /**
483 * Fills the specified address range within the current process' address space with zeroes.
484 *
485 * @param dest_addr The starting virtual address of the range to zero out.
486 * @param size The size of the address range to zero out, in bytes.
487 *
488 * @post The range [dest_addr, size) within the current process' address space is
489 * filled with zeroes.
490 */
491 void ZeroBlock(VAddr dest_addr, std::size_t size);
492
493 /**
494 * Copies data within a process' address space to another location within the 414 * Copies data within a process' address space to another location within the
495 * same address space. 415 * same address space.
496 * 416 *
@@ -506,19 +426,6 @@ public:
506 std::size_t size); 426 std::size_t size);
507 427
508 /** 428 /**
509 * Copies data within the current process' address space to another location within the
510 * same address space.
511 *
512 * @param dest_addr The destination virtual address to begin copying the data into.
513 * @param src_addr The source virtual address to begin copying the data from.
514 * @param size The size of the data to copy, in bytes.
515 *
516 * @post The range [dest_addr, size) within the current process' address space
517 * contains the same data within the range [src_addr, size).
518 */
519 void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size);
520
521 /**
522 * Marks each page within the specified address range as cached or uncached. 429 * Marks each page within the specified address range as cached or uncached.
523 * 430 *
524 * @param vaddr The virtual address indicating the start of the address range. 431 * @param vaddr The virtual address indicating the start of the address range.
@@ -535,7 +442,4 @@ private:
535 std::unique_ptr<Impl> impl; 442 std::unique_ptr<Impl> impl;
536}; 443};
537 444
538/// Determines if the given VAddr is a kernel address
539bool IsKernelVirtualAddress(VAddr vaddr);
540
541} // namespace Core::Memory 445} // namespace Core::Memory
diff --git a/src/core/network/network.cpp b/src/core/network/network.cpp
index 526bfa110..375bc79ec 100644
--- a/src/core/network/network.cpp
+++ b/src/core/network/network.cpp
@@ -570,7 +570,7 @@ std::pair<s32, Errno> Socket::SendTo(u32 flags, const std::vector<u8>& message,
570 ASSERT(flags == 0); 570 ASSERT(flags == 0);
571 571
572 const sockaddr* to = nullptr; 572 const sockaddr* to = nullptr;
573 const int tolen = addr ? 0 : sizeof(sockaddr); 573 const int tolen = addr ? sizeof(sockaddr) : 0;
574 sockaddr host_addr_in; 574 sockaddr host_addr_in;
575 575
576 if (addr) { 576 if (addr) {
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index 8de3d4520..ff23230f0 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -304,10 +304,10 @@ std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers([
304} 304}
305 305
306std::string GenerateKeyboardParam(int key_code) { 306std::string GenerateKeyboardParam(int key_code) {
307 Common::ParamPackage param{ 307 Common::ParamPackage param;
308 {"engine", "keyboard"}, 308 param.Set("engine", "keyboard");
309 {"code", std::to_string(key_code)}, 309 param.Set("code", key_code);
310 }; 310 param.Set("toggle", false);
311 return param.Serialize(); 311 return param.Serialize();
312} 312}
313 313
diff --git a/src/input_common/mouse/mouse_poller.cpp b/src/input_common/mouse/mouse_poller.cpp
index efcdd85d2..090b26972 100644
--- a/src/input_common/mouse/mouse_poller.cpp
+++ b/src/input_common/mouse/mouse_poller.cpp
@@ -57,6 +57,7 @@ Common::ParamPackage MouseButtonFactory::GetNextInput() const {
57 if (pad.button != MouseInput::MouseButton::Undefined) { 57 if (pad.button != MouseInput::MouseButton::Undefined) {
58 params.Set("engine", "mouse"); 58 params.Set("engine", "mouse");
59 params.Set("button", static_cast<u16>(pad.button)); 59 params.Set("button", static_cast<u16>(pad.button));
60 params.Set("toggle", false);
60 return params; 61 return params;
61 } 62 }
62 } 63 }
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
index 70a0ba09c..f1f950d8a 100644
--- a/src/input_common/sdl/sdl_impl.cpp
+++ b/src/input_common/sdl/sdl_impl.cpp
@@ -82,6 +82,12 @@ public:
82 state.buttons.insert_or_assign(button, value); 82 state.buttons.insert_or_assign(button, value);
83 } 83 }
84 84
85 void PreSetButton(int button) {
86 if (!state.buttons.contains(button)) {
87 SetButton(button, false);
88 }
89 }
90
85 void SetMotion(SDL_ControllerSensorEvent event) { 91 void SetMotion(SDL_ControllerSensorEvent event) {
86 constexpr float gravity_constant = 9.80665f; 92 constexpr float gravity_constant = 9.80665f;
87 std::lock_guard lock{mutex}; 93 std::lock_guard lock{mutex};
@@ -155,9 +161,16 @@ public:
155 state.axes.insert_or_assign(axis, value); 161 state.axes.insert_or_assign(axis, value);
156 } 162 }
157 163
158 float GetAxis(int axis, float range) const { 164 void PreSetAxis(int axis) {
165 if (!state.axes.contains(axis)) {
166 SetAxis(axis, 0);
167 }
168 }
169
170 float GetAxis(int axis, float range, float offset) const {
159 std::lock_guard lock{mutex}; 171 std::lock_guard lock{mutex};
160 return static_cast<float>(state.axes.at(axis)) / (32767.0f * range); 172 const float value = static_cast<float>(state.axes.at(axis)) / 32767.0f;
173 return (value + offset) / range;
161 } 174 }
162 175
163 bool RumblePlay(u16 amp_low, u16 amp_high) { 176 bool RumblePlay(u16 amp_low, u16 amp_high) {
@@ -174,9 +187,10 @@ public:
174 return false; 187 return false;
175 } 188 }
176 189
177 std::tuple<float, float> GetAnalog(int axis_x, int axis_y, float range) const { 190 std::tuple<float, float> GetAnalog(int axis_x, int axis_y, float range, float offset_x,
178 float x = GetAxis(axis_x, range); 191 float offset_y) const {
179 float y = GetAxis(axis_y, range); 192 float x = GetAxis(axis_x, range, offset_x);
193 float y = GetAxis(axis_y, range, offset_y);
180 y = -y; // 3DS uses an y-axis inverse from SDL 194 y = -y; // 3DS uses an y-axis inverse from SDL
181 195
182 // Make sure the coordinates are in the unit circle, 196 // Make sure the coordinates are in the unit circle,
@@ -483,7 +497,7 @@ public:
483 trigger_if_greater(trigger_if_greater_) {} 497 trigger_if_greater(trigger_if_greater_) {}
484 498
485 bool GetStatus() const override { 499 bool GetStatus() const override {
486 const float axis_value = joystick->GetAxis(axis, 1.0f); 500 const float axis_value = joystick->GetAxis(axis, 1.0f, 0.0f);
487 if (trigger_if_greater) { 501 if (trigger_if_greater) {
488 return axis_value > threshold; 502 return axis_value > threshold;
489 } 503 }
@@ -500,12 +514,14 @@ private:
500class SDLAnalog final : public Input::AnalogDevice { 514class SDLAnalog final : public Input::AnalogDevice {
501public: 515public:
502 explicit SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_, 516 explicit SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_,
503 bool invert_x_, bool invert_y_, float deadzone_, float range_) 517 bool invert_x_, bool invert_y_, float deadzone_, float range_,
518 float offset_x_, float offset_y_)
504 : joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), invert_x(invert_x_), 519 : joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), invert_x(invert_x_),
505 invert_y(invert_y_), deadzone(deadzone_), range(range_) {} 520 invert_y(invert_y_), deadzone(deadzone_), range(range_), offset_x(offset_x_),
521 offset_y(offset_y_) {}
506 522
507 std::tuple<float, float> GetStatus() const override { 523 std::tuple<float, float> GetStatus() const override {
508 auto [x, y] = joystick->GetAnalog(axis_x, axis_y, range); 524 auto [x, y] = joystick->GetAnalog(axis_x, axis_y, range, offset_x, offset_y);
509 const float r = std::sqrt((x * x) + (y * y)); 525 const float r = std::sqrt((x * x) + (y * y));
510 if (invert_x) { 526 if (invert_x) {
511 x = -x; 527 x = -x;
@@ -522,8 +538,8 @@ public:
522 } 538 }
523 539
524 std::tuple<float, float> GetRawStatus() const override { 540 std::tuple<float, float> GetRawStatus() const override {
525 const float x = joystick->GetAxis(axis_x, range); 541 const float x = joystick->GetAxis(axis_x, range, offset_x);
526 const float y = joystick->GetAxis(axis_y, range); 542 const float y = joystick->GetAxis(axis_y, range, offset_y);
527 return {x, -y}; 543 return {x, -y};
528 } 544 }
529 545
@@ -555,6 +571,8 @@ private:
555 const bool invert_y; 571 const bool invert_y;
556 const float deadzone; 572 const float deadzone;
557 const float range; 573 const float range;
574 const float offset_x;
575 const float offset_y;
558}; 576};
559 577
560class SDLVibration final : public Input::VibrationDevice { 578class SDLVibration final : public Input::VibrationDevice {
@@ -621,7 +639,7 @@ public:
621 trigger_if_greater(trigger_if_greater_) {} 639 trigger_if_greater(trigger_if_greater_) {}
622 640
623 Input::MotionStatus GetStatus() const override { 641 Input::MotionStatus GetStatus() const override {
624 const float axis_value = joystick->GetAxis(axis, 1.0f); 642 const float axis_value = joystick->GetAxis(axis, 1.0f, 0.0f);
625 bool trigger = axis_value < threshold; 643 bool trigger = axis_value < threshold;
626 if (trigger_if_greater) { 644 if (trigger_if_greater) {
627 trigger = axis_value > threshold; 645 trigger = axis_value > threshold;
@@ -720,13 +738,13 @@ public:
720 LOG_ERROR(Input, "Unknown direction {}", direction_name); 738 LOG_ERROR(Input, "Unknown direction {}", direction_name);
721 } 739 }
722 // This is necessary so accessing GetAxis with axis won't crash 740 // This is necessary so accessing GetAxis with axis won't crash
723 joystick->SetAxis(axis, 0); 741 joystick->PreSetAxis(axis);
724 return std::make_unique<SDLAxisButton>(joystick, axis, threshold, trigger_if_greater); 742 return std::make_unique<SDLAxisButton>(joystick, axis, threshold, trigger_if_greater);
725 } 743 }
726 744
727 const int button = params.Get("button", 0); 745 const int button = params.Get("button", 0);
728 // This is necessary so accessing GetButton with button won't crash 746 // This is necessary so accessing GetButton with button won't crash
729 joystick->SetButton(button, false); 747 joystick->PreSetButton(button);
730 return std::make_unique<SDLButton>(joystick, button, toggle); 748 return std::make_unique<SDLButton>(joystick, button, toggle);
731 } 749 }
732 750
@@ -757,13 +775,15 @@ public:
757 const std::string invert_y_value = params.Get("invert_y", "+"); 775 const std::string invert_y_value = params.Get("invert_y", "+");
758 const bool invert_x = invert_x_value == "-"; 776 const bool invert_x = invert_x_value == "-";
759 const bool invert_y = invert_y_value == "-"; 777 const bool invert_y = invert_y_value == "-";
778 const float offset_x = params.Get("offset_x", 0.0f);
779 const float offset_y = params.Get("offset_y", 0.0f);
760 auto joystick = state.GetSDLJoystickByGUID(guid, port); 780 auto joystick = state.GetSDLJoystickByGUID(guid, port);
761 781
762 // This is necessary so accessing GetAxis with axis_x and axis_y won't crash 782 // This is necessary so accessing GetAxis with axis_x and axis_y won't crash
763 joystick->SetAxis(axis_x, 0); 783 joystick->PreSetAxis(axis_x);
764 joystick->SetAxis(axis_y, 0); 784 joystick->PreSetAxis(axis_y);
765 return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y, invert_x, invert_y, deadzone, 785 return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y, invert_x, invert_y, deadzone,
766 range); 786 range, offset_x, offset_y);
767 } 787 }
768 788
769private: 789private:
@@ -844,13 +864,13 @@ public:
844 LOG_ERROR(Input, "Unknown direction {}", direction_name); 864 LOG_ERROR(Input, "Unknown direction {}", direction_name);
845 } 865 }
846 // This is necessary so accessing GetAxis with axis won't crash 866 // This is necessary so accessing GetAxis with axis won't crash
847 joystick->SetAxis(axis, 0); 867 joystick->PreSetAxis(axis);
848 return std::make_unique<SDLAxisMotion>(joystick, axis, threshold, trigger_if_greater); 868 return std::make_unique<SDLAxisMotion>(joystick, axis, threshold, trigger_if_greater);
849 } 869 }
850 870
851 const int button = params.Get("button", 0); 871 const int button = params.Get("button", 0);
852 // This is necessary so accessing GetButton with button won't crash 872 // This is necessary so accessing GetButton with button won't crash
853 joystick->SetButton(button, false); 873 joystick->PreSetButton(button);
854 return std::make_unique<SDLButtonMotion>(joystick, button); 874 return std::make_unique<SDLButtonMotion>(joystick, button);
855 } 875 }
856 876
@@ -995,6 +1015,7 @@ Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid
995 params.Set("port", port); 1015 params.Set("port", port);
996 params.Set("guid", std::move(guid)); 1016 params.Set("guid", std::move(guid));
997 params.Set("button", button); 1017 params.Set("button", button);
1018 params.Set("toggle", false);
998 return params; 1019 return params;
999} 1020}
1000 1021
@@ -1134,13 +1155,15 @@ Common::ParamPackage BuildParamPackageForBinding(int port, const std::string& gu
1134} 1155}
1135 1156
1136Common::ParamPackage BuildParamPackageForAnalog(int port, const std::string& guid, int axis_x, 1157Common::ParamPackage BuildParamPackageForAnalog(int port, const std::string& guid, int axis_x,
1137 int axis_y) { 1158 int axis_y, float offset_x, float offset_y) {
1138 Common::ParamPackage params; 1159 Common::ParamPackage params;
1139 params.Set("engine", "sdl"); 1160 params.Set("engine", "sdl");
1140 params.Set("port", port); 1161 params.Set("port", port);
1141 params.Set("guid", guid); 1162 params.Set("guid", guid);
1142 params.Set("axis_x", axis_x); 1163 params.Set("axis_x", axis_x);
1143 params.Set("axis_y", axis_y); 1164 params.Set("axis_y", axis_y);
1165 params.Set("offset_x", offset_x);
1166 params.Set("offset_y", offset_y);
1144 params.Set("invert_x", "+"); 1167 params.Set("invert_x", "+");
1145 params.Set("invert_y", "+"); 1168 params.Set("invert_y", "+");
1146 return params; 1169 return params;
@@ -1342,24 +1365,39 @@ AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& pa
1342 const auto& binding_left_y = 1365 const auto& binding_left_y =
1343 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY); 1366 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
1344 if (params.Has("guid2")) { 1367 if (params.Has("guid2")) {
1368 joystick2->PreSetAxis(binding_left_x.value.axis);
1369 joystick2->PreSetAxis(binding_left_y.value.axis);
1370 const auto left_offset_x = -joystick2->GetAxis(binding_left_x.value.axis, 1.0f, 0);
1371 const auto left_offset_y = -joystick2->GetAxis(binding_left_y.value.axis, 1.0f, 0);
1345 mapping.insert_or_assign( 1372 mapping.insert_or_assign(
1346 Settings::NativeAnalog::LStick, 1373 Settings::NativeAnalog::LStick,
1347 BuildParamPackageForAnalog(joystick2->GetPort(), joystick2->GetGUID(), 1374 BuildParamPackageForAnalog(joystick2->GetPort(), joystick2->GetGUID(),
1348 binding_left_x.value.axis, binding_left_y.value.axis)); 1375 binding_left_x.value.axis, binding_left_y.value.axis,
1376 left_offset_x, left_offset_y));
1349 } else { 1377 } else {
1378 joystick->PreSetAxis(binding_left_x.value.axis);
1379 joystick->PreSetAxis(binding_left_y.value.axis);
1380 const auto left_offset_x = -joystick->GetAxis(binding_left_x.value.axis, 1.0f, 0);
1381 const auto left_offset_y = -joystick->GetAxis(binding_left_y.value.axis, 1.0f, 0);
1350 mapping.insert_or_assign( 1382 mapping.insert_or_assign(
1351 Settings::NativeAnalog::LStick, 1383 Settings::NativeAnalog::LStick,
1352 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), 1384 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
1353 binding_left_x.value.axis, binding_left_y.value.axis)); 1385 binding_left_x.value.axis, binding_left_y.value.axis,
1386 left_offset_x, left_offset_y));
1354 } 1387 }
1355 const auto& binding_right_x = 1388 const auto& binding_right_x =
1356 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX); 1389 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX);
1357 const auto& binding_right_y = 1390 const auto& binding_right_y =
1358 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY); 1391 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY);
1392 joystick->PreSetAxis(binding_right_x.value.axis);
1393 joystick->PreSetAxis(binding_right_y.value.axis);
1394 const auto right_offset_x = -joystick->GetAxis(binding_right_x.value.axis, 1.0f, 0);
1395 const auto right_offset_y = -joystick->GetAxis(binding_right_y.value.axis, 1.0f, 0);
1359 mapping.insert_or_assign(Settings::NativeAnalog::RStick, 1396 mapping.insert_or_assign(Settings::NativeAnalog::RStick,
1360 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), 1397 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
1361 binding_right_x.value.axis, 1398 binding_right_x.value.axis,
1362 binding_right_y.value.axis)); 1399 binding_right_y.value.axis, right_offset_x,
1400 right_offset_y));
1363 return mapping; 1401 return mapping;
1364} 1402}
1365 1403
@@ -1563,8 +1601,9 @@ public:
1563 } 1601 }
1564 1602
1565 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) { 1603 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) {
1604 // Set offset to zero since the joystick is not on center
1566 auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), 1605 auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
1567 first_axis, axis); 1606 first_axis, axis, 0, 0);
1568 first_axis = -1; 1607 first_axis = -1;
1569 return params; 1608 return params;
1570 } 1609 }
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 007ecc13e..2f6cdd216 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -1,5 +1,10 @@
1add_subdirectory(host_shaders) 1add_subdirectory(host_shaders)
2 2
3if(LIBVA_FOUND)
4 set_source_files_properties(command_classes/codecs/codec.cpp
5 PROPERTIES COMPILE_DEFINITIONS LIBVA_FOUND=1)
6endif()
7
3add_library(video_core STATIC 8add_library(video_core STATIC
4 buffer_cache/buffer_base.h 9 buffer_cache/buffer_base.h
5 buffer_cache/buffer_cache.cpp 10 buffer_cache/buffer_cache.cpp
@@ -92,6 +97,7 @@ add_library(video_core STATIC
92 renderer_opengl/gl_stream_buffer.h 97 renderer_opengl/gl_stream_buffer.h
93 renderer_opengl/gl_texture_cache.cpp 98 renderer_opengl/gl_texture_cache.cpp
94 renderer_opengl/gl_texture_cache.h 99 renderer_opengl/gl_texture_cache.h
100 renderer_opengl/gl_texture_cache_base.cpp
95 renderer_opengl/gl_query_cache.cpp 101 renderer_opengl/gl_query_cache.cpp
96 renderer_opengl/gl_query_cache.h 102 renderer_opengl/gl_query_cache.h
97 renderer_opengl/maxwell_to_gl.h 103 renderer_opengl/maxwell_to_gl.h
@@ -106,6 +112,8 @@ add_library(video_core STATIC
106 renderer_vulkan/maxwell_to_vk.cpp 112 renderer_vulkan/maxwell_to_vk.cpp
107 renderer_vulkan/maxwell_to_vk.h 113 renderer_vulkan/maxwell_to_vk.h
108 renderer_vulkan/pipeline_helper.h 114 renderer_vulkan/pipeline_helper.h
115 renderer_vulkan/pipeline_statistics.cpp
116 renderer_vulkan/pipeline_statistics.h
109 renderer_vulkan/renderer_vulkan.h 117 renderer_vulkan/renderer_vulkan.h
110 renderer_vulkan/renderer_vulkan.cpp 118 renderer_vulkan/renderer_vulkan.cpp
111 renderer_vulkan/vk_blit_screen.cpp 119 renderer_vulkan/vk_blit_screen.cpp
@@ -148,6 +156,7 @@ add_library(video_core STATIC
148 renderer_vulkan/vk_swapchain.h 156 renderer_vulkan/vk_swapchain.h
149 renderer_vulkan/vk_texture_cache.cpp 157 renderer_vulkan/vk_texture_cache.cpp
150 renderer_vulkan/vk_texture_cache.h 158 renderer_vulkan/vk_texture_cache.h
159 renderer_vulkan/vk_texture_cache_base.cpp
151 renderer_vulkan/vk_update_descriptor.cpp 160 renderer_vulkan/vk_update_descriptor.cpp
152 renderer_vulkan/vk_update_descriptor.h 161 renderer_vulkan/vk_update_descriptor.h
153 shader_cache.cpp 162 shader_cache.cpp
@@ -179,6 +188,7 @@ add_library(video_core STATIC
179 texture_cache/samples_helper.h 188 texture_cache/samples_helper.h
180 texture_cache/slot_vector.h 189 texture_cache/slot_vector.h
181 texture_cache/texture_cache.h 190 texture_cache/texture_cache.h
191 texture_cache/texture_cache_base.h
182 texture_cache/types.h 192 texture_cache/types.h
183 texture_cache/util.cpp 193 texture_cache/util.cpp
184 texture_cache/util.h 194 texture_cache/util.h
diff --git a/src/video_core/command_classes/codecs/codec.cpp b/src/video_core/command_classes/codecs/codec.cpp
index 1b4bbc8ac..f798a0053 100644
--- a/src/video_core/command_classes/codecs/codec.cpp
+++ b/src/video_core/command_classes/codecs/codec.cpp
@@ -2,7 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cstring>
6#include <fstream> 5#include <fstream>
7#include <vector> 6#include <vector>
8#include "common/assert.h" 7#include "common/assert.h"
@@ -17,10 +16,47 @@ extern "C" {
17} 16}
18 17
19namespace Tegra { 18namespace Tegra {
19#if defined(LIBVA_FOUND)
20// Hardware acceleration code from FFmpeg/doc/examples/hw_decode.c originally under MIT license
21namespace {
22constexpr std::array<const char*, 2> VAAPI_DRIVERS = {
23 "i915",
24 "amdgpu",
25};
26
27AVPixelFormat GetHwFormat(AVCodecContext*, const AVPixelFormat* pix_fmts) {
28 for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) {
29 if (*p == AV_PIX_FMT_VAAPI) {
30 return AV_PIX_FMT_VAAPI;
31 }
32 }
33 LOG_INFO(Service_NVDRV, "Could not find compatible GPU AV format, falling back to CPU");
34 return *pix_fmts;
35}
36
37bool CreateVaapiHwdevice(AVBufferRef** av_hw_device) {
38 AVDictionary* hwdevice_options = nullptr;
39 av_dict_set(&hwdevice_options, "connection_type", "drm", 0);
40 for (const auto& driver : VAAPI_DRIVERS) {
41 av_dict_set(&hwdevice_options, "kernel_driver", driver, 0);
42 const int hwdevice_error = av_hwdevice_ctx_create(av_hw_device, AV_HWDEVICE_TYPE_VAAPI,
43 nullptr, hwdevice_options, 0);
44 if (hwdevice_error >= 0) {
45 LOG_INFO(Service_NVDRV, "Using VA-API with {}", driver);
46 av_dict_free(&hwdevice_options);
47 return true;
48 }
49 LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed {}", hwdevice_error);
50 }
51 LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed for all drivers");
52 av_dict_free(&hwdevice_options);
53 return false;
54}
55} // namespace
56#endif
20 57
21void AVFrameDeleter(AVFrame* ptr) { 58void AVFrameDeleter(AVFrame* ptr) {
22 av_frame_unref(ptr); 59 av_frame_free(&ptr);
23 av_free(ptr);
24} 60}
25 61
26Codec::Codec(GPU& gpu_, const NvdecCommon::NvdecRegisters& regs) 62Codec::Codec(GPU& gpu_, const NvdecCommon::NvdecRegisters& regs)
@@ -32,19 +68,31 @@ Codec::~Codec() {
32 return; 68 return;
33 } 69 }
34 // Free libav memory 70 // Free libav memory
35 AVFrame* av_frame{nullptr};
36 avcodec_send_packet(av_codec_ctx, nullptr); 71 avcodec_send_packet(av_codec_ctx, nullptr);
37 av_frame = av_frame_alloc(); 72 AVFrame* av_frame = av_frame_alloc();
38 avcodec_receive_frame(av_codec_ctx, av_frame); 73 avcodec_receive_frame(av_codec_ctx, av_frame);
39 avcodec_flush_buffers(av_codec_ctx); 74 avcodec_flush_buffers(av_codec_ctx);
40 75 av_frame_free(&av_frame);
41 av_frame_unref(av_frame);
42 av_free(av_frame);
43 avcodec_close(av_codec_ctx); 76 avcodec_close(av_codec_ctx);
77 av_buffer_unref(&av_hw_device);
78}
79
80void Codec::InitializeHwdec() {
81 // Prioritize integrated GPU to mitigate bandwidth bottlenecks
82#if defined(LIBVA_FOUND)
83 if (CreateVaapiHwdevice(&av_hw_device)) {
84 const auto hw_device_ctx = av_buffer_ref(av_hw_device);
85 ASSERT_MSG(hw_device_ctx, "av_buffer_ref failed");
86 av_codec_ctx->hw_device_ctx = hw_device_ctx;
87 av_codec_ctx->get_format = GetHwFormat;
88 return;
89 }
90#endif
91 // TODO more GPU accelerated decoders
44} 92}
45 93
46void Codec::Initialize() { 94void Codec::Initialize() {
47 AVCodecID codec{AV_CODEC_ID_NONE}; 95 AVCodecID codec;
48 switch (current_codec) { 96 switch (current_codec) {
49 case NvdecCommon::VideoCodec::H264: 97 case NvdecCommon::VideoCodec::H264:
50 codec = AV_CODEC_ID_H264; 98 codec = AV_CODEC_ID_H264;
@@ -53,22 +101,24 @@ void Codec::Initialize() {
53 codec = AV_CODEC_ID_VP9; 101 codec = AV_CODEC_ID_VP9;
54 break; 102 break;
55 default: 103 default:
104 UNIMPLEMENTED_MSG("Unknown codec {}", current_codec);
56 return; 105 return;
57 } 106 }
58 av_codec = avcodec_find_decoder(codec); 107 av_codec = avcodec_find_decoder(codec);
59 av_codec_ctx = avcodec_alloc_context3(av_codec); 108 av_codec_ctx = avcodec_alloc_context3(av_codec);
60 av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0); 109 av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0);
61 110 InitializeHwdec();
62 // TODO(ameerj): libavcodec gpu hw acceleration 111 if (!av_codec_ctx->hw_device_ctx) {
63 112 LOG_INFO(Service_NVDRV, "Using FFmpeg software decoding");
113 }
64 const auto av_error = avcodec_open2(av_codec_ctx, av_codec, nullptr); 114 const auto av_error = avcodec_open2(av_codec_ctx, av_codec, nullptr);
65 if (av_error < 0) { 115 if (av_error < 0) {
66 LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed."); 116 LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed.");
67 avcodec_close(av_codec_ctx); 117 avcodec_close(av_codec_ctx);
118 av_buffer_unref(&av_hw_device);
68 return; 119 return;
69 } 120 }
70 initialized = true; 121 initialized = true;
71 return;
72} 122}
73 123
74void Codec::SetTargetCodec(NvdecCommon::VideoCodec codec) { 124void Codec::SetTargetCodec(NvdecCommon::VideoCodec codec) {
@@ -80,36 +130,64 @@ void Codec::SetTargetCodec(NvdecCommon::VideoCodec codec) {
80 130
81void Codec::Decode() { 131void Codec::Decode() {
82 const bool is_first_frame = !initialized; 132 const bool is_first_frame = !initialized;
83 if (!initialized) { 133 if (is_first_frame) {
84 Initialize(); 134 Initialize();
85 } 135 }
86
87 bool vp9_hidden_frame = false; 136 bool vp9_hidden_frame = false;
88 AVPacket packet{};
89 av_init_packet(&packet);
90 std::vector<u8> frame_data; 137 std::vector<u8> frame_data;
91
92 if (current_codec == NvdecCommon::VideoCodec::H264) { 138 if (current_codec == NvdecCommon::VideoCodec::H264) {
93 frame_data = h264_decoder->ComposeFrameHeader(state, is_first_frame); 139 frame_data = h264_decoder->ComposeFrameHeader(state, is_first_frame);
94 } else if (current_codec == NvdecCommon::VideoCodec::Vp9) { 140 } else if (current_codec == NvdecCommon::VideoCodec::Vp9) {
95 frame_data = vp9_decoder->ComposeFrameHeader(state); 141 frame_data = vp9_decoder->ComposeFrameHeader(state);
96 vp9_hidden_frame = vp9_decoder->WasFrameHidden(); 142 vp9_hidden_frame = vp9_decoder->WasFrameHidden();
97 } 143 }
98 144 AVPacket packet{};
145 av_init_packet(&packet);
99 packet.data = frame_data.data(); 146 packet.data = frame_data.data();
100 packet.size = static_cast<s32>(frame_data.size()); 147 packet.size = static_cast<s32>(frame_data.size());
101 148 if (const int ret = avcodec_send_packet(av_codec_ctx, &packet); ret) {
102 avcodec_send_packet(av_codec_ctx, &packet); 149 LOG_DEBUG(Service_NVDRV, "avcodec_send_packet error {}", ret);
103 150 return;
104 if (!vp9_hidden_frame) { 151 }
105 // Only receive/store visible frames 152 // Only receive/store visible frames
106 AVFramePtr frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter}; 153 if (vp9_hidden_frame) {
107 avcodec_receive_frame(av_codec_ctx, frame.get()); 154 return;
108 av_frames.push(std::move(frame)); 155 }
109 // Limit queue to 10 frames. Workaround for ZLA decode and queue spam 156 AVFrame* hw_frame = av_frame_alloc();
110 if (av_frames.size() > 10) { 157 AVFrame* sw_frame = hw_frame;
111 av_frames.pop(); 158 ASSERT_MSG(hw_frame, "av_frame_alloc hw_frame failed");
112 } 159 if (const int ret = avcodec_receive_frame(av_codec_ctx, hw_frame); ret) {
160 LOG_DEBUG(Service_NVDRV, "avcodec_receive_frame error {}", ret);
161 av_frame_free(&hw_frame);
162 return;
163 }
164 if (!hw_frame->width || !hw_frame->height) {
165 LOG_WARNING(Service_NVDRV, "Zero width or height in frame");
166 av_frame_free(&hw_frame);
167 return;
168 }
169#if defined(LIBVA_FOUND)
170 // Hardware acceleration code from FFmpeg/doc/examples/hw_decode.c under MIT license
171 if (hw_frame->format == AV_PIX_FMT_VAAPI) {
172 sw_frame = av_frame_alloc();
173 ASSERT_MSG(sw_frame, "av_frame_alloc sw_frame failed");
174 // Can't use AV_PIX_FMT_YUV420P and share code with software decoding in vic.cpp
175 // because Intel drivers crash unless using AV_PIX_FMT_NV12
176 sw_frame->format = AV_PIX_FMT_NV12;
177 const int transfer_data_ret = av_hwframe_transfer_data(sw_frame, hw_frame, 0);
178 ASSERT_MSG(!transfer_data_ret, "av_hwframe_transfer_data error {}", transfer_data_ret);
179 av_frame_free(&hw_frame);
180 }
181#endif
182 if (sw_frame->format != AV_PIX_FMT_YUV420P && sw_frame->format != AV_PIX_FMT_NV12) {
183 UNIMPLEMENTED_MSG("Unexpected video format from host graphics: {}", sw_frame->format);
184 av_frame_free(&sw_frame);
185 return;
186 }
187 av_frames.push(AVFramePtr{sw_frame, AVFrameDeleter});
188 if (av_frames.size() > 10) {
189 LOG_TRACE(Service_NVDRV, "av_frames.push overflow dropped frame");
190 av_frames.pop();
113 } 191 }
114} 192}
115 193
@@ -119,7 +197,6 @@ AVFramePtr Codec::GetCurrentFrame() {
119 if (av_frames.empty()) { 197 if (av_frames.empty()) {
120 return AVFramePtr{nullptr, AVFrameDeleter}; 198 return AVFramePtr{nullptr, AVFrameDeleter};
121 } 199 }
122
123 AVFramePtr frame = std::move(av_frames.front()); 200 AVFramePtr frame = std::move(av_frames.front());
124 av_frames.pop(); 201 av_frames.pop();
125 return frame; 202 return frame;
@@ -144,6 +221,5 @@ std::string_view Codec::GetCurrentCodecName() const {
144 default: 221 default:
145 return "Unknown"; 222 return "Unknown";
146 } 223 }
147}; 224}
148
149} // namespace Tegra 225} // namespace Tegra
diff --git a/src/video_core/command_classes/codecs/codec.h b/src/video_core/command_classes/codecs/codec.h
index 96c823c76..71936203f 100644
--- a/src/video_core/command_classes/codecs/codec.h
+++ b/src/video_core/command_classes/codecs/codec.h
@@ -22,7 +22,6 @@ extern "C" {
22 22
23namespace Tegra { 23namespace Tegra {
24class GPU; 24class GPU;
25struct VicRegisters;
26 25
27void AVFrameDeleter(AVFrame* ptr); 26void AVFrameDeleter(AVFrame* ptr);
28using AVFramePtr = std::unique_ptr<AVFrame, decltype(&AVFrameDeleter)>; 27using AVFramePtr = std::unique_ptr<AVFrame, decltype(&AVFrameDeleter)>;
@@ -55,10 +54,13 @@ public:
55 [[nodiscard]] std::string_view GetCurrentCodecName() const; 54 [[nodiscard]] std::string_view GetCurrentCodecName() const;
56 55
57private: 56private:
57 void InitializeHwdec();
58
58 bool initialized{}; 59 bool initialized{};
59 NvdecCommon::VideoCodec current_codec{NvdecCommon::VideoCodec::None}; 60 NvdecCommon::VideoCodec current_codec{NvdecCommon::VideoCodec::None};
60 61
61 AVCodec* av_codec{nullptr}; 62 AVCodec* av_codec{nullptr};
63 AVBufferRef* av_hw_device{nullptr};
62 AVCodecContext* av_codec_ctx{nullptr}; 64 AVCodecContext* av_codec_ctx{nullptr};
63 65
64 GPU& gpu; 66 GPU& gpu;
diff --git a/src/video_core/command_classes/codecs/vp9.cpp b/src/video_core/command_classes/codecs/vp9.cpp
index 902bc2a98..70030066a 100644
--- a/src/video_core/command_classes/codecs/vp9.cpp
+++ b/src/video_core/command_classes/codecs/vp9.cpp
@@ -11,6 +11,9 @@
11 11
12namespace Tegra::Decoder { 12namespace Tegra::Decoder {
13namespace { 13namespace {
14constexpr u32 diff_update_probability = 252;
15constexpr u32 frame_sync_code = 0x498342;
16
14// Default compressed header probabilities once frame context resets 17// Default compressed header probabilities once frame context resets
15constexpr Vp9EntropyProbs default_probs{ 18constexpr Vp9EntropyProbs default_probs{
16 .y_mode_prob{ 19 .y_mode_prob{
@@ -361,8 +364,7 @@ Vp9PictureInfo VP9::GetVp9PictureInfo(const NvdecCommon::NvdecRegisters& state)
361 InsertEntropy(state.vp9_entropy_probs_offset, vp9_info.entropy); 364 InsertEntropy(state.vp9_entropy_probs_offset, vp9_info.entropy);
362 365
363 // surface_luma_offset[0:3] contains the address of the reference frame offsets in the following 366 // surface_luma_offset[0:3] contains the address of the reference frame offsets in the following
364 // order: last, golden, altref, current. It may be worthwhile to track the updates done here 367 // order: last, golden, altref, current.
365 // to avoid buffering frame data needed for reference frame updating in the header composition.
366 std::copy(state.surface_luma_offset.begin(), state.surface_luma_offset.begin() + 4, 368 std::copy(state.surface_luma_offset.begin(), state.surface_luma_offset.begin() + 4,
367 vp9_info.frame_offsets.begin()); 369 vp9_info.frame_offsets.begin());
368 370
@@ -384,40 +386,25 @@ Vp9FrameContainer VP9::GetCurrentFrame(const NvdecCommon::NvdecRegisters& state)
384 gpu.MemoryManager().ReadBlock(state.frame_bitstream_offset, current_frame.bit_stream.data(), 386 gpu.MemoryManager().ReadBlock(state.frame_bitstream_offset, current_frame.bit_stream.data(),
385 current_frame.info.bitstream_size); 387 current_frame.info.bitstream_size);
386 } 388 }
387 // Buffer two frames, saving the last show frame info 389 if (!next_frame.bit_stream.empty()) {
388 if (!next_next_frame.bit_stream.empty()) {
389 Vp9FrameContainer temp{ 390 Vp9FrameContainer temp{
390 .info = current_frame.info, 391 .info = current_frame.info,
391 .bit_stream = std::move(current_frame.bit_stream), 392 .bit_stream = std::move(current_frame.bit_stream),
392 }; 393 };
393 next_next_frame.info.show_frame = current_frame.info.last_frame_shown; 394 next_frame.info.show_frame = current_frame.info.last_frame_shown;
394 current_frame.info = next_next_frame.info; 395 current_frame.info = next_frame.info;
395 current_frame.bit_stream = std::move(next_next_frame.bit_stream); 396 current_frame.bit_stream = std::move(next_frame.bit_stream);
396 next_next_frame = std::move(temp); 397 next_frame = std::move(temp);
397
398 if (!next_frame.bit_stream.empty()) {
399 Vp9FrameContainer temp2{
400 .info = current_frame.info,
401 .bit_stream = std::move(current_frame.bit_stream),
402 };
403 next_frame.info.show_frame = current_frame.info.last_frame_shown;
404 current_frame.info = next_frame.info;
405 current_frame.bit_stream = std::move(next_frame.bit_stream);
406 next_frame = std::move(temp2);
407 } else {
408 next_frame.info = current_frame.info;
409 next_frame.bit_stream = std::move(current_frame.bit_stream);
410 }
411 } else { 398 } else {
412 next_next_frame.info = current_frame.info; 399 next_frame.info = current_frame.info;
413 next_next_frame.bit_stream = std::move(current_frame.bit_stream); 400 next_frame.bit_stream = current_frame.bit_stream;
414 } 401 }
415 return current_frame; 402 return current_frame;
416} 403}
417 404
418std::vector<u8> VP9::ComposeCompressedHeader() { 405std::vector<u8> VP9::ComposeCompressedHeader() {
419 VpxRangeEncoder writer{}; 406 VpxRangeEncoder writer{};
420 const bool update_probs = current_frame_info.show_frame && !current_frame_info.is_key_frame; 407 const bool update_probs = !current_frame_info.is_key_frame && current_frame_info.show_frame;
421 if (!current_frame_info.lossless) { 408 if (!current_frame_info.lossless) {
422 if (static_cast<u32>(current_frame_info.transform_mode) >= 3) { 409 if (static_cast<u32>(current_frame_info.transform_mode) >= 3) {
423 writer.Write(3, 2); 410 writer.Write(3, 2);
@@ -613,86 +600,64 @@ VpxBitStreamWriter VP9::ComposeUncompressedHeader() {
613 600
614 // Reset context 601 // Reset context
615 prev_frame_probs = default_probs; 602 prev_frame_probs = default_probs;
616 swap_next_golden = false; 603 swap_ref_indices = false;
617 loop_filter_ref_deltas.fill(0); 604 loop_filter_ref_deltas.fill(0);
618 loop_filter_mode_deltas.fill(0); 605 loop_filter_mode_deltas.fill(0);
619 606 frame_ctxs.fill(default_probs);
620 // allow frames offsets to stabilize before checking for golden frames
621 grace_period = 4;
622
623 // On key frames, all frame slots are set to the current frame,
624 // so the value of the selected slot doesn't really matter.
625 frame_ctxs.fill({current_frame_number, false, default_probs});
626 607
627 // intra only, meaning the frame can be recreated with no other references 608 // intra only, meaning the frame can be recreated with no other references
628 current_frame_info.intra_only = true; 609 current_frame_info.intra_only = true;
629
630 } else { 610 } else {
631
632 if (!current_frame_info.show_frame) { 611 if (!current_frame_info.show_frame) {
633 uncomp_writer.WriteBit(current_frame_info.intra_only); 612 uncomp_writer.WriteBit(current_frame_info.intra_only);
634 if (!current_frame_info.last_frame_was_key) {
635 swap_next_golden = !swap_next_golden;
636 }
637 } else { 613 } else {
638 current_frame_info.intra_only = false; 614 current_frame_info.intra_only = false;
639 } 615 }
640 if (!current_frame_info.error_resilient_mode) { 616 if (!current_frame_info.error_resilient_mode) {
641 uncomp_writer.WriteU(0, 2); // Reset frame context. 617 uncomp_writer.WriteU(0, 2); // Reset frame context.
642 } 618 }
643 619 const auto& curr_offsets = current_frame_info.frame_offsets;
644 // Last, Golden, Altref frames 620 const auto& next_offsets = next_frame.info.frame_offsets;
645 std::array<s32, 3> ref_frame_index{0, 1, 2}; 621 const bool ref_frames_different = curr_offsets[1] != curr_offsets[2];
646 622 const bool next_references_swap =
647 // Set when next frame is hidden 623 (next_offsets[1] == curr_offsets[2]) || (next_offsets[2] == curr_offsets[1]);
648 // altref and golden references are swapped 624 const bool needs_ref_swap = ref_frames_different && next_references_swap;
649 if (swap_next_golden) { 625 if (needs_ref_swap) {
650 ref_frame_index = std::array<s32, 3>{0, 2, 1}; 626 swap_ref_indices = !swap_ref_indices;
651 } 627 }
652 628 union {
653 // update Last Frame 629 u32 raw;
654 u64 refresh_frame_flags = 1; 630 BitField<0, 1, u32> refresh_last;
655 631 BitField<1, 2, u32> refresh_golden;
656 // golden frame may refresh, determined if the next golden frame offset is changed 632 BitField<2, 1, u32> refresh_alt;
657 bool golden_refresh = false; 633 } refresh_frame_flags;
658 if (grace_period <= 0) { 634
659 for (s32 index = 1; index < 3; ++index) { 635 refresh_frame_flags.raw = 0;
660 if (current_frame_info.frame_offsets[index] != 636 for (u32 index = 0; index < 3; ++index) {
661 next_frame.info.frame_offsets[index]) { 637 // Refresh indices that use the current frame as an index
662 current_frame_info.refresh_frame[index] = true; 638 if (curr_offsets[3] == next_offsets[index]) {
663 golden_refresh = true; 639 refresh_frame_flags.raw |= 1u << index;
664 grace_period = 3;
665 }
666 } 640 }
667 } 641 }
668 642 if (swap_ref_indices) {
669 if (current_frame_info.show_frame && 643 const u32 temp = refresh_frame_flags.refresh_golden;
670 (!next_frame.info.show_frame || next_frame.info.is_key_frame)) { 644 refresh_frame_flags.refresh_golden.Assign(refresh_frame_flags.refresh_alt.Value());
671 // Update golden frame 645 refresh_frame_flags.refresh_alt.Assign(temp);
672 refresh_frame_flags = swap_next_golden ? 2 : 4;
673 }
674
675 if (!current_frame_info.show_frame) {
676 // Update altref
677 refresh_frame_flags = swap_next_golden ? 2 : 4;
678 } else if (golden_refresh) {
679 refresh_frame_flags = 3;
680 } 646 }
681
682 if (current_frame_info.intra_only) { 647 if (current_frame_info.intra_only) {
683 uncomp_writer.WriteU(frame_sync_code, 24); 648 uncomp_writer.WriteU(frame_sync_code, 24);
684 uncomp_writer.WriteU(static_cast<s32>(refresh_frame_flags), 8); 649 uncomp_writer.WriteU(refresh_frame_flags.raw, 8);
685 uncomp_writer.WriteU(current_frame_info.frame_size.width - 1, 16); 650 uncomp_writer.WriteU(current_frame_info.frame_size.width - 1, 16);
686 uncomp_writer.WriteU(current_frame_info.frame_size.height - 1, 16); 651 uncomp_writer.WriteU(current_frame_info.frame_size.height - 1, 16);
687 uncomp_writer.WriteBit(false); // Render and frame size different. 652 uncomp_writer.WriteBit(false); // Render and frame size different.
688 } else { 653 } else {
689 uncomp_writer.WriteU(static_cast<s32>(refresh_frame_flags), 8); 654 const bool swap_indices = needs_ref_swap ^ swap_ref_indices;
690 655 const auto ref_frame_index = swap_indices ? std::array{0, 2, 1} : std::array{0, 1, 2};
691 for (s32 index = 1; index < 4; index++) { 656 uncomp_writer.WriteU(refresh_frame_flags.raw, 8);
657 for (size_t index = 1; index < 4; index++) {
692 uncomp_writer.WriteU(ref_frame_index[index - 1], 3); 658 uncomp_writer.WriteU(ref_frame_index[index - 1], 3);
693 uncomp_writer.WriteU(current_frame_info.ref_frame_sign_bias[index], 1); 659 uncomp_writer.WriteU(current_frame_info.ref_frame_sign_bias[index], 1);
694 } 660 }
695
696 uncomp_writer.WriteBit(true); // Frame size with refs. 661 uncomp_writer.WriteBit(true); // Frame size with refs.
697 uncomp_writer.WriteBit(false); // Render and frame size different. 662 uncomp_writer.WriteBit(false); // Render and frame size different.
698 uncomp_writer.WriteBit(current_frame_info.allow_high_precision_mv); 663 uncomp_writer.WriteBit(current_frame_info.allow_high_precision_mv);
@@ -714,10 +679,9 @@ VpxBitStreamWriter VP9::ComposeUncompressedHeader() {
714 frame_ctx_idx = 1; 679 frame_ctx_idx = 1;
715 } 680 }
716 681
717 uncomp_writer.WriteU(frame_ctx_idx, 2); // Frame context index. 682 uncomp_writer.WriteU(frame_ctx_idx, 2); // Frame context index.
718 prev_frame_probs = 683 prev_frame_probs = frame_ctxs[frame_ctx_idx]; // reference probabilities for compressed header
719 frame_ctxs[frame_ctx_idx].probs; // reference probabilities for compressed header 684 frame_ctxs[frame_ctx_idx] = current_frame_info.entropy;
720 frame_ctxs[frame_ctx_idx] = {current_frame_number, false, current_frame_info.entropy};
721 685
722 uncomp_writer.WriteU(current_frame_info.first_level, 6); 686 uncomp_writer.WriteU(current_frame_info.first_level, 6);
723 uncomp_writer.WriteU(current_frame_info.sharpness_level, 3); 687 uncomp_writer.WriteU(current_frame_info.sharpness_level, 3);
@@ -812,7 +776,6 @@ const std::vector<u8>& VP9::ComposeFrameHeader(const NvdecCommon::NvdecRegisters
812 current_frame_info = curr_frame.info; 776 current_frame_info = curr_frame.info;
813 bitstream = std::move(curr_frame.bit_stream); 777 bitstream = std::move(curr_frame.bit_stream);
814 } 778 }
815
816 // The uncompressed header routine sets PrevProb parameters needed for the compressed header 779 // The uncompressed header routine sets PrevProb parameters needed for the compressed header
817 auto uncomp_writer = ComposeUncompressedHeader(); 780 auto uncomp_writer = ComposeUncompressedHeader();
818 std::vector<u8> compressed_header = ComposeCompressedHeader(); 781 std::vector<u8> compressed_header = ComposeCompressedHeader();
@@ -828,13 +791,6 @@ const std::vector<u8>& VP9::ComposeFrameHeader(const NvdecCommon::NvdecRegisters
828 frame.begin() + uncompressed_header.size()); 791 frame.begin() + uncompressed_header.size());
829 std::copy(bitstream.begin(), bitstream.end(), 792 std::copy(bitstream.begin(), bitstream.end(),
830 frame.begin() + uncompressed_header.size() + compressed_header.size()); 793 frame.begin() + uncompressed_header.size() + compressed_header.size());
831
832 // keep track of frame number
833 current_frame_number++;
834 grace_period--;
835
836 // don't display hidden frames
837 hidden = !current_frame_info.show_frame;
838 return frame; 794 return frame;
839} 795}
840 796
diff --git a/src/video_core/command_classes/codecs/vp9.h b/src/video_core/command_classes/codecs/vp9.h
index 8396c8105..e6e9fc17e 100644
--- a/src/video_core/command_classes/codecs/vp9.h
+++ b/src/video_core/command_classes/codecs/vp9.h
@@ -14,7 +14,6 @@
14 14
15namespace Tegra { 15namespace Tegra {
16class GPU; 16class GPU;
17enum class FrameType { KeyFrame = 0, InterFrame = 1 };
18namespace Decoder { 17namespace Decoder {
19 18
20/// The VpxRangeEncoder, and VpxBitStreamWriter classes are used to compose the 19/// The VpxRangeEncoder, and VpxBitStreamWriter classes are used to compose the
@@ -124,7 +123,7 @@ public:
124 123
125 /// Returns true if the most recent frame was a hidden frame. 124 /// Returns true if the most recent frame was a hidden frame.
126 [[nodiscard]] bool WasFrameHidden() const { 125 [[nodiscard]] bool WasFrameHidden() const {
127 return hidden; 126 return !current_frame_info.show_frame;
128 } 127 }
129 128
130private: 129private:
@@ -178,19 +177,12 @@ private:
178 std::array<s8, 4> loop_filter_ref_deltas{}; 177 std::array<s8, 4> loop_filter_ref_deltas{};
179 std::array<s8, 2> loop_filter_mode_deltas{}; 178 std::array<s8, 2> loop_filter_mode_deltas{};
180 179
181 bool hidden = false;
182 s64 current_frame_number = -2; // since we buffer 2 frames
183 s32 grace_period = 6; // frame offsets need to stabilize
184 std::array<FrameContexts, 4> frame_ctxs{};
185 Vp9FrameContainer next_frame{}; 180 Vp9FrameContainer next_frame{};
186 Vp9FrameContainer next_next_frame{}; 181 std::array<Vp9EntropyProbs, 4> frame_ctxs{};
187 bool swap_next_golden{}; 182 bool swap_ref_indices{};
188 183
189 Vp9PictureInfo current_frame_info{}; 184 Vp9PictureInfo current_frame_info{};
190 Vp9EntropyProbs prev_frame_probs{}; 185 Vp9EntropyProbs prev_frame_probs{};
191
192 s32 diff_update_probability = 252;
193 s32 frame_sync_code = 0x498342;
194}; 186};
195 187
196} // namespace Decoder 188} // namespace Decoder
diff --git a/src/video_core/command_classes/codecs/vp9_types.h b/src/video_core/command_classes/codecs/vp9_types.h
index 2da14f3ca..87eafdb03 100644
--- a/src/video_core/command_classes/codecs/vp9_types.h
+++ b/src/video_core/command_classes/codecs/vp9_types.h
@@ -176,7 +176,7 @@ struct PictureInfo {
176 .frame_size_changed = (vp9_flags & FrameFlags::FrameSizeChanged) != 0, 176 .frame_size_changed = (vp9_flags & FrameFlags::FrameSizeChanged) != 0,
177 .error_resilient_mode = (vp9_flags & FrameFlags::ErrorResilientMode) != 0, 177 .error_resilient_mode = (vp9_flags & FrameFlags::ErrorResilientMode) != 0,
178 .last_frame_shown = (vp9_flags & FrameFlags::LastShowFrame) != 0, 178 .last_frame_shown = (vp9_flags & FrameFlags::LastShowFrame) != 0,
179 .show_frame = false, 179 .show_frame = true,
180 .ref_frame_sign_bias = ref_frame_sign_bias, 180 .ref_frame_sign_bias = ref_frame_sign_bias,
181 .base_q_index = base_q_index, 181 .base_q_index = base_q_index,
182 .y_dc_delta_q = y_dc_delta_q, 182 .y_dc_delta_q = y_dc_delta_q,
@@ -296,12 +296,6 @@ struct RefPoolElement {
296 bool refresh{}; 296 bool refresh{};
297}; 297};
298 298
299struct FrameContexts {
300 s64 from;
301 bool adapted;
302 Vp9EntropyProbs probs;
303};
304
305#define ASSERT_POSITION(field_name, position) \ 299#define ASSERT_POSITION(field_name, position) \
306 static_assert(offsetof(Vp9EntropyProbs, field_name) == position, \ 300 static_assert(offsetof(Vp9EntropyProbs, field_name) == position, \
307 "Field " #field_name " has invalid position") 301 "Field " #field_name " has invalid position")
diff --git a/src/video_core/command_classes/nvdec.cpp b/src/video_core/command_classes/nvdec.cpp
index b5e3b70fc..b5c55f14a 100644
--- a/src/video_core/command_classes/nvdec.cpp
+++ b/src/video_core/command_classes/nvdec.cpp
@@ -39,7 +39,7 @@ void Nvdec::Execute() {
39 codec->Decode(); 39 codec->Decode();
40 break; 40 break;
41 default: 41 default:
42 UNIMPLEMENTED_MSG("Unknown codec {}", static_cast<u32>(codec->GetCurrentCodec())); 42 UNIMPLEMENTED_MSG("Codec {}", codec->GetCurrentCodecName());
43 break; 43 break;
44 } 44 }
45} 45}
diff --git a/src/video_core/command_classes/vic.cpp b/src/video_core/command_classes/vic.cpp
index ffb7c82a1..d5e77941c 100644
--- a/src/video_core/command_classes/vic.cpp
+++ b/src/video_core/command_classes/vic.cpp
@@ -46,11 +46,8 @@ void Vic::ProcessMethod(Method method, u32 argument) {
46 case Method::SetOutputSurfaceLumaOffset: 46 case Method::SetOutputSurfaceLumaOffset:
47 output_surface_luma_address = arg; 47 output_surface_luma_address = arg;
48 break; 48 break;
49 case Method::SetOutputSurfaceChromaUOffset: 49 case Method::SetOutputSurfaceChromaOffset:
50 output_surface_chroma_u_address = arg; 50 output_surface_chroma_address = arg;
51 break;
52 case Method::SetOutputSurfaceChromaVOffset:
53 output_surface_chroma_v_address = arg;
54 break; 51 break;
55 default: 52 default:
56 break; 53 break;
@@ -65,11 +62,10 @@ void Vic::Execute() {
65 const VicConfig config{gpu.MemoryManager().Read<u64>(config_struct_address + 0x20)}; 62 const VicConfig config{gpu.MemoryManager().Read<u64>(config_struct_address + 0x20)};
66 const AVFramePtr frame_ptr = nvdec_processor->GetFrame(); 63 const AVFramePtr frame_ptr = nvdec_processor->GetFrame();
67 const auto* frame = frame_ptr.get(); 64 const auto* frame = frame_ptr.get();
68 if (!frame || frame->width == 0 || frame->height == 0) { 65 if (!frame) {
69 return; 66 return;
70 } 67 }
71 const VideoPixelFormat pixel_format = 68 const auto pixel_format = static_cast<VideoPixelFormat>(config.pixel_format.Value());
72 static_cast<VideoPixelFormat>(config.pixel_format.Value());
73 switch (pixel_format) { 69 switch (pixel_format) {
74 case VideoPixelFormat::BGRA8: 70 case VideoPixelFormat::BGRA8:
75 case VideoPixelFormat::RGBA8: { 71 case VideoPixelFormat::RGBA8: {
@@ -83,16 +79,18 @@ void Vic::Execute() {
83 sws_freeContext(scaler_ctx); 79 sws_freeContext(scaler_ctx);
84 scaler_ctx = nullptr; 80 scaler_ctx = nullptr;
85 81
86 // FFmpeg returns all frames in YUV420, convert it into expected format 82 // Frames are decoded into either YUV420 or NV12 formats. Convert to desired format
87 scaler_ctx = 83 scaler_ctx = sws_getContext(frame->width, frame->height,
88 sws_getContext(frame->width, frame->height, AV_PIX_FMT_YUV420P, frame->width, 84 static_cast<AVPixelFormat>(frame->format), frame->width,
89 frame->height, target_format, 0, nullptr, nullptr, nullptr); 85 frame->height, target_format, 0, nullptr, nullptr, nullptr);
90 86
91 scaler_width = frame->width; 87 scaler_width = frame->width;
92 scaler_height = frame->height; 88 scaler_height = frame->height;
93 } 89 }
94 // Get Converted frame 90 // Get Converted frame
95 const std::size_t linear_size = frame->width * frame->height * 4; 91 const u32 width = static_cast<u32>(frame->width);
92 const u32 height = static_cast<u32>(frame->height);
93 const std::size_t linear_size = width * height * 4;
96 94
97 // Only allocate frame_buffer once per stream, as the size is not expected to change 95 // Only allocate frame_buffer once per stream, as the size is not expected to change
98 if (!converted_frame_buffer) { 96 if (!converted_frame_buffer) {
@@ -109,11 +107,10 @@ void Vic::Execute() {
109 if (blk_kind != 0) { 107 if (blk_kind != 0) {
110 // swizzle pitch linear to block linear 108 // swizzle pitch linear to block linear
111 const u32 block_height = static_cast<u32>(config.block_linear_height_log2); 109 const u32 block_height = static_cast<u32>(config.block_linear_height_log2);
112 const auto size = Tegra::Texture::CalculateSize(true, 4, frame->width, frame->height, 1, 110 const auto size =
113 block_height, 0); 111 Tegra::Texture::CalculateSize(true, 4, width, height, 1, block_height, 0);
114 luma_buffer.resize(size); 112 luma_buffer.resize(size);
115 Tegra::Texture::SwizzleSubrect(frame->width, frame->height, frame->width * 4, 113 Tegra::Texture::SwizzleSubrect(width, height, width * 4, width, 4, luma_buffer.data(),
116 frame->width, 4, luma_buffer.data(),
117 converted_frame_buffer.get(), block_height, 0, 0); 114 converted_frame_buffer.get(), block_height, 0, 0);
118 115
119 gpu.MemoryManager().WriteBlock(output_surface_luma_address, luma_buffer.data(), size); 116 gpu.MemoryManager().WriteBlock(output_surface_luma_address, luma_buffer.data(), size);
@@ -131,41 +128,65 @@ void Vic::Execute() {
131 const std::size_t surface_height = config.surface_height_minus1 + 1; 128 const std::size_t surface_height = config.surface_height_minus1 + 1;
132 const auto frame_width = std::min(surface_width, static_cast<size_t>(frame->width)); 129 const auto frame_width = std::min(surface_width, static_cast<size_t>(frame->width));
133 const auto frame_height = std::min(surface_height, static_cast<size_t>(frame->height)); 130 const auto frame_height = std::min(surface_height, static_cast<size_t>(frame->height));
134 const std::size_t half_width = frame_width / 2; 131 const std::size_t aligned_width = (surface_width + 0xff) & ~0xffUL;
135 const std::size_t half_height = frame_height / 2;
136 const std::size_t aligned_width = (surface_width + 0xff) & ~0xff;
137 132
138 const auto* luma_ptr = frame->data[0];
139 const auto* chroma_b_ptr = frame->data[1];
140 const auto* chroma_r_ptr = frame->data[2];
141 const auto stride = static_cast<size_t>(frame->linesize[0]); 133 const auto stride = static_cast<size_t>(frame->linesize[0]);
142 const auto half_stride = static_cast<size_t>(frame->linesize[1]);
143 134
144 luma_buffer.resize(aligned_width * surface_height); 135 luma_buffer.resize(aligned_width * surface_height);
145 chroma_buffer.resize(aligned_width * surface_height / 2); 136 chroma_buffer.resize(aligned_width * surface_height / 2);
146 137
147 // Populate luma buffer 138 // Populate luma buffer
139 const u8* luma_src = frame->data[0];
148 for (std::size_t y = 0; y < frame_height; ++y) { 140 for (std::size_t y = 0; y < frame_height; ++y) {
149 const std::size_t src = y * stride; 141 const std::size_t src = y * stride;
150 const std::size_t dst = y * aligned_width; 142 const std::size_t dst = y * aligned_width;
151 for (std::size_t x = 0; x < frame_width; ++x) { 143 for (std::size_t x = 0; x < frame_width; ++x) {
152 luma_buffer[dst + x] = luma_ptr[src + x]; 144 luma_buffer[dst + x] = luma_src[src + x];
153 } 145 }
154 } 146 }
155 gpu.MemoryManager().WriteBlock(output_surface_luma_address, luma_buffer.data(), 147 gpu.MemoryManager().WriteBlock(output_surface_luma_address, luma_buffer.data(),
156 luma_buffer.size()); 148 luma_buffer.size());
157 149
158 // Populate chroma buffer from both channels with interleaving. 150 // Chroma
159 for (std::size_t y = 0; y < half_height; ++y) { 151 const std::size_t half_height = frame_height / 2;
160 const std::size_t src = y * half_stride; 152 const auto half_stride = static_cast<size_t>(frame->linesize[1]);
161 const std::size_t dst = y * aligned_width;
162 153
163 for (std::size_t x = 0; x < half_width; ++x) { 154 switch (frame->format) {
164 chroma_buffer[dst + x * 2] = chroma_b_ptr[src + x]; 155 case AV_PIX_FMT_YUV420P: {
165 chroma_buffer[dst + x * 2 + 1] = chroma_r_ptr[src + x]; 156 // Frame from FFmpeg software
157 // Populate chroma buffer from both channels with interleaving.
158 const std::size_t half_width = frame_width / 2;
159 const u8* chroma_b_src = frame->data[1];
160 const u8* chroma_r_src = frame->data[2];
161 for (std::size_t y = 0; y < half_height; ++y) {
162 const std::size_t src = y * half_stride;
163 const std::size_t dst = y * aligned_width;
164
165 for (std::size_t x = 0; x < half_width; ++x) {
166 chroma_buffer[dst + x * 2] = chroma_b_src[src + x];
167 chroma_buffer[dst + x * 2 + 1] = chroma_r_src[src + x];
168 }
169 }
170 break;
171 }
172 case AV_PIX_FMT_NV12: {
173 // Frame from VA-API hardware
174 // This is already interleaved so just copy
175 const u8* chroma_src = frame->data[1];
176 for (std::size_t y = 0; y < half_height; ++y) {
177 const std::size_t src = y * stride;
178 const std::size_t dst = y * aligned_width;
179 for (std::size_t x = 0; x < frame_width; ++x) {
180 chroma_buffer[dst + x] = chroma_src[src + x];
181 }
166 } 182 }
183 break;
184 }
185 default:
186 UNREACHABLE();
187 break;
167 } 188 }
168 gpu.MemoryManager().WriteBlock(output_surface_chroma_u_address, chroma_buffer.data(), 189 gpu.MemoryManager().WriteBlock(output_surface_chroma_address, chroma_buffer.data(),
169 chroma_buffer.size()); 190 chroma_buffer.size());
170 break; 191 break;
171 } 192 }
diff --git a/src/video_core/command_classes/vic.h b/src/video_core/command_classes/vic.h
index f5a2ed100..74246e08c 100644
--- a/src/video_core/command_classes/vic.h
+++ b/src/video_core/command_classes/vic.h
@@ -22,8 +22,8 @@ public:
22 SetControlParams = 0x1c1, 22 SetControlParams = 0x1c1,
23 SetConfigStructOffset = 0x1c2, 23 SetConfigStructOffset = 0x1c2,
24 SetOutputSurfaceLumaOffset = 0x1c8, 24 SetOutputSurfaceLumaOffset = 0x1c8,
25 SetOutputSurfaceChromaUOffset = 0x1c9, 25 SetOutputSurfaceChromaOffset = 0x1c9,
26 SetOutputSurfaceChromaVOffset = 0x1ca 26 SetOutputSurfaceChromaUnusedOffset = 0x1ca
27 }; 27 };
28 28
29 explicit Vic(GPU& gpu, std::shared_ptr<Nvdec> nvdec_processor); 29 explicit Vic(GPU& gpu, std::shared_ptr<Nvdec> nvdec_processor);
@@ -64,8 +64,7 @@ private:
64 64
65 GPUVAddr config_struct_address{}; 65 GPUVAddr config_struct_address{};
66 GPUVAddr output_surface_luma_address{}; 66 GPUVAddr output_surface_luma_address{};
67 GPUVAddr output_surface_chroma_u_address{}; 67 GPUVAddr output_surface_chroma_address{};
68 GPUVAddr output_surface_chroma_v_address{};
69 68
70 SwsContext* scaler_ctx{}; 69 SwsContext* scaler_ctx{};
71 s32 scaler_width{}; 70 s32 scaler_width{};
diff --git a/src/video_core/host_shaders/astc_decoder.comp b/src/video_core/host_shaders/astc_decoder.comp
index c37f15bfd..f34c5f5d9 100644
--- a/src/video_core/host_shaders/astc_decoder.comp
+++ b/src/video_core/host_shaders/astc_decoder.comp
@@ -10,33 +10,27 @@
10#define END_PUSH_CONSTANTS }; 10#define END_PUSH_CONSTANTS };
11#define UNIFORM(n) 11#define UNIFORM(n)
12#define BINDING_INPUT_BUFFER 0 12#define BINDING_INPUT_BUFFER 0
13#define BINDING_ENC_BUFFER 1 13#define BINDING_OUTPUT_IMAGE 1
14#define BINDING_SWIZZLE_BUFFER 2
15#define BINDING_OUTPUT_IMAGE 3
16 14
17#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv 15#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv
18 16
19#define BEGIN_PUSH_CONSTANTS 17#define BEGIN_PUSH_CONSTANTS
20#define END_PUSH_CONSTANTS 18#define END_PUSH_CONSTANTS
21#define UNIFORM(n) layout(location = n) uniform 19#define UNIFORM(n) layout(location = n) uniform
22#define BINDING_SWIZZLE_BUFFER 0 20#define BINDING_INPUT_BUFFER 0
23#define BINDING_INPUT_BUFFER 1
24#define BINDING_ENC_BUFFER 2
25#define BINDING_OUTPUT_IMAGE 0 21#define BINDING_OUTPUT_IMAGE 0
26 22
27#endif 23#endif
28 24
29layout(local_size_x = 32, local_size_y = 32, local_size_z = 1) in; 25layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
30 26
31BEGIN_PUSH_CONSTANTS 27BEGIN_PUSH_CONSTANTS
32UNIFORM(1) uvec2 block_dims; 28UNIFORM(1) uvec2 block_dims;
33 29UNIFORM(2) uint layer_stride;
34UNIFORM(2) uint bytes_per_block_log2; 30UNIFORM(3) uint block_size;
35UNIFORM(3) uint layer_stride; 31UNIFORM(4) uint x_shift;
36UNIFORM(4) uint block_size; 32UNIFORM(5) uint block_height;
37UNIFORM(5) uint x_shift; 33UNIFORM(6) uint block_height_mask;
38UNIFORM(6) uint block_height;
39UNIFORM(7) uint block_height_mask;
40END_PUSH_CONSTANTS 34END_PUSH_CONSTANTS
41 35
42struct EncodingData { 36struct EncodingData {
@@ -55,45 +49,35 @@ struct TexelWeightParams {
55 bool void_extent_hdr; 49 bool void_extent_hdr;
56}; 50};
57 51
58// Swizzle data
59layout(binding = BINDING_SWIZZLE_BUFFER, std430) readonly buffer SwizzleTable {
60 uint swizzle_table[];
61};
62
63layout(binding = BINDING_INPUT_BUFFER, std430) readonly buffer InputBufferU32 { 52layout(binding = BINDING_INPUT_BUFFER, std430) readonly buffer InputBufferU32 {
64 uint astc_data[]; 53 uvec4 astc_data[];
65};
66
67// ASTC Encodings data
68layout(binding = BINDING_ENC_BUFFER, std430) readonly buffer EncodingsValues {
69 EncodingData encoding_values[];
70}; 54};
71 55
72layout(binding = BINDING_OUTPUT_IMAGE, rgba8) uniform writeonly image2DArray dest_image; 56layout(binding = BINDING_OUTPUT_IMAGE, rgba8) uniform writeonly image2DArray dest_image;
73 57
74const uint GOB_SIZE_X = 64;
75const uint GOB_SIZE_Y = 8;
76const uint GOB_SIZE_Z = 1;
77const uint GOB_SIZE = GOB_SIZE_X * GOB_SIZE_Y * GOB_SIZE_Z;
78
79const uint GOB_SIZE_X_SHIFT = 6; 58const uint GOB_SIZE_X_SHIFT = 6;
80const uint GOB_SIZE_Y_SHIFT = 3; 59const uint GOB_SIZE_Y_SHIFT = 3;
81const uint GOB_SIZE_Z_SHIFT = 0; 60const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT;
82const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT + GOB_SIZE_Z_SHIFT;
83
84const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1, GOB_SIZE_Y - 1);
85 61
86const int BLOCK_SIZE_IN_BYTES = 16; 62const uint BYTES_PER_BLOCK_LOG2 = 4;
87
88const int BLOCK_INFO_ERROR = 0;
89const int BLOCK_INFO_VOID_EXTENT_HDR = 1;
90const int BLOCK_INFO_VOID_EXTENT_LDR = 2;
91const int BLOCK_INFO_NORMAL = 3;
92 63
93const int JUST_BITS = 0; 64const int JUST_BITS = 0;
94const int QUINT = 1; 65const int QUINT = 1;
95const int TRIT = 2; 66const int TRIT = 2;
96 67
68// ASTC Encodings data, sorted in ascending order based on their BitLength value
69// (see GetBitLength() function)
70EncodingData encoding_values[22] = EncodingData[](
71 EncodingData(JUST_BITS, 0, 0, 0), EncodingData(JUST_BITS, 1, 0, 0), EncodingData(TRIT, 0, 0, 0),
72 EncodingData(JUST_BITS, 2, 0, 0), EncodingData(QUINT, 0, 0, 0), EncodingData(TRIT, 1, 0, 0),
73 EncodingData(JUST_BITS, 3, 0, 0), EncodingData(QUINT, 1, 0, 0), EncodingData(TRIT, 2, 0, 0),
74 EncodingData(JUST_BITS, 4, 0, 0), EncodingData(QUINT, 2, 0, 0), EncodingData(TRIT, 3, 0, 0),
75 EncodingData(JUST_BITS, 5, 0, 0), EncodingData(QUINT, 3, 0, 0), EncodingData(TRIT, 4, 0, 0),
76 EncodingData(JUST_BITS, 6, 0, 0), EncodingData(QUINT, 4, 0, 0), EncodingData(TRIT, 5, 0, 0),
77 EncodingData(JUST_BITS, 7, 0, 0), EncodingData(QUINT, 5, 0, 0), EncodingData(TRIT, 6, 0, 0),
78 EncodingData(JUST_BITS, 8, 0, 0)
79);
80
97// The following constants are expanded variants of the Replicate() 81// The following constants are expanded variants of the Replicate()
98// function calls corresponding to the following arguments: 82// function calls corresponding to the following arguments:
99// value: index into the generated table 83// value: index into the generated table
@@ -135,44 +119,37 @@ const uint REPLICATE_7_BIT_TO_8_TABLE[128] =
135// Input ASTC texture globals 119// Input ASTC texture globals
136uint current_index = 0; 120uint current_index = 0;
137int bitsread = 0; 121int bitsread = 0;
138uint total_bitsread = 0; 122int total_bitsread = 0;
139uint local_buff[16]; 123uvec4 local_buff;
140 124
141// Color data globals 125// Color data globals
142uint color_endpoint_data[16]; 126uvec4 color_endpoint_data;
143int color_bitsread = 0; 127int color_bitsread = 0;
144uint total_color_bitsread = 0;
145int color_index = 0;
146 128
147// Four values, two endpoints, four maximum paritions 129// Four values, two endpoints, four maximum paritions
148uint color_values[32]; 130uint color_values[32];
149int colvals_index = 0; 131int colvals_index = 0;
150 132
151// Weight data globals 133// Weight data globals
152uint texel_weight_data[16]; 134uvec4 texel_weight_data;
153int texel_bitsread = 0; 135int texel_bitsread = 0;
154uint total_texel_bitsread = 0;
155int texel_index = 0;
156 136
157bool texel_flag = false; 137bool texel_flag = false;
158 138
159// Global "vectors" to be pushed into when decoding 139// Global "vectors" to be pushed into when decoding
160EncodingData result_vector[100]; 140EncodingData result_vector[144];
161int result_index = 0; 141int result_index = 0;
162 142
163EncodingData texel_vector[100]; 143EncodingData texel_vector[144];
164int texel_vector_index = 0; 144int texel_vector_index = 0;
165 145
166uint unquantized_texel_weights[2][144]; 146uint unquantized_texel_weights[2][144];
167 147
168uint SwizzleOffset(uvec2 pos) { 148uint SwizzleOffset(uvec2 pos) {
169 pos = pos & SWIZZLE_MASK; 149 uint x = pos.x;
170 return swizzle_table[pos.y * 64 + pos.x]; 150 uint y = pos.y;
171} 151 return ((x % 64) / 32) * 256 + ((y % 8) / 2) * 64 + ((x % 32) / 16) * 32 +
172 152 (y % 2) * 16 + (x % 16);
173uint ReadTexel(uint offset) {
174 // extract the 8-bit value from the 32-bit packed data.
175 return bitfieldExtract(astc_data[offset / 4], int((offset * 8) & 24), 8);
176} 153}
177 154
178// Replicates low num_bits such that [(to_bit - 1):(to_bit - 1 - from_bit)] 155// Replicates low num_bits such that [(to_bit - 1):(to_bit - 1 - from_bit)]
@@ -278,14 +255,10 @@ uint Hash52(uint p) {
278 return p; 255 return p;
279} 256}
280 257
281uint SelectPartition(uint seed, uint x, uint y, uint z, uint partition_count, bool small_block) { 258uint Select2DPartition(uint seed, uint x, uint y, uint partition_count, bool small_block) {
282 if (partition_count == 1) {
283 return 0;
284 }
285 if (small_block) { 259 if (small_block) {
286 x <<= 1; 260 x <<= 1;
287 y <<= 1; 261 y <<= 1;
288 z <<= 1;
289 } 262 }
290 263
291 seed += (partition_count - 1) * 1024; 264 seed += (partition_count - 1) * 1024;
@@ -299,10 +272,6 @@ uint SelectPartition(uint seed, uint x, uint y, uint z, uint partition_count, bo
299 uint seed6 = uint((rnum >> 20) & 0xF); 272 uint seed6 = uint((rnum >> 20) & 0xF);
300 uint seed7 = uint((rnum >> 24) & 0xF); 273 uint seed7 = uint((rnum >> 24) & 0xF);
301 uint seed8 = uint((rnum >> 28) & 0xF); 274 uint seed8 = uint((rnum >> 28) & 0xF);
302 uint seed9 = uint((rnum >> 18) & 0xF);
303 uint seed10 = uint((rnum >> 22) & 0xF);
304 uint seed11 = uint((rnum >> 26) & 0xF);
305 uint seed12 = uint(((rnum >> 30) | (rnum << 2)) & 0xF);
306 275
307 seed1 = (seed1 * seed1); 276 seed1 = (seed1 * seed1);
308 seed2 = (seed2 * seed2); 277 seed2 = (seed2 * seed2);
@@ -312,12 +281,8 @@ uint SelectPartition(uint seed, uint x, uint y, uint z, uint partition_count, bo
312 seed6 = (seed6 * seed6); 281 seed6 = (seed6 * seed6);
313 seed7 = (seed7 * seed7); 282 seed7 = (seed7 * seed7);
314 seed8 = (seed8 * seed8); 283 seed8 = (seed8 * seed8);
315 seed9 = (seed9 * seed9);
316 seed10 = (seed10 * seed10);
317 seed11 = (seed11 * seed11);
318 seed12 = (seed12 * seed12);
319 284
320 int sh1, sh2, sh3; 285 uint sh1, sh2;
321 if ((seed & 1) > 0) { 286 if ((seed & 1) > 0) {
322 sh1 = (seed & 2) > 0 ? 4 : 5; 287 sh1 = (seed & 2) > 0 ? 4 : 5;
323 sh2 = (partition_count == 3) ? 6 : 5; 288 sh2 = (partition_count == 3) ? 6 : 5;
@@ -325,25 +290,19 @@ uint SelectPartition(uint seed, uint x, uint y, uint z, uint partition_count, bo
325 sh1 = (partition_count == 3) ? 6 : 5; 290 sh1 = (partition_count == 3) ? 6 : 5;
326 sh2 = (seed & 2) > 0 ? 4 : 5; 291 sh2 = (seed & 2) > 0 ? 4 : 5;
327 } 292 }
328 sh3 = (seed & 0x10) > 0 ? sh1 : sh2; 293 seed1 >>= sh1;
329 294 seed2 >>= sh2;
330 seed1 = (seed1 >> sh1); 295 seed3 >>= sh1;
331 seed2 = (seed2 >> sh2); 296 seed4 >>= sh2;
332 seed3 = (seed3 >> sh1); 297 seed5 >>= sh1;
333 seed4 = (seed4 >> sh2); 298 seed6 >>= sh2;
334 seed5 = (seed5 >> sh1); 299 seed7 >>= sh1;
335 seed6 = (seed6 >> sh2); 300 seed8 >>= sh2;
336 seed7 = (seed7 >> sh1); 301
337 seed8 = (seed8 >> sh2); 302 uint a = seed1 * x + seed2 * y + (rnum >> 14);
338 seed9 = (seed9 >> sh3); 303 uint b = seed3 * x + seed4 * y + (rnum >> 10);
339 seed10 = (seed10 >> sh3); 304 uint c = seed5 * x + seed6 * y + (rnum >> 6);
340 seed11 = (seed11 >> sh3); 305 uint d = seed7 * x + seed8 * y + (rnum >> 2);
341 seed12 = (seed12 >> sh3);
342
343 uint a = seed1 * x + seed2 * y + seed11 * z + (rnum >> 14);
344 uint b = seed3 * x + seed4 * y + seed12 * z + (rnum >> 10);
345 uint c = seed5 * x + seed6 * y + seed9 * z + (rnum >> 6);
346 uint d = seed7 * x + seed8 * y + seed10 * z + (rnum >> 2);
347 306
348 a &= 0x3F; 307 a &= 0x3F;
349 b &= 0x3F; 308 b &= 0x3F;
@@ -368,58 +327,37 @@ uint SelectPartition(uint seed, uint x, uint y, uint z, uint partition_count, bo
368 } 327 }
369} 328}
370 329
371uint Select2DPartition(uint seed, uint x, uint y, uint partition_count, bool small_block) { 330uint ExtractBits(uvec4 payload, int offset, int bits) {
372 return SelectPartition(seed, x, y, 0, partition_count, small_block); 331 if (bits <= 0) {
373}
374
375uint ReadBit() {
376 if (current_index >= local_buff.length()) {
377 return 0; 332 return 0;
378 } 333 }
379 uint bit = bitfieldExtract(local_buff[current_index], bitsread, 1); 334 int last_offset = offset + bits - 1;
380 ++bitsread; 335 int shifted_offset = offset >> 5;
381 ++total_bitsread; 336 if ((last_offset >> 5) == shifted_offset) {
382 if (bitsread == 8) { 337 return bitfieldExtract(payload[shifted_offset], offset & 31, bits);
383 ++current_index;
384 bitsread = 0;
385 } 338 }
386 return bit; 339 int first_bits = 32 - (offset & 31);
340 int result_first = int(bitfieldExtract(payload[shifted_offset], offset & 31, first_bits));
341 int result_second = int(bitfieldExtract(payload[shifted_offset + 1], 0, bits - first_bits));
342 return result_first | (result_second << first_bits);
387} 343}
388 344
389uint StreamBits(uint num_bits) { 345uint StreamBits(uint num_bits) {
390 uint ret = 0; 346 int int_bits = int(num_bits);
391 for (uint i = 0; i < num_bits; i++) { 347 uint ret = ExtractBits(local_buff, total_bitsread, int_bits);
392 ret |= ((ReadBit() & 1) << i); 348 total_bitsread += int_bits;
393 }
394 return ret; 349 return ret;
395} 350}
396 351
397uint ReadColorBit() {
398 uint bit = 0;
399 if (texel_flag) {
400 bit = bitfieldExtract(texel_weight_data[texel_index], texel_bitsread, 1);
401 ++texel_bitsread;
402 ++total_texel_bitsread;
403 if (texel_bitsread == 8) {
404 ++texel_index;
405 texel_bitsread = 0;
406 }
407 } else {
408 bit = bitfieldExtract(color_endpoint_data[color_index], color_bitsread, 1);
409 ++color_bitsread;
410 ++total_color_bitsread;
411 if (color_bitsread == 8) {
412 ++color_index;
413 color_bitsread = 0;
414 }
415 }
416 return bit;
417}
418
419uint StreamColorBits(uint num_bits) { 352uint StreamColorBits(uint num_bits) {
420 uint ret = 0; 353 uint ret = 0;
421 for (uint i = 0; i < num_bits; i++) { 354 int int_bits = int(num_bits);
422 ret |= ((ReadColorBit() & 1) << i); 355 if (texel_flag) {
356 ret = ExtractBits(texel_weight_data, texel_bitsread, int_bits);
357 texel_bitsread += int_bits;
358 } else {
359 ret = ExtractBits(color_endpoint_data, color_bitsread, int_bits);
360 color_bitsread += int_bits;
423 } 361 }
424 return ret; 362 return ret;
425} 363}
@@ -596,22 +534,16 @@ void DecodeColorValues(uvec4 modes, uint num_partitions, uint color_data_bits) {
596 for (uint i = 0; i < num_partitions; i++) { 534 for (uint i = 0; i < num_partitions; i++) {
597 num_values += ((modes[i] >> 2) + 1) << 1; 535 num_values += ((modes[i] >> 2) + 1) << 1;
598 } 536 }
599 int range = 256; 537 // Find the largest encoding that's within color_data_bits
600 while (--range > 0) { 538 // TODO(ameerj): profile with binary search
601 EncodingData val = encoding_values[range]; 539 int range = 0;
540 while (++range < encoding_values.length()) {
602 uint bit_length = GetBitLength(num_values, range); 541 uint bit_length = GetBitLength(num_values, range);
603 if (bit_length <= color_data_bits) { 542 if (bit_length > color_data_bits) {
604 while (--range > 0) {
605 EncodingData newval = encoding_values[range];
606 if (newval.encoding != val.encoding && newval.num_bits != val.num_bits) {
607 break;
608 }
609 }
610 ++range;
611 break; 543 break;
612 } 544 }
613 } 545 }
614 DecodeIntegerSequence(range, num_values); 546 DecodeIntegerSequence(range - 1, num_values);
615 uint out_index = 0; 547 uint out_index = 0;
616 for (int itr = 0; itr < result_index; ++itr) { 548 for (int itr = 0; itr < result_index; ++itr) {
617 if (out_index >= num_values) { 549 if (out_index >= num_values) {
@@ -1028,7 +960,7 @@ int FindLayout(uint mode) {
1028 return 5; 960 return 5;
1029} 961}
1030 962
1031TexelWeightParams DecodeBlockInfo(uint block_index) { 963TexelWeightParams DecodeBlockInfo() {
1032 TexelWeightParams params = TexelWeightParams(uvec2(0), 0, false, false, false, false); 964 TexelWeightParams params = TexelWeightParams(uvec2(0), 0, false, false, false, false);
1033 uint mode = StreamBits(11); 965 uint mode = StreamBits(11);
1034 if ((mode & 0x1ff) == 0x1fc) { 966 if ((mode & 0x1ff) == 0x1fc) {
@@ -1110,10 +1042,10 @@ TexelWeightParams DecodeBlockInfo(uint block_index) {
1110 } 1042 }
1111 weight_index -= 2; 1043 weight_index -= 2;
1112 if ((mode_layout != 9) && ((mode & 0x200) != 0)) { 1044 if ((mode_layout != 9) && ((mode & 0x200) != 0)) {
1113 const int max_weights[6] = int[6](9, 11, 15, 19, 23, 31); 1045 const int max_weights[6] = int[6](7, 8, 9, 10, 11, 12);
1114 params.max_weight = max_weights[weight_index]; 1046 params.max_weight = max_weights[weight_index];
1115 } else { 1047 } else {
1116 const int max_weights[6] = int[6](1, 2, 3, 4, 5, 7); 1048 const int max_weights[6] = int[6](1, 2, 3, 4, 5, 6);
1117 params.max_weight = max_weights[weight_index]; 1049 params.max_weight = max_weights[weight_index];
1118 } 1050 }
1119 return params; 1051 return params;
@@ -1144,8 +1076,8 @@ void FillVoidExtentLDR(ivec3 coord) {
1144 } 1076 }
1145} 1077}
1146 1078
1147void DecompressBlock(ivec3 coord, uint block_index) { 1079void DecompressBlock(ivec3 coord) {
1148 TexelWeightParams params = DecodeBlockInfo(block_index); 1080 TexelWeightParams params = DecodeBlockInfo();
1149 if (params.error_state) { 1081 if (params.error_state) {
1150 FillError(coord); 1082 FillError(coord);
1151 return; 1083 return;
@@ -1212,7 +1144,7 @@ void DecompressBlock(ivec3 coord, uint block_index) {
1212 // Read color data... 1144 // Read color data...
1213 uint color_data_bits = remaining_bits; 1145 uint color_data_bits = remaining_bits;
1214 while (remaining_bits > 0) { 1146 while (remaining_bits > 0) {
1215 int nb = int(min(remaining_bits, 8U)); 1147 int nb = int(min(remaining_bits, 32U));
1216 uint b = StreamBits(nb); 1148 uint b = StreamBits(nb);
1217 color_endpoint_data[ced_pointer] = uint(bitfieldExtract(b, 0, nb)); 1149 color_endpoint_data[ced_pointer] = uint(bitfieldExtract(b, 0, nb));
1218 ++ced_pointer; 1150 ++ced_pointer;
@@ -1254,25 +1186,20 @@ void DecompressBlock(ivec3 coord, uint block_index) {
1254 ComputeEndpoints(endpoints[i][0], endpoints[i][1], color_endpoint_mode[i]); 1186 ComputeEndpoints(endpoints[i][0], endpoints[i][1], color_endpoint_mode[i]);
1255 } 1187 }
1256 1188
1257 for (uint i = 0; i < 16; i++) { 1189 texel_weight_data = local_buff;
1258 texel_weight_data[i] = local_buff[i]; 1190 texel_weight_data = bitfieldReverse(texel_weight_data).wzyx;
1259 }
1260 for (uint i = 0; i < 8; i++) {
1261#define REVERSE_BYTE(b) ((b * 0x0802U & 0x22110U) | (b * 0x8020U & 0x88440U)) * 0x10101U >> 16
1262 uint a = REVERSE_BYTE(texel_weight_data[i]);
1263 uint b = REVERSE_BYTE(texel_weight_data[15 - i]);
1264#undef REVERSE_BYTE
1265 texel_weight_data[i] = uint(bitfieldExtract(b, 0, 8));
1266 texel_weight_data[15 - i] = uint(bitfieldExtract(a, 0, 8));
1267 }
1268 uint clear_byte_start = 1191 uint clear_byte_start =
1269 (GetPackedBitSize(params.size, params.dual_plane, params.max_weight) >> 3) + 1; 1192 (GetPackedBitSize(params.size, params.dual_plane, params.max_weight) >> 3) + 1;
1270 texel_weight_data[clear_byte_start - 1] = 1193
1271 texel_weight_data[clear_byte_start - 1] & 1194 uint byte_insert = ExtractBits(texel_weight_data, int(clear_byte_start - 1) * 8, 8) &
1272 uint( 1195 uint(
1273 ((1 << (GetPackedBitSize(params.size, params.dual_plane, params.max_weight) % 8)) - 1)); 1196 ((1 << (GetPackedBitSize(params.size, params.dual_plane, params.max_weight) % 8)) - 1));
1274 for (uint i = 0; i < 16 - clear_byte_start; i++) { 1197 uint vec_index = (clear_byte_start - 1) >> 2;
1275 texel_weight_data[clear_byte_start + i] = 0U; 1198 texel_weight_data[vec_index] =
1199 bitfieldInsert(texel_weight_data[vec_index], byte_insert, int((clear_byte_start - 1) % 4) * 8, 8);
1200 for (uint i = clear_byte_start; i < 16; ++i) {
1201 uint idx = i >> 2;
1202 texel_weight_data[idx] = bitfieldInsert(texel_weight_data[idx], 0, int(i % 4) * 8, 8);
1276 } 1203 }
1277 texel_flag = true; // use texel "vector" and bit stream in integer decoding 1204 texel_flag = true; // use texel "vector" and bit stream in integer decoding
1278 DecodeIntegerSequence(params.max_weight, GetNumWeightValues(params.size, params.dual_plane)); 1205 DecodeIntegerSequence(params.max_weight, GetNumWeightValues(params.size, params.dual_plane));
@@ -1281,8 +1208,11 @@ void DecompressBlock(ivec3 coord, uint block_index) {
1281 1208
1282 for (uint j = 0; j < block_dims.y; j++) { 1209 for (uint j = 0; j < block_dims.y; j++) {
1283 for (uint i = 0; i < block_dims.x; i++) { 1210 for (uint i = 0; i < block_dims.x; i++) {
1284 uint local_partition = Select2DPartition(partition_index, i, j, num_partitions, 1211 uint local_partition = 0;
1212 if (num_partitions > 1) {
1213 local_partition = Select2DPartition(partition_index, i, j, num_partitions,
1285 (block_dims.y * block_dims.x) < 32); 1214 (block_dims.y * block_dims.x) < 32);
1215 }
1286 vec4 p; 1216 vec4 p;
1287 uvec4 C0 = ReplicateByteTo16(endpoints[local_partition][0]); 1217 uvec4 C0 = ReplicateByteTo16(endpoints[local_partition][0]);
1288 uvec4 C1 = ReplicateByteTo16(endpoints[local_partition][1]); 1218 uvec4 C1 = ReplicateByteTo16(endpoints[local_partition][1]);
@@ -1303,7 +1233,7 @@ void DecompressBlock(ivec3 coord, uint block_index) {
1303 1233
1304void main() { 1234void main() {
1305 uvec3 pos = gl_GlobalInvocationID; 1235 uvec3 pos = gl_GlobalInvocationID;
1306 pos.x <<= bytes_per_block_log2; 1236 pos.x <<= BYTES_PER_BLOCK_LOG2;
1307 1237
1308 // Read as soon as possible due to its latency 1238 // Read as soon as possible due to its latency
1309 const uint swizzle = SwizzleOffset(pos.xy); 1239 const uint swizzle = SwizzleOffset(pos.xy);
@@ -1321,13 +1251,8 @@ void main() {
1321 if (any(greaterThanEqual(coord, imageSize(dest_image)))) { 1251 if (any(greaterThanEqual(coord, imageSize(dest_image)))) {
1322 return; 1252 return;
1323 } 1253 }
1324 uint block_index =
1325 pos.z * gl_WorkGroupSize.x * gl_WorkGroupSize.y + pos.y * gl_WorkGroupSize.x + pos.x;
1326
1327 current_index = 0; 1254 current_index = 0;
1328 bitsread = 0; 1255 bitsread = 0;
1329 for (int i = 0; i < 16; i++) { 1256 local_buff = astc_data[offset / 16];
1330 local_buff[i] = ReadTexel(offset + i); 1257 DecompressBlock(coord);
1331 }
1332 DecompressBlock(coord, block_index);
1333} 1258}
diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp
index 3ea72fda9..a99c33c37 100644
--- a/src/video_core/renderer_base.cpp
+++ b/src/video_core/renderer_base.cpp
@@ -27,7 +27,7 @@ void RendererBase::UpdateCurrentFramebufferLayout() {
27 render_window.UpdateCurrentFramebufferLayout(layout.width, layout.height); 27 render_window.UpdateCurrentFramebufferLayout(layout.width, layout.height);
28} 28}
29 29
30void RendererBase::RequestScreenshot(void* data, std::function<void()> callback, 30void RendererBase::RequestScreenshot(void* data, std::function<void(bool)> callback,
31 const Layout::FramebufferLayout& layout) { 31 const Layout::FramebufferLayout& layout) {
32 if (renderer_settings.screenshot_requested) { 32 if (renderer_settings.screenshot_requested) {
33 LOG_ERROR(Render, "A screenshot is already requested or in progress, ignoring the request"); 33 LOG_ERROR(Render, "A screenshot is already requested or in progress, ignoring the request");
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index 22b80c328..bb204454e 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -24,7 +24,7 @@ struct RendererSettings {
24 // Screenshot 24 // Screenshot
25 std::atomic<bool> screenshot_requested{false}; 25 std::atomic<bool> screenshot_requested{false};
26 void* screenshot_bits{}; 26 void* screenshot_bits{};
27 std::function<void()> screenshot_complete_callback; 27 std::function<void(bool)> screenshot_complete_callback;
28 Layout::FramebufferLayout screenshot_framebuffer_layout; 28 Layout::FramebufferLayout screenshot_framebuffer_layout;
29}; 29};
30 30
@@ -80,7 +80,7 @@ public:
80 void RefreshBaseSettings(); 80 void RefreshBaseSettings();
81 81
82 /// Request a screenshot of the next frame 82 /// Request a screenshot of the next frame
83 void RequestScreenshot(void* data, std::function<void()> callback, 83 void RequestScreenshot(void* data, std::function<void(bool)> callback,
84 const Layout::FramebufferLayout& layout); 84 const Layout::FramebufferLayout& layout);
85 85
86protected: 86protected:
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
index fac0034fb..bccb37a58 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
@@ -15,7 +15,7 @@
15#include "video_core/renderer_opengl/gl_shader_util.h" 15#include "video_core/renderer_opengl/gl_shader_util.h"
16#include "video_core/renderer_opengl/gl_state_tracker.h" 16#include "video_core/renderer_opengl/gl_state_tracker.h"
17#include "video_core/shader_notify.h" 17#include "video_core/shader_notify.h"
18#include "video_core/texture_cache/texture_cache.h" 18#include "video_core/texture_cache/texture_cache_base.h"
19 19
20#if defined(_MSC_VER) && defined(NDEBUG) 20#if defined(_MSC_VER) && defined(NDEBUG)
21#define LAMBDA_FORCEINLINE [[msvc::forceinline]] 21#define LAMBDA_FORCEINLINE [[msvc::forceinline]]
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 41d2b73f4..b909c387e 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -32,7 +32,7 @@
32#include "video_core/renderer_opengl/maxwell_to_gl.h" 32#include "video_core/renderer_opengl/maxwell_to_gl.h"
33#include "video_core/renderer_opengl/renderer_opengl.h" 33#include "video_core/renderer_opengl/renderer_opengl.h"
34#include "video_core/shader_cache.h" 34#include "video_core/shader_cache.h"
35#include "video_core/texture_cache/texture_cache.h" 35#include "video_core/texture_cache/texture_cache_base.h"
36 36
37namespace OpenGL { 37namespace OpenGL {
38 38
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index c373c9cb4..b0aee6cc1 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -18,10 +18,8 @@
18#include "video_core/renderer_opengl/maxwell_to_gl.h" 18#include "video_core/renderer_opengl/maxwell_to_gl.h"
19#include "video_core/renderer_opengl/util_shaders.h" 19#include "video_core/renderer_opengl/util_shaders.h"
20#include "video_core/surface.h" 20#include "video_core/surface.h"
21#include "video_core/texture_cache/format_lookup_table.h" 21#include "video_core/texture_cache/formatter.h"
22#include "video_core/texture_cache/samples_helper.h" 22#include "video_core/texture_cache/samples_helper.h"
23#include "video_core/texture_cache/texture_cache.h"
24#include "video_core/textures/decoders.h"
25 23
26namespace OpenGL { 24namespace OpenGL {
27namespace { 25namespace {
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index 921072ebe..4a4f6301c 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -12,7 +12,7 @@
12#include "shader_recompiler/shader_info.h" 12#include "shader_recompiler/shader_info.h"
13#include "video_core/renderer_opengl/gl_resource_manager.h" 13#include "video_core/renderer_opengl/gl_resource_manager.h"
14#include "video_core/renderer_opengl/util_shaders.h" 14#include "video_core/renderer_opengl/util_shaders.h"
15#include "video_core/texture_cache/texture_cache.h" 15#include "video_core/texture_cache/texture_cache_base.h"
16 16
17namespace OpenGL { 17namespace OpenGL {
18 18
diff --git a/src/video_core/renderer_opengl/gl_texture_cache_base.cpp b/src/video_core/renderer_opengl/gl_texture_cache_base.cpp
new file mode 100644
index 000000000..385358fea
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_texture_cache_base.cpp
@@ -0,0 +1,10 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "video_core/renderer_opengl/gl_texture_cache.h"
6#include "video_core/texture_cache/texture_cache.h"
7
8namespace VideoCommon {
9template class VideoCommon::TextureCache<OpenGL::TextureCacheParams>;
10}
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index f1b00c24c..7d7cba69c 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -486,7 +486,7 @@ void RendererOpenGL::RenderScreenshot() {
486 glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb); 486 glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb);
487 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb); 487 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
488 488
489 renderer_settings.screenshot_complete_callback(); 489 renderer_settings.screenshot_complete_callback(true);
490 renderer_settings.screenshot_requested = false; 490 renderer_settings.screenshot_requested = false;
491} 491}
492 492
diff --git a/src/video_core/renderer_opengl/util_shaders.cpp b/src/video_core/renderer_opengl/util_shaders.cpp
index 37a4d1d9d..333f35a1c 100644
--- a/src/video_core/renderer_opengl/util_shaders.cpp
+++ b/src/video_core/renderer_opengl/util_shaders.cpp
@@ -60,19 +60,14 @@ UtilShaders::UtilShaders(ProgramManager& program_manager_)
60 copy_bc4_program(MakeProgram(OPENGL_COPY_BC4_COMP)) { 60 copy_bc4_program(MakeProgram(OPENGL_COPY_BC4_COMP)) {
61 const auto swizzle_table = Tegra::Texture::MakeSwizzleTable(); 61 const auto swizzle_table = Tegra::Texture::MakeSwizzleTable();
62 swizzle_table_buffer.Create(); 62 swizzle_table_buffer.Create();
63 astc_buffer.Create();
64 glNamedBufferStorage(swizzle_table_buffer.handle, sizeof(swizzle_table), &swizzle_table, 0); 63 glNamedBufferStorage(swizzle_table_buffer.handle, sizeof(swizzle_table), &swizzle_table, 0);
65 glNamedBufferStorage(astc_buffer.handle, sizeof(ASTC_ENCODINGS_VALUES), &ASTC_ENCODINGS_VALUES,
66 0);
67} 64}
68 65
69UtilShaders::~UtilShaders() = default; 66UtilShaders::~UtilShaders() = default;
70 67
71void UtilShaders::ASTCDecode(Image& image, const ImageBufferMap& map, 68void UtilShaders::ASTCDecode(Image& image, const ImageBufferMap& map,
72 std::span<const VideoCommon::SwizzleParameters> swizzles) { 69 std::span<const VideoCommon::SwizzleParameters> swizzles) {
73 static constexpr GLuint BINDING_SWIZZLE_BUFFER = 0; 70 static constexpr GLuint BINDING_INPUT_BUFFER = 0;
74 static constexpr GLuint BINDING_INPUT_BUFFER = 1;
75 static constexpr GLuint BINDING_ENC_BUFFER = 2;
76 static constexpr GLuint BINDING_OUTPUT_IMAGE = 0; 71 static constexpr GLuint BINDING_OUTPUT_IMAGE = 0;
77 72
78 const Extent2D tile_size{ 73 const Extent2D tile_size{
@@ -80,34 +75,32 @@ void UtilShaders::ASTCDecode(Image& image, const ImageBufferMap& map,
80 .height = VideoCore::Surface::DefaultBlockHeight(image.info.format), 75 .height = VideoCore::Surface::DefaultBlockHeight(image.info.format),
81 }; 76 };
82 program_manager.BindComputeProgram(astc_decoder_program.handle); 77 program_manager.BindComputeProgram(astc_decoder_program.handle);
83 glBindBufferBase(GL_SHADER_STORAGE_BUFFER, BINDING_SWIZZLE_BUFFER, swizzle_table_buffer.handle);
84 glBindBufferBase(GL_SHADER_STORAGE_BUFFER, BINDING_ENC_BUFFER, astc_buffer.handle);
85
86 glFlushMappedNamedBufferRange(map.buffer, map.offset, image.guest_size_bytes); 78 glFlushMappedNamedBufferRange(map.buffer, map.offset, image.guest_size_bytes);
87 glUniform2ui(1, tile_size.width, tile_size.height); 79 glUniform2ui(1, tile_size.width, tile_size.height);
80
88 // Ensure buffer data is valid before dispatching 81 // Ensure buffer data is valid before dispatching
89 glFlush(); 82 glFlush();
90 for (const SwizzleParameters& swizzle : swizzles) { 83 for (const SwizzleParameters& swizzle : swizzles) {
91 const size_t input_offset = swizzle.buffer_offset + map.offset; 84 const size_t input_offset = swizzle.buffer_offset + map.offset;
92 const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 32U); 85 const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 8U);
93 const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 32U); 86 const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 8U);
94 87
95 const auto params = MakeBlockLinearSwizzle2DParams(swizzle, image.info); 88 const auto params = MakeBlockLinearSwizzle2DParams(swizzle, image.info);
96 ASSERT(params.origin == (std::array<u32, 3>{0, 0, 0})); 89 ASSERT(params.origin == (std::array<u32, 3>{0, 0, 0}));
97 ASSERT(params.destination == (std::array<s32, 3>{0, 0, 0})); 90 ASSERT(params.destination == (std::array<s32, 3>{0, 0, 0}));
91 ASSERT(params.bytes_per_block_log2 == 4);
98 92
99 glUniform1ui(2, params.bytes_per_block_log2); 93 glUniform1ui(2, params.layer_stride);
100 glUniform1ui(3, params.layer_stride); 94 glUniform1ui(3, params.block_size);
101 glUniform1ui(4, params.block_size); 95 glUniform1ui(4, params.x_shift);
102 glUniform1ui(5, params.x_shift); 96 glUniform1ui(5, params.block_height);
103 glUniform1ui(6, params.block_height); 97 glUniform1ui(6, params.block_height_mask);
104 glUniform1ui(7, params.block_height_mask);
105 98
106 glBindImageTexture(BINDING_OUTPUT_IMAGE, image.StorageHandle(), swizzle.level, GL_TRUE, 0,
107 GL_WRITE_ONLY, GL_RGBA8);
108 // ASTC texture data 99 // ASTC texture data
109 glBindBufferRange(GL_SHADER_STORAGE_BUFFER, BINDING_INPUT_BUFFER, map.buffer, input_offset, 100 glBindBufferRange(GL_SHADER_STORAGE_BUFFER, BINDING_INPUT_BUFFER, map.buffer, input_offset,
110 image.guest_size_bytes - swizzle.buffer_offset); 101 image.guest_size_bytes - swizzle.buffer_offset);
102 glBindImageTexture(BINDING_OUTPUT_IMAGE, image.StorageHandle(), swizzle.level, GL_TRUE, 0,
103 GL_WRITE_ONLY, GL_RGBA8);
111 104
112 glDispatchCompute(num_dispatches_x, num_dispatches_y, image.info.resources.layers); 105 glDispatchCompute(num_dispatches_x, num_dispatches_y, image.info.resources.layers);
113 } 106 }
diff --git a/src/video_core/renderer_opengl/util_shaders.h b/src/video_core/renderer_opengl/util_shaders.h
index 53d65f368..ef881e35f 100644
--- a/src/video_core/renderer_opengl/util_shaders.h
+++ b/src/video_core/renderer_opengl/util_shaders.h
@@ -62,7 +62,6 @@ private:
62 ProgramManager& program_manager; 62 ProgramManager& program_manager;
63 63
64 OGLBuffer swizzle_table_buffer; 64 OGLBuffer swizzle_table_buffer;
65 OGLBuffer astc_buffer;
66 65
67 OGLProgram astc_decoder_program; 66 OGLProgram astc_decoder_program;
68 OGLProgram block_linear_unswizzle_2d_program; 67 OGLProgram block_linear_unswizzle_2d_program;
diff --git a/src/video_core/renderer_vulkan/pipeline_statistics.cpp b/src/video_core/renderer_vulkan/pipeline_statistics.cpp
new file mode 100644
index 000000000..bfec931a6
--- /dev/null
+++ b/src/video_core/renderer_vulkan/pipeline_statistics.cpp
@@ -0,0 +1,100 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <string_view>
6
7#include <fmt/format.h>
8
9#include "common/common_types.h"
10#include "common/logging/log.h"
11#include "video_core/renderer_vulkan/pipeline_statistics.h"
12#include "video_core/vulkan_common/vulkan_device.h"
13#include "video_core/vulkan_common/vulkan_wrapper.h"
14
15namespace Vulkan {
16
17using namespace std::string_view_literals;
18
19static u64 GetUint64(const VkPipelineExecutableStatisticKHR& statistic) {
20 switch (statistic.format) {
21 case VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_INT64_KHR:
22 return static_cast<u64>(statistic.value.i64);
23 case VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR:
24 return statistic.value.u64;
25 case VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_FLOAT64_KHR:
26 return static_cast<u64>(statistic.value.f64);
27 default:
28 return 0;
29 }
30}
31
32PipelineStatistics::PipelineStatistics(const Device& device_) : device{device_} {}
33
34void PipelineStatistics::Collect(VkPipeline pipeline) {
35 const auto& dev{device.GetLogical()};
36 const std::vector properties{dev.GetPipelineExecutablePropertiesKHR(pipeline)};
37 const u32 num_executables{static_cast<u32>(properties.size())};
38 for (u32 executable = 0; executable < num_executables; ++executable) {
39 const auto statistics{dev.GetPipelineExecutableStatisticsKHR(pipeline, executable)};
40 if (statistics.empty()) {
41 continue;
42 }
43 Stats stage_stats;
44 for (const auto& statistic : statistics) {
45 const char* const name{statistic.name};
46 if (name == "Binary Size"sv || name == "Code size"sv || name == "Instruction Count"sv) {
47 stage_stats.code_size = GetUint64(statistic);
48 } else if (name == "Register Count"sv) {
49 stage_stats.register_count = GetUint64(statistic);
50 } else if (name == "SGPRs"sv || name == "numUsedSgprs"sv) {
51 stage_stats.sgpr_count = GetUint64(statistic);
52 } else if (name == "VGPRs"sv || name == "numUsedVgprs"sv) {
53 stage_stats.vgpr_count = GetUint64(statistic);
54 } else if (name == "Branches"sv) {
55 stage_stats.branches_count = GetUint64(statistic);
56 } else if (name == "Basic Block Count"sv) {
57 stage_stats.basic_block_count = GetUint64(statistic);
58 }
59 }
60 std::lock_guard lock{mutex};
61 collected_stats.push_back(stage_stats);
62 }
63}
64
65void PipelineStatistics::Report() const {
66 double num{};
67 Stats total;
68 {
69 std::lock_guard lock{mutex};
70 for (const Stats& stats : collected_stats) {
71 total.code_size += stats.code_size;
72 total.register_count += stats.register_count;
73 total.sgpr_count += stats.sgpr_count;
74 total.vgpr_count += stats.vgpr_count;
75 total.branches_count += stats.branches_count;
76 total.basic_block_count += stats.basic_block_count;
77 }
78 num = static_cast<double>(collected_stats.size());
79 }
80 std::string report;
81 const auto add = [&](const char* fmt, u64 value) {
82 if (value > 0) {
83 report += fmt::format(fmt::runtime(fmt), static_cast<double>(value) / num);
84 }
85 };
86 add("Code size: {:9.03f}\n", total.code_size);
87 add("Register count: {:9.03f}\n", total.register_count);
88 add("SGPRs: {:9.03f}\n", total.sgpr_count);
89 add("VGPRs: {:9.03f}\n", total.vgpr_count);
90 add("Branches count: {:9.03f}\n", total.branches_count);
91 add("Basic blocks: {:9.03f}\n", total.basic_block_count);
92
93 LOG_INFO(Render_Vulkan,
94 "\nAverage pipeline statistics\n"
95 "==========================================\n"
96 "{}\n",
97 report);
98}
99
100} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/pipeline_statistics.h b/src/video_core/renderer_vulkan/pipeline_statistics.h
new file mode 100644
index 000000000..b61840107
--- /dev/null
+++ b/src/video_core/renderer_vulkan/pipeline_statistics.h
@@ -0,0 +1,40 @@
1// Copyright 2021 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 <mutex>
8#include <vector>
9
10#include "common/common_types.h"
11#include "video_core/vulkan_common/vulkan_wrapper.h"
12
13namespace Vulkan {
14
15class Device;
16
17class PipelineStatistics {
18public:
19 explicit PipelineStatistics(const Device& device_);
20
21 void Collect(VkPipeline pipeline);
22
23 void Report() const;
24
25private:
26 struct Stats {
27 u64 code_size{};
28 u64 register_count{};
29 u64 sgpr_count{};
30 u64 vgpr_count{};
31 u64 branches_count{};
32 u64 basic_block_count{};
33 };
34
35 const Device& device;
36 mutable std::mutex mutex;
37 std::vector<Stats> collected_stats;
38};
39
40} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index a8d04dc61..7c9b0d6db 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -138,6 +138,7 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
138 const bool use_accelerated = 138 const bool use_accelerated =
139 rasterizer.AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride); 139 rasterizer.AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride);
140 const bool is_srgb = use_accelerated && screen_info.is_srgb; 140 const bool is_srgb = use_accelerated && screen_info.is_srgb;
141 RenderScreenshot(*framebuffer, use_accelerated);
141 142
142 bool has_been_recreated = false; 143 bool has_been_recreated = false;
143 const auto recreate_swapchain = [&] { 144 const auto recreate_swapchain = [&] {
@@ -162,7 +163,7 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
162 if (has_been_recreated) { 163 if (has_been_recreated) {
163 blit_screen.Recreate(); 164 blit_screen.Recreate();
164 } 165 }
165 const VkSemaphore render_semaphore = blit_screen.Draw(*framebuffer, use_accelerated); 166 const VkSemaphore render_semaphore = blit_screen.DrawToSwapchain(*framebuffer, use_accelerated);
166 scheduler.Flush(render_semaphore); 167 scheduler.Flush(render_semaphore);
167 scheduler.WaitWorker(); 168 scheduler.WaitWorker();
168 swapchain.Present(render_semaphore); 169 swapchain.Present(render_semaphore);
@@ -193,4 +194,153 @@ void RendererVulkan::Report() const {
193 telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions); 194 telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions);
194} 195}
195 196
197void Vulkan::RendererVulkan::RenderScreenshot(const Tegra::FramebufferConfig& framebuffer,
198 bool use_accelerated) {
199 if (!renderer_settings.screenshot_requested) {
200 return;
201 }
202 const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout};
203 vk::Image staging_image = device.GetLogical().CreateImage(VkImageCreateInfo{
204 .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
205 .pNext = nullptr,
206 .flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT,
207 .imageType = VK_IMAGE_TYPE_2D,
208 .format = VK_FORMAT_B8G8R8A8_UNORM,
209 .extent =
210 {
211 .width = layout.width,
212 .height = layout.height,
213 .depth = 1,
214 },
215 .mipLevels = 1,
216 .arrayLayers = 1,
217 .samples = VK_SAMPLE_COUNT_1_BIT,
218 .tiling = VK_IMAGE_TILING_OPTIMAL,
219 .usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
220 VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
221 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
222 .queueFamilyIndexCount = 0,
223 .pQueueFamilyIndices = nullptr,
224 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
225 });
226 const auto image_commit = memory_allocator.Commit(staging_image, MemoryUsage::DeviceLocal);
227
228 const vk::ImageView dst_view = device.GetLogical().CreateImageView(VkImageViewCreateInfo{
229 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
230 .pNext = nullptr,
231 .flags = 0,
232 .image = *staging_image,
233 .viewType = VK_IMAGE_VIEW_TYPE_2D,
234 .format = screen_info.is_srgb ? VK_FORMAT_B8G8R8A8_SRGB : VK_FORMAT_B8G8R8A8_UNORM,
235 .components{
236 .r = VK_COMPONENT_SWIZZLE_IDENTITY,
237 .g = VK_COMPONENT_SWIZZLE_IDENTITY,
238 .b = VK_COMPONENT_SWIZZLE_IDENTITY,
239 .a = VK_COMPONENT_SWIZZLE_IDENTITY,
240 },
241 .subresourceRange{
242 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
243 .baseMipLevel = 0,
244 .levelCount = 1,
245 .baseArrayLayer = 0,
246 .layerCount = VK_REMAINING_ARRAY_LAYERS,
247 },
248 });
249 const VkExtent2D render_area{.width = layout.width, .height = layout.height};
250 const vk::Framebuffer screenshot_fb = blit_screen.CreateFramebuffer(*dst_view, render_area);
251 // Since we're not rendering to the screen, ignore the render semaphore.
252 void(blit_screen.Draw(framebuffer, *screenshot_fb, layout, render_area, use_accelerated));
253
254 const auto buffer_size = static_cast<VkDeviceSize>(layout.width * layout.height * 4);
255 const VkBufferCreateInfo dst_buffer_info{
256 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
257 .pNext = nullptr,
258 .flags = 0,
259 .size = buffer_size,
260 .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT,
261 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
262 .queueFamilyIndexCount = 0,
263 .pQueueFamilyIndices = nullptr,
264 };
265 const vk::Buffer dst_buffer = device.GetLogical().CreateBuffer(dst_buffer_info);
266 MemoryCommit dst_buffer_memory = memory_allocator.Commit(dst_buffer, MemoryUsage::Download);
267
268 scheduler.RequestOutsideRenderPassOperationContext();
269 scheduler.Record([&](vk::CommandBuffer cmdbuf) {
270 const VkImageMemoryBarrier read_barrier{
271 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
272 .pNext = nullptr,
273 .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
274 .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
275 .oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
276 .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
277 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
278 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
279 .image = *staging_image,
280 .subresourceRange{
281 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
282 .baseMipLevel = 0,
283 .levelCount = VK_REMAINING_MIP_LEVELS,
284 .baseArrayLayer = 0,
285 .layerCount = VK_REMAINING_ARRAY_LAYERS,
286 },
287 };
288 const VkImageMemoryBarrier image_write_barrier{
289 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
290 .pNext = nullptr,
291 .srcAccessMask = 0,
292 .dstAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
293 .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
294 .newLayout = VK_IMAGE_LAYOUT_GENERAL,
295 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
296 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
297 .image = *staging_image,
298 .subresourceRange{
299 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
300 .baseMipLevel = 0,
301 .levelCount = VK_REMAINING_MIP_LEVELS,
302 .baseArrayLayer = 0,
303 .layerCount = VK_REMAINING_ARRAY_LAYERS,
304 },
305 };
306 static constexpr VkMemoryBarrier memory_write_barrier{
307 .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
308 .pNext = nullptr,
309 .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
310 .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
311 };
312 const VkBufferImageCopy copy{
313 .bufferOffset = 0,
314 .bufferRowLength = 0,
315 .bufferImageHeight = 0,
316 .imageSubresource{
317 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
318 .mipLevel = 0,
319 .baseArrayLayer = 0,
320 .layerCount = 1,
321 },
322 .imageOffset{.x = 0, .y = 0, .z = 0},
323 .imageExtent{
324 .width = layout.width,
325 .height = layout.height,
326 .depth = 1,
327 },
328 };
329 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
330 0, read_barrier);
331 cmdbuf.CopyImageToBuffer(*staging_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *dst_buffer,
332 copy);
333 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
334 0, memory_write_barrier, nullptr, image_write_barrier);
335 });
336 // Ensure the copy is fully completed before saving the screenshot
337 scheduler.Finish();
338
339 // Copy backing image data to the QImage screenshot buffer
340 const auto dst_memory_map = dst_buffer_memory.Map();
341 std::memcpy(renderer_settings.screenshot_bits, dst_memory_map.data(), dst_memory_map.size());
342 renderer_settings.screenshot_complete_callback(false);
343 renderer_settings.screenshot_requested = false;
344}
345
196} // namespace Vulkan 346} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index d7d17e110..6dc985109 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -54,6 +54,8 @@ public:
54private: 54private:
55 void Report() const; 55 void Report() const;
56 56
57 void RenderScreenshot(const Tegra::FramebufferConfig& framebuffer, bool use_accelerated);
58
57 Core::TelemetrySession& telemetry_session; 59 Core::TelemetrySession& telemetry_session;
58 Core::Memory::Memory& cpu_memory; 60 Core::Memory::Memory& cpu_memory;
59 Tegra::GPU& gpu; 61 Tegra::GPU& gpu;
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index 516f428e7..5c43b8acf 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -130,7 +130,10 @@ void VKBlitScreen::Recreate() {
130 CreateDynamicResources(); 130 CreateDynamicResources();
131} 131}
132 132
133VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, bool use_accelerated) { 133VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
134 const VkFramebuffer& host_framebuffer,
135 const Layout::FramebufferLayout layout, VkExtent2D render_area,
136 bool use_accelerated) {
134 RefreshResources(framebuffer); 137 RefreshResources(framebuffer);
135 138
136 // Finish any pending renderpass 139 // Finish any pending renderpass
@@ -145,8 +148,8 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, bool
145 use_accelerated ? screen_info.image_view : *raw_image_views[image_index]); 148 use_accelerated ? screen_info.image_view : *raw_image_views[image_index]);
146 149
147 BufferData data; 150 BufferData data;
148 SetUniformData(data, framebuffer); 151 SetUniformData(data, layout);
149 SetVertexData(data, framebuffer); 152 SetVertexData(data, framebuffer, layout);
150 153
151 const std::span<u8> mapped_span = buffer_commit.Map(); 154 const std::span<u8> mapped_span = buffer_commit.Map();
152 std::memcpy(mapped_span.data(), &data, sizeof(data)); 155 std::memcpy(mapped_span.data(), &data, sizeof(data));
@@ -220,52 +223,75 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, bool
220 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, write_barrier); 223 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, write_barrier);
221 }); 224 });
222 } 225 }
223 scheduler.Record([this, image_index, size = swapchain.GetSize()](vk::CommandBuffer cmdbuf) { 226 scheduler.Record(
224 const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f; 227 [this, host_framebuffer, image_index, size = render_area](vk::CommandBuffer cmdbuf) {
225 const f32 bg_green = Settings::values.bg_green.GetValue() / 255.0f; 228 const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f;
226 const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f; 229 const f32 bg_green = Settings::values.bg_green.GetValue() / 255.0f;
227 const VkClearValue clear_color{ 230 const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f;
228 .color = {.float32 = {bg_red, bg_green, bg_blue, 1.0f}}, 231 const VkClearValue clear_color{
229 }; 232 .color = {.float32 = {bg_red, bg_green, bg_blue, 1.0f}},
230 const VkRenderPassBeginInfo renderpass_bi{ 233 };
231 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, 234 const VkRenderPassBeginInfo renderpass_bi{
232 .pNext = nullptr, 235 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
233 .renderPass = *renderpass, 236 .pNext = nullptr,
234 .framebuffer = *framebuffers[image_index], 237 .renderPass = *renderpass,
235 .renderArea = 238 .framebuffer = host_framebuffer,
236 { 239 .renderArea =
237 .offset = {0, 0}, 240 {
238 .extent = size, 241 .offset = {0, 0},
239 }, 242 .extent = size,
240 .clearValueCount = 1, 243 },
241 .pClearValues = &clear_color, 244 .clearValueCount = 1,
242 }; 245 .pClearValues = &clear_color,
243 const VkViewport viewport{ 246 };
244 .x = 0.0f, 247 const VkViewport viewport{
245 .y = 0.0f, 248 .x = 0.0f,
246 .width = static_cast<float>(size.width), 249 .y = 0.0f,
247 .height = static_cast<float>(size.height), 250 .width = static_cast<float>(size.width),
248 .minDepth = 0.0f, 251 .height = static_cast<float>(size.height),
249 .maxDepth = 1.0f, 252 .minDepth = 0.0f,
250 }; 253 .maxDepth = 1.0f,
251 const VkRect2D scissor{ 254 };
252 .offset = {0, 0}, 255 const VkRect2D scissor{
253 .extent = size, 256 .offset = {0, 0},
254 }; 257 .extent = size,
255 cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE); 258 };
256 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline); 259 cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
257 cmdbuf.SetViewport(0, viewport); 260 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
258 cmdbuf.SetScissor(0, scissor); 261 cmdbuf.SetViewport(0, viewport);
259 262 cmdbuf.SetScissor(0, scissor);
260 cmdbuf.BindVertexBuffer(0, *buffer, offsetof(BufferData, vertices)); 263
261 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline_layout, 0, 264 cmdbuf.BindVertexBuffer(0, *buffer, offsetof(BufferData, vertices));
262 descriptor_sets[image_index], {}); 265 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline_layout, 0,
263 cmdbuf.Draw(4, 1, 0, 0); 266 descriptor_sets[image_index], {});
264 cmdbuf.EndRenderPass(); 267 cmdbuf.Draw(4, 1, 0, 0);
265 }); 268 cmdbuf.EndRenderPass();
269 });
266 return *semaphores[image_index]; 270 return *semaphores[image_index];
267} 271}
268 272
273VkSemaphore VKBlitScreen::DrawToSwapchain(const Tegra::FramebufferConfig& framebuffer,
274 bool use_accelerated) {
275 const std::size_t image_index = swapchain.GetImageIndex();
276 const VkExtent2D render_area = swapchain.GetSize();
277 const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout();
278 return Draw(framebuffer, *framebuffers[image_index], layout, render_area, use_accelerated);
279}
280
281vk::Framebuffer VKBlitScreen::CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent) {
282 return device.GetLogical().CreateFramebuffer(VkFramebufferCreateInfo{
283 .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
284 .pNext = nullptr,
285 .flags = 0,
286 .renderPass = *renderpass,
287 .attachmentCount = 1,
288 .pAttachments = &image_view,
289 .width = extent.width,
290 .height = extent.height,
291 .layers = 1,
292 });
293}
294
269void VKBlitScreen::CreateStaticResources() { 295void VKBlitScreen::CreateStaticResources() {
270 CreateShaders(); 296 CreateShaders();
271 CreateSemaphores(); 297 CreateSemaphores();
@@ -609,22 +635,9 @@ void VKBlitScreen::CreateFramebuffers() {
609 const VkExtent2D size{swapchain.GetSize()}; 635 const VkExtent2D size{swapchain.GetSize()};
610 framebuffers.resize(image_count); 636 framebuffers.resize(image_count);
611 637
612 VkFramebufferCreateInfo ci{
613 .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
614 .pNext = nullptr,
615 .flags = 0,
616 .renderPass = *renderpass,
617 .attachmentCount = 1,
618 .pAttachments = nullptr,
619 .width = size.width,
620 .height = size.height,
621 .layers = 1,
622 };
623
624 for (std::size_t i = 0; i < image_count; ++i) { 638 for (std::size_t i = 0; i < image_count; ++i) {
625 const VkImageView image_view{swapchain.GetImageViewIndex(i)}; 639 const VkImageView image_view{swapchain.GetImageViewIndex(i)};
626 ci.pAttachments = &image_view; 640 framebuffers[i] = CreateFramebuffer(image_view, size);
627 framebuffers[i] = device.GetLogical().CreateFramebuffer(ci);
628 } 641 }
629} 642}
630 643
@@ -752,15 +765,13 @@ void VKBlitScreen::UpdateDescriptorSet(std::size_t image_index, VkImageView imag
752 device.GetLogical().UpdateDescriptorSets(std::array{ubo_write, sampler_write}, {}); 765 device.GetLogical().UpdateDescriptorSets(std::array{ubo_write, sampler_write}, {});
753} 766}
754 767
755void VKBlitScreen::SetUniformData(BufferData& data, 768void VKBlitScreen::SetUniformData(BufferData& data, const Layout::FramebufferLayout layout) const {
756 const Tegra::FramebufferConfig& framebuffer) const {
757 const auto& layout = render_window.GetFramebufferLayout();
758 data.uniform.modelview_matrix = 769 data.uniform.modelview_matrix =
759 MakeOrthographicMatrix(static_cast<f32>(layout.width), static_cast<f32>(layout.height)); 770 MakeOrthographicMatrix(static_cast<f32>(layout.width), static_cast<f32>(layout.height));
760} 771}
761 772
762void VKBlitScreen::SetVertexData(BufferData& data, 773void VKBlitScreen::SetVertexData(BufferData& data, const Tegra::FramebufferConfig& framebuffer,
763 const Tegra::FramebufferConfig& framebuffer) const { 774 const Layout::FramebufferLayout layout) const {
764 const auto& framebuffer_transform_flags = framebuffer.transform_flags; 775 const auto& framebuffer_transform_flags = framebuffer.transform_flags;
765 const auto& framebuffer_crop_rect = framebuffer.crop_rect; 776 const auto& framebuffer_crop_rect = framebuffer.crop_rect;
766 777
@@ -798,7 +809,7 @@ void VKBlitScreen::SetVertexData(BufferData& data,
798 static_cast<f32>(screen_info.height); 809 static_cast<f32>(screen_info.height);
799 } 810 }
800 811
801 const auto& screen = render_window.GetFramebufferLayout().screen; 812 const auto& screen = layout.screen;
802 const auto x = static_cast<f32>(screen.left); 813 const auto x = static_cast<f32>(screen.left);
803 const auto y = static_cast<f32>(screen.top); 814 const auto y = static_cast<f32>(screen.top);
804 const auto w = static_cast<f32>(screen.GetWidth()); 815 const auto w = static_cast<f32>(screen.GetWidth());
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.h b/src/video_core/renderer_vulkan/vk_blit_screen.h
index 5e3177685..430bcfbca 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.h
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.h
@@ -56,8 +56,16 @@ public:
56 void Recreate(); 56 void Recreate();
57 57
58 [[nodiscard]] VkSemaphore Draw(const Tegra::FramebufferConfig& framebuffer, 58 [[nodiscard]] VkSemaphore Draw(const Tegra::FramebufferConfig& framebuffer,
59 const VkFramebuffer& host_framebuffer,
60 const Layout::FramebufferLayout layout, VkExtent2D render_area,
59 bool use_accelerated); 61 bool use_accelerated);
60 62
63 [[nodiscard]] VkSemaphore DrawToSwapchain(const Tegra::FramebufferConfig& framebuffer,
64 bool use_accelerated);
65
66 [[nodiscard]] vk::Framebuffer CreateFramebuffer(const VkImageView& image_view,
67 VkExtent2D extent);
68
61private: 69private:
62 struct BufferData; 70 struct BufferData;
63 71
@@ -81,8 +89,9 @@ private:
81 void CreateRawImages(const Tegra::FramebufferConfig& framebuffer); 89 void CreateRawImages(const Tegra::FramebufferConfig& framebuffer);
82 90
83 void UpdateDescriptorSet(std::size_t image_index, VkImageView image_view) const; 91 void UpdateDescriptorSet(std::size_t image_index, VkImageView image_view) const;
84 void SetUniformData(BufferData& data, const Tegra::FramebufferConfig& framebuffer) const; 92 void SetUniformData(BufferData& data, const Layout::FramebufferLayout layout) const;
85 void SetVertexData(BufferData& data, const Tegra::FramebufferConfig& framebuffer) const; 93 void SetVertexData(BufferData& data, const Tegra::FramebufferConfig& framebuffer,
94 const Layout::FramebufferLayout layout) const;
86 95
87 u64 CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const; 96 u64 CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const;
88 u64 GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer, 97 u64 GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer,
diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
index 561cf5e11..3e96c0f60 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
@@ -30,16 +30,12 @@
30namespace Vulkan { 30namespace Vulkan {
31 31
32using Tegra::Texture::SWIZZLE_TABLE; 32using Tegra::Texture::SWIZZLE_TABLE;
33using Tegra::Texture::ASTC::ASTC_ENCODINGS_VALUES;
34using namespace Tegra::Texture::ASTC;
35 33
36namespace { 34namespace {
37 35
38constexpr u32 ASTC_BINDING_INPUT_BUFFER = 0; 36constexpr u32 ASTC_BINDING_INPUT_BUFFER = 0;
39constexpr u32 ASTC_BINDING_ENC_BUFFER = 1; 37constexpr u32 ASTC_BINDING_OUTPUT_IMAGE = 1;
40constexpr u32 ASTC_BINDING_SWIZZLE_BUFFER = 2; 38constexpr size_t ASTC_NUM_BINDINGS = 2;
41constexpr u32 ASTC_BINDING_OUTPUT_IMAGE = 3;
42constexpr size_t ASTC_NUM_BINDINGS = 4;
43 39
44template <size_t size> 40template <size_t size>
45inline constexpr VkPushConstantRange COMPUTE_PUSH_CONSTANT_RANGE{ 41inline constexpr VkPushConstantRange COMPUTE_PUSH_CONSTANT_RANGE{
@@ -75,7 +71,7 @@ constexpr DescriptorBankInfo INPUT_OUTPUT_BANK_INFO{
75 .score = 2, 71 .score = 2,
76}; 72};
77 73
78constexpr std::array<VkDescriptorSetLayoutBinding, 4> ASTC_DESCRIPTOR_SET_BINDINGS{{ 74constexpr std::array<VkDescriptorSetLayoutBinding, ASTC_NUM_BINDINGS> ASTC_DESCRIPTOR_SET_BINDINGS{{
79 { 75 {
80 .binding = ASTC_BINDING_INPUT_BUFFER, 76 .binding = ASTC_BINDING_INPUT_BUFFER,
81 .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 77 .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
@@ -84,20 +80,6 @@ constexpr std::array<VkDescriptorSetLayoutBinding, 4> ASTC_DESCRIPTOR_SET_BINDIN
84 .pImmutableSamplers = nullptr, 80 .pImmutableSamplers = nullptr,
85 }, 81 },
86 { 82 {
87 .binding = ASTC_BINDING_ENC_BUFFER,
88 .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
89 .descriptorCount = 1,
90 .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
91 .pImmutableSamplers = nullptr,
92 },
93 {
94 .binding = ASTC_BINDING_SWIZZLE_BUFFER,
95 .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
96 .descriptorCount = 1,
97 .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
98 .pImmutableSamplers = nullptr,
99 },
100 {
101 .binding = ASTC_BINDING_OUTPUT_IMAGE, 83 .binding = ASTC_BINDING_OUTPUT_IMAGE,
102 .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 84 .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
103 .descriptorCount = 1, 85 .descriptorCount = 1,
@@ -108,12 +90,12 @@ constexpr std::array<VkDescriptorSetLayoutBinding, 4> ASTC_DESCRIPTOR_SET_BINDIN
108 90
109constexpr DescriptorBankInfo ASTC_BANK_INFO{ 91constexpr DescriptorBankInfo ASTC_BANK_INFO{
110 .uniform_buffers = 0, 92 .uniform_buffers = 0,
111 .storage_buffers = 3, 93 .storage_buffers = 1,
112 .texture_buffers = 0, 94 .texture_buffers = 0,
113 .image_buffers = 0, 95 .image_buffers = 0,
114 .textures = 0, 96 .textures = 0,
115 .images = 1, 97 .images = 1,
116 .score = 4, 98 .score = 2,
117}; 99};
118 100
119constexpr VkDescriptorUpdateTemplateEntryKHR INPUT_OUTPUT_DESCRIPTOR_UPDATE_TEMPLATE{ 101constexpr VkDescriptorUpdateTemplateEntryKHR INPUT_OUTPUT_DESCRIPTOR_UPDATE_TEMPLATE{
@@ -136,22 +118,6 @@ constexpr std::array<VkDescriptorUpdateTemplateEntryKHR, ASTC_NUM_BINDINGS>
136 .stride = sizeof(DescriptorUpdateEntry), 118 .stride = sizeof(DescriptorUpdateEntry),
137 }, 119 },
138 { 120 {
139 .dstBinding = ASTC_BINDING_ENC_BUFFER,
140 .dstArrayElement = 0,
141 .descriptorCount = 1,
142 .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
143 .offset = ASTC_BINDING_ENC_BUFFER * sizeof(DescriptorUpdateEntry),
144 .stride = sizeof(DescriptorUpdateEntry),
145 },
146 {
147 .dstBinding = ASTC_BINDING_SWIZZLE_BUFFER,
148 .dstArrayElement = 0,
149 .descriptorCount = 1,
150 .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
151 .offset = ASTC_BINDING_SWIZZLE_BUFFER * sizeof(DescriptorUpdateEntry),
152 .stride = sizeof(DescriptorUpdateEntry),
153 },
154 {
155 .dstBinding = ASTC_BINDING_OUTPUT_IMAGE, 121 .dstBinding = ASTC_BINDING_OUTPUT_IMAGE,
156 .dstArrayElement = 0, 122 .dstArrayElement = 0,
157 .descriptorCount = 1, 123 .descriptorCount = 1,
@@ -163,7 +129,6 @@ constexpr std::array<VkDescriptorUpdateTemplateEntryKHR, ASTC_NUM_BINDINGS>
163 129
164struct AstcPushConstants { 130struct AstcPushConstants {
165 std::array<u32, 2> blocks_dims; 131 std::array<u32, 2> blocks_dims;
166 u32 bytes_per_block_log2;
167 u32 layer_stride; 132 u32 layer_stride;
168 u32 block_size; 133 u32 block_size;
169 u32 x_shift; 134 u32 x_shift;
@@ -354,46 +319,6 @@ ASTCDecoderPass::ASTCDecoderPass(const Device& device_, VKScheduler& scheduler_,
354 319
355ASTCDecoderPass::~ASTCDecoderPass() = default; 320ASTCDecoderPass::~ASTCDecoderPass() = default;
356 321
357void ASTCDecoderPass::MakeDataBuffer() {
358 constexpr size_t TOTAL_BUFFER_SIZE = sizeof(ASTC_ENCODINGS_VALUES) + sizeof(SWIZZLE_TABLE);
359 data_buffer = device.GetLogical().CreateBuffer(VkBufferCreateInfo{
360 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
361 .pNext = nullptr,
362 .flags = 0,
363 .size = TOTAL_BUFFER_SIZE,
364 .usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
365 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
366 .queueFamilyIndexCount = 0,
367 .pQueueFamilyIndices = nullptr,
368 });
369 data_buffer_commit = memory_allocator.Commit(data_buffer, MemoryUsage::Upload);
370
371 const auto staging_ref = staging_buffer_pool.Request(TOTAL_BUFFER_SIZE, MemoryUsage::Upload);
372 std::memcpy(staging_ref.mapped_span.data(), &ASTC_ENCODINGS_VALUES,
373 sizeof(ASTC_ENCODINGS_VALUES));
374 // Tack on the swizzle table at the end of the buffer
375 std::memcpy(staging_ref.mapped_span.data() + sizeof(ASTC_ENCODINGS_VALUES), &SWIZZLE_TABLE,
376 sizeof(SWIZZLE_TABLE));
377
378 scheduler.Record([src = staging_ref.buffer, offset = staging_ref.offset, dst = *data_buffer,
379 TOTAL_BUFFER_SIZE](vk::CommandBuffer cmdbuf) {
380 static constexpr VkMemoryBarrier write_barrier{
381 .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
382 .pNext = nullptr,
383 .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
384 .dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
385 };
386 const VkBufferCopy copy{
387 .srcOffset = offset,
388 .dstOffset = 0,
389 .size = TOTAL_BUFFER_SIZE,
390 };
391 cmdbuf.CopyBuffer(src, dst, copy);
392 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
393 0, write_barrier);
394 });
395}
396
397void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map, 322void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map,
398 std::span<const VideoCommon::SwizzleParameters> swizzles) { 323 std::span<const VideoCommon::SwizzleParameters> swizzles) {
399 using namespace VideoCommon::Accelerated; 324 using namespace VideoCommon::Accelerated;
@@ -402,9 +327,6 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map,
402 VideoCore::Surface::DefaultBlockHeight(image.info.format), 327 VideoCore::Surface::DefaultBlockHeight(image.info.format),
403 }; 328 };
404 scheduler.RequestOutsideRenderPassOperationContext(); 329 scheduler.RequestOutsideRenderPassOperationContext();
405 if (!data_buffer) {
406 MakeDataBuffer();
407 }
408 const VkPipeline vk_pipeline = *pipeline; 330 const VkPipeline vk_pipeline = *pipeline;
409 const VkImageAspectFlags aspect_mask = image.AspectMask(); 331 const VkImageAspectFlags aspect_mask = image.AspectMask();
410 const VkImage vk_image = image.Handle(); 332 const VkImage vk_image = image.Handle();
@@ -436,16 +358,13 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map,
436 }); 358 });
437 for (const VideoCommon::SwizzleParameters& swizzle : swizzles) { 359 for (const VideoCommon::SwizzleParameters& swizzle : swizzles) {
438 const size_t input_offset = swizzle.buffer_offset + map.offset; 360 const size_t input_offset = swizzle.buffer_offset + map.offset;
439 const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 32U); 361 const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 8U);
440 const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 32U); 362 const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 8U);
441 const u32 num_dispatches_z = image.info.resources.layers; 363 const u32 num_dispatches_z = image.info.resources.layers;
442 364
443 update_descriptor_queue.Acquire(); 365 update_descriptor_queue.Acquire();
444 update_descriptor_queue.AddBuffer(map.buffer, input_offset, 366 update_descriptor_queue.AddBuffer(map.buffer, input_offset,
445 image.guest_size_bytes - swizzle.buffer_offset); 367 image.guest_size_bytes - swizzle.buffer_offset);
446 update_descriptor_queue.AddBuffer(*data_buffer, 0, sizeof(ASTC_ENCODINGS_VALUES));
447 update_descriptor_queue.AddBuffer(*data_buffer, sizeof(ASTC_ENCODINGS_VALUES),
448 sizeof(SWIZZLE_TABLE));
449 update_descriptor_queue.AddImage(image.StorageImageView(swizzle.level)); 368 update_descriptor_queue.AddImage(image.StorageImageView(swizzle.level));
450 const void* const descriptor_data{update_descriptor_queue.UpdateData()}; 369 const void* const descriptor_data{update_descriptor_queue.UpdateData()};
451 370
@@ -453,11 +372,11 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map,
453 const auto params = MakeBlockLinearSwizzle2DParams(swizzle, image.info); 372 const auto params = MakeBlockLinearSwizzle2DParams(swizzle, image.info);
454 ASSERT(params.origin == (std::array<u32, 3>{0, 0, 0})); 373 ASSERT(params.origin == (std::array<u32, 3>{0, 0, 0}));
455 ASSERT(params.destination == (std::array<s32, 3>{0, 0, 0})); 374 ASSERT(params.destination == (std::array<s32, 3>{0, 0, 0}));
375 ASSERT(params.bytes_per_block_log2 == 4);
456 scheduler.Record([this, num_dispatches_x, num_dispatches_y, num_dispatches_z, block_dims, 376 scheduler.Record([this, num_dispatches_x, num_dispatches_y, num_dispatches_z, block_dims,
457 params, descriptor_data](vk::CommandBuffer cmdbuf) { 377 params, descriptor_data](vk::CommandBuffer cmdbuf) {
458 const AstcPushConstants uniforms{ 378 const AstcPushConstants uniforms{
459 .blocks_dims = block_dims, 379 .blocks_dims = block_dims,
460 .bytes_per_block_log2 = params.bytes_per_block_log2,
461 .layer_stride = params.layer_stride, 380 .layer_stride = params.layer_stride,
462 .block_size = params.block_size, 381 .block_size = params.block_size,
463 .x_shift = params.x_shift, 382 .x_shift = params.x_shift,
diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.h b/src/video_core/renderer_vulkan/vk_compute_pass.h
index 114aef2bd..c7b92cce0 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pass.h
+++ b/src/video_core/renderer_vulkan/vk_compute_pass.h
@@ -96,15 +96,10 @@ public:
96 std::span<const VideoCommon::SwizzleParameters> swizzles); 96 std::span<const VideoCommon::SwizzleParameters> swizzles);
97 97
98private: 98private:
99 void MakeDataBuffer();
100
101 VKScheduler& scheduler; 99 VKScheduler& scheduler;
102 StagingBufferPool& staging_buffer_pool; 100 StagingBufferPool& staging_buffer_pool;
103 VKUpdateDescriptorQueue& update_descriptor_queue; 101 VKUpdateDescriptorQueue& update_descriptor_queue;
104 MemoryAllocator& memory_allocator; 102 MemoryAllocator& memory_allocator;
105
106 vk::Buffer data_buffer;
107 MemoryCommit data_buffer_commit;
108}; 103};
109 104
110} // namespace Vulkan 105} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
index 70b84c7a6..44faf626a 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
@@ -8,6 +8,7 @@
8#include <boost/container/small_vector.hpp> 8#include <boost/container/small_vector.hpp>
9 9
10#include "video_core/renderer_vulkan/pipeline_helper.h" 10#include "video_core/renderer_vulkan/pipeline_helper.h"
11#include "video_core/renderer_vulkan/pipeline_statistics.h"
11#include "video_core/renderer_vulkan/vk_buffer_cache.h" 12#include "video_core/renderer_vulkan/vk_buffer_cache.h"
12#include "video_core/renderer_vulkan/vk_compute_pipeline.h" 13#include "video_core/renderer_vulkan/vk_compute_pipeline.h"
13#include "video_core/renderer_vulkan/vk_descriptor_pool.h" 14#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
@@ -26,6 +27,7 @@ using Tegra::Texture::TexturePair;
26ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descriptor_pool, 27ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descriptor_pool,
27 VKUpdateDescriptorQueue& update_descriptor_queue_, 28 VKUpdateDescriptorQueue& update_descriptor_queue_,
28 Common::ThreadWorker* thread_worker, 29 Common::ThreadWorker* thread_worker,
30 PipelineStatistics* pipeline_statistics,
29 VideoCore::ShaderNotify* shader_notify, const Shader::Info& info_, 31 VideoCore::ShaderNotify* shader_notify, const Shader::Info& info_,
30 vk::ShaderModule spv_module_) 32 vk::ShaderModule spv_module_)
31 : device{device_}, update_descriptor_queue{update_descriptor_queue_}, info{info_}, 33 : device{device_}, update_descriptor_queue{update_descriptor_queue_}, info{info_},
@@ -36,7 +38,7 @@ ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descript
36 std::copy_n(info.constant_buffer_used_sizes.begin(), uniform_buffer_sizes.size(), 38 std::copy_n(info.constant_buffer_used_sizes.begin(), uniform_buffer_sizes.size(),
37 uniform_buffer_sizes.begin()); 39 uniform_buffer_sizes.begin());
38 40
39 auto func{[this, &descriptor_pool, shader_notify] { 41 auto func{[this, &descriptor_pool, shader_notify, pipeline_statistics] {
40 DescriptorLayoutBuilder builder{device}; 42 DescriptorLayoutBuilder builder{device};
41 builder.Add(info, VK_SHADER_STAGE_COMPUTE_BIT); 43 builder.Add(info, VK_SHADER_STAGE_COMPUTE_BIT);
42 44
@@ -50,10 +52,14 @@ ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descript
50 .pNext = nullptr, 52 .pNext = nullptr,
51 .requiredSubgroupSize = GuestWarpSize, 53 .requiredSubgroupSize = GuestWarpSize,
52 }; 54 };
55 VkPipelineCreateFlags flags{};
56 if (device.IsKhrPipelineEexecutablePropertiesEnabled()) {
57 flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR;
58 }
53 pipeline = device.GetLogical().CreateComputePipeline({ 59 pipeline = device.GetLogical().CreateComputePipeline({
54 .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, 60 .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
55 .pNext = nullptr, 61 .pNext = nullptr,
56 .flags = 0, 62 .flags = flags,
57 .stage{ 63 .stage{
58 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, 64 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
59 .pNext = device.IsExtSubgroupSizeControlSupported() ? &subgroup_size_ci : nullptr, 65 .pNext = device.IsExtSubgroupSizeControlSupported() ? &subgroup_size_ci : nullptr,
@@ -67,6 +73,9 @@ ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descript
67 .basePipelineHandle = 0, 73 .basePipelineHandle = 0,
68 .basePipelineIndex = 0, 74 .basePipelineIndex = 0,
69 }); 75 });
76 if (pipeline_statistics) {
77 pipeline_statistics->Collect(*pipeline);
78 }
70 std::lock_guard lock{build_mutex}; 79 std::lock_guard lock{build_mutex};
71 is_built = true; 80 is_built = true;
72 build_condvar.notify_one(); 81 build_condvar.notify_one();
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.h b/src/video_core/renderer_vulkan/vk_compute_pipeline.h
index 52fec04d3..8c4b0a301 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pipeline.h
+++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.h
@@ -25,6 +25,7 @@ class ShaderNotify;
25namespace Vulkan { 25namespace Vulkan {
26 26
27class Device; 27class Device;
28class PipelineStatistics;
28class VKScheduler; 29class VKScheduler;
29 30
30class ComputePipeline { 31class ComputePipeline {
@@ -32,6 +33,7 @@ public:
32 explicit ComputePipeline(const Device& device, DescriptorPool& descriptor_pool, 33 explicit ComputePipeline(const Device& device, DescriptorPool& descriptor_pool,
33 VKUpdateDescriptorQueue& update_descriptor_queue, 34 VKUpdateDescriptorQueue& update_descriptor_queue,
34 Common::ThreadWorker* thread_worker, 35 Common::ThreadWorker* thread_worker,
36 PipelineStatistics* pipeline_statistics,
35 VideoCore::ShaderNotify* shader_notify, const Shader::Info& info, 37 VideoCore::ShaderNotify* shader_notify, const Shader::Info& info,
36 vk::ShaderModule spv_module); 38 vk::ShaderModule spv_module);
37 39
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 18482e1d0..7c0f91007 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -11,6 +11,7 @@
11#include "common/bit_field.h" 11#include "common/bit_field.h"
12#include "video_core/renderer_vulkan/maxwell_to_vk.h" 12#include "video_core/renderer_vulkan/maxwell_to_vk.h"
13#include "video_core/renderer_vulkan/pipeline_helper.h" 13#include "video_core/renderer_vulkan/pipeline_helper.h"
14#include "video_core/renderer_vulkan/pipeline_statistics.h"
14#include "video_core/renderer_vulkan/vk_buffer_cache.h" 15#include "video_core/renderer_vulkan/vk_buffer_cache.h"
15#include "video_core/renderer_vulkan/vk_graphics_pipeline.h" 16#include "video_core/renderer_vulkan/vk_graphics_pipeline.h"
16#include "video_core/renderer_vulkan/vk_render_pass_cache.h" 17#include "video_core/renderer_vulkan/vk_render_pass_cache.h"
@@ -217,8 +218,8 @@ GraphicsPipeline::GraphicsPipeline(
217 VKScheduler& scheduler_, BufferCache& buffer_cache_, TextureCache& texture_cache_, 218 VKScheduler& scheduler_, BufferCache& buffer_cache_, TextureCache& texture_cache_,
218 VideoCore::ShaderNotify* shader_notify, const Device& device_, DescriptorPool& descriptor_pool, 219 VideoCore::ShaderNotify* shader_notify, const Device& device_, DescriptorPool& descriptor_pool,
219 VKUpdateDescriptorQueue& update_descriptor_queue_, Common::ThreadWorker* worker_thread, 220 VKUpdateDescriptorQueue& update_descriptor_queue_, Common::ThreadWorker* worker_thread,
220 RenderPassCache& render_pass_cache, const GraphicsPipelineCacheKey& key_, 221 PipelineStatistics* pipeline_statistics, RenderPassCache& render_pass_cache,
221 std::array<vk::ShaderModule, NUM_STAGES> stages, 222 const GraphicsPipelineCacheKey& key_, std::array<vk::ShaderModule, NUM_STAGES> stages,
222 const std::array<const Shader::Info*, NUM_STAGES>& infos) 223 const std::array<const Shader::Info*, NUM_STAGES>& infos)
223 : key{key_}, maxwell3d{maxwell3d_}, gpu_memory{gpu_memory_}, device{device_}, 224 : key{key_}, maxwell3d{maxwell3d_}, gpu_memory{gpu_memory_}, device{device_},
224 texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, scheduler{scheduler_}, 225 texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, scheduler{scheduler_},
@@ -235,7 +236,7 @@ GraphicsPipeline::GraphicsPipeline(
235 enabled_uniform_buffer_masks[stage] = info->constant_buffer_mask; 236 enabled_uniform_buffer_masks[stage] = info->constant_buffer_mask;
236 std::ranges::copy(info->constant_buffer_used_sizes, uniform_buffer_sizes[stage].begin()); 237 std::ranges::copy(info->constant_buffer_used_sizes, uniform_buffer_sizes[stage].begin());
237 } 238 }
238 auto func{[this, shader_notify, &render_pass_cache, &descriptor_pool] { 239 auto func{[this, shader_notify, &render_pass_cache, &descriptor_pool, pipeline_statistics] {
239 DescriptorLayoutBuilder builder{MakeBuilder(device, stage_infos)}; 240 DescriptorLayoutBuilder builder{MakeBuilder(device, stage_infos)};
240 uses_push_descriptor = builder.CanUsePushDescriptor(); 241 uses_push_descriptor = builder.CanUsePushDescriptor();
241 descriptor_set_layout = builder.CreateDescriptorSetLayout(uses_push_descriptor); 242 descriptor_set_layout = builder.CreateDescriptorSetLayout(uses_push_descriptor);
@@ -250,6 +251,9 @@ GraphicsPipeline::GraphicsPipeline(
250 const VkRenderPass render_pass{render_pass_cache.Get(MakeRenderPassKey(key.state))}; 251 const VkRenderPass render_pass{render_pass_cache.Get(MakeRenderPassKey(key.state))};
251 Validate(); 252 Validate();
252 MakePipeline(render_pass); 253 MakePipeline(render_pass);
254 if (pipeline_statistics) {
255 pipeline_statistics->Collect(*pipeline);
256 }
253 257
254 std::lock_guard lock{build_mutex}; 258 std::lock_guard lock{build_mutex};
255 is_built = true; 259 is_built = true;
@@ -782,10 +786,14 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
782 } 786 }
783 */ 787 */
784 } 788 }
789 VkPipelineCreateFlags flags{};
790 if (device.IsKhrPipelineEexecutablePropertiesEnabled()) {
791 flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR;
792 }
785 pipeline = device.GetLogical().CreateGraphicsPipeline({ 793 pipeline = device.GetLogical().CreateGraphicsPipeline({
786 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, 794 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
787 .pNext = nullptr, 795 .pNext = nullptr,
788 .flags = 0, 796 .flags = flags,
789 .stageCount = static_cast<u32>(shader_stages.size()), 797 .stageCount = static_cast<u32>(shader_stages.size()),
790 .pStages = shader_stages.data(), 798 .pStages = shader_stages.data(),
791 .pVertexInputState = &vertex_input_ci, 799 .pVertexInputState = &vertex_input_ci,
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
index 2bd48d697..1c780e944 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
@@ -60,6 +60,7 @@ struct hash<Vulkan::GraphicsPipelineCacheKey> {
60namespace Vulkan { 60namespace Vulkan {
61 61
62class Device; 62class Device;
63class PipelineStatistics;
63class RenderPassCache; 64class RenderPassCache;
64class VKScheduler; 65class VKScheduler;
65class VKUpdateDescriptorQueue; 66class VKUpdateDescriptorQueue;
@@ -73,8 +74,9 @@ public:
73 VKScheduler& scheduler, BufferCache& buffer_cache, TextureCache& texture_cache, 74 VKScheduler& scheduler, BufferCache& buffer_cache, TextureCache& texture_cache,
74 VideoCore::ShaderNotify* shader_notify, const Device& device, 75 VideoCore::ShaderNotify* shader_notify, const Device& device,
75 DescriptorPool& descriptor_pool, VKUpdateDescriptorQueue& update_descriptor_queue, 76 DescriptorPool& descriptor_pool, VKUpdateDescriptorQueue& update_descriptor_queue,
76 Common::ThreadWorker* worker_thread, RenderPassCache& render_pass_cache, 77 Common::ThreadWorker* worker_thread, PipelineStatistics* pipeline_statistics,
77 const GraphicsPipelineCacheKey& key, std::array<vk::ShaderModule, NUM_STAGES> stages, 78 RenderPassCache& render_pass_cache, const GraphicsPipelineCacheKey& key,
79 std::array<vk::ShaderModule, NUM_STAGES> stages,
78 const std::array<const Shader::Info*, NUM_STAGES>& infos); 80 const std::array<const Shader::Info*, NUM_STAGES>& infos);
79 81
80 GraphicsPipeline& operator=(GraphicsPipeline&&) noexcept = delete; 82 GraphicsPipeline& operator=(GraphicsPipeline&&) noexcept = delete;
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 57b163247..a37ca1fdf 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -29,6 +29,7 @@
29#include "video_core/renderer_vulkan/fixed_pipeline_state.h" 29#include "video_core/renderer_vulkan/fixed_pipeline_state.h"
30#include "video_core/renderer_vulkan/maxwell_to_vk.h" 30#include "video_core/renderer_vulkan/maxwell_to_vk.h"
31#include "video_core/renderer_vulkan/pipeline_helper.h" 31#include "video_core/renderer_vulkan/pipeline_helper.h"
32#include "video_core/renderer_vulkan/pipeline_statistics.h"
32#include "video_core/renderer_vulkan/vk_compute_pipeline.h" 33#include "video_core/renderer_vulkan/vk_compute_pipeline.h"
33#include "video_core/renderer_vulkan/vk_descriptor_pool.h" 34#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
34#include "video_core/renderer_vulkan/vk_pipeline_cache.h" 35#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
@@ -389,15 +390,19 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
389 size_t total{}; 390 size_t total{};
390 size_t built{}; 391 size_t built{};
391 bool has_loaded{}; 392 bool has_loaded{};
393 std::unique_ptr<PipelineStatistics> statistics;
392 } state; 394 } state;
393 395
396 if (device.IsKhrPipelineEexecutablePropertiesEnabled()) {
397 state.statistics = std::make_unique<PipelineStatistics>(device);
398 }
394 const auto load_compute{[&](std::ifstream& file, FileEnvironment env) { 399 const auto load_compute{[&](std::ifstream& file, FileEnvironment env) {
395 ComputePipelineCacheKey key; 400 ComputePipelineCacheKey key;
396 file.read(reinterpret_cast<char*>(&key), sizeof(key)); 401 file.read(reinterpret_cast<char*>(&key), sizeof(key));
397 402
398 workers.QueueWork([this, key, env = std::move(env), &state, &callback]() mutable { 403 workers.QueueWork([this, key, env = std::move(env), &state, &callback]() mutable {
399 ShaderPools pools; 404 ShaderPools pools;
400 auto pipeline{CreateComputePipeline(pools, key, env, false)}; 405 auto pipeline{CreateComputePipeline(pools, key, env, state.statistics.get(), false)};
401 std::lock_guard lock{state.mutex}; 406 std::lock_guard lock{state.mutex};
402 if (pipeline) { 407 if (pipeline) {
403 compute_cache.emplace(key, std::move(pipeline)); 408 compute_cache.emplace(key, std::move(pipeline));
@@ -425,7 +430,8 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
425 for (auto& env : envs) { 430 for (auto& env : envs) {
426 env_ptrs.push_back(&env); 431 env_ptrs.push_back(&env);
427 } 432 }
428 auto pipeline{CreateGraphicsPipeline(pools, key, MakeSpan(env_ptrs), false)}; 433 auto pipeline{CreateGraphicsPipeline(pools, key, MakeSpan(env_ptrs),
434 state.statistics.get(), false)};
429 435
430 std::lock_guard lock{state.mutex}; 436 std::lock_guard lock{state.mutex};
431 graphics_cache.emplace(key, std::move(pipeline)); 437 graphics_cache.emplace(key, std::move(pipeline));
@@ -445,6 +451,10 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
445 lock.unlock(); 451 lock.unlock();
446 452
447 workers.WaitForRequests(); 453 workers.WaitForRequests();
454
455 if (state.statistics) {
456 state.statistics->Report();
457 }
448} 458}
449 459
450GraphicsPipeline* PipelineCache::CurrentGraphicsPipelineSlowPath() { 460GraphicsPipeline* PipelineCache::CurrentGraphicsPipelineSlowPath() {
@@ -486,7 +496,8 @@ GraphicsPipeline* PipelineCache::BuiltPipeline(GraphicsPipeline* pipeline) const
486 496
487std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline( 497std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
488 ShaderPools& pools, const GraphicsPipelineCacheKey& key, 498 ShaderPools& pools, const GraphicsPipelineCacheKey& key,
489 std::span<Shader::Environment* const> envs, bool build_in_parallel) try { 499 std::span<Shader::Environment* const> envs, PipelineStatistics* statistics,
500 bool build_in_parallel) try {
490 LOG_INFO(Render_Vulkan, "0x{:016x}", key.Hash()); 501 LOG_INFO(Render_Vulkan, "0x{:016x}", key.Hash());
491 size_t env_index{0}; 502 size_t env_index{0};
492 std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs; 503 std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs;
@@ -540,7 +551,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
540 Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr}; 551 Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr};
541 return std::make_unique<GraphicsPipeline>( 552 return std::make_unique<GraphicsPipeline>(
542 maxwell3d, gpu_memory, scheduler, buffer_cache, texture_cache, &shader_notify, device, 553 maxwell3d, gpu_memory, scheduler, buffer_cache, texture_cache, &shader_notify, device,
543 descriptor_pool, update_descriptor_queue, thread_worker, render_pass_cache, key, 554 descriptor_pool, update_descriptor_queue, thread_worker, statistics, render_pass_cache, key,
544 std::move(modules), infos); 555 std::move(modules), infos);
545 556
546} catch (const Shader::Exception& exception) { 557} catch (const Shader::Exception& exception) {
@@ -553,7 +564,8 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline() {
553 GetGraphicsEnvironments(environments, graphics_key.unique_hashes); 564 GetGraphicsEnvironments(environments, graphics_key.unique_hashes);
554 565
555 main_pools.ReleaseContents(); 566 main_pools.ReleaseContents();
556 auto pipeline{CreateGraphicsPipeline(main_pools, graphics_key, environments.Span(), true)}; 567 auto pipeline{
568 CreateGraphicsPipeline(main_pools, graphics_key, environments.Span(), nullptr, true)};
557 if (!pipeline || pipeline_cache_filename.empty()) { 569 if (!pipeline || pipeline_cache_filename.empty()) {
558 return pipeline; 570 return pipeline;
559 } 571 }
@@ -578,7 +590,7 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
578 env.SetCachedSize(shader->size_bytes); 590 env.SetCachedSize(shader->size_bytes);
579 591
580 main_pools.ReleaseContents(); 592 main_pools.ReleaseContents();
581 auto pipeline{CreateComputePipeline(main_pools, key, env, true)}; 593 auto pipeline{CreateComputePipeline(main_pools, key, env, nullptr, true)};
582 if (!pipeline || pipeline_cache_filename.empty()) { 594 if (!pipeline || pipeline_cache_filename.empty()) {
583 return pipeline; 595 return pipeline;
584 } 596 }
@@ -591,7 +603,7 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
591 603
592std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline( 604std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
593 ShaderPools& pools, const ComputePipelineCacheKey& key, Shader::Environment& env, 605 ShaderPools& pools, const ComputePipelineCacheKey& key, Shader::Environment& env,
594 bool build_in_parallel) try { 606 PipelineStatistics* statistics, bool build_in_parallel) try {
595 LOG_INFO(Render_Vulkan, "0x{:016x}", key.Hash()); 607 LOG_INFO(Render_Vulkan, "0x{:016x}", key.Hash());
596 608
597 Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()}; 609 Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
@@ -605,8 +617,8 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
605 } 617 }
606 Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr}; 618 Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr};
607 return std::make_unique<ComputePipeline>(device, descriptor_pool, update_descriptor_queue, 619 return std::make_unique<ComputePipeline>(device, descriptor_pool, update_descriptor_queue,
608 thread_worker, &shader_notify, program.info, 620 thread_worker, statistics, &shader_notify,
609 std::move(spv_module)); 621 program.info, std::move(spv_module));
610 622
611} catch (const Shader::Exception& exception) { 623} catch (const Shader::Exception& exception) {
612 LOG_ERROR(Render_Vulkan, "{}", exception.what()); 624 LOG_ERROR(Render_Vulkan, "{}", exception.what());
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
index efe5a7ed8..4c135b5dd 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
@@ -80,8 +80,9 @@ struct hash<Vulkan::ComputePipelineCacheKey> {
80namespace Vulkan { 80namespace Vulkan {
81 81
82class ComputePipeline; 82class ComputePipeline;
83class Device;
84class DescriptorPool; 83class DescriptorPool;
84class Device;
85class PipelineStatistics;
85class RasterizerVulkan; 86class RasterizerVulkan;
86class RenderPassCache; 87class RenderPassCache;
87class VKScheduler; 88class VKScheduler;
@@ -128,7 +129,8 @@ private:
128 129
129 std::unique_ptr<GraphicsPipeline> CreateGraphicsPipeline( 130 std::unique_ptr<GraphicsPipeline> CreateGraphicsPipeline(
130 ShaderPools& pools, const GraphicsPipelineCacheKey& key, 131 ShaderPools& pools, const GraphicsPipelineCacheKey& key,
131 std::span<Shader::Environment* const> envs, bool build_in_parallel); 132 std::span<Shader::Environment* const> envs, PipelineStatistics* statistics,
133 bool build_in_parallel);
132 134
133 std::unique_ptr<ComputePipeline> CreateComputePipeline(const ComputePipelineCacheKey& key, 135 std::unique_ptr<ComputePipeline> CreateComputePipeline(const ComputePipelineCacheKey& key,
134 const ShaderInfo* shader); 136 const ShaderInfo* shader);
@@ -136,6 +138,7 @@ private:
136 std::unique_ptr<ComputePipeline> CreateComputePipeline(ShaderPools& pools, 138 std::unique_ptr<ComputePipeline> CreateComputePipeline(ShaderPools& pools,
137 const ComputePipelineCacheKey& key, 139 const ComputePipelineCacheKey& key,
138 Shader::Environment& env, 140 Shader::Environment& env,
141 PipelineStatistics* statistics,
139 bool build_in_parallel); 142 bool build_in_parallel);
140 143
141 const Device& device; 144 const Device& device;
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index c7a07fdd8..3ac18ea54 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -32,7 +32,7 @@
32#include "video_core/renderer_vulkan/vk_texture_cache.h" 32#include "video_core/renderer_vulkan/vk_texture_cache.h"
33#include "video_core/renderer_vulkan/vk_update_descriptor.h" 33#include "video_core/renderer_vulkan/vk_update_descriptor.h"
34#include "video_core/shader_cache.h" 34#include "video_core/shader_cache.h"
35#include "video_core/texture_cache/texture_cache.h" 35#include "video_core/texture_cache/texture_cache_base.h"
36#include "video_core/vulkan_common/vulkan_device.h" 36#include "video_core/vulkan_common/vulkan_device.h"
37#include "video_core/vulkan_common/vulkan_wrapper.h" 37#include "video_core/vulkan_common/vulkan_wrapper.h"
38 38
@@ -61,11 +61,16 @@ struct DrawParams {
61VkViewport GetViewportState(const Device& device, const Maxwell& regs, size_t index) { 61VkViewport GetViewportState(const Device& device, const Maxwell& regs, size_t index) {
62 const auto& src = regs.viewport_transform[index]; 62 const auto& src = regs.viewport_transform[index];
63 const float width = src.scale_x * 2.0f; 63 const float width = src.scale_x * 2.0f;
64 const float height = src.scale_y * 2.0f; 64 float y = src.translate_y - src.scale_y;
65 float height = src.scale_y * 2.0f;
66 if (regs.screen_y_control.y_negate) {
67 y += height;
68 height = -height;
69 }
65 const float reduce_z = regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1.0f : 0.0f; 70 const float reduce_z = regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1.0f : 0.0f;
66 VkViewport viewport{ 71 VkViewport viewport{
67 .x = src.translate_x - src.scale_x, 72 .x = src.translate_x - src.scale_x,
68 .y = src.translate_y - src.scale_y, 73 .y = y,
69 .width = width != 0.0f ? width : 1.0f, 74 .width = width != 0.0f ? width : 1.0f,
70 .height = height != 0.0f ? height : 1.0f, 75 .height = height != 0.0f ? height : 1.0f,
71 .minDepth = src.translate_z - src.scale_z * reduce_z, 76 .minDepth = src.translate_z - src.scale_z * reduce_z,
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 8e029bcb3..8f4df7122 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -19,6 +19,8 @@
19#include "video_core/renderer_vulkan/vk_scheduler.h" 19#include "video_core/renderer_vulkan/vk_scheduler.h"
20#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" 20#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
21#include "video_core/renderer_vulkan/vk_texture_cache.h" 21#include "video_core/renderer_vulkan/vk_texture_cache.h"
22#include "video_core/texture_cache/formatter.h"
23#include "video_core/texture_cache/samples_helper.h"
22#include "video_core/vulkan_common/vulkan_device.h" 24#include "video_core/vulkan_common/vulkan_device.h"
23#include "video_core/vulkan_common/vulkan_memory_allocator.h" 25#include "video_core/vulkan_common/vulkan_memory_allocator.h"
24#include "video_core/vulkan_common/vulkan_wrapper.h" 26#include "video_core/vulkan_common/vulkan_wrapper.h"
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index 0b73d55f8..5fe6b7ba3 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -9,7 +9,7 @@
9 9
10#include "shader_recompiler/shader_info.h" 10#include "shader_recompiler/shader_info.h"
11#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" 11#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
12#include "video_core/texture_cache/texture_cache.h" 12#include "video_core/texture_cache/texture_cache_base.h"
13#include "video_core/vulkan_common/vulkan_memory_allocator.h" 13#include "video_core/vulkan_common/vulkan_memory_allocator.h"
14#include "video_core/vulkan_common/vulkan_wrapper.h" 14#include "video_core/vulkan_common/vulkan_wrapper.h"
15 15
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache_base.cpp b/src/video_core/renderer_vulkan/vk_texture_cache_base.cpp
new file mode 100644
index 000000000..44e688342
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_texture_cache_base.cpp
@@ -0,0 +1,10 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "video_core/renderer_vulkan/vk_texture_cache.h"
6#include "video_core/texture_cache/texture_cache.h"
7
8namespace VideoCommon {
9template class VideoCommon::TextureCache<Vulkan::TextureCacheParams>;
10}
diff --git a/src/video_core/texture_cache/image_view_info.cpp b/src/video_core/texture_cache/image_view_info.cpp
index faf5b151f..6527e14c8 100644
--- a/src/video_core/texture_cache/image_view_info.cpp
+++ b/src/video_core/texture_cache/image_view_info.cpp
@@ -6,7 +6,7 @@
6 6
7#include "common/assert.h" 7#include "common/assert.h"
8#include "video_core/texture_cache/image_view_info.h" 8#include "video_core/texture_cache/image_view_info.h"
9#include "video_core/texture_cache/texture_cache.h" 9#include "video_core/texture_cache/texture_cache_base.h"
10#include "video_core/texture_cache/types.h" 10#include "video_core/texture_cache/types.h"
11#include "video_core/textures/texture.h" 11#include "video_core/textures/texture.h"
12 12
@@ -14,6 +14,8 @@ namespace VideoCommon {
14 14
15namespace { 15namespace {
16 16
17using Tegra::Texture::TextureType;
18
17constexpr u8 RENDER_TARGET_SWIZZLE = std::numeric_limits<u8>::max(); 19constexpr u8 RENDER_TARGET_SWIZZLE = std::numeric_limits<u8>::max();
18 20
19[[nodiscard]] u8 CastSwizzle(SwizzleSource source) { 21[[nodiscard]] u8 CastSwizzle(SwizzleSource source) {
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index f34c9d9ca..a087498ff 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -4,48 +4,11 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <algorithm>
8#include <array>
9#include <bit>
10#include <memory>
11#include <mutex>
12#include <optional>
13#include <span>
14#include <type_traits>
15#include <unordered_map>
16#include <unordered_set>
17#include <utility>
18#include <vector>
19
20#include <boost/container/small_vector.hpp>
21
22#include "common/alignment.h" 7#include "common/alignment.h"
23#include "common/common_types.h"
24#include "common/literals.h"
25#include "common/logging/log.h"
26#include "common/settings.h" 8#include "common/settings.h"
27#include "video_core/compatible_formats.h"
28#include "video_core/delayed_destruction_ring.h"
29#include "video_core/dirty_flags.h" 9#include "video_core/dirty_flags.h"
30#include "video_core/engines/fermi_2d.h"
31#include "video_core/engines/kepler_compute.h"
32#include "video_core/engines/maxwell_3d.h"
33#include "video_core/memory_manager.h"
34#include "video_core/rasterizer_interface.h"
35#include "video_core/surface.h"
36#include "video_core/texture_cache/descriptor_table.h"
37#include "video_core/texture_cache/format_lookup_table.h"
38#include "video_core/texture_cache/formatter.h"
39#include "video_core/texture_cache/image_base.h"
40#include "video_core/texture_cache/image_info.h"
41#include "video_core/texture_cache/image_view_base.h"
42#include "video_core/texture_cache/image_view_info.h"
43#include "video_core/texture_cache/render_targets.h"
44#include "video_core/texture_cache/samples_helper.h" 10#include "video_core/texture_cache/samples_helper.h"
45#include "video_core/texture_cache/slot_vector.h" 11#include "video_core/texture_cache/texture_cache_base.h"
46#include "video_core/texture_cache/types.h"
47#include "video_core/texture_cache/util.h"
48#include "video_core/textures/texture.h"
49 12
50namespace VideoCommon { 13namespace VideoCommon {
51 14
@@ -62,352 +25,6 @@ using VideoCore::Surface::SurfaceType;
62using namespace Common::Literals; 25using namespace Common::Literals;
63 26
64template <class P> 27template <class P>
65class TextureCache {
66 /// Address shift for caching images into a hash table
67 static constexpr u64 PAGE_BITS = 20;
68
69 /// Enables debugging features to the texture cache
70 static constexpr bool ENABLE_VALIDATION = P::ENABLE_VALIDATION;
71 /// Implement blits as copies between framebuffers
72 static constexpr bool FRAMEBUFFER_BLITS = P::FRAMEBUFFER_BLITS;
73 /// True when some copies have to be emulated
74 static constexpr bool HAS_EMULATED_COPIES = P::HAS_EMULATED_COPIES;
75 /// True when the API can provide info about the memory of the device.
76 static constexpr bool HAS_DEVICE_MEMORY_INFO = P::HAS_DEVICE_MEMORY_INFO;
77
78 /// Image view ID for null descriptors
79 static constexpr ImageViewId NULL_IMAGE_VIEW_ID{0};
80 /// Sampler ID for bugged sampler ids
81 static constexpr SamplerId NULL_SAMPLER_ID{0};
82
83 static constexpr u64 DEFAULT_EXPECTED_MEMORY = 1_GiB;
84 static constexpr u64 DEFAULT_CRITICAL_MEMORY = 2_GiB;
85
86 using Runtime = typename P::Runtime;
87 using Image = typename P::Image;
88 using ImageAlloc = typename P::ImageAlloc;
89 using ImageView = typename P::ImageView;
90 using Sampler = typename P::Sampler;
91 using Framebuffer = typename P::Framebuffer;
92
93 struct BlitImages {
94 ImageId dst_id;
95 ImageId src_id;
96 PixelFormat dst_format;
97 PixelFormat src_format;
98 };
99
100 template <typename T>
101 struct IdentityHash {
102 [[nodiscard]] size_t operator()(T value) const noexcept {
103 return static_cast<size_t>(value);
104 }
105 };
106
107public:
108 explicit TextureCache(Runtime&, VideoCore::RasterizerInterface&, Tegra::Engines::Maxwell3D&,
109 Tegra::Engines::KeplerCompute&, Tegra::MemoryManager&);
110
111 /// Notify the cache that a new frame has been queued
112 void TickFrame();
113
114 /// Return a constant reference to the given image view id
115 [[nodiscard]] const ImageView& GetImageView(ImageViewId id) const noexcept;
116
117 /// Return a reference to the given image view id
118 [[nodiscard]] ImageView& GetImageView(ImageViewId id) noexcept;
119
120 /// Mark an image as modified from the GPU
121 void MarkModification(ImageId id) noexcept;
122
123 /// Fill image_view_ids with the graphics images in indices
124 void FillGraphicsImageViews(std::span<const u32> indices,
125 std::span<ImageViewId> image_view_ids);
126
127 /// Fill image_view_ids with the compute images in indices
128 void FillComputeImageViews(std::span<const u32> indices, std::span<ImageViewId> image_view_ids);
129
130 /// Get the sampler from the graphics descriptor table in the specified index
131 Sampler* GetGraphicsSampler(u32 index);
132
133 /// Get the sampler from the compute descriptor table in the specified index
134 Sampler* GetComputeSampler(u32 index);
135
136 /// Refresh the state for graphics image view and sampler descriptors
137 void SynchronizeGraphicsDescriptors();
138
139 /// Refresh the state for compute image view and sampler descriptors
140 void SynchronizeComputeDescriptors();
141
142 /// Update bound render targets and upload memory if necessary
143 /// @param is_clear True when the render targets are being used for clears
144 void UpdateRenderTargets(bool is_clear);
145
146 /// Find a framebuffer with the currently bound render targets
147 /// UpdateRenderTargets should be called before this
148 Framebuffer* GetFramebuffer();
149
150 /// Mark images in a range as modified from the CPU
151 void WriteMemory(VAddr cpu_addr, size_t size);
152
153 /// Download contents of host images to guest memory in a region
154 void DownloadMemory(VAddr cpu_addr, size_t size);
155
156 /// Remove images in a region
157 void UnmapMemory(VAddr cpu_addr, size_t size);
158
159 /// Remove images in a region
160 void UnmapGPUMemory(GPUVAddr gpu_addr, size_t size);
161
162 /// Blit an image with the given parameters
163 void BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
164 const Tegra::Engines::Fermi2D::Surface& src,
165 const Tegra::Engines::Fermi2D::Config& copy);
166
167 /// Invalidate the contents of the color buffer index
168 /// These contents become unspecified, the cache can assume aggressive optimizations.
169 void InvalidateColorBuffer(size_t index);
170
171 /// Invalidate the contents of the depth buffer
172 /// These contents become unspecified, the cache can assume aggressive optimizations.
173 void InvalidateDepthBuffer();
174
175 /// Try to find a cached image view in the given CPU address
176 [[nodiscard]] ImageView* TryFindFramebufferImageView(VAddr cpu_addr);
177
178 /// Return true when there are uncommitted images to be downloaded
179 [[nodiscard]] bool HasUncommittedFlushes() const noexcept;
180
181 /// Return true when the caller should wait for async downloads
182 [[nodiscard]] bool ShouldWaitAsyncFlushes() const noexcept;
183
184 /// Commit asynchronous downloads
185 void CommitAsyncFlushes();
186
187 /// Pop asynchronous downloads
188 void PopAsyncFlushes();
189
190 /// Return true when a CPU region is modified from the GPU
191 [[nodiscard]] bool IsRegionGpuModified(VAddr addr, size_t size);
192
193 std::mutex mutex;
194
195private:
196 /// Iterate over all page indices in a range
197 template <typename Func>
198 static void ForEachCPUPage(VAddr addr, size_t size, Func&& func) {
199 static constexpr bool RETURNS_BOOL = std::is_same_v<std::invoke_result<Func, u64>, bool>;
200 const u64 page_end = (addr + size - 1) >> PAGE_BITS;
201 for (u64 page = addr >> PAGE_BITS; page <= page_end; ++page) {
202 if constexpr (RETURNS_BOOL) {
203 if (func(page)) {
204 break;
205 }
206 } else {
207 func(page);
208 }
209 }
210 }
211
212 template <typename Func>
213 static void ForEachGPUPage(GPUVAddr addr, size_t size, Func&& func) {
214 static constexpr bool RETURNS_BOOL = std::is_same_v<std::invoke_result<Func, u64>, bool>;
215 const u64 page_end = (addr + size - 1) >> PAGE_BITS;
216 for (u64 page = addr >> PAGE_BITS; page <= page_end; ++page) {
217 if constexpr (RETURNS_BOOL) {
218 if (func(page)) {
219 break;
220 }
221 } else {
222 func(page);
223 }
224 }
225 }
226
227 /// Runs the Garbage Collector.
228 void RunGarbageCollector();
229
230 /// Fills image_view_ids in the image views in indices
231 void FillImageViews(DescriptorTable<TICEntry>& table,
232 std::span<ImageViewId> cached_image_view_ids, std::span<const u32> indices,
233 std::span<ImageViewId> image_view_ids);
234
235 /// Find or create an image view in the guest descriptor table
236 ImageViewId VisitImageView(DescriptorTable<TICEntry>& table,
237 std::span<ImageViewId> cached_image_view_ids, u32 index);
238
239 /// Find or create a framebuffer with the given render target parameters
240 FramebufferId GetFramebufferId(const RenderTargets& key);
241
242 /// Refresh the contents (pixel data) of an image
243 void RefreshContents(Image& image, ImageId image_id);
244
245 /// Upload data from guest to an image
246 template <typename StagingBuffer>
247 void UploadImageContents(Image& image, StagingBuffer& staging_buffer);
248
249 /// Find or create an image view from a guest descriptor
250 [[nodiscard]] ImageViewId FindImageView(const TICEntry& config);
251
252 /// Create a new image view from a guest descriptor
253 [[nodiscard]] ImageViewId CreateImageView(const TICEntry& config);
254
255 /// Find or create an image from the given parameters
256 [[nodiscard]] ImageId FindOrInsertImage(const ImageInfo& info, GPUVAddr gpu_addr,
257 RelaxedOptions options = RelaxedOptions{});
258
259 /// Find an image from the given parameters
260 [[nodiscard]] ImageId FindImage(const ImageInfo& info, GPUVAddr gpu_addr,
261 RelaxedOptions options);
262
263 /// Create an image from the given parameters
264 [[nodiscard]] ImageId InsertImage(const ImageInfo& info, GPUVAddr gpu_addr,
265 RelaxedOptions options);
266
267 /// Create a new image and join perfectly matching existing images
268 /// Remove joined images from the cache
269 [[nodiscard]] ImageId JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VAddr cpu_addr);
270
271 /// Return a blit image pair from the given guest blit parameters
272 [[nodiscard]] BlitImages GetBlitImages(const Tegra::Engines::Fermi2D::Surface& dst,
273 const Tegra::Engines::Fermi2D::Surface& src);
274
275 /// Find or create a sampler from a guest descriptor sampler
276 [[nodiscard]] SamplerId FindSampler(const TSCEntry& config);
277
278 /// Find or create an image view for the given color buffer index
279 [[nodiscard]] ImageViewId FindColorBuffer(size_t index, bool is_clear);
280
281 /// Find or create an image view for the depth buffer
282 [[nodiscard]] ImageViewId FindDepthBuffer(bool is_clear);
283
284 /// Find or create a view for a render target with the given image parameters
285 [[nodiscard]] ImageViewId FindRenderTargetView(const ImageInfo& info, GPUVAddr gpu_addr,
286 bool is_clear);
287
288 /// Iterates over all the images in a region calling func
289 template <typename Func>
290 void ForEachImageInRegion(VAddr cpu_addr, size_t size, Func&& func);
291
292 template <typename Func>
293 void ForEachImageInRegionGPU(GPUVAddr gpu_addr, size_t size, Func&& func);
294
295 template <typename Func>
296 void ForEachSparseImageInRegion(GPUVAddr gpu_addr, size_t size, Func&& func);
297
298 /// Iterates over all the images in a region calling func
299 template <typename Func>
300 void ForEachSparseSegment(ImageBase& image, Func&& func);
301
302 /// Find or create an image view in the given image with the passed parameters
303 [[nodiscard]] ImageViewId FindOrEmplaceImageView(ImageId image_id, const ImageViewInfo& info);
304
305 /// Register image in the page table
306 void RegisterImage(ImageId image);
307
308 /// Unregister image from the page table
309 void UnregisterImage(ImageId image);
310
311 /// Track CPU reads and writes for image
312 void TrackImage(ImageBase& image, ImageId image_id);
313
314 /// Stop tracking CPU reads and writes for image
315 void UntrackImage(ImageBase& image, ImageId image_id);
316
317 /// Delete image from the cache
318 void DeleteImage(ImageId image);
319
320 /// Remove image views references from the cache
321 void RemoveImageViewReferences(std::span<const ImageViewId> removed_views);
322
323 /// Remove framebuffers using the given image views from the cache
324 void RemoveFramebuffers(std::span<const ImageViewId> removed_views);
325
326 /// Mark an image as modified from the GPU
327 void MarkModification(ImageBase& image) noexcept;
328
329 /// Synchronize image aliases, copying data if needed
330 void SynchronizeAliases(ImageId image_id);
331
332 /// Prepare an image to be used
333 void PrepareImage(ImageId image_id, bool is_modification, bool invalidate);
334
335 /// Prepare an image view to be used
336 void PrepareImageView(ImageViewId image_view_id, bool is_modification, bool invalidate);
337
338 /// Execute copies from one image to the other, even if they are incompatible
339 void CopyImage(ImageId dst_id, ImageId src_id, std::span<const ImageCopy> copies);
340
341 /// Bind an image view as render target, downloading resources preemtively if needed
342 void BindRenderTarget(ImageViewId* old_id, ImageViewId new_id);
343
344 /// Create a render target from a given image and image view parameters
345 [[nodiscard]] std::pair<FramebufferId, ImageViewId> RenderTargetFromImage(
346 ImageId, const ImageViewInfo& view_info);
347
348 /// Returns true if the current clear parameters clear the whole image of a given image view
349 [[nodiscard]] bool IsFullClear(ImageViewId id);
350
351 Runtime& runtime;
352 VideoCore::RasterizerInterface& rasterizer;
353 Tegra::Engines::Maxwell3D& maxwell3d;
354 Tegra::Engines::KeplerCompute& kepler_compute;
355 Tegra::MemoryManager& gpu_memory;
356
357 DescriptorTable<TICEntry> graphics_image_table{gpu_memory};
358 DescriptorTable<TSCEntry> graphics_sampler_table{gpu_memory};
359 std::vector<SamplerId> graphics_sampler_ids;
360 std::vector<ImageViewId> graphics_image_view_ids;
361
362 DescriptorTable<TICEntry> compute_image_table{gpu_memory};
363 DescriptorTable<TSCEntry> compute_sampler_table{gpu_memory};
364 std::vector<SamplerId> compute_sampler_ids;
365 std::vector<ImageViewId> compute_image_view_ids;
366
367 RenderTargets render_targets;
368
369 std::unordered_map<TICEntry, ImageViewId> image_views;
370 std::unordered_map<TSCEntry, SamplerId> samplers;
371 std::unordered_map<RenderTargets, FramebufferId> framebuffers;
372
373 std::unordered_map<u64, std::vector<ImageMapId>, IdentityHash<u64>> page_table;
374 std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>> gpu_page_table;
375 std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>> sparse_page_table;
376
377 std::unordered_map<ImageId, std::vector<ImageViewId>> sparse_views;
378
379 VAddr virtual_invalid_space{};
380
381 bool has_deleted_images = false;
382 u64 total_used_memory = 0;
383 u64 minimum_memory;
384 u64 expected_memory;
385 u64 critical_memory;
386
387 SlotVector<Image> slot_images;
388 SlotVector<ImageMapView> slot_map_views;
389 SlotVector<ImageView> slot_image_views;
390 SlotVector<ImageAlloc> slot_image_allocs;
391 SlotVector<Sampler> slot_samplers;
392 SlotVector<Framebuffer> slot_framebuffers;
393
394 // TODO: This data structure is not optimal and it should be reworked
395 std::vector<ImageId> uncommitted_downloads;
396 std::queue<std::vector<ImageId>> committed_downloads;
397
398 static constexpr size_t TICKS_TO_DESTROY = 6;
399 DelayedDestructionRing<Image, TICKS_TO_DESTROY> sentenced_images;
400 DelayedDestructionRing<ImageView, TICKS_TO_DESTROY> sentenced_image_view;
401 DelayedDestructionRing<Framebuffer, TICKS_TO_DESTROY> sentenced_framebuffers;
402
403 std::unordered_map<GPUVAddr, ImageAllocId> image_allocs_table;
404
405 u64 modification_tick = 0;
406 u64 frame_tick = 0;
407 typename SlotVector<Image>::Iterator deletion_iterator;
408};
409
410template <class P>
411TextureCache<P>::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface& rasterizer_, 28TextureCache<P>::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface& rasterizer_,
412 Tegra::Engines::Maxwell3D& maxwell3d_, 29 Tegra::Engines::Maxwell3D& maxwell3d_,
413 Tegra::Engines::KeplerCompute& kepler_compute_, 30 Tegra::Engines::KeplerCompute& kepler_compute_,
@@ -821,40 +438,6 @@ void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
821} 438}
822 439
823template <class P> 440template <class P>
824void TextureCache<P>::InvalidateColorBuffer(size_t index) {
825 ImageViewId& color_buffer_id = render_targets.color_buffer_ids[index];
826 color_buffer_id = FindColorBuffer(index, false);
827 if (!color_buffer_id) {
828 LOG_ERROR(HW_GPU, "Invalidating invalid color buffer in index={}", index);
829 return;
830 }
831 // When invalidating a color buffer, the old contents are no longer relevant
832 ImageView& color_buffer = slot_image_views[color_buffer_id];
833 Image& image = slot_images[color_buffer.image_id];
834 image.flags &= ~ImageFlagBits::CpuModified;
835 image.flags &= ~ImageFlagBits::GpuModified;
836
837 runtime.InvalidateColorBuffer(color_buffer, index);
838}
839
840template <class P>
841void TextureCache<P>::InvalidateDepthBuffer() {
842 ImageViewId& depth_buffer_id = render_targets.depth_buffer_id;
843 depth_buffer_id = FindDepthBuffer(false);
844 if (!depth_buffer_id) {
845 LOG_ERROR(HW_GPU, "Invalidating invalid depth buffer");
846 return;
847 }
848 // When invalidating the depth buffer, the old contents are no longer relevant
849 ImageBase& image = slot_images[slot_image_views[depth_buffer_id].image_id];
850 image.flags &= ~ImageFlagBits::CpuModified;
851 image.flags &= ~ImageFlagBits::GpuModified;
852
853 ImageView& depth_buffer = slot_image_views[depth_buffer_id];
854 runtime.InvalidateDepthBuffer(depth_buffer);
855}
856
857template <class P>
858typename P::ImageView* TextureCache<P>::TryFindFramebufferImageView(VAddr cpu_addr) { 441typename P::ImageView* TextureCache<P>::TryFindFramebufferImageView(VAddr cpu_addr) {
859 // TODO: Properly implement this 442 // TODO: Properly implement this
860 const auto it = page_table.find(cpu_addr >> PAGE_BITS); 443 const auto it = page_table.find(cpu_addr >> PAGE_BITS);
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
new file mode 100644
index 000000000..e4ae351cb
--- /dev/null
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -0,0 +1,385 @@
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 <array>
8#include <mutex>
9#include <span>
10#include <type_traits>
11#include <unordered_map>
12#include <unordered_set>
13#include <vector>
14
15#include "common/common_types.h"
16#include "common/literals.h"
17#include "video_core/compatible_formats.h"
18#include "video_core/delayed_destruction_ring.h"
19#include "video_core/engines/fermi_2d.h"
20#include "video_core/engines/kepler_compute.h"
21#include "video_core/engines/maxwell_3d.h"
22#include "video_core/memory_manager.h"
23#include "video_core/rasterizer_interface.h"
24#include "video_core/surface.h"
25#include "video_core/texture_cache/descriptor_table.h"
26#include "video_core/texture_cache/image_base.h"
27#include "video_core/texture_cache/image_info.h"
28#include "video_core/texture_cache/image_view_info.h"
29#include "video_core/texture_cache/render_targets.h"
30#include "video_core/texture_cache/slot_vector.h"
31#include "video_core/texture_cache/types.h"
32#include "video_core/texture_cache/util.h"
33#include "video_core/textures/texture.h"
34
35namespace VideoCommon {
36
37using Tegra::Texture::SwizzleSource;
38using Tegra::Texture::TICEntry;
39using Tegra::Texture::TSCEntry;
40using VideoCore::Surface::GetFormatType;
41using VideoCore::Surface::IsCopyCompatible;
42using VideoCore::Surface::PixelFormat;
43using VideoCore::Surface::PixelFormatFromDepthFormat;
44using VideoCore::Surface::PixelFormatFromRenderTargetFormat;
45using namespace Common::Literals;
46
47template <class P>
48class TextureCache {
49 /// Address shift for caching images into a hash table
50 static constexpr u64 PAGE_BITS = 20;
51
52 /// Enables debugging features to the texture cache
53 static constexpr bool ENABLE_VALIDATION = P::ENABLE_VALIDATION;
54 /// Implement blits as copies between framebuffers
55 static constexpr bool FRAMEBUFFER_BLITS = P::FRAMEBUFFER_BLITS;
56 /// True when some copies have to be emulated
57 static constexpr bool HAS_EMULATED_COPIES = P::HAS_EMULATED_COPIES;
58 /// True when the API can provide info about the memory of the device.
59 static constexpr bool HAS_DEVICE_MEMORY_INFO = P::HAS_DEVICE_MEMORY_INFO;
60
61 /// Image view ID for null descriptors
62 static constexpr ImageViewId NULL_IMAGE_VIEW_ID{0};
63 /// Sampler ID for bugged sampler ids
64 static constexpr SamplerId NULL_SAMPLER_ID{0};
65
66 static constexpr u64 DEFAULT_EXPECTED_MEMORY = 1_GiB;
67 static constexpr u64 DEFAULT_CRITICAL_MEMORY = 2_GiB;
68
69 using Runtime = typename P::Runtime;
70 using Image = typename P::Image;
71 using ImageAlloc = typename P::ImageAlloc;
72 using ImageView = typename P::ImageView;
73 using Sampler = typename P::Sampler;
74 using Framebuffer = typename P::Framebuffer;
75
76 struct BlitImages {
77 ImageId dst_id;
78 ImageId src_id;
79 PixelFormat dst_format;
80 PixelFormat src_format;
81 };
82
83 template <typename T>
84 struct IdentityHash {
85 [[nodiscard]] size_t operator()(T value) const noexcept {
86 return static_cast<size_t>(value);
87 }
88 };
89
90public:
91 explicit TextureCache(Runtime&, VideoCore::RasterizerInterface&, Tegra::Engines::Maxwell3D&,
92 Tegra::Engines::KeplerCompute&, Tegra::MemoryManager&);
93
94 /// Notify the cache that a new frame has been queued
95 void TickFrame();
96
97 /// Return a constant reference to the given image view id
98 [[nodiscard]] const ImageView& GetImageView(ImageViewId id) const noexcept;
99
100 /// Return a reference to the given image view id
101 [[nodiscard]] ImageView& GetImageView(ImageViewId id) noexcept;
102
103 /// Mark an image as modified from the GPU
104 void MarkModification(ImageId id) noexcept;
105
106 /// Fill image_view_ids with the graphics images in indices
107 void FillGraphicsImageViews(std::span<const u32> indices,
108 std::span<ImageViewId> image_view_ids);
109
110 /// Fill image_view_ids with the compute images in indices
111 void FillComputeImageViews(std::span<const u32> indices, std::span<ImageViewId> image_view_ids);
112
113 /// Get the sampler from the graphics descriptor table in the specified index
114 Sampler* GetGraphicsSampler(u32 index);
115
116 /// Get the sampler from the compute descriptor table in the specified index
117 Sampler* GetComputeSampler(u32 index);
118
119 /// Refresh the state for graphics image view and sampler descriptors
120 void SynchronizeGraphicsDescriptors();
121
122 /// Refresh the state for compute image view and sampler descriptors
123 void SynchronizeComputeDescriptors();
124
125 /// Update bound render targets and upload memory if necessary
126 /// @param is_clear True when the render targets are being used for clears
127 void UpdateRenderTargets(bool is_clear);
128
129 /// Find a framebuffer with the currently bound render targets
130 /// UpdateRenderTargets should be called before this
131 Framebuffer* GetFramebuffer();
132
133 /// Mark images in a range as modified from the CPU
134 void WriteMemory(VAddr cpu_addr, size_t size);
135
136 /// Download contents of host images to guest memory in a region
137 void DownloadMemory(VAddr cpu_addr, size_t size);
138
139 /// Remove images in a region
140 void UnmapMemory(VAddr cpu_addr, size_t size);
141
142 /// Remove images in a region
143 void UnmapGPUMemory(GPUVAddr gpu_addr, size_t size);
144
145 /// Blit an image with the given parameters
146 void BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
147 const Tegra::Engines::Fermi2D::Surface& src,
148 const Tegra::Engines::Fermi2D::Config& copy);
149
150 /// Try to find a cached image view in the given CPU address
151 [[nodiscard]] ImageView* TryFindFramebufferImageView(VAddr cpu_addr);
152
153 /// Return true when there are uncommitted images to be downloaded
154 [[nodiscard]] bool HasUncommittedFlushes() const noexcept;
155
156 /// Return true when the caller should wait for async downloads
157 [[nodiscard]] bool ShouldWaitAsyncFlushes() const noexcept;
158
159 /// Commit asynchronous downloads
160 void CommitAsyncFlushes();
161
162 /// Pop asynchronous downloads
163 void PopAsyncFlushes();
164
165 /// Return true when a CPU region is modified from the GPU
166 [[nodiscard]] bool IsRegionGpuModified(VAddr addr, size_t size);
167
168 std::mutex mutex;
169
170private:
171 /// Iterate over all page indices in a range
172 template <typename Func>
173 static void ForEachCPUPage(VAddr addr, size_t size, Func&& func) {
174 static constexpr bool RETURNS_BOOL = std::is_same_v<std::invoke_result<Func, u64>, bool>;
175 const u64 page_end = (addr + size - 1) >> PAGE_BITS;
176 for (u64 page = addr >> PAGE_BITS; page <= page_end; ++page) {
177 if constexpr (RETURNS_BOOL) {
178 if (func(page)) {
179 break;
180 }
181 } else {
182 func(page);
183 }
184 }
185 }
186
187 template <typename Func>
188 static void ForEachGPUPage(GPUVAddr addr, size_t size, Func&& func) {
189 static constexpr bool RETURNS_BOOL = std::is_same_v<std::invoke_result<Func, u64>, bool>;
190 const u64 page_end = (addr + size - 1) >> PAGE_BITS;
191 for (u64 page = addr >> PAGE_BITS; page <= page_end; ++page) {
192 if constexpr (RETURNS_BOOL) {
193 if (func(page)) {
194 break;
195 }
196 } else {
197 func(page);
198 }
199 }
200 }
201
202 /// Runs the Garbage Collector.
203 void RunGarbageCollector();
204
205 /// Fills image_view_ids in the image views in indices
206 void FillImageViews(DescriptorTable<TICEntry>& table,
207 std::span<ImageViewId> cached_image_view_ids, std::span<const u32> indices,
208 std::span<ImageViewId> image_view_ids);
209
210 /// Find or create an image view in the guest descriptor table
211 ImageViewId VisitImageView(DescriptorTable<TICEntry>& table,
212 std::span<ImageViewId> cached_image_view_ids, u32 index);
213
214 /// Find or create a framebuffer with the given render target parameters
215 FramebufferId GetFramebufferId(const RenderTargets& key);
216
217 /// Refresh the contents (pixel data) of an image
218 void RefreshContents(Image& image, ImageId image_id);
219
220 /// Upload data from guest to an image
221 template <typename StagingBuffer>
222 void UploadImageContents(Image& image, StagingBuffer& staging_buffer);
223
224 /// Find or create an image view from a guest descriptor
225 [[nodiscard]] ImageViewId FindImageView(const TICEntry& config);
226
227 /// Create a new image view from a guest descriptor
228 [[nodiscard]] ImageViewId CreateImageView(const TICEntry& config);
229
230 /// Find or create an image from the given parameters
231 [[nodiscard]] ImageId FindOrInsertImage(const ImageInfo& info, GPUVAddr gpu_addr,
232 RelaxedOptions options = RelaxedOptions{});
233
234 /// Find an image from the given parameters
235 [[nodiscard]] ImageId FindImage(const ImageInfo& info, GPUVAddr gpu_addr,
236 RelaxedOptions options);
237
238 /// Create an image from the given parameters
239 [[nodiscard]] ImageId InsertImage(const ImageInfo& info, GPUVAddr gpu_addr,
240 RelaxedOptions options);
241
242 /// Create a new image and join perfectly matching existing images
243 /// Remove joined images from the cache
244 [[nodiscard]] ImageId JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VAddr cpu_addr);
245
246 /// Return a blit image pair from the given guest blit parameters
247 [[nodiscard]] BlitImages GetBlitImages(const Tegra::Engines::Fermi2D::Surface& dst,
248 const Tegra::Engines::Fermi2D::Surface& src);
249
250 /// Find or create a sampler from a guest descriptor sampler
251 [[nodiscard]] SamplerId FindSampler(const TSCEntry& config);
252
253 /// Find or create an image view for the given color buffer index
254 [[nodiscard]] ImageViewId FindColorBuffer(size_t index, bool is_clear);
255
256 /// Find or create an image view for the depth buffer
257 [[nodiscard]] ImageViewId FindDepthBuffer(bool is_clear);
258
259 /// Find or create a view for a render target with the given image parameters
260 [[nodiscard]] ImageViewId FindRenderTargetView(const ImageInfo& info, GPUVAddr gpu_addr,
261 bool is_clear);
262
263 /// Iterates over all the images in a region calling func
264 template <typename Func>
265 void ForEachImageInRegion(VAddr cpu_addr, size_t size, Func&& func);
266
267 template <typename Func>
268 void ForEachImageInRegionGPU(GPUVAddr gpu_addr, size_t size, Func&& func);
269
270 template <typename Func>
271 void ForEachSparseImageInRegion(GPUVAddr gpu_addr, size_t size, Func&& func);
272
273 /// Iterates over all the images in a region calling func
274 template <typename Func>
275 void ForEachSparseSegment(ImageBase& image, Func&& func);
276
277 /// Find or create an image view in the given image with the passed parameters
278 [[nodiscard]] ImageViewId FindOrEmplaceImageView(ImageId image_id, const ImageViewInfo& info);
279
280 /// Register image in the page table
281 void RegisterImage(ImageId image);
282
283 /// Unregister image from the page table
284 void UnregisterImage(ImageId image);
285
286 /// Track CPU reads and writes for image
287 void TrackImage(ImageBase& image, ImageId image_id);
288
289 /// Stop tracking CPU reads and writes for image
290 void UntrackImage(ImageBase& image, ImageId image_id);
291
292 /// Delete image from the cache
293 void DeleteImage(ImageId image);
294
295 /// Remove image views references from the cache
296 void RemoveImageViewReferences(std::span<const ImageViewId> removed_views);
297
298 /// Remove framebuffers using the given image views from the cache
299 void RemoveFramebuffers(std::span<const ImageViewId> removed_views);
300
301 /// Mark an image as modified from the GPU
302 void MarkModification(ImageBase& image) noexcept;
303
304 /// Synchronize image aliases, copying data if needed
305 void SynchronizeAliases(ImageId image_id);
306
307 /// Prepare an image to be used
308 void PrepareImage(ImageId image_id, bool is_modification, bool invalidate);
309
310 /// Prepare an image view to be used
311 void PrepareImageView(ImageViewId image_view_id, bool is_modification, bool invalidate);
312
313 /// Execute copies from one image to the other, even if they are incompatible
314 void CopyImage(ImageId dst_id, ImageId src_id, std::span<const ImageCopy> copies);
315
316 /// Bind an image view as render target, downloading resources preemtively if needed
317 void BindRenderTarget(ImageViewId* old_id, ImageViewId new_id);
318
319 /// Create a render target from a given image and image view parameters
320 [[nodiscard]] std::pair<FramebufferId, ImageViewId> RenderTargetFromImage(
321 ImageId, const ImageViewInfo& view_info);
322
323 /// Returns true if the current clear parameters clear the whole image of a given image view
324 [[nodiscard]] bool IsFullClear(ImageViewId id);
325
326 Runtime& runtime;
327 VideoCore::RasterizerInterface& rasterizer;
328 Tegra::Engines::Maxwell3D& maxwell3d;
329 Tegra::Engines::KeplerCompute& kepler_compute;
330 Tegra::MemoryManager& gpu_memory;
331
332 DescriptorTable<TICEntry> graphics_image_table{gpu_memory};
333 DescriptorTable<TSCEntry> graphics_sampler_table{gpu_memory};
334 std::vector<SamplerId> graphics_sampler_ids;
335 std::vector<ImageViewId> graphics_image_view_ids;
336
337 DescriptorTable<TICEntry> compute_image_table{gpu_memory};
338 DescriptorTable<TSCEntry> compute_sampler_table{gpu_memory};
339 std::vector<SamplerId> compute_sampler_ids;
340 std::vector<ImageViewId> compute_image_view_ids;
341
342 RenderTargets render_targets;
343
344 std::unordered_map<TICEntry, ImageViewId> image_views;
345 std::unordered_map<TSCEntry, SamplerId> samplers;
346 std::unordered_map<RenderTargets, FramebufferId> framebuffers;
347
348 std::unordered_map<u64, std::vector<ImageMapId>, IdentityHash<u64>> page_table;
349 std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>> gpu_page_table;
350 std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>> sparse_page_table;
351
352 std::unordered_map<ImageId, std::vector<ImageViewId>> sparse_views;
353
354 VAddr virtual_invalid_space{};
355
356 bool has_deleted_images = false;
357 u64 total_used_memory = 0;
358 u64 minimum_memory;
359 u64 expected_memory;
360 u64 critical_memory;
361
362 SlotVector<Image> slot_images;
363 SlotVector<ImageMapView> slot_map_views;
364 SlotVector<ImageView> slot_image_views;
365 SlotVector<ImageAlloc> slot_image_allocs;
366 SlotVector<Sampler> slot_samplers;
367 SlotVector<Framebuffer> slot_framebuffers;
368
369 // TODO: This data structure is not optimal and it should be reworked
370 std::vector<ImageId> uncommitted_downloads;
371 std::queue<std::vector<ImageId>> committed_downloads;
372
373 static constexpr size_t TICKS_TO_DESTROY = 6;
374 DelayedDestructionRing<Image, TICKS_TO_DESTROY> sentenced_images;
375 DelayedDestructionRing<ImageView, TICKS_TO_DESTROY> sentenced_image_view;
376 DelayedDestructionRing<Framebuffer, TICKS_TO_DESTROY> sentenced_framebuffers;
377
378 std::unordered_map<GPUVAddr, ImageAllocId> image_allocs_table;
379
380 u64 modification_tick = 0;
381 u64 frame_tick = 0;
382 typename SlotVector<Image>::Iterator deletion_iterator;
383};
384
385} // namespace VideoCommon
diff --git a/src/video_core/textures/astc.cpp b/src/video_core/textures/astc.cpp
index 3ab500760..25161df1f 100644
--- a/src/video_core/textures/astc.cpp
+++ b/src/video_core/textures/astc.cpp
@@ -151,6 +151,76 @@ private:
151 const IntType& m_Bits; 151 const IntType& m_Bits;
152}; 152};
153 153
154enum class IntegerEncoding { JustBits, Quint, Trit };
155
156struct IntegerEncodedValue {
157 constexpr IntegerEncodedValue() = default;
158
159 constexpr IntegerEncodedValue(IntegerEncoding encoding_, u32 num_bits_)
160 : encoding{encoding_}, num_bits{num_bits_} {}
161
162 constexpr bool MatchesEncoding(const IntegerEncodedValue& other) const {
163 return encoding == other.encoding && num_bits == other.num_bits;
164 }
165
166 // Returns the number of bits required to encode num_vals values.
167 u32 GetBitLength(u32 num_vals) const {
168 u32 total_bits = num_bits * num_vals;
169 if (encoding == IntegerEncoding::Trit) {
170 total_bits += (num_vals * 8 + 4) / 5;
171 } else if (encoding == IntegerEncoding::Quint) {
172 total_bits += (num_vals * 7 + 2) / 3;
173 }
174 return total_bits;
175 }
176
177 IntegerEncoding encoding{};
178 u32 num_bits = 0;
179 u32 bit_value = 0;
180 union {
181 u32 quint_value = 0;
182 u32 trit_value;
183 };
184};
185
186// Returns a new instance of this struct that corresponds to the
187// can take no more than mav_value values
188static constexpr IntegerEncodedValue CreateEncoding(u32 mav_value) {
189 while (mav_value > 0) {
190 u32 check = mav_value + 1;
191
192 // Is mav_value a power of two?
193 if (!(check & (check - 1))) {
194 return IntegerEncodedValue(IntegerEncoding::JustBits, std::popcount(mav_value));
195 }
196
197 // Is mav_value of the type 3*2^n - 1?
198 if ((check % 3 == 0) && !((check / 3) & ((check / 3) - 1))) {
199 return IntegerEncodedValue(IntegerEncoding::Trit, std::popcount(check / 3 - 1));
200 }
201
202 // Is mav_value of the type 5*2^n - 1?
203 if ((check % 5 == 0) && !((check / 5) & ((check / 5) - 1))) {
204 return IntegerEncodedValue(IntegerEncoding::Quint, std::popcount(check / 5 - 1));
205 }
206
207 // Apparently it can't be represented with a bounded integer sequence...
208 // just iterate.
209 mav_value--;
210 }
211 return IntegerEncodedValue(IntegerEncoding::JustBits, 0);
212}
213
214static constexpr std::array<IntegerEncodedValue, 256> MakeEncodedValues() {
215 std::array<IntegerEncodedValue, 256> encodings{};
216 for (std::size_t i = 0; i < encodings.size(); ++i) {
217 encodings[i] = CreateEncoding(static_cast<u32>(i));
218 }
219 return encodings;
220}
221
222static constexpr std::array<IntegerEncodedValue, 256> ASTC_ENCODINGS_VALUES = MakeEncodedValues();
223
154namespace Tegra::Texture::ASTC { 224namespace Tegra::Texture::ASTC {
155using IntegerEncodedVector = boost::container::static_vector< 225using IntegerEncodedVector = boost::container::static_vector<
156 IntegerEncodedValue, 256, 226 IntegerEncodedValue, 256,
@@ -521,35 +591,41 @@ static TexelWeightParams DecodeBlockInfo(InputBitStream& strm) {
521 return params; 591 return params;
522} 592}
523 593
524static void FillVoidExtentLDR(InputBitStream& strm, std::span<u32> outBuf, u32 blockWidth, 594// Replicates low num_bits such that [(to_bit - 1):(to_bit - 1 - from_bit)]
525 u32 blockHeight) { 595// is the same as [(num_bits - 1):0] and repeats all the way down.
526 // Don't actually care about the void extent, just read the bits... 596template <typename IntType>
527 for (s32 i = 0; i < 4; ++i) { 597static constexpr IntType Replicate(IntType val, u32 num_bits, u32 to_bit) {
528 strm.ReadBits<13>(); 598 if (num_bits == 0 || to_bit == 0) {
599 return 0;
529 } 600 }
530 601 const IntType v = val & static_cast<IntType>((1 << num_bits) - 1);
531 // Decode the RGBA components and renormalize them to the range [0, 255] 602 IntType res = v;
532 u16 r = static_cast<u16>(strm.ReadBits<16>()); 603 u32 reslen = num_bits;
533 u16 g = static_cast<u16>(strm.ReadBits<16>()); 604 while (reslen < to_bit) {
534 u16 b = static_cast<u16>(strm.ReadBits<16>()); 605 u32 comp = 0;
535 u16 a = static_cast<u16>(strm.ReadBits<16>()); 606 if (num_bits > to_bit - reslen) {
536 607 u32 newshift = to_bit - reslen;
537 u32 rgba = (r >> 8) | (g & 0xFF00) | (static_cast<u32>(b) & 0xFF00) << 8 | 608 comp = num_bits - newshift;
538 (static_cast<u32>(a) & 0xFF00) << 16; 609 num_bits = newshift;
539
540 for (u32 j = 0; j < blockHeight; j++) {
541 for (u32 i = 0; i < blockWidth; i++) {
542 outBuf[j * blockWidth + i] = rgba;
543 } 610 }
611 res = static_cast<IntType>(res << num_bits);
612 res = static_cast<IntType>(res | (v >> comp));
613 reslen += num_bits;
544 } 614 }
615 return res;
545} 616}
546 617
547static void FillError(std::span<u32> outBuf, u32 blockWidth, u32 blockHeight) { 618static constexpr std::size_t NumReplicateEntries(u32 num_bits) {
548 for (u32 j = 0; j < blockHeight; j++) { 619 return std::size_t(1) << num_bits;
549 for (u32 i = 0; i < blockWidth; i++) { 620}
550 outBuf[j * blockWidth + i] = 0xFFFF00FF; 621
551 } 622template <typename IntType, u32 num_bits, u32 to_bit>
623static constexpr auto MakeReplicateTable() {
624 std::array<IntType, NumReplicateEntries(num_bits)> table{};
625 for (IntType value = 0; value < static_cast<IntType>(std::size(table)); ++value) {
626 table[value] = Replicate(value, num_bits, to_bit);
552 } 627 }
628 return table;
553} 629}
554 630
555static constexpr auto REPLICATE_BYTE_TO_16_TABLE = MakeReplicateTable<u32, 8, 16>(); 631static constexpr auto REPLICATE_BYTE_TO_16_TABLE = MakeReplicateTable<u32, 8, 16>();
@@ -572,6 +648,9 @@ static constexpr auto REPLICATE_2_BIT_TO_8_TABLE = MakeReplicateTable<u32, 2, 8>
572static constexpr auto REPLICATE_3_BIT_TO_8_TABLE = MakeReplicateTable<u32, 3, 8>(); 648static constexpr auto REPLICATE_3_BIT_TO_8_TABLE = MakeReplicateTable<u32, 3, 8>();
573static constexpr auto REPLICATE_4_BIT_TO_8_TABLE = MakeReplicateTable<u32, 4, 8>(); 649static constexpr auto REPLICATE_4_BIT_TO_8_TABLE = MakeReplicateTable<u32, 4, 8>();
574static constexpr auto REPLICATE_5_BIT_TO_8_TABLE = MakeReplicateTable<u32, 5, 8>(); 650static constexpr auto REPLICATE_5_BIT_TO_8_TABLE = MakeReplicateTable<u32, 5, 8>();
651static constexpr auto REPLICATE_6_BIT_TO_8_TABLE = MakeReplicateTable<u32, 6, 8>();
652static constexpr auto REPLICATE_7_BIT_TO_8_TABLE = MakeReplicateTable<u32, 7, 8>();
653static constexpr auto REPLICATE_8_BIT_TO_8_TABLE = MakeReplicateTable<u32, 8, 8>();
575/// Use a precompiled table with the most common usages, if it's not in the expected range, fallback 654/// Use a precompiled table with the most common usages, if it's not in the expected range, fallback
576/// to the runtime implementation 655/// to the runtime implementation
577static constexpr u32 FastReplicateTo8(u32 value, u32 num_bits) { 656static constexpr u32 FastReplicateTo8(u32 value, u32 num_bits) {
@@ -1316,6 +1395,37 @@ static void ComputeEndpoints(Pixel& ep1, Pixel& ep2, const u32*& colorValues,
1316#undef READ_INT_VALUES 1395#undef READ_INT_VALUES
1317} 1396}
1318 1397
1398static void FillVoidExtentLDR(InputBitStream& strm, std::span<u32> outBuf, u32 blockWidth,
1399 u32 blockHeight) {
1400 // Don't actually care about the void extent, just read the bits...
1401 for (s32 i = 0; i < 4; ++i) {
1402 strm.ReadBits<13>();
1403 }
1404
1405 // Decode the RGBA components and renormalize them to the range [0, 255]
1406 u16 r = static_cast<u16>(strm.ReadBits<16>());
1407 u16 g = static_cast<u16>(strm.ReadBits<16>());
1408 u16 b = static_cast<u16>(strm.ReadBits<16>());
1409 u16 a = static_cast<u16>(strm.ReadBits<16>());
1410
1411 u32 rgba = (r >> 8) | (g & 0xFF00) | (static_cast<u32>(b) & 0xFF00) << 8 |
1412 (static_cast<u32>(a) & 0xFF00) << 16;
1413
1414 for (u32 j = 0; j < blockHeight; j++) {
1415 for (u32 i = 0; i < blockWidth; i++) {
1416 outBuf[j * blockWidth + i] = rgba;
1417 }
1418 }
1419}
1420
1421static void FillError(std::span<u32> outBuf, u32 blockWidth, u32 blockHeight) {
1422 for (u32 j = 0; j < blockHeight; j++) {
1423 for (u32 i = 0; i < blockWidth; i++) {
1424 outBuf[j * blockWidth + i] = 0xFFFF00FF;
1425 }
1426 }
1427}
1428
1319static void DecompressBlock(std::span<const u8, 16> inBuf, const u32 blockWidth, 1429static void DecompressBlock(std::span<const u8, 16> inBuf, const u32 blockWidth,
1320 const u32 blockHeight, std::span<u32, 12 * 12> outBuf) { 1430 const u32 blockHeight, std::span<u32, 12 * 12> outBuf) {
1321 InputBitStream strm(inBuf); 1431 InputBitStream strm(inBuf);
diff --git a/src/video_core/textures/astc.h b/src/video_core/textures/astc.h
index 0229ae122..14d2beec0 100644
--- a/src/video_core/textures/astc.h
+++ b/src/video_core/textures/astc.h
@@ -9,117 +9,6 @@
9 9
10namespace Tegra::Texture::ASTC { 10namespace Tegra::Texture::ASTC {
11 11
12enum class IntegerEncoding { JustBits, Quint, Trit };
13
14struct IntegerEncodedValue {
15 constexpr IntegerEncodedValue() = default;
16
17 constexpr IntegerEncodedValue(IntegerEncoding encoding_, u32 num_bits_)
18 : encoding{encoding_}, num_bits{num_bits_} {}
19
20 constexpr bool MatchesEncoding(const IntegerEncodedValue& other) const {
21 return encoding == other.encoding && num_bits == other.num_bits;
22 }
23
24 // Returns the number of bits required to encode num_vals values.
25 u32 GetBitLength(u32 num_vals) const {
26 u32 total_bits = num_bits * num_vals;
27 if (encoding == IntegerEncoding::Trit) {
28 total_bits += (num_vals * 8 + 4) / 5;
29 } else if (encoding == IntegerEncoding::Quint) {
30 total_bits += (num_vals * 7 + 2) / 3;
31 }
32 return total_bits;
33 }
34
35 IntegerEncoding encoding{};
36 u32 num_bits = 0;
37 u32 bit_value = 0;
38 union {
39 u32 quint_value = 0;
40 u32 trit_value;
41 };
42};
43
44// Returns a new instance of this struct that corresponds to the
45// can take no more than mav_value values
46constexpr IntegerEncodedValue CreateEncoding(u32 mav_value) {
47 while (mav_value > 0) {
48 u32 check = mav_value + 1;
49
50 // Is mav_value a power of two?
51 if (!(check & (check - 1))) {
52 return IntegerEncodedValue(IntegerEncoding::JustBits, std::popcount(mav_value));
53 }
54
55 // Is mav_value of the type 3*2^n - 1?
56 if ((check % 3 == 0) && !((check / 3) & ((check / 3) - 1))) {
57 return IntegerEncodedValue(IntegerEncoding::Trit, std::popcount(check / 3 - 1));
58 }
59
60 // Is mav_value of the type 5*2^n - 1?
61 if ((check % 5 == 0) && !((check / 5) & ((check / 5) - 1))) {
62 return IntegerEncodedValue(IntegerEncoding::Quint, std::popcount(check / 5 - 1));
63 }
64
65 // Apparently it can't be represented with a bounded integer sequence...
66 // just iterate.
67 mav_value--;
68 }
69 return IntegerEncodedValue(IntegerEncoding::JustBits, 0);
70}
71
72constexpr std::array<IntegerEncodedValue, 256> MakeEncodedValues() {
73 std::array<IntegerEncodedValue, 256> encodings{};
74 for (std::size_t i = 0; i < encodings.size(); ++i) {
75 encodings[i] = CreateEncoding(static_cast<u32>(i));
76 }
77 return encodings;
78}
79
80constexpr std::array<IntegerEncodedValue, 256> ASTC_ENCODINGS_VALUES = MakeEncodedValues();
81
82// Replicates low num_bits such that [(to_bit - 1):(to_bit - 1 - from_bit)]
83// is the same as [(num_bits - 1):0] and repeats all the way down.
84template <typename IntType>
85constexpr IntType Replicate(IntType val, u32 num_bits, u32 to_bit) {
86 if (num_bits == 0 || to_bit == 0) {
87 return 0;
88 }
89 const IntType v = val & static_cast<IntType>((1 << num_bits) - 1);
90 IntType res = v;
91 u32 reslen = num_bits;
92 while (reslen < to_bit) {
93 u32 comp = 0;
94 if (num_bits > to_bit - reslen) {
95 u32 newshift = to_bit - reslen;
96 comp = num_bits - newshift;
97 num_bits = newshift;
98 }
99 res = static_cast<IntType>(res << num_bits);
100 res = static_cast<IntType>(res | (v >> comp));
101 reslen += num_bits;
102 }
103 return res;
104}
105
106constexpr std::size_t NumReplicateEntries(u32 num_bits) {
107 return std::size_t(1) << num_bits;
108}
109
110template <typename IntType, u32 num_bits, u32 to_bit>
111constexpr auto MakeReplicateTable() {
112 std::array<IntType, NumReplicateEntries(num_bits)> table{};
113 for (IntType value = 0; value < static_cast<IntType>(std::size(table)); ++value) {
114 table[value] = Replicate(value, num_bits, to_bit);
115 }
116 return table;
117}
118
119constexpr auto REPLICATE_6_BIT_TO_8_TABLE = MakeReplicateTable<u32, 6, 8>();
120constexpr auto REPLICATE_7_BIT_TO_8_TABLE = MakeReplicateTable<u32, 7, 8>();
121constexpr auto REPLICATE_8_BIT_TO_8_TABLE = MakeReplicateTable<u32, 8, 8>();
122
123void Decompress(std::span<const uint8_t> data, uint32_t width, uint32_t height, uint32_t depth, 12void Decompress(std::span<const uint8_t> data, uint32_t width, uint32_t height, uint32_t depth,
124 uint32_t block_width, uint32_t block_height, std::span<uint8_t> output); 13 uint32_t block_width, uint32_t block_height, std::span<uint8_t> output);
125 14
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index f1f523ad1..c32ae956a 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -18,9 +18,9 @@
18 18
19namespace Tegra::Texture { 19namespace Tegra::Texture {
20namespace { 20namespace {
21template <bool TO_LINEAR> 21template <bool TO_LINEAR, u32 BYTES_PER_PIXEL>
22void Swizzle(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, u32 width, 22void SwizzleImpl(std::span<u8> output, std::span<const u8> input, u32 width, u32 height, u32 depth,
23 u32 height, u32 depth, u32 block_height, u32 block_depth, u32 stride_alignment) { 23 u32 block_height, u32 block_depth, u32 stride_alignment) {
24 // The origin of the transformation can be configured here, leave it as zero as the current API 24 // The origin of the transformation can be configured here, leave it as zero as the current API
25 // doesn't expose it. 25 // doesn't expose it.
26 static constexpr u32 origin_x = 0; 26 static constexpr u32 origin_x = 0;
@@ -28,9 +28,9 @@ void Swizzle(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixe
28 static constexpr u32 origin_z = 0; 28 static constexpr u32 origin_z = 0;
29 29
30 // We can configure here a custom pitch 30 // We can configure here a custom pitch
31 // As it's not exposed 'width * bpp' will be the expected pitch. 31 // As it's not exposed 'width * BYTES_PER_PIXEL' will be the expected pitch.
32 const u32 pitch = width * bytes_per_pixel; 32 const u32 pitch = width * BYTES_PER_PIXEL;
33 const u32 stride = Common::AlignUpLog2(width, stride_alignment) * bytes_per_pixel; 33 const u32 stride = Common::AlignUpLog2(width, stride_alignment) * BYTES_PER_PIXEL;
34 34
35 const u32 gobs_in_x = Common::DivCeilLog2(stride, GOB_SIZE_X_SHIFT); 35 const u32 gobs_in_x = Common::DivCeilLog2(stride, GOB_SIZE_X_SHIFT);
36 const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth); 36 const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth);
@@ -54,14 +54,14 @@ void Swizzle(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixe
54 ((block_y & block_height_mask) << GOB_SIZE_SHIFT); 54 ((block_y & block_height_mask) << GOB_SIZE_SHIFT);
55 55
56 for (u32 column = 0; column < width; ++column) { 56 for (u32 column = 0; column < width; ++column) {
57 const u32 x = (column + origin_x) * bytes_per_pixel; 57 const u32 x = (column + origin_x) * BYTES_PER_PIXEL;
58 const u32 offset_x = (x >> GOB_SIZE_X_SHIFT) << x_shift; 58 const u32 offset_x = (x >> GOB_SIZE_X_SHIFT) << x_shift;
59 59
60 const u32 base_swizzled_offset = offset_z + offset_y + offset_x; 60 const u32 base_swizzled_offset = offset_z + offset_y + offset_x;
61 const u32 swizzled_offset = base_swizzled_offset + table[x % GOB_SIZE_X]; 61 const u32 swizzled_offset = base_swizzled_offset + table[x % GOB_SIZE_X];
62 62
63 const u32 unswizzled_offset = 63 const u32 unswizzled_offset =
64 slice * pitch * height + line * pitch + column * bytes_per_pixel; 64 slice * pitch * height + line * pitch + column * BYTES_PER_PIXEL;
65 65
66 if (const auto offset = (TO_LINEAR ? unswizzled_offset : swizzled_offset); 66 if (const auto offset = (TO_LINEAR ? unswizzled_offset : swizzled_offset);
67 offset >= input.size()) { 67 offset >= input.size()) {
@@ -73,11 +73,45 @@ void Swizzle(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixe
73 73
74 u8* const dst = &output[TO_LINEAR ? swizzled_offset : unswizzled_offset]; 74 u8* const dst = &output[TO_LINEAR ? swizzled_offset : unswizzled_offset];
75 const u8* const src = &input[TO_LINEAR ? unswizzled_offset : swizzled_offset]; 75 const u8* const src = &input[TO_LINEAR ? unswizzled_offset : swizzled_offset];
76 std::memcpy(dst, src, bytes_per_pixel); 76
77 std::memcpy(dst, src, BYTES_PER_PIXEL);
77 } 78 }
78 } 79 }
79 } 80 }
80} 81}
82
83template <bool TO_LINEAR>
84void Swizzle(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, u32 width,
85 u32 height, u32 depth, u32 block_height, u32 block_depth, u32 stride_alignment) {
86 switch (bytes_per_pixel) {
87 case 1:
88 return SwizzleImpl<TO_LINEAR, 1>(output, input, width, height, depth, block_height,
89 block_depth, stride_alignment);
90 case 2:
91 return SwizzleImpl<TO_LINEAR, 2>(output, input, width, height, depth, block_height,
92 block_depth, stride_alignment);
93 case 3:
94 return SwizzleImpl<TO_LINEAR, 3>(output, input, width, height, depth, block_height,
95 block_depth, stride_alignment);
96 case 4:
97 return SwizzleImpl<TO_LINEAR, 4>(output, input, width, height, depth, block_height,
98 block_depth, stride_alignment);
99 case 6:
100 return SwizzleImpl<TO_LINEAR, 6>(output, input, width, height, depth, block_height,
101 block_depth, stride_alignment);
102 case 8:
103 return SwizzleImpl<TO_LINEAR, 8>(output, input, width, height, depth, block_height,
104 block_depth, stride_alignment);
105 case 12:
106 return SwizzleImpl<TO_LINEAR, 12>(output, input, width, height, depth, block_height,
107 block_depth, stride_alignment);
108 case 16:
109 return SwizzleImpl<TO_LINEAR, 16>(output, input, width, height, depth, block_height,
110 block_depth, stride_alignment);
111 default:
112 UNREACHABLE_MSG("Invalid bytes_per_pixel={}", bytes_per_pixel);
113 }
114}
81} // Anonymous namespace 115} // Anonymous namespace
82 116
83void UnswizzleTexture(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, 117void UnswizzleTexture(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel,
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 44afdc1cd..8e56a89e1 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -526,6 +526,17 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
526 SetNext(next, workgroup_layout); 526 SetNext(next, workgroup_layout);
527 } 527 }
528 528
529 VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR executable_properties;
530 if (khr_pipeline_executable_properties) {
531 LOG_INFO(Render_Vulkan, "Enabling shader feedback, expect slower shader build times");
532 executable_properties = {
533 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_EXECUTABLE_PROPERTIES_FEATURES_KHR,
534 .pNext = nullptr,
535 .pipelineExecutableInfo = VK_TRUE,
536 };
537 SetNext(next, executable_properties);
538 }
539
529 if (!ext_depth_range_unrestricted) { 540 if (!ext_depth_range_unrestricted) {
530 LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted"); 541 LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted");
531 } 542 }
@@ -824,6 +835,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
824 835
825 bool has_khr_shader_float16_int8{}; 836 bool has_khr_shader_float16_int8{};
826 bool has_khr_workgroup_memory_explicit_layout{}; 837 bool has_khr_workgroup_memory_explicit_layout{};
838 bool has_khr_pipeline_executable_properties{};
827 bool has_ext_subgroup_size_control{}; 839 bool has_ext_subgroup_size_control{};
828 bool has_ext_transform_feedback{}; 840 bool has_ext_transform_feedback{};
829 bool has_ext_custom_border_color{}; 841 bool has_ext_custom_border_color{};
@@ -878,6 +890,10 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
878 test(nv_device_diagnostics_config, VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, 890 test(nv_device_diagnostics_config, VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME,
879 true); 891 true);
880 } 892 }
893 if (Settings::values.renderer_shader_feedback) {
894 test(has_khr_pipeline_executable_properties,
895 VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME, false);
896 }
881 } 897 }
882 VkPhysicalDeviceFeatures2KHR features{}; 898 VkPhysicalDeviceFeatures2KHR features{};
883 features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR; 899 features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR;
@@ -1033,6 +1049,19 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
1033 khr_workgroup_memory_explicit_layout = true; 1049 khr_workgroup_memory_explicit_layout = true;
1034 } 1050 }
1035 } 1051 }
1052 if (has_khr_pipeline_executable_properties) {
1053 VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR executable_properties;
1054 executable_properties.sType =
1055 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_EXECUTABLE_PROPERTIES_FEATURES_KHR;
1056 executable_properties.pNext = nullptr;
1057 features.pNext = &executable_properties;
1058 physical.GetFeatures2KHR(features);
1059
1060 if (executable_properties.pipelineExecutableInfo) {
1061 extensions.push_back(VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME);
1062 khr_pipeline_executable_properties = true;
1063 }
1064 }
1036 if (khr_push_descriptor) { 1065 if (khr_push_descriptor) {
1037 VkPhysicalDevicePushDescriptorPropertiesKHR push_descriptor; 1066 VkPhysicalDevicePushDescriptorPropertiesKHR push_descriptor;
1038 push_descriptor.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR; 1067 push_descriptor.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR;
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index df394e384..c19f40746 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -214,6 +214,11 @@ public:
214 return khr_push_descriptor; 214 return khr_push_descriptor;
215 } 215 }
216 216
217 /// Returns true if VK_KHR_pipeline_executable_properties is enabled.
218 bool IsKhrPipelineEexecutablePropertiesEnabled() const {
219 return khr_pipeline_executable_properties;
220 }
221
217 /// Returns true if the device supports VK_KHR_workgroup_memory_explicit_layout. 222 /// Returns true if the device supports VK_KHR_workgroup_memory_explicit_layout.
218 bool IsKhrWorkgroupMemoryExplicitLayoutSupported() const { 223 bool IsKhrWorkgroupMemoryExplicitLayoutSupported() const {
219 return khr_workgroup_memory_explicit_layout; 224 return khr_workgroup_memory_explicit_layout;
@@ -378,6 +383,7 @@ private:
378 bool khr_spirv_1_4{}; ///< Support for VK_KHR_spirv_1_4. 383 bool khr_spirv_1_4{}; ///< Support for VK_KHR_spirv_1_4.
379 bool khr_workgroup_memory_explicit_layout{}; ///< Support for explicit workgroup layouts. 384 bool khr_workgroup_memory_explicit_layout{}; ///< Support for explicit workgroup layouts.
380 bool khr_push_descriptor{}; ///< Support for VK_KHR_push_descritor. 385 bool khr_push_descriptor{}; ///< Support for VK_KHR_push_descritor.
386 bool khr_pipeline_executable_properties{}; ///< Support for executable properties.
381 bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8. 387 bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8.
382 bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax. 388 bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax.
383 bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted. 389 bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted.
diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
index aa173d19e..300a61205 100644
--- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
+++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
@@ -228,7 +228,9 @@ void MemoryCommit::Release() {
228 228
229MemoryAllocator::MemoryAllocator(const Device& device_, bool export_allocations_) 229MemoryAllocator::MemoryAllocator(const Device& device_, bool export_allocations_)
230 : device{device_}, properties{device_.GetPhysical().GetMemoryProperties()}, 230 : device{device_}, properties{device_.GetPhysical().GetMemoryProperties()},
231 export_allocations{export_allocations_} {} 231 export_allocations{export_allocations_},
232 buffer_image_granularity{
233 device_.GetPhysical().GetProperties().limits.bufferImageGranularity} {}
232 234
233MemoryAllocator::~MemoryAllocator() = default; 235MemoryAllocator::~MemoryAllocator() = default;
234 236
@@ -258,7 +260,9 @@ MemoryCommit MemoryAllocator::Commit(const vk::Buffer& buffer, MemoryUsage usage
258} 260}
259 261
260MemoryCommit MemoryAllocator::Commit(const vk::Image& image, MemoryUsage usage) { 262MemoryCommit MemoryAllocator::Commit(const vk::Image& image, MemoryUsage usage) {
261 auto commit = Commit(device.GetLogical().GetImageMemoryRequirements(*image), usage); 263 VkMemoryRequirements requirements = device.GetLogical().GetImageMemoryRequirements(*image);
264 requirements.size = Common::AlignUp(requirements.size, buffer_image_granularity);
265 auto commit = Commit(requirements, usage);
262 image.BindMemory(commit.Memory(), commit.Offset()); 266 image.BindMemory(commit.Memory(), commit.Offset());
263 return commit; 267 return commit;
264} 268}
diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.h b/src/video_core/vulkan_common/vulkan_memory_allocator.h
index b61e931e0..86e8ed119 100644
--- a/src/video_core/vulkan_common/vulkan_memory_allocator.h
+++ b/src/video_core/vulkan_common/vulkan_memory_allocator.h
@@ -123,6 +123,8 @@ private:
123 const VkPhysicalDeviceMemoryProperties properties; ///< Physical device properties. 123 const VkPhysicalDeviceMemoryProperties properties; ///< Physical device properties.
124 const bool export_allocations; ///< True when memory allocations have to be exported. 124 const bool export_allocations; ///< True when memory allocations have to be exported.
125 std::vector<std::unique_ptr<MemoryAllocation>> allocations; ///< Current allocations. 125 std::vector<std::unique_ptr<MemoryAllocation>> allocations; ///< Current allocations.
126 VkDeviceSize buffer_image_granularity; // The granularity for adjacent offsets between buffers
127 // and optimal images
126}; 128};
127 129
128/// Returns true when a memory usage is guaranteed to be host visible. 130/// Returns true when a memory usage is guaranteed to be host visible.
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp
index 70898004a..a9faa4807 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.cpp
+++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp
@@ -181,6 +181,8 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
181 X(vkGetMemoryWin32HandleKHR); 181 X(vkGetMemoryWin32HandleKHR);
182#endif 182#endif
183 X(vkGetQueryPoolResults); 183 X(vkGetQueryPoolResults);
184 X(vkGetPipelineExecutablePropertiesKHR);
185 X(vkGetPipelineExecutableStatisticsKHR);
184 X(vkGetSemaphoreCounterValueKHR); 186 X(vkGetSemaphoreCounterValueKHR);
185 X(vkMapMemory); 187 X(vkMapMemory);
186 X(vkQueueSubmit); 188 X(vkQueueSubmit);
@@ -809,6 +811,42 @@ VkMemoryRequirements Device::GetImageMemoryRequirements(VkImage image) const noe
809 return requirements; 811 return requirements;
810} 812}
811 813
814std::vector<VkPipelineExecutablePropertiesKHR> Device::GetPipelineExecutablePropertiesKHR(
815 VkPipeline pipeline) const {
816 const VkPipelineInfoKHR info{
817 .sType = VK_STRUCTURE_TYPE_PIPELINE_INFO_KHR,
818 .pNext = nullptr,
819 .pipeline = pipeline,
820 };
821 u32 num{};
822 dld->vkGetPipelineExecutablePropertiesKHR(handle, &info, &num, nullptr);
823 std::vector<VkPipelineExecutablePropertiesKHR> properties(num);
824 for (auto& property : properties) {
825 property.sType = VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_PROPERTIES_KHR;
826 }
827 Check(dld->vkGetPipelineExecutablePropertiesKHR(handle, &info, &num, properties.data()));
828 return properties;
829}
830
831std::vector<VkPipelineExecutableStatisticKHR> Device::GetPipelineExecutableStatisticsKHR(
832 VkPipeline pipeline, u32 executable_index) const {
833 const VkPipelineExecutableInfoKHR executable_info{
834 .sType = VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_INFO_KHR,
835 .pNext = nullptr,
836 .pipeline = pipeline,
837 .executableIndex = executable_index,
838 };
839 u32 num{};
840 dld->vkGetPipelineExecutableStatisticsKHR(handle, &executable_info, &num, nullptr);
841 std::vector<VkPipelineExecutableStatisticKHR> statistics(num);
842 for (auto& statistic : statistics) {
843 statistic.sType = VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_STATISTIC_KHR;
844 }
845 Check(dld->vkGetPipelineExecutableStatisticsKHR(handle, &executable_info, &num,
846 statistics.data()));
847 return statistics;
848}
849
812void Device::UpdateDescriptorSets(Span<VkWriteDescriptorSet> writes, 850void Device::UpdateDescriptorSets(Span<VkWriteDescriptorSet> writes,
813 Span<VkCopyDescriptorSet> copies) const noexcept { 851 Span<VkCopyDescriptorSet> copies) const noexcept {
814 dld->vkUpdateDescriptorSets(handle, writes.size(), writes.data(), copies.size(), copies.data()); 852 dld->vkUpdateDescriptorSets(handle, writes.size(), writes.data(), copies.size(), copies.data());
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h
index d76bb4324..b7ae01c6c 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.h
+++ b/src/video_core/vulkan_common/vulkan_wrapper.h
@@ -295,6 +295,8 @@ struct DeviceDispatch : InstanceDispatch {
295#ifdef _WIN32 295#ifdef _WIN32
296 PFN_vkGetMemoryWin32HandleKHR vkGetMemoryWin32HandleKHR{}; 296 PFN_vkGetMemoryWin32HandleKHR vkGetMemoryWin32HandleKHR{};
297#endif 297#endif
298 PFN_vkGetPipelineExecutablePropertiesKHR vkGetPipelineExecutablePropertiesKHR{};
299 PFN_vkGetPipelineExecutableStatisticsKHR vkGetPipelineExecutableStatisticsKHR{};
298 PFN_vkGetQueryPoolResults vkGetQueryPoolResults{}; 300 PFN_vkGetQueryPoolResults vkGetQueryPoolResults{};
299 PFN_vkGetSemaphoreCounterValueKHR vkGetSemaphoreCounterValueKHR{}; 301 PFN_vkGetSemaphoreCounterValueKHR vkGetSemaphoreCounterValueKHR{};
300 PFN_vkMapMemory vkMapMemory{}; 302 PFN_vkMapMemory vkMapMemory{};
@@ -879,6 +881,12 @@ public:
879 881
880 VkMemoryRequirements GetImageMemoryRequirements(VkImage image) const noexcept; 882 VkMemoryRequirements GetImageMemoryRequirements(VkImage image) const noexcept;
881 883
884 std::vector<VkPipelineExecutablePropertiesKHR> GetPipelineExecutablePropertiesKHR(
885 VkPipeline pipeline) const;
886
887 std::vector<VkPipelineExecutableStatisticKHR> GetPipelineExecutableStatisticsKHR(
888 VkPipeline pipeline, u32 executable_index) const;
889
882 void UpdateDescriptorSets(Span<VkWriteDescriptorSet> writes, 890 void UpdateDescriptorSets(Span<VkWriteDescriptorSet> writes,
883 Span<VkCopyDescriptorSet> copies) const noexcept; 891 Span<VkCopyDescriptorSet> copies) const noexcept;
884 892
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 25b658b2a..2e0ade815 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -632,9 +632,9 @@ void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_p
632 screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32); 632 screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32);
633 renderer.RequestScreenshot( 633 renderer.RequestScreenshot(
634 screenshot_image.bits(), 634 screenshot_image.bits(),
635 [=, this] { 635 [=, this](bool invert_y) {
636 const std::string std_screenshot_path = screenshot_path.toStdString(); 636 const std::string std_screenshot_path = screenshot_path.toStdString();
637 if (screenshot_image.mirrored(false, true).save(screenshot_path)) { 637 if (screenshot_image.mirrored(false, invert_y).save(screenshot_path)) {
638 LOG_INFO(Frontend, "Screenshot saved to \"{}\"", std_screenshot_path); 638 LOG_INFO(Frontend, "Screenshot saved to \"{}\"", std_screenshot_path);
639 } else { 639 } else {
640 LOG_ERROR(Frontend, "Failed to save screenshot to \"{}\"", std_screenshot_path); 640 LOG_ERROR(Frontend, "Failed to save screenshot to \"{}\"", std_screenshot_path);
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index ecd5dfac1..380379eb4 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -825,6 +825,7 @@ void Config::ReadRendererValues() {
825 if (global) { 825 if (global) {
826 ReadBasicSetting(Settings::values.fps_cap); 826 ReadBasicSetting(Settings::values.fps_cap);
827 ReadBasicSetting(Settings::values.renderer_debug); 827 ReadBasicSetting(Settings::values.renderer_debug);
828 ReadBasicSetting(Settings::values.renderer_shader_feedback);
828 ReadBasicSetting(Settings::values.enable_nsight_aftermath); 829 ReadBasicSetting(Settings::values.enable_nsight_aftermath);
829 ReadBasicSetting(Settings::values.disable_shader_loop_safety_checks); 830 ReadBasicSetting(Settings::values.disable_shader_loop_safety_checks);
830 } 831 }
@@ -872,10 +873,6 @@ void Config::ReadShortcutValues() {
872void Config::ReadSystemValues() { 873void Config::ReadSystemValues() {
873 qt_config->beginGroup(QStringLiteral("System")); 874 qt_config->beginGroup(QStringLiteral("System"));
874 875
875 ReadBasicSetting(Settings::values.current_user);
876 Settings::values.current_user = std::clamp<int>(Settings::values.current_user.GetValue(), 0,
877 Service::Account::MAX_USERS - 1);
878
879 ReadGlobalSetting(Settings::values.language_index); 876 ReadGlobalSetting(Settings::values.language_index);
880 877
881 ReadGlobalSetting(Settings::values.region_index); 878 ReadGlobalSetting(Settings::values.region_index);
@@ -896,6 +893,10 @@ void Config::ReadSystemValues() {
896 } 893 }
897 894
898 if (global) { 895 if (global) {
896 ReadBasicSetting(Settings::values.current_user);
897 Settings::values.current_user = std::clamp<int>(Settings::values.current_user.GetValue(), 0,
898 Service::Account::MAX_USERS - 1);
899
899 const auto custom_rtc_enabled = 900 const auto custom_rtc_enabled =
900 ReadSetting(QStringLiteral("custom_rtc_enabled"), false).toBool(); 901 ReadSetting(QStringLiteral("custom_rtc_enabled"), false).toBool();
901 if (custom_rtc_enabled) { 902 if (custom_rtc_enabled) {
@@ -945,7 +946,8 @@ void Config::ReadUIGamelistValues() {
945 qt_config->beginGroup(QStringLiteral("UIGameList")); 946 qt_config->beginGroup(QStringLiteral("UIGameList"));
946 947
947 ReadBasicSetting(UISettings::values.show_add_ons); 948 ReadBasicSetting(UISettings::values.show_add_ons);
948 ReadBasicSetting(UISettings::values.icon_size); 949 ReadBasicSetting(UISettings::values.game_icon_size);
950 ReadBasicSetting(UISettings::values.folder_icon_size);
949 ReadBasicSetting(UISettings::values.row_1_text_id); 951 ReadBasicSetting(UISettings::values.row_1_text_id);
950 ReadBasicSetting(UISettings::values.row_2_text_id); 952 ReadBasicSetting(UISettings::values.row_2_text_id);
951 ReadBasicSetting(UISettings::values.cache_game_list); 953 ReadBasicSetting(UISettings::values.cache_game_list);
@@ -1363,6 +1365,7 @@ void Config::SaveRendererValues() {
1363 if (global) { 1365 if (global) {
1364 WriteBasicSetting(Settings::values.fps_cap); 1366 WriteBasicSetting(Settings::values.fps_cap);
1365 WriteBasicSetting(Settings::values.renderer_debug); 1367 WriteBasicSetting(Settings::values.renderer_debug);
1368 WriteBasicSetting(Settings::values.renderer_shader_feedback);
1366 WriteBasicSetting(Settings::values.enable_nsight_aftermath); 1369 WriteBasicSetting(Settings::values.enable_nsight_aftermath);
1367 WriteBasicSetting(Settings::values.disable_shader_loop_safety_checks); 1370 WriteBasicSetting(Settings::values.disable_shader_loop_safety_checks);
1368 } 1371 }
@@ -1403,7 +1406,6 @@ void Config::SaveShortcutValues() {
1403void Config::SaveSystemValues() { 1406void Config::SaveSystemValues() {
1404 qt_config->beginGroup(QStringLiteral("System")); 1407 qt_config->beginGroup(QStringLiteral("System"));
1405 1408
1406 WriteBasicSetting(Settings::values.current_user);
1407 WriteGlobalSetting(Settings::values.language_index); 1409 WriteGlobalSetting(Settings::values.language_index);
1408 WriteGlobalSetting(Settings::values.region_index); 1410 WriteGlobalSetting(Settings::values.region_index);
1409 WriteGlobalSetting(Settings::values.time_zone_index); 1411 WriteGlobalSetting(Settings::values.time_zone_index);
@@ -1415,6 +1417,8 @@ void Config::SaveSystemValues() {
1415 0, Settings::values.rng_seed.UsingGlobal()); 1417 0, Settings::values.rng_seed.UsingGlobal());
1416 1418
1417 if (global) { 1419 if (global) {
1420 WriteBasicSetting(Settings::values.current_user);
1421
1418 WriteSetting(QStringLiteral("custom_rtc_enabled"), Settings::values.custom_rtc.has_value(), 1422 WriteSetting(QStringLiteral("custom_rtc_enabled"), Settings::values.custom_rtc.has_value(),
1419 false); 1423 false);
1420 WriteSetting(QStringLiteral("custom_rtc"), 1424 WriteSetting(QStringLiteral("custom_rtc"),
@@ -1461,7 +1465,8 @@ void Config::SaveUIGamelistValues() {
1461 qt_config->beginGroup(QStringLiteral("UIGameList")); 1465 qt_config->beginGroup(QStringLiteral("UIGameList"));
1462 1466
1463 WriteBasicSetting(UISettings::values.show_add_ons); 1467 WriteBasicSetting(UISettings::values.show_add_ons);
1464 WriteBasicSetting(UISettings::values.icon_size); 1468 WriteBasicSetting(UISettings::values.game_icon_size);
1469 WriteBasicSetting(UISettings::values.folder_icon_size);
1465 WriteBasicSetting(UISettings::values.row_1_text_id); 1470 WriteBasicSetting(UISettings::values.row_1_text_id);
1466 WriteBasicSetting(UISettings::values.row_2_text_id); 1471 WriteBasicSetting(UISettings::values.row_2_text_id);
1467 WriteBasicSetting(UISettings::values.cache_game_list); 1472 WriteBasicSetting(UISettings::values.cache_game_list);
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index f7e29dbd7..c0b240c1e 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -43,6 +43,8 @@ void ConfigureDebug::SetConfiguration() {
43 ui->use_auto_stub->setChecked(Settings::values.use_auto_stub.GetValue()); 43 ui->use_auto_stub->setChecked(Settings::values.use_auto_stub.GetValue());
44 ui->enable_graphics_debugging->setEnabled(runtime_lock); 44 ui->enable_graphics_debugging->setEnabled(runtime_lock);
45 ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug.GetValue()); 45 ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug.GetValue());
46 ui->enable_shader_feedback->setEnabled(runtime_lock);
47 ui->enable_shader_feedback->setChecked(Settings::values.renderer_shader_feedback.GetValue());
46 ui->enable_cpu_debugging->setEnabled(runtime_lock); 48 ui->enable_cpu_debugging->setEnabled(runtime_lock);
47 ui->enable_cpu_debugging->setChecked(Settings::values.cpu_debug_mode.GetValue()); 49 ui->enable_cpu_debugging->setChecked(Settings::values.cpu_debug_mode.GetValue());
48 ui->enable_nsight_aftermath->setEnabled(runtime_lock); 50 ui->enable_nsight_aftermath->setEnabled(runtime_lock);
@@ -65,6 +67,7 @@ void ConfigureDebug::ApplyConfiguration() {
65 Settings::values.use_debug_asserts = ui->use_debug_asserts->isChecked(); 67 Settings::values.use_debug_asserts = ui->use_debug_asserts->isChecked();
66 Settings::values.use_auto_stub = ui->use_auto_stub->isChecked(); 68 Settings::values.use_auto_stub = ui->use_auto_stub->isChecked();
67 Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked(); 69 Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked();
70 Settings::values.renderer_shader_feedback = ui->enable_shader_feedback->isChecked();
68 Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked(); 71 Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked();
69 Settings::values.enable_nsight_aftermath = ui->enable_nsight_aftermath->isChecked(); 72 Settings::values.enable_nsight_aftermath = ui->enable_nsight_aftermath->isChecked();
70 Settings::values.disable_shader_loop_safety_checks = 73 Settings::values.disable_shader_loop_safety_checks =
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index c8baf2921..3fe9ff7de 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -111,8 +111,8 @@
111 <property name="title"> 111 <property name="title">
112 <string>Graphics</string> 112 <string>Graphics</string>
113 </property> 113 </property>
114 <layout class="QVBoxLayout" name="verticalLayout_6"> 114 <layout class="QGridLayout" name="gridLayout_3">
115 <item> 115 <item row="0" column="0">
116 <widget class="QCheckBox" name="enable_graphics_debugging"> 116 <widget class="QCheckBox" name="enable_graphics_debugging">
117 <property name="enabled"> 117 <property name="enabled">
118 <bool>true</bool> 118 <bool>true</bool>
@@ -125,7 +125,7 @@
125 </property> 125 </property>
126 </widget> 126 </widget>
127 </item> 127 </item>
128 <item> 128 <item row="2" column="0">
129 <widget class="QCheckBox" name="enable_nsight_aftermath"> 129 <widget class="QCheckBox" name="enable_nsight_aftermath">
130 <property name="toolTip"> 130 <property name="toolTip">
131 <string>When checked, it enables Nsight Aftermath crash dumps</string> 131 <string>When checked, it enables Nsight Aftermath crash dumps</string>
@@ -135,7 +135,7 @@
135 </property> 135 </property>
136 </widget> 136 </widget>
137 </item> 137 </item>
138 <item> 138 <item row="0" column="1">
139 <widget class="QCheckBox" name="disable_macro_jit"> 139 <widget class="QCheckBox" name="disable_macro_jit">
140 <property name="enabled"> 140 <property name="enabled">
141 <bool>true</bool> 141 <bool>true</bool>
@@ -148,7 +148,17 @@
148 </property> 148 </property>
149 </widget> 149 </widget>
150 </item> 150 </item>
151 <item> 151 <item row="1" column="0">
152 <widget class="QCheckBox" name="enable_shader_feedback">
153 <property name="toolTip">
154 <string>When checked, yuzu will log statistics about the compiled pipeline cache</string>
155 </property>
156 <property name="text">
157 <string>Enable Shader Feedback</string>
158 </property>
159 </widget>
160 </item>
161 <item row="1" column="1">
152 <widget class="QCheckBox" name="disable_loop_safety_checks"> 162 <widget class="QCheckBox" name="disable_loop_safety_checks">
153 <property name="toolTip"> 163 <property name="toolTip">
154 <string>When checked, it executes shaders without loop logic changes</string> 164 <string>When checked, it executes shaders without loop logic changes</string>
@@ -276,11 +286,14 @@
276 <tabstop>open_log_button</tabstop> 286 <tabstop>open_log_button</tabstop>
277 <tabstop>homebrew_args_edit</tabstop> 287 <tabstop>homebrew_args_edit</tabstop>
278 <tabstop>enable_graphics_debugging</tabstop> 288 <tabstop>enable_graphics_debugging</tabstop>
289 <tabstop>enable_shader_feedback</tabstop>
279 <tabstop>enable_nsight_aftermath</tabstop> 290 <tabstop>enable_nsight_aftermath</tabstop>
280 <tabstop>disable_macro_jit</tabstop> 291 <tabstop>disable_macro_jit</tabstop>
281 <tabstop>disable_loop_safety_checks</tabstop> 292 <tabstop>disable_loop_safety_checks</tabstop>
293 <tabstop>fs_access_log</tabstop>
282 <tabstop>reporting_services</tabstop> 294 <tabstop>reporting_services</tabstop>
283 <tabstop>quest_flag</tabstop> 295 <tabstop>quest_flag</tabstop>
296 <tabstop>enable_cpu_debugging</tabstop>
284 <tabstop>use_debug_asserts</tabstop> 297 <tabstop>use_debug_asserts</tabstop>
285 <tabstop>use_auto_stub</tabstop> 298 <tabstop>use_auto_stub</tabstop>
286 </tabstops> 299 </tabstops>
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui
index 8ce97edec..69b6c2d66 100644
--- a/src/yuzu/configuration/configure_general.ui
+++ b/src/yuzu/configuration/configure_general.ui
@@ -25,6 +25,36 @@
25 <item> 25 <item>
26 <layout class="QVBoxLayout" name="GeneralVerticalLayout"> 26 <layout class="QVBoxLayout" name="GeneralVerticalLayout">
27 <item> 27 <item>
28 <layout class="QHBoxLayout" name="horizontalLayout_2">
29 <item>
30 <widget class="QLabel" name="fps_cap_label">
31 <property name="text">
32 <string>Framerate Cap</string>
33 </property>
34 <property name="toolTip">
35 <string>Requires the use of the FPS Limiter Toggle hotkey to take effect.</string>
36 </property>
37 </widget>
38 </item>
39 <item>
40 <widget class="QSpinBox" name="fps_cap">
41 <property name="suffix">
42 <string>x</string>
43 </property>
44 <property name="minimum">
45 <number>1</number>
46 </property>
47 <property name="maximum">
48 <number>1000</number>
49 </property>
50 <property name="value">
51 <number>500</number>
52 </property>
53 </widget>
54 </item>
55 </layout>
56 </item>
57 <item>
28 <layout class="QHBoxLayout" name="horizontalLayout_2"> 58 <layout class="QHBoxLayout" name="horizontalLayout_2">
29 <item> 59 <item>
30 <widget class="QCheckBox" name="toggle_speed_limit"> 60 <widget class="QCheckBox" name="toggle_speed_limit">
@@ -52,36 +82,6 @@
52 </layout> 82 </layout>
53 </item> 83 </item>
54 <item> 84 <item>
55 <layout class="QHBoxLayout" name="horizontalLayout_2">
56 <item>
57 <widget class="QLabel" name="fps_cap_label">
58 <property name="text">
59 <string>Framerate Cap</string>
60 </property>
61 <property name="toolTip">
62 <string>Requires the use of the FPS Limiter Toggle hotkey to take effect.</string>
63 </property>
64 </widget>
65 </item>
66 <item>
67 <widget class="QSpinBox" name="fps_cap">
68 <property name="suffix">
69 <string>x</string>
70 </property>
71 <property name="minimum">
72 <number>1</number>
73 </property>
74 <property name="maximum">
75 <number>1000</number>
76 </property>
77 <property name="value">
78 <number>500</number>
79 </property>
80 </widget>
81 </item>
82 </layout>
83 </item>
84 <item>
85 <widget class="QCheckBox" name="use_multi_core"> 85 <widget class="QCheckBox" name="use_multi_core">
86 <property name="text"> 86 <property name="text">
87 <string>Multicore CPU Emulation</string> 87 <string>Multicore CPU Emulation</string>
diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui
index 379dc5d2e..4fe6b86ae 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.ui
+++ b/src/yuzu/configuration/configure_graphics_advanced.ui
@@ -82,14 +82,17 @@
82 <string>Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental.</string> 82 <string>Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental.</string>
83 </property> 83 </property>
84 <property name="text"> 84 <property name="text">
85 <string>Use asynchronous shader building</string> 85 <string>Use asynchronous shader building (hack)</string>
86 </property> 86 </property>
87 </widget> 87 </widget>
88 </item> 88 </item>
89 <item> 89 <item>
90 <widget class="QCheckBox" name="use_fast_gpu_time"> 90 <widget class="QCheckBox" name="use_fast_gpu_time">
91 <property name="toolTip">
92 <string>Enables Fast GPU Time. This option will force most games to run at their highest native resolution.</string>
93 </property>
91 <property name="text"> 94 <property name="text">
92 <string>Use Fast GPU Time</string> 95 <string>Use Fast GPU Time (hack)</string>
93 </property> 96 </property>
94 </widget> 97 </widget>
95 </item> 98 </item>
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 6b9bd05f1..7527c068b 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -309,11 +309,14 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
309 buttons_param[button_id].Clear(); 309 buttons_param[button_id].Clear();
310 button_map[button_id]->setText(tr("[not set]")); 310 button_map[button_id]->setText(tr("[not set]"));
311 }); 311 });
312 context_menu.addAction(tr("Toggle button"), [&] { 312 if (buttons_param[button_id].Has("toggle")) {
313 const bool toggle_value = !buttons_param[button_id].Get("toggle", false); 313 context_menu.addAction(tr("Toggle button"), [&] {
314 buttons_param[button_id].Set("toggle", toggle_value); 314 const bool toggle_value =
315 button_map[button_id]->setText(ButtonToText(buttons_param[button_id])); 315 !buttons_param[button_id].Get("toggle", false);
316 }); 316 buttons_param[button_id].Set("toggle", toggle_value);
317 button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
318 });
319 }
317 if (buttons_param[button_id].Has("threshold")) { 320 if (buttons_param[button_id].Has("threshold")) {
318 context_menu.addAction(tr("Set threshold"), [&] { 321 context_menu.addAction(tr("Set threshold"), [&] {
319 const int button_threshold = static_cast<int>( 322 const int button_threshold = static_cast<int>(
diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp
index f50cda2f3..cd633e45f 100644
--- a/src/yuzu/configuration/configure_input_player_widget.cpp
+++ b/src/yuzu/configuration/configure_input_player_widget.cpp
@@ -122,6 +122,7 @@ void PlayerControlPreview::UpdateColors() {
122 colors.slider_arrow = QColor(14, 15, 18); 122 colors.slider_arrow = QColor(14, 15, 18);
123 colors.font2 = QColor(255, 255, 255); 123 colors.font2 = QColor(255, 255, 255);
124 colors.indicator = QColor(170, 238, 255); 124 colors.indicator = QColor(170, 238, 255);
125 colors.indicator2 = QColor(100, 255, 100);
125 colors.deadzone = QColor(204, 136, 136); 126 colors.deadzone = QColor(204, 136, 136);
126 colors.slider_button = colors.button; 127 colors.slider_button = colors.button;
127 } 128 }
@@ -139,6 +140,7 @@ void PlayerControlPreview::UpdateColors() {
139 colors.slider_arrow = QColor(65, 68, 73); 140 colors.slider_arrow = QColor(65, 68, 73);
140 colors.font2 = QColor(0, 0, 0); 141 colors.font2 = QColor(0, 0, 0);
141 colors.indicator = QColor(0, 0, 200); 142 colors.indicator = QColor(0, 0, 200);
143 colors.indicator2 = QColor(0, 150, 0);
142 colors.deadzone = QColor(170, 0, 0); 144 colors.deadzone = QColor(170, 0, 0);
143 colors.slider_button = QColor(153, 149, 149); 145 colors.slider_button = QColor(153, 149, 149);
144 } 146 }
@@ -317,8 +319,7 @@ void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center)
317 using namespace Settings::NativeAnalog; 319 using namespace Settings::NativeAnalog;
318 DrawJoystick(p, center + QPointF(9, -69) + (axis_values[LStick].value * 8), 1.8f, 320 DrawJoystick(p, center + QPointF(9, -69) + (axis_values[LStick].value * 8), 1.8f,
319 button_values[Settings::NativeButton::LStick]); 321 button_values[Settings::NativeButton::LStick]);
320 DrawRawJoystick(p, center + QPointF(-140, 90), axis_values[LStick].raw_value, 322 DrawRawJoystick(p, center + QPointF(-140, 90), QPointF(0, 0));
321 axis_values[LStick].properties);
322 } 323 }
323 324
324 using namespace Settings::NativeButton; 325 using namespace Settings::NativeButton;
@@ -432,8 +433,7 @@ void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center
432 using namespace Settings::NativeAnalog; 433 using namespace Settings::NativeAnalog;
433 DrawJoystick(p, center + QPointF(-9, 11) + (axis_values[RStick].value * 8), 1.8f, 434 DrawJoystick(p, center + QPointF(-9, 11) + (axis_values[RStick].value * 8), 1.8f,
434 button_values[Settings::NativeButton::RStick]); 435 button_values[Settings::NativeButton::RStick]);
435 DrawRawJoystick(p, center + QPointF(140, 90), axis_values[RStick].raw_value, 436 DrawRawJoystick(p, QPointF(0, 0), center + QPointF(140, 90));
436 axis_values[RStick].properties);
437 } 437 }
438 438
439 using namespace Settings::NativeButton; 439 using namespace Settings::NativeButton;
@@ -547,8 +547,7 @@ void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center)
547 547
548 DrawJoystick(p, center + QPointF(-65, -65) + (l_stick.value * 7), 1.62f, l_button); 548 DrawJoystick(p, center + QPointF(-65, -65) + (l_stick.value * 7), 1.62f, l_button);
549 DrawJoystick(p, center + QPointF(65, 12) + (r_stick.value * 7), 1.62f, r_button); 549 DrawJoystick(p, center + QPointF(65, 12) + (r_stick.value * 7), 1.62f, r_button);
550 DrawRawJoystick(p, center + QPointF(-180, 90), l_stick.raw_value, l_stick.properties); 550 DrawRawJoystick(p, center + QPointF(-180, 90), center + QPointF(180, 90));
551 DrawRawJoystick(p, center + QPointF(180, 90), r_stick.raw_value, r_stick.properties);
552 } 551 }
553 552
554 using namespace Settings::NativeButton; 553 using namespace Settings::NativeButton;
@@ -634,8 +633,7 @@ void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF cen
634 633
635 DrawJoystick(p, center + QPointF(-171, -41) + (l_stick.value * 4), 1.0f, l_button); 634 DrawJoystick(p, center + QPointF(-171, -41) + (l_stick.value * 4), 1.0f, l_button);
636 DrawJoystick(p, center + QPointF(171, 8) + (r_stick.value * 4), 1.0f, r_button); 635 DrawJoystick(p, center + QPointF(171, 8) + (r_stick.value * 4), 1.0f, r_button);
637 DrawRawJoystick(p, center + QPointF(-50, 0), l_stick.raw_value, l_stick.properties); 636 DrawRawJoystick(p, center + QPointF(-50, 0), center + QPointF(50, 0));
638 DrawRawJoystick(p, center + QPointF(50, 0), r_stick.raw_value, r_stick.properties);
639 } 637 }
640 638
641 using namespace Settings::NativeButton; 639 using namespace Settings::NativeButton;
@@ -728,10 +726,7 @@ void PlayerControlPreview::DrawProController(QPainter& p, const QPointF center)
728 button_values[Settings::NativeButton::LStick]); 726 button_values[Settings::NativeButton::LStick]);
729 DrawProJoystick(p, center + QPointF(51, 0), axis_values[RStick].value, 11, 727 DrawProJoystick(p, center + QPointF(51, 0), axis_values[RStick].value, 11,
730 button_values[Settings::NativeButton::RStick]); 728 button_values[Settings::NativeButton::RStick]);
731 DrawRawJoystick(p, center + QPointF(-50, 105), axis_values[LStick].raw_value, 729 DrawRawJoystick(p, center + QPointF(-50, 105), center + QPointF(50, 105));
732 axis_values[LStick].properties);
733 DrawRawJoystick(p, center + QPointF(50, 105), axis_values[RStick].raw_value,
734 axis_values[RStick].properties);
735 } 730 }
736 731
737 using namespace Settings::NativeButton; 732 using namespace Settings::NativeButton;
@@ -821,10 +816,7 @@ void PlayerControlPreview::DrawGCController(QPainter& p, const QPointF center) {
821 p.setBrush(colors.font); 816 p.setBrush(colors.font);
822 DrawSymbol(p, center + QPointF(61, 37) + (axis_values[RStick].value * 9.5f), Symbol::C, 817 DrawSymbol(p, center + QPointF(61, 37) + (axis_values[RStick].value * 9.5f), Symbol::C,
823 1.0f); 818 1.0f);
824 DrawRawJoystick(p, center + QPointF(-198, -125), axis_values[LStick].raw_value, 819 DrawRawJoystick(p, center + QPointF(-198, -125), center + QPointF(198, -125));
825 axis_values[LStick].properties);
826 DrawRawJoystick(p, center + QPointF(198, -125), axis_values[RStick].raw_value,
827 axis_values[RStick].properties);
828 } 820 }
829 821
830 using namespace Settings::NativeButton; 822 using namespace Settings::NativeButton;
@@ -2358,8 +2350,33 @@ void PlayerControlPreview::DrawGCJoystick(QPainter& p, const QPointF center, boo
2358 DrawCircle(p, center, 7.5f); 2350 DrawCircle(p, center, 7.5f);
2359} 2351}
2360 2352
2361void PlayerControlPreview::DrawRawJoystick(QPainter& p, const QPointF center, const QPointF value, 2353void PlayerControlPreview::DrawRawJoystick(QPainter& p, QPointF center_left, QPointF center_right) {
2362 const Input::AnalogProperties& properties) { 2354 using namespace Settings::NativeAnalog;
2355 if (controller_type != Settings::ControllerType::LeftJoycon) {
2356 DrawJoystickProperties(p, center_right, axis_values[RStick].properties);
2357 p.setPen(colors.indicator);
2358 p.setBrush(colors.indicator);
2359 DrawJoystickDot(p, center_right, axis_values[RStick].raw_value,
2360 axis_values[RStick].properties);
2361 p.setPen(colors.indicator2);
2362 p.setBrush(colors.indicator2);
2363 DrawJoystickDot(p, center_right, axis_values[RStick].value, axis_values[RStick].properties);
2364 }
2365
2366 if (controller_type != Settings::ControllerType::RightJoycon) {
2367 DrawJoystickProperties(p, center_left, axis_values[LStick].properties);
2368 p.setPen(colors.indicator);
2369 p.setBrush(colors.indicator);
2370 DrawJoystickDot(p, center_left, axis_values[LStick].raw_value,
2371 axis_values[LStick].properties);
2372 p.setPen(colors.indicator2);
2373 p.setBrush(colors.indicator2);
2374 DrawJoystickDot(p, center_left, axis_values[LStick].value, axis_values[LStick].properties);
2375 }
2376}
2377
2378void PlayerControlPreview::DrawJoystickProperties(QPainter& p, const QPointF center,
2379 const Input::AnalogProperties& properties) {
2363 constexpr float size = 45.0f; 2380 constexpr float size = 45.0f;
2364 const float range = size * properties.range; 2381 const float range = size * properties.range;
2365 const float deadzone = size * properties.deadzone; 2382 const float deadzone = size * properties.deadzone;
@@ -2376,10 +2393,14 @@ void PlayerControlPreview::DrawRawJoystick(QPainter& p, const QPointF center, co
2376 pen.setColor(colors.deadzone); 2393 pen.setColor(colors.deadzone);
2377 p.setPen(pen); 2394 p.setPen(pen);
2378 DrawCircle(p, center, deadzone); 2395 DrawCircle(p, center, deadzone);
2396}
2397
2398void PlayerControlPreview::DrawJoystickDot(QPainter& p, const QPointF center, const QPointF value,
2399 const Input::AnalogProperties& properties) {
2400 constexpr float size = 45.0f;
2401 const float range = size * properties.range;
2379 2402
2380 // Dot pointer 2403 // Dot pointer
2381 p.setPen(colors.indicator);
2382 p.setBrush(colors.indicator);
2383 DrawCircle(p, center + (value * range), 2); 2404 DrawCircle(p, center + (value * range), 2);
2384} 2405}
2385 2406
diff --git a/src/yuzu/configuration/configure_input_player_widget.h b/src/yuzu/configuration/configure_input_player_widget.h
index 5fc16d8af..f4a6a5e1b 100644
--- a/src/yuzu/configuration/configure_input_player_widget.h
+++ b/src/yuzu/configuration/configure_input_player_widget.h
@@ -90,6 +90,7 @@ private:
90 QColor highlight2{}; 90 QColor highlight2{};
91 QColor transparent{}; 91 QColor transparent{};
92 QColor indicator{}; 92 QColor indicator{};
93 QColor indicator2{};
93 QColor led_on{}; 94 QColor led_on{};
94 QColor led_off{}; 95 QColor led_off{};
95 QColor slider{}; 96 QColor slider{};
@@ -139,7 +140,10 @@ private:
139 // Draw joystick functions 140 // Draw joystick functions
140 void DrawJoystick(QPainter& p, QPointF center, float size, bool pressed); 141 void DrawJoystick(QPainter& p, QPointF center, float size, bool pressed);
141 void DrawJoystickSideview(QPainter& p, QPointF center, float angle, float size, bool pressed); 142 void DrawJoystickSideview(QPainter& p, QPointF center, float angle, float size, bool pressed);
142 void DrawRawJoystick(QPainter& p, QPointF center, QPointF value, 143 void DrawRawJoystick(QPainter& p, QPointF center_left, QPointF center_right);
144 void DrawJoystickProperties(QPainter& p, QPointF center,
145 const Input::AnalogProperties& properties);
146 void DrawJoystickDot(QPainter& p, QPointF center, QPointF value,
143 const Input::AnalogProperties& properties); 147 const Input::AnalogProperties& properties);
144 void DrawProJoystick(QPainter& p, QPointF center, QPointF offset, float scalar, bool pressed); 148 void DrawProJoystick(QPainter& p, QPointF center, QPointF offset, float scalar, bool pressed);
145 void DrawGCJoystick(QPainter& p, QPointF center, bool pressed); 149 void DrawGCJoystick(QPainter& p, QPointF center, bool pressed);
diff --git a/src/yuzu/configuration/configure_system.ui b/src/yuzu/configuration/configure_system.ui
index 53b95658b..27f552f59 100644
--- a/src/yuzu/configuration/configure_system.ui
+++ b/src/yuzu/configuration/configure_system.ui
@@ -401,6 +401,11 @@
401 <string>Traditional Chinese (正體中文)</string> 401 <string>Traditional Chinese (正體中文)</string>
402 </property> 402 </property>
403 </item> 403 </item>
404 <item>
405 <property name="text">
406 <string>Brazilian Portuguese (português do Brasil)</string>
407 </property>
408 </item>
404 </widget> 409 </widget>
405 </item> 410 </item>
406 <item row="5" column="0"> 411 <item row="5" column="0">
diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp
index e8f41bf65..9d7d51126 100644
--- a/src/yuzu/configuration/configure_ui.cpp
+++ b/src/yuzu/configuration/configure_ui.cpp
@@ -16,7 +16,7 @@
16#include "yuzu/uisettings.h" 16#include "yuzu/uisettings.h"
17 17
18namespace { 18namespace {
19constexpr std::array default_icon_sizes{ 19constexpr std::array default_game_icon_sizes{
20 std::make_pair(0, QT_TRANSLATE_NOOP("ConfigureUI", "None")), 20 std::make_pair(0, QT_TRANSLATE_NOOP("ConfigureUI", "None")),
21 std::make_pair(32, QT_TRANSLATE_NOOP("ConfigureUI", "Small (32x32)")), 21 std::make_pair(32, QT_TRANSLATE_NOOP("ConfigureUI", "Small (32x32)")),
22 std::make_pair(64, QT_TRANSLATE_NOOP("ConfigureUI", "Standard (64x64)")), 22 std::make_pair(64, QT_TRANSLATE_NOOP("ConfigureUI", "Standard (64x64)")),
@@ -24,6 +24,13 @@ constexpr std::array default_icon_sizes{
24 std::make_pair(256, QT_TRANSLATE_NOOP("ConfigureUI", "Full Size (256x256)")), 24 std::make_pair(256, QT_TRANSLATE_NOOP("ConfigureUI", "Full Size (256x256)")),
25}; 25};
26 26
27constexpr std::array default_folder_icon_sizes{
28 std::make_pair(0, QT_TRANSLATE_NOOP("ConfigureUI", "None")),
29 std::make_pair(24, QT_TRANSLATE_NOOP("ConfigureUI", "Small (24x24)")),
30 std::make_pair(48, QT_TRANSLATE_NOOP("ConfigureUI", "Standard (48x48)")),
31 std::make_pair(72, QT_TRANSLATE_NOOP("ConfigureUI", "Large (72x72)")),
32};
33
27// clang-format off 34// clang-format off
28constexpr std::array row_text_names{ 35constexpr std::array row_text_names{
29 QT_TRANSLATE_NOOP("ConfigureUI", "Filename"), 36 QT_TRANSLATE_NOOP("ConfigureUI", "Filename"),
@@ -34,8 +41,12 @@ constexpr std::array row_text_names{
34}; 41};
35// clang-format on 42// clang-format on
36 43
37QString GetTranslatedIconSize(size_t index) { 44QString GetTranslatedGameIconSize(size_t index) {
38 return QCoreApplication::translate("ConfigureUI", default_icon_sizes[index].second); 45 return QCoreApplication::translate("ConfigureUI", default_game_icon_sizes[index].second);
46}
47
48QString GetTranslatedFolderIconSize(size_t index) {
49 return QCoreApplication::translate("ConfigureUI", default_folder_icon_sizes[index].second);
39} 50}
40 51
41QString GetTranslatedRowTextName(size_t index) { 52QString GetTranslatedRowTextName(size_t index) {
@@ -60,8 +71,10 @@ ConfigureUi::ConfigureUi(QWidget* parent) : QWidget(parent), ui(new Ui::Configur
60 71
61 // Force game list reload if any of the relevant settings are changed. 72 // Force game list reload if any of the relevant settings are changed.
62 connect(ui->show_add_ons, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate); 73 connect(ui->show_add_ons, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate);
63 connect(ui->icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, 74 connect(ui->game_icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
64 &ConfigureUi::RequestGameListUpdate); 75 &ConfigureUi::RequestGameListUpdate);
76 connect(ui->folder_icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged),
77 this, &ConfigureUi::RequestGameListUpdate);
65 connect(ui->row_1_text_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, 78 connect(ui->row_1_text_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
66 &ConfigureUi::RequestGameListUpdate); 79 &ConfigureUi::RequestGameListUpdate);
67 connect(ui->row_2_text_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, 80 connect(ui->row_2_text_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
@@ -95,7 +108,8 @@ void ConfigureUi::ApplyConfiguration() {
95 UISettings::values.theme = 108 UISettings::values.theme =
96 ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); 109 ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString();
97 UISettings::values.show_add_ons = ui->show_add_ons->isChecked(); 110 UISettings::values.show_add_ons = ui->show_add_ons->isChecked();
98 UISettings::values.icon_size = ui->icon_size_combobox->currentData().toUInt(); 111 UISettings::values.game_icon_size = ui->game_icon_size_combobox->currentData().toUInt();
112 UISettings::values.folder_icon_size = ui->folder_icon_size_combobox->currentData().toUInt();
99 UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt(); 113 UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt();
100 UISettings::values.row_2_text_id = ui->row_2_text_combobox->currentData().toUInt(); 114 UISettings::values.row_2_text_id = ui->row_2_text_combobox->currentData().toUInt();
101 115
@@ -114,8 +128,10 @@ void ConfigureUi::SetConfiguration() {
114 ui->language_combobox->setCurrentIndex( 128 ui->language_combobox->setCurrentIndex(
115 ui->language_combobox->findData(UISettings::values.language)); 129 ui->language_combobox->findData(UISettings::values.language));
116 ui->show_add_ons->setChecked(UISettings::values.show_add_ons.GetValue()); 130 ui->show_add_ons->setChecked(UISettings::values.show_add_ons.GetValue());
117 ui->icon_size_combobox->setCurrentIndex( 131 ui->game_icon_size_combobox->setCurrentIndex(
118 ui->icon_size_combobox->findData(UISettings::values.icon_size.GetValue())); 132 ui->game_icon_size_combobox->findData(UISettings::values.game_icon_size.GetValue()));
133 ui->folder_icon_size_combobox->setCurrentIndex(
134 ui->folder_icon_size_combobox->findData(UISettings::values.folder_icon_size.GetValue()));
119 135
120 ui->enable_screenshot_save_as->setChecked( 136 ui->enable_screenshot_save_as->setChecked(
121 UISettings::values.enable_screenshot_save_as.GetValue()); 137 UISettings::values.enable_screenshot_save_as.GetValue());
@@ -134,8 +150,14 @@ void ConfigureUi::changeEvent(QEvent* event) {
134void ConfigureUi::RetranslateUI() { 150void ConfigureUi::RetranslateUI() {
135 ui->retranslateUi(this); 151 ui->retranslateUi(this);
136 152
137 for (int i = 0; i < ui->icon_size_combobox->count(); i++) { 153 for (int i = 0; i < ui->game_icon_size_combobox->count(); i++) {
138 ui->icon_size_combobox->setItemText(i, GetTranslatedIconSize(static_cast<size_t>(i))); 154 ui->game_icon_size_combobox->setItemText(i,
155 GetTranslatedGameIconSize(static_cast<size_t>(i)));
156 }
157
158 for (int i = 0; i < ui->folder_icon_size_combobox->count(); i++) {
159 ui->folder_icon_size_combobox->setItemText(
160 i, GetTranslatedFolderIconSize(static_cast<size_t>(i)));
139 } 161 }
140 162
141 for (int i = 0; i < ui->row_1_text_combobox->count(); i++) { 163 for (int i = 0; i < ui->row_1_text_combobox->count(); i++) {
@@ -166,9 +188,13 @@ void ConfigureUi::InitializeLanguageComboBox() {
166} 188}
167 189
168void ConfigureUi::InitializeIconSizeComboBox() { 190void ConfigureUi::InitializeIconSizeComboBox() {
169 for (size_t i = 0; i < default_icon_sizes.size(); i++) { 191 for (size_t i = 0; i < default_game_icon_sizes.size(); i++) {
170 const auto size = default_icon_sizes[i].first; 192 const auto size = default_game_icon_sizes[i].first;
171 ui->icon_size_combobox->addItem(GetTranslatedIconSize(i), size); 193 ui->game_icon_size_combobox->addItem(GetTranslatedGameIconSize(i), size);
194 }
195 for (size_t i = 0; i < default_folder_icon_sizes.size(); i++) {
196 const auto size = default_folder_icon_sizes[i].first;
197 ui->folder_icon_size_combobox->addItem(GetTranslatedFolderIconSize(i), size);
172 } 198 }
173} 199}
174 200
diff --git a/src/yuzu/configuration/configure_ui.ui b/src/yuzu/configuration/configure_ui.ui
index d895b799f..394f9fe04 100644
--- a/src/yuzu/configuration/configure_ui.ui
+++ b/src/yuzu/configuration/configure_ui.ui
@@ -81,16 +81,30 @@
81 </widget> 81 </widget>
82 </item> 82 </item>
83 <item> 83 <item>
84 <layout class="QHBoxLayout" name="icon_size_qhbox_layout_2"> 84 <layout class="QHBoxLayout" name="game_icon_size_qhbox_layout_2">
85 <item> 85 <item>
86 <widget class="QLabel" name="icon_size_label"> 86 <widget class="QLabel" name="game_icon_size_label">
87 <property name="text"> 87 <property name="text">
88 <string>Icon Size:</string> 88 <string>Game Icon Size:</string>
89 </property> 89 </property>
90 </widget> 90 </widget>
91 </item> 91 </item>
92 <item> 92 <item>
93 <widget class="QComboBox" name="icon_size_combobox"/> 93 <widget class="QComboBox" name="game_icon_size_combobox"/>
94 </item>
95 </layout>
96 </item>
97 <item>
98 <layout class="QHBoxLayout" name="folder_icon_size_qhbox_layout_2">
99 <item>
100 <widget class="QLabel" name="folder_icon_size_label">
101 <property name="text">
102 <string>Folder Icon Size:</string>
103 </property>
104 </widget>
105 </item>
106 <item>
107 <widget class="QComboBox" name="folder_icon_size_combobox"/>
94 </item> 108 </item>
95 </layout> 109 </layout>
96 </item> 110 </item>
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index f746bd85d..e97804220 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -244,8 +244,8 @@ void GameList::OnUpdateThemedIcons() {
244 for (int i = 0; i < item_model->invisibleRootItem()->rowCount(); i++) { 244 for (int i = 0; i < item_model->invisibleRootItem()->rowCount(); i++) {
245 QStandardItem* child = item_model->invisibleRootItem()->child(i); 245 QStandardItem* child = item_model->invisibleRootItem()->child(i);
246 246
247 const int icon_size = 247 const int icon_size = UISettings::values.folder_icon_size.GetValue();
248 std::min(static_cast<int>(UISettings::values.icon_size.GetValue()), 64); 248
249 switch (child->data(GameListItem::TypeRole).value<GameListItemType>()) { 249 switch (child->data(GameListItem::TypeRole).value<GameListItemType>()) {
250 case GameListItemType::SdmcDir: 250 case GameListItemType::SdmcDir:
251 child->setData( 251 child->setData(
diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h
index 982c0789d..9dc3cc7c3 100644
--- a/src/yuzu/game_list_p.h
+++ b/src/yuzu/game_list_p.h
@@ -80,7 +80,7 @@ public:
80 setData(qulonglong(program_id), ProgramIdRole); 80 setData(qulonglong(program_id), ProgramIdRole);
81 setData(game_type, FileTypeRole); 81 setData(game_type, FileTypeRole);
82 82
83 const u32 size = UISettings::values.icon_size.GetValue(); 83 const u32 size = UISettings::values.game_icon_size.GetValue();
84 84
85 QPixmap picture; 85 QPixmap picture;
86 if (!picture.loadFromData(picture_data.data(), static_cast<u32>(picture_data.size()))) { 86 if (!picture.loadFromData(picture_data.data(), static_cast<u32>(picture_data.size()))) {
@@ -233,8 +233,7 @@ public:
233 UISettings::GameDir* game_dir = &directory; 233 UISettings::GameDir* game_dir = &directory;
234 setData(QVariant(UISettings::values.game_dirs.indexOf(directory)), GameDirRole); 234 setData(QVariant(UISettings::values.game_dirs.indexOf(directory)), GameDirRole);
235 235
236 const int icon_size = 236 const int icon_size = UISettings::values.folder_icon_size.GetValue();
237 std::min(static_cast<int>(UISettings::values.icon_size.GetValue()), 64);
238 switch (dir_type) { 237 switch (dir_type) {
239 case GameListItemType::SdmcDir: 238 case GameListItemType::SdmcDir:
240 setData( 239 setData(
@@ -295,8 +294,8 @@ public:
295 explicit GameListAddDir() { 294 explicit GameListAddDir() {
296 setData(type(), TypeRole); 295 setData(type(), TypeRole);
297 296
298 const int icon_size = 297 const int icon_size = UISettings::values.folder_icon_size.GetValue();
299 std::min(static_cast<int>(UISettings::values.icon_size.GetValue()), 64); 298
300 setData(QIcon::fromTheme(QStringLiteral("plus")) 299 setData(QIcon::fromTheme(QStringLiteral("plus"))
301 .pixmap(icon_size) 300 .pixmap(icon_size)
302 .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), 301 .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
@@ -318,8 +317,8 @@ public:
318 explicit GameListFavorites() { 317 explicit GameListFavorites() {
319 setData(type(), TypeRole); 318 setData(type(), TypeRole);
320 319
321 const int icon_size = 320 const int icon_size = UISettings::values.folder_icon_size.GetValue();
322 std::min(static_cast<int>(UISettings::values.icon_size.GetValue()), 64); 321
323 setData(QIcon::fromTheme(QStringLiteral("star")) 322 setData(QIcon::fromTheme(QStringLiteral("star"))
324 .pixmap(icon_size) 323 .pixmap(icon_size)
325 .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), 324 .scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 9544f0fb0..5940e0cfd 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -2814,8 +2814,6 @@ void GMainWindow::OnToggleFilterBar() {
2814} 2814}
2815 2815
2816void GMainWindow::OnCaptureScreenshot() { 2816void GMainWindow::OnCaptureScreenshot() {
2817 OnPauseGame();
2818
2819 const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); 2817 const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
2820 const auto screenshot_path = 2818 const auto screenshot_path =
2821 QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::ScreenshotsDir)); 2819 QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::ScreenshotsDir));
@@ -2827,23 +2825,22 @@ void GMainWindow::OnCaptureScreenshot() {
2827 .arg(date); 2825 .arg(date);
2828 2826
2829 if (!Common::FS::CreateDir(screenshot_path.toStdString())) { 2827 if (!Common::FS::CreateDir(screenshot_path.toStdString())) {
2830 OnStartGame();
2831 return; 2828 return;
2832 } 2829 }
2833 2830
2834#ifdef _WIN32 2831#ifdef _WIN32
2835 if (UISettings::values.enable_screenshot_save_as) { 2832 if (UISettings::values.enable_screenshot_save_as) {
2833 OnPauseGame();
2836 filename = QFileDialog::getSaveFileName(this, tr("Capture Screenshot"), filename, 2834 filename = QFileDialog::getSaveFileName(this, tr("Capture Screenshot"), filename,
2837 tr("PNG Image (*.png)")); 2835 tr("PNG Image (*.png)"));
2836 OnStartGame();
2838 if (filename.isEmpty()) { 2837 if (filename.isEmpty()) {
2839 OnStartGame();
2840 return; 2838 return;
2841 } 2839 }
2842 } 2840 }
2843#endif 2841#endif
2844 render_window->CaptureScreenshot(UISettings::values.screenshot_resolution_factor.GetValue(), 2842 render_window->CaptureScreenshot(UISettings::values.screenshot_resolution_factor.GetValue(),
2845 filename); 2843 filename);
2846 OnStartGame();
2847} 2844}
2848 2845
2849// TODO: Written 2020-10-01: Remove per-game config migration code when it is irrelevant 2846// TODO: Written 2020-10-01: Remove per-game config migration code when it is irrelevant
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index 7b9d2dd53..81f741f20 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -91,7 +91,8 @@ struct Values {
91 91
92 // Game List 92 // Game List
93 Settings::BasicSetting<bool> show_add_ons{true, "show_add_ons"}; 93 Settings::BasicSetting<bool> show_add_ons{true, "show_add_ons"};
94 Settings::BasicSetting<uint32_t> icon_size{64, "icon_size"}; 94 Settings::BasicSetting<uint32_t> game_icon_size{64, "game_icon_size"};
95 Settings::BasicSetting<uint32_t> folder_icon_size{48, "folder_icon_size"};
95 Settings::BasicSetting<uint8_t> row_1_text_id{3, "row_1_text_id"}; 96 Settings::BasicSetting<uint8_t> row_1_text_id{3, "row_1_text_id"};
96 Settings::BasicSetting<uint8_t> row_2_text_id{2, "row_2_text_id"}; 97 Settings::BasicSetting<uint8_t> row_2_text_id{2, "row_2_text_id"};
97 std::atomic_bool is_game_list_reload_pending{false}; 98 std::atomic_bool is_game_list_reload_pending{false};
diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt
index e55a19649..74fc24972 100644
--- a/src/yuzu_cmd/CMakeLists.txt
+++ b/src/yuzu_cmd/CMakeLists.txt
@@ -1,5 +1,6 @@
1set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules) 1set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules)
2 2
3# Credits to Samantas5855 and others for this function.
3function(create_resource file output filename) 4function(create_resource file output filename)
4 # Read hex data from file 5 # Read hex data from file
5 file(READ ${file} filedata HEX) 6 file(READ ${file} filedata HEX)
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 5af1ee6a8..4f14be524 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -278,6 +278,9 @@ void Config::ReadValues() {
278 if (Settings::values.players.GetValue()[p].analogs[i].empty()) 278 if (Settings::values.players.GetValue()[p].analogs[i].empty())
279 Settings::values.players.GetValue()[p].analogs[i] = default_param; 279 Settings::values.players.GetValue()[p].analogs[i] = default_param;
280 } 280 }
281
282 Settings::values.players.GetValue()[p].connected =
283 sdl2_config->GetBoolean(group, "connected", false);
281 } 284 }
282 285
283 ReadSetting("ControlsGeneral", Settings::values.mouse_enabled); 286 ReadSetting("ControlsGeneral", Settings::values.mouse_enabled);
@@ -444,6 +447,7 @@ void Config::ReadValues() {
444 // Renderer 447 // Renderer
445 ReadSetting("Renderer", Settings::values.renderer_backend); 448 ReadSetting("Renderer", Settings::values.renderer_backend);
446 ReadSetting("Renderer", Settings::values.renderer_debug); 449 ReadSetting("Renderer", Settings::values.renderer_debug);
450 ReadSetting("Renderer", Settings::values.renderer_shader_feedback);
447 ReadSetting("Renderer", Settings::values.enable_nsight_aftermath); 451 ReadSetting("Renderer", Settings::values.enable_nsight_aftermath);
448 ReadSetting("Renderer", Settings::values.disable_shader_loop_safety_checks); 452 ReadSetting("Renderer", Settings::values.disable_shader_loop_safety_checks);
449 ReadSetting("Renderer", Settings::values.vulkan_device); 453 ReadSetting("Renderer", Settings::values.vulkan_device);
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index e646e2d2f..e02eceb99 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -221,6 +221,10 @@ backend =
221# 0 (default): Disabled, 1: Enabled 221# 0 (default): Disabled, 1: Enabled
222debug = 222debug =
223 223
224# Enable shader feedback.
225# 0 (default): Disabled, 1: Enabled
226renderer_shader_feedback =
227
224# Enable Nsight Aftermath crash dumps 228# Enable Nsight Aftermath crash dumps
225# 0 (default): Disabled, 1: Enabled 229# 0 (default): Disabled, 1: Enabled
226nsight_aftermath = 230nsight_aftermath =
@@ -363,7 +367,7 @@ custom_rtc =
363# Sets the systems language index 367# Sets the systems language index
364# 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese, 368# 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese,
365# 7: Korean, 8: Dutch, 9: Portuguese, 10: Russian, 11: Taiwanese, 12: British English, 13: Canadian French, 369# 7: Korean, 8: Dutch, 9: Portuguese, 10: Russian, 11: Taiwanese, 12: British English, 13: Canadian French,
366# 14: Latin American Spanish, 15: Simplified Chinese, 16: Traditional Chinese 370# 14: Latin American Spanish, 15: Simplified Chinese, 16: Traditional Chinese, 17: Brazilian Portuguese
367language_index = 371language_index =
368 372
369# The system region that yuzu will use during emulation 373# The system region that yuzu will use during emulation
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index ea3e0ada4..87fce0c23 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -16,8 +16,8 @@
16#include "yuzu_cmd/emu_window/emu_window_sdl2.h" 16#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
17#include "yuzu_cmd/yuzu_icon.h" 17#include "yuzu_cmd/yuzu_icon.h"
18 18
19EmuWindow_SDL2::EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_) 19EmuWindow_SDL2::EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_)
20 : input_subsystem{input_subsystem_} { 20 : input_subsystem{input_subsystem_}, system{system_} {
21 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { 21 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
22 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); 22 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting...");
23 exit(1); 23 exit(1);
@@ -122,6 +122,10 @@ void EmuWindow_SDL2::OnResize() {
122 UpdateCurrentFramebufferLayout(width, height); 122 UpdateCurrentFramebufferLayout(width, height);
123} 123}
124 124
125void EmuWindow_SDL2::ShowCursor(bool show_cursor) {
126 SDL_ShowCursor(show_cursor ? SDL_ENABLE : SDL_DISABLE);
127}
128
125void EmuWindow_SDL2::Fullscreen() { 129void EmuWindow_SDL2::Fullscreen() {
126 switch (Settings::values.fullscreen_mode.GetValue()) { 130 switch (Settings::values.fullscreen_mode.GetValue()) {
127 case Settings::FullscreenMode::Exclusive: 131 case Settings::FullscreenMode::Exclusive:
@@ -218,7 +222,7 @@ void EmuWindow_SDL2::WaitEvent() {
218 222
219 const u32 current_time = SDL_GetTicks(); 223 const u32 current_time = SDL_GetTicks();
220 if (current_time > last_time + 2000) { 224 if (current_time > last_time + 2000) {
221 const auto results = Core::System::GetInstance().GetAndResetPerfStats(); 225 const auto results = system.GetAndResetPerfStats();
222 const auto title = 226 const auto title =
223 fmt::format("yuzu {} | {}-{} | FPS: {:.0f} ({:.0f}%)", Common::g_build_fullname, 227 fmt::format("yuzu {} | {}-{} | FPS: {:.0f} ({:.0f}%)", Common::g_build_fullname,
224 Common::g_scm_branch, Common::g_scm_desc, results.average_game_fps, 228 Common::g_scm_branch, Common::g_scm_desc, results.average_game_fps,
@@ -228,6 +232,7 @@ void EmuWindow_SDL2::WaitEvent() {
228 } 232 }
229} 233}
230 234
235// Credits to Samantas5855 and others for this function.
231void EmuWindow_SDL2::SetWindowIcon() { 236void EmuWindow_SDL2::SetWindowIcon() {
232 SDL_RWops* const yuzu_icon_stream = SDL_RWFromConstMem((void*)yuzu_icon, yuzu_icon_size); 237 SDL_RWops* const yuzu_icon_stream = SDL_RWFromConstMem((void*)yuzu_icon, yuzu_icon_size);
233 if (yuzu_icon_stream == nullptr) { 238 if (yuzu_icon_stream == nullptr) {
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
index 1b9ab5b93..4810f8775 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
@@ -24,7 +24,7 @@ enum class MouseButton;
24 24
25class EmuWindow_SDL2 : public Core::Frontend::EmuWindow { 25class EmuWindow_SDL2 : public Core::Frontend::EmuWindow {
26public: 26public:
27 explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem); 27 explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem, Core::System& system_);
28 ~EmuWindow_SDL2(); 28 ~EmuWindow_SDL2();
29 29
30 /// Whether the window is still open, and a close request hasn't yet been sent 30 /// Whether the window is still open, and a close request hasn't yet been sent
@@ -67,6 +67,9 @@ protected:
67 /// Called by WaitEvent when any event that may cause the window to be resized occurs 67 /// Called by WaitEvent when any event that may cause the window to be resized occurs
68 void OnResize(); 68 void OnResize();
69 69
70 /// Called when users want to hide the mouse cursor
71 void ShowCursor(bool show_cursor);
72
70 /// Called when user passes the fullscreen parameter flag 73 /// Called when user passes the fullscreen parameter flag
71 void Fullscreen(); 74 void Fullscreen();
72 75
@@ -87,4 +90,7 @@ protected:
87 90
88 /// Input subsystem to use with this window. 91 /// Input subsystem to use with this window.
89 InputCommon::InputSubsystem* input_subsystem; 92 InputCommon::InputSubsystem* input_subsystem;
93
94 /// yuzu core instance
95 Core::System& system;
90}; 96};
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
index eadb41790..a075ad08a 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
@@ -76,8 +76,9 @@ bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() {
76 return unsupported_ext.empty(); 76 return unsupported_ext.empty();
77} 77}
78 78
79EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem, bool fullscreen) 79EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem,
80 : EmuWindow_SDL2{input_subsystem} { 80 Core::System& system_, bool fullscreen)
81 : EmuWindow_SDL2{input_subsystem, system_} {
81 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); 82 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
82 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 6); 83 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 6);
83 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); 84 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
@@ -110,6 +111,7 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsyste
110 111
111 if (fullscreen) { 112 if (fullscreen) {
112 Fullscreen(); 113 Fullscreen();
114 ShowCursor(false);
113 } 115 }
114 116
115 window_context = SDL_GL_CreateContext(render_window); 117 window_context = SDL_GL_CreateContext(render_window);
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h
index 9e694d985..d7f2c83d8 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h
@@ -8,13 +8,18 @@
8#include "core/frontend/emu_window.h" 8#include "core/frontend/emu_window.h"
9#include "yuzu_cmd/emu_window/emu_window_sdl2.h" 9#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
10 10
11namespace Core {
12class System;
13}
14
11namespace InputCommon { 15namespace InputCommon {
12class InputSubsystem; 16class InputSubsystem;
13} 17}
14 18
15class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 { 19class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 {
16public: 20public:
17 explicit EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem, bool fullscreen); 21 explicit EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem, Core::System& system_,
22 bool fullscreen);
18 ~EmuWindow_SDL2_GL(); 23 ~EmuWindow_SDL2_GL();
19 24
20 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; 25 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
index d1473dbab..de40b76bf 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
@@ -24,8 +24,9 @@
24#include <SDL.h> 24#include <SDL.h>
25#include <SDL_syswm.h> 25#include <SDL_syswm.h>
26 26
27EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem, bool fullscreen) 27EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem,
28 : EmuWindow_SDL2{input_subsystem} { 28 Core::System& system_, bool fullscreen)
29 : EmuWindow_SDL2{input_subsystem, system_} {
29 const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name, 30 const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name,
30 Common::g_scm_branch, Common::g_scm_desc); 31 Common::g_scm_branch, Common::g_scm_desc);
31 render_window = 32 render_window =
@@ -44,6 +45,7 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsyste
44 45
45 if (fullscreen) { 46 if (fullscreen) {
46 Fullscreen(); 47 Fullscreen();
48 ShowCursor(false);
47 } 49 }
48 50
49 switch (wm.subsystem) { 51 switch (wm.subsystem) {
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
index de53844f0..3ea521b2a 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
@@ -19,7 +19,8 @@ class InputSubsystem;
19 19
20class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 { 20class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 {
21public: 21public:
22 explicit EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem, bool fullscreen); 22 explicit EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem, Core::System& system,
23 bool fullscreen);
23 ~EmuWindow_SDL2_VK() override; 24 ~EmuWindow_SDL2_VK() override;
24 25
25 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; 26 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 35ce23696..c10093820 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -172,10 +172,10 @@ int main(int argc, char** argv) {
172 std::unique_ptr<EmuWindow_SDL2> emu_window; 172 std::unique_ptr<EmuWindow_SDL2> emu_window;
173 switch (Settings::values.renderer_backend.GetValue()) { 173 switch (Settings::values.renderer_backend.GetValue()) {
174 case Settings::RendererBackend::OpenGL: 174 case Settings::RendererBackend::OpenGL:
175 emu_window = std::make_unique<EmuWindow_SDL2_GL>(&input_subsystem, fullscreen); 175 emu_window = std::make_unique<EmuWindow_SDL2_GL>(&input_subsystem, system, fullscreen);
176 break; 176 break;
177 case Settings::RendererBackend::Vulkan: 177 case Settings::RendererBackend::Vulkan:
178 emu_window = std::make_unique<EmuWindow_SDL2_VK>(&input_subsystem, fullscreen); 178 emu_window = std::make_unique<EmuWindow_SDL2_VK>(&input_subsystem, system, fullscreen);
179 break; 179 break;
180 } 180 }
181 181