summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x.ci/scripts/linux/docker.sh3
-rw-r--r--CMakeLists.txt51
-rw-r--r--dist/qt_themes/default/style.qss4
-rw-r--r--externals/CMakeLists.txt5
m---------externals/SDL0
m---------externals/dynarmic0
-rw-r--r--externals/libusb/CMakeLists.txt2
-rw-r--r--src/common/assert.h8
-rw-r--r--src/common/fs/fs_util.cpp4
-rw-r--r--src/common/fs/fs_util.h11
-rw-r--r--src/common/hex_util.h2
-rw-r--r--src/common/host_memory.cpp9
-rw-r--r--src/common/logging/backend.cpp350
-rw-r--r--src/common/logging/backend.h113
-rw-r--r--src/common/settings.h194
-rw-r--r--src/common/threadsafe_queue.h10
-rw-r--r--src/common/uuid.cpp56
-rw-r--r--src/common/uuid.h30
-rw-r--r--src/common/x64/xbyak_abi.h2
-rw-r--r--src/common/x64/xbyak_util.h2
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/core.cpp15
-rw-r--r--src/core/core.h12
-rw-r--r--src/core/cpu_manager.cpp40
-rw-r--r--src/core/cpu_manager.h6
-rw-r--r--src/core/hle/api_version.h14
-rw-r--r--src/core/hle/kernel/k_address_arbiter.cpp4
-rw-r--r--src/core/hle/kernel/k_auto_object.h4
-rw-r--r--src/core/hle/kernel/k_condition_variable.cpp2
-rw-r--r--src/core/hle/kernel/k_handle_table.cpp6
-rw-r--r--src/core/hle/kernel/k_handle_table.h2
-rw-r--r--src/core/hle/kernel/k_process.cpp1
-rw-r--r--src/core/hle/kernel/k_scheduler.cpp85
-rw-r--r--src/core/hle/kernel/k_scheduler.h2
-rw-r--r--src/core/hle/kernel/k_thread.cpp21
-rw-r--r--src/core/hle/kernel/k_thread.h36
-rw-r--r--src/core/hle/kernel/kernel.cpp57
-rw-r--r--src/core/hle/kernel/kernel.h3
-rw-r--r--src/core/hle/kernel/svc.cpp4
-rw-r--r--src/core/hle/service/acc/acc.cpp22
-rw-r--r--src/core/hle/service/am/applets/applet_error.cpp31
-rw-r--r--src/core/hle/service/am/applets/applet_software_keyboard.cpp6
-rw-r--r--src/core/hle/service/friend/friend.cpp6
-rw-r--r--src/core/hle/service/nifm/nifm.cpp45
-rw-r--r--src/core/hle/service/ns/language.cpp3
-rw-r--r--src/core/hle/service/ns/ns_language.h42
-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/nvflinger/buffer_queue.cpp25
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.h11
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp15
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h3
-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/hle/service/vi/display/vi_display.cpp17
-rw-r--r--src/core/hle/service/vi/display/vi_display.h13
-rw-r--r--src/core/hle/service/vi/vi.cpp2
-rw-r--r--src/core/memory.cpp552
-rw-r--r--src/core/memory.h102
-rw-r--r--src/core/network/network.cpp66
-rw-r--r--src/core/network/network.h24
-rw-r--r--src/core/network/network_interface.cpp203
-rw-r--r--src/core/network/network_interface.h29
-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.cpp90
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp8
-rw-r--r--src/shader_recompiler/frontend/ir/value.h8
-rw-r--r--src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp2
-rw-r--r--src/shader_recompiler/ir_opt/constant_propagation_pass.cpp205
-rw-r--r--src/tests/common/param_package.cpp2
-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.cpp92
-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/macro/macro_jit_x64.h2
-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/render_targets.h6
-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.cpp175
-rw-r--r--src/video_core/textures/texture.h2
-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/CMakeLists.txt6
-rw-r--r--src/yuzu/applets/qt_software_keyboard.cpp2
-rw-r--r--src/yuzu/applets/qt_web_browser.cpp15
-rw-r--r--src/yuzu/applets/qt_web_browser.h3
-rw-r--r--src/yuzu/applets/qt_web_browser_scripts.h6
-rw-r--r--src/yuzu/bootmanager.cpp4
-rw-r--r--src/yuzu/configuration/config.cpp30
-rw-r--r--src/yuzu/configuration/config.h3
-rw-r--r--src/yuzu/configuration/configuration_shared.cpp14
-rw-r--r--src/yuzu/configuration/configuration_shared.h15
-rw-r--r--src/yuzu/configuration/configure.ui10
-rw-r--r--src/yuzu/configuration/configure_cpu.cpp17
-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_dialog.cpp4
-rw-r--r--src/yuzu/configuration/configure_general.ui60
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp8
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.cpp20
-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.cpp95
-rw-r--r--src/yuzu/configuration/configure_input_player_widget.h6
-rw-r--r--src/yuzu/configuration/configure_network.cpp (renamed from src/yuzu/configuration/configure_service.cpp)39
-rw-r--r--src/yuzu/configuration/configure_network.h (renamed from src/yuzu/configuration/configure_service.h)10
-rw-r--r--src/yuzu/configuration/configure_network.ui (renamed from src/yuzu/configuration/configure_service.ui)63
-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/debugger/console.cpp11
-rw-r--r--src/yuzu/game_list.cpp4
-rw-r--r--src/yuzu/game_list_p.h13
-rw-r--r--src/yuzu/main.cpp34
-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.cpp15
-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.cpp26
167 files changed, 3609 insertions, 2681 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 857550e71..0e064ab44 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
@@ -376,7 +376,7 @@ if (ENABLE_SDL2)
376 if (YUZU_USE_BUNDLED_SDL2) 376 if (YUZU_USE_BUNDLED_SDL2)
377 # Detect toolchain and platform 377 # Detect toolchain and platform
378 if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1930) AND ARCHITECTURE_x86_64) 378 if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1930) AND ARCHITECTURE_x86_64)
379 set(SDL2_VER "SDL2-2.0.15-prerelease") 379 set(SDL2_VER "SDL2-2.0.16")
380 else() 380 else()
381 message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable YUZU_USE_BUNDLED_SDL2 and provide your own.") 381 message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable YUZU_USE_BUNDLED_SDL2 and provide your own.")
382 endif() 382 endif()
@@ -396,7 +396,7 @@ if (ENABLE_SDL2)
396 elseif (YUZU_USE_EXTERNAL_SDL2) 396 elseif (YUZU_USE_EXTERNAL_SDL2)
397 message(STATUS "Using SDL2 from externals.") 397 message(STATUS "Using SDL2 from externals.")
398 else() 398 else()
399 find_package(SDL2 2.0.15 REQUIRED) 399 find_package(SDL2 2.0.16 REQUIRED)
400 400
401 # Some installations don't set SDL2_LIBRARIES 401 # Some installations don't set SDL2_LIBRARIES
402 if("${SDL2_LIBRARIES}" STREQUAL "") 402 if("${SDL2_LIBRARIES}" STREQUAL "")
@@ -496,7 +496,7 @@ endif()
496# Ensure libusb is properly configured (based on dolphin libusb include) 496# Ensure libusb is properly configured (based on dolphin libusb include)
497if(NOT APPLE AND NOT YUZU_USE_BUNDLED_LIBUSB) 497if(NOT APPLE AND NOT YUZU_USE_BUNDLED_LIBUSB)
498 include(FindPkgConfig) 498 include(FindPkgConfig)
499 if (PKG_CONFIG_FOUND) 499 if (PKG_CONFIG_FOUND AND NOT CMAKE_SYSTEM_NAME MATCHES "DragonFly|FreeBSD")
500 pkg_check_modules(LIBUSB QUIET libusb-1.0>=1.0.24) 500 pkg_check_modules(LIBUSB QUIET libusb-1.0>=1.0.24)
501 else() 501 else()
502 find_package(LibUSB) 502 find_package(LibUSB)
@@ -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}")
@@ -668,7 +701,7 @@ if (APPLE)
668elseif (WIN32) 701elseif (WIN32)
669 # WSAPoll and SHGetKnownFolderPath (AppData/Roaming) didn't exist before WinNT 6.x (Vista) 702 # WSAPoll and SHGetKnownFolderPath (AppData/Roaming) didn't exist before WinNT 6.x (Vista)
670 add_definitions(-D_WIN32_WINNT=0x0600 -DWINVER=0x0600) 703 add_definitions(-D_WIN32_WINNT=0x0600 -DWINVER=0x0600)
671 set(PLATFORM_LIBRARIES winmm ws2_32) 704 set(PLATFORM_LIBRARIES winmm ws2_32 iphlpapi)
672 if (MINGW) 705 if (MINGW)
673 # PSAPI is the Process Status API 706 # PSAPI is the Process Status API
674 set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} psapi imm32 version) 707 set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} psapi imm32 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/CMakeLists.txt b/externals/CMakeLists.txt
index 4b8d35548..0c2c059a9 100644
--- a/externals/CMakeLists.txt
+++ b/externals/CMakeLists.txt
@@ -7,7 +7,9 @@ include(DownloadExternals)
7# xbyak 7# xbyak
8if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64) 8if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64)
9 add_library(xbyak INTERFACE) 9 add_library(xbyak INTERFACE)
10 target_include_directories(xbyak SYSTEM INTERFACE ./xbyak/xbyak) 10 file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include)
11 file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/xbyak/xbyak DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include)
12 target_include_directories(xbyak SYSTEM INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include)
11 target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES) 13 target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES)
12endif() 14endif()
13 15
@@ -19,6 +21,7 @@ target_include_directories(catch-single-include INTERFACE catch/single_include)
19if (ARCHITECTURE_x86_64) 21if (ARCHITECTURE_x86_64)
20 set(DYNARMIC_TESTS OFF) 22 set(DYNARMIC_TESTS OFF)
21 set(DYNARMIC_NO_BUNDLED_FMT ON) 23 set(DYNARMIC_NO_BUNDLED_FMT ON)
24 set(DYNARMIC_IGNORE_ASSERTS ON CACHE BOOL "" FORCE)
22 add_subdirectory(dynarmic) 25 add_subdirectory(dynarmic)
23endif() 26endif()
24 27
diff --git a/externals/SDL b/externals/SDL
Subproject 2f248a2a31c3323ecc37c00ad5e269e347ae392 Subproject 25f9ed87ff6947d9576fc9d79dee0784e638ac5
diff --git a/externals/dynarmic b/externals/dynarmic
Subproject 7946868af49d403fe54c92d2d60ef986513d1fe Subproject 517e35f845e010788b6febe42fd6ddb187b8c23
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/fs/fs_util.cpp b/src/common/fs/fs_util.cpp
index 357cf5855..9f8671982 100644
--- a/src/common/fs/fs_util.cpp
+++ b/src/common/fs/fs_util.cpp
@@ -20,6 +20,10 @@ std::string ToUTF8String(std::u8string_view u8_string) {
20 return std::string{u8_string.begin(), u8_string.end()}; 20 return std::string{u8_string.begin(), u8_string.end()};
21} 21}
22 22
23std::string BufferToUTF8String(std::span<const u8> buffer) {
24 return std::string{buffer.begin(), std::ranges::find(buffer, u8{0})};
25}
26
23std::string PathToUTF8String(const std::filesystem::path& path) { 27std::string PathToUTF8String(const std::filesystem::path& path) {
24 return ToUTF8String(path.u8string()); 28 return ToUTF8String(path.u8string());
25} 29}
diff --git a/src/common/fs/fs_util.h b/src/common/fs/fs_util.h
index ec9950ee7..1ec82eb35 100644
--- a/src/common/fs/fs_util.h
+++ b/src/common/fs/fs_util.h
@@ -47,6 +47,17 @@ concept IsChar = std::same_as<T, char>;
47[[nodiscard]] std::string ToUTF8String(std::u8string_view u8_string); 47[[nodiscard]] std::string ToUTF8String(std::u8string_view u8_string);
48 48
49/** 49/**
50 * Converts a buffer of bytes to a UTF8-encoded std::string.
51 * This converts from the start of the buffer until the first encountered null-terminator.
52 * If no null-terminator is found, this converts the entire buffer instead.
53 *
54 * @param buffer Buffer of bytes
55 *
56 * @returns UTF-8 encoded std::string.
57 */
58[[nodiscard]] std::string BufferToUTF8String(std::span<const u8> buffer);
59
60/**
50 * Converts a filesystem path to a UTF-8 encoded std::string. 61 * Converts a filesystem path to a UTF-8 encoded std::string.
51 * 62 *
52 * @param path Filesystem path 63 * @param path Filesystem path
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/host_memory.cpp b/src/common/host_memory.cpp
index 2a5a7596c..6661244cf 100644
--- a/src/common/host_memory.cpp
+++ b/src/common/host_memory.cpp
@@ -6,7 +6,7 @@
6#include <windows.h> 6#include <windows.h>
7#include "common/dynamic_library.h" 7#include "common/dynamic_library.h"
8 8
9#elif defined(__linux__) // ^^^ Windows ^^^ vvv Linux vvv 9#elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv
10 10
11#ifndef _GNU_SOURCE 11#ifndef _GNU_SOURCE
12#define _GNU_SOURCE 12#define _GNU_SOURCE
@@ -343,7 +343,7 @@ private:
343 std::unordered_map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset 343 std::unordered_map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset
344}; 344};
345 345
346#elif defined(__linux__) // ^^^ Windows ^^^ vvv Linux vvv 346#elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv
347 347
348class HostMemory::Impl { 348class HostMemory::Impl {
349public: 349public:
@@ -357,7 +357,12 @@ public:
357 }); 357 });
358 358
359 // Backing memory initialization 359 // Backing memory initialization
360#if defined(__FreeBSD__) && __FreeBSD__ < 13
361 // XXX Drop after FreeBSD 12.* reaches EOL on 2024-06-30
362 fd = shm_open(SHM_ANON, O_RDWR, 0600);
363#else
360 fd = memfd_create("HostMemory", 0); 364 fd = memfd_create("HostMemory", 0);
365#endif
361 if (fd == -1) { 366 if (fd == -1) {
362 LOG_CRITICAL(HW_Memory, "memfd_create failed: {}", strerror(errno)); 367 LOG_CRITICAL(HW_Memory, "memfd_create failed: {}", strerror(errno));
363 throw std::bad_alloc{}; 368 throw std::bad_alloc{};
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 61dddab3f..13edda9c9 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -2,13 +2,9 @@
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 <algorithm>
6#include <atomic> 5#include <atomic>
7#include <chrono> 6#include <chrono>
8#include <climits> 7#include <climits>
9#include <condition_variable>
10#include <memory>
11#include <mutex>
12#include <thread> 8#include <thread>
13#include <vector> 9#include <vector>
14 10
@@ -16,104 +12,229 @@
16#include <windows.h> // For OutputDebugStringW 12#include <windows.h> // For OutputDebugStringW
17#endif 13#endif
18 14
19#include "common/assert.h"
20#include "common/fs/file.h" 15#include "common/fs/file.h"
21#include "common/fs/fs.h" 16#include "common/fs/fs.h"
17#include "common/fs/fs_paths.h"
18#include "common/fs/path_util.h"
22#include "common/literals.h" 19#include "common/literals.h"
23 20
24#include "common/logging/backend.h" 21#include "common/logging/backend.h"
25#include "common/logging/log.h" 22#include "common/logging/log.h"
26#include "common/logging/text_formatter.h" 23#include "common/logging/text_formatter.h"
27#include "common/settings.h" 24#include "common/settings.h"
25#ifdef _WIN32
28#include "common/string_util.h" 26#include "common/string_util.h"
27#endif
29#include "common/threadsafe_queue.h" 28#include "common/threadsafe_queue.h"
30 29
31namespace Common::Log { 30namespace Common::Log {
32 31
32namespace {
33
33/** 34/**
34 * Static state as a singleton. 35 * Interface for logging backends.
35 */ 36 */
36class Impl { 37class Backend {
37public: 38public:
38 static Impl& Instance() { 39 virtual ~Backend() = default;
39 static Impl backend; 40
40 return backend; 41 virtual void Write(const Entry& entry) = 0;
42
43 virtual void EnableForStacktrace() = 0;
44
45 virtual void Flush() = 0;
46};
47
48/**
49 * Backend that writes to stderr and with color
50 */
51class ColorConsoleBackend final : public Backend {
52public:
53 explicit ColorConsoleBackend() = default;
54
55 ~ColorConsoleBackend() override = default;
56
57 void Write(const Entry& entry) override {
58 if (enabled.load(std::memory_order_relaxed)) {
59 PrintColoredMessage(entry);
60 }
41 } 61 }
42 62
43 Impl(const Impl&) = delete; 63 void Flush() override {
44 Impl& operator=(const Impl&) = delete; 64 // stderr shouldn't be buffered
65 }
45 66
46 Impl(Impl&&) = delete; 67 void EnableForStacktrace() override {
47 Impl& operator=(Impl&&) = delete; 68 enabled = true;
69 }
48 70
49 void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num, 71 void SetEnabled(bool enabled_) {
50 const char* function, std::string message) { 72 enabled = enabled_;
51 message_queue.Push( 73 }
52 CreateEntry(log_class, log_level, filename, line_num, function, std::move(message))); 74
75private:
76 std::atomic_bool enabled{false};
77};
78
79/**
80 * Backend that writes to a file passed into the constructor
81 */
82class FileBackend final : public Backend {
83public:
84 explicit FileBackend(const std::filesystem::path& filename) {
85 auto old_filename = filename;
86 old_filename += ".old.txt";
87
88 // Existence checks are done within the functions themselves.
89 // We don't particularly care if these succeed or not.
90 static_cast<void>(FS::RemoveFile(old_filename));
91 static_cast<void>(FS::RenameFile(filename, old_filename));
92
93 file = std::make_unique<FS::IOFile>(filename, FS::FileAccessMode::Write,
94 FS::FileType::TextFile);
95 }
96
97 ~FileBackend() override = default;
98
99 void Write(const Entry& entry) override {
100 if (!enabled) {
101 return;
102 }
103
104 bytes_written += file->WriteString(FormatLogMessage(entry).append(1, '\n'));
105
106 using namespace Common::Literals;
107 // Prevent logs from exceeding a set maximum size in the event that log entries are spammed.
108 const auto write_limit = Settings::values.extended_logging ? 1_GiB : 100_MiB;
109 const bool write_limit_exceeded = bytes_written > write_limit;
110 if (entry.log_level >= Level::Error || write_limit_exceeded) {
111 if (write_limit_exceeded) {
112 // Stop writing after the write limit is exceeded.
113 // Don't close the file so we can print a stacktrace if necessary
114 enabled = false;
115 }
116 file->Flush();
117 }
118 }
119
120 void Flush() override {
121 file->Flush();
122 }
123
124 void EnableForStacktrace() override {
125 enabled = true;
126 bytes_written = 0;
53 } 127 }
54 128
55 void AddBackend(std::unique_ptr<Backend> backend) { 129private:
56 std::lock_guard lock{writing_mutex}; 130 std::unique_ptr<FS::IOFile> file;
57 backends.push_back(std::move(backend)); 131 bool enabled = true;
132 std::size_t bytes_written = 0;
133};
134
135/**
136 * Backend that writes to Visual Studio's output window
137 */
138class DebuggerBackend final : public Backend {
139public:
140 explicit DebuggerBackend() = default;
141
142 ~DebuggerBackend() override = default;
143
144 void Write(const Entry& entry) override {
145#ifdef _WIN32
146 ::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str());
147#endif
58 } 148 }
59 149
60 void RemoveBackend(std::string_view backend_name) { 150 void Flush() override {}
61 std::lock_guard lock{writing_mutex}; 151
152 void EnableForStacktrace() override {}
153};
154
155bool initialization_in_progress_suppress_logging = false;
62 156
63 std::erase_if(backends, [&backend_name](const auto& backend) { 157/**
64 return backend_name == backend->GetName(); 158 * Static state as a singleton.
65 }); 159 */
160class Impl {
161public:
162 static Impl& Instance() {
163 if (!instance) {
164 abort();
165 }
166 return *instance;
66 } 167 }
67 168
68 const Filter& GetGlobalFilter() const { 169 static void Initialize() {
69 return filter; 170 if (instance) {
171 abort();
172 }
173 using namespace Common::FS;
174 initialization_in_progress_suppress_logging = true;
175 const auto& log_dir = GetYuzuPath(YuzuPath::LogDir);
176 void(CreateDir(log_dir));
177 Filter filter;
178 filter.ParseFilterString(Settings::values.log_filter.GetValue());
179 instance = std::unique_ptr<Impl, decltype(&Deleter)>(new Impl(log_dir / LOG_FILE, filter),
180 Deleter);
181 initialization_in_progress_suppress_logging = false;
70 } 182 }
71 183
184 Impl(const Impl&) = delete;
185 Impl& operator=(const Impl&) = delete;
186
187 Impl(Impl&&) = delete;
188 Impl& operator=(Impl&&) = delete;
189
72 void SetGlobalFilter(const Filter& f) { 190 void SetGlobalFilter(const Filter& f) {
73 filter = f; 191 filter = f;
74 } 192 }
75 193
76 Backend* GetBackend(std::string_view backend_name) { 194 void SetColorConsoleBackendEnabled(bool enabled) {
77 const auto it = 195 color_console_backend.SetEnabled(enabled);
78 std::find_if(backends.begin(), backends.end(), 196 }
79 [&backend_name](const auto& i) { return backend_name == i->GetName(); }); 197
80 if (it == backends.end()) 198 void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num,
81 return nullptr; 199 const char* function, std::string message) {
82 return it->get(); 200 if (!filter.CheckMessage(log_class, log_level))
201 return;
202 const Entry& entry =
203 CreateEntry(log_class, log_level, filename, line_num, function, std::move(message));
204 message_queue.Push(entry);
83 } 205 }
84 206
85private: 207private:
86 Impl() { 208 Impl(const std::filesystem::path& file_backend_filename, const Filter& filter_)
87 backend_thread = std::thread([&] { 209 : filter{filter_}, file_backend{file_backend_filename}, backend_thread{std::thread([this] {
88 Entry entry; 210 Common::SetCurrentThreadName("yuzu:Log");
89 auto write_logs = [&](Entry& e) { 211 Entry entry;
90 std::lock_guard lock{writing_mutex}; 212 const auto write_logs = [this, &entry]() {
91 for (const auto& backend : backends) { 213 ForEachBackend([&entry](Backend& backend) { backend.Write(entry); });
92 backend->Write(e); 214 };
93 } 215 while (true) {
94 }; 216 entry = message_queue.PopWait();
95 while (true) { 217 if (entry.final_entry) {
96 entry = message_queue.PopWait(); 218 break;
97 if (entry.final_entry) { 219 }
98 break; 220 write_logs();
99 } 221 }
100 write_logs(entry); 222 // Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a
101 } 223 // case where a system is repeatedly spamming logs even on close.
224 int max_logs_to_write = filter.IsDebug() ? INT_MAX : 100;
225 while (max_logs_to_write-- && message_queue.Pop(entry)) {
226 write_logs();
227 }
228 })} {}
102 229
103 // Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a 230 ~Impl() {
104 // case where a system is repeatedly spamming logs even on close. 231 StopBackendThread();
105 const int MAX_LOGS_TO_WRITE = filter.IsDebug() ? INT_MAX : 100;
106 int logs_written = 0;
107 while (logs_written++ < MAX_LOGS_TO_WRITE && message_queue.Pop(entry)) {
108 write_logs(entry);
109 }
110 });
111 } 232 }
112 233
113 ~Impl() { 234 void StopBackendThread() {
114 Entry entry; 235 Entry stop_entry{};
115 entry.final_entry = true; 236 stop_entry.final_entry = true;
116 message_queue.Push(entry); 237 message_queue.Push(stop_entry);
117 backend_thread.join(); 238 backend_thread.join();
118 } 239 }
119 240
@@ -135,100 +256,51 @@ private:
135 }; 256 };
136 } 257 }
137 258
138 std::mutex writing_mutex; 259 void ForEachBackend(auto lambda) {
139 std::thread backend_thread; 260 lambda(static_cast<Backend&>(debugger_backend));
140 std::vector<std::unique_ptr<Backend>> backends; 261 lambda(static_cast<Backend&>(color_console_backend));
141 MPSCQueue<Entry> message_queue; 262 lambda(static_cast<Backend&>(file_backend));
142 Filter filter; 263 }
143 std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()};
144};
145
146ConsoleBackend::~ConsoleBackend() = default;
147
148void ConsoleBackend::Write(const Entry& entry) {
149 PrintMessage(entry);
150}
151
152ColorConsoleBackend::~ColorConsoleBackend() = default;
153
154void ColorConsoleBackend::Write(const Entry& entry) {
155 PrintColoredMessage(entry);
156}
157
158FileBackend::FileBackend(const std::filesystem::path& filename) {
159 auto old_filename = filename;
160 old_filename += ".old.txt";
161
162 // Existence checks are done within the functions themselves.
163 // We don't particularly care if these succeed or not.
164 FS::RemoveFile(old_filename);
165 void(FS::RenameFile(filename, old_filename));
166
167 file =
168 std::make_unique<FS::IOFile>(filename, FS::FileAccessMode::Write, FS::FileType::TextFile);
169}
170
171FileBackend::~FileBackend() = default;
172 264
173void FileBackend::Write(const Entry& entry) { 265 static void Deleter(Impl* ptr) {
174 if (!file->IsOpen()) { 266 delete ptr;
175 return;
176 } 267 }
177 268
178 using namespace Common::Literals; 269 static inline std::unique_ptr<Impl, decltype(&Deleter)> instance{nullptr, Deleter};
179 // Prevent logs from exceeding a set maximum size in the event that log entries are spammed.
180 constexpr std::size_t MAX_BYTES_WRITTEN = 100_MiB;
181 constexpr std::size_t MAX_BYTES_WRITTEN_EXTENDED = 1_GiB;
182 270
183 const bool write_limit_exceeded = 271 Filter filter;
184 bytes_written > MAX_BYTES_WRITTEN_EXTENDED || 272 DebuggerBackend debugger_backend{};
185 (bytes_written > MAX_BYTES_WRITTEN && !Settings::values.extended_logging); 273 ColorConsoleBackend color_console_backend{};
274 FileBackend file_backend;
186 275
187 // Close the file after the write limit is exceeded. 276 std::thread backend_thread;
188 if (write_limit_exceeded) { 277 MPSCQueue<Entry> message_queue{};
189 file->Close(); 278 std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()};
190 return; 279};
191 } 280} // namespace
192 281
193 bytes_written += file->WriteString(FormatLogMessage(entry).append(1, '\n')); 282void Initialize() {
194 if (entry.log_level >= Level::Error) { 283 Impl::Initialize();
195 file->Flush();
196 }
197} 284}
198 285
199DebuggerBackend::~DebuggerBackend() = default; 286void DisableLoggingInTests() {
200 287 initialization_in_progress_suppress_logging = true;
201void DebuggerBackend::Write(const Entry& entry) {
202#ifdef _WIN32
203 ::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str());
204#endif
205} 288}
206 289
207void SetGlobalFilter(const Filter& filter) { 290void SetGlobalFilter(const Filter& filter) {
208 Impl::Instance().SetGlobalFilter(filter); 291 Impl::Instance().SetGlobalFilter(filter);
209} 292}
210 293
211void AddBackend(std::unique_ptr<Backend> backend) { 294void SetColorConsoleBackendEnabled(bool enabled) {
212 Impl::Instance().AddBackend(std::move(backend)); 295 Impl::Instance().SetColorConsoleBackendEnabled(enabled);
213}
214
215void RemoveBackend(std::string_view backend_name) {
216 Impl::Instance().RemoveBackend(backend_name);
217}
218
219Backend* GetBackend(std::string_view backend_name) {
220 return Impl::Instance().GetBackend(backend_name);
221} 296}
222 297
223void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, 298void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
224 unsigned int line_num, const char* function, const char* format, 299 unsigned int line_num, const char* function, const char* format,
225 const fmt::format_args& args) { 300 const fmt::format_args& args) {
226 auto& instance = Impl::Instance(); 301 if (!initialization_in_progress_suppress_logging) {
227 const auto& filter = instance.GetGlobalFilter(); 302 Impl::Instance().PushEntry(log_class, log_level, filename, line_num, function,
228 if (!filter.CheckMessage(log_class, log_level)) 303 fmt::vformat(format, args));
229 return; 304 }
230
231 instance.PushEntry(log_class, log_level, filename, line_num, function,
232 fmt::vformat(format, args));
233} 305}
234} // namespace Common::Log 306} // namespace Common::Log
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h
index 4b9a910c1..cb7839ee9 100644
--- a/src/common/logging/backend.h
+++ b/src/common/logging/backend.h
@@ -5,120 +5,21 @@
5#pragma once 5#pragma once
6 6
7#include <filesystem> 7#include <filesystem>
8#include <memory>
9#include <string>
10#include <string_view>
11#include "common/logging/filter.h" 8#include "common/logging/filter.h"
12#include "common/logging/log.h"
13
14namespace Common::FS {
15class IOFile;
16}
17 9
18namespace Common::Log { 10namespace Common::Log {
19 11
20class Filter; 12class Filter;
21 13
22/** 14/// Initializes the logging system. This should be the first thing called in main.
23 * Interface for logging backends. As loggers can be created and removed at runtime, this can be 15void Initialize();
24 * used by a frontend for adding a custom logging backend as needed
25 */
26class Backend {
27public:
28 virtual ~Backend() = default;
29
30 virtual void SetFilter(const Filter& new_filter) {
31 filter = new_filter;
32 }
33 virtual const char* GetName() const = 0;
34 virtual void Write(const Entry& entry) = 0;
35
36private:
37 Filter filter;
38};
39
40/**
41 * Backend that writes to stderr without any color commands
42 */
43class ConsoleBackend : public Backend {
44public:
45 ~ConsoleBackend() override;
46
47 static const char* Name() {
48 return "console";
49 }
50 const char* GetName() const override {
51 return Name();
52 }
53 void Write(const Entry& entry) override;
54};
55
56/**
57 * Backend that writes to stderr and with color
58 */
59class ColorConsoleBackend : public Backend {
60public:
61 ~ColorConsoleBackend() override;
62
63 static const char* Name() {
64 return "color_console";
65 }
66
67 const char* GetName() const override {
68 return Name();
69 }
70 void Write(const Entry& entry) override;
71};
72 16
73/** 17void DisableLoggingInTests();
74 * Backend that writes to a file passed into the constructor
75 */
76class FileBackend : public Backend {
77public:
78 explicit FileBackend(const std::filesystem::path& filename);
79 ~FileBackend() override;
80
81 static const char* Name() {
82 return "file";
83 }
84
85 const char* GetName() const override {
86 return Name();
87 }
88
89 void Write(const Entry& entry) override;
90
91private:
92 std::unique_ptr<FS::IOFile> file;
93 std::size_t bytes_written = 0;
94};
95
96/**
97 * Backend that writes to Visual Studio's output window
98 */
99class DebuggerBackend : public Backend {
100public:
101 ~DebuggerBackend() override;
102
103 static const char* Name() {
104 return "debugger";
105 }
106 const char* GetName() const override {
107 return Name();
108 }
109 void Write(const Entry& entry) override;
110};
111
112void AddBackend(std::unique_ptr<Backend> backend);
113
114void RemoveBackend(std::string_view backend_name);
115
116Backend* GetBackend(std::string_view backend_name);
117 18
118/** 19/**
119 * The global filter will prevent any messages from even being processed if they are filtered. Each 20 * The global filter will prevent any messages from even being processed if they are filtered.
120 * backend can have a filter, but if the level is lower than the global filter, the backend will
121 * never get the message
122 */ 21 */
123void SetGlobalFilter(const Filter& filter); 22void SetGlobalFilter(const Filter& filter);
124} // namespace Common::Log \ No newline at end of file 23
24void SetColorConsoleBackendEnabled(bool enabled);
25} // namespace Common::Log
diff --git a/src/common/settings.h b/src/common/settings.h
index d8730f515..20769d310 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <algorithm>
7#include <array> 8#include <array>
8#include <atomic> 9#include <atomic>
9#include <chrono> 10#include <chrono>
@@ -42,6 +43,11 @@ enum class CPUAccuracy : u32 {
42 Unsafe = 2, 43 Unsafe = 2,
43}; 44};
44 45
46enum class FullscreenMode : u32 {
47 Borderless = 0,
48 Exclusive = 1,
49};
50
45/** The BasicSetting class is a simple resource manager. It defines a label and default value 51/** The BasicSetting class is a simple resource manager. It defines a label and default value
46 * alongside the actual value of the setting for simpler and less-error prone use with frontend 52 * alongside the actual value of the setting for simpler and less-error prone use with frontend
47 * configurations. Setting a default value and label is required, though subclasses may deviate from 53 * configurations. Setting a default value and label is required, though subclasses may deviate from
@@ -69,14 +75,14 @@ public:
69 */ 75 */
70 explicit BasicSetting(const Type& default_val, const std::string& name) 76 explicit BasicSetting(const Type& default_val, const std::string& name)
71 : default_value{default_val}, global{default_val}, label{name} {} 77 : default_value{default_val}, global{default_val}, label{name} {}
72 ~BasicSetting() = default; 78 virtual ~BasicSetting() = default;
73 79
74 /** 80 /**
75 * Returns a reference to the setting's value. 81 * Returns a reference to the setting's value.
76 * 82 *
77 * @returns A reference to the setting 83 * @returns A reference to the setting
78 */ 84 */
79 [[nodiscard]] const Type& GetValue() const { 85 [[nodiscard]] virtual const Type& GetValue() const {
80 return global; 86 return global;
81 } 87 }
82 88
@@ -85,7 +91,7 @@ public:
85 * 91 *
86 * @param value The desired value 92 * @param value The desired value
87 */ 93 */
88 void SetValue(const Type& value) { 94 virtual void SetValue(const Type& value) {
89 Type temp{value}; 95 Type temp{value};
90 std::swap(global, temp); 96 std::swap(global, temp);
91 } 97 }
@@ -115,7 +121,7 @@ public:
115 * 121 *
116 * @returns A reference to the setting 122 * @returns A reference to the setting
117 */ 123 */
118 const Type& operator=(const Type& value) { 124 virtual const Type& operator=(const Type& value) {
119 Type temp{value}; 125 Type temp{value};
120 std::swap(global, temp); 126 std::swap(global, temp);
121 return global; 127 return global;
@@ -126,7 +132,7 @@ public:
126 * 132 *
127 * @returns A reference to the setting 133 * @returns A reference to the setting
128 */ 134 */
129 explicit operator const Type&() const { 135 explicit virtual operator const Type&() const {
130 return global; 136 return global;
131 } 137 }
132 138
@@ -137,6 +143,51 @@ protected:
137}; 143};
138 144
139/** 145/**
146 * BasicRangedSetting class is intended for use with quantifiable settings that need a more
147 * restrictive range than implicitly defined by its type. Implements a minimum and maximum that is
148 * simply used to sanitize SetValue and the assignment overload.
149 */
150template <typename Type>
151class BasicRangedSetting : virtual public BasicSetting<Type> {
152public:
153 /**
154 * Sets a default value, minimum value, maximum value, and label.
155 *
156 * @param default_val Intial value of the setting, and default value of the setting
157 * @param min_val Sets the minimum allowed value of the setting
158 * @param max_val Sets the maximum allowed value of the setting
159 * @param name Label for the setting
160 */
161 explicit BasicRangedSetting(const Type& default_val, const Type& min_val, const Type& max_val,
162 const std::string& name)
163 : BasicSetting<Type>{default_val, name}, minimum{min_val}, maximum{max_val} {}
164 virtual ~BasicRangedSetting() = default;
165
166 /**
167 * Like BasicSetting's SetValue, except value is clamped to the range of the setting.
168 *
169 * @param value The desired value
170 */
171 void SetValue(const Type& value) override {
172 this->global = std::clamp(value, minimum, maximum);
173 }
174
175 /**
176 * Like BasicSetting's assignment overload, except value is clamped to the range of the setting.
177 *
178 * @param value The desired value
179 * @returns A reference to the setting's value
180 */
181 const Type& operator=(const Type& value) override {
182 this->global = std::clamp(value, minimum, maximum);
183 return this->global;
184 }
185
186 const Type minimum; ///< Minimum allowed value of the setting
187 const Type maximum; ///< Maximum allowed value of the setting
188};
189
190/**
140 * The Setting class is a slightly more complex version of the BasicSetting class. This adds a 191 * The Setting class is a slightly more complex version of the BasicSetting class. This adds a
141 * custom setting to switch to when a guest application specifically requires it. The effect is that 192 * custom setting to switch to when a guest application specifically requires it. The effect is that
142 * other components of the emulator can access the setting's intended value without any need for the 193 * other components of the emulator can access the setting's intended value without any need for the
@@ -147,7 +198,7 @@ protected:
147 * Like the BasicSetting, this requires setting a default value and label to use. 198 * Like the BasicSetting, this requires setting a default value and label to use.
148 */ 199 */
149template <typename Type> 200template <typename Type>
150class Setting final : public BasicSetting<Type> { 201class Setting : virtual public BasicSetting<Type> {
151public: 202public:
152 /** 203 /**
153 * Sets a default value, label, and setting value. 204 * Sets a default value, label, and setting value.
@@ -157,7 +208,7 @@ public:
157 */ 208 */
158 explicit Setting(const Type& default_val, const std::string& name) 209 explicit Setting(const Type& default_val, const std::string& name)
159 : BasicSetting<Type>(default_val, name) {} 210 : BasicSetting<Type>(default_val, name) {}
160 ~Setting() = default; 211 virtual ~Setting() = default;
161 212
162 /** 213 /**
163 * Tells this setting to represent either the global or custom setting when other member 214 * Tells this setting to represent either the global or custom setting when other member
@@ -186,7 +237,13 @@ public:
186 * 237 *
187 * @returns The required value of the setting 238 * @returns The required value of the setting
188 */ 239 */
189 [[nodiscard]] const Type& GetValue(bool need_global = false) const { 240 [[nodiscard]] virtual const Type& GetValue() const override {
241 if (use_global) {
242 return this->global;
243 }
244 return custom;
245 }
246 [[nodiscard]] virtual const Type& GetValue(bool need_global) const {
190 if (use_global || need_global) { 247 if (use_global || need_global) {
191 return this->global; 248 return this->global;
192 } 249 }
@@ -198,7 +255,7 @@ public:
198 * 255 *
199 * @param value The new value 256 * @param value The new value
200 */ 257 */
201 void SetValue(const Type& value) { 258 void SetValue(const Type& value) override {
202 Type temp{value}; 259 Type temp{value};
203 if (use_global) { 260 if (use_global) {
204 std::swap(this->global, temp); 261 std::swap(this->global, temp);
@@ -214,7 +271,7 @@ public:
214 * 271 *
215 * @returns A reference to the current setting value 272 * @returns A reference to the current setting value
216 */ 273 */
217 const Type& operator=(const Type& value) { 274 const Type& operator=(const Type& value) override {
218 Type temp{value}; 275 Type temp{value};
219 if (use_global) { 276 if (use_global) {
220 std::swap(this->global, temp); 277 std::swap(this->global, temp);
@@ -229,19 +286,88 @@ public:
229 * 286 *
230 * @returns A reference to the current setting value 287 * @returns A reference to the current setting value
231 */ 288 */
232 explicit operator const Type&() const { 289 virtual explicit operator const Type&() const override {
233 if (use_global) { 290 if (use_global) {
234 return this->global; 291 return this->global;
235 } 292 }
236 return custom; 293 return custom;
237 } 294 }
238 295
239private: 296protected:
240 bool use_global{true}; ///< The setting's global state 297 bool use_global{true}; ///< The setting's global state
241 Type custom{}; ///< The custom value of the setting 298 Type custom{}; ///< The custom value of the setting
242}; 299};
243 300
244/** 301/**
302 * RangedSetting is a Setting that implements a maximum and minimum value for its setting. Intended
303 * for use with quantifiable settings.
304 */
305template <typename Type>
306class RangedSetting final : public BasicRangedSetting<Type>, public Setting<Type> {
307public:
308 /**
309 * Sets a default value, minimum value, maximum value, and label.
310 *
311 * @param default_val Intial value of the setting, and default value of the setting
312 * @param min_val Sets the minimum allowed value of the setting
313 * @param max_val Sets the maximum allowed value of the setting
314 * @param name Label for the setting
315 */
316 explicit RangedSetting(const Type& default_val, const Type& min_val, const Type& max_val,
317 const std::string& name)
318 : BasicSetting<Type>{default_val, name},
319 BasicRangedSetting<Type>{default_val, min_val, max_val, name}, Setting<Type>{default_val,
320 name} {}
321 virtual ~RangedSetting() = default;
322
323 // The following are needed to avoid a MSVC bug
324 // (source: https://stackoverflow.com/questions/469508)
325 [[nodiscard]] const Type& GetValue() const override {
326 return Setting<Type>::GetValue();
327 }
328 [[nodiscard]] const Type& GetValue(bool need_global) const override {
329 return Setting<Type>::GetValue(need_global);
330 }
331 explicit operator const Type&() const override {
332 if (this->use_global) {
333 return this->global;
334 }
335 return this->custom;
336 }
337
338 /**
339 * Like BasicSetting's SetValue, except value is clamped to the range of the setting. Sets the
340 * appropriate value depending on the global state.
341 *
342 * @param value The desired value
343 */
344 void SetValue(const Type& value) override {
345 const Type temp = std::clamp(value, this->minimum, this->maximum);
346 if (this->use_global) {
347 this->global = temp;
348 }
349 this->custom = temp;
350 }
351
352 /**
353 * Like BasicSetting's assignment overload, except value is clamped to the range of the setting.
354 * Uses the appropriate value depending on the global state.
355 *
356 * @param value The desired value
357 * @returns A reference to the setting's value
358 */
359 const Type& operator=(const Type& value) override {
360 const Type temp = std::clamp(value, this->minimum, this->maximum);
361 if (this->use_global) {
362 this->global = temp;
363 return this->global;
364 }
365 this->custom = temp;
366 return this->custom;
367 }
368};
369
370/**
245 * The InputSetting class allows for getting a reference to either the global or custom members. 371 * The InputSetting class allows for getting a reference to either the global or custom members.
246 * This is required as we cannot easily modify the values of user-defined types within containers 372 * This is required as we cannot easily modify the values of user-defined types within containers
247 * using the SetValue() member function found in the Setting class. The primary purpose of this 373 * using the SetValue() member function found in the Setting class. The primary purpose of this
@@ -284,13 +410,14 @@ struct Values {
284 BasicSetting<std::string> sink_id{"auto", "output_engine"}; 410 BasicSetting<std::string> sink_id{"auto", "output_engine"};
285 BasicSetting<bool> audio_muted{false, "audio_muted"}; 411 BasicSetting<bool> audio_muted{false, "audio_muted"};
286 Setting<bool> enable_audio_stretching{true, "enable_audio_stretching"}; 412 Setting<bool> enable_audio_stretching{true, "enable_audio_stretching"};
287 Setting<u8> volume{100, "volume"}; 413 RangedSetting<u8> volume{100, 0, 100, "volume"};
288 414
289 // Core 415 // Core
290 Setting<bool> use_multi_core{true, "use_multi_core"}; 416 Setting<bool> use_multi_core{true, "use_multi_core"};
291 417
292 // Cpu 418 // Cpu
293 Setting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, "cpu_accuracy"}; 419 RangedSetting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto,
420 CPUAccuracy::Unsafe, "cpu_accuracy"};
294 // TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021 421 // TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021
295 BasicSetting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"}; 422 BasicSetting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"};
296 BasicSetting<bool> cpu_debug_mode{false, "cpu_debug_mode"}; 423 BasicSetting<bool> cpu_debug_mode{false, "cpu_debug_mode"};
@@ -312,8 +439,10 @@ struct Values {
312 Setting<bool> cpuopt_unsafe_fastmem_check{true, "cpuopt_unsafe_fastmem_check"}; 439 Setting<bool> cpuopt_unsafe_fastmem_check{true, "cpuopt_unsafe_fastmem_check"};
313 440
314 // Renderer 441 // Renderer
315 Setting<RendererBackend> renderer_backend{RendererBackend::OpenGL, "backend"}; 442 RangedSetting<RendererBackend> renderer_backend{
443 RendererBackend::OpenGL, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"};
316 BasicSetting<bool> renderer_debug{false, "debug"}; 444 BasicSetting<bool> renderer_debug{false, "debug"};
445 BasicSetting<bool> renderer_shader_feedback{false, "shader_feedback"};
317 BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"}; 446 BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"};
318 BasicSetting<bool> disable_shader_loop_safety_checks{false, 447 BasicSetting<bool> disable_shader_loop_safety_checks{false,
319 "disable_shader_loop_safety_checks"}; 448 "disable_shader_loop_safety_checks"};
@@ -322,26 +451,28 @@ struct Values {
322 Setting<u16> resolution_factor{1, "resolution_factor"}; 451 Setting<u16> resolution_factor{1, "resolution_factor"};
323 // *nix platforms may have issues with the borderless windowed fullscreen mode. 452 // *nix platforms may have issues with the borderless windowed fullscreen mode.
324 // Default to exclusive fullscreen on these platforms for now. 453 // Default to exclusive fullscreen on these platforms for now.
325 Setting<int> fullscreen_mode{ 454 RangedSetting<FullscreenMode> fullscreen_mode{
326#ifdef _WIN32 455#ifdef _WIN32
327 0, 456 FullscreenMode::Borderless,
328#else 457#else
329 1, 458 FullscreenMode::Exclusive,
330#endif 459#endif
331 "fullscreen_mode"}; 460 FullscreenMode::Borderless, FullscreenMode::Exclusive, "fullscreen_mode"};
332 Setting<int> aspect_ratio{0, "aspect_ratio"}; 461 RangedSetting<int> aspect_ratio{0, 0, 3, "aspect_ratio"};
333 Setting<int> max_anisotropy{0, "max_anisotropy"}; 462 RangedSetting<int> max_anisotropy{0, 0, 4, "max_anisotropy"};
334 Setting<bool> use_speed_limit{true, "use_speed_limit"}; 463 Setting<bool> use_speed_limit{true, "use_speed_limit"};
335 Setting<u16> speed_limit{100, "speed_limit"}; 464 RangedSetting<u16> speed_limit{100, 0, 9999, "speed_limit"};
336 Setting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"}; 465 Setting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"};
337 Setting<GPUAccuracy> gpu_accuracy{GPUAccuracy::High, "gpu_accuracy"}; 466 RangedSetting<GPUAccuracy> gpu_accuracy{GPUAccuracy::High, GPUAccuracy::Normal,
467 GPUAccuracy::Extreme, "gpu_accuracy"};
338 Setting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"}; 468 Setting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"};
339 Setting<bool> use_nvdec_emulation{true, "use_nvdec_emulation"}; 469 Setting<bool> use_nvdec_emulation{true, "use_nvdec_emulation"};
340 Setting<bool> accelerate_astc{true, "accelerate_astc"}; 470 Setting<bool> accelerate_astc{true, "accelerate_astc"};
341 Setting<bool> use_vsync{true, "use_vsync"}; 471 Setting<bool> use_vsync{true, "use_vsync"};
342 BasicSetting<u16> fps_cap{1000, "fps_cap"}; 472 BasicRangedSetting<u16> fps_cap{1000, 1, 1000, "fps_cap"};
343 BasicSetting<bool> disable_fps_limit{false, "disable_fps_limit"}; 473 BasicSetting<bool> disable_fps_limit{false, "disable_fps_limit"};
344 Setting<ShaderBackend> shader_backend{ShaderBackend::GLASM, "shader_backend"}; 474 RangedSetting<ShaderBackend> shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL,
475 ShaderBackend::SPIRV, "shader_backend"};
345 Setting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"}; 476 Setting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"};
346 Setting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"}; 477 Setting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"};
347 Setting<bool> use_caches_gc{false, "use_caches_gc"}; 478 Setting<bool> use_caches_gc{false, "use_caches_gc"};
@@ -358,10 +489,10 @@ struct Values {
358 std::chrono::seconds custom_rtc_differential; 489 std::chrono::seconds custom_rtc_differential;
359 490
360 BasicSetting<s32> current_user{0, "current_user"}; 491 BasicSetting<s32> current_user{0, "current_user"};
361 Setting<s32> language_index{1, "language_index"}; 492 RangedSetting<s32> language_index{1, 0, 17, "language_index"};
362 Setting<s32> region_index{1, "region_index"}; 493 RangedSetting<s32> region_index{1, 0, 6, "region_index"};
363 Setting<s32> time_zone_index{0, "time_zone_index"}; 494 RangedSetting<s32> time_zone_index{0, 0, 45, "time_zone_index"};
364 Setting<s32> sound_index{1, "sound_index"}; 495 RangedSetting<s32> sound_index{1, 0, 2, "sound_index"};
365 496
366 // Controls 497 // Controls
367 InputSetting<std::array<PlayerInput, 10>> players; 498 InputSetting<std::array<PlayerInput, 10>> players;
@@ -378,7 +509,7 @@ struct Values {
378 "udp_input_servers"}; 509 "udp_input_servers"};
379 510
380 BasicSetting<bool> mouse_panning{false, "mouse_panning"}; 511 BasicSetting<bool> mouse_panning{false, "mouse_panning"};
381 BasicSetting<u8> mouse_panning_sensitivity{10, "mouse_panning_sensitivity"}; 512 BasicRangedSetting<u8> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"};
382 BasicSetting<bool> mouse_enabled{false, "mouse_enabled"}; 513 BasicSetting<bool> mouse_enabled{false, "mouse_enabled"};
383 std::string mouse_device; 514 std::string mouse_device;
384 MouseButtonsRaw mouse_buttons; 515 MouseButtonsRaw mouse_buttons;
@@ -427,9 +558,10 @@ struct Values {
427 BasicSetting<std::string> log_filter{"*:Info", "log_filter"}; 558 BasicSetting<std::string> log_filter{"*:Info", "log_filter"};
428 BasicSetting<bool> use_dev_keys{false, "use_dev_keys"}; 559 BasicSetting<bool> use_dev_keys{false, "use_dev_keys"};
429 560
430 // Services 561 // Network
431 BasicSetting<std::string> bcat_backend{"none", "bcat_backend"}; 562 BasicSetting<std::string> bcat_backend{"none", "bcat_backend"};
432 BasicSetting<bool> bcat_boxcat_local{false, "bcat_boxcat_local"}; 563 BasicSetting<bool> bcat_boxcat_local{false, "bcat_boxcat_local"};
564 BasicSetting<std::string> network_interface{std::string(), "network_interface"};
433 565
434 // WebService 566 // WebService
435 BasicSetting<bool> enable_telemetry{true, "enable_telemetry"}; 567 BasicSetting<bool> enable_telemetry{true, "enable_telemetry"};
diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h
index ad04df8ca..8430b9778 100644
--- a/src/common/threadsafe_queue.h
+++ b/src/common/threadsafe_queue.h
@@ -46,15 +46,13 @@ public:
46 ElementPtr* new_ptr = new ElementPtr(); 46 ElementPtr* new_ptr = new ElementPtr();
47 write_ptr->next.store(new_ptr, std::memory_order_release); 47 write_ptr->next.store(new_ptr, std::memory_order_release);
48 write_ptr = new_ptr; 48 write_ptr = new_ptr;
49 ++size;
49 50
50 const size_t previous_size{size++}; 51 // cv_mutex must be held or else there will be a missed wakeup if the other thread is in the
51 52 // line before cv.wait
52 // Acquire the mutex and then immediately release it as a fence.
53 // TODO(bunnei): This can be replaced with C++20 waitable atomics when properly supported. 53 // TODO(bunnei): This can be replaced with C++20 waitable atomics when properly supported.
54 // See discussion on https://github.com/yuzu-emu/yuzu/pull/3173 for details. 54 // See discussion on https://github.com/yuzu-emu/yuzu/pull/3173 for details.
55 if (previous_size == 0) { 55 std::lock_guard lock{cv_mutex};
56 std::lock_guard lock{cv_mutex};
57 }
58 cv.notify_one(); 56 cv.notify_one();
59 } 57 }
60 58
diff --git a/src/common/uuid.cpp b/src/common/uuid.cpp
index 26db03fba..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());
@@ -18,7 +72,7 @@ UUID UUID::Generate() {
18} 72}
19 73
20std::string UUID::Format() const { 74std::string UUID::Format() const {
21 return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]); 75 return fmt::format("{:016x}{:016x}", uuid[1], uuid[0]);
22} 76}
23 77
24std::string UUID::FormatSwitch() const { 78std::string UUID::FormatSwitch() const {
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/common/x64/xbyak_abi.h b/src/common/x64/xbyak_abi.h
index c2c9b6134..0ddf9b83e 100644
--- a/src/common/x64/xbyak_abi.h
+++ b/src/common/x64/xbyak_abi.h
@@ -6,7 +6,7 @@
6 6
7#include <bitset> 7#include <bitset>
8#include <initializer_list> 8#include <initializer_list>
9#include <xbyak.h> 9#include <xbyak/xbyak.h>
10#include "common/assert.h" 10#include "common/assert.h"
11 11
12namespace Common::X64 { 12namespace Common::X64 {
diff --git a/src/common/x64/xbyak_util.h b/src/common/x64/xbyak_util.h
index df17f8cbe..44d2558f1 100644
--- a/src/common/x64/xbyak_util.h
+++ b/src/common/x64/xbyak_util.h
@@ -5,7 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <type_traits> 7#include <type_traits>
8#include <xbyak.h> 8#include <xbyak/xbyak.h>
9#include "common/x64/xbyak_abi.h" 9#include "common/x64/xbyak_abi.h"
10 10
11namespace Common::X64 { 11namespace Common::X64 {
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 5c99c00f5..f5cf5c16a 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -636,6 +636,8 @@ add_library(core STATIC
636 memory.h 636 memory.h
637 network/network.cpp 637 network/network.cpp
638 network/network.h 638 network/network.h
639 network/network_interface.cpp
640 network/network_interface.h
639 network/sockets.h 641 network/sockets.h
640 perf_stats.cpp 642 perf_stats.cpp
641 perf_stats.h 643 perf_stats.h
diff --git a/src/core/core.cpp b/src/core/core.cpp
index d3e84c4ef..b0dc594d4 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -84,8 +84,6 @@ FileSys::StorageId GetStorageIdForFrontendSlot(
84 84
85} // Anonymous namespace 85} // Anonymous namespace
86 86
87/*static*/ System System::s_instance;
88
89FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, 87FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
90 const std::string& path) { 88 const std::string& path) {
91 // To account for split 00+01+etc files. 89 // To account for split 00+01+etc files.
@@ -425,6 +423,13 @@ struct System::Impl {
425System::System() : impl{std::make_unique<Impl>(*this)} {} 423System::System() : impl{std::make_unique<Impl>(*this)} {}
426System::~System() = default; 424System::~System() = default;
427 425
426void System::InitializeGlobalInstance() {
427 if (s_instance) {
428 abort();
429 }
430 s_instance = std::unique_ptr<System>(new System);
431}
432
428CpuManager& System::GetCpuManager() { 433CpuManager& System::GetCpuManager() {
429 return impl->cpu_manager; 434 return impl->cpu_manager;
430} 435}
@@ -494,12 +499,6 @@ const ARM_Interface& System::CurrentArmInterface() const {
494 return impl->kernel.CurrentPhysicalCore().ArmInterface(); 499 return impl->kernel.CurrentPhysicalCore().ArmInterface();
495} 500}
496 501
497std::size_t System::CurrentCoreIndex() const {
498 std::size_t core = impl->kernel.GetCurrentHostThreadID();
499 ASSERT(core < Core::Hardware::NUM_CPU_CORES);
500 return core;
501}
502
503Kernel::PhysicalCore& System::CurrentPhysicalCore() { 502Kernel::PhysicalCore& System::CurrentPhysicalCore() {
504 return impl->kernel.CurrentPhysicalCore(); 503 return impl->kernel.CurrentPhysicalCore();
505} 504}
diff --git a/src/core/core.h b/src/core/core.h
index ea143043c..65b447a1c 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -121,9 +121,14 @@ public:
121 * @returns Reference to the instance of the System singleton class. 121 * @returns Reference to the instance of the System singleton class.
122 */ 122 */
123 [[deprecated("Use of the global system instance is deprecated")]] static System& GetInstance() { 123 [[deprecated("Use of the global system instance is deprecated")]] static System& GetInstance() {
124 return s_instance; 124 if (!s_instance) {
125 abort();
126 }
127 return *s_instance;
125 } 128 }
126 129
130 static void InitializeGlobalInstance();
131
127 /// Enumeration representing the return values of the System Initialize and Load process. 132 /// Enumeration representing the return values of the System Initialize and Load process.
128 enum class ResultStatus : u32 { 133 enum class ResultStatus : u32 {
129 Success, ///< Succeeded 134 Success, ///< Succeeded
@@ -205,9 +210,6 @@ public:
205 /// Gets an ARM interface to the CPU core that is currently running 210 /// Gets an ARM interface to the CPU core that is currently running
206 [[nodiscard]] const ARM_Interface& CurrentArmInterface() const; 211 [[nodiscard]] const ARM_Interface& CurrentArmInterface() const;
207 212
208 /// Gets the index of the currently running CPU core
209 [[nodiscard]] std::size_t CurrentCoreIndex() const;
210
211 /// Gets the physical core for the CPU core that is currently running 213 /// Gets the physical core for the CPU core that is currently running
212 [[nodiscard]] Kernel::PhysicalCore& CurrentPhysicalCore(); 214 [[nodiscard]] Kernel::PhysicalCore& CurrentPhysicalCore();
213 215
@@ -396,7 +398,7 @@ private:
396 struct Impl; 398 struct Impl;
397 std::unique_ptr<Impl> impl; 399 std::unique_ptr<Impl> impl;
398 400
399 static System s_instance; 401 inline static std::unique_ptr<System> s_instance{};
400}; 402};
401 403
402} // namespace Core 404} // namespace Core
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index 7e195346b..de2e5563e 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -21,34 +21,25 @@ namespace Core {
21CpuManager::CpuManager(System& system_) : system{system_} {} 21CpuManager::CpuManager(System& system_) : system{system_} {}
22CpuManager::~CpuManager() = default; 22CpuManager::~CpuManager() = default;
23 23
24void CpuManager::ThreadStart(CpuManager& cpu_manager, std::size_t core) { 24void CpuManager::ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager,
25 cpu_manager.RunThread(core); 25 std::size_t core) {
26 cpu_manager.RunThread(stop_token, core);
26} 27}
27 28
28void CpuManager::Initialize() { 29void CpuManager::Initialize() {
29 running_mode = true; 30 running_mode = true;
30 if (is_multicore) { 31 if (is_multicore) {
31 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { 32 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
32 core_data[core].host_thread = 33 core_data[core].host_thread = std::jthread(ThreadStart, std::ref(*this), core);
33 std::make_unique<std::thread>(ThreadStart, std::ref(*this), core);
34 } 34 }
35 } else { 35 } else {
36 core_data[0].host_thread = std::make_unique<std::thread>(ThreadStart, std::ref(*this), 0); 36 core_data[0].host_thread = std::jthread(ThreadStart, std::ref(*this), 0);
37 } 37 }
38} 38}
39 39
40void CpuManager::Shutdown() { 40void CpuManager::Shutdown() {
41 running_mode = false; 41 running_mode = false;
42 Pause(false); 42 Pause(false);
43 if (is_multicore) {
44 for (auto& data : core_data) {
45 data.host_thread->join();
46 data.host_thread.reset();
47 }
48 } else {
49 core_data[0].host_thread->join();
50 core_data[0].host_thread.reset();
51 }
52} 43}
53 44
54std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() { 45std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() {
@@ -127,17 +118,18 @@ void CpuManager::MultiCoreRunGuestLoop() {
127 physical_core = &kernel.CurrentPhysicalCore(); 118 physical_core = &kernel.CurrentPhysicalCore();
128 } 119 }
129 system.ExitDynarmicProfile(); 120 system.ExitDynarmicProfile();
130 physical_core->ArmInterface().ClearExclusiveState(); 121 {
131 kernel.CurrentScheduler()->RescheduleCurrentCore(); 122 Kernel::KScopedDisableDispatch dd(kernel);
123 physical_core->ArmInterface().ClearExclusiveState();
124 }
132 } 125 }
133} 126}
134 127
135void CpuManager::MultiCoreRunIdleThread() { 128void CpuManager::MultiCoreRunIdleThread() {
136 auto& kernel = system.Kernel(); 129 auto& kernel = system.Kernel();
137 while (true) { 130 while (true) {
138 auto& physical_core = kernel.CurrentPhysicalCore(); 131 Kernel::KScopedDisableDispatch dd(kernel);
139 physical_core.Idle(); 132 kernel.CurrentPhysicalCore().Idle();
140 kernel.CurrentScheduler()->RescheduleCurrentCore();
141 } 133 }
142} 134}
143 135
@@ -145,12 +137,12 @@ void CpuManager::MultiCoreRunSuspendThread() {
145 auto& kernel = system.Kernel(); 137 auto& kernel = system.Kernel();
146 kernel.CurrentScheduler()->OnThreadStart(); 138 kernel.CurrentScheduler()->OnThreadStart();
147 while (true) { 139 while (true) {
148 auto core = kernel.GetCurrentHostThreadID(); 140 auto core = kernel.CurrentPhysicalCoreIndex();
149 auto& scheduler = *kernel.CurrentScheduler(); 141 auto& scheduler = *kernel.CurrentScheduler();
150 Kernel::KThread* current_thread = scheduler.GetCurrentThread(); 142 Kernel::KThread* current_thread = scheduler.GetCurrentThread();
151 Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context); 143 Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context);
152 ASSERT(scheduler.ContextSwitchPending()); 144 ASSERT(scheduler.ContextSwitchPending());
153 ASSERT(core == kernel.GetCurrentHostThreadID()); 145 ASSERT(core == kernel.CurrentPhysicalCoreIndex());
154 scheduler.RescheduleCurrentCore(); 146 scheduler.RescheduleCurrentCore();
155 } 147 }
156} 148}
@@ -317,7 +309,7 @@ void CpuManager::Pause(bool paused) {
317 } 309 }
318} 310}
319 311
320void CpuManager::RunThread(std::size_t core) { 312void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
321 /// Initialization 313 /// Initialization
322 system.RegisterCoreThread(core); 314 system.RegisterCoreThread(core);
323 std::string name; 315 std::string name;
@@ -356,8 +348,8 @@ void CpuManager::RunThread(std::size_t core) {
356 sc_sync_first_use = false; 348 sc_sync_first_use = false;
357 } 349 }
358 350
359 // Abort if emulation was killed before the session really starts 351 // Emulation was stopped
360 if (!system.IsPoweredOn()) { 352 if (stop_token.stop_requested()) {
361 return; 353 return;
362 } 354 }
363 355
diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h
index 140263b09..9d92d4af0 100644
--- a/src/core/cpu_manager.h
+++ b/src/core/cpu_manager.h
@@ -78,9 +78,9 @@ private:
78 void SingleCoreRunSuspendThread(); 78 void SingleCoreRunSuspendThread();
79 void SingleCorePause(bool paused); 79 void SingleCorePause(bool paused);
80 80
81 static void ThreadStart(CpuManager& cpu_manager, std::size_t core); 81 static void ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, std::size_t core);
82 82
83 void RunThread(std::size_t core); 83 void RunThread(std::stop_token stop_token, std::size_t core);
84 84
85 struct CoreData { 85 struct CoreData {
86 std::shared_ptr<Common::Fiber> host_context; 86 std::shared_ptr<Common::Fiber> host_context;
@@ -89,7 +89,7 @@ private:
89 std::atomic<bool> is_running; 89 std::atomic<bool> is_running;
90 std::atomic<bool> is_paused; 90 std::atomic<bool> is_paused;
91 std::atomic<bool> initialized; 91 std::atomic<bool> initialized;
92 std::unique_ptr<std::thread> host_thread; 92 std::jthread host_thread;
93 }; 93 };
94 94
95 std::atomic<bool> running_mode{}; 95 std::atomic<bool> running_mode{};
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/kernel/k_address_arbiter.cpp b/src/core/hle/kernel/k_address_arbiter.cpp
index 1b429bc1e..6771ef621 100644
--- a/src/core/hle/kernel/k_address_arbiter.cpp
+++ b/src/core/hle/kernel/k_address_arbiter.cpp
@@ -28,7 +28,7 @@ bool ReadFromUser(Core::System& system, s32* out, VAddr address) {
28 28
29bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 value) { 29bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 value) {
30 auto& monitor = system.Monitor(); 30 auto& monitor = system.Monitor();
31 const auto current_core = system.CurrentCoreIndex(); 31 const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
32 32
33 // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable. 33 // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
34 // TODO(bunnei): We should call CanAccessAtomic(..) here. 34 // TODO(bunnei): We should call CanAccessAtomic(..) here.
@@ -58,7 +58,7 @@ bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 valu
58 58
59bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32 new_value) { 59bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32 new_value) {
60 auto& monitor = system.Monitor(); 60 auto& monitor = system.Monitor();
61 const auto current_core = system.CurrentCoreIndex(); 61 const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
62 62
63 // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable. 63 // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
64 // TODO(bunnei): We should call CanAccessAtomic(..) here. 64 // TODO(bunnei): We should call CanAccessAtomic(..) here.
diff --git a/src/core/hle/kernel/k_auto_object.h b/src/core/hle/kernel/k_auto_object.h
index e4fcdbc67..165b76747 100644
--- a/src/core/hle/kernel/k_auto_object.h
+++ b/src/core/hle/kernel/k_auto_object.h
@@ -170,6 +170,10 @@ public:
170 } 170 }
171 } 171 }
172 172
173 const std::string& GetName() const {
174 return name;
175 }
176
173private: 177private:
174 void RegisterWithKernel(); 178 void RegisterWithKernel();
175 void UnregisterWithKernel(); 179 void UnregisterWithKernel();
diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp
index ef14ad1d2..4174f35fd 100644
--- a/src/core/hle/kernel/k_condition_variable.cpp
+++ b/src/core/hle/kernel/k_condition_variable.cpp
@@ -35,7 +35,7 @@ bool WriteToUser(Core::System& system, VAddr address, const u32* p) {
35bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero, 35bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero,
36 u32 new_orr_mask) { 36 u32 new_orr_mask) {
37 auto& monitor = system.Monitor(); 37 auto& monitor = system.Monitor();
38 const auto current_core = system.CurrentCoreIndex(); 38 const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
39 39
40 // Load the value from the address. 40 // Load the value from the address.
41 const auto expected = monitor.ExclusiveRead32(current_core, address); 41 const auto expected = monitor.ExclusiveRead32(current_core, address);
diff --git a/src/core/hle/kernel/k_handle_table.cpp b/src/core/hle/kernel/k_handle_table.cpp
index 6a420d5b0..d720c2dda 100644
--- a/src/core/hle/kernel/k_handle_table.cpp
+++ b/src/core/hle/kernel/k_handle_table.cpp
@@ -13,6 +13,7 @@ ResultCode KHandleTable::Finalize() {
13 // Get the table and clear our record of it. 13 // Get the table and clear our record of it.
14 u16 saved_table_size = 0; 14 u16 saved_table_size = 0;
15 { 15 {
16 KScopedDisableDispatch dd(kernel);
16 KScopedSpinLock lk(m_lock); 17 KScopedSpinLock lk(m_lock);
17 18
18 std::swap(m_table_size, saved_table_size); 19 std::swap(m_table_size, saved_table_size);
@@ -43,6 +44,7 @@ bool KHandleTable::Remove(Handle handle) {
43 // Find the object and free the entry. 44 // Find the object and free the entry.
44 KAutoObject* obj = nullptr; 45 KAutoObject* obj = nullptr;
45 { 46 {
47 KScopedDisableDispatch dd(kernel);
46 KScopedSpinLock lk(m_lock); 48 KScopedSpinLock lk(m_lock);
47 49
48 if (this->IsValidHandle(handle)) { 50 if (this->IsValidHandle(handle)) {
@@ -61,6 +63,7 @@ bool KHandleTable::Remove(Handle handle) {
61} 63}
62 64
63ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) { 65ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) {
66 KScopedDisableDispatch dd(kernel);
64 KScopedSpinLock lk(m_lock); 67 KScopedSpinLock lk(m_lock);
65 68
66 // Never exceed our capacity. 69 // Never exceed our capacity.
@@ -83,6 +86,7 @@ ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) {
83} 86}
84 87
85ResultCode KHandleTable::Reserve(Handle* out_handle) { 88ResultCode KHandleTable::Reserve(Handle* out_handle) {
89 KScopedDisableDispatch dd(kernel);
86 KScopedSpinLock lk(m_lock); 90 KScopedSpinLock lk(m_lock);
87 91
88 // Never exceed our capacity. 92 // Never exceed our capacity.
@@ -93,6 +97,7 @@ ResultCode KHandleTable::Reserve(Handle* out_handle) {
93} 97}
94 98
95void KHandleTable::Unreserve(Handle handle) { 99void KHandleTable::Unreserve(Handle handle) {
100 KScopedDisableDispatch dd(kernel);
96 KScopedSpinLock lk(m_lock); 101 KScopedSpinLock lk(m_lock);
97 102
98 // Unpack the handle. 103 // Unpack the handle.
@@ -111,6 +116,7 @@ void KHandleTable::Unreserve(Handle handle) {
111} 116}
112 117
113void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) { 118void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) {
119 KScopedDisableDispatch dd(kernel);
114 KScopedSpinLock lk(m_lock); 120 KScopedSpinLock lk(m_lock);
115 121
116 // Unpack the handle. 122 // Unpack the handle.
diff --git a/src/core/hle/kernel/k_handle_table.h b/src/core/hle/kernel/k_handle_table.h
index 2ff6aa160..75dcec7df 100644
--- a/src/core/hle/kernel/k_handle_table.h
+++ b/src/core/hle/kernel/k_handle_table.h
@@ -69,6 +69,7 @@ public:
69 template <typename T = KAutoObject> 69 template <typename T = KAutoObject>
70 KScopedAutoObject<T> GetObjectWithoutPseudoHandle(Handle handle) const { 70 KScopedAutoObject<T> GetObjectWithoutPseudoHandle(Handle handle) const {
71 // Lock and look up in table. 71 // Lock and look up in table.
72 KScopedDisableDispatch dd(kernel);
72 KScopedSpinLock lk(m_lock); 73 KScopedSpinLock lk(m_lock);
73 74
74 if constexpr (std::is_same_v<T, KAutoObject>) { 75 if constexpr (std::is_same_v<T, KAutoObject>) {
@@ -123,6 +124,7 @@ public:
123 size_t num_opened; 124 size_t num_opened;
124 { 125 {
125 // Lock the table. 126 // Lock the table.
127 KScopedDisableDispatch dd(kernel);
126 KScopedSpinLock lk(m_lock); 128 KScopedSpinLock lk(m_lock);
127 for (num_opened = 0; num_opened < num_handles; num_opened++) { 129 for (num_opened = 0; num_opened < num_handles; num_opened++) {
128 // Get the current handle. 130 // Get the current handle.
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index 8ead1a769..3d7e6707e 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -59,6 +59,7 @@ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority
59 thread->GetContext64().cpu_registers[0] = 0; 59 thread->GetContext64().cpu_registers[0] = 0;
60 thread->GetContext32().cpu_registers[1] = thread_handle; 60 thread->GetContext32().cpu_registers[1] = thread_handle;
61 thread->GetContext64().cpu_registers[1] = thread_handle; 61 thread->GetContext64().cpu_registers[1] = thread_handle;
62 thread->DisableDispatch();
62 63
63 auto& kernel = system.Kernel(); 64 auto& kernel = system.Kernel();
64 // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires 65 // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp
index 6a7d80d03..6ddbae52c 100644
--- a/src/core/hle/kernel/k_scheduler.cpp
+++ b/src/core/hle/kernel/k_scheduler.cpp
@@ -376,20 +376,18 @@ void KScheduler::ClearSchedulerUpdateNeeded(KernelCore& kernel) {
376} 376}
377 377
378void KScheduler::DisableScheduling(KernelCore& kernel) { 378void KScheduler::DisableScheduling(KernelCore& kernel) {
379 if (auto* scheduler = kernel.CurrentScheduler(); scheduler) { 379 ASSERT(GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() >= 0);
380 ASSERT(scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 0); 380 GetCurrentThreadPointer(kernel)->DisableDispatch();
381 scheduler->GetCurrentThread()->DisableDispatch();
382 }
383} 381}
384 382
385void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) { 383void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) {
386 if (auto* scheduler = kernel.CurrentScheduler(); scheduler) { 384 ASSERT(GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() >= 1);
387 ASSERT(scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1); 385
388 if (scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1) { 386 if (GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() > 1) {
389 scheduler->GetCurrentThread()->EnableDispatch(); 387 GetCurrentThreadPointer(kernel)->EnableDispatch();
390 } 388 } else {
389 RescheduleCores(kernel, cores_needing_scheduling);
391 } 390 }
392 RescheduleCores(kernel, cores_needing_scheduling);
393} 391}
394 392
395u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) { 393u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) {
@@ -617,13 +615,17 @@ KScheduler::KScheduler(Core::System& system_, s32 core_id_) : system{system_}, c
617 state.highest_priority_thread = nullptr; 615 state.highest_priority_thread = nullptr;
618} 616}
619 617
620KScheduler::~KScheduler() { 618void KScheduler::Finalize() {
621 if (idle_thread) { 619 if (idle_thread) {
622 idle_thread->Close(); 620 idle_thread->Close();
623 idle_thread = nullptr; 621 idle_thread = nullptr;
624 } 622 }
625} 623}
626 624
625KScheduler::~KScheduler() {
626 ASSERT(!idle_thread);
627}
628
627KThread* KScheduler::GetCurrentThread() const { 629KThread* KScheduler::GetCurrentThread() const {
628 if (auto result = current_thread.load(); result) { 630 if (auto result = current_thread.load(); result) {
629 return result; 631 return result;
@@ -642,10 +644,12 @@ void KScheduler::RescheduleCurrentCore() {
642 if (phys_core.IsInterrupted()) { 644 if (phys_core.IsInterrupted()) {
643 phys_core.ClearInterrupt(); 645 phys_core.ClearInterrupt();
644 } 646 }
647
645 guard.Lock(); 648 guard.Lock();
646 if (state.needs_scheduling.load()) { 649 if (state.needs_scheduling.load()) {
647 Schedule(); 650 Schedule();
648 } else { 651 } else {
652 GetCurrentThread()->EnableDispatch();
649 guard.Unlock(); 653 guard.Unlock();
650 } 654 }
651} 655}
@@ -655,26 +659,33 @@ void KScheduler::OnThreadStart() {
655} 659}
656 660
657void KScheduler::Unload(KThread* thread) { 661void KScheduler::Unload(KThread* thread) {
662 ASSERT(thread);
663
658 LOG_TRACE(Kernel, "core {}, unload thread {}", core_id, thread ? thread->GetName() : "nullptr"); 664 LOG_TRACE(Kernel, "core {}, unload thread {}", core_id, thread ? thread->GetName() : "nullptr");
659 665
660 if (thread) { 666 if (thread->IsCallingSvc()) {
661 if (thread->IsCallingSvc()) { 667 thread->ClearIsCallingSvc();
662 thread->ClearIsCallingSvc();
663 }
664 if (!thread->IsTerminationRequested()) {
665 prev_thread = thread;
666
667 Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
668 cpu_core.SaveContext(thread->GetContext32());
669 cpu_core.SaveContext(thread->GetContext64());
670 // Save the TPIDR_EL0 system register in case it was modified.
671 thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
672 cpu_core.ClearExclusiveState();
673 } else {
674 prev_thread = nullptr;
675 }
676 thread->context_guard.Unlock();
677 } 668 }
669
670 auto& physical_core = system.Kernel().PhysicalCore(core_id);
671 if (!physical_core.IsInitialized()) {
672 return;
673 }
674
675 Core::ARM_Interface& cpu_core = physical_core.ArmInterface();
676 cpu_core.SaveContext(thread->GetContext32());
677 cpu_core.SaveContext(thread->GetContext64());
678 // Save the TPIDR_EL0 system register in case it was modified.
679 thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
680 cpu_core.ClearExclusiveState();
681
682 if (!thread->IsTerminationRequested() && thread->GetActiveCore() == core_id) {
683 prev_thread = thread;
684 } else {
685 prev_thread = nullptr;
686 }
687
688 thread->context_guard.Unlock();
678} 689}
679 690
680void KScheduler::Reload(KThread* thread) { 691void KScheduler::Reload(KThread* thread) {
@@ -683,11 +694,6 @@ void KScheduler::Reload(KThread* thread) {
683 if (thread) { 694 if (thread) {
684 ASSERT_MSG(thread->GetState() == ThreadState::Runnable, "Thread must be runnable."); 695 ASSERT_MSG(thread->GetState() == ThreadState::Runnable, "Thread must be runnable.");
685 696
686 auto* const thread_owner_process = thread->GetOwnerProcess();
687 if (thread_owner_process != nullptr) {
688 system.Kernel().MakeCurrentProcess(thread_owner_process);
689 }
690
691 Core::ARM_Interface& cpu_core = system.ArmInterface(core_id); 697 Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
692 cpu_core.LoadContext(thread->GetContext32()); 698 cpu_core.LoadContext(thread->GetContext32());
693 cpu_core.LoadContext(thread->GetContext64()); 699 cpu_core.LoadContext(thread->GetContext64());
@@ -705,7 +711,7 @@ void KScheduler::SwitchContextStep2() {
705} 711}
706 712
707void KScheduler::ScheduleImpl() { 713void KScheduler::ScheduleImpl() {
708 KThread* previous_thread = current_thread.load(); 714 KThread* previous_thread = GetCurrentThread();
709 KThread* next_thread = state.highest_priority_thread; 715 KThread* next_thread = state.highest_priority_thread;
710 716
711 state.needs_scheduling = false; 717 state.needs_scheduling = false;
@@ -717,10 +723,15 @@ void KScheduler::ScheduleImpl() {
717 723
718 // If we're not actually switching thread, there's nothing to do. 724 // If we're not actually switching thread, there's nothing to do.
719 if (next_thread == current_thread.load()) { 725 if (next_thread == current_thread.load()) {
726 previous_thread->EnableDispatch();
720 guard.Unlock(); 727 guard.Unlock();
721 return; 728 return;
722 } 729 }
723 730
731 if (next_thread->GetCurrentCore() != core_id) {
732 next_thread->SetCurrentCore(core_id);
733 }
734
724 current_thread.store(next_thread); 735 current_thread.store(next_thread);
725 736
726 KProcess* const previous_process = system.Kernel().CurrentProcess(); 737 KProcess* const previous_process = system.Kernel().CurrentProcess();
@@ -731,11 +742,7 @@ void KScheduler::ScheduleImpl() {
731 Unload(previous_thread); 742 Unload(previous_thread);
732 743
733 std::shared_ptr<Common::Fiber>* old_context; 744 std::shared_ptr<Common::Fiber>* old_context;
734 if (previous_thread != nullptr) { 745 old_context = &previous_thread->GetHostContext();
735 old_context = &previous_thread->GetHostContext();
736 } else {
737 old_context = &idle_thread->GetHostContext();
738 }
739 guard.Unlock(); 746 guard.Unlock();
740 747
741 Common::Fiber::YieldTo(*old_context, *switch_fiber); 748 Common::Fiber::YieldTo(*old_context, *switch_fiber);
diff --git a/src/core/hle/kernel/k_scheduler.h b/src/core/hle/kernel/k_scheduler.h
index 12cfae919..516e0cdba 100644
--- a/src/core/hle/kernel/k_scheduler.h
+++ b/src/core/hle/kernel/k_scheduler.h
@@ -33,6 +33,8 @@ public:
33 explicit KScheduler(Core::System& system_, s32 core_id_); 33 explicit KScheduler(Core::System& system_, s32 core_id_);
34 ~KScheduler(); 34 ~KScheduler();
35 35
36 void Finalize();
37
36 /// Reschedules to the next available thread (call after current thread is suspended) 38 /// Reschedules to the next available thread (call after current thread is suspended)
37 void RescheduleCurrentCore(); 39 void RescheduleCurrentCore();
38 40
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index 9f1d3156b..0f6808ade 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -14,6 +14,7 @@
14#include "common/fiber.h" 14#include "common/fiber.h"
15#include "common/logging/log.h" 15#include "common/logging/log.h"
16#include "common/scope_exit.h" 16#include "common/scope_exit.h"
17#include "common/settings.h"
17#include "common/thread_queue_list.h" 18#include "common/thread_queue_list.h"
18#include "core/core.h" 19#include "core/core.h"
19#include "core/cpu_manager.h" 20#include "core/cpu_manager.h"
@@ -188,7 +189,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
188 // Setup the stack parameters. 189 // Setup the stack parameters.
189 StackParameters& sp = GetStackParameters(); 190 StackParameters& sp = GetStackParameters();
190 sp.cur_thread = this; 191 sp.cur_thread = this;
191 sp.disable_count = 1; 192 sp.disable_count = 0;
192 SetInExceptionHandler(); 193 SetInExceptionHandler();
193 194
194 // Set thread ID. 195 // Set thread ID.
@@ -215,9 +216,10 @@ ResultCode KThread::InitializeThread(KThread* thread, KThreadFunction func, uint
215 // Initialize the thread. 216 // Initialize the thread.
216 R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type)); 217 R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type));
217 218
218 // Initialize host context. 219 // Initialize emulation parameters.
219 thread->host_context = 220 thread->host_context =
220 std::make_shared<Common::Fiber>(std::move(init_func), init_func_parameter); 221 std::make_shared<Common::Fiber>(std::move(init_func), init_func_parameter);
222 thread->is_single_core = !Settings::values.use_multi_core.GetValue();
221 223
222 return ResultSuccess; 224 return ResultSuccess;
223} 225}
@@ -970,6 +972,9 @@ ResultCode KThread::Run() {
970 972
971 // Set our state and finish. 973 // Set our state and finish.
972 SetState(ThreadState::Runnable); 974 SetState(ThreadState::Runnable);
975
976 DisableDispatch();
977
973 return ResultSuccess; 978 return ResultSuccess;
974 } 979 }
975} 980}
@@ -1054,4 +1059,16 @@ s32 GetCurrentCoreId(KernelCore& kernel) {
1054 return GetCurrentThread(kernel).GetCurrentCore(); 1059 return GetCurrentThread(kernel).GetCurrentCore();
1055} 1060}
1056 1061
1062KScopedDisableDispatch::~KScopedDisableDispatch() {
1063 if (GetCurrentThread(kernel).GetDisableDispatchCount() <= 1) {
1064 auto scheduler = kernel.CurrentScheduler();
1065
1066 if (scheduler) {
1067 scheduler->RescheduleCurrentCore();
1068 }
1069 } else {
1070 GetCurrentThread(kernel).EnableDispatch();
1071 }
1072}
1073
1057} // namespace Kernel 1074} // namespace Kernel
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index c77f44ad4..e4c4c877d 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -450,16 +450,39 @@ public:
450 sleeping_queue = q; 450 sleeping_queue = q;
451 } 451 }
452 452
453 [[nodiscard]] bool IsKernelThread() const {
454 return GetActiveCore() == 3;
455 }
456
457 [[nodiscard]] bool IsDispatchTrackingDisabled() const {
458 return is_single_core || IsKernelThread();
459 }
460
453 [[nodiscard]] s32 GetDisableDispatchCount() const { 461 [[nodiscard]] s32 GetDisableDispatchCount() const {
462 if (IsDispatchTrackingDisabled()) {
463 // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
464 return 1;
465 }
466
454 return this->GetStackParameters().disable_count; 467 return this->GetStackParameters().disable_count;
455 } 468 }
456 469
457 void DisableDispatch() { 470 void DisableDispatch() {
471 if (IsDispatchTrackingDisabled()) {
472 // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
473 return;
474 }
475
458 ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0); 476 ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0);
459 this->GetStackParameters().disable_count++; 477 this->GetStackParameters().disable_count++;
460 } 478 }
461 479
462 void EnableDispatch() { 480 void EnableDispatch() {
481 if (IsDispatchTrackingDisabled()) {
482 // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
483 return;
484 }
485
463 ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() > 0); 486 ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() > 0);
464 this->GetStackParameters().disable_count--; 487 this->GetStackParameters().disable_count--;
465 } 488 }
@@ -708,6 +731,7 @@ private:
708 731
709 // For emulation 732 // For emulation
710 std::shared_ptr<Common::Fiber> host_context{}; 733 std::shared_ptr<Common::Fiber> host_context{};
734 bool is_single_core{};
711 735
712 // For debugging 736 // For debugging
713 std::vector<KSynchronizationObject*> wait_objects_for_debugging; 737 std::vector<KSynchronizationObject*> wait_objects_for_debugging;
@@ -752,4 +776,16 @@ public:
752 } 776 }
753}; 777};
754 778
779class KScopedDisableDispatch {
780public:
781 [[nodiscard]] explicit KScopedDisableDispatch(KernelCore& kernel_) : kernel{kernel_} {
782 GetCurrentThread(kernel).DisableDispatch();
783 }
784
785 ~KScopedDisableDispatch();
786
787private:
788 KernelCore& kernel;
789};
790
755} // namespace Kernel 791} // namespace Kernel
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 92fbc5532..8673384ee 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -85,8 +85,9 @@ struct KernelCore::Impl {
85 } 85 }
86 86
87 void InitializeCores() { 87 void InitializeCores() {
88 for (auto& core : cores) { 88 for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
89 core.Initialize(current_process->Is64BitProcess()); 89 cores[core_id].Initialize(current_process->Is64BitProcess());
90 system.Memory().SetCurrentPageTable(*current_process, core_id);
90 } 91 }
91 } 92 }
92 93
@@ -131,15 +132,6 @@ struct KernelCore::Impl {
131 next_user_process_id = KProcess::ProcessIDMin; 132 next_user_process_id = KProcess::ProcessIDMin;
132 next_thread_id = 1; 133 next_thread_id = 1;
133 134
134 for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
135 if (suspend_threads[core_id]) {
136 suspend_threads[core_id]->Close();
137 suspend_threads[core_id] = nullptr;
138 }
139
140 schedulers[core_id].reset();
141 }
142
143 cores.clear(); 135 cores.clear();
144 136
145 global_handle_table->Finalize(); 137 global_handle_table->Finalize();
@@ -167,6 +159,16 @@ struct KernelCore::Impl {
167 CleanupObject(time_shared_mem); 159 CleanupObject(time_shared_mem);
168 CleanupObject(system_resource_limit); 160 CleanupObject(system_resource_limit);
169 161
162 for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
163 if (suspend_threads[core_id]) {
164 suspend_threads[core_id]->Close();
165 suspend_threads[core_id] = nullptr;
166 }
167
168 schedulers[core_id]->Finalize();
169 schedulers[core_id].reset();
170 }
171
170 // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others 172 // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
171 next_host_thread_id = Core::Hardware::NUM_CPU_CORES; 173 next_host_thread_id = Core::Hardware::NUM_CPU_CORES;
172 174
@@ -257,14 +259,6 @@ struct KernelCore::Impl {
257 259
258 void MakeCurrentProcess(KProcess* process) { 260 void MakeCurrentProcess(KProcess* process) {
259 current_process = process; 261 current_process = process;
260 if (process == nullptr) {
261 return;
262 }
263
264 const u32 core_id = GetCurrentHostThreadID();
265 if (core_id < Core::Hardware::NUM_CPU_CORES) {
266 system.Memory().SetCurrentPageTable(*process, core_id);
267 }
268 } 262 }
269 263
270 /// Creates a new host thread ID, should only be called by GetHostThreadId 264 /// Creates a new host thread ID, should only be called by GetHostThreadId
@@ -824,16 +818,20 @@ const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const {
824 return impl->cores[id]; 818 return impl->cores[id];
825} 819}
826 820
821size_t KernelCore::CurrentPhysicalCoreIndex() const {
822 const u32 core_id = impl->GetCurrentHostThreadID();
823 if (core_id >= Core::Hardware::NUM_CPU_CORES) {
824 return Core::Hardware::NUM_CPU_CORES - 1;
825 }
826 return core_id;
827}
828
827Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() { 829Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() {
828 u32 core_id = impl->GetCurrentHostThreadID(); 830 return impl->cores[CurrentPhysicalCoreIndex()];
829 ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
830 return impl->cores[core_id];
831} 831}
832 832
833const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const { 833const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const {
834 u32 core_id = impl->GetCurrentHostThreadID(); 834 return impl->cores[CurrentPhysicalCoreIndex()];
835 ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
836 return impl->cores[core_id];
837} 835}
838 836
839Kernel::KScheduler* KernelCore::CurrentScheduler() { 837Kernel::KScheduler* KernelCore::CurrentScheduler() {
@@ -1026,6 +1024,9 @@ void KernelCore::Suspend(bool in_suspention) {
1026 impl->suspend_threads[core_id]->SetState(state); 1024 impl->suspend_threads[core_id]->SetState(state);
1027 impl->suspend_threads[core_id]->SetWaitReasonForDebugging( 1025 impl->suspend_threads[core_id]->SetWaitReasonForDebugging(
1028 ThreadWaitReasonForDebugging::Suspended); 1026 ThreadWaitReasonForDebugging::Suspended);
1027 if (!should_suspend) {
1028 impl->suspend_threads[core_id]->DisableDispatch();
1029 }
1029 } 1030 }
1030 } 1031 }
1031} 1032}
@@ -1040,13 +1041,11 @@ void KernelCore::ExceptionalExit() {
1040} 1041}
1041 1042
1042void KernelCore::EnterSVCProfile() { 1043void KernelCore::EnterSVCProfile() {
1043 std::size_t core = impl->GetCurrentHostThreadID(); 1044 impl->svc_ticks[CurrentPhysicalCoreIndex()] = MicroProfileEnter(MICROPROFILE_TOKEN(Kernel_SVC));
1044 impl->svc_ticks[core] = MicroProfileEnter(MICROPROFILE_TOKEN(Kernel_SVC));
1045} 1045}
1046 1046
1047void KernelCore::ExitSVCProfile() { 1047void KernelCore::ExitSVCProfile() {
1048 std::size_t core = impl->GetCurrentHostThreadID(); 1048 MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[CurrentPhysicalCoreIndex()]);
1049 MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[core]);
1050} 1049}
1051 1050
1052std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::string& name) { 1051std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::string& name) {
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 3a6db0b1c..57535433b 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -146,6 +146,9 @@ public:
146 /// Gets the an instance of the respective physical CPU core. 146 /// Gets the an instance of the respective physical CPU core.
147 const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const; 147 const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const;
148 148
149 /// Gets the current physical core index for the running host thread.
150 std::size_t CurrentPhysicalCoreIndex() const;
151
149 /// Gets the sole instance of the Scheduler at the current running core. 152 /// Gets the sole instance of the Scheduler at the current running core.
150 Kernel::KScheduler* CurrentScheduler(); 153 Kernel::KScheduler* CurrentScheduler();
151 154
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 2eb532472..890c52198 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -877,7 +877,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle
877 const u64 thread_ticks = current_thread->GetCpuTime(); 877 const u64 thread_ticks = current_thread->GetCpuTime();
878 878
879 out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks); 879 out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks);
880 } else if (same_thread && info_sub_id == system.CurrentCoreIndex()) { 880 } else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) {
881 out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks; 881 out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks;
882 } 882 }
883 883
@@ -1078,8 +1078,8 @@ static ResultCode GetThreadContext(Core::System& system, VAddr out_context, Hand
1078 for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) { 1078 for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
1079 if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetCurrentThread()) { 1079 if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetCurrentThread()) {
1080 current = true; 1080 current = true;
1081 break;
1081 } 1082 }
1082 break;
1083 } 1083 }
1084 1084
1085 // If the thread is current, retry until it isn't. 1085 // If the thread is current, retry until it isn't.
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index 2e969f2a8..882fc1492 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -292,7 +292,7 @@ public:
292 292
293protected: 293protected:
294 void Get(Kernel::HLERequestContext& ctx) { 294 void Get(Kernel::HLERequestContext& ctx) {
295 LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format()); 295 LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format());
296 ProfileBase profile_base{}; 296 ProfileBase profile_base{};
297 ProfileData data{}; 297 ProfileData data{};
298 if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) { 298 if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) {
@@ -301,7 +301,7 @@ protected:
301 rb.Push(ResultSuccess); 301 rb.Push(ResultSuccess);
302 rb.PushRaw(profile_base); 302 rb.PushRaw(profile_base);
303 } else { 303 } else {
304 LOG_ERROR(Service_ACC, "Failed to get profile base and data for user={}", 304 LOG_ERROR(Service_ACC, "Failed to get profile base and data for user=0x{}",
305 user_id.Format()); 305 user_id.Format());
306 IPC::ResponseBuilder rb{ctx, 2}; 306 IPC::ResponseBuilder rb{ctx, 2};
307 rb.Push(ResultUnknown); // TODO(ogniK): Get actual error code 307 rb.Push(ResultUnknown); // TODO(ogniK): Get actual error code
@@ -309,14 +309,14 @@ protected:
309 } 309 }
310 310
311 void GetBase(Kernel::HLERequestContext& ctx) { 311 void GetBase(Kernel::HLERequestContext& ctx) {
312 LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format()); 312 LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format());
313 ProfileBase profile_base{}; 313 ProfileBase profile_base{};
314 if (profile_manager.GetProfileBase(user_id, profile_base)) { 314 if (profile_manager.GetProfileBase(user_id, profile_base)) {
315 IPC::ResponseBuilder rb{ctx, 16}; 315 IPC::ResponseBuilder rb{ctx, 16};
316 rb.Push(ResultSuccess); 316 rb.Push(ResultSuccess);
317 rb.PushRaw(profile_base); 317 rb.PushRaw(profile_base);
318 } else { 318 } else {
319 LOG_ERROR(Service_ACC, "Failed to get profile base for user={}", user_id.Format()); 319 LOG_ERROR(Service_ACC, "Failed to get profile base for user=0x{}", user_id.Format());
320 IPC::ResponseBuilder rb{ctx, 2}; 320 IPC::ResponseBuilder rb{ctx, 2};
321 rb.Push(ResultUnknown); // TODO(ogniK): Get actual error code 321 rb.Push(ResultUnknown); // TODO(ogniK): Get actual error code
322 } 322 }
@@ -372,7 +372,7 @@ protected:
372 372
373 const auto user_data = ctx.ReadBuffer(); 373 const auto user_data = ctx.ReadBuffer();
374 374
375 LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid={}", 375 LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}",
376 Common::StringFromFixedZeroTerminatedBuffer( 376 Common::StringFromFixedZeroTerminatedBuffer(
377 reinterpret_cast<const char*>(base.username.data()), base.username.size()), 377 reinterpret_cast<const char*>(base.username.data()), base.username.size()),
378 base.timestamp, base.user_uuid.Format()); 378 base.timestamp, base.user_uuid.Format());
@@ -405,7 +405,7 @@ protected:
405 const auto user_data = ctx.ReadBuffer(); 405 const auto user_data = ctx.ReadBuffer();
406 const auto image_data = ctx.ReadBuffer(1); 406 const auto image_data = ctx.ReadBuffer(1);
407 407
408 LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid={}", 408 LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}",
409 Common::StringFromFixedZeroTerminatedBuffer( 409 Common::StringFromFixedZeroTerminatedBuffer(
410 reinterpret_cast<const char*>(base.username.data()), base.username.size()), 410 reinterpret_cast<const char*>(base.username.data()), base.username.size()),
411 base.timestamp, base.user_uuid.Format()); 411 base.timestamp, base.user_uuid.Format());
@@ -662,7 +662,7 @@ void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) {
662void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) { 662void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) {
663 IPC::RequestParser rp{ctx}; 663 IPC::RequestParser rp{ctx};
664 Common::UUID user_id = rp.PopRaw<Common::UUID>(); 664 Common::UUID user_id = rp.PopRaw<Common::UUID>();
665 LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format()); 665 LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format());
666 666
667 IPC::ResponseBuilder rb{ctx, 3}; 667 IPC::ResponseBuilder rb{ctx, 3};
668 rb.Push(ResultSuccess); 668 rb.Push(ResultSuccess);
@@ -693,7 +693,7 @@ void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) {
693void Module::Interface::GetProfile(Kernel::HLERequestContext& ctx) { 693void Module::Interface::GetProfile(Kernel::HLERequestContext& ctx) {
694 IPC::RequestParser rp{ctx}; 694 IPC::RequestParser rp{ctx};
695 Common::UUID user_id = rp.PopRaw<Common::UUID>(); 695 Common::UUID user_id = rp.PopRaw<Common::UUID>();
696 LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format()); 696 LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format());
697 697
698 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 698 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
699 rb.Push(ResultSuccess); 699 rb.Push(ResultSuccess);
@@ -802,7 +802,7 @@ void Module::Interface::GetProfileEditor(Kernel::HLERequestContext& ctx) {
802 IPC::RequestParser rp{ctx}; 802 IPC::RequestParser rp{ctx};
803 Common::UUID user_id = rp.PopRaw<Common::UUID>(); 803 Common::UUID user_id = rp.PopRaw<Common::UUID>();
804 804
805 LOG_DEBUG(Service_ACC, "called, user_id={}", user_id.Format()); 805 LOG_DEBUG(Service_ACC, "called, user_id=0x{}", user_id.Format());
806 806
807 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 807 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
808 rb.Push(ResultSuccess); 808 rb.Push(ResultSuccess);
@@ -844,7 +844,7 @@ void Module::Interface::StoreSaveDataThumbnailApplication(Kernel::HLERequestCont
844 IPC::RequestParser rp{ctx}; 844 IPC::RequestParser rp{ctx};
845 const auto uuid = rp.PopRaw<Common::UUID>(); 845 const auto uuid = rp.PopRaw<Common::UUID>();
846 846
847 LOG_WARNING(Service_ACC, "(STUBBED) called, uuid={}", uuid.Format()); 847 LOG_WARNING(Service_ACC, "(STUBBED) called, uuid=0x{}", uuid.Format());
848 848
849 // TODO(ogniK): Check if application ID is zero on acc initialize. As we don't have a reliable 849 // TODO(ogniK): Check if application ID is zero on acc initialize. As we don't have a reliable
850 // way of confirming things like the TID, we're going to assume a non zero value for the time 850 // way of confirming things like the TID, we're going to assume a non zero value for the time
@@ -858,7 +858,7 @@ void Module::Interface::StoreSaveDataThumbnailSystem(Kernel::HLERequestContext&
858 const auto uuid = rp.PopRaw<Common::UUID>(); 858 const auto uuid = rp.PopRaw<Common::UUID>();
859 const auto tid = rp.Pop<u64_le>(); 859 const auto tid = rp.Pop<u64_le>();
860 860
861 LOG_WARNING(Service_ACC, "(STUBBED) called, uuid={}, tid={:016X}", uuid.Format(), tid); 861 LOG_WARNING(Service_ACC, "(STUBBED) called, uuid=0x{}, tid={:016X}", uuid.Format(), tid);
862 StoreSaveDataThumbnail(ctx, uuid, tid); 862 StoreSaveDataThumbnail(ctx, uuid, tid);
863} 863}
864 864
diff --git a/src/core/hle/service/am/applets/applet_error.cpp b/src/core/hle/service/am/applets/applet_error.cpp
index ef6854d62..36a4aa9cd 100644
--- a/src/core/hle/service/am/applets/applet_error.cpp
+++ b/src/core/hle/service/am/applets/applet_error.cpp
@@ -16,6 +16,30 @@
16 16
17namespace Service::AM::Applets { 17namespace Service::AM::Applets {
18 18
19struct ErrorCode {
20 u32 error_category{};
21 u32 error_number{};
22
23 static constexpr ErrorCode FromU64(u64 error_code) {
24 return {
25 .error_category{static_cast<u32>(error_code >> 32)},
26 .error_number{static_cast<u32>(error_code & 0xFFFFFFFF)},
27 };
28 }
29
30 static constexpr ErrorCode FromResultCode(ResultCode result) {
31 return {
32 .error_category{2000 + static_cast<u32>(result.module.Value())},
33 .error_number{result.description.Value()},
34 };
35 }
36
37 constexpr ResultCode ToResultCode() const {
38 return ResultCode{static_cast<ErrorModule>(error_category - 2000), error_number};
39 }
40};
41static_assert(sizeof(ErrorCode) == 0x8, "ErrorCode has incorrect size.");
42
19#pragma pack(push, 4) 43#pragma pack(push, 4)
20struct ShowError { 44struct ShowError {
21 u8 mode; 45 u8 mode;
@@ -76,12 +100,7 @@ void CopyArgumentData(const std::vector<u8>& data, T& variable) {
76} 100}
77 101
78ResultCode Decode64BitError(u64 error) { 102ResultCode Decode64BitError(u64 error) {
79 const auto description = (error >> 32) & 0x1FFF; 103 return ErrorCode::FromU64(error).ToResultCode();
80 auto module = error & 0x3FF;
81 if (module >= 2000)
82 module -= 2000;
83 module &= 0x1FF;
84 return {static_cast<ErrorModule>(module), static_cast<u32>(description)};
85} 104}
86 105
87} // Anonymous namespace 106} // Anonymous namespace
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/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp
index a3c939c0c..b58c152ce 100644
--- a/src/core/hle/service/friend/friend.cpp
+++ b/src/core/hle/service/friend/friend.cpp
@@ -158,7 +158,7 @@ private:
158 const auto local_play = rp.Pop<bool>(); 158 const auto local_play = rp.Pop<bool>();
159 const auto uuid = rp.PopRaw<Common::UUID>(); 159 const auto uuid = rp.PopRaw<Common::UUID>();
160 160
161 LOG_WARNING(Service_Friend, "(STUBBED) called local_play={} uuid={}", local_play, 161 LOG_WARNING(Service_Friend, "(STUBBED) called, local_play={}, uuid=0x{}", local_play,
162 uuid.Format()); 162 uuid.Format());
163 163
164 IPC::ResponseBuilder rb{ctx, 2}; 164 IPC::ResponseBuilder rb{ctx, 2};
@@ -171,7 +171,7 @@ private:
171 const auto uuid = rp.PopRaw<Common::UUID>(); 171 const auto uuid = rp.PopRaw<Common::UUID>();
172 [[maybe_unused]] const auto filter = rp.PopRaw<SizedFriendFilter>(); 172 [[maybe_unused]] const auto filter = rp.PopRaw<SizedFriendFilter>();
173 const auto pid = rp.Pop<u64>(); 173 const auto pid = rp.Pop<u64>();
174 LOG_WARNING(Service_Friend, "(STUBBED) called, offset={}, uuid={}, pid={}", friend_offset, 174 LOG_WARNING(Service_Friend, "(STUBBED) called, offset={}, uuid=0x{}, pid={}", friend_offset,
175 uuid.Format(), pid); 175 uuid.Format(), pid);
176 176
177 IPC::ResponseBuilder rb{ctx, 3}; 177 IPC::ResponseBuilder rb{ctx, 3};
@@ -289,7 +289,7 @@ void Module::Interface::CreateNotificationService(Kernel::HLERequestContext& ctx
289 IPC::RequestParser rp{ctx}; 289 IPC::RequestParser rp{ctx};
290 auto uuid = rp.PopRaw<Common::UUID>(); 290 auto uuid = rp.PopRaw<Common::UUID>();
291 291
292 LOG_DEBUG(Service_Friend, "called, uuid={}", uuid.Format()); 292 LOG_DEBUG(Service_Friend, "called, uuid=0x{}", uuid.Format());
293 293
294 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 294 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
295 rb.Push(ResultSuccess); 295 rb.Push(ResultSuccess);
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index e742db48f..0a53c0c81 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -11,6 +11,7 @@
11#include "core/hle/service/nifm/nifm.h" 11#include "core/hle/service/nifm/nifm.h"
12#include "core/hle/service/service.h" 12#include "core/hle/service/service.h"
13#include "core/network/network.h" 13#include "core/network/network.h"
14#include "core/network/network_interface.h"
14 15
15namespace Service::NIFM { 16namespace Service::NIFM {
16 17
@@ -179,10 +180,10 @@ private:
179 IPC::ResponseBuilder rb{ctx, 3}; 180 IPC::ResponseBuilder rb{ctx, 3};
180 rb.Push(ResultSuccess); 181 rb.Push(ResultSuccess);
181 182
182 if (Settings::values.bcat_backend.GetValue() == "none") { 183 if (Network::GetHostIPv4Address().has_value()) {
183 rb.PushEnum(RequestState::NotSubmitted);
184 } else {
185 rb.PushEnum(RequestState::Connected); 184 rb.PushEnum(RequestState::Connected);
185 } else {
186 rb.PushEnum(RequestState::NotSubmitted);
186 } 187 }
187 } 188 }
188 189
@@ -322,12 +323,15 @@ private:
322 void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) { 323 void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) {
323 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 324 LOG_WARNING(Service_NIFM, "(STUBBED) called");
324 325
325 const auto [ipv4, error] = Network::GetHostIPv4Address(); 326 auto ipv4 = Network::GetHostIPv4Address();
326 UNIMPLEMENTED_IF(error != Network::Errno::SUCCESS); 327 if (!ipv4) {
328 LOG_ERROR(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0");
329 ipv4.emplace(Network::IPv4Address{0, 0, 0, 0});
330 }
327 331
328 IPC::ResponseBuilder rb{ctx, 3}; 332 IPC::ResponseBuilder rb{ctx, 3};
329 rb.Push(ResultSuccess); 333 rb.Push(ResultSuccess);
330 rb.PushRaw(ipv4); 334 rb.PushRaw(*ipv4);
331 } 335 }
332 void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { 336 void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
333 LOG_DEBUG(Service_NIFM, "called"); 337 LOG_DEBUG(Service_NIFM, "called");
@@ -354,10 +358,10 @@ private:
354 static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting), 358 static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting),
355 "IpConfigInfo has incorrect size."); 359 "IpConfigInfo has incorrect size.");
356 360
357 const IpConfigInfo ip_config_info{ 361 IpConfigInfo ip_config_info{
358 .ip_address_setting{ 362 .ip_address_setting{
359 .is_automatic{true}, 363 .is_automatic{true},
360 .current_address{192, 168, 1, 100}, 364 .current_address{0, 0, 0, 0},
361 .subnet_mask{255, 255, 255, 0}, 365 .subnet_mask{255, 255, 255, 0},
362 .gateway{192, 168, 1, 1}, 366 .gateway{192, 168, 1, 1},
363 }, 367 },
@@ -368,6 +372,19 @@ private:
368 }, 372 },
369 }; 373 };
370 374
375 const auto iface = Network::GetSelectedNetworkInterface();
376 if (iface) {
377 ip_config_info.ip_address_setting =
378 IpAddressSetting{.is_automatic{true},
379 .current_address{Network::TranslateIPv4(iface->ip_address)},
380 .subnet_mask{Network::TranslateIPv4(iface->subnet_mask)},
381 .gateway{Network::TranslateIPv4(iface->gateway)}};
382
383 } else {
384 LOG_ERROR(Service_NIFM,
385 "Couldn't get host network configuration info, using default values");
386 }
387
371 IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)}; 388 IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)};
372 rb.Push(ResultSuccess); 389 rb.Push(ResultSuccess);
373 rb.PushRaw<IpConfigInfo>(ip_config_info); 390 rb.PushRaw<IpConfigInfo>(ip_config_info);
@@ -384,10 +401,10 @@ private:
384 401
385 IPC::ResponseBuilder rb{ctx, 3}; 402 IPC::ResponseBuilder rb{ctx, 3};
386 rb.Push(ResultSuccess); 403 rb.Push(ResultSuccess);
387 if (Settings::values.bcat_backend.GetValue() == "none") { 404 if (Network::GetHostIPv4Address().has_value()) {
388 rb.Push<u8>(0);
389 } else {
390 rb.Push<u8>(1); 405 rb.Push<u8>(1);
406 } else {
407 rb.Push<u8>(0);
391 } 408 }
392 } 409 }
393 void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) { 410 void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) {
@@ -395,10 +412,10 @@ private:
395 412
396 IPC::ResponseBuilder rb{ctx, 3}; 413 IPC::ResponseBuilder rb{ctx, 3};
397 rb.Push(ResultSuccess); 414 rb.Push(ResultSuccess);
398 if (Settings::values.bcat_backend.GetValue() == "none") { 415 if (Network::GetHostIPv4Address().has_value()) {
399 rb.Push<u8>(0);
400 } else {
401 rb.Push<u8>(1); 416 rb.Push<u8>(1);
417 } else {
418 rb.Push<u8>(0);
402 } 419 }
403 } 420 }
404}; 421};
diff --git a/src/core/hle/service/ns/language.cpp b/src/core/hle/service/ns/language.cpp
index 29c4a820c..7d9e4a20b 100644
--- a/src/core/hle/service/ns/language.cpp
+++ b/src/core/hle/service/ns/language.cpp
@@ -339,13 +339,16 @@ 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;
345 case Set::LanguageCode::KO: 346 case Set::LanguageCode::KO:
346 return ApplicationLanguage::Korean; 347 return ApplicationLanguage::Korean;
348 case Set::LanguageCode::ZH_TW:
347 case Set::LanguageCode::ZH_HANT: 349 case Set::LanguageCode::ZH_HANT:
348 return ApplicationLanguage::TraditionalChinese; 350 return ApplicationLanguage::TraditionalChinese;
351 case Set::LanguageCode::ZH_CN:
349 case Set::LanguageCode::ZH_HANS: 352 case Set::LanguageCode::ZH_HANS:
350 return ApplicationLanguage::SimplifiedChinese; 353 return ApplicationLanguage::SimplifiedChinese;
351 default: 354 default:
diff --git a/src/core/hle/service/ns/ns_language.h b/src/core/hle/service/ns/ns_language.h
deleted file mode 100644
index 59ac85a19..000000000
--- a/src/core/hle/service/ns/ns_language.h
+++ /dev/null
@@ -1,42 +0,0 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6#include <optional>
7#include <string>
8#include "common/common_types.h"
9#include "core/hle/service/set/set.h"
10
11namespace Service::NS {
12/// This is nn::ns::detail::ApplicationLanguage
13enum class ApplicationLanguage : u8 {
14 AmericanEnglish = 0,
15 BritishEnglish,
16 Japanese,
17 French,
18 German,
19 LatinAmericanSpanish,
20 Spanish,
21 Italian,
22 Dutch,
23 CanadianFrench,
24 Portuguese,
25 Russian,
26 Korean,
27 TraditionalChinese,
28 SimplifiedChinese,
29 Count
30};
31using ApplicationLanguagePriorityList =
32 const std::array<ApplicationLanguage, static_cast<std::size_t>(ApplicationLanguage::Count)>;
33
34constexpr u32 GetSupportedLanguageFlag(const ApplicationLanguage lang) {
35 return 1U << static_cast<u32>(lang);
36}
37
38const ApplicationLanguagePriorityList* GetApplicationLanguagePriorityList(ApplicationLanguage lang);
39std::optional<ApplicationLanguage> ConvertToApplicationLanguage(
40 Service::Set::LanguageCode language_code);
41std::optional<Service::Set::LanguageCode> ConvertToLanguageCode(ApplicationLanguage lang);
42} // namespace Service::NS \ No newline at end of file
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/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index 59ddf6298..b4c3a6099 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -9,17 +9,20 @@
9#include "core/core.h" 9#include "core/core.h"
10#include "core/hle/kernel/k_writable_event.h" 10#include "core/hle/kernel/k_writable_event.h"
11#include "core/hle/kernel/kernel.h" 11#include "core/hle/kernel/kernel.h"
12#include "core/hle/service/kernel_helpers.h"
12#include "core/hle/service/nvflinger/buffer_queue.h" 13#include "core/hle/service/nvflinger/buffer_queue.h"
13 14
14namespace Service::NVFlinger { 15namespace Service::NVFlinger {
15 16
16BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_) 17BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_,
17 : id(id_), layer_id(layer_id_), buffer_wait_event{kernel} { 18 KernelHelpers::ServiceContext& service_context_)
18 Kernel::KAutoObject::Create(std::addressof(buffer_wait_event)); 19 : id(id_), layer_id(layer_id_), service_context{service_context_} {
19 buffer_wait_event.Initialize("BufferQueue:WaitEvent"); 20 buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent");
20} 21}
21 22
22BufferQueue::~BufferQueue() = default; 23BufferQueue::~BufferQueue() {
24 service_context.CloseEvent(buffer_wait_event);
25}
23 26
24void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) { 27void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) {
25 ASSERT(slot < buffer_slots); 28 ASSERT(slot < buffer_slots);
@@ -41,7 +44,7 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer)
41 .multi_fence = {}, 44 .multi_fence = {},
42 }; 45 };
43 46
44 buffer_wait_event.GetWritableEvent().Signal(); 47 buffer_wait_event->GetWritableEvent().Signal();
45} 48}
46 49
47std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width, 50std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width,
@@ -119,7 +122,7 @@ void BufferQueue::CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& mult
119 } 122 }
120 free_buffers_condition.notify_one(); 123 free_buffers_condition.notify_one();
121 124
122 buffer_wait_event.GetWritableEvent().Signal(); 125 buffer_wait_event->GetWritableEvent().Signal();
123} 126}
124 127
125std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() { 128std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() {
@@ -154,7 +157,7 @@ void BufferQueue::ReleaseBuffer(u32 slot) {
154 } 157 }
155 free_buffers_condition.notify_one(); 158 free_buffers_condition.notify_one();
156 159
157 buffer_wait_event.GetWritableEvent().Signal(); 160 buffer_wait_event->GetWritableEvent().Signal();
158} 161}
159 162
160void BufferQueue::Connect() { 163void BufferQueue::Connect() {
@@ -169,7 +172,7 @@ void BufferQueue::Disconnect() {
169 std::unique_lock lock{queue_sequence_mutex}; 172 std::unique_lock lock{queue_sequence_mutex};
170 queue_sequence.clear(); 173 queue_sequence.clear();
171 } 174 }
172 buffer_wait_event.GetWritableEvent().Signal(); 175 buffer_wait_event->GetWritableEvent().Signal();
173 is_connect = false; 176 is_connect = false;
174 free_buffers_condition.notify_one(); 177 free_buffers_condition.notify_one();
175} 178}
@@ -189,11 +192,11 @@ u32 BufferQueue::Query(QueryType type) {
189} 192}
190 193
191Kernel::KWritableEvent& BufferQueue::GetWritableBufferWaitEvent() { 194Kernel::KWritableEvent& BufferQueue::GetWritableBufferWaitEvent() {
192 return buffer_wait_event.GetWritableEvent(); 195 return buffer_wait_event->GetWritableEvent();
193} 196}
194 197
195Kernel::KReadableEvent& BufferQueue::GetBufferWaitEvent() { 198Kernel::KReadableEvent& BufferQueue::GetBufferWaitEvent() {
196 return buffer_wait_event.GetReadableEvent(); 199 return buffer_wait_event->GetReadableEvent();
197} 200}
198 201
199} // namespace Service::NVFlinger 202} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
index 61e337ac5..759247eb0 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ b/src/core/hle/service/nvflinger/buffer_queue.h
@@ -24,6 +24,10 @@ class KReadableEvent;
24class KWritableEvent; 24class KWritableEvent;
25} // namespace Kernel 25} // namespace Kernel
26 26
27namespace Service::KernelHelpers {
28class ServiceContext;
29} // namespace Service::KernelHelpers
30
27namespace Service::NVFlinger { 31namespace Service::NVFlinger {
28 32
29constexpr u32 buffer_slots = 0x40; 33constexpr u32 buffer_slots = 0x40;
@@ -54,7 +58,8 @@ public:
54 NativeWindowFormat = 2, 58 NativeWindowFormat = 2,
55 }; 59 };
56 60
57 explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_); 61 explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_,
62 KernelHelpers::ServiceContext& service_context_);
58 ~BufferQueue(); 63 ~BufferQueue();
59 64
60 enum class BufferTransformFlags : u32 { 65 enum class BufferTransformFlags : u32 {
@@ -130,12 +135,14 @@ private:
130 std::list<u32> free_buffers; 135 std::list<u32> free_buffers;
131 std::array<Buffer, buffer_slots> buffers; 136 std::array<Buffer, buffer_slots> buffers;
132 std::list<u32> queue_sequence; 137 std::list<u32> queue_sequence;
133 Kernel::KEvent buffer_wait_event; 138 Kernel::KEvent* buffer_wait_event{};
134 139
135 std::mutex free_buffers_mutex; 140 std::mutex free_buffers_mutex;
136 std::condition_variable free_buffers_condition; 141 std::condition_variable free_buffers_condition;
137 142
138 std::mutex queue_sequence_mutex; 143 std::mutex queue_sequence_mutex;
144
145 KernelHelpers::ServiceContext& service_context;
139}; 146};
140 147
141} // namespace Service::NVFlinger 148} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 941748970..00bff8caf 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -61,12 +61,13 @@ void NVFlinger::SplitVSync() {
61 } 61 }
62} 62}
63 63
64NVFlinger::NVFlinger(Core::System& system_) : system(system_) { 64NVFlinger::NVFlinger(Core::System& system_)
65 displays.emplace_back(0, "Default", system); 65 : system(system_), service_context(system_, "nvflinger") {
66 displays.emplace_back(1, "External", system); 66 displays.emplace_back(0, "Default", service_context, system);
67 displays.emplace_back(2, "Edid", system); 67 displays.emplace_back(1, "External", service_context, system);
68 displays.emplace_back(3, "Internal", system); 68 displays.emplace_back(2, "Edid", service_context, system);
69 displays.emplace_back(4, "Null", system); 69 displays.emplace_back(3, "Internal", service_context, system);
70 displays.emplace_back(4, "Null", service_context, system);
70 guard = std::make_shared<std::mutex>(); 71 guard = std::make_shared<std::mutex>();
71 72
72 // Schedule the screen composition events 73 // Schedule the screen composition events
@@ -146,7 +147,7 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
146void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) { 147void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) {
147 const u32 buffer_queue_id = next_buffer_queue_id++; 148 const u32 buffer_queue_id = next_buffer_queue_id++;
148 buffer_queues.emplace_back( 149 buffer_queues.emplace_back(
149 std::make_unique<BufferQueue>(system.Kernel(), buffer_queue_id, layer_id)); 150 std::make_unique<BufferQueue>(system.Kernel(), buffer_queue_id, layer_id, service_context));
150 display.CreateLayer(layer_id, *buffer_queues.back()); 151 display.CreateLayer(layer_id, *buffer_queues.back());
151} 152}
152 153
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index d80fd07ef..6d84cafb4 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -15,6 +15,7 @@
15#include <vector> 15#include <vector>
16 16
17#include "common/common_types.h" 17#include "common/common_types.h"
18#include "core/hle/service/kernel_helpers.h"
18 19
19namespace Common { 20namespace Common {
20class Event; 21class Event;
@@ -135,6 +136,8 @@ private:
135 std::unique_ptr<std::thread> vsync_thread; 136 std::unique_ptr<std::thread> vsync_thread;
136 std::unique_ptr<Common::Event> wait_event; 137 std::unique_ptr<Common::Event> wait_event;
137 std::atomic<bool> is_running{}; 138 std::atomic<bool> is_running{};
139
140 KernelHelpers::ServiceContext service_context;
138}; 141};
139 142
140} // namespace Service::NVFlinger 143} // namespace Service::NVFlinger
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/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp
index 0dd342dbf..b7705c02a 100644
--- a/src/core/hle/service/vi/display/vi_display.cpp
+++ b/src/core/hle/service/vi/display/vi_display.cpp
@@ -12,18 +12,21 @@
12#include "core/hle/kernel/k_event.h" 12#include "core/hle/kernel/k_event.h"
13#include "core/hle/kernel/k_readable_event.h" 13#include "core/hle/kernel/k_readable_event.h"
14#include "core/hle/kernel/k_writable_event.h" 14#include "core/hle/kernel/k_writable_event.h"
15#include "core/hle/service/kernel_helpers.h"
15#include "core/hle/service/vi/display/vi_display.h" 16#include "core/hle/service/vi/display/vi_display.h"
16#include "core/hle/service/vi/layer/vi_layer.h" 17#include "core/hle/service/vi/layer/vi_layer.h"
17 18
18namespace Service::VI { 19namespace Service::VI {
19 20
20Display::Display(u64 id, std::string name_, Core::System& system) 21Display::Display(u64 id, std::string name_, KernelHelpers::ServiceContext& service_context_,
21 : display_id{id}, name{std::move(name_)}, vsync_event{system.Kernel()} { 22 Core::System& system_)
22 Kernel::KAutoObject::Create(std::addressof(vsync_event)); 23 : display_id{id}, name{std::move(name_)}, service_context{service_context_} {
23 vsync_event.Initialize(fmt::format("Display VSync Event {}", id)); 24 vsync_event = service_context.CreateEvent(fmt::format("Display VSync Event {}", id));
24} 25}
25 26
26Display::~Display() = default; 27Display::~Display() {
28 service_context.CloseEvent(vsync_event);
29}
27 30
28Layer& Display::GetLayer(std::size_t index) { 31Layer& Display::GetLayer(std::size_t index) {
29 return *layers.at(index); 32 return *layers.at(index);
@@ -34,11 +37,11 @@ const Layer& Display::GetLayer(std::size_t index) const {
34} 37}
35 38
36Kernel::KReadableEvent& Display::GetVSyncEvent() { 39Kernel::KReadableEvent& Display::GetVSyncEvent() {
37 return vsync_event.GetReadableEvent(); 40 return vsync_event->GetReadableEvent();
38} 41}
39 42
40void Display::SignalVSyncEvent() { 43void Display::SignalVSyncEvent() {
41 vsync_event.GetWritableEvent().Signal(); 44 vsync_event->GetWritableEvent().Signal();
42} 45}
43 46
44void Display::CreateLayer(u64 layer_id, NVFlinger::BufferQueue& buffer_queue) { 47void Display::CreateLayer(u64 layer_id, NVFlinger::BufferQueue& buffer_queue) {
diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h
index 166f2a4cc..0979fc421 100644
--- a/src/core/hle/service/vi/display/vi_display.h
+++ b/src/core/hle/service/vi/display/vi_display.h
@@ -18,6 +18,9 @@ class KEvent;
18namespace Service::NVFlinger { 18namespace Service::NVFlinger {
19class BufferQueue; 19class BufferQueue;
20} 20}
21namespace Service::KernelHelpers {
22class ServiceContext;
23} // namespace Service::KernelHelpers
21 24
22namespace Service::VI { 25namespace Service::VI {
23 26
@@ -31,10 +34,13 @@ class Display {
31public: 34public:
32 /// Constructs a display with a given unique ID and name. 35 /// Constructs a display with a given unique ID and name.
33 /// 36 ///
34 /// @param id The unique ID for this display. 37 /// @param id The unique ID for this display.
38 /// @param service_context_ The ServiceContext for the owning service.
35 /// @param name_ The name for this display. 39 /// @param name_ The name for this display.
40 /// @param system_ The global system instance.
36 /// 41 ///
37 Display(u64 id, std::string name_, Core::System& system); 42 Display(u64 id, std::string name_, KernelHelpers::ServiceContext& service_context_,
43 Core::System& system_);
38 ~Display(); 44 ~Display();
39 45
40 /// Gets the unique ID assigned to this display. 46 /// Gets the unique ID assigned to this display.
@@ -98,9 +104,10 @@ public:
98private: 104private:
99 u64 display_id; 105 u64 display_id;
100 std::string name; 106 std::string name;
107 KernelHelpers::ServiceContext& service_context;
101 108
102 std::vector<std::shared_ptr<Layer>> layers; 109 std::vector<std::shared_ptr<Layer>> layers;
103 Kernel::KEvent vsync_event; 110 Kernel::KEvent* vsync_event{};
104}; 111};
105 112
106} // namespace Service::VI 113} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 3e5949d52..8e8fc40ca 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -1158,7 +1158,7 @@ private:
1158 1158
1159 const auto layer_id = nv_flinger.CreateLayer(display_id); 1159 const auto layer_id = nv_flinger.CreateLayer(display_id);
1160 if (!layer_id) { 1160 if (!layer_id) {
1161 LOG_ERROR(Service_VI, "Layer not found! layer_id={}", *layer_id); 1161 LOG_ERROR(Service_VI, "Layer not found! display_id={}", display_id);
1162 IPC::ResponseBuilder rb{ctx, 2}; 1162 IPC::ResponseBuilder rb{ctx, 2};
1163 rb.Push(ERR_NOT_FOUND); 1163 rb.Push(ERR_NOT_FOUND);
1164 return; 1164 return;
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..4732d4485 100644
--- a/src/core/network/network.cpp
+++ b/src/core/network/network.cpp
@@ -10,9 +10,10 @@
10#include "common/common_funcs.h" 10#include "common/common_funcs.h"
11 11
12#ifdef _WIN32 12#ifdef _WIN32
13#define _WINSOCK_DEPRECATED_NO_WARNINGS // gethostname
14#include <winsock2.h> 13#include <winsock2.h>
14#include <ws2tcpip.h>
15#elif YUZU_UNIX 15#elif YUZU_UNIX
16#include <arpa/inet.h>
16#include <errno.h> 17#include <errno.h>
17#include <fcntl.h> 18#include <fcntl.h>
18#include <netdb.h> 19#include <netdb.h>
@@ -27,7 +28,9 @@
27#include "common/assert.h" 28#include "common/assert.h"
28#include "common/common_types.h" 29#include "common/common_types.h"
29#include "common/logging/log.h" 30#include "common/logging/log.h"
31#include "common/settings.h"
30#include "core/network/network.h" 32#include "core/network/network.h"
33#include "core/network/network_interface.h"
31#include "core/network/sockets.h" 34#include "core/network/sockets.h"
32 35
33namespace Network { 36namespace Network {
@@ -47,11 +50,6 @@ void Finalize() {
47 WSACleanup(); 50 WSACleanup();
48} 51}
49 52
50constexpr IPv4Address TranslateIPv4(in_addr addr) {
51 auto& bytes = addr.S_un.S_un_b;
52 return IPv4Address{bytes.s_b1, bytes.s_b2, bytes.s_b3, bytes.s_b4};
53}
54
55sockaddr TranslateFromSockAddrIn(SockAddrIn input) { 53sockaddr TranslateFromSockAddrIn(SockAddrIn input) {
56 sockaddr_in result; 54 sockaddr_in result;
57 55
@@ -138,12 +136,6 @@ void Initialize() {}
138 136
139void Finalize() {} 137void Finalize() {}
140 138
141constexpr IPv4Address TranslateIPv4(in_addr addr) {
142 const u32 bytes = addr.s_addr;
143 return IPv4Address{static_cast<u8>(bytes), static_cast<u8>(bytes >> 8),
144 static_cast<u8>(bytes >> 16), static_cast<u8>(bytes >> 24)};
145}
146
147sockaddr TranslateFromSockAddrIn(SockAddrIn input) { 139sockaddr TranslateFromSockAddrIn(SockAddrIn input) {
148 sockaddr_in result; 140 sockaddr_in result;
149 141
@@ -182,7 +174,7 @@ linger MakeLinger(bool enable, u32 linger_value) {
182} 174}
183 175
184bool EnableNonBlock(int fd, bool enable) { 176bool EnableNonBlock(int fd, bool enable) {
185 int flags = fcntl(fd, F_GETFD); 177 int flags = fcntl(fd, F_GETFL);
186 if (flags == -1) { 178 if (flags == -1) {
187 return false; 179 return false;
188 } 180 }
@@ -191,7 +183,7 @@ bool EnableNonBlock(int fd, bool enable) {
191 } else { 183 } else {
192 flags &= ~O_NONBLOCK; 184 flags &= ~O_NONBLOCK;
193 } 185 }
194 return fcntl(fd, F_SETFD, flags) == 0; 186 return fcntl(fd, F_SETFL, flags) == 0;
195} 187}
196 188
197Errno TranslateNativeError(int e) { 189Errno TranslateNativeError(int e) {
@@ -227,8 +219,12 @@ Errno GetAndLogLastError() {
227#else 219#else
228 int e = errno; 220 int e = errno;
229#endif 221#endif
222 const Errno err = TranslateNativeError(e);
223 if (err == Errno::AGAIN) {
224 return err;
225 }
230 LOG_ERROR(Network, "Socket operation error: {}", NativeErrorToString(e)); 226 LOG_ERROR(Network, "Socket operation error: {}", NativeErrorToString(e));
231 return TranslateNativeError(e); 227 return err;
232} 228}
233 229
234int TranslateDomain(Domain domain) { 230int TranslateDomain(Domain domain) {
@@ -353,27 +349,29 @@ NetworkInstance::~NetworkInstance() {
353 Finalize(); 349 Finalize();
354} 350}
355 351
356std::pair<IPv4Address, Errno> GetHostIPv4Address() { 352std::optional<IPv4Address> GetHostIPv4Address() {
357 std::array<char, 256> name{}; 353 const std::string& selected_network_interface = Settings::values.network_interface.GetValue();
358 if (gethostname(name.data(), static_cast<int>(name.size()) - 1) == SOCKET_ERROR) { 354 const auto network_interfaces = Network::GetAvailableNetworkInterfaces();
359 return {IPv4Address{}, GetAndLogLastError()}; 355 if (network_interfaces.size() == 0) {
356 LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces");
357 return {};
360 } 358 }
361 359
362 hostent* const ent = gethostbyname(name.data()); 360 const auto res =
363 if (!ent) { 361 std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) {
364 return {IPv4Address{}, GetAndLogLastError()}; 362 return iface.name == selected_network_interface;
365 } 363 });
366 if (ent->h_addr_list == nullptr) { 364
367 UNIMPLEMENTED_MSG("No addr provided in hostent->h_addr_list"); 365 if (res != network_interfaces.end()) {
368 return {IPv4Address{}, Errno::SUCCESS}; 366 char ip_addr[16] = {};
369 } 367 ASSERT(inet_ntop(AF_INET, &res->ip_address, ip_addr, sizeof(ip_addr)) != nullptr);
370 if (ent->h_length != sizeof(in_addr)) { 368 LOG_INFO(Network, "IP address: {}", ip_addr);
371 UNIMPLEMENTED_MSG("Unexpected size={} in hostent->h_length", ent->h_length);
372 }
373 369
374 in_addr addr; 370 return TranslateIPv4(res->ip_address);
375 std::memcpy(&addr, ent->h_addr_list[0], sizeof(addr)); 371 } else {
376 return {TranslateIPv4(addr), Errno::SUCCESS}; 372 LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface);
373 return {};
374 }
377} 375}
378 376
379std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) { 377std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) {
@@ -570,7 +568,7 @@ std::pair<s32, Errno> Socket::SendTo(u32 flags, const std::vector<u8>& message,
570 ASSERT(flags == 0); 568 ASSERT(flags == 0);
571 569
572 const sockaddr* to = nullptr; 570 const sockaddr* to = nullptr;
573 const int tolen = addr ? 0 : sizeof(sockaddr); 571 const int tolen = addr ? sizeof(sockaddr) : 0;
574 sockaddr host_addr_in; 572 sockaddr host_addr_in;
575 573
576 if (addr) { 574 if (addr) {
diff --git a/src/core/network/network.h b/src/core/network/network.h
index bd30f1899..fdd3e4655 100644
--- a/src/core/network/network.h
+++ b/src/core/network/network.h
@@ -5,11 +5,18 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <optional>
8#include <utility> 9#include <utility>
9 10
10#include "common/common_funcs.h" 11#include "common/common_funcs.h"
11#include "common/common_types.h" 12#include "common/common_types.h"
12 13
14#ifdef _WIN32
15#include <winsock2.h>
16#elif YUZU_UNIX
17#include <netinet/in.h>
18#endif
19
13namespace Network { 20namespace Network {
14 21
15class Socket; 22class Socket;
@@ -92,8 +99,21 @@ public:
92 ~NetworkInstance(); 99 ~NetworkInstance();
93}; 100};
94 101
102#ifdef _WIN32
103constexpr IPv4Address TranslateIPv4(in_addr addr) {
104 auto& bytes = addr.S_un.S_un_b;
105 return IPv4Address{bytes.s_b1, bytes.s_b2, bytes.s_b3, bytes.s_b4};
106}
107#elif YUZU_UNIX
108constexpr IPv4Address TranslateIPv4(in_addr addr) {
109 const u32 bytes = addr.s_addr;
110 return IPv4Address{static_cast<u8>(bytes), static_cast<u8>(bytes >> 8),
111 static_cast<u8>(bytes >> 16), static_cast<u8>(bytes >> 24)};
112}
113#endif
114
95/// @brief Returns host's IPv4 address 115/// @brief Returns host's IPv4 address
96/// @return Pair of an array of human ordered IPv4 address (e.g. 192.168.0.1) and an error code 116/// @return human ordered IPv4 address (e.g. 192.168.0.1) as an array
97std::pair<IPv4Address, Errno> GetHostIPv4Address(); 117std::optional<IPv4Address> GetHostIPv4Address();
98 118
99} // namespace Network 119} // namespace Network
diff --git a/src/core/network/network_interface.cpp b/src/core/network/network_interface.cpp
new file mode 100644
index 000000000..cecc9aa11
--- /dev/null
+++ b/src/core/network/network_interface.cpp
@@ -0,0 +1,203 @@
1// Copyright 2021 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <fstream>
7#include <sstream>
8#include <vector>
9
10#include "common/bit_cast.h"
11#include "common/common_types.h"
12#include "common/logging/log.h"
13#include "common/settings.h"
14#include "common/string_util.h"
15#include "core/network/network_interface.h"
16
17#ifdef _WIN32
18#include <iphlpapi.h>
19#else
20#include <cerrno>
21#include <ifaddrs.h>
22#include <net/if.h>
23#endif
24
25namespace Network {
26
27#ifdef _WIN32
28
29std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
30 std::vector<IP_ADAPTER_ADDRESSES> adapter_addresses;
31 DWORD ret = ERROR_BUFFER_OVERFLOW;
32 DWORD buf_size = 0;
33
34 // retry up to 5 times
35 for (int i = 0; i < 5 && ret == ERROR_BUFFER_OVERFLOW; i++) {
36 ret = GetAdaptersAddresses(
37 AF_INET, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_GATEWAYS,
38 nullptr, adapter_addresses.data(), &buf_size);
39
40 if (ret == ERROR_BUFFER_OVERFLOW) {
41 adapter_addresses.resize((buf_size / sizeof(IP_ADAPTER_ADDRESSES)) + 1);
42 } else {
43 break;
44 }
45 }
46
47 if (ret == NO_ERROR) {
48 std::vector<NetworkInterface> result;
49
50 for (auto current_address = adapter_addresses.data(); current_address != nullptr;
51 current_address = current_address->Next) {
52 if (current_address->FirstUnicastAddress == nullptr ||
53 current_address->FirstUnicastAddress->Address.lpSockaddr == nullptr) {
54 continue;
55 }
56
57 if (current_address->OperStatus != IfOperStatusUp) {
58 continue;
59 }
60
61 const auto ip_addr = Common::BitCast<struct sockaddr_in>(
62 *current_address->FirstUnicastAddress->Address.lpSockaddr)
63 .sin_addr;
64
65 ULONG mask = 0;
66 if (ConvertLengthToIpv4Mask(current_address->FirstUnicastAddress->OnLinkPrefixLength,
67 &mask) != NO_ERROR) {
68 LOG_ERROR(Network, "Failed to convert IPv4 prefix length to subnet mask");
69 continue;
70 }
71
72 struct in_addr gateway = {.S_un{.S_addr{0}}};
73 if (current_address->FirstGatewayAddress != nullptr &&
74 current_address->FirstGatewayAddress->Address.lpSockaddr != nullptr) {
75 gateway = Common::BitCast<struct sockaddr_in>(
76 *current_address->FirstGatewayAddress->Address.lpSockaddr)
77 .sin_addr;
78 }
79
80 result.push_back(NetworkInterface{
81 .name{Common::UTF16ToUTF8(std::wstring{current_address->FriendlyName})},
82 .ip_address{ip_addr},
83 .subnet_mask = in_addr{.S_un{.S_addr{mask}}},
84 .gateway = gateway});
85 }
86
87 return result;
88 } else {
89 LOG_ERROR(Network, "Failed to get network interfaces with GetAdaptersAddresses");
90 return {};
91 }
92}
93
94#else
95
96std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
97 std::vector<NetworkInterface> result;
98
99 struct ifaddrs* ifaddr = nullptr;
100
101 if (getifaddrs(&ifaddr) != 0) {
102 LOG_ERROR(Network, "Failed to get network interfaces with getifaddrs: {}",
103 std::strerror(errno));
104 return result;
105 }
106
107 for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
108 if (ifa->ifa_addr == nullptr || ifa->ifa_netmask == nullptr) {
109 continue;
110 }
111
112 if (ifa->ifa_addr->sa_family != AF_INET) {
113 continue;
114 }
115
116 if ((ifa->ifa_flags & IFF_UP) == 0 || (ifa->ifa_flags & IFF_LOOPBACK) != 0) {
117 continue;
118 }
119
120 std::uint32_t gateway{0};
121 std::ifstream file{"/proc/net/route"};
122 if (file.is_open()) {
123
124 // ignore header
125 file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
126
127 bool gateway_found = false;
128
129 for (std::string line; std::getline(file, line);) {
130 std::istringstream iss{line};
131
132 std::string iface_name{};
133 iss >> iface_name;
134 if (iface_name != ifa->ifa_name) {
135 continue;
136 }
137
138 iss >> std::hex;
139
140 std::uint32_t dest{0};
141 iss >> dest;
142 if (dest != 0) {
143 // not the default route
144 continue;
145 }
146
147 iss >> gateway;
148
149 std::uint16_t flags{0};
150 iss >> flags;
151
152 // flag RTF_GATEWAY (defined in <linux/route.h>)
153 if ((flags & 0x2) == 0) {
154 continue;
155 }
156
157 gateway_found = true;
158 break;
159 }
160
161 if (!gateway_found) {
162 gateway = 0;
163 }
164 } else {
165 LOG_ERROR(Network, "Failed to open \"/proc/net/route\"");
166 }
167
168 result.push_back(NetworkInterface{
169 .name{ifa->ifa_name},
170 .ip_address{Common::BitCast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr},
171 .subnet_mask{Common::BitCast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr},
172 .gateway{in_addr{.s_addr = gateway}}});
173 }
174
175 freeifaddrs(ifaddr);
176
177 return result;
178}
179
180#endif
181
182std::optional<NetworkInterface> GetSelectedNetworkInterface() {
183 const std::string& selected_network_interface = Settings::values.network_interface.GetValue();
184 const auto network_interfaces = Network::GetAvailableNetworkInterfaces();
185 if (network_interfaces.size() == 0) {
186 LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces");
187 return {};
188 }
189
190 const auto res =
191 std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) {
192 return iface.name == selected_network_interface;
193 });
194
195 if (res != network_interfaces.end()) {
196 return *res;
197 } else {
198 LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface);
199 return {};
200 }
201}
202
203} // namespace Network
diff --git a/src/core/network/network_interface.h b/src/core/network/network_interface.h
new file mode 100644
index 000000000..980edb2f5
--- /dev/null
+++ b/src/core/network/network_interface.h
@@ -0,0 +1,29 @@
1// Copyright 2021 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <optional>
8#include <string>
9#include <vector>
10
11#ifdef _WIN32
12#include <winsock2.h>
13#else
14#include <netinet/in.h>
15#endif
16
17namespace Network {
18
19struct NetworkInterface {
20 std::string name;
21 struct in_addr ip_address;
22 struct in_addr subnet_mask;
23 struct in_addr gateway;
24};
25
26std::vector<NetworkInterface> GetAvailableNetworkInterfaces();
27std::optional<NetworkInterface> GetSelectedNetworkInterface();
28
29} // namespace Network
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..f102410d1 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
@@ -869,6 +889,9 @@ SDLState::SDLState() {
869 RegisterFactory<VibrationDevice>("sdl", vibration_factory); 889 RegisterFactory<VibrationDevice>("sdl", vibration_factory);
870 RegisterFactory<MotionDevice>("sdl", motion_factory); 890 RegisterFactory<MotionDevice>("sdl", motion_factory);
871 891
892 // Disable raw input. When enabled this setting causes SDL to die when a web applet opens
893 SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, "0");
894
872 // Enable HIDAPI rumble. This prevents SDL from disabling motion on PS4 and PS5 controllers 895 // Enable HIDAPI rumble. This prevents SDL from disabling motion on PS4 and PS5 controllers
873 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1"); 896 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1");
874 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1"); 897 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1");
@@ -995,6 +1018,7 @@ Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid
995 params.Set("port", port); 1018 params.Set("port", port);
996 params.Set("guid", std::move(guid)); 1019 params.Set("guid", std::move(guid));
997 params.Set("button", button); 1020 params.Set("button", button);
1021 params.Set("toggle", false);
998 return params; 1022 return params;
999} 1023}
1000 1024
@@ -1134,13 +1158,15 @@ Common::ParamPackage BuildParamPackageForBinding(int port, const std::string& gu
1134} 1158}
1135 1159
1136Common::ParamPackage BuildParamPackageForAnalog(int port, const std::string& guid, int axis_x, 1160Common::ParamPackage BuildParamPackageForAnalog(int port, const std::string& guid, int axis_x,
1137 int axis_y) { 1161 int axis_y, float offset_x, float offset_y) {
1138 Common::ParamPackage params; 1162 Common::ParamPackage params;
1139 params.Set("engine", "sdl"); 1163 params.Set("engine", "sdl");
1140 params.Set("port", port); 1164 params.Set("port", port);
1141 params.Set("guid", guid); 1165 params.Set("guid", guid);
1142 params.Set("axis_x", axis_x); 1166 params.Set("axis_x", axis_x);
1143 params.Set("axis_y", axis_y); 1167 params.Set("axis_y", axis_y);
1168 params.Set("offset_x", offset_x);
1169 params.Set("offset_y", offset_y);
1144 params.Set("invert_x", "+"); 1170 params.Set("invert_x", "+");
1145 params.Set("invert_y", "+"); 1171 params.Set("invert_y", "+");
1146 return params; 1172 return params;
@@ -1342,24 +1368,39 @@ AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& pa
1342 const auto& binding_left_y = 1368 const auto& binding_left_y =
1343 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY); 1369 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
1344 if (params.Has("guid2")) { 1370 if (params.Has("guid2")) {
1371 joystick2->PreSetAxis(binding_left_x.value.axis);
1372 joystick2->PreSetAxis(binding_left_y.value.axis);
1373 const auto left_offset_x = -joystick2->GetAxis(binding_left_x.value.axis, 1.0f, 0);
1374 const auto left_offset_y = -joystick2->GetAxis(binding_left_y.value.axis, 1.0f, 0);
1345 mapping.insert_or_assign( 1375 mapping.insert_or_assign(
1346 Settings::NativeAnalog::LStick, 1376 Settings::NativeAnalog::LStick,
1347 BuildParamPackageForAnalog(joystick2->GetPort(), joystick2->GetGUID(), 1377 BuildParamPackageForAnalog(joystick2->GetPort(), joystick2->GetGUID(),
1348 binding_left_x.value.axis, binding_left_y.value.axis)); 1378 binding_left_x.value.axis, binding_left_y.value.axis,
1379 left_offset_x, left_offset_y));
1349 } else { 1380 } else {
1381 joystick->PreSetAxis(binding_left_x.value.axis);
1382 joystick->PreSetAxis(binding_left_y.value.axis);
1383 const auto left_offset_x = -joystick->GetAxis(binding_left_x.value.axis, 1.0f, 0);
1384 const auto left_offset_y = -joystick->GetAxis(binding_left_y.value.axis, 1.0f, 0);
1350 mapping.insert_or_assign( 1385 mapping.insert_or_assign(
1351 Settings::NativeAnalog::LStick, 1386 Settings::NativeAnalog::LStick,
1352 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), 1387 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
1353 binding_left_x.value.axis, binding_left_y.value.axis)); 1388 binding_left_x.value.axis, binding_left_y.value.axis,
1389 left_offset_x, left_offset_y));
1354 } 1390 }
1355 const auto& binding_right_x = 1391 const auto& binding_right_x =
1356 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX); 1392 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX);
1357 const auto& binding_right_y = 1393 const auto& binding_right_y =
1358 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY); 1394 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY);
1395 joystick->PreSetAxis(binding_right_x.value.axis);
1396 joystick->PreSetAxis(binding_right_y.value.axis);
1397 const auto right_offset_x = -joystick->GetAxis(binding_right_x.value.axis, 1.0f, 0);
1398 const auto right_offset_y = -joystick->GetAxis(binding_right_y.value.axis, 1.0f, 0);
1359 mapping.insert_or_assign(Settings::NativeAnalog::RStick, 1399 mapping.insert_or_assign(Settings::NativeAnalog::RStick,
1360 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), 1400 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
1361 binding_right_x.value.axis, 1401 binding_right_x.value.axis,
1362 binding_right_y.value.axis)); 1402 binding_right_y.value.axis, right_offset_x,
1403 right_offset_y));
1363 return mapping; 1404 return mapping;
1364} 1405}
1365 1406
@@ -1563,8 +1604,9 @@ public:
1563 } 1604 }
1564 1605
1565 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) { 1606 if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) {
1607 // Set offset to zero since the joystick is not on center
1566 auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), 1608 auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
1567 first_axis, axis); 1609 first_axis, axis, 0, 0);
1568 first_axis = -1; 1610 first_axis = -1;
1569 return params; 1611 return params;
1570 } 1612 }
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
index fb8c02a77..14c77f162 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
@@ -298,14 +298,10 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
298 if (IR::IsGeneric(attr)) { 298 if (IR::IsGeneric(attr)) {
299 const u32 index{IR::GenericAttributeIndex(attr)}; 299 const u32 index{IR::GenericAttributeIndex(attr)};
300 const std::optional<AttrInfo> type{AttrTypes(ctx, index)}; 300 const std::optional<AttrInfo> type{AttrTypes(ctx, index)};
301 if (!type) { 301 if (!type || !ctx.runtime_info.previous_stage_stores.Generic(index, element)) {
302 // Attribute is disabled 302 // Attribute is disabled or varying component is not written
303 return ctx.Const(element == 3 ? 1.0f : 0.0f); 303 return ctx.Const(element == 3 ? 1.0f : 0.0f);
304 } 304 }
305 if (!ctx.runtime_info.previous_stage_stores.Generic(index, element)) {
306 // Varying component is not written
307 return ctx.Const(type && element == 3 ? 1.0f : 0.0f);
308 }
309 const Id generic_id{ctx.input_generics.at(index)}; 305 const Id generic_id{ctx.input_generics.at(index)};
310 const Id pointer{AttrPointer(ctx, type->pointer, vertex, generic_id, ctx.Const(element))}; 306 const Id pointer{AttrPointer(ctx, type->pointer, vertex, generic_id, ctx.Const(element))};
311 const Id value{ctx.OpLoad(type->id, pointer)}; 307 const Id value{ctx.OpLoad(type->id, pointer)};
diff --git a/src/shader_recompiler/frontend/ir/value.h b/src/shader_recompiler/frontend/ir/value.h
index 795194d41..334bb47aa 100644
--- a/src/shader_recompiler/frontend/ir/value.h
+++ b/src/shader_recompiler/frontend/ir/value.h
@@ -57,6 +57,7 @@ public:
57 57
58 [[nodiscard]] IR::Inst* Inst() const; 58 [[nodiscard]] IR::Inst* Inst() const;
59 [[nodiscard]] IR::Inst* InstRecursive() const; 59 [[nodiscard]] IR::Inst* InstRecursive() const;
60 [[nodiscard]] IR::Inst* TryInstRecursive() const;
60 [[nodiscard]] IR::Value Resolve() const; 61 [[nodiscard]] IR::Value Resolve() const;
61 [[nodiscard]] IR::Reg Reg() const; 62 [[nodiscard]] IR::Reg Reg() const;
62 [[nodiscard]] IR::Pred Pred() const; 63 [[nodiscard]] IR::Pred Pred() const;
@@ -308,6 +309,13 @@ inline IR::Inst* Value::InstRecursive() const {
308 return inst; 309 return inst;
309} 310}
310 311
312inline IR::Inst* Value::TryInstRecursive() const {
313 if (IsIdentity()) {
314 return inst->Arg(0).TryInstRecursive();
315 }
316 return type == Type::Opaque ? inst : nullptr;
317}
318
311inline IR::Value Value::Resolve() const { 319inline IR::Value Value::Resolve() const {
312 if (IsIdentity()) { 320 if (IsIdentity()) {
313 return inst->Arg(0).Resolve(); 321 return inst->Arg(0).Resolve();
diff --git a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
index 5ead930f1..f69e1c9cc 100644
--- a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
+++ b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
@@ -111,6 +111,8 @@ void VisitUsages(Info& info, IR::Inst& inst) {
111 case IR::Opcode::ConvertF16U16: 111 case IR::Opcode::ConvertF16U16:
112 case IR::Opcode::ConvertF16U32: 112 case IR::Opcode::ConvertF16U32:
113 case IR::Opcode::ConvertF16U64: 113 case IR::Opcode::ConvertF16U64:
114 case IR::Opcode::ConvertF16F32:
115 case IR::Opcode::ConvertF32F16:
114 case IR::Opcode::FPAbs16: 116 case IR::Opcode::FPAbs16:
115 case IR::Opcode::FPAdd16: 117 case IR::Opcode::FPAdd16:
116 case IR::Opcode::FPCeil16: 118 case IR::Opcode::FPCeil16:
diff --git a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
index 8dd6d6c2c..d089fdd12 100644
--- a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
+++ b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <functional>
6#include <tuple> 7#include <tuple>
7#include <type_traits> 8#include <type_traits>
8 9
@@ -88,6 +89,26 @@ bool FoldWhenAllImmediates(IR::Inst& inst, Func&& func) {
88 return true; 89 return true;
89} 90}
90 91
92/// Return true when all values in a range are equal
93template <typename Range>
94bool AreEqual(const Range& range) {
95 auto resolver{[](const auto& value) { return value.Resolve(); }};
96 auto equal{[](const IR::Value& lhs, const IR::Value& rhs) {
97 if (lhs == rhs) {
98 return true;
99 }
100 // Not equal, but try to match if they read the same constant buffer
101 if (!lhs.IsImmediate() && !rhs.IsImmediate() &&
102 lhs.Inst()->GetOpcode() == IR::Opcode::GetCbufU32 &&
103 rhs.Inst()->GetOpcode() == IR::Opcode::GetCbufU32 &&
104 lhs.Inst()->Arg(0) == rhs.Inst()->Arg(0) && lhs.Inst()->Arg(1) == rhs.Inst()->Arg(1)) {
105 return true;
106 }
107 return false;
108 }};
109 return std::ranges::adjacent_find(range, std::not_fn(equal), resolver) == std::end(range);
110}
111
91void FoldGetRegister(IR::Inst& inst) { 112void FoldGetRegister(IR::Inst& inst) {
92 if (inst.Arg(0).Reg() == IR::Reg::RZ) { 113 if (inst.Arg(0).Reg() == IR::Reg::RZ) {
93 inst.ReplaceUsesWith(IR::Value{u32{0}}); 114 inst.ReplaceUsesWith(IR::Value{u32{0}});
@@ -100,6 +121,157 @@ void FoldGetPred(IR::Inst& inst) {
100 } 121 }
101} 122}
102 123
124/// Replaces the XMAD pattern generated by an integer FMA
125bool FoldXmadMultiplyAdd(IR::Block& block, IR::Inst& inst) {
126 /*
127 * We are looking for this specific pattern:
128 * %6 = BitFieldUExtract %op_b, #0, #16
129 * %7 = BitFieldUExtract %op_a', #16, #16
130 * %8 = IMul32 %6, %7
131 * %10 = BitFieldUExtract %op_a', #0, #16
132 * %11 = BitFieldInsert %8, %10, #16, #16
133 * %15 = BitFieldUExtract %op_b, #0, #16
134 * %16 = BitFieldUExtract %op_a, #0, #16
135 * %17 = IMul32 %15, %16
136 * %18 = IAdd32 %17, %op_c
137 * %22 = BitFieldUExtract %op_b, #16, #16
138 * %23 = BitFieldUExtract %11, #16, #16
139 * %24 = IMul32 %22, %23
140 * %25 = ShiftLeftLogical32 %24, #16
141 * %26 = ShiftLeftLogical32 %11, #16
142 * %27 = IAdd32 %26, %18
143 * %result = IAdd32 %25, %27
144 *
145 * And replace it with:
146 * %temp = IMul32 %op_a, %op_b
147 * %result = IAdd32 %temp, %op_c
148 *
149 * This optimization has been proven safe by Nvidia's compiler logic being reversed.
150 * (If Nvidia generates this code from 'fma(a, b, c)', we can do the same in the reverse order.)
151 */
152 const IR::Value zero{0u};
153 const IR::Value sixteen{16u};
154 IR::Inst* const _25{inst.Arg(0).TryInstRecursive()};
155 IR::Inst* const _27{inst.Arg(1).TryInstRecursive()};
156 if (!_25 || !_27) {
157 return false;
158 }
159 if (_27->GetOpcode() != IR::Opcode::IAdd32) {
160 return false;
161 }
162 if (_25->GetOpcode() != IR::Opcode::ShiftLeftLogical32 || _25->Arg(1) != sixteen) {
163 return false;
164 }
165 IR::Inst* const _24{_25->Arg(0).TryInstRecursive()};
166 if (!_24 || _24->GetOpcode() != IR::Opcode::IMul32) {
167 return false;
168 }
169 IR::Inst* const _22{_24->Arg(0).TryInstRecursive()};
170 IR::Inst* const _23{_24->Arg(1).TryInstRecursive()};
171 if (!_22 || !_23) {
172 return false;
173 }
174 if (_22->GetOpcode() != IR::Opcode::BitFieldUExtract) {
175 return false;
176 }
177 if (_23->GetOpcode() != IR::Opcode::BitFieldUExtract) {
178 return false;
179 }
180 if (_22->Arg(1) != sixteen || _22->Arg(2) != sixteen) {
181 return false;
182 }
183 if (_23->Arg(1) != sixteen || _23->Arg(2) != sixteen) {
184 return false;
185 }
186 IR::Inst* const _11{_23->Arg(0).TryInstRecursive()};
187 if (!_11 || _11->GetOpcode() != IR::Opcode::BitFieldInsert) {
188 return false;
189 }
190 if (_11->Arg(2) != sixteen || _11->Arg(3) != sixteen) {
191 return false;
192 }
193 IR::Inst* const _8{_11->Arg(0).TryInstRecursive()};
194 IR::Inst* const _10{_11->Arg(1).TryInstRecursive()};
195 if (!_8 || !_10) {
196 return false;
197 }
198 if (_8->GetOpcode() != IR::Opcode::IMul32) {
199 return false;
200 }
201 if (_10->GetOpcode() != IR::Opcode::BitFieldUExtract) {
202 return false;
203 }
204 IR::Inst* const _6{_8->Arg(0).TryInstRecursive()};
205 IR::Inst* const _7{_8->Arg(1).TryInstRecursive()};
206 if (!_6 || !_7) {
207 return false;
208 }
209 if (_6->GetOpcode() != IR::Opcode::BitFieldUExtract) {
210 return false;
211 }
212 if (_7->GetOpcode() != IR::Opcode::BitFieldUExtract) {
213 return false;
214 }
215 if (_6->Arg(1) != zero || _6->Arg(2) != sixteen) {
216 return false;
217 }
218 if (_7->Arg(1) != sixteen || _7->Arg(2) != sixteen) {
219 return false;
220 }
221 IR::Inst* const _26{_27->Arg(0).TryInstRecursive()};
222 IR::Inst* const _18{_27->Arg(1).TryInstRecursive()};
223 if (!_26 || !_18) {
224 return false;
225 }
226 if (_26->GetOpcode() != IR::Opcode::ShiftLeftLogical32 || _26->Arg(1) != sixteen) {
227 return false;
228 }
229 if (_26->Arg(0).InstRecursive() != _11) {
230 return false;
231 }
232 if (_18->GetOpcode() != IR::Opcode::IAdd32) {
233 return false;
234 }
235 IR::Inst* const _17{_18->Arg(0).TryInstRecursive()};
236 if (!_17 || _17->GetOpcode() != IR::Opcode::IMul32) {
237 return false;
238 }
239 IR::Inst* const _15{_17->Arg(0).TryInstRecursive()};
240 IR::Inst* const _16{_17->Arg(1).TryInstRecursive()};
241 if (!_15 || !_16) {
242 return false;
243 }
244 if (_15->GetOpcode() != IR::Opcode::BitFieldUExtract) {
245 return false;
246 }
247 if (_16->GetOpcode() != IR::Opcode::BitFieldUExtract) {
248 return false;
249 }
250 if (_15->Arg(1) != zero || _16->Arg(1) != zero || _10->Arg(1) != zero) {
251 return false;
252 }
253 if (_15->Arg(2) != sixteen || _16->Arg(2) != sixteen || _10->Arg(2) != sixteen) {
254 return false;
255 }
256 const std::array<IR::Value, 3> op_as{
257 _7->Arg(0).Resolve(),
258 _16->Arg(0).Resolve(),
259 _10->Arg(0).Resolve(),
260 };
261 const std::array<IR::Value, 3> op_bs{
262 _22->Arg(0).Resolve(),
263 _6->Arg(0).Resolve(),
264 _15->Arg(0).Resolve(),
265 };
266 const IR::U32 op_c{_18->Arg(1)};
267 if (!AreEqual(op_as) || !AreEqual(op_bs)) {
268 return false;
269 }
270 IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
271 inst.ReplaceUsesWith(ir.IAdd(ir.IMul(IR::U32{op_as[0]}, IR::U32{op_bs[1]}), op_c));
272 return true;
273}
274
103/// Replaces the pattern generated by two XMAD multiplications 275/// Replaces the pattern generated by two XMAD multiplications
104bool FoldXmadMultiply(IR::Block& block, IR::Inst& inst) { 276bool FoldXmadMultiply(IR::Block& block, IR::Inst& inst) {
105 /* 277 /*
@@ -116,33 +288,31 @@ bool FoldXmadMultiply(IR::Block& block, IR::Inst& inst) {
116 * 288 *
117 * This optimization has been proven safe by LLVM and MSVC. 289 * This optimization has been proven safe by LLVM and MSVC.
118 */ 290 */
119 const IR::Value lhs_arg{inst.Arg(0)}; 291 IR::Inst* const lhs_shl{inst.Arg(0).TryInstRecursive()};
120 const IR::Value rhs_arg{inst.Arg(1)}; 292 IR::Inst* const rhs_mul{inst.Arg(1).TryInstRecursive()};
121 if (lhs_arg.IsImmediate() || rhs_arg.IsImmediate()) { 293 if (!lhs_shl || !rhs_mul) {
122 return false; 294 return false;
123 } 295 }
124 IR::Inst* const lhs_shl{lhs_arg.InstRecursive()};
125 if (lhs_shl->GetOpcode() != IR::Opcode::ShiftLeftLogical32 || 296 if (lhs_shl->GetOpcode() != IR::Opcode::ShiftLeftLogical32 ||
126 lhs_shl->Arg(1) != IR::Value{16U}) { 297 lhs_shl->Arg(1) != IR::Value{16U}) {
127 return false; 298 return false;
128 } 299 }
129 if (lhs_shl->Arg(0).IsImmediate()) { 300 IR::Inst* const lhs_mul{lhs_shl->Arg(0).TryInstRecursive()};
301 if (!lhs_mul) {
130 return false; 302 return false;
131 } 303 }
132 IR::Inst* const lhs_mul{lhs_shl->Arg(0).InstRecursive()};
133 IR::Inst* const rhs_mul{rhs_arg.InstRecursive()};
134 if (lhs_mul->GetOpcode() != IR::Opcode::IMul32 || rhs_mul->GetOpcode() != IR::Opcode::IMul32) { 304 if (lhs_mul->GetOpcode() != IR::Opcode::IMul32 || rhs_mul->GetOpcode() != IR::Opcode::IMul32) {
135 return false; 305 return false;
136 } 306 }
137 if (lhs_mul->Arg(1).Resolve() != rhs_mul->Arg(1).Resolve()) { 307 const IR::U32 factor_b{lhs_mul->Arg(1)};
308 if (factor_b.Resolve() != rhs_mul->Arg(1).Resolve()) {
138 return false; 309 return false;
139 } 310 }
140 const IR::U32 factor_b{lhs_mul->Arg(1)}; 311 IR::Inst* const lhs_bfe{lhs_mul->Arg(0).TryInstRecursive()};
141 if (lhs_mul->Arg(0).IsImmediate() || rhs_mul->Arg(0).IsImmediate()) { 312 IR::Inst* const rhs_bfe{rhs_mul->Arg(0).TryInstRecursive()};
313 if (!lhs_bfe || !rhs_bfe) {
142 return false; 314 return false;
143 } 315 }
144 IR::Inst* const lhs_bfe{lhs_mul->Arg(0).InstRecursive()};
145 IR::Inst* const rhs_bfe{rhs_mul->Arg(0).InstRecursive()};
146 if (lhs_bfe->GetOpcode() != IR::Opcode::BitFieldUExtract) { 316 if (lhs_bfe->GetOpcode() != IR::Opcode::BitFieldUExtract) {
147 return false; 317 return false;
148 } 318 }
@@ -155,10 +325,10 @@ bool FoldXmadMultiply(IR::Block& block, IR::Inst& inst) {
155 if (rhs_bfe->Arg(1) != IR::Value{0U} || rhs_bfe->Arg(2) != IR::Value{16U}) { 325 if (rhs_bfe->Arg(1) != IR::Value{0U} || rhs_bfe->Arg(2) != IR::Value{16U}) {
156 return false; 326 return false;
157 } 327 }
158 if (lhs_bfe->Arg(0).Resolve() != rhs_bfe->Arg(0).Resolve()) { 328 const IR::U32 factor_a{lhs_bfe->Arg(0)};
329 if (factor_a.Resolve() != rhs_bfe->Arg(0).Resolve()) {
159 return false; 330 return false;
160 } 331 }
161 const IR::U32 factor_a{lhs_bfe->Arg(0)};
162 IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; 332 IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
163 inst.ReplaceUsesWith(ir.IMul(factor_a, factor_b)); 333 inst.ReplaceUsesWith(ir.IMul(factor_a, factor_b));
164 return true; 334 return true;
@@ -181,6 +351,9 @@ void FoldAdd(IR::Block& block, IR::Inst& inst) {
181 if (FoldXmadMultiply(block, inst)) { 351 if (FoldXmadMultiply(block, inst)) {
182 return; 352 return;
183 } 353 }
354 if (FoldXmadMultiplyAdd(block, inst)) {
355 return;
356 }
184 } 357 }
185} 358}
186 359
@@ -476,6 +649,10 @@ void ConstantPropagation(IR::Block& block, IR::Inst& inst) {
476 return FoldInverseFunc(inst, IR::Opcode::UnpackHalf2x16); 649 return FoldInverseFunc(inst, IR::Opcode::UnpackHalf2x16);
477 case IR::Opcode::UnpackHalf2x16: 650 case IR::Opcode::UnpackHalf2x16:
478 return FoldInverseFunc(inst, IR::Opcode::PackHalf2x16); 651 return FoldInverseFunc(inst, IR::Opcode::PackHalf2x16);
652 case IR::Opcode::PackFloat2x16:
653 return FoldInverseFunc(inst, IR::Opcode::UnpackFloat2x16);
654 case IR::Opcode::UnpackFloat2x16:
655 return FoldInverseFunc(inst, IR::Opcode::PackFloat2x16);
479 case IR::Opcode::SelectU1: 656 case IR::Opcode::SelectU1:
480 case IR::Opcode::SelectU8: 657 case IR::Opcode::SelectU8:
481 case IR::Opcode::SelectU16: 658 case IR::Opcode::SelectU16:
diff --git a/src/tests/common/param_package.cpp b/src/tests/common/param_package.cpp
index 4c0f9654f..e31ca3544 100644
--- a/src/tests/common/param_package.cpp
+++ b/src/tests/common/param_package.cpp
@@ -4,11 +4,13 @@
4 4
5#include <catch2/catch.hpp> 5#include <catch2/catch.hpp>
6#include <math.h> 6#include <math.h>
7#include "common/logging/backend.h"
7#include "common/param_package.h" 8#include "common/param_package.h"
8 9
9namespace Common { 10namespace Common {
10 11
11TEST_CASE("ParamPackage", "[common]") { 12TEST_CASE("ParamPackage", "[common]") {
13 Common::Log::DisableLoggingInTests();
12 ParamPackage original{ 14 ParamPackage original{
13 {"abc", "xyz"}, 15 {"abc", "xyz"},
14 {"def", "42"}, 16 {"def", "42"},
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..0ee07f398 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,37 +79,37 @@ 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) {
99 converted_frame_buffer = AVMallocPtr{static_cast<u8*>(av_malloc(linear_size)), av_free}; 97 converted_frame_buffer = AVMallocPtr{static_cast<u8*>(av_malloc(linear_size)), av_free};
100 } 98 }
101 99 const std::array<int, 4> converted_stride{frame->width * 4, frame->height * 4, 0, 0};
102 const int converted_stride{frame->width * 4};
103 u8* const converted_frame_buf_addr{converted_frame_buffer.get()}; 100 u8* const converted_frame_buf_addr{converted_frame_buffer.get()};
104 101
105 sws_scale(scaler_ctx, frame->data, frame->linesize, 0, frame->height, 102 sws_scale(scaler_ctx, frame->data, frame->linesize, 0, frame->height,
106 &converted_frame_buf_addr, &converted_stride); 103 &converted_frame_buf_addr, converted_stride.data());
107 104
108 const u32 blk_kind = static_cast<u32>(config.block_linear_kind); 105 const u32 blk_kind = static_cast<u32>(config.block_linear_kind);
109 if (blk_kind != 0) { 106 if (blk_kind != 0) {
110 // swizzle pitch linear to block linear 107 // swizzle pitch linear to block linear
111 const u32 block_height = static_cast<u32>(config.block_linear_height_log2); 108 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, 109 const auto size =
113 block_height, 0); 110 Tegra::Texture::CalculateSize(true, 4, width, height, 1, block_height, 0);
114 luma_buffer.resize(size); 111 luma_buffer.resize(size);
115 Tegra::Texture::SwizzleSubrect(frame->width, frame->height, frame->width * 4, 112 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); 113 converted_frame_buffer.get(), block_height, 0, 0);
118 114
119 gpu.MemoryManager().WriteBlock(output_surface_luma_address, luma_buffer.data(), size); 115 gpu.MemoryManager().WriteBlock(output_surface_luma_address, luma_buffer.data(), size);
@@ -131,41 +127,65 @@ void Vic::Execute() {
131 const std::size_t surface_height = config.surface_height_minus1 + 1; 127 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)); 128 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)); 129 const auto frame_height = std::min(surface_height, static_cast<size_t>(frame->height));
134 const std::size_t half_width = frame_width / 2; 130 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 131
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]); 132 const auto stride = static_cast<size_t>(frame->linesize[0]);
142 const auto half_stride = static_cast<size_t>(frame->linesize[1]);
143 133
144 luma_buffer.resize(aligned_width * surface_height); 134 luma_buffer.resize(aligned_width * surface_height);
145 chroma_buffer.resize(aligned_width * surface_height / 2); 135 chroma_buffer.resize(aligned_width * surface_height / 2);
146 136
147 // Populate luma buffer 137 // Populate luma buffer
138 const u8* luma_src = frame->data[0];
148 for (std::size_t y = 0; y < frame_height; ++y) { 139 for (std::size_t y = 0; y < frame_height; ++y) {
149 const std::size_t src = y * stride; 140 const std::size_t src = y * stride;
150 const std::size_t dst = y * aligned_width; 141 const std::size_t dst = y * aligned_width;
151 for (std::size_t x = 0; x < frame_width; ++x) { 142 for (std::size_t x = 0; x < frame_width; ++x) {
152 luma_buffer[dst + x] = luma_ptr[src + x]; 143 luma_buffer[dst + x] = luma_src[src + x];
153 } 144 }
154 } 145 }
155 gpu.MemoryManager().WriteBlock(output_surface_luma_address, luma_buffer.data(), 146 gpu.MemoryManager().WriteBlock(output_surface_luma_address, luma_buffer.data(),
156 luma_buffer.size()); 147 luma_buffer.size());
157 148
158 // Populate chroma buffer from both channels with interleaving. 149 // Chroma
159 for (std::size_t y = 0; y < half_height; ++y) { 150 const std::size_t half_height = frame_height / 2;
160 const std::size_t src = y * half_stride; 151 const auto half_stride = static_cast<size_t>(frame->linesize[1]);
161 const std::size_t dst = y * aligned_width;
162 152
163 for (std::size_t x = 0; x < half_width; ++x) { 153 switch (frame->format) {
164 chroma_buffer[dst + x * 2] = chroma_b_ptr[src + x]; 154 case AV_PIX_FMT_YUV420P: {
165 chroma_buffer[dst + x * 2 + 1] = chroma_r_ptr[src + x]; 155 // Frame from FFmpeg software
156 // Populate chroma buffer from both channels with interleaving.
157 const std::size_t half_width = frame_width / 2;
158 const u8* chroma_b_src = frame->data[1];
159 const u8* chroma_r_src = frame->data[2];
160 for (std::size_t y = 0; y < half_height; ++y) {
161 const std::size_t src = y * half_stride;
162 const std::size_t dst = y * aligned_width;
163
164 for (std::size_t x = 0; x < half_width; ++x) {
165 chroma_buffer[dst + x * 2] = chroma_b_src[src + x];
166 chroma_buffer[dst + x * 2 + 1] = chroma_r_src[src + x];
167 }
166 } 168 }
169 break;
170 }
171 case AV_PIX_FMT_NV12: {
172 // Frame from VA-API hardware
173 // This is already interleaved so just copy
174 const u8* chroma_src = frame->data[1];
175 for (std::size_t y = 0; y < half_height; ++y) {
176 const std::size_t src = y * stride;
177 const std::size_t dst = y * aligned_width;
178 for (std::size_t x = 0; x < frame_width; ++x) {
179 chroma_buffer[dst + x] = chroma_src[src + x];
180 }
181 }
182 break;
183 }
184 default:
185 UNREACHABLE();
186 break;
167 } 187 }
168 gpu.MemoryManager().WriteBlock(output_surface_chroma_u_address, chroma_buffer.data(), 188 gpu.MemoryManager().WriteBlock(output_surface_chroma_address, chroma_buffer.data(),
169 chroma_buffer.size()); 189 chroma_buffer.size());
170 break; 190 break;
171 } 191 }
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/macro/macro_jit_x64.h b/src/video_core/macro/macro_jit_x64.h
index 7f50ac2f8..d03d480b4 100644
--- a/src/video_core/macro/macro_jit_x64.h
+++ b/src/video_core/macro/macro_jit_x64.h
@@ -6,7 +6,7 @@
6 6
7#include <array> 7#include <array>
8#include <bitset> 8#include <bitset>
9#include <xbyak.h> 9#include <xbyak/xbyak.h>
10#include "common/bit_field.h" 10#include "common/bit_field.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/x64/xbyak_abi.h" 12#include "common/x64/xbyak_abi.h"
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/render_targets.h b/src/video_core/texture_cache/render_targets.h
index 9b9544b07..0cb227d69 100644
--- a/src/video_core/texture_cache/render_targets.h
+++ b/src/video_core/texture_cache/render_targets.h
@@ -24,10 +24,10 @@ struct RenderTargets {
24 return std::ranges::any_of(color_buffer_ids, contains) || contains(depth_buffer_id); 24 return std::ranges::any_of(color_buffer_ids, contains) || contains(depth_buffer_id);
25 } 25 }
26 26
27 std::array<ImageViewId, NUM_RT> color_buffer_ids; 27 std::array<ImageViewId, NUM_RT> color_buffer_ids{};
28 ImageViewId depth_buffer_id; 28 ImageViewId depth_buffer_id{};
29 std::array<u8, NUM_RT> draw_buffers{}; 29 std::array<u8, NUM_RT> draw_buffers{};
30 Extent2D size; 30 Extent2D size{};
31}; 31};
32 32
33} // namespace VideoCommon 33} // namespace VideoCommon
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..c010b9353 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,33 +73,42 @@ 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}
81} // Anonymous namespace
82 82
83void UnswizzleTexture(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, 83template <bool TO_LINEAR>
84 u32 width, u32 height, u32 depth, u32 block_height, u32 block_depth, 84void Swizzle(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, u32 width,
85 u32 stride_alignment) { 85 u32 height, u32 depth, u32 block_height, u32 block_depth, u32 stride_alignment) {
86 Swizzle<false>(output, input, bytes_per_pixel, width, height, depth, block_height, block_depth, 86 switch (bytes_per_pixel) {
87 stride_alignment); 87#define BPP_CASE(x) \
88} 88 case x: \
89 89 return SwizzleImpl<TO_LINEAR, x>(output, input, width, height, depth, block_height, \
90void SwizzleTexture(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, u32 width, 90 block_depth, stride_alignment);
91 u32 height, u32 depth, u32 block_height, u32 block_depth, 91 BPP_CASE(1)
92 u32 stride_alignment) { 92 BPP_CASE(2)
93 Swizzle<true>(output, input, bytes_per_pixel, width, height, depth, block_height, block_depth, 93 BPP_CASE(3)
94 stride_alignment); 94 BPP_CASE(4)
95 BPP_CASE(6)
96 BPP_CASE(8)
97 BPP_CASE(12)
98 BPP_CASE(16)
99#undef BPP_CASE
100 default:
101 UNREACHABLE_MSG("Invalid bytes_per_pixel={}", bytes_per_pixel);
102 }
95} 103}
96 104
105template <u32 BYTES_PER_PIXEL>
97void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width, 106void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width,
98 u32 bytes_per_pixel, u8* swizzled_data, const u8* unswizzled_data, 107 u8* swizzled_data, const u8* unswizzled_data, u32 block_height_bit,
99 u32 block_height_bit, u32 offset_x, u32 offset_y) { 108 u32 offset_x, u32 offset_y) {
100 const u32 block_height = 1U << block_height_bit; 109 const u32 block_height = 1U << block_height_bit;
101 const u32 image_width_in_gobs = 110 const u32 image_width_in_gobs =
102 (swizzled_width * bytes_per_pixel + (GOB_SIZE_X - 1)) / GOB_SIZE_X; 111 (swizzled_width * BYTES_PER_PIXEL + (GOB_SIZE_X - 1)) / GOB_SIZE_X;
103 for (u32 line = 0; line < subrect_height; ++line) { 112 for (u32 line = 0; line < subrect_height; ++line) {
104 const u32 dst_y = line + offset_y; 113 const u32 dst_y = line + offset_y;
105 const u32 gob_address_y = 114 const u32 gob_address_y =
@@ -109,20 +118,21 @@ void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32
109 for (u32 x = 0; x < subrect_width; ++x) { 118 for (u32 x = 0; x < subrect_width; ++x) {
110 const u32 dst_x = x + offset_x; 119 const u32 dst_x = x + offset_x;
111 const u32 gob_address = 120 const u32 gob_address =
112 gob_address_y + (dst_x * bytes_per_pixel / GOB_SIZE_X) * GOB_SIZE * block_height; 121 gob_address_y + (dst_x * BYTES_PER_PIXEL / GOB_SIZE_X) * GOB_SIZE * block_height;
113 const u32 swizzled_offset = gob_address + table[(dst_x * bytes_per_pixel) % GOB_SIZE_X]; 122 const u32 swizzled_offset = gob_address + table[(dst_x * BYTES_PER_PIXEL) % GOB_SIZE_X];
114 const u32 unswizzled_offset = line * source_pitch + x * bytes_per_pixel; 123 const u32 unswizzled_offset = line * source_pitch + x * BYTES_PER_PIXEL;
115 124
116 const u8* const source_line = unswizzled_data + unswizzled_offset; 125 const u8* const source_line = unswizzled_data + unswizzled_offset;
117 u8* const dest_addr = swizzled_data + swizzled_offset; 126 u8* const dest_addr = swizzled_data + swizzled_offset;
118 std::memcpy(dest_addr, source_line, bytes_per_pixel); 127 std::memcpy(dest_addr, source_line, BYTES_PER_PIXEL);
119 } 128 }
120 } 129 }
121} 130}
122 131
123void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 bytes_per_pixel, 132template <u32 BYTES_PER_PIXEL>
124 u32 block_height, u32 origin_x, u32 origin_y, u8* output, const u8* input) { 133void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 block_height,
125 const u32 stride = width * bytes_per_pixel; 134 u32 origin_x, u32 origin_y, u8* output, const u8* input) {
135 const u32 stride = width * BYTES_PER_PIXEL;
126 const u32 gobs_in_x = (stride + GOB_SIZE_X - 1) / GOB_SIZE_X; 136 const u32 gobs_in_x = (stride + GOB_SIZE_X - 1) / GOB_SIZE_X;
127 const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height); 137 const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height);
128 138
@@ -137,24 +147,25 @@ void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width,
137 const u32 src_offset_y = (block_y >> block_height) * block_size + 147 const u32 src_offset_y = (block_y >> block_height) * block_size +
138 ((block_y & block_height_mask) << GOB_SIZE_SHIFT); 148 ((block_y & block_height_mask) << GOB_SIZE_SHIFT);
139 for (u32 column = 0; column < line_length_in; ++column) { 149 for (u32 column = 0; column < line_length_in; ++column) {
140 const u32 src_x = (column + origin_x) * bytes_per_pixel; 150 const u32 src_x = (column + origin_x) * BYTES_PER_PIXEL;
141 const u32 src_offset_x = (src_x >> GOB_SIZE_X_SHIFT) << x_shift; 151 const u32 src_offset_x = (src_x >> GOB_SIZE_X_SHIFT) << x_shift;
142 152
143 const u32 swizzled_offset = src_offset_y + src_offset_x + table[src_x % GOB_SIZE_X]; 153 const u32 swizzled_offset = src_offset_y + src_offset_x + table[src_x % GOB_SIZE_X];
144 const u32 unswizzled_offset = line * pitch + column * bytes_per_pixel; 154 const u32 unswizzled_offset = line * pitch + column * BYTES_PER_PIXEL;
145 155
146 std::memcpy(output + unswizzled_offset, input + swizzled_offset, bytes_per_pixel); 156 std::memcpy(output + unswizzled_offset, input + swizzled_offset, BYTES_PER_PIXEL);
147 } 157 }
148 } 158 }
149} 159}
150 160
161template <u32 BYTES_PER_PIXEL>
151void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 height, 162void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 height,
152 u32 bytes_per_pixel, u32 block_height, u32 block_depth, u32 origin_x, 163 u32 block_height, u32 block_depth, u32 origin_x, u32 origin_y, u8* output,
153 u32 origin_y, u8* output, const u8* input) { 164 const u8* input) {
154 UNIMPLEMENTED_IF(origin_x > 0); 165 UNIMPLEMENTED_IF(origin_x > 0);
155 UNIMPLEMENTED_IF(origin_y > 0); 166 UNIMPLEMENTED_IF(origin_y > 0);
156 167
157 const u32 stride = width * bytes_per_pixel; 168 const u32 stride = width * BYTES_PER_PIXEL;
158 const u32 gobs_in_x = (stride + GOB_SIZE_X - 1) / GOB_SIZE_X; 169 const u32 gobs_in_x = (stride + GOB_SIZE_X - 1) / GOB_SIZE_X;
159 const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth); 170 const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth);
160 171
@@ -169,11 +180,93 @@ void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 widt
169 for (u32 x = 0; x < line_length_in; ++x) { 180 for (u32 x = 0; x < line_length_in; ++x) {
170 const u32 dst_offset = 181 const u32 dst_offset =
171 ((x / GOB_SIZE_X) << x_shift) + dst_offset_y + table[x % GOB_SIZE_X]; 182 ((x / GOB_SIZE_X) << x_shift) + dst_offset_y + table[x % GOB_SIZE_X];
172 const u32 src_offset = x * bytes_per_pixel + line * pitch; 183 const u32 src_offset = x * BYTES_PER_PIXEL + line * pitch;
173 std::memcpy(output + dst_offset, input + src_offset, bytes_per_pixel); 184 std::memcpy(output + dst_offset, input + src_offset, BYTES_PER_PIXEL);
174 } 185 }
175 } 186 }
176} 187}
188} // Anonymous namespace
189
190void UnswizzleTexture(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel,
191 u32 width, u32 height, u32 depth, u32 block_height, u32 block_depth,
192 u32 stride_alignment) {
193 Swizzle<false>(output, input, bytes_per_pixel, width, height, depth, block_height, block_depth,
194 stride_alignment);
195}
196
197void SwizzleTexture(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, u32 width,
198 u32 height, u32 depth, u32 block_height, u32 block_depth,
199 u32 stride_alignment) {
200 Swizzle<true>(output, input, bytes_per_pixel, width, height, depth, block_height, block_depth,
201 stride_alignment);
202}
203
204void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width,
205 u32 bytes_per_pixel, u8* swizzled_data, const u8* unswizzled_data,
206 u32 block_height_bit, u32 offset_x, u32 offset_y) {
207 switch (bytes_per_pixel) {
208#define BPP_CASE(x) \
209 case x: \
210 return SwizzleSubrect<x>(subrect_width, subrect_height, source_pitch, swizzled_width, \
211 swizzled_data, unswizzled_data, block_height_bit, offset_x, \
212 offset_y);
213 BPP_CASE(1)
214 BPP_CASE(2)
215 BPP_CASE(3)
216 BPP_CASE(4)
217 BPP_CASE(6)
218 BPP_CASE(8)
219 BPP_CASE(12)
220 BPP_CASE(16)
221#undef BPP_CASE
222 default:
223 UNREACHABLE_MSG("Invalid bytes_per_pixel={}", bytes_per_pixel);
224 }
225}
226
227void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 bytes_per_pixel,
228 u32 block_height, u32 origin_x, u32 origin_y, u8* output, const u8* input) {
229 switch (bytes_per_pixel) {
230#define BPP_CASE(x) \
231 case x: \
232 return UnswizzleSubrect<x>(line_length_in, line_count, pitch, width, block_height, \
233 origin_x, origin_y, output, input);
234 BPP_CASE(1)
235 BPP_CASE(2)
236 BPP_CASE(3)
237 BPP_CASE(4)
238 BPP_CASE(6)
239 BPP_CASE(8)
240 BPP_CASE(12)
241 BPP_CASE(16)
242#undef BPP_CASE
243 default:
244 UNREACHABLE_MSG("Invalid bytes_per_pixel={}", bytes_per_pixel);
245 }
246}
247
248void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 height,
249 u32 bytes_per_pixel, u32 block_height, u32 block_depth, u32 origin_x,
250 u32 origin_y, u8* output, const u8* input) {
251 switch (bytes_per_pixel) {
252#define BPP_CASE(x) \
253 case x: \
254 return SwizzleSliceToVoxel<x>(line_length_in, line_count, pitch, width, height, \
255 block_height, block_depth, origin_x, origin_y, output, \
256 input);
257 BPP_CASE(1)
258 BPP_CASE(2)
259 BPP_CASE(3)
260 BPP_CASE(4)
261 BPP_CASE(6)
262 BPP_CASE(8)
263 BPP_CASE(12)
264 BPP_CASE(16)
265#undef BPP_CASE
266 default:
267 UNREACHABLE_MSG("Invalid bytes_per_pixel={}", bytes_per_pixel);
268 }
269}
177 270
178void SwizzleKepler(const u32 width, const u32 height, const u32 dst_x, const u32 dst_y, 271void SwizzleKepler(const u32 width, const u32 height, const u32 dst_x, const u32 dst_y,
179 const u32 block_height_bit, const std::size_t copy_size, const u8* source_data, 272 const u32 block_height_bit, const std::size_t copy_size, const u8* source_data,
@@ -194,7 +287,7 @@ void SwizzleKepler(const u32 width, const u32 height, const u32 dst_x, const u32
194 u8* dest_addr = swizzle_data + swizzled_offset; 287 u8* dest_addr = swizzle_data + swizzled_offset;
195 count++; 288 count++;
196 289
197 std::memcpy(dest_addr, source_line, 1); 290 *dest_addr = *source_line;
198 } 291 }
199 } 292 }
200} 293}
diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h
index 1a9399455..7994cb859 100644
--- a/src/video_core/textures/texture.h
+++ b/src/video_core/textures/texture.h
@@ -159,7 +159,7 @@ static_assert(sizeof(TextureHandle) == 4, "TextureHandle has wrong size");
159 return {raw, raw}; 159 return {raw, raw};
160 } else { 160 } else {
161 const Tegra::Texture::TextureHandle handle{raw}; 161 const Tegra::Texture::TextureHandle handle{raw};
162 return {handle.tic_id, via_header_index ? handle.tic_id : handle.tsc_id}; 162 return {handle.tic_id, handle.tsc_id};
163 } 163 }
164} 164}
165 165
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/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 7a5fcff20..19ba0dbba 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -102,9 +102,9 @@ add_executable(yuzu
102 configuration/configure_profile_manager.cpp 102 configuration/configure_profile_manager.cpp
103 configuration/configure_profile_manager.h 103 configuration/configure_profile_manager.h
104 configuration/configure_profile_manager.ui 104 configuration/configure_profile_manager.ui
105 configuration/configure_service.cpp 105 configuration/configure_network.cpp
106 configuration/configure_service.h 106 configuration/configure_network.h
107 configuration/configure_service.ui 107 configuration/configure_network.ui
108 configuration/configure_system.cpp 108 configuration/configure_system.cpp
109 configuration/configure_system.h 109 configuration/configure_system.h
110 configuration/configure_system.ui 110 configuration/configure_system.ui
diff --git a/src/yuzu/applets/qt_software_keyboard.cpp b/src/yuzu/applets/qt_software_keyboard.cpp
index 848801cec..8fc0c5a36 100644
--- a/src/yuzu/applets/qt_software_keyboard.cpp
+++ b/src/yuzu/applets/qt_software_keyboard.cpp
@@ -438,7 +438,7 @@ void QtSoftwareKeyboardDialog::ShowInlineKeyboard(
438 initialize_parameters.key_disable_flags = appear_parameters.key_disable_flags; 438 initialize_parameters.key_disable_flags = appear_parameters.key_disable_flags;
439 initialize_parameters.enable_backspace_button = appear_parameters.enable_backspace_button; 439 initialize_parameters.enable_backspace_button = appear_parameters.enable_backspace_button;
440 initialize_parameters.enable_return_button = appear_parameters.enable_return_button; 440 initialize_parameters.enable_return_button = appear_parameters.enable_return_button;
441 initialize_parameters.disable_cancel_button = initialize_parameters.disable_cancel_button; 441 initialize_parameters.disable_cancel_button = appear_parameters.disable_cancel_button;
442 442
443 SetKeyboardType(); 443 SetKeyboardType();
444 SetControllerImage(); 444 SetControllerImage();
diff --git a/src/yuzu/applets/qt_web_browser.cpp b/src/yuzu/applets/qt_web_browser.cpp
index b112dd7b0..652d99570 100644
--- a/src/yuzu/applets/qt_web_browser.cpp
+++ b/src/yuzu/applets/qt_web_browser.cpp
@@ -107,6 +107,7 @@ void QtNXWebEngineView::LoadLocalWebPage(const std::string& main_url,
107 is_local = true; 107 is_local = true;
108 108
109 LoadExtractedFonts(); 109 LoadExtractedFonts();
110 FocusFirstLinkElement();
110 SetUserAgent(UserAgent::WebApplet); 111 SetUserAgent(UserAgent::WebApplet);
111 SetFinished(false); 112 SetFinished(false);
112 SetExitReason(Service::AM::Applets::WebExitReason::EndButtonPressed); 113 SetExitReason(Service::AM::Applets::WebExitReason::EndButtonPressed);
@@ -121,6 +122,7 @@ void QtNXWebEngineView::LoadExternalWebPage(const std::string& main_url,
121 const std::string& additional_args) { 122 const std::string& additional_args) {
122 is_local = false; 123 is_local = false;
123 124
125 FocusFirstLinkElement();
124 SetUserAgent(UserAgent::WebApplet); 126 SetUserAgent(UserAgent::WebApplet);
125 SetFinished(false); 127 SetFinished(false);
126 SetExitReason(Service::AM::Applets::WebExitReason::EndButtonPressed); 128 SetExitReason(Service::AM::Applets::WebExitReason::EndButtonPressed);
@@ -208,7 +210,7 @@ void QtNXWebEngineView::HandleWindowFooterButtonPressedOnce() {
208 if (input_interpreter->IsButtonPressedOnce(button)) { 210 if (input_interpreter->IsButtonPressedOnce(button)) {
209 page()->runJavaScript( 211 page()->runJavaScript(
210 QStringLiteral("yuzu_key_callbacks[%1] == null;").arg(static_cast<u8>(button)), 212 QStringLiteral("yuzu_key_callbacks[%1] == null;").arg(static_cast<u8>(button)),
211 [&](const QVariant& variant) { 213 [this, button](const QVariant& variant) {
212 if (variant.toBool()) { 214 if (variant.toBool()) {
213 switch (button) { 215 switch (button) {
214 case HIDButton::A: 216 case HIDButton::A:
@@ -364,6 +366,17 @@ void QtNXWebEngineView::LoadExtractedFonts() {
364 Qt::QueuedConnection); 366 Qt::QueuedConnection);
365} 367}
366 368
369void QtNXWebEngineView::FocusFirstLinkElement() {
370 QWebEngineScript focus_link_element;
371
372 focus_link_element.setName(QStringLiteral("focus_link_element.js"));
373 focus_link_element.setSourceCode(QString::fromStdString(FOCUS_LINK_ELEMENT_SCRIPT));
374 focus_link_element.setWorldId(QWebEngineScript::MainWorld);
375 focus_link_element.setInjectionPoint(QWebEngineScript::Deferred);
376 focus_link_element.setRunsOnSubFrames(true);
377 default_profile->scripts()->insert(focus_link_element);
378}
379
367#endif 380#endif
368 381
369QtWebBrowser::QtWebBrowser(GMainWindow& main_window) { 382QtWebBrowser::QtWebBrowser(GMainWindow& main_window) {
diff --git a/src/yuzu/applets/qt_web_browser.h b/src/yuzu/applets/qt_web_browser.h
index 7ad07409f..7e9f703fc 100644
--- a/src/yuzu/applets/qt_web_browser.h
+++ b/src/yuzu/applets/qt_web_browser.h
@@ -161,6 +161,9 @@ private:
161 /// Loads the extracted fonts using JavaScript. 161 /// Loads the extracted fonts using JavaScript.
162 void LoadExtractedFonts(); 162 void LoadExtractedFonts();
163 163
164 /// Brings focus to the first available link element.
165 void FocusFirstLinkElement();
166
164 InputCommon::InputSubsystem* input_subsystem; 167 InputCommon::InputSubsystem* input_subsystem;
165 168
166 std::unique_ptr<UrlRequestInterceptor> url_interceptor; 169 std::unique_ptr<UrlRequestInterceptor> url_interceptor;
diff --git a/src/yuzu/applets/qt_web_browser_scripts.h b/src/yuzu/applets/qt_web_browser_scripts.h
index 992837a85..c4ba8d40f 100644
--- a/src/yuzu/applets/qt_web_browser_scripts.h
+++ b/src/yuzu/applets/qt_web_browser_scripts.h
@@ -73,6 +73,12 @@ constexpr char LOAD_NX_FONT[] = R"(
73})(); 73})();
74)"; 74)";
75 75
76constexpr char FOCUS_LINK_ELEMENT_SCRIPT[] = R"(
77if (document.getElementsByTagName("a").length > 0) {
78 document.getElementsByTagName("a")[0].focus();
79}
80)";
81
76constexpr char GAMEPAD_SCRIPT[] = R"( 82constexpr char GAMEPAD_SCRIPT[] = R"(
77window.addEventListener("gamepadconnected", function(e) { 83window.addEventListener("gamepadconnected", function(e) {
78 console.log("Gamepad connected at index %d: %s. %d buttons, %d axes.", 84 console.log("Gamepad connected at index %d: %s. %d buttons, %d axes.",
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 72027e773..377795326 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -692,6 +692,7 @@ void Config::ReadServiceValues() {
692 qt_config->beginGroup(QStringLiteral("Services")); 692 qt_config->beginGroup(QStringLiteral("Services"));
693 ReadBasicSetting(Settings::values.bcat_backend); 693 ReadBasicSetting(Settings::values.bcat_backend);
694 ReadBasicSetting(Settings::values.bcat_boxcat_local); 694 ReadBasicSetting(Settings::values.bcat_boxcat_local);
695 ReadBasicSetting(Settings::values.network_interface);
695 qt_config->endGroup(); 696 qt_config->endGroup();
696} 697}
697 698
@@ -825,6 +826,7 @@ void Config::ReadRendererValues() {
825 if (global) { 826 if (global) {
826 ReadBasicSetting(Settings::values.fps_cap); 827 ReadBasicSetting(Settings::values.fps_cap);
827 ReadBasicSetting(Settings::values.renderer_debug); 828 ReadBasicSetting(Settings::values.renderer_debug);
829 ReadBasicSetting(Settings::values.renderer_shader_feedback);
828 ReadBasicSetting(Settings::values.enable_nsight_aftermath); 830 ReadBasicSetting(Settings::values.enable_nsight_aftermath);
829 ReadBasicSetting(Settings::values.disable_shader_loop_safety_checks); 831 ReadBasicSetting(Settings::values.disable_shader_loop_safety_checks);
830 } 832 }
@@ -872,10 +874,6 @@ void Config::ReadShortcutValues() {
872void Config::ReadSystemValues() { 874void Config::ReadSystemValues() {
873 qt_config->beginGroup(QStringLiteral("System")); 875 qt_config->beginGroup(QStringLiteral("System"));
874 876
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); 877 ReadGlobalSetting(Settings::values.language_index);
880 878
881 ReadGlobalSetting(Settings::values.region_index); 879 ReadGlobalSetting(Settings::values.region_index);
@@ -896,6 +894,10 @@ void Config::ReadSystemValues() {
896 } 894 }
897 895
898 if (global) { 896 if (global) {
897 ReadBasicSetting(Settings::values.current_user);
898 Settings::values.current_user = std::clamp<int>(Settings::values.current_user.GetValue(), 0,
899 Service::Account::MAX_USERS - 1);
900
899 const auto custom_rtc_enabled = 901 const auto custom_rtc_enabled =
900 ReadSetting(QStringLiteral("custom_rtc_enabled"), false).toBool(); 902 ReadSetting(QStringLiteral("custom_rtc_enabled"), false).toBool();
901 if (custom_rtc_enabled) { 903 if (custom_rtc_enabled) {
@@ -945,7 +947,8 @@ void Config::ReadUIGamelistValues() {
945 qt_config->beginGroup(QStringLiteral("UIGameList")); 947 qt_config->beginGroup(QStringLiteral("UIGameList"));
946 948
947 ReadBasicSetting(UISettings::values.show_add_ons); 949 ReadBasicSetting(UISettings::values.show_add_ons);
948 ReadBasicSetting(UISettings::values.icon_size); 950 ReadBasicSetting(UISettings::values.game_icon_size);
951 ReadBasicSetting(UISettings::values.folder_icon_size);
949 ReadBasicSetting(UISettings::values.row_1_text_id); 952 ReadBasicSetting(UISettings::values.row_1_text_id);
950 ReadBasicSetting(UISettings::values.row_2_text_id); 953 ReadBasicSetting(UISettings::values.row_2_text_id);
951 ReadBasicSetting(UISettings::values.cache_game_list); 954 ReadBasicSetting(UISettings::values.cache_game_list);
@@ -1142,7 +1145,7 @@ void Config::SaveValues() {
1142 SaveDataStorageValues(); 1145 SaveDataStorageValues();
1143 SaveDebuggingValues(); 1146 SaveDebuggingValues();
1144 SaveDisabledAddOnValues(); 1147 SaveDisabledAddOnValues();
1145 SaveServiceValues(); 1148 SaveNetworkValues();
1146 SaveUIValues(); 1149 SaveUIValues();
1147 SaveWebServiceValues(); 1150 SaveWebServiceValues();
1148 SaveMiscellaneousValues(); 1151 SaveMiscellaneousValues();
@@ -1236,11 +1239,12 @@ void Config::SaveDebuggingValues() {
1236 qt_config->endGroup(); 1239 qt_config->endGroup();
1237} 1240}
1238 1241
1239void Config::SaveServiceValues() { 1242void Config::SaveNetworkValues() {
1240 qt_config->beginGroup(QStringLiteral("Services")); 1243 qt_config->beginGroup(QStringLiteral("Services"));
1241 1244
1242 WriteBasicSetting(Settings::values.bcat_backend); 1245 WriteBasicSetting(Settings::values.bcat_backend);
1243 WriteBasicSetting(Settings::values.bcat_boxcat_local); 1246 WriteBasicSetting(Settings::values.bcat_boxcat_local);
1247 WriteBasicSetting(Settings::values.network_interface);
1244 1248
1245 qt_config->endGroup(); 1249 qt_config->endGroup();
1246} 1250}
@@ -1332,7 +1336,10 @@ void Config::SaveRendererValues() {
1332 static_cast<u32>(Settings::values.renderer_backend.GetDefault()), 1336 static_cast<u32>(Settings::values.renderer_backend.GetDefault()),
1333 Settings::values.renderer_backend.UsingGlobal()); 1337 Settings::values.renderer_backend.UsingGlobal());
1334 WriteGlobalSetting(Settings::values.vulkan_device); 1338 WriteGlobalSetting(Settings::values.vulkan_device);
1335 WriteGlobalSetting(Settings::values.fullscreen_mode); 1339 WriteSetting(QString::fromStdString(Settings::values.fullscreen_mode.GetLabel()),
1340 static_cast<u32>(Settings::values.fullscreen_mode.GetValue(global)),
1341 static_cast<u32>(Settings::values.fullscreen_mode.GetDefault()),
1342 Settings::values.fullscreen_mode.UsingGlobal());
1336 WriteGlobalSetting(Settings::values.aspect_ratio); 1343 WriteGlobalSetting(Settings::values.aspect_ratio);
1337 WriteGlobalSetting(Settings::values.max_anisotropy); 1344 WriteGlobalSetting(Settings::values.max_anisotropy);
1338 WriteGlobalSetting(Settings::values.use_speed_limit); 1345 WriteGlobalSetting(Settings::values.use_speed_limit);
@@ -1360,6 +1367,7 @@ void Config::SaveRendererValues() {
1360 if (global) { 1367 if (global) {
1361 WriteBasicSetting(Settings::values.fps_cap); 1368 WriteBasicSetting(Settings::values.fps_cap);
1362 WriteBasicSetting(Settings::values.renderer_debug); 1369 WriteBasicSetting(Settings::values.renderer_debug);
1370 WriteBasicSetting(Settings::values.renderer_shader_feedback);
1363 WriteBasicSetting(Settings::values.enable_nsight_aftermath); 1371 WriteBasicSetting(Settings::values.enable_nsight_aftermath);
1364 WriteBasicSetting(Settings::values.disable_shader_loop_safety_checks); 1372 WriteBasicSetting(Settings::values.disable_shader_loop_safety_checks);
1365 } 1373 }
@@ -1400,7 +1408,6 @@ void Config::SaveShortcutValues() {
1400void Config::SaveSystemValues() { 1408void Config::SaveSystemValues() {
1401 qt_config->beginGroup(QStringLiteral("System")); 1409 qt_config->beginGroup(QStringLiteral("System"));
1402 1410
1403 WriteBasicSetting(Settings::values.current_user);
1404 WriteGlobalSetting(Settings::values.language_index); 1411 WriteGlobalSetting(Settings::values.language_index);
1405 WriteGlobalSetting(Settings::values.region_index); 1412 WriteGlobalSetting(Settings::values.region_index);
1406 WriteGlobalSetting(Settings::values.time_zone_index); 1413 WriteGlobalSetting(Settings::values.time_zone_index);
@@ -1412,6 +1419,8 @@ void Config::SaveSystemValues() {
1412 0, Settings::values.rng_seed.UsingGlobal()); 1419 0, Settings::values.rng_seed.UsingGlobal());
1413 1420
1414 if (global) { 1421 if (global) {
1422 WriteBasicSetting(Settings::values.current_user);
1423
1415 WriteSetting(QStringLiteral("custom_rtc_enabled"), Settings::values.custom_rtc.has_value(), 1424 WriteSetting(QStringLiteral("custom_rtc_enabled"), Settings::values.custom_rtc.has_value(),
1416 false); 1425 false);
1417 WriteSetting(QStringLiteral("custom_rtc"), 1426 WriteSetting(QStringLiteral("custom_rtc"),
@@ -1458,7 +1467,8 @@ void Config::SaveUIGamelistValues() {
1458 qt_config->beginGroup(QStringLiteral("UIGameList")); 1467 qt_config->beginGroup(QStringLiteral("UIGameList"));
1459 1468
1460 WriteBasicSetting(UISettings::values.show_add_ons); 1469 WriteBasicSetting(UISettings::values.show_add_ons);
1461 WriteBasicSetting(UISettings::values.icon_size); 1470 WriteBasicSetting(UISettings::values.game_icon_size);
1471 WriteBasicSetting(UISettings::values.folder_icon_size);
1462 WriteBasicSetting(UISettings::values.row_1_text_id); 1472 WriteBasicSetting(UISettings::values.row_1_text_id);
1463 WriteBasicSetting(UISettings::values.row_2_text_id); 1473 WriteBasicSetting(UISettings::values.row_2_text_id);
1464 WriteBasicSetting(UISettings::values.cache_game_list); 1474 WriteBasicSetting(UISettings::values.cache_game_list);
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index 4bbb9f1cd..9555f4498 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -88,7 +88,7 @@ private:
88 void SaveCoreValues(); 88 void SaveCoreValues();
89 void SaveDataStorageValues(); 89 void SaveDataStorageValues();
90 void SaveDebuggingValues(); 90 void SaveDebuggingValues();
91 void SaveServiceValues(); 91 void SaveNetworkValues();
92 void SaveDisabledAddOnValues(); 92 void SaveDisabledAddOnValues();
93 void SaveMiscellaneousValues(); 93 void SaveMiscellaneousValues();
94 void SavePathValues(); 94 void SavePathValues();
@@ -181,5 +181,6 @@ private:
181// These metatype declarations cannot be in common/settings.h because core is devoid of QT 181// These metatype declarations cannot be in common/settings.h because core is devoid of QT
182Q_DECLARE_METATYPE(Settings::CPUAccuracy); 182Q_DECLARE_METATYPE(Settings::CPUAccuracy);
183Q_DECLARE_METATYPE(Settings::GPUAccuracy); 183Q_DECLARE_METATYPE(Settings::GPUAccuracy);
184Q_DECLARE_METATYPE(Settings::FullscreenMode);
184Q_DECLARE_METATYPE(Settings::RendererBackend); 185Q_DECLARE_METATYPE(Settings::RendererBackend);
185Q_DECLARE_METATYPE(Settings::ShaderBackend); 186Q_DECLARE_METATYPE(Settings::ShaderBackend);
diff --git a/src/yuzu/configuration/configuration_shared.cpp b/src/yuzu/configuration/configuration_shared.cpp
index 096e42e94..251aab912 100644
--- a/src/yuzu/configuration/configuration_shared.cpp
+++ b/src/yuzu/configuration/configuration_shared.cpp
@@ -25,20 +25,6 @@ void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<bool>* setting,
25 } 25 }
26} 26}
27 27
28void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<int>* setting,
29 const QComboBox* combobox) {
30 if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) {
31 setting->SetValue(combobox->currentIndex());
32 } else if (!Settings::IsConfiguringGlobal()) {
33 if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
34 setting->SetGlobal(true);
35 } else {
36 setting->SetGlobal(false);
37 setting->SetValue(combobox->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET);
38 }
39 }
40}
41
42void ConfigurationShared::SetPerGameSetting(QCheckBox* checkbox, 28void ConfigurationShared::SetPerGameSetting(QCheckBox* checkbox,
43 const Settings::Setting<bool>* setting) { 29 const Settings::Setting<bool>* setting) {
44 if (setting->UsingGlobal()) { 30 if (setting->UsingGlobal()) {
diff --git a/src/yuzu/configuration/configuration_shared.h b/src/yuzu/configuration/configuration_shared.h
index 1e0ef01ca..5423dbc92 100644
--- a/src/yuzu/configuration/configuration_shared.h
+++ b/src/yuzu/configuration/configuration_shared.h
@@ -28,7 +28,20 @@ enum class CheckState {
28// ApplyPerGameSetting, given a Settings::Setting and a Qt UI element, properly applies a Setting 28// ApplyPerGameSetting, given a Settings::Setting and a Qt UI element, properly applies a Setting
29void ApplyPerGameSetting(Settings::Setting<bool>* setting, const QCheckBox* checkbox, 29void ApplyPerGameSetting(Settings::Setting<bool>* setting, const QCheckBox* checkbox,
30 const CheckState& tracker); 30 const CheckState& tracker);
31void ApplyPerGameSetting(Settings::Setting<int>* setting, const QComboBox* combobox); 31template <typename Type>
32void ApplyPerGameSetting(Settings::Setting<Type>* setting, const QComboBox* combobox) {
33 if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) {
34 setting->SetValue(static_cast<Type>(combobox->currentIndex()));
35 } else if (!Settings::IsConfiguringGlobal()) {
36 if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
37 setting->SetGlobal(true);
38 } else {
39 setting->SetGlobal(false);
40 setting->SetValue(static_cast<Type>(combobox->currentIndex() -
41 ConfigurationShared::USE_GLOBAL_OFFSET));
42 }
43 }
44}
32 45
33// Sets a Qt UI element given a Settings::Setting 46// Sets a Qt UI element given a Settings::Setting
34void SetPerGameSetting(QCheckBox* checkbox, const Settings::Setting<bool>* setting); 47void SetPerGameSetting(QCheckBox* checkbox, const Settings::Setting<bool>* setting);
diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui
index fca9aed5f..6258dcf20 100644
--- a/src/yuzu/configuration/configure.ui
+++ b/src/yuzu/configuration/configure.ui
@@ -147,12 +147,12 @@
147 <string>Web</string> 147 <string>Web</string>
148 </attribute> 148 </attribute>
149 </widget> 149 </widget>
150 <widget class="ConfigureService" name="serviceTab"> 150 <widget class="ConfigureNetwork" name="networkTab">
151 <property name="accessibleName"> 151 <property name="accessibleName">
152 <string>Services</string> 152 <string>Network</string>
153 </property> 153 </property>
154 <attribute name="title"> 154 <attribute name="title">
155 <string>Services</string> 155 <string>Network</string>
156 </attribute> 156 </attribute>
157 </widget> 157 </widget>
158 </widget> 158 </widget>
@@ -242,9 +242,9 @@
242 <container>1</container> 242 <container>1</container>
243 </customwidget> 243 </customwidget>
244 <customwidget> 244 <customwidget>
245 <class>ConfigureService</class> 245 <class>ConfigureNetwork</class>
246 <extends>QWidget</extends> 246 <extends>QWidget</extends>
247 <header>configuration/configure_service.h</header> 247 <header>configuration/configure_network.h</header>
248 <container>1</container> 248 <container>1</container>
249 </customwidget> 249 </customwidget>
250 <customwidget> 250 <customwidget>
diff --git a/src/yuzu/configuration/configure_cpu.cpp b/src/yuzu/configuration/configure_cpu.cpp
index 8d7171487..784b6484e 100644
--- a/src/yuzu/configuration/configure_cpu.cpp
+++ b/src/yuzu/configuration/configure_cpu.cpp
@@ -65,6 +65,7 @@ void ConfigureCpu::UpdateGroup(int index) {
65} 65}
66 66
67void ConfigureCpu::ApplyConfiguration() { 67void ConfigureCpu::ApplyConfiguration() {
68 ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpu_accuracy, ui->accuracy);
68 ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_unfuse_fma, 69 ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_unfuse_fma,
69 ui->cpuopt_unsafe_unfuse_fma, 70 ui->cpuopt_unsafe_unfuse_fma,
70 cpuopt_unsafe_unfuse_fma); 71 cpuopt_unsafe_unfuse_fma);
@@ -80,22 +81,6 @@ void ConfigureCpu::ApplyConfiguration() {
80 ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_fastmem_check, 81 ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_fastmem_check,
81 ui->cpuopt_unsafe_fastmem_check, 82 ui->cpuopt_unsafe_fastmem_check,
82 cpuopt_unsafe_fastmem_check); 83 cpuopt_unsafe_fastmem_check);
83
84 if (Settings::IsConfiguringGlobal()) {
85 // Guard if during game and set to game-specific value
86 if (Settings::values.cpu_accuracy.UsingGlobal()) {
87 Settings::values.cpu_accuracy.SetValue(
88 static_cast<Settings::CPUAccuracy>(ui->accuracy->currentIndex()));
89 }
90 } else {
91 if (ui->accuracy->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
92 Settings::values.cpu_accuracy.SetGlobal(true);
93 } else {
94 Settings::values.cpu_accuracy.SetGlobal(false);
95 Settings::values.cpu_accuracy.SetValue(static_cast<Settings::CPUAccuracy>(
96 ui->accuracy->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET));
97 }
98 }
99} 84}
100 85
101void ConfigureCpu::changeEvent(QEvent* event) { 86void ConfigureCpu::changeEvent(QEvent* event) {
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_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index bc009b6b3..fe4186157 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -67,7 +67,7 @@ void ConfigureDialog::ApplyConfiguration() {
67 ui->audioTab->ApplyConfiguration(); 67 ui->audioTab->ApplyConfiguration();
68 ui->debugTab->ApplyConfiguration(); 68 ui->debugTab->ApplyConfiguration();
69 ui->webTab->ApplyConfiguration(); 69 ui->webTab->ApplyConfiguration();
70 ui->serviceTab->ApplyConfiguration(); 70 ui->networkTab->ApplyConfiguration();
71 Core::System::GetInstance().ApplySettings(); 71 Core::System::GetInstance().ApplySettings();
72 Settings::LogSettings(); 72 Settings::LogSettings();
73} 73}
@@ -103,7 +103,7 @@ Q_DECLARE_METATYPE(QList<QWidget*>);
103void ConfigureDialog::PopulateSelectionList() { 103void ConfigureDialog::PopulateSelectionList() {
104 const std::array<std::pair<QString, QList<QWidget*>>, 6> items{ 104 const std::array<std::pair<QString, QList<QWidget*>>, 6> items{
105 {{tr("General"), {ui->generalTab, ui->hotkeysTab, ui->uiTab, ui->webTab, ui->debugTab}}, 105 {{tr("General"), {ui->generalTab, ui->hotkeysTab, ui->uiTab, ui->webTab, ui->debugTab}},
106 {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->serviceTab, ui->filesystemTab}}, 106 {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->networkTab, ui->filesystemTab}},
107 {tr("CPU"), {ui->cpuTab}}, 107 {tr("CPU"), {ui->cpuTab}},
108 {tr("Graphics"), {ui->graphicsTab, ui->graphicsAdvancedTab}}, 108 {tr("Graphics"), {ui->graphicsTab, ui->graphicsAdvancedTab}},
109 {tr("Audio"), {ui->audioTab}}, 109 {tr("Audio"), {ui->audioTab}},
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.cpp b/src/yuzu/configuration/configure_graphics.cpp
index 1bc477c96..37e896258 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -98,7 +98,8 @@ void ConfigureGraphics::SetConfiguration() {
98 98
99 if (Settings::IsConfiguringGlobal()) { 99 if (Settings::IsConfiguringGlobal()) {
100 ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend.GetValue())); 100 ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend.GetValue()));
101 ui->fullscreen_mode_combobox->setCurrentIndex(Settings::values.fullscreen_mode.GetValue()); 101 ui->fullscreen_mode_combobox->setCurrentIndex(
102 static_cast<int>(Settings::values.fullscreen_mode.GetValue()));
102 ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue()); 103 ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue());
103 } else { 104 } else {
104 ConfigurationShared::SetPerGameSetting(ui->api, &Settings::values.renderer_backend); 105 ConfigurationShared::SetPerGameSetting(ui->api, &Settings::values.renderer_backend);
@@ -310,8 +311,9 @@ void ConfigureGraphics::SetupPerGameUI() {
310 311
311 ConfigurationShared::SetColoredComboBox(ui->aspect_ratio_combobox, ui->ar_label, 312 ConfigurationShared::SetColoredComboBox(ui->aspect_ratio_combobox, ui->ar_label,
312 Settings::values.aspect_ratio.GetValue(true)); 313 Settings::values.aspect_ratio.GetValue(true));
313 ConfigurationShared::SetColoredComboBox(ui->fullscreen_mode_combobox, ui->fullscreen_mode_label, 314 ConfigurationShared::SetColoredComboBox(
314 Settings::values.fullscreen_mode.GetValue(true)); 315 ui->fullscreen_mode_combobox, ui->fullscreen_mode_label,
316 static_cast<int>(Settings::values.fullscreen_mode.GetValue(true)));
315 ConfigurationShared::InsertGlobalItem( 317 ConfigurationShared::InsertGlobalItem(
316 ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true))); 318 ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true)));
317} 319}
diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp
index 38276feb1..a31b8e192 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.cpp
+++ b/src/yuzu/configuration/configure_graphics_advanced.cpp
@@ -48,11 +48,7 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
48} 48}
49 49
50void ConfigureGraphicsAdvanced::ApplyConfiguration() { 50void ConfigureGraphicsAdvanced::ApplyConfiguration() {
51 // Subtract 2 if configuring per-game (separator and "use global configuration" take 2 slots) 51 ConfigurationShared::ApplyPerGameSetting(&Settings::values.gpu_accuracy, ui->gpu_accuracy);
52 const auto gpu_accuracy = static_cast<Settings::GPUAccuracy>(
53 ui->gpu_accuracy->currentIndex() -
54 ((Settings::IsConfiguringGlobal()) ? 0 : ConfigurationShared::USE_GLOBAL_OFFSET));
55
56 ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy, 52 ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy,
57 ui->anisotropic_filtering_combobox); 53 ui->anisotropic_filtering_combobox);
58 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->use_vsync, use_vsync); 54 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->use_vsync, use_vsync);
@@ -63,20 +59,6 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
63 use_caches_gc); 59 use_caches_gc);
64 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time, 60 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time,
65 ui->use_fast_gpu_time, use_fast_gpu_time); 61 ui->use_fast_gpu_time, use_fast_gpu_time);
66
67 if (Settings::IsConfiguringGlobal()) {
68 // Must guard in case of a during-game configuration when set to be game-specific.
69 if (Settings::values.gpu_accuracy.UsingGlobal()) {
70 Settings::values.gpu_accuracy.SetValue(gpu_accuracy);
71 }
72 } else {
73 if (ui->gpu_accuracy->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
74 Settings::values.gpu_accuracy.SetGlobal(true);
75 } else {
76 Settings::values.gpu_accuracy.SetGlobal(false);
77 Settings::values.gpu_accuracy.SetValue(gpu_accuracy);
78 }
79 }
80} 62}
81 63
82void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) { 64void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) {
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..9c890ed5d 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;
@@ -649,18 +647,18 @@ void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF cen
649 // Face buttons 647 // Face buttons
650 p.setPen(colors.outline); 648 p.setPen(colors.outline);
651 button_color = colors.button; 649 button_color = colors.button;
652 DrawCircleButton(p, face_center + QPoint(face_distance, 0), button_values[A], face_radius); 650 DrawCircleButton(p, face_center + QPointF(face_distance, 0), button_values[A], face_radius);
653 DrawCircleButton(p, face_center + QPoint(0, face_distance), button_values[B], face_radius); 651 DrawCircleButton(p, face_center + QPointF(0, face_distance), button_values[B], face_radius);
654 DrawCircleButton(p, face_center + QPoint(0, -face_distance), button_values[X], face_radius); 652 DrawCircleButton(p, face_center + QPointF(0, -face_distance), button_values[X], face_radius);
655 DrawCircleButton(p, face_center + QPoint(-face_distance, 0), button_values[Y], face_radius); 653 DrawCircleButton(p, face_center + QPointF(-face_distance, 0), button_values[Y], face_radius);
656 654
657 // Face buttons text 655 // Face buttons text
658 p.setPen(colors.transparent); 656 p.setPen(colors.transparent);
659 p.setBrush(colors.font); 657 p.setBrush(colors.font);
660 DrawSymbol(p, face_center + QPoint(face_distance, 0), Symbol::A, text_size); 658 DrawSymbol(p, face_center + QPointF(face_distance, 0), Symbol::A, text_size);
661 DrawSymbol(p, face_center + QPoint(0, face_distance), Symbol::B, text_size); 659 DrawSymbol(p, face_center + QPointF(0, face_distance), Symbol::B, text_size);
662 DrawSymbol(p, face_center + QPoint(0, -face_distance), Symbol::X, text_size); 660 DrawSymbol(p, face_center + QPointF(0, -face_distance), Symbol::X, text_size);
663 DrawSymbol(p, face_center + QPoint(-face_distance, 1), Symbol::Y, text_size); 661 DrawSymbol(p, face_center + QPointF(-face_distance, 1), Symbol::Y, text_size);
664 662
665 // D-pad constants 663 // D-pad constants
666 const QPointF dpad_center = center + QPoint(-171, 8); 664 const QPointF dpad_center = center + QPoint(-171, 8);
@@ -671,18 +669,20 @@ void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF cen
671 // D-pad buttons 669 // D-pad buttons
672 p.setPen(colors.outline); 670 p.setPen(colors.outline);
673 button_color = colors.button; 671 button_color = colors.button;
674 DrawCircleButton(p, dpad_center + QPoint(dpad_distance, 0), button_values[DRight], dpad_radius); 672 DrawCircleButton(p, dpad_center + QPointF(dpad_distance, 0), button_values[DRight],
675 DrawCircleButton(p, dpad_center + QPoint(0, dpad_distance), button_values[DDown], dpad_radius); 673 dpad_radius);
676 DrawCircleButton(p, dpad_center + QPoint(0, -dpad_distance), button_values[DUp], dpad_radius); 674 DrawCircleButton(p, dpad_center + QPointF(0, dpad_distance), button_values[DDown], dpad_radius);
677 DrawCircleButton(p, dpad_center + QPoint(-dpad_distance, 0), button_values[DLeft], dpad_radius); 675 DrawCircleButton(p, dpad_center + QPointF(0, -dpad_distance), button_values[DUp], dpad_radius);
676 DrawCircleButton(p, dpad_center + QPointF(-dpad_distance, 0), button_values[DLeft],
677 dpad_radius);
678 678
679 // D-pad arrows 679 // D-pad arrows
680 p.setPen(colors.font2); 680 p.setPen(colors.font2);
681 p.setBrush(colors.font2); 681 p.setBrush(colors.font2);
682 DrawArrow(p, dpad_center + QPoint(dpad_distance, 0), Direction::Right, dpad_arrow_size); 682 DrawArrow(p, dpad_center + QPointF(dpad_distance, 0), Direction::Right, dpad_arrow_size);
683 DrawArrow(p, dpad_center + QPoint(0, dpad_distance), Direction::Down, dpad_arrow_size); 683 DrawArrow(p, dpad_center + QPointF(0, dpad_distance), Direction::Down, dpad_arrow_size);
684 DrawArrow(p, dpad_center + QPoint(0, -dpad_distance), Direction::Up, dpad_arrow_size); 684 DrawArrow(p, dpad_center + QPointF(0, -dpad_distance), Direction::Up, dpad_arrow_size);
685 DrawArrow(p, dpad_center + QPoint(-dpad_distance, 0), Direction::Left, dpad_arrow_size); 685 DrawArrow(p, dpad_center + QPointF(-dpad_distance, 0), Direction::Left, dpad_arrow_size);
686 686
687 // ZL and ZR buttons 687 // ZL and ZR buttons
688 p.setPen(colors.outline); 688 p.setPen(colors.outline);
@@ -728,10 +728,7 @@ void PlayerControlPreview::DrawProController(QPainter& p, const QPointF center)
728 button_values[Settings::NativeButton::LStick]); 728 button_values[Settings::NativeButton::LStick]);
729 DrawProJoystick(p, center + QPointF(51, 0), axis_values[RStick].value, 11, 729 DrawProJoystick(p, center + QPointF(51, 0), axis_values[RStick].value, 11,
730 button_values[Settings::NativeButton::RStick]); 730 button_values[Settings::NativeButton::RStick]);
731 DrawRawJoystick(p, center + QPointF(-50, 105), axis_values[LStick].raw_value, 731 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 } 732 }
736 733
737 using namespace Settings::NativeButton; 734 using namespace Settings::NativeButton;
@@ -821,10 +818,7 @@ void PlayerControlPreview::DrawGCController(QPainter& p, const QPointF center) {
821 p.setBrush(colors.font); 818 p.setBrush(colors.font);
822 DrawSymbol(p, center + QPointF(61, 37) + (axis_values[RStick].value * 9.5f), Symbol::C, 819 DrawSymbol(p, center + QPointF(61, 37) + (axis_values[RStick].value * 9.5f), Symbol::C,
823 1.0f); 820 1.0f);
824 DrawRawJoystick(p, center + QPointF(-198, -125), axis_values[LStick].raw_value, 821 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 } 822 }
829 823
830 using namespace Settings::NativeButton; 824 using namespace Settings::NativeButton;
@@ -2358,8 +2352,33 @@ void PlayerControlPreview::DrawGCJoystick(QPainter& p, const QPointF center, boo
2358 DrawCircle(p, center, 7.5f); 2352 DrawCircle(p, center, 7.5f);
2359} 2353}
2360 2354
2361void PlayerControlPreview::DrawRawJoystick(QPainter& p, const QPointF center, const QPointF value, 2355void PlayerControlPreview::DrawRawJoystick(QPainter& p, QPointF center_left, QPointF center_right) {
2362 const Input::AnalogProperties& properties) { 2356 using namespace Settings::NativeAnalog;
2357 if (controller_type != Settings::ControllerType::LeftJoycon) {
2358 DrawJoystickProperties(p, center_right, axis_values[RStick].properties);
2359 p.setPen(colors.indicator);
2360 p.setBrush(colors.indicator);
2361 DrawJoystickDot(p, center_right, axis_values[RStick].raw_value,
2362 axis_values[RStick].properties);
2363 p.setPen(colors.indicator2);
2364 p.setBrush(colors.indicator2);
2365 DrawJoystickDot(p, center_right, axis_values[RStick].value, axis_values[RStick].properties);
2366 }
2367
2368 if (controller_type != Settings::ControllerType::RightJoycon) {
2369 DrawJoystickProperties(p, center_left, axis_values[LStick].properties);
2370 p.setPen(colors.indicator);
2371 p.setBrush(colors.indicator);
2372 DrawJoystickDot(p, center_left, axis_values[LStick].raw_value,
2373 axis_values[LStick].properties);
2374 p.setPen(colors.indicator2);
2375 p.setBrush(colors.indicator2);
2376 DrawJoystickDot(p, center_left, axis_values[LStick].value, axis_values[LStick].properties);
2377 }
2378}
2379
2380void PlayerControlPreview::DrawJoystickProperties(QPainter& p, const QPointF center,
2381 const Input::AnalogProperties& properties) {
2363 constexpr float size = 45.0f; 2382 constexpr float size = 45.0f;
2364 const float range = size * properties.range; 2383 const float range = size * properties.range;
2365 const float deadzone = size * properties.deadzone; 2384 const float deadzone = size * properties.deadzone;
@@ -2376,10 +2395,14 @@ void PlayerControlPreview::DrawRawJoystick(QPainter& p, const QPointF center, co
2376 pen.setColor(colors.deadzone); 2395 pen.setColor(colors.deadzone);
2377 p.setPen(pen); 2396 p.setPen(pen);
2378 DrawCircle(p, center, deadzone); 2397 DrawCircle(p, center, deadzone);
2398}
2399
2400void PlayerControlPreview::DrawJoystickDot(QPainter& p, const QPointF center, const QPointF value,
2401 const Input::AnalogProperties& properties) {
2402 constexpr float size = 45.0f;
2403 const float range = size * properties.range;
2379 2404
2380 // Dot pointer 2405 // Dot pointer
2381 p.setPen(colors.indicator);
2382 p.setBrush(colors.indicator);
2383 DrawCircle(p, center + (value * range), 2); 2406 DrawCircle(p, center + (value * range), 2);
2384} 2407}
2385 2408
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_service.cpp b/src/yuzu/configuration/configure_network.cpp
index 4aa424803..ae22f1018 100644
--- a/src/yuzu/configuration/configure_service.cpp
+++ b/src/yuzu/configuration/configure_network.cpp
@@ -5,9 +5,11 @@
5#include <QGraphicsItem> 5#include <QGraphicsItem>
6#include <QtConcurrent/QtConcurrent> 6#include <QtConcurrent/QtConcurrent>
7#include "common/settings.h" 7#include "common/settings.h"
8#include "core/core.h"
8#include "core/hle/service/bcat/backend/boxcat.h" 9#include "core/hle/service/bcat/backend/boxcat.h"
9#include "ui_configure_service.h" 10#include "core/network/network_interface.h"
10#include "yuzu/configuration/configure_service.h" 11#include "ui_configure_network.h"
12#include "yuzu/configuration/configure_network.h"
11 13
12#ifdef YUZU_ENABLE_BOXCAT 14#ifdef YUZU_ENABLE_BOXCAT
13namespace { 15namespace {
@@ -35,8 +37,8 @@ QString FormatEventStatusString(const Service::BCAT::EventStatus& status) {
35} // Anonymous namespace 37} // Anonymous namespace
36#endif 38#endif
37 39
38ConfigureService::ConfigureService(QWidget* parent) 40ConfigureNetwork::ConfigureNetwork(QWidget* parent)
39 : QWidget(parent), ui(std::make_unique<Ui::ConfigureService>()) { 41 : QWidget(parent), ui(std::make_unique<Ui::ConfigureNetwork>()) {
40 ui->setupUi(this); 42 ui->setupUi(this);
41 43
42 ui->bcat_source->addItem(QStringLiteral("None")); 44 ui->bcat_source->addItem(QStringLiteral("None"));
@@ -47,29 +49,42 @@ ConfigureService::ConfigureService(QWidget* parent)
47 ui->bcat_source->addItem(QStringLiteral("Boxcat"), QStringLiteral("boxcat")); 49 ui->bcat_source->addItem(QStringLiteral("Boxcat"), QStringLiteral("boxcat"));
48#endif 50#endif
49 51
52 ui->network_interface->addItem(tr("None"));
53 for (const auto& iface : Network::GetAvailableNetworkInterfaces()) {
54 ui->network_interface->addItem(QString::fromStdString(iface.name));
55 }
56
50 connect(ui->bcat_source, QOverload<int>::of(&QComboBox::currentIndexChanged), this, 57 connect(ui->bcat_source, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
51 &ConfigureService::OnBCATImplChanged); 58 &ConfigureNetwork::OnBCATImplChanged);
52 59
53 this->SetConfiguration(); 60 this->SetConfiguration();
54} 61}
55 62
56ConfigureService::~ConfigureService() = default; 63ConfigureNetwork::~ConfigureNetwork() = default;
57 64
58void ConfigureService::ApplyConfiguration() { 65void ConfigureNetwork::ApplyConfiguration() {
59 Settings::values.bcat_backend = ui->bcat_source->currentText().toLower().toStdString(); 66 Settings::values.bcat_backend = ui->bcat_source->currentText().toLower().toStdString();
67 Settings::values.network_interface = ui->network_interface->currentText().toStdString();
60} 68}
61 69
62void ConfigureService::RetranslateUi() { 70void ConfigureNetwork::RetranslateUi() {
63 ui->retranslateUi(this); 71 ui->retranslateUi(this);
64} 72}
65 73
66void ConfigureService::SetConfiguration() { 74void ConfigureNetwork::SetConfiguration() {
75 const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
76
67 const int index = 77 const int index =
68 ui->bcat_source->findData(QString::fromStdString(Settings::values.bcat_backend.GetValue())); 78 ui->bcat_source->findData(QString::fromStdString(Settings::values.bcat_backend.GetValue()));
69 ui->bcat_source->setCurrentIndex(index == -1 ? 0 : index); 79 ui->bcat_source->setCurrentIndex(index == -1 ? 0 : index);
80
81 const std::string& network_interface = Settings::values.network_interface.GetValue();
82
83 ui->network_interface->setCurrentText(QString::fromStdString(network_interface));
84 ui->network_interface->setEnabled(runtime_lock);
70} 85}
71 86
72std::pair<QString, QString> ConfigureService::BCATDownloadEvents() { 87std::pair<QString, QString> ConfigureNetwork::BCATDownloadEvents() {
73#ifdef YUZU_ENABLE_BOXCAT 88#ifdef YUZU_ENABLE_BOXCAT
74 std::optional<std::string> global; 89 std::optional<std::string> global;
75 std::map<std::string, Service::BCAT::EventStatus> map; 90 std::map<std::string, Service::BCAT::EventStatus> map;
@@ -114,7 +129,7 @@ std::pair<QString, QString> ConfigureService::BCATDownloadEvents() {
114#endif 129#endif
115} 130}
116 131
117void ConfigureService::OnBCATImplChanged() { 132void ConfigureNetwork::OnBCATImplChanged() {
118#ifdef YUZU_ENABLE_BOXCAT 133#ifdef YUZU_ENABLE_BOXCAT
119 const auto boxcat = ui->bcat_source->currentText() == QStringLiteral("Boxcat"); 134 const auto boxcat = ui->bcat_source->currentText() == QStringLiteral("Boxcat");
120 ui->bcat_empty_header->setHidden(!boxcat); 135 ui->bcat_empty_header->setHidden(!boxcat);
@@ -133,7 +148,7 @@ void ConfigureService::OnBCATImplChanged() {
133#endif 148#endif
134} 149}
135 150
136void ConfigureService::OnUpdateBCATEmptyLabel(std::pair<QString, QString> string) { 151void ConfigureNetwork::OnUpdateBCATEmptyLabel(std::pair<QString, QString> string) {
137#ifdef YUZU_ENABLE_BOXCAT 152#ifdef YUZU_ENABLE_BOXCAT
138 const auto boxcat = ui->bcat_source->currentText() == QStringLiteral("Boxcat"); 153 const auto boxcat = ui->bcat_source->currentText() == QStringLiteral("Boxcat");
139 if (boxcat) { 154 if (boxcat) {
diff --git a/src/yuzu/configuration/configure_service.h b/src/yuzu/configuration/configure_network.h
index f5c1b703a..442b68e6b 100644
--- a/src/yuzu/configuration/configure_service.h
+++ b/src/yuzu/configuration/configure_network.h
@@ -9,15 +9,15 @@
9#include <QWidget> 9#include <QWidget>
10 10
11namespace Ui { 11namespace Ui {
12class ConfigureService; 12class ConfigureNetwork;
13} 13}
14 14
15class ConfigureService : public QWidget { 15class ConfigureNetwork : public QWidget {
16 Q_OBJECT 16 Q_OBJECT
17 17
18public: 18public:
19 explicit ConfigureService(QWidget* parent = nullptr); 19 explicit ConfigureNetwork(QWidget* parent = nullptr);
20 ~ConfigureService() override; 20 ~ConfigureNetwork() override;
21 21
22 void ApplyConfiguration(); 22 void ApplyConfiguration();
23 void RetranslateUi(); 23 void RetranslateUi();
@@ -29,6 +29,6 @@ private:
29 void OnBCATImplChanged(); 29 void OnBCATImplChanged();
30 void OnUpdateBCATEmptyLabel(std::pair<QString, QString> string); 30 void OnUpdateBCATEmptyLabel(std::pair<QString, QString> string);
31 31
32 std::unique_ptr<Ui::ConfigureService> ui; 32 std::unique_ptr<Ui::ConfigureNetwork> ui;
33 QFutureWatcher<std::pair<QString, QString>> watcher{this}; 33 QFutureWatcher<std::pair<QString, QString>> watcher{this};
34}; 34};
diff --git a/src/yuzu/configuration/configure_service.ui b/src/yuzu/configuration/configure_network.ui
index 9668dd557..5f9b7e97b 100644
--- a/src/yuzu/configuration/configure_service.ui
+++ b/src/yuzu/configuration/configure_network.ui
@@ -1,7 +1,7 @@
1<?xml version="1.0" encoding="UTF-8"?> 1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0"> 2<ui version="4.0">
3 <class>ConfigureService</class> 3 <class>ConfigureNetwork</class>
4 <widget class="QWidget" name="ConfigureService"> 4 <widget class="QWidget" name="ConfigureNetwork">
5 <property name="geometry"> 5 <property name="geometry">
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
@@ -17,21 +17,37 @@
17 <item> 17 <item>
18 <layout class="QVBoxLayout" name="verticalLayout_3"> 18 <layout class="QVBoxLayout" name="verticalLayout_3">
19 <item> 19 <item>
20 <widget class="QGroupBox" name="groupBox_2">
21 <property name="title">
22 <string>General</string>
23 </property>
24 <layout class="QGridLayout" name="gridLayout_2">
25 <item row="1" column="1">
26 <widget class="QComboBox" name="network_interface"/>
27 </item>
28 <item row="1" column="0">
29 <widget class="QLabel" name="label_4">
30 <property name="text">
31 <string>Network Interface</string>
32 </property>
33 </widget>
34 </item>
35 </layout>
36 </widget>
37 </item>
38 <item>
20 <widget class="QGroupBox" name="groupBox"> 39 <widget class="QGroupBox" name="groupBox">
21 <property name="title"> 40 <property name="title">
22 <string>BCAT</string> 41 <string>BCAT</string>
23 </property> 42 </property>
24 <layout class="QGridLayout" name="gridLayout"> 43 <layout class="QGridLayout" name="gridLayout">
25 <item row="1" column="1" colspan="2"> 44 <item row="3" column="0">
26 <widget class="QLabel" name="label_2"> 45 <widget class="QLabel" name="bcat_empty_header">
27 <property name="maximumSize">
28 <size>
29 <width>260</width>
30 <height>16777215</height>
31 </size>
32 </property>
33 <property name="text"> 46 <property name="text">
34 <string>BCAT is Nintendo's way of sending data to games to engage its community and unlock additional content.</string> 47 <string/>
48 </property>
49 <property name="alignment">
50 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
35 </property> 51 </property>
36 <property name="wordWrap"> 52 <property name="wordWrap">
37 <bool>true</bool> 53 <bool>true</bool>
@@ -51,11 +67,8 @@
51 </property> 67 </property>
52 </widget> 68 </widget>
53 </item> 69 </item>
54 <item row="3" column="1" colspan="2"> 70 <item row="1" column="1" colspan="2">
55 <widget class="QLabel" name="bcat_empty_label"> 71 <widget class="QLabel" name="label_2">
56 <property name="enabled">
57 <bool>true</bool>
58 </property>
59 <property name="maximumSize"> 72 <property name="maximumSize">
60 <size> 73 <size>
61 <width>260</width> 74 <width>260</width>
@@ -63,10 +76,7 @@
63 </size> 76 </size>
64 </property> 77 </property>
65 <property name="text"> 78 <property name="text">
66 <string/> 79 <string>BCAT is Nintendo's way of sending data to games to engage its community and unlock additional content.</string>
67 </property>
68 <property name="alignment">
69 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
70 </property> 80 </property>
71 <property name="wordWrap"> 81 <property name="wordWrap">
72 <bool>true</bool> 82 <bool>true</bool>
@@ -86,8 +96,17 @@
86 <item row="0" column="1" colspan="2"> 96 <item row="0" column="1" colspan="2">
87 <widget class="QComboBox" name="bcat_source"/> 97 <widget class="QComboBox" name="bcat_source"/>
88 </item> 98 </item>
89 <item row="3" column="0"> 99 <item row="3" column="1" colspan="2">
90 <widget class="QLabel" name="bcat_empty_header"> 100 <widget class="QLabel" name="bcat_empty_label">
101 <property name="enabled">
102 <bool>true</bool>
103 </property>
104 <property name="maximumSize">
105 <size>
106 <width>260</width>
107 <height>16777215</height>
108 </size>
109 </property>
91 <property name="text"> 110 <property name="text">
92 <string/> 111 <string/>
93 </property> 112 </property>
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/debugger/console.cpp b/src/yuzu/debugger/console.cpp
index 22ca1285d..f89ea8ea7 100644
--- a/src/yuzu/debugger/console.cpp
+++ b/src/yuzu/debugger/console.cpp
@@ -21,6 +21,7 @@ void ToggleConsole() {
21 console_shown = UISettings::values.show_console.GetValue(); 21 console_shown = UISettings::values.show_console.GetValue();
22 } 22 }
23 23
24 using namespace Common::Log;
24#if defined(_WIN32) && !defined(_DEBUG) 25#if defined(_WIN32) && !defined(_DEBUG)
25 FILE* temp; 26 FILE* temp;
26 if (UISettings::values.show_console) { 27 if (UISettings::values.show_console) {
@@ -29,24 +30,20 @@ void ToggleConsole() {
29 freopen_s(&temp, "CONIN$", "r", stdin); 30 freopen_s(&temp, "CONIN$", "r", stdin);
30 freopen_s(&temp, "CONOUT$", "w", stdout); 31 freopen_s(&temp, "CONOUT$", "w", stdout);
31 freopen_s(&temp, "CONOUT$", "w", stderr); 32 freopen_s(&temp, "CONOUT$", "w", stderr);
32 Common::Log::AddBackend(std::make_unique<Common::Log::ColorConsoleBackend>()); 33 SetColorConsoleBackendEnabled(true);
33 } 34 }
34 } else { 35 } else {
35 if (FreeConsole()) { 36 if (FreeConsole()) {
36 // In order to close the console, we have to also detach the streams on it. 37 // In order to close the console, we have to also detach the streams on it.
37 // Just redirect them to NUL if there is no console window 38 // Just redirect them to NUL if there is no console window
38 Common::Log::RemoveBackend(Common::Log::ColorConsoleBackend::Name()); 39 SetColorConsoleBackendEnabled(false);
39 freopen_s(&temp, "NUL", "r", stdin); 40 freopen_s(&temp, "NUL", "r", stdin);
40 freopen_s(&temp, "NUL", "w", stdout); 41 freopen_s(&temp, "NUL", "w", stdout);
41 freopen_s(&temp, "NUL", "w", stderr); 42 freopen_s(&temp, "NUL", "w", stderr);
42 } 43 }
43 } 44 }
44#else 45#else
45 if (UISettings::values.show_console) { 46 SetColorConsoleBackendEnabled(UISettings::values.show_console.GetValue());
46 Common::Log::AddBackend(std::make_unique<Common::Log::ColorConsoleBackend>());
47 } else {
48 Common::Log::RemoveBackend(Common::Log::ColorConsoleBackend::Name());
49 }
50#endif 47#endif
51} 48}
52} // namespace Debugger 49} // namespace Debugger
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 e172d2ff4..1bae1489f 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -175,21 +175,6 @@ void GMainWindow::ShowTelemetryCallout() {
175 175
176const int GMainWindow::max_recent_files_item; 176const int GMainWindow::max_recent_files_item;
177 177
178static void InitializeLogging() {
179 using namespace Common;
180
181 Log::Filter log_filter;
182 log_filter.ParseFilterString(Settings::values.log_filter.GetValue());
183 Log::SetGlobalFilter(log_filter);
184
185 const auto log_dir = FS::GetYuzuPath(FS::YuzuPath::LogDir);
186 void(FS::CreateDir(log_dir));
187 Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir / LOG_FILE));
188#ifdef _WIN32
189 Log::AddBackend(std::make_unique<Log::DebuggerBackend>());
190#endif
191}
192
193static void RemoveCachedContents() { 178static void RemoveCachedContents() {
194 const auto cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir); 179 const auto cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir);
195 const auto offline_fonts = cache_dir / "fonts"; 180 const auto offline_fonts = cache_dir / "fonts";
@@ -207,8 +192,6 @@ GMainWindow::GMainWindow()
207 : input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, 192 : input_subsystem{std::make_shared<InputCommon::InputSubsystem>()},
208 config{std::make_unique<Config>()}, vfs{std::make_shared<FileSys::RealVfsFilesystem>()}, 193 config{std::make_unique<Config>()}, vfs{std::make_shared<FileSys::RealVfsFilesystem>()},
209 provider{std::make_unique<FileSys::ManualContentProvider>()} { 194 provider{std::make_unique<FileSys::ManualContentProvider>()} {
210 InitializeLogging();
211
212 LoadTranslation(); 195 LoadTranslation();
213 196
214 setAcceptDrops(true); 197 setAcceptDrops(true);
@@ -2513,7 +2496,7 @@ void GMainWindow::ShowFullscreen() {
2513 ui.menubar->hide(); 2496 ui.menubar->hide();
2514 statusBar()->hide(); 2497 statusBar()->hide();
2515 2498
2516 if (Settings::values.fullscreen_mode.GetValue() == 1) { 2499 if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) {
2517 showFullScreen(); 2500 showFullScreen();
2518 return; 2501 return;
2519 } 2502 }
@@ -2528,7 +2511,7 @@ void GMainWindow::ShowFullscreen() {
2528 } else { 2511 } else {
2529 UISettings::values.renderwindow_geometry = render_window->saveGeometry(); 2512 UISettings::values.renderwindow_geometry = render_window->saveGeometry();
2530 2513
2531 if (Settings::values.fullscreen_mode.GetValue() == 1) { 2514 if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) {
2532 render_window->showFullScreen(); 2515 render_window->showFullScreen();
2533 return; 2516 return;
2534 } 2517 }
@@ -2545,7 +2528,7 @@ void GMainWindow::ShowFullscreen() {
2545 2528
2546void GMainWindow::HideFullscreen() { 2529void GMainWindow::HideFullscreen() {
2547 if (ui.action_Single_Window_Mode->isChecked()) { 2530 if (ui.action_Single_Window_Mode->isChecked()) {
2548 if (Settings::values.fullscreen_mode.GetValue() == 1) { 2531 if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) {
2549 showNormal(); 2532 showNormal();
2550 restoreGeometry(UISettings::values.geometry); 2533 restoreGeometry(UISettings::values.geometry);
2551 } else { 2534 } else {
@@ -2559,7 +2542,7 @@ void GMainWindow::HideFullscreen() {
2559 statusBar()->setVisible(ui.action_Show_Status_Bar->isChecked()); 2542 statusBar()->setVisible(ui.action_Show_Status_Bar->isChecked());
2560 ui.menubar->show(); 2543 ui.menubar->show();
2561 } else { 2544 } else {
2562 if (Settings::values.fullscreen_mode.GetValue() == 1) { 2545 if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) {
2563 render_window->showNormal(); 2546 render_window->showNormal();
2564 render_window->restoreGeometry(UISettings::values.renderwindow_geometry); 2547 render_window->restoreGeometry(UISettings::values.renderwindow_geometry);
2565 } else { 2548 } else {
@@ -2814,8 +2797,6 @@ void GMainWindow::OnToggleFilterBar() {
2814} 2797}
2815 2798
2816void GMainWindow::OnCaptureScreenshot() { 2799void GMainWindow::OnCaptureScreenshot() {
2817 OnPauseGame();
2818
2819 const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); 2800 const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
2820 const auto screenshot_path = 2801 const auto screenshot_path =
2821 QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::ScreenshotsDir)); 2802 QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::ScreenshotsDir));
@@ -2827,23 +2808,22 @@ void GMainWindow::OnCaptureScreenshot() {
2827 .arg(date); 2808 .arg(date);
2828 2809
2829 if (!Common::FS::CreateDir(screenshot_path.toStdString())) { 2810 if (!Common::FS::CreateDir(screenshot_path.toStdString())) {
2830 OnStartGame();
2831 return; 2811 return;
2832 } 2812 }
2833 2813
2834#ifdef _WIN32 2814#ifdef _WIN32
2835 if (UISettings::values.enable_screenshot_save_as) { 2815 if (UISettings::values.enable_screenshot_save_as) {
2816 OnPauseGame();
2836 filename = QFileDialog::getSaveFileName(this, tr("Capture Screenshot"), filename, 2817 filename = QFileDialog::getSaveFileName(this, tr("Capture Screenshot"), filename,
2837 tr("PNG Image (*.png)")); 2818 tr("PNG Image (*.png)"));
2819 OnStartGame();
2838 if (filename.isEmpty()) { 2820 if (filename.isEmpty()) {
2839 OnStartGame();
2840 return; 2821 return;
2841 } 2822 }
2842 } 2823 }
2843#endif 2824#endif
2844 render_window->CaptureScreenshot(UISettings::values.screenshot_resolution_factor.GetValue(), 2825 render_window->CaptureScreenshot(UISettings::values.screenshot_resolution_factor.GetValue(),
2845 filename); 2826 filename);
2846 OnStartGame();
2847} 2827}
2848 2828
2849// TODO: Written 2020-10-01: Remove per-game config migration code when it is irrelevant 2829// TODO: Written 2020-10-01: Remove per-game config migration code when it is irrelevant
@@ -3401,6 +3381,7 @@ void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
3401#endif 3381#endif
3402 3382
3403int main(int argc, char* argv[]) { 3383int main(int argc, char* argv[]) {
3384 Common::Log::Initialize();
3404 Common::DetachedTasks detached_tasks; 3385 Common::DetachedTasks detached_tasks;
3405 MicroProfileOnThreadCreate("Frontend"); 3386 MicroProfileOnThreadCreate("Frontend");
3406 SCOPE_EXIT({ MicroProfileShutdown(); }); 3387 SCOPE_EXIT({ MicroProfileShutdown(); });
@@ -3440,6 +3421,7 @@ int main(int argc, char* argv[]) {
3440 // generating shaders 3421 // generating shaders
3441 setlocale(LC_ALL, "C"); 3422 setlocale(LC_ALL, "C");
3442 3423
3424 Core::System::InitializeGlobalInstance();
3443 GMainWindow main_window; 3425 GMainWindow main_window;
3444 // After settings have been loaded by GMainWindow, apply the filter 3426 // After settings have been loaded by GMainWindow, apply the filter
3445 main_window.show(); 3427 main_window.show();
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 353e51ea7..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,9 +122,13 @@ 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 1: // Exclusive fullscreen 131 case Settings::FullscreenMode::Exclusive:
128 // Set window size to render size before entering fullscreen -- SDL does not resize to 132 // Set window size to render size before entering fullscreen -- SDL does not resize to
129 // display dimensions in this mode. 133 // display dimensions in this mode.
130 // TODO: Multiply the window size by resolution_factor (for both docked modes) 134 // TODO: Multiply the window size by resolution_factor (for both docked modes)
@@ -140,7 +144,7 @@ void EmuWindow_SDL2::Fullscreen() {
140 LOG_ERROR(Frontend, "Fullscreening failed: {}", SDL_GetError()); 144 LOG_ERROR(Frontend, "Fullscreening failed: {}", SDL_GetError());
141 LOG_INFO(Frontend, "Attempting to use borderless fullscreen..."); 145 LOG_INFO(Frontend, "Attempting to use borderless fullscreen...");
142 [[fallthrough]]; 146 [[fallthrough]];
143 case 0: // Borderless window 147 case Settings::FullscreenMode::Borderless:
144 if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN_DESKTOP) == 0) { 148 if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN_DESKTOP) == 0) {
145 return; 149 return;
146 } 150 }
@@ -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..ba2c993ba 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -74,31 +74,14 @@ static void PrintVersion() {
74 std::cout << "yuzu " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl; 74 std::cout << "yuzu " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl;
75} 75}
76 76
77static void InitializeLogging() {
78 using namespace Common;
79
80 Log::Filter log_filter(Log::Level::Debug);
81 log_filter.ParseFilterString(static_cast<std::string>(Settings::values.log_filter));
82 Log::SetGlobalFilter(log_filter);
83
84 Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
85
86 const auto& log_dir = FS::GetYuzuPath(FS::YuzuPath::LogDir);
87 void(FS::CreateDir(log_dir));
88 Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir / LOG_FILE));
89#ifdef _WIN32
90 Log::AddBackend(std::make_unique<Log::DebuggerBackend>());
91#endif
92}
93
94/// Application entry point 77/// Application entry point
95int main(int argc, char** argv) { 78int main(int argc, char** argv) {
79 Common::Log::Initialize();
80 Common::Log::SetColorConsoleBackendEnabled(true);
96 Common::DetachedTasks detached_tasks; 81 Common::DetachedTasks detached_tasks;
97 Config config; 82 Config config;
98 83
99 int option_index = 0; 84 int option_index = 0;
100
101 InitializeLogging();
102#ifdef _WIN32 85#ifdef _WIN32
103 int argc_w; 86 int argc_w;
104 auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w); 87 auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w);
@@ -163,6 +146,7 @@ int main(int argc, char** argv) {
163 return -1; 146 return -1;
164 } 147 }
165 148
149 Core::System::InitializeGlobalInstance();
166 auto& system{Core::System::GetInstance()}; 150 auto& system{Core::System::GetInstance()};
167 InputCommon::InputSubsystem input_subsystem; 151 InputCommon::InputSubsystem input_subsystem;
168 152
@@ -172,10 +156,10 @@ int main(int argc, char** argv) {
172 std::unique_ptr<EmuWindow_SDL2> emu_window; 156 std::unique_ptr<EmuWindow_SDL2> emu_window;
173 switch (Settings::values.renderer_backend.GetValue()) { 157 switch (Settings::values.renderer_backend.GetValue()) {
174 case Settings::RendererBackend::OpenGL: 158 case Settings::RendererBackend::OpenGL:
175 emu_window = std::make_unique<EmuWindow_SDL2_GL>(&input_subsystem, fullscreen); 159 emu_window = std::make_unique<EmuWindow_SDL2_GL>(&input_subsystem, system, fullscreen);
176 break; 160 break;
177 case Settings::RendererBackend::Vulkan: 161 case Settings::RendererBackend::Vulkan:
178 emu_window = std::make_unique<EmuWindow_SDL2_VK>(&input_subsystem, fullscreen); 162 emu_window = std::make_unique<EmuWindow_SDL2_VK>(&input_subsystem, system, fullscreen);
179 break; 163 break;
180 } 164 }
181 165