diff options
76 files changed, 1136 insertions, 346 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 0e064ab44..5df2ff3fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
| @@ -518,6 +518,10 @@ set(FFmpeg_COMPONENTS | |||
| 518 | avutil | 518 | avutil |
| 519 | swscale) | 519 | swscale) |
| 520 | 520 | ||
| 521 | if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") | ||
| 522 | Include(FindPkgConfig REQUIRED) | ||
| 523 | pkg_check_modules(LIBVA libva) | ||
| 524 | endif() | ||
| 521 | if (NOT YUZU_USE_BUNDLED_FFMPEG) | 525 | if (NOT YUZU_USE_BUNDLED_FFMPEG) |
| 522 | # Use system installed FFmpeg | 526 | # Use system installed FFmpeg |
| 523 | find_package(FFmpeg QUIET COMPONENTS ${FFmpeg_COMPONENTS}) | 527 | find_package(FFmpeg QUIET COMPONENTS ${FFmpeg_COMPONENTS}) |
| @@ -540,6 +544,9 @@ endif() | |||
| 540 | 544 | ||
| 541 | if (YUZU_USE_BUNDLED_FFMPEG) | 545 | if (YUZU_USE_BUNDLED_FFMPEG) |
| 542 | if (NOT WIN32) | 546 | if (NOT WIN32) |
| 547 | # TODO(lat9nq): Move this to externals/ffmpeg/CMakeLists.txt (and move externals/ffmpeg to | ||
| 548 | # externals/ffmpeg/ffmpeg) | ||
| 549 | |||
| 543 | # Build FFmpeg from externals | 550 | # Build FFmpeg from externals |
| 544 | message(STATUS "Using FFmpeg from externals") | 551 | message(STATUS "Using FFmpeg from externals") |
| 545 | 552 | ||
| @@ -579,20 +586,23 @@ if (YUZU_USE_BUNDLED_FFMPEG) | |||
| 579 | CACHE PATH "Paths to FFmpeg libraries" FORCE) | 586 | CACHE PATH "Paths to FFmpeg libraries" FORCE) |
| 580 | endforeach() | 587 | endforeach() |
| 581 | 588 | ||
| 582 | set(FFmpeg_INCLUDE_DIR | 589 | Include(FindPkgConfig REQUIRED) |
| 583 | "${FFmpeg_PREFIX};${FFmpeg_BUILD_DIR}" | 590 | pkg_check_modules(LIBVA libva) |
| 584 | CACHE PATH "Path to FFmpeg headers" FORCE) | 591 | pkg_check_modules(CUDA cuda) |
| 592 | pkg_check_modules(FFNVCODEC ffnvcodec) | ||
| 593 | pkg_check_modules(VDPAU vdpau) | ||
| 594 | |||
| 595 | set(FFmpeg_HWACCEL_LIBRARIES) | ||
| 596 | set(FFmpeg_HWACCEL_FLAGS) | ||
| 597 | set(FFmpeg_HWACCEL_INCLUDE_DIRS) | ||
| 598 | set(FFmpeg_HWACCEL_LDFLAGS) | ||
| 585 | 599 | ||
| 586 | if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") | ||
| 587 | Include(FindPkgConfig REQUIRED) | ||
| 588 | pkg_check_modules(LIBVA libva) | ||
| 589 | endif() | ||
| 590 | if(LIBVA_FOUND) | 600 | if(LIBVA_FOUND) |
| 591 | pkg_check_modules(LIBDRM libdrm REQUIRED) | 601 | pkg_check_modules(LIBDRM libdrm REQUIRED) |
| 592 | find_package(X11 REQUIRED) | 602 | find_package(X11 REQUIRED) |
| 593 | pkg_check_modules(LIBVA-DRM libva-drm REQUIRED) | 603 | pkg_check_modules(LIBVA-DRM libva-drm REQUIRED) |
| 594 | pkg_check_modules(LIBVA-X11 libva-x11 REQUIRED) | 604 | pkg_check_modules(LIBVA-X11 libva-x11 REQUIRED) |
| 595 | set(FFmpeg_LIBVA_LIBRARIES | 605 | list(APPEND FFmpeg_HWACCEL_LIBRARIES |
| 596 | ${LIBDRM_LIBRARIES} | 606 | ${LIBDRM_LIBRARIES} |
| 597 | ${X11_LIBRARIES} | 607 | ${X11_LIBRARIES} |
| 598 | ${LIBVA-DRM_LIBRARIES} | 608 | ${LIBVA-DRM_LIBRARIES} |
| @@ -602,11 +612,56 @@ if (YUZU_USE_BUNDLED_FFMPEG) | |||
| 602 | --enable-hwaccel=h264_vaapi | 612 | --enable-hwaccel=h264_vaapi |
| 603 | --enable-hwaccel=vp9_vaapi | 613 | --enable-hwaccel=vp9_vaapi |
| 604 | --enable-libdrm) | 614 | --enable-libdrm) |
| 615 | list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS | ||
| 616 | ${LIBDRM_INCLUDE_DIRS} | ||
| 617 | ${X11_INCLUDE_DIRS} | ||
| 618 | ${LIBVA-DRM_INCLUDE_DIRS} | ||
| 619 | ${LIBVA-X11_INCLUDE_DIRS} | ||
| 620 | ${LIBVA_INCLUDE_DIRS} | ||
| 621 | ) | ||
| 605 | message(STATUS "VA-API found") | 622 | message(STATUS "VA-API found") |
| 606 | else() | 623 | else() |
| 607 | set(FFmpeg_HWACCEL_FLAGS --disable-vaapi) | 624 | set(FFmpeg_HWACCEL_FLAGS --disable-vaapi) |
| 608 | endif() | 625 | endif() |
| 609 | 626 | ||
| 627 | if (FFNVCODEC_FOUND AND CUDA_FOUND) | ||
| 628 | list(APPEND FFmpeg_HWACCEL_FLAGS | ||
| 629 | --enable-cuvid | ||
| 630 | --enable-ffnvcodec | ||
| 631 | --enable-nvdec | ||
| 632 | --enable-hwaccel=h264_nvdec | ||
| 633 | --enable-hwaccel=vp9_nvdec | ||
| 634 | --extra-cflags=-I${CUDA_INCLUDE_DIRS} | ||
| 635 | ) | ||
| 636 | list(APPEND FFmpeg_HWACCEL_LIBRARIES | ||
| 637 | ${FFNVCODEC_LIBRARIES} | ||
| 638 | ${CUDA_LIBRARIES} | ||
| 639 | ) | ||
| 640 | list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS | ||
| 641 | ${FFNVCODEC_INCLUDE_DIRS} | ||
| 642 | ${CUDA_INCLUDE_DIRS} | ||
| 643 | ) | ||
| 644 | list(APPEND FFmpeg_HWACCEL_LDFLAGS | ||
| 645 | ${FFNVCODEC_LDFLAGS} | ||
| 646 | ${CUDA_LDFLAGS} | ||
| 647 | ) | ||
| 648 | message(STATUS "ffnvcodec libraries version ${FFNVCODEC_VERSION} found") | ||
| 649 | endif() | ||
| 650 | |||
| 651 | if (VDPAU_FOUND) | ||
| 652 | list(APPEND FFmpeg_HWACCEL_FLAGS | ||
| 653 | --enable-vdpau | ||
| 654 | --enable-hwaccel=h264_vdpau | ||
| 655 | --enable-hwaccel=vp9_vdpau | ||
| 656 | ) | ||
| 657 | list(APPEND FFmpeg_HWACCEL_LIBRARIES ${VDPAU_LIBRARIES}) | ||
| 658 | list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS ${VDPAU_INCLUDE_DIRS}) | ||
| 659 | list(APPEND FFmpeg_HWACCEL_LDFLAGS ${VDPAU_LDFLAGS}) | ||
| 660 | message(STATUS "vdpau libraries version ${VDPAU_VERSION} found") | ||
| 661 | else() | ||
| 662 | list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vdpau) | ||
| 663 | endif() | ||
| 664 | |||
| 610 | # `configure` parameters builds only exactly what yuzu needs from FFmpeg | 665 | # `configure` parameters builds only exactly what yuzu needs from FFmpeg |
| 611 | # `--disable-vdpau` is needed to avoid linking issues | 666 | # `--disable-vdpau` is needed to avoid linking issues |
| 612 | add_custom_command( | 667 | add_custom_command( |
| @@ -624,7 +679,6 @@ if (YUZU_USE_BUNDLED_FFMPEG) | |||
| 624 | --disable-network | 679 | --disable-network |
| 625 | --disable-postproc | 680 | --disable-postproc |
| 626 | --disable-swresample | 681 | --disable-swresample |
| 627 | --disable-vdpau | ||
| 628 | --enable-decoder=h264 | 682 | --enable-decoder=h264 |
| 629 | --enable-decoder=vp9 | 683 | --enable-decoder=vp9 |
| 630 | --cc="${CMAKE_C_COMPILER}" | 684 | --cc="${CMAKE_C_COMPILER}" |
| @@ -653,15 +707,26 @@ if (YUZU_USE_BUNDLED_FFMPEG) | |||
| 653 | ${FFmpeg_BUILD_DIR} | 707 | ${FFmpeg_BUILD_DIR} |
| 654 | ) | 708 | ) |
| 655 | 709 | ||
| 710 | set(FFmpeg_INCLUDE_DIR | ||
| 711 | "${FFmpeg_PREFIX};${FFmpeg_BUILD_DIR};${FFmpeg_HWACCEL_INCLUDE_DIRS}" | ||
| 712 | CACHE PATH "Path to FFmpeg headers" FORCE) | ||
| 713 | |||
| 714 | set(FFmpeg_LDFLAGS | ||
| 715 | "${FFmpeg_HWACCEL_LDFLAGS}" | ||
| 716 | CACHE STRING "FFmpeg linker flags" FORCE) | ||
| 717 | |||
| 656 | # ALL makes this custom target build every time | 718 | # ALL makes this custom target build every time |
| 657 | # but it won't actually build if the DEPENDS parameter is up to date | 719 | # but it won't actually build if the DEPENDS parameter is up to date |
| 658 | add_custom_target(ffmpeg-configure ALL DEPENDS ${FFmpeg_MAKEFILE}) | 720 | add_custom_target(ffmpeg-configure ALL DEPENDS ${FFmpeg_MAKEFILE}) |
| 659 | add_custom_target(ffmpeg-build ALL DEPENDS ${FFmpeg_BUILD_LIBRARIES} ffmpeg-configure) | 721 | add_custom_target(ffmpeg-build ALL DEPENDS ${FFmpeg_BUILD_LIBRARIES} ffmpeg-configure) |
| 660 | link_libraries(${FFmpeg_LIBVA_LIBRARIES}) | 722 | link_libraries(${FFmpeg_LIBVA_LIBRARIES}) |
| 661 | set(FFmpeg_LIBRARIES ${FFmpeg_LIBVA_LIBRARIES} ${FFmpeg_BUILD_LIBRARIES} | 723 | set(FFmpeg_LIBRARIES ${FFmpeg_BUILD_LIBRARIES} ${FFmpeg_HWACCEL_LIBRARIES} |
| 662 | CACHE PATH "Paths to FFmpeg libraries" FORCE) | 724 | CACHE PATH "Paths to FFmpeg libraries" FORCE) |
| 663 | unset(FFmpeg_BUILD_LIBRARIES) | 725 | unset(FFmpeg_BUILD_LIBRARIES) |
| 664 | unset(FFmpeg_LIBVA_LIBRARIES) | 726 | unset(FFmpeg_HWACCEL_FLAGS) |
| 727 | unset(FFmpeg_HWACCEL_INCLUDE_DIRS) | ||
| 728 | unset(FFmpeg_HWACCEL_LDFLAGS) | ||
| 729 | unset(FFmpeg_HWACCEL_LIBRARIES) | ||
| 665 | 730 | ||
| 666 | if (FFmpeg_FOUND) | 731 | if (FFmpeg_FOUND) |
| 667 | message(STATUS "Found FFmpeg version ${FFmpeg_VERSION}") | 732 | message(STATUS "Found FFmpeg version ${FFmpeg_VERSION}") |
| @@ -670,12 +735,13 @@ if (YUZU_USE_BUNDLED_FFMPEG) | |||
| 670 | endif() | 735 | endif() |
| 671 | else() # WIN32 | 736 | else() # WIN32 |
| 672 | # Use yuzu FFmpeg binaries | 737 | # Use yuzu FFmpeg binaries |
| 673 | set(FFmpeg_EXT_NAME "ffmpeg-4.3.1") | 738 | set(FFmpeg_EXT_NAME "ffmpeg-4.4") |
| 674 | set(FFmpeg_PATH "${CMAKE_BINARY_DIR}/externals/${FFmpeg_EXT_NAME}") | 739 | set(FFmpeg_PATH "${CMAKE_BINARY_DIR}/externals/${FFmpeg_EXT_NAME}") |
| 675 | download_bundled_external("ffmpeg/" ${FFmpeg_EXT_NAME} "") | 740 | download_bundled_external("ffmpeg/" ${FFmpeg_EXT_NAME} "") |
| 676 | set(FFmpeg_FOUND YES) | 741 | set(FFmpeg_FOUND YES) |
| 677 | set(FFmpeg_INCLUDE_DIR "${FFmpeg_PATH}/include" CACHE PATH "Path to FFmpeg headers" FORCE) | 742 | set(FFmpeg_INCLUDE_DIR "${FFmpeg_PATH}/include" CACHE PATH "Path to FFmpeg headers" FORCE) |
| 678 | set(FFmpeg_LIBRARY_DIR "${FFmpeg_PATH}/bin" CACHE PATH "Path to FFmpeg library directory" FORCE) | 743 | set(FFmpeg_LIBRARY_DIR "${FFmpeg_PATH}/bin" CACHE PATH "Path to FFmpeg library directory" FORCE) |
| 744 | set(FFmpeg_LDFLAGS "" CACHE STRING "FFmpeg linker flags" FORCE) | ||
| 679 | set(FFmpeg_DLL_DIR "${FFmpeg_PATH}/bin" CACHE PATH "Path to FFmpeg dll's" FORCE) | 745 | set(FFmpeg_DLL_DIR "${FFmpeg_PATH}/bin" CACHE PATH "Path to FFmpeg dll's" FORCE) |
| 680 | set(FFmpeg_LIBRARIES | 746 | set(FFmpeg_LIBRARIES |
| 681 | ${FFmpeg_LIBRARY_DIR}/swscale.lib | 747 | ${FFmpeg_LIBRARY_DIR}/swscale.lib |
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 57922b51c..b18a2a2f5 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -53,6 +53,8 @@ add_library(common STATIC | |||
| 53 | div_ceil.h | 53 | div_ceil.h |
| 54 | dynamic_library.cpp | 54 | dynamic_library.cpp |
| 55 | dynamic_library.h | 55 | dynamic_library.h |
| 56 | error.cpp | ||
| 57 | error.h | ||
| 56 | fiber.cpp | 58 | fiber.cpp |
| 57 | fiber.h | 59 | fiber.h |
| 58 | fs/file.cpp | 60 | fs/file.cpp |
| @@ -88,7 +90,6 @@ add_library(common STATIC | |||
| 88 | microprofile.cpp | 90 | microprofile.cpp |
| 89 | microprofile.h | 91 | microprofile.h |
| 90 | microprofileui.h | 92 | microprofileui.h |
| 91 | misc.cpp | ||
| 92 | nvidia_flags.cpp | 93 | nvidia_flags.cpp |
| 93 | nvidia_flags.h | 94 | nvidia_flags.h |
| 94 | page_table.cpp | 95 | page_table.cpp |
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h index 53bd7da60..4c1e29de6 100644 --- a/src/common/common_funcs.h +++ b/src/common/common_funcs.h | |||
| @@ -4,9 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <algorithm> | ||
| 8 | #include <array> | 7 | #include <array> |
| 9 | #include <string> | 8 | #include <iterator> |
| 10 | 9 | ||
| 11 | #if !defined(ARCHITECTURE_x86_64) | 10 | #if !defined(ARCHITECTURE_x86_64) |
| 12 | #include <cstdlib> // for exit | 11 | #include <cstdlib> // for exit |
| @@ -49,16 +48,6 @@ __declspec(dllimport) void __stdcall DebugBreak(void); | |||
| 49 | 48 | ||
| 50 | #endif // _MSC_VER ndef | 49 | #endif // _MSC_VER ndef |
| 51 | 50 | ||
| 52 | // Generic function to get last error message. | ||
| 53 | // Call directly after the command or use the error num. | ||
| 54 | // This function might change the error code. | ||
| 55 | // Defined in misc.cpp. | ||
| 56 | [[nodiscard]] std::string GetLastErrorMsg(); | ||
| 57 | |||
| 58 | // Like GetLastErrorMsg(), but passing an explicit error code. | ||
| 59 | // Defined in misc.cpp. | ||
| 60 | [[nodiscard]] std::string NativeErrorToString(int e); | ||
| 61 | |||
| 62 | #define DECLARE_ENUM_FLAG_OPERATORS(type) \ | 51 | #define DECLARE_ENUM_FLAG_OPERATORS(type) \ |
| 63 | [[nodiscard]] constexpr type operator|(type a, type b) noexcept { \ | 52 | [[nodiscard]] constexpr type operator|(type a, type b) noexcept { \ |
| 64 | using T = std::underlying_type_t<type>; \ | 53 | using T = std::underlying_type_t<type>; \ |
| @@ -72,6 +61,14 @@ __declspec(dllimport) void __stdcall DebugBreak(void); | |||
| 72 | using T = std::underlying_type_t<type>; \ | 61 | using T = std::underlying_type_t<type>; \ |
| 73 | return static_cast<type>(static_cast<T>(a) ^ static_cast<T>(b)); \ | 62 | return static_cast<type>(static_cast<T>(a) ^ static_cast<T>(b)); \ |
| 74 | } \ | 63 | } \ |
| 64 | [[nodiscard]] constexpr type operator<<(type a, type b) noexcept { \ | ||
| 65 | using T = std::underlying_type_t<type>; \ | ||
| 66 | return static_cast<type>(static_cast<T>(a) << static_cast<T>(b)); \ | ||
| 67 | } \ | ||
| 68 | [[nodiscard]] constexpr type operator>>(type a, type b) noexcept { \ | ||
| 69 | using T = std::underlying_type_t<type>; \ | ||
| 70 | return static_cast<type>(static_cast<T>(a) >> static_cast<T>(b)); \ | ||
| 71 | } \ | ||
| 75 | constexpr type& operator|=(type& a, type b) noexcept { \ | 72 | constexpr type& operator|=(type& a, type b) noexcept { \ |
| 76 | a = a | b; \ | 73 | a = a | b; \ |
| 77 | return a; \ | 74 | return a; \ |
| @@ -84,6 +81,14 @@ __declspec(dllimport) void __stdcall DebugBreak(void); | |||
| 84 | a = a ^ b; \ | 81 | a = a ^ b; \ |
| 85 | return a; \ | 82 | return a; \ |
| 86 | } \ | 83 | } \ |
| 84 | constexpr type& operator<<=(type& a, type b) noexcept { \ | ||
| 85 | a = a << b; \ | ||
| 86 | return a; \ | ||
| 87 | } \ | ||
| 88 | constexpr type& operator>>=(type& a, type b) noexcept { \ | ||
| 89 | a = a >> b; \ | ||
| 90 | return a; \ | ||
| 91 | } \ | ||
| 87 | [[nodiscard]] constexpr type operator~(type key) noexcept { \ | 92 | [[nodiscard]] constexpr type operator~(type key) noexcept { \ |
| 88 | using T = std::underlying_type_t<type>; \ | 93 | using T = std::underlying_type_t<type>; \ |
| 89 | return static_cast<type>(~static_cast<T>(key)); \ | 94 | return static_cast<type>(~static_cast<T>(key)); \ |
diff --git a/src/common/misc.cpp b/src/common/error.cpp index 495385b9e..d4455e310 100644 --- a/src/common/misc.cpp +++ b/src/common/error.cpp | |||
| @@ -10,7 +10,9 @@ | |||
| 10 | #include <cstring> | 10 | #include <cstring> |
| 11 | #endif | 11 | #endif |
| 12 | 12 | ||
| 13 | #include "common/common_funcs.h" | 13 | #include "common/error.h" |
| 14 | |||
| 15 | namespace Common { | ||
| 14 | 16 | ||
| 15 | std::string NativeErrorToString(int e) { | 17 | std::string NativeErrorToString(int e) { |
| 16 | #ifdef _WIN32 | 18 | #ifdef _WIN32 |
| @@ -50,3 +52,5 @@ std::string GetLastErrorMsg() { | |||
| 50 | return NativeErrorToString(errno); | 52 | return NativeErrorToString(errno); |
| 51 | #endif | 53 | #endif |
| 52 | } | 54 | } |
| 55 | |||
| 56 | } // namespace Common | ||
diff --git a/src/common/error.h b/src/common/error.h new file mode 100644 index 000000000..e084d4b0f --- /dev/null +++ b/src/common/error.h | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra 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 <string> | ||
| 8 | |||
| 9 | namespace Common { | ||
| 10 | |||
| 11 | // Generic function to get last error message. | ||
| 12 | // Call directly after the command or use the error num. | ||
| 13 | // This function might change the error code. | ||
| 14 | // Defined in error.cpp. | ||
| 15 | [[nodiscard]] std::string GetLastErrorMsg(); | ||
| 16 | |||
| 17 | // Like GetLastErrorMsg(), but passing an explicit error code. | ||
| 18 | // Defined in error.cpp. | ||
| 19 | [[nodiscard]] std::string NativeErrorToString(int e); | ||
| 20 | |||
| 21 | } // namespace Common | ||
diff --git a/src/common/settings.cpp b/src/common/settings.cpp index aa4613d08..e1fa90c5a 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp | |||
| @@ -54,7 +54,7 @@ void LogSettings() { | |||
| 54 | log_setting("Renderer_GPUAccuracyLevel", values.gpu_accuracy.GetValue()); | 54 | log_setting("Renderer_GPUAccuracyLevel", values.gpu_accuracy.GetValue()); |
| 55 | log_setting("Renderer_UseAsynchronousGpuEmulation", | 55 | log_setting("Renderer_UseAsynchronousGpuEmulation", |
| 56 | values.use_asynchronous_gpu_emulation.GetValue()); | 56 | values.use_asynchronous_gpu_emulation.GetValue()); |
| 57 | log_setting("Renderer_UseNvdecEmulation", values.use_nvdec_emulation.GetValue()); | 57 | log_setting("Renderer_NvdecEmulation", values.nvdec_emulation.GetValue()); |
| 58 | log_setting("Renderer_AccelerateASTC", values.accelerate_astc.GetValue()); | 58 | log_setting("Renderer_AccelerateASTC", values.accelerate_astc.GetValue()); |
| 59 | log_setting("Renderer_UseVsync", values.use_vsync.GetValue()); | 59 | log_setting("Renderer_UseVsync", values.use_vsync.GetValue()); |
| 60 | log_setting("Renderer_ShaderBackend", values.shader_backend.GetValue()); | 60 | log_setting("Renderer_ShaderBackend", values.shader_backend.GetValue()); |
| @@ -139,7 +139,7 @@ void RestoreGlobalState(bool is_powered_on) { | |||
| 139 | values.use_disk_shader_cache.SetGlobal(true); | 139 | values.use_disk_shader_cache.SetGlobal(true); |
| 140 | values.gpu_accuracy.SetGlobal(true); | 140 | values.gpu_accuracy.SetGlobal(true); |
| 141 | values.use_asynchronous_gpu_emulation.SetGlobal(true); | 141 | values.use_asynchronous_gpu_emulation.SetGlobal(true); |
| 142 | values.use_nvdec_emulation.SetGlobal(true); | 142 | values.nvdec_emulation.SetGlobal(true); |
| 143 | values.accelerate_astc.SetGlobal(true); | 143 | values.accelerate_astc.SetGlobal(true); |
| 144 | values.use_vsync.SetGlobal(true); | 144 | values.use_vsync.SetGlobal(true); |
| 145 | values.shader_backend.SetGlobal(true); | 145 | values.shader_backend.SetGlobal(true); |
diff --git a/src/common/settings.h b/src/common/settings.h index 34065e937..e674ccc5c 100644 --- a/src/common/settings.h +++ b/src/common/settings.h | |||
| @@ -47,6 +47,12 @@ enum class FullscreenMode : u32 { | |||
| 47 | Exclusive = 1, | 47 | Exclusive = 1, |
| 48 | }; | 48 | }; |
| 49 | 49 | ||
| 50 | enum class NvdecEmulation : u32 { | ||
| 51 | Off = 0, | ||
| 52 | CPU = 1, | ||
| 53 | GPU = 2, | ||
| 54 | }; | ||
| 55 | |||
| 50 | /** The BasicSetting class is a simple resource manager. It defines a label and default value | 56 | /** The BasicSetting class is a simple resource manager. It defines a label and default value |
| 51 | * alongside the actual value of the setting for simpler and less-error prone use with frontend | 57 | * alongside the actual value of the setting for simpler and less-error prone use with frontend |
| 52 | * configurations. Setting a default value and label is required, though subclasses may deviate from | 58 | * configurations. Setting a default value and label is required, though subclasses may deviate from |
| @@ -465,7 +471,7 @@ struct Values { | |||
| 465 | RangedSetting<GPUAccuracy> gpu_accuracy{GPUAccuracy::High, GPUAccuracy::Normal, | 471 | RangedSetting<GPUAccuracy> gpu_accuracy{GPUAccuracy::High, GPUAccuracy::Normal, |
| 466 | GPUAccuracy::Extreme, "gpu_accuracy"}; | 472 | GPUAccuracy::Extreme, "gpu_accuracy"}; |
| 467 | Setting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"}; | 473 | Setting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"}; |
| 468 | Setting<bool> use_nvdec_emulation{true, "use_nvdec_emulation"}; | 474 | Setting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"}; |
| 469 | Setting<bool> accelerate_astc{true, "accelerate_astc"}; | 475 | Setting<bool> accelerate_astc{true, "accelerate_astc"}; |
| 470 | Setting<bool> use_vsync{true, "use_vsync"}; | 476 | Setting<bool> use_vsync{true, "use_vsync"}; |
| 471 | BasicRangedSetting<u16> fps_cap{1000, 1, 1000, "fps_cap"}; | 477 | BasicRangedSetting<u16> fps_cap{1000, 1, 1000, "fps_cap"}; |
diff --git a/src/common/thread.cpp b/src/common/thread.cpp index d2c1ac60d..946a1114d 100644 --- a/src/common/thread.cpp +++ b/src/common/thread.cpp | |||
| @@ -2,7 +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 "common/common_funcs.h" | 5 | #include <string> |
| 6 | |||
| 7 | #include "common/error.h" | ||
| 6 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 7 | #include "common/thread.h" | 9 | #include "common/thread.h" |
| 8 | #ifdef __APPLE__ | 10 | #ifdef __APPLE__ |
| @@ -21,8 +23,6 @@ | |||
| 21 | #include <unistd.h> | 23 | #include <unistd.h> |
| 22 | #endif | 24 | #endif |
| 23 | 25 | ||
| 24 | #include <string> | ||
| 25 | |||
| 26 | #ifdef __FreeBSD__ | 26 | #ifdef __FreeBSD__ |
| 27 | #define cpu_set_t cpuset_t | 27 | #define cpu_set_t cpuset_t |
| 28 | #endif | 28 | #endif |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 87d47e2e5..7140d0db8 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -263,6 +263,8 @@ add_library(core STATIC | |||
| 263 | hle/service/acc/acc_u0.h | 263 | hle/service/acc/acc_u0.h |
| 264 | hle/service/acc/acc_u1.cpp | 264 | hle/service/acc/acc_u1.cpp |
| 265 | hle/service/acc/acc_u1.h | 265 | hle/service/acc/acc_u1.h |
| 266 | hle/service/acc/async_context.cpp | ||
| 267 | hle/service/acc/async_context.h | ||
| 266 | hle/service/acc/errors.h | 268 | hle/service/acc/errors.h |
| 267 | hle/service/acc/profile_manager.cpp | 269 | hle/service/acc/profile_manager.cpp |
| 268 | hle/service/acc/profile_manager.h | 270 | hle/service/acc/profile_manager.h |
diff --git a/src/core/core.cpp b/src/core/core.cpp index ba4629993..b13350f6e 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -83,6 +83,12 @@ FileSys::StorageId GetStorageIdForFrontendSlot( | |||
| 83 | } | 83 | } |
| 84 | } | 84 | } |
| 85 | 85 | ||
| 86 | void KProcessDeleter(Kernel::KProcess* process) { | ||
| 87 | process->Destroy(); | ||
| 88 | } | ||
| 89 | |||
| 90 | using KProcessPtr = std::unique_ptr<Kernel::KProcess, decltype(&KProcessDeleter)>; | ||
| 91 | |||
| 86 | } // Anonymous namespace | 92 | } // Anonymous namespace |
| 87 | 93 | ||
| 88 | FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, | 94 | FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, |
| @@ -233,8 +239,8 @@ struct System::Impl { | |||
| 233 | } | 239 | } |
| 234 | 240 | ||
| 235 | telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider); | 241 | telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider); |
| 236 | auto main_process = Kernel::KProcess::Create(system.Kernel()); | 242 | main_process = KProcessPtr{Kernel::KProcess::Create(system.Kernel()), KProcessDeleter}; |
| 237 | ASSERT(Kernel::KProcess::Initialize(main_process, system, "main", | 243 | ASSERT(Kernel::KProcess::Initialize(main_process.get(), system, "main", |
| 238 | Kernel::KProcess::ProcessType::Userland) | 244 | Kernel::KProcess::ProcessType::Userland) |
| 239 | .IsSuccess()); | 245 | .IsSuccess()); |
| 240 | main_process->Open(); | 246 | main_process->Open(); |
| @@ -247,7 +253,7 @@ struct System::Impl { | |||
| 247 | static_cast<u32>(load_result)); | 253 | static_cast<u32>(load_result)); |
| 248 | } | 254 | } |
| 249 | AddGlueRegistrationForProcess(*app_loader, *main_process); | 255 | AddGlueRegistrationForProcess(*app_loader, *main_process); |
| 250 | kernel.MakeCurrentProcess(main_process); | 256 | kernel.MakeCurrentProcess(main_process.get()); |
| 251 | kernel.InitializeCores(); | 257 | kernel.InitializeCores(); |
| 252 | 258 | ||
| 253 | // Initialize cheat engine | 259 | // Initialize cheat engine |
| @@ -316,6 +322,8 @@ struct System::Impl { | |||
| 316 | kernel.Shutdown(); | 322 | kernel.Shutdown(); |
| 317 | memory.Reset(); | 323 | memory.Reset(); |
| 318 | applet_manager.ClearAll(); | 324 | applet_manager.ClearAll(); |
| 325 | // TODO: The main process should be freed based on KAutoObject ref counting. | ||
| 326 | main_process.reset(); | ||
| 319 | 327 | ||
| 320 | LOG_DEBUG(Core, "Shutdown OK"); | 328 | LOG_DEBUG(Core, "Shutdown OK"); |
| 321 | } | 329 | } |
| @@ -374,6 +382,7 @@ struct System::Impl { | |||
| 374 | std::unique_ptr<Tegra::GPU> gpu_core; | 382 | std::unique_ptr<Tegra::GPU> gpu_core; |
| 375 | std::unique_ptr<Hardware::InterruptManager> interrupt_manager; | 383 | std::unique_ptr<Hardware::InterruptManager> interrupt_manager; |
| 376 | std::unique_ptr<Core::DeviceMemory> device_memory; | 384 | std::unique_ptr<Core::DeviceMemory> device_memory; |
| 385 | KProcessPtr main_process{nullptr, KProcessDeleter}; | ||
| 377 | Core::Memory::Memory memory; | 386 | Core::Memory::Memory memory; |
| 378 | CpuManager cpu_manager; | 387 | CpuManager cpu_manager; |
| 379 | std::atomic_bool is_powered_on{}; | 388 | std::atomic_bool is_powered_on{}; |
diff --git a/src/core/file_sys/kernel_executable.h b/src/core/file_sys/kernel_executable.h index 044c554d3..79ca82f8b 100644 --- a/src/core/file_sys/kernel_executable.h +++ b/src/core/file_sys/kernel_executable.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <string> | ||
| 8 | #include <vector> | 9 | #include <vector> |
| 9 | 10 | ||
| 10 | #include "common/common_funcs.h" | 11 | #include "common/common_funcs.h" |
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp index 368419eca..f5ad10b15 100644 --- a/src/core/file_sys/vfs.cpp +++ b/src/core/file_sys/vfs.cpp | |||
| @@ -273,6 +273,10 @@ VirtualFile VfsDirectory::GetFile(std::string_view name) const { | |||
| 273 | return iter == files.end() ? nullptr : *iter; | 273 | return iter == files.end() ? nullptr : *iter; |
| 274 | } | 274 | } |
| 275 | 275 | ||
| 276 | FileTimeStampRaw VfsDirectory::GetFileTimeStamp([[maybe_unused]] std::string_view path) const { | ||
| 277 | return {}; | ||
| 278 | } | ||
| 279 | |||
| 276 | VirtualDir VfsDirectory::GetSubdirectory(std::string_view name) const { | 280 | VirtualDir VfsDirectory::GetSubdirectory(std::string_view name) const { |
| 277 | const auto& subs = GetSubdirectories(); | 281 | const auto& subs = GetSubdirectories(); |
| 278 | const auto iter = std::find_if(subs.begin(), subs.end(), | 282 | const auto iter = std::find_if(subs.begin(), subs.end(), |
diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h index afd64e95c..ff6935da6 100644 --- a/src/core/file_sys/vfs.h +++ b/src/core/file_sys/vfs.h | |||
| @@ -199,6 +199,9 @@ public: | |||
| 199 | // file with name. | 199 | // file with name. |
| 200 | virtual VirtualFile GetFile(std::string_view name) const; | 200 | virtual VirtualFile GetFile(std::string_view name) const; |
| 201 | 201 | ||
| 202 | // Returns a struct containing the file's timestamp. | ||
| 203 | virtual FileTimeStampRaw GetFileTimeStamp(std::string_view path) const; | ||
| 204 | |||
| 202 | // Returns a vector containing all of the subdirectories in this directory. | 205 | // Returns a vector containing all of the subdirectories in this directory. |
| 203 | virtual std::vector<VirtualDir> GetSubdirectories() const = 0; | 206 | virtual std::vector<VirtualDir> GetSubdirectories() const = 0; |
| 204 | // Returns the directory with name matching name. Returns nullptr if directory dosen't have a | 207 | // Returns the directory with name matching name. Returns nullptr if directory dosen't have a |
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp index 3dad54f49..f4073b76a 100644 --- a/src/core/file_sys/vfs_real.cpp +++ b/src/core/file_sys/vfs_real.cpp | |||
| @@ -13,6 +13,13 @@ | |||
| 13 | #include "common/logging/log.h" | 13 | #include "common/logging/log.h" |
| 14 | #include "core/file_sys/vfs_real.h" | 14 | #include "core/file_sys/vfs_real.h" |
| 15 | 15 | ||
| 16 | // For FileTimeStampRaw | ||
| 17 | #include <sys/stat.h> | ||
| 18 | |||
| 19 | #ifdef _MSC_VER | ||
| 20 | #define stat _stat64 | ||
| 21 | #endif | ||
| 22 | |||
| 16 | namespace FileSys { | 23 | namespace FileSys { |
| 17 | 24 | ||
| 18 | namespace FS = Common::FS; | 25 | namespace FS = Common::FS; |
| @@ -392,6 +399,28 @@ std::vector<VirtualFile> RealVfsDirectory::GetFiles() const { | |||
| 392 | return IterateEntries<RealVfsFile, VfsFile>(); | 399 | return IterateEntries<RealVfsFile, VfsFile>(); |
| 393 | } | 400 | } |
| 394 | 401 | ||
| 402 | FileTimeStampRaw RealVfsDirectory::GetFileTimeStamp(std::string_view path_) const { | ||
| 403 | const auto full_path = FS::SanitizePath(path + '/' + std::string(path_)); | ||
| 404 | const auto fs_path = std::filesystem::path{FS::ToU8String(full_path)}; | ||
| 405 | struct stat file_status; | ||
| 406 | |||
| 407 | #ifdef _WIN32 | ||
| 408 | const auto stat_result = _wstat64(fs_path.c_str(), &file_status); | ||
| 409 | #else | ||
| 410 | const auto stat_result = stat(fs_path.c_str(), &file_status); | ||
| 411 | #endif | ||
| 412 | |||
| 413 | if (stat_result != 0) { | ||
| 414 | return {}; | ||
| 415 | } | ||
| 416 | |||
| 417 | return { | ||
| 418 | .created{static_cast<u64>(file_status.st_ctime)}, | ||
| 419 | .accessed{static_cast<u64>(file_status.st_atime)}, | ||
| 420 | .modified{static_cast<u64>(file_status.st_mtime)}, | ||
| 421 | }; | ||
| 422 | } | ||
| 423 | |||
| 395 | std::vector<VirtualDir> RealVfsDirectory::GetSubdirectories() const { | 424 | std::vector<VirtualDir> RealVfsDirectory::GetSubdirectories() const { |
| 396 | return IterateEntries<RealVfsDirectory, VfsDirectory>(); | 425 | return IterateEntries<RealVfsDirectory, VfsDirectory>(); |
| 397 | } | 426 | } |
diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h index e4d1bba79..746e624cb 100644 --- a/src/core/file_sys/vfs_real.h +++ b/src/core/file_sys/vfs_real.h | |||
| @@ -86,6 +86,7 @@ public: | |||
| 86 | VirtualDir CreateDirectoryRelative(std::string_view relative_path) override; | 86 | VirtualDir CreateDirectoryRelative(std::string_view relative_path) override; |
| 87 | bool DeleteSubdirectoryRecursive(std::string_view name) override; | 87 | bool DeleteSubdirectoryRecursive(std::string_view name) override; |
| 88 | std::vector<VirtualFile> GetFiles() const override; | 88 | std::vector<VirtualFile> GetFiles() const override; |
| 89 | FileTimeStampRaw GetFileTimeStamp(std::string_view path) const override; | ||
| 89 | std::vector<VirtualDir> GetSubdirectories() const override; | 90 | std::vector<VirtualDir> GetSubdirectories() const override; |
| 90 | bool IsWritable() const override; | 91 | bool IsWritable() const override; |
| 91 | bool IsReadable() const override; | 92 | bool IsReadable() const override; |
diff --git a/src/core/file_sys/vfs_types.h b/src/core/file_sys/vfs_types.h index 6215ed7af..ed0724717 100644 --- a/src/core/file_sys/vfs_types.h +++ b/src/core/file_sys/vfs_types.h | |||
| @@ -6,6 +6,8 @@ | |||
| 6 | 6 | ||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | 8 | ||
| 9 | #include "common/common_types.h" | ||
| 10 | |||
| 9 | namespace FileSys { | 11 | namespace FileSys { |
| 10 | 12 | ||
| 11 | class VfsDirectory; | 13 | class VfsDirectory; |
| @@ -18,4 +20,11 @@ using VirtualDir = std::shared_ptr<VfsDirectory>; | |||
| 18 | using VirtualFile = std::shared_ptr<VfsFile>; | 20 | using VirtualFile = std::shared_ptr<VfsFile>; |
| 19 | using VirtualFilesystem = std::shared_ptr<VfsFilesystem>; | 21 | using VirtualFilesystem = std::shared_ptr<VfsFilesystem>; |
| 20 | 22 | ||
| 23 | struct FileTimeStampRaw { | ||
| 24 | u64 created{}; | ||
| 25 | u64 accessed{}; | ||
| 26 | u64 modified{}; | ||
| 27 | u64 padding{}; | ||
| 28 | }; | ||
| 29 | |||
| 21 | } // namespace FileSys | 30 | } // namespace FileSys |
diff --git a/src/core/hle/api_version.h b/src/core/hle/api_version.h index 43d5670a9..626e30753 100644 --- a/src/core/hle/api_version.h +++ b/src/core/hle/api_version.h | |||
| @@ -28,13 +28,20 @@ constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 12.1.0-1.0"; | |||
| 28 | 28 | ||
| 29 | // Atmosphere version constants. | 29 | // Atmosphere version constants. |
| 30 | 30 | ||
| 31 | constexpr u8 ATMOSPHERE_RELEASE_VERSION_MAJOR = 0; | 31 | constexpr u8 ATMOSPHERE_RELEASE_VERSION_MAJOR = 1; |
| 32 | constexpr u8 ATMOSPHERE_RELEASE_VERSION_MINOR = 19; | 32 | constexpr u8 ATMOSPHERE_RELEASE_VERSION_MINOR = 0; |
| 33 | constexpr u8 ATMOSPHERE_RELEASE_VERSION_MICRO = 5; | 33 | constexpr u8 ATMOSPHERE_RELEASE_VERSION_MICRO = 0; |
| 34 | |||
| 35 | constexpr u32 AtmosphereTargetFirmwareWithRevision(u8 major, u8 minor, u8 micro, u8 rev) { | ||
| 36 | return u32{major} << 24 | u32{minor} << 16 | u32{micro} << 8 | u32{rev}; | ||
| 37 | } | ||
| 38 | |||
| 39 | constexpr u32 AtmosphereTargetFirmware(u8 major, u8 minor, u8 micro) { | ||
| 40 | return AtmosphereTargetFirmwareWithRevision(major, minor, micro, 0); | ||
| 41 | } | ||
| 34 | 42 | ||
| 35 | constexpr u32 GetTargetFirmware() { | 43 | constexpr u32 GetTargetFirmware() { |
| 36 | return u32{HOS_VERSION_MAJOR} << 24 | u32{HOS_VERSION_MINOR} << 16 | | 44 | return AtmosphereTargetFirmware(HOS_VERSION_MAJOR, HOS_VERSION_MINOR, HOS_VERSION_MICRO); |
| 37 | u32{HOS_VERSION_MICRO} << 8 | 0U; | ||
| 38 | } | 45 | } |
| 39 | 46 | ||
| 40 | } // namespace HLE::ApiVersion | 47 | } // namespace HLE::ApiVersion |
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 3a6db0b1c..901d43da9 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <functional> | ||
| 8 | #include <memory> | 9 | #include <memory> |
| 9 | #include <string> | 10 | #include <string> |
| 10 | #include <unordered_map> | 11 | #include <unordered_map> |
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index 882fc1492..6d9ec0a8a 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp | |||
| @@ -23,6 +23,7 @@ | |||
| 23 | #include "core/hle/service/acc/acc_su.h" | 23 | #include "core/hle/service/acc/acc_su.h" |
| 24 | #include "core/hle/service/acc/acc_u0.h" | 24 | #include "core/hle/service/acc/acc_u0.h" |
| 25 | #include "core/hle/service/acc/acc_u1.h" | 25 | #include "core/hle/service/acc/acc_u1.h" |
| 26 | #include "core/hle/service/acc/async_context.h" | ||
| 26 | #include "core/hle/service/acc/errors.h" | 27 | #include "core/hle/service/acc/errors.h" |
| 27 | #include "core/hle/service/acc/profile_manager.h" | 28 | #include "core/hle/service/acc/profile_manager.h" |
| 28 | #include "core/hle/service/glue/arp.h" | 29 | #include "core/hle/service/glue/arp.h" |
| @@ -454,22 +455,6 @@ public: | |||
| 454 | : IProfileCommon{system_, "IProfileEditor", true, user_id_, profile_manager_} {} | 455 | : IProfileCommon{system_, "IProfileEditor", true, user_id_, profile_manager_} {} |
| 455 | }; | 456 | }; |
| 456 | 457 | ||
| 457 | class IAsyncContext final : public ServiceFramework<IAsyncContext> { | ||
| 458 | public: | ||
| 459 | explicit IAsyncContext(Core::System& system_) : ServiceFramework{system_, "IAsyncContext"} { | ||
| 460 | // clang-format off | ||
| 461 | static const FunctionInfo functions[] = { | ||
| 462 | {0, nullptr, "GetSystemEvent"}, | ||
| 463 | {1, nullptr, "Cancel"}, | ||
| 464 | {2, nullptr, "HasDone"}, | ||
| 465 | {3, nullptr, "GetResult"}, | ||
| 466 | }; | ||
| 467 | // clang-format on | ||
| 468 | |||
| 469 | RegisterHandlers(functions); | ||
| 470 | } | ||
| 471 | }; | ||
| 472 | |||
| 473 | class ISessionObject final : public ServiceFramework<ISessionObject> { | 458 | class ISessionObject final : public ServiceFramework<ISessionObject> { |
| 474 | public: | 459 | public: |
| 475 | explicit ISessionObject(Core::System& system_, Common::UUID) | 460 | explicit ISessionObject(Core::System& system_, Common::UUID) |
| @@ -504,16 +489,44 @@ public: | |||
| 504 | } | 489 | } |
| 505 | }; | 490 | }; |
| 506 | 491 | ||
| 492 | class EnsureTokenIdCacheAsyncInterface final : public IAsyncContext { | ||
| 493 | public: | ||
| 494 | explicit EnsureTokenIdCacheAsyncInterface(Core::System& system_) : IAsyncContext{system_} { | ||
| 495 | MarkComplete(); | ||
| 496 | } | ||
| 497 | ~EnsureTokenIdCacheAsyncInterface() = default; | ||
| 498 | |||
| 499 | void LoadIdTokenCache(Kernel::HLERequestContext& ctx) { | ||
| 500 | LOG_WARNING(Service_ACC, "(STUBBED) called"); | ||
| 501 | |||
| 502 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 503 | rb.Push(ResultSuccess); | ||
| 504 | } | ||
| 505 | |||
| 506 | protected: | ||
| 507 | bool IsComplete() const override { | ||
| 508 | return true; | ||
| 509 | } | ||
| 510 | |||
| 511 | void Cancel() override {} | ||
| 512 | |||
| 513 | ResultCode GetResult() const override { | ||
| 514 | return ResultSuccess; | ||
| 515 | } | ||
| 516 | }; | ||
| 517 | |||
| 507 | class IManagerForApplication final : public ServiceFramework<IManagerForApplication> { | 518 | class IManagerForApplication final : public ServiceFramework<IManagerForApplication> { |
| 508 | public: | 519 | public: |
| 509 | explicit IManagerForApplication(Core::System& system_, Common::UUID user_id_) | 520 | explicit IManagerForApplication(Core::System& system_, Common::UUID user_id_) |
| 510 | : ServiceFramework{system_, "IManagerForApplication"}, user_id{user_id_} { | 521 | : ServiceFramework{system_, "IManagerForApplication"}, |
| 522 | ensure_token_id{std::make_shared<EnsureTokenIdCacheAsyncInterface>(system)}, | ||
| 523 | user_id{user_id_} { | ||
| 511 | // clang-format off | 524 | // clang-format off |
| 512 | static const FunctionInfo functions[] = { | 525 | static const FunctionInfo functions[] = { |
| 513 | {0, &IManagerForApplication::CheckAvailability, "CheckAvailability"}, | 526 | {0, &IManagerForApplication::CheckAvailability, "CheckAvailability"}, |
| 514 | {1, &IManagerForApplication::GetAccountId, "GetAccountId"}, | 527 | {1, &IManagerForApplication::GetAccountId, "GetAccountId"}, |
| 515 | {2, nullptr, "EnsureIdTokenCacheAsync"}, | 528 | {2, &IManagerForApplication::EnsureIdTokenCacheAsync, "EnsureIdTokenCacheAsync"}, |
| 516 | {3, nullptr, "LoadIdTokenCache"}, | 529 | {3, &IManagerForApplication::LoadIdTokenCache, "LoadIdTokenCache"}, |
| 517 | {130, &IManagerForApplication::GetNintendoAccountUserResourceCacheForApplication, "GetNintendoAccountUserResourceCacheForApplication"}, | 530 | {130, &IManagerForApplication::GetNintendoAccountUserResourceCacheForApplication, "GetNintendoAccountUserResourceCacheForApplication"}, |
| 518 | {150, nullptr, "CreateAuthorizationRequest"}, | 531 | {150, nullptr, "CreateAuthorizationRequest"}, |
| 519 | {160, &IManagerForApplication::StoreOpenContext, "StoreOpenContext"}, | 532 | {160, &IManagerForApplication::StoreOpenContext, "StoreOpenContext"}, |
| @@ -540,6 +553,20 @@ private: | |||
| 540 | rb.PushRaw<u64>(user_id.GetNintendoID()); | 553 | rb.PushRaw<u64>(user_id.GetNintendoID()); |
| 541 | } | 554 | } |
| 542 | 555 | ||
| 556 | void EnsureIdTokenCacheAsync(Kernel::HLERequestContext& ctx) { | ||
| 557 | LOG_WARNING(Service_ACC, "(STUBBED) called"); | ||
| 558 | |||
| 559 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 560 | rb.Push(ResultSuccess); | ||
| 561 | rb.PushIpcInterface(ensure_token_id); | ||
| 562 | } | ||
| 563 | |||
| 564 | void LoadIdTokenCache(Kernel::HLERequestContext& ctx) { | ||
| 565 | LOG_WARNING(Service_ACC, "(STUBBED) called"); | ||
| 566 | |||
| 567 | ensure_token_id->LoadIdTokenCache(ctx); | ||
| 568 | } | ||
| 569 | |||
| 543 | void GetNintendoAccountUserResourceCacheForApplication(Kernel::HLERequestContext& ctx) { | 570 | void GetNintendoAccountUserResourceCacheForApplication(Kernel::HLERequestContext& ctx) { |
| 544 | LOG_WARNING(Service_ACC, "(STUBBED) called"); | 571 | LOG_WARNING(Service_ACC, "(STUBBED) called"); |
| 545 | 572 | ||
| @@ -562,6 +589,7 @@ private: | |||
| 562 | rb.Push(ResultSuccess); | 589 | rb.Push(ResultSuccess); |
| 563 | } | 590 | } |
| 564 | 591 | ||
| 592 | std::shared_ptr<EnsureTokenIdCacheAsyncInterface> ensure_token_id{}; | ||
| 565 | Common::UUID user_id{Common::INVALID_UUID}; | 593 | Common::UUID user_id{Common::INVALID_UUID}; |
| 566 | }; | 594 | }; |
| 567 | 595 | ||
diff --git a/src/core/hle/service/acc/async_context.cpp b/src/core/hle/service/acc/async_context.cpp new file mode 100644 index 000000000..459323132 --- /dev/null +++ b/src/core/hle/service/acc/async_context.cpp | |||
| @@ -0,0 +1,68 @@ | |||
| 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 "core/core.h" | ||
| 6 | #include "core/hle/ipc_helpers.h" | ||
| 7 | #include "core/hle/service/acc/async_context.h" | ||
| 8 | |||
| 9 | namespace Service::Account { | ||
| 10 | IAsyncContext::IAsyncContext(Core::System& system_) | ||
| 11 | : ServiceFramework{system_, "IAsyncContext"}, compeletion_event{system_.Kernel()} { | ||
| 12 | |||
| 13 | Kernel::KAutoObject::Create(std::addressof(compeletion_event)); | ||
| 14 | compeletion_event.Initialize("IAsyncContext:CompletionEvent"); | ||
| 15 | |||
| 16 | // clang-format off | ||
| 17 | static const FunctionInfo functions[] = { | ||
| 18 | {0, &IAsyncContext::GetSystemEvent, "GetSystemEvent"}, | ||
| 19 | {1, &IAsyncContext::Cancel, "Cancel"}, | ||
| 20 | {2, &IAsyncContext::HasDone, "HasDone"}, | ||
| 21 | {3, &IAsyncContext::GetResult, "GetResult"}, | ||
| 22 | }; | ||
| 23 | // clang-format on | ||
| 24 | |||
| 25 | RegisterHandlers(functions); | ||
| 26 | } | ||
| 27 | |||
| 28 | void IAsyncContext::GetSystemEvent(Kernel::HLERequestContext& ctx) { | ||
| 29 | LOG_DEBUG(Service_ACC, "called"); | ||
| 30 | |||
| 31 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 32 | rb.Push(ResultSuccess); | ||
| 33 | rb.PushCopyObjects(compeletion_event.GetReadableEvent()); | ||
| 34 | } | ||
| 35 | |||
| 36 | void IAsyncContext::Cancel(Kernel::HLERequestContext& ctx) { | ||
| 37 | LOG_DEBUG(Service_ACC, "called"); | ||
| 38 | |||
| 39 | Cancel(); | ||
| 40 | MarkComplete(); | ||
| 41 | |||
| 42 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 43 | rb.Push(ResultSuccess); | ||
| 44 | } | ||
| 45 | |||
| 46 | void IAsyncContext::HasDone(Kernel::HLERequestContext& ctx) { | ||
| 47 | LOG_DEBUG(Service_ACC, "called"); | ||
| 48 | |||
| 49 | is_complete.store(IsComplete()); | ||
| 50 | |||
| 51 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 52 | rb.Push(ResultSuccess); | ||
| 53 | rb.Push(is_complete.load()); | ||
| 54 | } | ||
| 55 | |||
| 56 | void IAsyncContext::GetResult(Kernel::HLERequestContext& ctx) { | ||
| 57 | LOG_DEBUG(Service_ACC, "called"); | ||
| 58 | |||
| 59 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 60 | rb.Push(GetResult()); | ||
| 61 | } | ||
| 62 | |||
| 63 | void IAsyncContext::MarkComplete() { | ||
| 64 | is_complete.store(true); | ||
| 65 | compeletion_event.GetWritableEvent().Signal(); | ||
| 66 | } | ||
| 67 | |||
| 68 | } // namespace Service::Account | ||
diff --git a/src/core/hle/service/acc/async_context.h b/src/core/hle/service/acc/async_context.h new file mode 100644 index 000000000..c694b4946 --- /dev/null +++ b/src/core/hle/service/acc/async_context.h | |||
| @@ -0,0 +1,37 @@ | |||
| 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 <atomic> | ||
| 8 | #include "core/hle/kernel/k_event.h" | ||
| 9 | #include "core/hle/service/service.h" | ||
| 10 | |||
| 11 | namespace Core { | ||
| 12 | class System; | ||
| 13 | } | ||
| 14 | |||
| 15 | namespace Service::Account { | ||
| 16 | |||
| 17 | class IAsyncContext : public ServiceFramework<IAsyncContext> { | ||
| 18 | public: | ||
| 19 | explicit IAsyncContext(Core::System& system_); | ||
| 20 | |||
| 21 | void GetSystemEvent(Kernel::HLERequestContext& ctx); | ||
| 22 | void Cancel(Kernel::HLERequestContext& ctx); | ||
| 23 | void HasDone(Kernel::HLERequestContext& ctx); | ||
| 24 | void GetResult(Kernel::HLERequestContext& ctx); | ||
| 25 | |||
| 26 | protected: | ||
| 27 | virtual bool IsComplete() const = 0; | ||
| 28 | virtual void Cancel() = 0; | ||
| 29 | virtual ResultCode GetResult() const = 0; | ||
| 30 | |||
| 31 | void MarkComplete(); | ||
| 32 | |||
| 33 | std::atomic<bool> is_complete{false}; | ||
| 34 | Kernel::KEvent compeletion_event; | ||
| 35 | }; | ||
| 36 | |||
| 37 | } // namespace Service::Account | ||
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index a538f82e3..c3ac73131 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -1270,7 +1270,8 @@ void ILibraryAppletCreator::CreateHandleStorage(Kernel::HLERequestContext& ctx) | |||
| 1270 | IApplicationFunctions::IApplicationFunctions(Core::System& system_) | 1270 | IApplicationFunctions::IApplicationFunctions(Core::System& system_) |
| 1271 | : ServiceFramework{system_, "IApplicationFunctions"}, gpu_error_detected_event{system.Kernel()}, | 1271 | : ServiceFramework{system_, "IApplicationFunctions"}, gpu_error_detected_event{system.Kernel()}, |
| 1272 | friend_invitation_storage_channel_event{system.Kernel()}, | 1272 | friend_invitation_storage_channel_event{system.Kernel()}, |
| 1273 | health_warning_disappeared_system_event{system.Kernel()} { | 1273 | notification_storage_channel_event{system.Kernel()}, health_warning_disappeared_system_event{ |
| 1274 | system.Kernel()} { | ||
| 1274 | // clang-format off | 1275 | // clang-format off |
| 1275 | static const FunctionInfo functions[] = { | 1276 | static const FunctionInfo functions[] = { |
| 1276 | {1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"}, | 1277 | {1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"}, |
| @@ -1322,7 +1323,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_) | |||
| 1322 | {131, nullptr, "SetDelayTimeToAbortOnGpuError"}, | 1323 | {131, nullptr, "SetDelayTimeToAbortOnGpuError"}, |
| 1323 | {140, &IApplicationFunctions::GetFriendInvitationStorageChannelEvent, "GetFriendInvitationStorageChannelEvent"}, | 1324 | {140, &IApplicationFunctions::GetFriendInvitationStorageChannelEvent, "GetFriendInvitationStorageChannelEvent"}, |
| 1324 | {141, &IApplicationFunctions::TryPopFromFriendInvitationStorageChannel, "TryPopFromFriendInvitationStorageChannel"}, | 1325 | {141, &IApplicationFunctions::TryPopFromFriendInvitationStorageChannel, "TryPopFromFriendInvitationStorageChannel"}, |
| 1325 | {150, nullptr, "GetNotificationStorageChannelEvent"}, | 1326 | {150, &IApplicationFunctions::GetNotificationStorageChannelEvent, "GetNotificationStorageChannelEvent"}, |
| 1326 | {151, nullptr, "TryPopFromNotificationStorageChannel"}, | 1327 | {151, nullptr, "TryPopFromNotificationStorageChannel"}, |
| 1327 | {160, &IApplicationFunctions::GetHealthWarningDisappearedSystemEvent, "GetHealthWarningDisappearedSystemEvent"}, | 1328 | {160, &IApplicationFunctions::GetHealthWarningDisappearedSystemEvent, "GetHealthWarningDisappearedSystemEvent"}, |
| 1328 | {170, nullptr, "SetHdcpAuthenticationActivated"}, | 1329 | {170, nullptr, "SetHdcpAuthenticationActivated"}, |
| @@ -1340,11 +1341,14 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_) | |||
| 1340 | 1341 | ||
| 1341 | Kernel::KAutoObject::Create(std::addressof(gpu_error_detected_event)); | 1342 | Kernel::KAutoObject::Create(std::addressof(gpu_error_detected_event)); |
| 1342 | Kernel::KAutoObject::Create(std::addressof(friend_invitation_storage_channel_event)); | 1343 | Kernel::KAutoObject::Create(std::addressof(friend_invitation_storage_channel_event)); |
| 1344 | Kernel::KAutoObject::Create(std::addressof(notification_storage_channel_event)); | ||
| 1343 | Kernel::KAutoObject::Create(std::addressof(health_warning_disappeared_system_event)); | 1345 | Kernel::KAutoObject::Create(std::addressof(health_warning_disappeared_system_event)); |
| 1344 | 1346 | ||
| 1345 | gpu_error_detected_event.Initialize("IApplicationFunctions:GpuErrorDetectedSystemEvent"); | 1347 | gpu_error_detected_event.Initialize("IApplicationFunctions:GpuErrorDetectedSystemEvent"); |
| 1346 | friend_invitation_storage_channel_event.Initialize( | 1348 | friend_invitation_storage_channel_event.Initialize( |
| 1347 | "IApplicationFunctions:FriendInvitationStorageChannelEvent"); | 1349 | "IApplicationFunctions:FriendInvitationStorageChannelEvent"); |
| 1350 | notification_storage_channel_event.Initialize( | ||
| 1351 | "IApplicationFunctions:NotificationStorageChannelEvent"); | ||
| 1348 | health_warning_disappeared_system_event.Initialize( | 1352 | health_warning_disappeared_system_event.Initialize( |
| 1349 | "IApplicationFunctions:HealthWarningDisappearedSystemEvent"); | 1353 | "IApplicationFunctions:HealthWarningDisappearedSystemEvent"); |
| 1350 | } | 1354 | } |
| @@ -1762,6 +1766,14 @@ void IApplicationFunctions::TryPopFromFriendInvitationStorageChannel( | |||
| 1762 | rb.Push(ERR_NO_DATA_IN_CHANNEL); | 1766 | rb.Push(ERR_NO_DATA_IN_CHANNEL); |
| 1763 | } | 1767 | } |
| 1764 | 1768 | ||
| 1769 | void IApplicationFunctions::GetNotificationStorageChannelEvent(Kernel::HLERequestContext& ctx) { | ||
| 1770 | LOG_DEBUG(Service_AM, "called"); | ||
| 1771 | |||
| 1772 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 1773 | rb.Push(ResultSuccess); | ||
| 1774 | rb.PushCopyObjects(notification_storage_channel_event.GetReadableEvent()); | ||
| 1775 | } | ||
| 1776 | |||
| 1765 | void IApplicationFunctions::GetHealthWarningDisappearedSystemEvent(Kernel::HLERequestContext& ctx) { | 1777 | void IApplicationFunctions::GetHealthWarningDisappearedSystemEvent(Kernel::HLERequestContext& ctx) { |
| 1766 | LOG_DEBUG(Service_AM, "called"); | 1778 | LOG_DEBUG(Service_AM, "called"); |
| 1767 | 1779 | ||
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 184030a8e..c13aa5787 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h | |||
| @@ -295,6 +295,7 @@ private: | |||
| 295 | void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx); | 295 | void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx); |
| 296 | void GetFriendInvitationStorageChannelEvent(Kernel::HLERequestContext& ctx); | 296 | void GetFriendInvitationStorageChannelEvent(Kernel::HLERequestContext& ctx); |
| 297 | void TryPopFromFriendInvitationStorageChannel(Kernel::HLERequestContext& ctx); | 297 | void TryPopFromFriendInvitationStorageChannel(Kernel::HLERequestContext& ctx); |
| 298 | void GetNotificationStorageChannelEvent(Kernel::HLERequestContext& ctx); | ||
| 298 | void GetHealthWarningDisappearedSystemEvent(Kernel::HLERequestContext& ctx); | 299 | void GetHealthWarningDisappearedSystemEvent(Kernel::HLERequestContext& ctx); |
| 299 | 300 | ||
| 300 | bool launch_popped_application_specific = false; | 301 | bool launch_popped_application_specific = false; |
| @@ -302,6 +303,7 @@ private: | |||
| 302 | s32 previous_program_index{-1}; | 303 | s32 previous_program_index{-1}; |
| 303 | Kernel::KEvent gpu_error_detected_event; | 304 | Kernel::KEvent gpu_error_detected_event; |
| 304 | Kernel::KEvent friend_invitation_storage_channel_event; | 305 | Kernel::KEvent friend_invitation_storage_channel_event; |
| 306 | Kernel::KEvent notification_storage_channel_event; | ||
| 305 | Kernel::KEvent health_warning_disappeared_system_event; | 307 | Kernel::KEvent health_warning_disappeared_system_event; |
| 306 | }; | 308 | }; |
| 307 | 309 | ||
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index 4a9b13e45..f8f9e32f7 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp | |||
| @@ -97,14 +97,24 @@ ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) cons | |||
| 97 | 97 | ||
| 98 | ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const { | 98 | ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const { |
| 99 | std::string path(Common::FS::SanitizePath(path_)); | 99 | std::string path(Common::FS::SanitizePath(path_)); |
| 100 | auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path)); | 100 | |
| 101 | if (dir == nullptr || Common::FS::GetFilename(Common::FS::GetParentPath(path)).empty()) { | 101 | // NOTE: This is inaccurate behavior. CreateDirectory is not recursive. |
| 102 | dir = backing; | 102 | // CreateDirectory should return PathNotFound if the parent directory does not exist. |
| 103 | } | 103 | // This is here temporarily in order to have UMM "work" in the meantime. |
| 104 | auto new_dir = dir->CreateSubdirectory(Common::FS::GetFilename(path)); | 104 | // TODO (Morph): Remove this when a hardware test verifies the correct behavior. |
| 105 | if (new_dir == nullptr) { | 105 | const auto components = Common::FS::SplitPathComponents(path); |
| 106 | // TODO(DarkLordZach): Find a better error code for this | 106 | std::string relative_path; |
| 107 | return ResultUnknown; | 107 | for (const auto& component : components) { |
| 108 | // Skip empty path components | ||
| 109 | if (component.empty()) { | ||
| 110 | continue; | ||
| 111 | } | ||
| 112 | relative_path = Common::FS::SanitizePath(relative_path + '/' + component); | ||
| 113 | auto new_dir = backing->CreateSubdirectory(relative_path); | ||
| 114 | if (new_dir == nullptr) { | ||
| 115 | // TODO(DarkLordZach): Find a better error code for this | ||
| 116 | return ResultUnknown; | ||
| 117 | } | ||
| 108 | } | 118 | } |
| 109 | return ResultSuccess; | 119 | return ResultSuccess; |
| 110 | } | 120 | } |
| @@ -251,6 +261,18 @@ ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType( | |||
| 251 | return FileSys::ERROR_PATH_NOT_FOUND; | 261 | return FileSys::ERROR_PATH_NOT_FOUND; |
| 252 | } | 262 | } |
| 253 | 263 | ||
| 264 | ResultVal<FileSys::FileTimeStampRaw> VfsDirectoryServiceWrapper::GetFileTimeStampRaw( | ||
| 265 | const std::string& path) const { | ||
| 266 | auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path)); | ||
| 267 | if (dir == nullptr) { | ||
| 268 | return FileSys::ERROR_PATH_NOT_FOUND; | ||
| 269 | } | ||
| 270 | if (GetEntryType(path).Failed()) { | ||
| 271 | return FileSys::ERROR_PATH_NOT_FOUND; | ||
| 272 | } | ||
| 273 | return MakeResult(dir->GetFileTimeStamp(Common::FS::GetFilename(path))); | ||
| 274 | } | ||
| 275 | |||
| 254 | FileSystemController::FileSystemController(Core::System& system_) : system{system_} {} | 276 | FileSystemController::FileSystemController(Core::System& system_) : system{system_} {} |
| 255 | 277 | ||
| 256 | FileSystemController::~FileSystemController() = default; | 278 | FileSystemController::~FileSystemController() = default; |
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index d387af3cb..b155e0811 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h | |||
| @@ -240,6 +240,12 @@ public: | |||
| 240 | */ | 240 | */ |
| 241 | ResultVal<FileSys::EntryType> GetEntryType(const std::string& path) const; | 241 | ResultVal<FileSys::EntryType> GetEntryType(const std::string& path) const; |
| 242 | 242 | ||
| 243 | /** | ||
| 244 | * Get the timestamp of the specified path | ||
| 245 | * @return The timestamp of the specified path or error code | ||
| 246 | */ | ||
| 247 | ResultVal<FileSys::FileTimeStampRaw> GetFileTimeStampRaw(const std::string& path) const; | ||
| 248 | |||
| 243 | private: | 249 | private: |
| 244 | FileSys::VirtualDir backing; | 250 | FileSys::VirtualDir backing; |
| 245 | }; | 251 | }; |
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index db4d44c12..50c788dd6 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp | |||
| @@ -326,7 +326,7 @@ public: | |||
| 326 | {11, &IFileSystem::GetFreeSpaceSize, "GetFreeSpaceSize"}, | 326 | {11, &IFileSystem::GetFreeSpaceSize, "GetFreeSpaceSize"}, |
| 327 | {12, &IFileSystem::GetTotalSpaceSize, "GetTotalSpaceSize"}, | 327 | {12, &IFileSystem::GetTotalSpaceSize, "GetTotalSpaceSize"}, |
| 328 | {13, &IFileSystem::CleanDirectoryRecursively, "CleanDirectoryRecursively"}, | 328 | {13, &IFileSystem::CleanDirectoryRecursively, "CleanDirectoryRecursively"}, |
| 329 | {14, nullptr, "GetFileTimeStampRaw"}, | 329 | {14, &IFileSystem::GetFileTimeStampRaw, "GetFileTimeStampRaw"}, |
| 330 | {15, nullptr, "QueryEntry"}, | 330 | {15, nullptr, "QueryEntry"}, |
| 331 | }; | 331 | }; |
| 332 | RegisterHandlers(functions); | 332 | RegisterHandlers(functions); |
| @@ -501,6 +501,24 @@ public: | |||
| 501 | rb.Push(size.get_total_size()); | 501 | rb.Push(size.get_total_size()); |
| 502 | } | 502 | } |
| 503 | 503 | ||
| 504 | void GetFileTimeStampRaw(Kernel::HLERequestContext& ctx) { | ||
| 505 | const auto file_buffer = ctx.ReadBuffer(); | ||
| 506 | const std::string name = Common::StringFromBuffer(file_buffer); | ||
| 507 | |||
| 508 | LOG_WARNING(Service_FS, "(Partial Implementation) called. file={}", name); | ||
| 509 | |||
| 510 | auto result = backend.GetFileTimeStampRaw(name); | ||
| 511 | if (result.Failed()) { | ||
| 512 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 513 | rb.Push(result.Code()); | ||
| 514 | return; | ||
| 515 | } | ||
| 516 | |||
| 517 | IPC::ResponseBuilder rb{ctx, 10}; | ||
| 518 | rb.Push(ResultSuccess); | ||
| 519 | rb.PushRaw(*result); | ||
| 520 | } | ||
| 521 | |||
| 504 | private: | 522 | private: |
| 505 | VfsDirectoryServiceWrapper backend; | 523 | VfsDirectoryServiceWrapper backend; |
| 506 | SizeGetter size; | 524 | SizeGetter size; |
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h index ef2becefd..8e9b40c0a 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.h +++ b/src/core/hle/service/hid/controllers/touchscreen.h | |||
| @@ -15,6 +15,20 @@ | |||
| 15 | namespace Service::HID { | 15 | namespace Service::HID { |
| 16 | class Controller_Touchscreen final : public ControllerBase { | 16 | class Controller_Touchscreen final : public ControllerBase { |
| 17 | public: | 17 | public: |
| 18 | enum class TouchScreenModeForNx : u8 { | ||
| 19 | UseSystemSetting, | ||
| 20 | Finger, | ||
| 21 | Heat2, | ||
| 22 | }; | ||
| 23 | |||
| 24 | struct TouchScreenConfigurationForNx { | ||
| 25 | TouchScreenModeForNx mode; | ||
| 26 | INSERT_PADDING_BYTES_NOINIT(0x7); | ||
| 27 | INSERT_PADDING_BYTES_NOINIT(0xF); // Reserved | ||
| 28 | }; | ||
| 29 | static_assert(sizeof(TouchScreenConfigurationForNx) == 0x17, | ||
| 30 | "TouchScreenConfigurationForNx is an invalid size"); | ||
| 31 | |||
| 18 | explicit Controller_Touchscreen(Core::System& system_); | 32 | explicit Controller_Touchscreen(Core::System& system_); |
| 19 | ~Controller_Touchscreen() override; | 33 | ~Controller_Touchscreen() override; |
| 20 | 34 | ||
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index b8b80570d..a1707a72a 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp | |||
| @@ -331,7 +331,7 @@ Hid::Hid(Core::System& system_) | |||
| 331 | {529, nullptr, "SetDisallowedPalmaConnection"}, | 331 | {529, nullptr, "SetDisallowedPalmaConnection"}, |
| 332 | {1000, &Hid::SetNpadCommunicationMode, "SetNpadCommunicationMode"}, | 332 | {1000, &Hid::SetNpadCommunicationMode, "SetNpadCommunicationMode"}, |
| 333 | {1001, &Hid::GetNpadCommunicationMode, "GetNpadCommunicationMode"}, | 333 | {1001, &Hid::GetNpadCommunicationMode, "GetNpadCommunicationMode"}, |
| 334 | {1002, nullptr, "SetTouchScreenConfiguration"}, | 334 | {1002, &Hid::SetTouchScreenConfiguration, "SetTouchScreenConfiguration"}, |
| 335 | {1003, nullptr, "IsFirmwareUpdateNeededForNotification"}, | 335 | {1003, nullptr, "IsFirmwareUpdateNeededForNotification"}, |
| 336 | {2000, nullptr, "ActivateDigitizer"}, | 336 | {2000, nullptr, "ActivateDigitizer"}, |
| 337 | }; | 337 | }; |
| @@ -1631,6 +1631,18 @@ void Hid::GetNpadCommunicationMode(Kernel::HLERequestContext& ctx) { | |||
| 1631 | .GetNpadCommunicationMode()); | 1631 | .GetNpadCommunicationMode()); |
| 1632 | } | 1632 | } |
| 1633 | 1633 | ||
| 1634 | void Hid::SetTouchScreenConfiguration(Kernel::HLERequestContext& ctx) { | ||
| 1635 | IPC::RequestParser rp{ctx}; | ||
| 1636 | const auto touchscreen_mode{rp.PopRaw<Controller_Touchscreen::TouchScreenConfigurationForNx>()}; | ||
| 1637 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 1638 | |||
| 1639 | LOG_WARNING(Service_HID, "(STUBBED) called, touchscreen_mode={}, applet_resource_user_id={}", | ||
| 1640 | touchscreen_mode.mode, applet_resource_user_id); | ||
| 1641 | |||
| 1642 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1643 | rb.Push(ResultSuccess); | ||
| 1644 | } | ||
| 1645 | |||
| 1634 | class HidDbg final : public ServiceFramework<HidDbg> { | 1646 | class HidDbg final : public ServiceFramework<HidDbg> { |
| 1635 | public: | 1647 | public: |
| 1636 | explicit HidDbg(Core::System& system_) : ServiceFramework{system_, "hid:dbg"} { | 1648 | explicit HidDbg(Core::System& system_) : ServiceFramework{system_, "hid:dbg"} { |
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 9c5c7f252..b1fe75e94 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h | |||
| @@ -159,6 +159,7 @@ private: | |||
| 159 | void SetPalmaBoostMode(Kernel::HLERequestContext& ctx); | 159 | void SetPalmaBoostMode(Kernel::HLERequestContext& ctx); |
| 160 | void SetNpadCommunicationMode(Kernel::HLERequestContext& ctx); | 160 | void SetNpadCommunicationMode(Kernel::HLERequestContext& ctx); |
| 161 | void GetNpadCommunicationMode(Kernel::HLERequestContext& ctx); | 161 | void GetNpadCommunicationMode(Kernel::HLERequestContext& ctx); |
| 162 | void SetTouchScreenConfiguration(Kernel::HLERequestContext& ctx); | ||
| 162 | 163 | ||
| 163 | enum class VibrationDeviceType : u32 { | 164 | enum class VibrationDeviceType : u32 { |
| 164 | Unknown = 0, | 165 | Unknown = 0, |
diff --git a/src/core/hle/service/ngct/ngct.cpp b/src/core/hle/service/ngct/ngct.cpp index deb3abb28..8ec7d5266 100644 --- a/src/core/hle/service/ngct/ngct.cpp +++ b/src/core/hle/service/ngct/ngct.cpp | |||
| @@ -15,7 +15,7 @@ public: | |||
| 15 | explicit IService(Core::System& system_) : ServiceFramework{system_, "ngct:u"} { | 15 | explicit IService(Core::System& system_) : ServiceFramework{system_, "ngct:u"} { |
| 16 | // clang-format off | 16 | // clang-format off |
| 17 | static const FunctionInfo functions[] = { | 17 | static const FunctionInfo functions[] = { |
| 18 | {0, nullptr, "Match"}, | 18 | {0, &IService::Match, "Match"}, |
| 19 | {1, &IService::Filter, "Filter"}, | 19 | {1, &IService::Filter, "Filter"}, |
| 20 | }; | 20 | }; |
| 21 | // clang-format on | 21 | // clang-format on |
| @@ -24,6 +24,19 @@ public: | |||
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | private: | 26 | private: |
| 27 | void Match(Kernel::HLERequestContext& ctx) { | ||
| 28 | const auto buffer = ctx.ReadBuffer(); | ||
| 29 | const auto text = Common::StringFromFixedZeroTerminatedBuffer( | ||
| 30 | reinterpret_cast<const char*>(buffer.data()), buffer.size()); | ||
| 31 | |||
| 32 | LOG_WARNING(Service_NGCT, "(STUBBED) called, text={}", text); | ||
| 33 | |||
| 34 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 35 | rb.Push(ResultSuccess); | ||
| 36 | // Return false since we don't censor anything | ||
| 37 | rb.Push(false); | ||
| 38 | } | ||
| 39 | |||
| 27 | void Filter(Kernel::HLERequestContext& ctx) { | 40 | void Filter(Kernel::HLERequestContext& ctx) { |
| 28 | const auto buffer = ctx.ReadBuffer(); | 41 | const auto buffer = ctx.ReadBuffer(); |
| 29 | const auto text = Common::StringFromFixedZeroTerminatedBuffer( | 42 | const auto text = Common::StringFromFixedZeroTerminatedBuffer( |
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index ce6065db2..a33e47d0b 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp | |||
| @@ -42,15 +42,14 @@ void nvdisp_disp0::OnClose(DeviceFD fd) {} | |||
| 42 | void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, | 42 | void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, |
| 43 | u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform, | 43 | u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform, |
| 44 | const Common::Rectangle<int>& crop_rect) { | 44 | const Common::Rectangle<int>& crop_rect) { |
| 45 | VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle); | 45 | const VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle); |
| 46 | LOG_TRACE(Service, | 46 | LOG_TRACE(Service, |
| 47 | "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}", | 47 | "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}", |
| 48 | addr, offset, width, height, stride, format); | 48 | addr, offset, width, height, stride, format); |
| 49 | 49 | ||
| 50 | using PixelFormat = Tegra::FramebufferConfig::PixelFormat; | 50 | const auto pixel_format = static_cast<Tegra::FramebufferConfig::PixelFormat>(format); |
| 51 | const Tegra::FramebufferConfig framebuffer{ | 51 | const Tegra::FramebufferConfig framebuffer{addr, offset, width, height, |
| 52 | addr, offset, width, height, stride, static_cast<PixelFormat>(format), | 52 | stride, pixel_format, transform, crop_rect}; |
| 53 | transform, crop_rect}; | ||
| 54 | 53 | ||
| 55 | system.GetPerfStats().EndSystemFrame(); | 54 | system.GetPerfStats().EndSystemFrame(); |
| 56 | system.GPU().SwapBuffers(&framebuffer); | 55 | system.GPU().SwapBuffers(&framebuffer); |
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 | ||
| 14 | namespace Service::NVFlinger { | 15 | namespace Service::NVFlinger { |
| 15 | 16 | ||
| 16 | BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_) | 17 | BufferQueue::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 | ||
| 22 | BufferQueue::~BufferQueue() = default; | 23 | BufferQueue::~BufferQueue() { |
| 24 | service_context.CloseEvent(buffer_wait_event); | ||
| 25 | } | ||
| 23 | 26 | ||
| 24 | void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) { | 27 | void 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 | ||
| 47 | std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width, | 50 | std::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 | ||
| 125 | std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() { | 128 | std::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 | ||
| 160 | void BufferQueue::Connect() { | 163 | void 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 | ||
| 191 | Kernel::KWritableEvent& BufferQueue::GetWritableBufferWaitEvent() { | 194 | Kernel::KWritableEvent& BufferQueue::GetWritableBufferWaitEvent() { |
| 192 | return buffer_wait_event.GetWritableEvent(); | 195 | return buffer_wait_event->GetWritableEvent(); |
| 193 | } | 196 | } |
| 194 | 197 | ||
| 195 | Kernel::KReadableEvent& BufferQueue::GetBufferWaitEvent() { | 198 | Kernel::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..78de3f354 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; | |||
| 24 | class KWritableEvent; | 24 | class KWritableEvent; |
| 25 | } // namespace Kernel | 25 | } // namespace Kernel |
| 26 | 26 | ||
| 27 | namespace Service::KernelHelpers { | ||
| 28 | class ServiceContext; | ||
| 29 | } // namespace Service::KernelHelpers | ||
| 30 | |||
| 27 | namespace Service::NVFlinger { | 31 | namespace Service::NVFlinger { |
| 28 | 32 | ||
| 29 | constexpr u32 buffer_slots = 0x40; | 33 | constexpr u32 buffer_slots = 0x40; |
| @@ -38,7 +42,9 @@ struct IGBPBuffer { | |||
| 38 | u32_le index; | 42 | u32_le index; |
| 39 | INSERT_PADDING_WORDS(3); | 43 | INSERT_PADDING_WORDS(3); |
| 40 | u32_le gpu_buffer_id; | 44 | u32_le gpu_buffer_id; |
| 41 | INSERT_PADDING_WORDS(17); | 45 | INSERT_PADDING_WORDS(6); |
| 46 | u32_le external_format; | ||
| 47 | INSERT_PADDING_WORDS(10); | ||
| 42 | u32_le nvmap_handle; | 48 | u32_le nvmap_handle; |
| 43 | u32_le offset; | 49 | u32_le offset; |
| 44 | INSERT_PADDING_WORDS(60); | 50 | INSERT_PADDING_WORDS(60); |
| @@ -54,7 +60,8 @@ public: | |||
| 54 | NativeWindowFormat = 2, | 60 | NativeWindowFormat = 2, |
| 55 | }; | 61 | }; |
| 56 | 62 | ||
| 57 | explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_); | 63 | explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_, |
| 64 | KernelHelpers::ServiceContext& service_context_); | ||
| 58 | ~BufferQueue(); | 65 | ~BufferQueue(); |
| 59 | 66 | ||
| 60 | enum class BufferTransformFlags : u32 { | 67 | enum class BufferTransformFlags : u32 { |
| @@ -130,12 +137,14 @@ private: | |||
| 130 | std::list<u32> free_buffers; | 137 | std::list<u32> free_buffers; |
| 131 | std::array<Buffer, buffer_slots> buffers; | 138 | std::array<Buffer, buffer_slots> buffers; |
| 132 | std::list<u32> queue_sequence; | 139 | std::list<u32> queue_sequence; |
| 133 | Kernel::KEvent buffer_wait_event; | 140 | Kernel::KEvent* buffer_wait_event{}; |
| 134 | 141 | ||
| 135 | std::mutex free_buffers_mutex; | 142 | std::mutex free_buffers_mutex; |
| 136 | std::condition_variable free_buffers_condition; | 143 | std::condition_variable free_buffers_condition; |
| 137 | 144 | ||
| 138 | std::mutex queue_sequence_mutex; | 145 | std::mutex queue_sequence_mutex; |
| 146 | |||
| 147 | KernelHelpers::ServiceContext& service_context; | ||
| 139 | }; | 148 | }; |
| 140 | 149 | ||
| 141 | } // namespace Service::NVFlinger | 150 | } // namespace Service::NVFlinger |
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 32d4e360a..3ead813b0 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp | |||
| @@ -147,7 +147,7 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) { | |||
| 147 | void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) { | 147 | void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) { |
| 148 | const u32 buffer_queue_id = next_buffer_queue_id++; | 148 | const u32 buffer_queue_id = next_buffer_queue_id++; |
| 149 | buffer_queues.emplace_back( | 149 | buffer_queues.emplace_back( |
| 150 | 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)); |
| 151 | display.CreateLayer(layer_id, *buffer_queues.back()); | 151 | display.CreateLayer(layer_id, *buffer_queues.back()); |
| 152 | } | 152 | } |
| 153 | 153 | ||
| @@ -298,7 +298,7 @@ void NVFlinger::Compose() { | |||
| 298 | auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0"); | 298 | auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0"); |
| 299 | ASSERT(nvdisp); | 299 | ASSERT(nvdisp); |
| 300 | 300 | ||
| 301 | nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.format, | 301 | nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.external_format, |
| 302 | igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, | 302 | igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, |
| 303 | buffer->get().transform, buffer->get().crop_rect); | 303 | buffer->get().transform, buffer->get().crop_rect); |
| 304 | 304 | ||
diff --git a/src/core/network/network.cpp b/src/core/network/network.cpp index 4732d4485..72eea52f0 100644 --- a/src/core/network/network.cpp +++ b/src/core/network/network.cpp | |||
| @@ -7,7 +7,8 @@ | |||
| 7 | #include <limits> | 7 | #include <limits> |
| 8 | #include <utility> | 8 | #include <utility> |
| 9 | #include <vector> | 9 | #include <vector> |
| 10 | #include "common/common_funcs.h" | 10 | |
| 11 | #include "common/error.h" | ||
| 11 | 12 | ||
| 12 | #ifdef _WIN32 | 13 | #ifdef _WIN32 |
| 13 | #include <winsock2.h> | 14 | #include <winsock2.h> |
| @@ -223,7 +224,7 @@ Errno GetAndLogLastError() { | |||
| 223 | if (err == Errno::AGAIN) { | 224 | if (err == Errno::AGAIN) { |
| 224 | return err; | 225 | return err; |
| 225 | } | 226 | } |
| 226 | LOG_ERROR(Network, "Socket operation error: {}", NativeErrorToString(e)); | 227 | LOG_ERROR(Network, "Socket operation error: {}", Common::NativeErrorToString(e)); |
| 227 | return err; | 228 | return err; |
| 228 | } | 229 | } |
| 229 | 230 | ||
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index 5a8cfd301..1f1607998 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp | |||
| @@ -72,6 +72,18 @@ static const char* TranslateGPUAccuracyLevel(Settings::GPUAccuracy backend) { | |||
| 72 | return "Unknown"; | 72 | return "Unknown"; |
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | static const char* TranslateNvdecEmulation(Settings::NvdecEmulation backend) { | ||
| 76 | switch (backend) { | ||
| 77 | case Settings::NvdecEmulation::Off: | ||
| 78 | return "Off"; | ||
| 79 | case Settings::NvdecEmulation::CPU: | ||
| 80 | return "CPU"; | ||
| 81 | case Settings::NvdecEmulation::GPU: | ||
| 82 | return "GPU"; | ||
| 83 | } | ||
| 84 | return "Unknown"; | ||
| 85 | } | ||
| 86 | |||
| 75 | u64 GetTelemetryId() { | 87 | u64 GetTelemetryId() { |
| 76 | u64 telemetry_id{}; | 88 | u64 telemetry_id{}; |
| 77 | const auto filename = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "telemetry_id"; | 89 | const auto filename = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "telemetry_id"; |
| @@ -229,8 +241,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader, | |||
| 229 | TranslateGPUAccuracyLevel(Settings::values.gpu_accuracy.GetValue())); | 241 | TranslateGPUAccuracyLevel(Settings::values.gpu_accuracy.GetValue())); |
| 230 | AddField(field_type, "Renderer_UseAsynchronousGpuEmulation", | 242 | AddField(field_type, "Renderer_UseAsynchronousGpuEmulation", |
| 231 | Settings::values.use_asynchronous_gpu_emulation.GetValue()); | 243 | Settings::values.use_asynchronous_gpu_emulation.GetValue()); |
| 232 | AddField(field_type, "Renderer_UseNvdecEmulation", | 244 | AddField(field_type, "Renderer_NvdecEmulation", |
| 233 | Settings::values.use_nvdec_emulation.GetValue()); | 245 | TranslateNvdecEmulation(Settings::values.nvdec_emulation.GetValue())); |
| 234 | AddField(field_type, "Renderer_AccelerateASTC", Settings::values.accelerate_astc.GetValue()); | 246 | AddField(field_type, "Renderer_AccelerateASTC", Settings::values.accelerate_astc.GetValue()); |
| 235 | AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync.GetValue()); | 247 | AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync.GetValue()); |
| 236 | AddField(field_type, "Renderer_ShaderBackend", | 248 | AddField(field_type, "Renderer_ShaderBackend", |
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_warp.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_warp.cpp index a982dd8a2..cd285e2c8 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_warp.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_warp.cpp | |||
| @@ -11,6 +11,8 @@ | |||
| 11 | 11 | ||
| 12 | namespace Shader::Backend::GLSL { | 12 | namespace Shader::Backend::GLSL { |
| 13 | namespace { | 13 | namespace { |
| 14 | constexpr char THREAD_ID[]{"gl_SubGroupInvocationARB"}; | ||
| 15 | |||
| 14 | void SetInBoundsFlag(EmitContext& ctx, IR::Inst& inst) { | 16 | void SetInBoundsFlag(EmitContext& ctx, IR::Inst& inst) { |
| 15 | IR::Inst* const in_bounds{inst.GetAssociatedPseudoOperation(IR::Opcode::GetInBoundsFromOp)}; | 17 | IR::Inst* const in_bounds{inst.GetAssociatedPseudoOperation(IR::Opcode::GetInBoundsFromOp)}; |
| 16 | if (!in_bounds) { | 18 | if (!in_bounds) { |
| @@ -43,84 +45,100 @@ void UseShuffleNv(EmitContext& ctx, IR::Inst& inst, std::string_view shfl_op, | |||
| 43 | ctx.AddU32("{}={}({},{},{},shfl_in_bounds);", inst, shfl_op, value, index, width); | 45 | ctx.AddU32("{}={}({},{},{},shfl_in_bounds);", inst, shfl_op, value, index, width); |
| 44 | SetInBoundsFlag(ctx, inst); | 46 | SetInBoundsFlag(ctx, inst); |
| 45 | } | 47 | } |
| 48 | |||
| 49 | std::string_view BallotIndex(EmitContext& ctx) { | ||
| 50 | if (!ctx.profile.warp_size_potentially_larger_than_guest) { | ||
| 51 | return ".x"; | ||
| 52 | } | ||
| 53 | return "[gl_SubGroupInvocationARB>>5]"; | ||
| 54 | } | ||
| 55 | |||
| 56 | std::string GetMask(EmitContext& ctx, std::string_view mask) { | ||
| 57 | const auto ballot_index{BallotIndex(ctx)}; | ||
| 58 | return fmt::format("uint(uvec2({}){})", mask, ballot_index); | ||
| 59 | } | ||
| 46 | } // Anonymous namespace | 60 | } // Anonymous namespace |
| 47 | 61 | ||
| 48 | void EmitLaneId(EmitContext& ctx, IR::Inst& inst) { | 62 | void EmitLaneId(EmitContext& ctx, IR::Inst& inst) { |
| 49 | ctx.AddU32("{}=gl_SubGroupInvocationARB&31u;", inst); | 63 | ctx.AddU32("{}={}&31u;", inst, THREAD_ID); |
| 50 | } | 64 | } |
| 51 | 65 | ||
| 52 | void EmitVoteAll(EmitContext& ctx, IR::Inst& inst, std::string_view pred) { | 66 | void EmitVoteAll(EmitContext& ctx, IR::Inst& inst, std::string_view pred) { |
| 53 | if (!ctx.profile.warp_size_potentially_larger_than_guest) { | 67 | if (!ctx.profile.warp_size_potentially_larger_than_guest) { |
| 54 | ctx.AddU1("{}=allInvocationsEqualARB({});", inst, pred); | 68 | ctx.AddU1("{}=allInvocationsEqualARB({});", inst, pred); |
| 55 | } else { | 69 | return; |
| 56 | const auto active_mask{fmt::format("uvec2(ballotARB(true))[gl_SubGroupInvocationARB]")}; | ||
| 57 | const auto ballot{fmt::format("uvec2(ballotARB({}))[gl_SubGroupInvocationARB]", pred)}; | ||
| 58 | ctx.AddU1("{}=({}&{})=={};", inst, ballot, active_mask, active_mask); | ||
| 59 | } | 70 | } |
| 71 | const auto ballot_index{BallotIndex(ctx)}; | ||
| 72 | const auto active_mask{fmt::format("uvec2(ballotARB(true)){}", ballot_index)}; | ||
| 73 | const auto ballot{fmt::format("uvec2(ballotARB({})){}", pred, ballot_index)}; | ||
| 74 | ctx.AddU1("{}=({}&{})=={};", inst, ballot, active_mask, active_mask); | ||
| 60 | } | 75 | } |
| 61 | 76 | ||
| 62 | void EmitVoteAny(EmitContext& ctx, IR::Inst& inst, std::string_view pred) { | 77 | void EmitVoteAny(EmitContext& ctx, IR::Inst& inst, std::string_view pred) { |
| 63 | if (!ctx.profile.warp_size_potentially_larger_than_guest) { | 78 | if (!ctx.profile.warp_size_potentially_larger_than_guest) { |
| 64 | ctx.AddU1("{}=anyInvocationARB({});", inst, pred); | 79 | ctx.AddU1("{}=anyInvocationARB({});", inst, pred); |
| 65 | } else { | 80 | return; |
| 66 | const auto active_mask{fmt::format("uvec2(ballotARB(true))[gl_SubGroupInvocationARB]")}; | ||
| 67 | const auto ballot{fmt::format("uvec2(ballotARB({}))[gl_SubGroupInvocationARB]", pred)}; | ||
| 68 | ctx.AddU1("{}=({}&{})!=0u;", inst, ballot, active_mask, active_mask); | ||
| 69 | } | 81 | } |
| 82 | const auto ballot_index{BallotIndex(ctx)}; | ||
| 83 | const auto active_mask{fmt::format("uvec2(ballotARB(true)){}", ballot_index)}; | ||
| 84 | const auto ballot{fmt::format("uvec2(ballotARB({})){}", pred, ballot_index)}; | ||
| 85 | ctx.AddU1("{}=({}&{})!=0u;", inst, ballot, active_mask, active_mask); | ||
| 70 | } | 86 | } |
| 71 | 87 | ||
| 72 | void EmitVoteEqual(EmitContext& ctx, IR::Inst& inst, std::string_view pred) { | 88 | void EmitVoteEqual(EmitContext& ctx, IR::Inst& inst, std::string_view pred) { |
| 73 | if (!ctx.profile.warp_size_potentially_larger_than_guest) { | 89 | if (!ctx.profile.warp_size_potentially_larger_than_guest) { |
| 74 | ctx.AddU1("{}=allInvocationsEqualARB({});", inst, pred); | 90 | ctx.AddU1("{}=allInvocationsEqualARB({});", inst, pred); |
| 75 | } else { | 91 | return; |
| 76 | const auto active_mask{fmt::format("uvec2(ballotARB(true))[gl_SubGroupInvocationARB]")}; | ||
| 77 | const auto ballot{fmt::format("uvec2(ballotARB({}))[gl_SubGroupInvocationARB]", pred)}; | ||
| 78 | const auto value{fmt::format("({}^{})", ballot, active_mask)}; | ||
| 79 | ctx.AddU1("{}=({}==0)||({}=={});", inst, value, value, active_mask); | ||
| 80 | } | 92 | } |
| 93 | const auto ballot_index{BallotIndex(ctx)}; | ||
| 94 | const auto active_mask{fmt::format("uvec2(ballotARB(true)){}", ballot_index)}; | ||
| 95 | const auto ballot{fmt::format("uvec2(ballotARB({})){}", pred, ballot_index)}; | ||
| 96 | const auto value{fmt::format("({}^{})", ballot, active_mask)}; | ||
| 97 | ctx.AddU1("{}=({}==0)||({}=={});", inst, value, value, active_mask); | ||
| 81 | } | 98 | } |
| 82 | 99 | ||
| 83 | void EmitSubgroupBallot(EmitContext& ctx, IR::Inst& inst, std::string_view pred) { | 100 | void EmitSubgroupBallot(EmitContext& ctx, IR::Inst& inst, std::string_view pred) { |
| 84 | if (!ctx.profile.warp_size_potentially_larger_than_guest) { | 101 | const auto ballot_index{BallotIndex(ctx)}; |
| 85 | ctx.AddU32("{}=uvec2(ballotARB({})).x;", inst, pred); | 102 | ctx.AddU32("{}=uvec2(ballotARB({})){};", inst, pred, ballot_index); |
| 86 | } else { | ||
| 87 | ctx.AddU32("{}=uvec2(ballotARB({}))[gl_SubGroupInvocationARB];", inst, pred); | ||
| 88 | } | ||
| 89 | } | 103 | } |
| 90 | 104 | ||
| 91 | void EmitSubgroupEqMask(EmitContext& ctx, IR::Inst& inst) { | 105 | void EmitSubgroupEqMask(EmitContext& ctx, IR::Inst& inst) { |
| 92 | ctx.AddU32("{}=uint(gl_SubGroupEqMaskARB.x);", inst); | 106 | ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupEqMaskARB")); |
| 93 | } | 107 | } |
| 94 | 108 | ||
| 95 | void EmitSubgroupLtMask(EmitContext& ctx, IR::Inst& inst) { | 109 | void EmitSubgroupLtMask(EmitContext& ctx, IR::Inst& inst) { |
| 96 | ctx.AddU32("{}=uint(gl_SubGroupLtMaskARB.x);", inst); | 110 | ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupLtMaskARB")); |
| 97 | } | 111 | } |
| 98 | 112 | ||
| 99 | void EmitSubgroupLeMask(EmitContext& ctx, IR::Inst& inst) { | 113 | void EmitSubgroupLeMask(EmitContext& ctx, IR::Inst& inst) { |
| 100 | ctx.AddU32("{}=uint(gl_SubGroupLeMaskARB.x);", inst); | 114 | ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupLeMaskARB")); |
| 101 | } | 115 | } |
| 102 | 116 | ||
| 103 | void EmitSubgroupGtMask(EmitContext& ctx, IR::Inst& inst) { | 117 | void EmitSubgroupGtMask(EmitContext& ctx, IR::Inst& inst) { |
| 104 | ctx.AddU32("{}=uint(gl_SubGroupGtMaskARB.x);", inst); | 118 | ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupGtMaskARB")); |
| 105 | } | 119 | } |
| 106 | 120 | ||
| 107 | void EmitSubgroupGeMask(EmitContext& ctx, IR::Inst& inst) { | 121 | void EmitSubgroupGeMask(EmitContext& ctx, IR::Inst& inst) { |
| 108 | ctx.AddU32("{}=uint(gl_SubGroupGeMaskARB.x);", inst); | 122 | ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupGeMaskARB")); |
| 109 | } | 123 | } |
| 110 | 124 | ||
| 111 | void EmitShuffleIndex(EmitContext& ctx, IR::Inst& inst, std::string_view value, | 125 | void EmitShuffleIndex(EmitContext& ctx, IR::Inst& inst, std::string_view value, |
| 112 | std::string_view index, std::string_view clamp, | 126 | std::string_view index, std::string_view clamp, std::string_view seg_mask) { |
| 113 | std::string_view segmentation_mask) { | ||
| 114 | if (ctx.profile.support_gl_warp_intrinsics) { | 127 | if (ctx.profile.support_gl_warp_intrinsics) { |
| 115 | UseShuffleNv(ctx, inst, "shuffleNV", value, index, clamp, segmentation_mask); | 128 | UseShuffleNv(ctx, inst, "shuffleNV", value, index, clamp, seg_mask); |
| 116 | return; | 129 | return; |
| 117 | } | 130 | } |
| 118 | const auto not_seg_mask{fmt::format("(~{})", segmentation_mask)}; | 131 | const bool big_warp{ctx.profile.warp_size_potentially_larger_than_guest}; |
| 119 | const auto thread_id{"gl_SubGroupInvocationARB"}; | 132 | const auto is_upper_partition{"int(gl_SubGroupInvocationARB)>=32"}; |
| 120 | const auto min_thread_id{ComputeMinThreadId(thread_id, segmentation_mask)}; | 133 | const auto upper_index{fmt::format("{}?{}+32:{}", is_upper_partition, index, index)}; |
| 121 | const auto max_thread_id{ComputeMaxThreadId(min_thread_id, clamp, not_seg_mask)}; | 134 | const auto upper_clamp{fmt::format("{}?{}+32:{}", is_upper_partition, clamp, clamp)}; |
| 135 | |||
| 136 | const auto not_seg_mask{fmt::format("(~{})", seg_mask)}; | ||
| 137 | const auto min_thread_id{ComputeMinThreadId(THREAD_ID, seg_mask)}; | ||
| 138 | const auto max_thread_id{ | ||
| 139 | ComputeMaxThreadId(min_thread_id, big_warp ? upper_clamp : clamp, not_seg_mask)}; | ||
| 122 | 140 | ||
| 123 | const auto lhs{fmt::format("({}&{})", index, not_seg_mask)}; | 141 | const auto lhs{fmt::format("({}&{})", big_warp ? upper_index : index, not_seg_mask)}; |
| 124 | const auto src_thread_id{fmt::format("({})|({})", lhs, min_thread_id)}; | 142 | const auto src_thread_id{fmt::format("({})|({})", lhs, min_thread_id)}; |
| 125 | ctx.Add("shfl_in_bounds=int({})<=int({});", src_thread_id, max_thread_id); | 143 | ctx.Add("shfl_in_bounds=int({})<=int({});", src_thread_id, max_thread_id); |
| 126 | SetInBoundsFlag(ctx, inst); | 144 | SetInBoundsFlag(ctx, inst); |
| @@ -128,29 +146,34 @@ void EmitShuffleIndex(EmitContext& ctx, IR::Inst& inst, std::string_view value, | |||
| 128 | } | 146 | } |
| 129 | 147 | ||
| 130 | void EmitShuffleUp(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view index, | 148 | void EmitShuffleUp(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view index, |
| 131 | std::string_view clamp, std::string_view segmentation_mask) { | 149 | std::string_view clamp, std::string_view seg_mask) { |
| 132 | if (ctx.profile.support_gl_warp_intrinsics) { | 150 | if (ctx.profile.support_gl_warp_intrinsics) { |
| 133 | UseShuffleNv(ctx, inst, "shuffleUpNV", value, index, clamp, segmentation_mask); | 151 | UseShuffleNv(ctx, inst, "shuffleUpNV", value, index, clamp, seg_mask); |
| 134 | return; | 152 | return; |
| 135 | } | 153 | } |
| 136 | const auto thread_id{"gl_SubGroupInvocationARB"}; | 154 | const bool big_warp{ctx.profile.warp_size_potentially_larger_than_guest}; |
| 137 | const auto max_thread_id{GetMaxThreadId(thread_id, clamp, segmentation_mask)}; | 155 | const auto is_upper_partition{"int(gl_SubGroupInvocationARB)>=32"}; |
| 138 | const auto src_thread_id{fmt::format("({}-{})", thread_id, index)}; | 156 | const auto upper_clamp{fmt::format("{}?{}+32:{}", is_upper_partition, clamp, clamp)}; |
| 157 | |||
| 158 | const auto max_thread_id{GetMaxThreadId(THREAD_ID, big_warp ? upper_clamp : clamp, seg_mask)}; | ||
| 159 | const auto src_thread_id{fmt::format("({}-{})", THREAD_ID, index)}; | ||
| 139 | ctx.Add("shfl_in_bounds=int({})>=int({});", src_thread_id, max_thread_id); | 160 | ctx.Add("shfl_in_bounds=int({})>=int({});", src_thread_id, max_thread_id); |
| 140 | SetInBoundsFlag(ctx, inst); | 161 | SetInBoundsFlag(ctx, inst); |
| 141 | ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value); | 162 | ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value); |
| 142 | } | 163 | } |
| 143 | 164 | ||
| 144 | void EmitShuffleDown(EmitContext& ctx, IR::Inst& inst, std::string_view value, | 165 | void EmitShuffleDown(EmitContext& ctx, IR::Inst& inst, std::string_view value, |
| 145 | std::string_view index, std::string_view clamp, | 166 | std::string_view index, std::string_view clamp, std::string_view seg_mask) { |
| 146 | std::string_view segmentation_mask) { | ||
| 147 | if (ctx.profile.support_gl_warp_intrinsics) { | 167 | if (ctx.profile.support_gl_warp_intrinsics) { |
| 148 | UseShuffleNv(ctx, inst, "shuffleDownNV", value, index, clamp, segmentation_mask); | 168 | UseShuffleNv(ctx, inst, "shuffleDownNV", value, index, clamp, seg_mask); |
| 149 | return; | 169 | return; |
| 150 | } | 170 | } |
| 151 | const auto thread_id{"gl_SubGroupInvocationARB"}; | 171 | const bool big_warp{ctx.profile.warp_size_potentially_larger_than_guest}; |
| 152 | const auto max_thread_id{GetMaxThreadId(thread_id, clamp, segmentation_mask)}; | 172 | const auto is_upper_partition{"int(gl_SubGroupInvocationARB)>=32"}; |
| 153 | const auto src_thread_id{fmt::format("({}+{})", thread_id, index)}; | 173 | const auto upper_clamp{fmt::format("{}?{}+32:{}", is_upper_partition, clamp, clamp)}; |
| 174 | |||
| 175 | const auto max_thread_id{GetMaxThreadId(THREAD_ID, big_warp ? upper_clamp : clamp, seg_mask)}; | ||
| 176 | const auto src_thread_id{fmt::format("({}+{})", THREAD_ID, index)}; | ||
| 154 | ctx.Add("shfl_in_bounds=int({})<=int({});", src_thread_id, max_thread_id); | 177 | ctx.Add("shfl_in_bounds=int({})<=int({});", src_thread_id, max_thread_id); |
| 155 | SetInBoundsFlag(ctx, inst); | 178 | SetInBoundsFlag(ctx, inst); |
| 156 | ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value); | 179 | ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value); |
| @@ -158,14 +181,17 @@ void EmitShuffleDown(EmitContext& ctx, IR::Inst& inst, std::string_view value, | |||
| 158 | 181 | ||
| 159 | void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, std::string_view value, | 182 | void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, std::string_view value, |
| 160 | std::string_view index, std::string_view clamp, | 183 | std::string_view index, std::string_view clamp, |
| 161 | std::string_view segmentation_mask) { | 184 | std::string_view seg_mask) { |
| 162 | if (ctx.profile.support_gl_warp_intrinsics) { | 185 | if (ctx.profile.support_gl_warp_intrinsics) { |
| 163 | UseShuffleNv(ctx, inst, "shuffleXorNV", value, index, clamp, segmentation_mask); | 186 | UseShuffleNv(ctx, inst, "shuffleXorNV", value, index, clamp, seg_mask); |
| 164 | return; | 187 | return; |
| 165 | } | 188 | } |
| 166 | const auto thread_id{"gl_SubGroupInvocationARB"}; | 189 | const bool big_warp{ctx.profile.warp_size_potentially_larger_than_guest}; |
| 167 | const auto max_thread_id{GetMaxThreadId(thread_id, clamp, segmentation_mask)}; | 190 | const auto is_upper_partition{"int(gl_SubGroupInvocationARB)>=32"}; |
| 168 | const auto src_thread_id{fmt::format("({}^{})", thread_id, index)}; | 191 | const auto upper_clamp{fmt::format("{}?{}+32:{}", is_upper_partition, clamp, clamp)}; |
| 192 | |||
| 193 | const auto max_thread_id{GetMaxThreadId(THREAD_ID, big_warp ? upper_clamp : clamp, seg_mask)}; | ||
| 194 | const auto src_thread_id{fmt::format("({}^{})", THREAD_ID, index)}; | ||
| 169 | ctx.Add("shfl_in_bounds=int({})<=int({});", src_thread_id, max_thread_id); | 195 | ctx.Add("shfl_in_bounds=int({})<=int({});", src_thread_id, max_thread_id); |
| 170 | SetInBoundsFlag(ctx, inst); | 196 | SetInBoundsFlag(ctx, inst); |
| 171 | ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value); | 197 | ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value); |
diff --git a/src/shader_recompiler/backend/spirv/emit_context.cpp b/src/shader_recompiler/backend/spirv/emit_context.cpp index 2d29d8c14..2885e6799 100644 --- a/src/shader_recompiler/backend/spirv/emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/emit_context.cpp | |||
| @@ -15,6 +15,8 @@ | |||
| 15 | 15 | ||
| 16 | namespace Shader::Backend::SPIRV { | 16 | namespace Shader::Backend::SPIRV { |
| 17 | namespace { | 17 | namespace { |
| 18 | constexpr size_t NUM_FIXEDFNCTEXTURE = 10; | ||
| 19 | |||
| 18 | enum class Operation { | 20 | enum class Operation { |
| 19 | Increment, | 21 | Increment, |
| 20 | Decrement, | 22 | Decrement, |
| @@ -427,6 +429,16 @@ Id DescType(EmitContext& ctx, Id sampled_type, Id pointer_type, u32 count) { | |||
| 427 | return pointer_type; | 429 | return pointer_type; |
| 428 | } | 430 | } |
| 429 | } | 431 | } |
| 432 | |||
| 433 | size_t FindNextUnusedLocation(const std::bitset<IR::NUM_GENERICS>& used_locations, | ||
| 434 | size_t start_offset) { | ||
| 435 | for (size_t location = start_offset; location < used_locations.size(); ++location) { | ||
| 436 | if (!used_locations.test(location)) { | ||
| 437 | return location; | ||
| 438 | } | ||
| 439 | } | ||
| 440 | throw RuntimeError("Unable to get an unused location for legacy attribute"); | ||
| 441 | } | ||
| 430 | } // Anonymous namespace | 442 | } // Anonymous namespace |
| 431 | 443 | ||
| 432 | void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name) { | 444 | void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name) { |
| @@ -1227,6 +1239,7 @@ void EmitContext::DefineInputs(const IR::Program& program) { | |||
| 1227 | loads[IR::Attribute::TessellationEvaluationPointV]) { | 1239 | loads[IR::Attribute::TessellationEvaluationPointV]) { |
| 1228 | tess_coord = DefineInput(*this, F32[3], false, spv::BuiltIn::TessCoord); | 1240 | tess_coord = DefineInput(*this, F32[3], false, spv::BuiltIn::TessCoord); |
| 1229 | } | 1241 | } |
| 1242 | std::bitset<IR::NUM_GENERICS> used_locations{}; | ||
| 1230 | for (size_t index = 0; index < IR::NUM_GENERICS; ++index) { | 1243 | for (size_t index = 0; index < IR::NUM_GENERICS; ++index) { |
| 1231 | const AttributeType input_type{runtime_info.generic_input_types[index]}; | 1244 | const AttributeType input_type{runtime_info.generic_input_types[index]}; |
| 1232 | if (!runtime_info.previous_stage_stores.Generic(index)) { | 1245 | if (!runtime_info.previous_stage_stores.Generic(index)) { |
| @@ -1238,6 +1251,7 @@ void EmitContext::DefineInputs(const IR::Program& program) { | |||
| 1238 | if (input_type == AttributeType::Disabled) { | 1251 | if (input_type == AttributeType::Disabled) { |
| 1239 | continue; | 1252 | continue; |
| 1240 | } | 1253 | } |
| 1254 | used_locations.set(index); | ||
| 1241 | const Id type{GetAttributeType(*this, input_type)}; | 1255 | const Id type{GetAttributeType(*this, input_type)}; |
| 1242 | const Id id{DefineInput(*this, type, true)}; | 1256 | const Id id{DefineInput(*this, type, true)}; |
| 1243 | Decorate(id, spv::Decoration::Location, static_cast<u32>(index)); | 1257 | Decorate(id, spv::Decoration::Location, static_cast<u32>(index)); |
| @@ -1263,6 +1277,26 @@ void EmitContext::DefineInputs(const IR::Program& program) { | |||
| 1263 | break; | 1277 | break; |
| 1264 | } | 1278 | } |
| 1265 | } | 1279 | } |
| 1280 | size_t previous_unused_location = 0; | ||
| 1281 | if (loads.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) { | ||
| 1282 | const size_t location = FindNextUnusedLocation(used_locations, previous_unused_location); | ||
| 1283 | previous_unused_location = location; | ||
| 1284 | used_locations.set(location); | ||
| 1285 | const Id id{DefineInput(*this, F32[4], true)}; | ||
| 1286 | Decorate(id, spv::Decoration::Location, location); | ||
| 1287 | input_front_color = id; | ||
| 1288 | } | ||
| 1289 | for (size_t index = 0; index < NUM_FIXEDFNCTEXTURE; ++index) { | ||
| 1290 | if (loads.AnyComponent(IR::Attribute::FixedFncTexture0S + index * 4)) { | ||
| 1291 | const size_t location = | ||
| 1292 | FindNextUnusedLocation(used_locations, previous_unused_location); | ||
| 1293 | previous_unused_location = location; | ||
| 1294 | used_locations.set(location); | ||
| 1295 | const Id id{DefineInput(*this, F32[4], true)}; | ||
| 1296 | Decorate(id, spv::Decoration::Location, location); | ||
| 1297 | input_fixed_fnc_textures[index] = id; | ||
| 1298 | } | ||
| 1299 | } | ||
| 1266 | if (stage == Stage::TessellationEval) { | 1300 | if (stage == Stage::TessellationEval) { |
| 1267 | for (size_t index = 0; index < info.uses_patches.size(); ++index) { | 1301 | for (size_t index = 0; index < info.uses_patches.size(); ++index) { |
| 1268 | if (!info.uses_patches[index]) { | 1302 | if (!info.uses_patches[index]) { |
| @@ -1313,9 +1347,31 @@ void EmitContext::DefineOutputs(const IR::Program& program) { | |||
| 1313 | viewport_mask = DefineOutput(*this, TypeArray(U32[1], Const(1u)), std::nullopt, | 1347 | viewport_mask = DefineOutput(*this, TypeArray(U32[1], Const(1u)), std::nullopt, |
| 1314 | spv::BuiltIn::ViewportMaskNV); | 1348 | spv::BuiltIn::ViewportMaskNV); |
| 1315 | } | 1349 | } |
| 1350 | std::bitset<IR::NUM_GENERICS> used_locations{}; | ||
| 1316 | for (size_t index = 0; index < IR::NUM_GENERICS; ++index) { | 1351 | for (size_t index = 0; index < IR::NUM_GENERICS; ++index) { |
| 1317 | if (info.stores.Generic(index)) { | 1352 | if (info.stores.Generic(index)) { |
| 1318 | DefineGenericOutput(*this, index, invocations); | 1353 | DefineGenericOutput(*this, index, invocations); |
| 1354 | used_locations.set(index); | ||
| 1355 | } | ||
| 1356 | } | ||
| 1357 | size_t previous_unused_location = 0; | ||
| 1358 | if (info.stores.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) { | ||
| 1359 | const size_t location = FindNextUnusedLocation(used_locations, previous_unused_location); | ||
| 1360 | previous_unused_location = location; | ||
| 1361 | used_locations.set(location); | ||
| 1362 | const Id id{DefineOutput(*this, F32[4], invocations)}; | ||
| 1363 | Decorate(id, spv::Decoration::Location, static_cast<u32>(location)); | ||
| 1364 | output_front_color = id; | ||
| 1365 | } | ||
| 1366 | for (size_t index = 0; index < NUM_FIXEDFNCTEXTURE; ++index) { | ||
| 1367 | if (info.stores.AnyComponent(IR::Attribute::FixedFncTexture0S + index * 4)) { | ||
| 1368 | const size_t location = | ||
| 1369 | FindNextUnusedLocation(used_locations, previous_unused_location); | ||
| 1370 | previous_unused_location = location; | ||
| 1371 | used_locations.set(location); | ||
| 1372 | const Id id{DefineOutput(*this, F32[4], invocations)}; | ||
| 1373 | Decorate(id, spv::Decoration::Location, location); | ||
| 1374 | output_fixed_fnc_textures[index] = id; | ||
| 1319 | } | 1375 | } |
| 1320 | } | 1376 | } |
| 1321 | switch (stage) { | 1377 | switch (stage) { |
diff --git a/src/shader_recompiler/backend/spirv/emit_context.h b/src/shader_recompiler/backend/spirv/emit_context.h index e277bc358..847d0c0e6 100644 --- a/src/shader_recompiler/backend/spirv/emit_context.h +++ b/src/shader_recompiler/backend/spirv/emit_context.h | |||
| @@ -268,10 +268,14 @@ public: | |||
| 268 | Id write_global_func_u32x4{}; | 268 | Id write_global_func_u32x4{}; |
| 269 | 269 | ||
| 270 | Id input_position{}; | 270 | Id input_position{}; |
| 271 | Id input_front_color{}; | ||
| 272 | std::array<Id, 10> input_fixed_fnc_textures{}; | ||
| 271 | std::array<Id, 32> input_generics{}; | 273 | std::array<Id, 32> input_generics{}; |
| 272 | 274 | ||
| 273 | Id output_point_size{}; | 275 | Id output_point_size{}; |
| 274 | Id output_position{}; | 276 | Id output_position{}; |
| 277 | Id output_front_color{}; | ||
| 278 | std::array<Id, 10> output_fixed_fnc_textures{}; | ||
| 275 | std::array<std::array<GenericElementInfo, 4>, 32> output_generics{}; | 279 | std::array<std::array<GenericElementInfo, 4>, 32> output_generics{}; |
| 276 | 280 | ||
| 277 | Id output_tess_level_outer{}; | 281 | Id output_tess_level_outer{}; |
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 9e54a17ee..68f360b3c 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 | |||
| @@ -43,6 +43,25 @@ Id AttrPointer(EmitContext& ctx, Id pointer_type, Id vertex, Id base, Args&&... | |||
| 43 | } | 43 | } |
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | bool IsFixedFncTexture(IR::Attribute attribute) { | ||
| 47 | return attribute >= IR::Attribute::FixedFncTexture0S && | ||
| 48 | attribute <= IR::Attribute::FixedFncTexture9Q; | ||
| 49 | } | ||
| 50 | |||
| 51 | u32 FixedFncTextureAttributeIndex(IR::Attribute attribute) { | ||
| 52 | if (!IsFixedFncTexture(attribute)) { | ||
| 53 | throw InvalidArgument("Attribute {} is not a FixedFncTexture", attribute); | ||
| 54 | } | ||
| 55 | return (static_cast<u32>(attribute) - static_cast<u32>(IR::Attribute::FixedFncTexture0S)) / 4u; | ||
| 56 | } | ||
| 57 | |||
| 58 | u32 FixedFncTextureAttributeElement(IR::Attribute attribute) { | ||
| 59 | if (!IsFixedFncTexture(attribute)) { | ||
| 60 | throw InvalidArgument("Attribute {} is not a FixedFncTexture", attribute); | ||
| 61 | } | ||
| 62 | return static_cast<u32>(attribute) % 4u; | ||
| 63 | } | ||
| 64 | |||
| 46 | template <typename... Args> | 65 | template <typename... Args> |
| 47 | Id OutputAccessChain(EmitContext& ctx, Id result_type, Id base, Args&&... args) { | 66 | Id OutputAccessChain(EmitContext& ctx, Id result_type, Id base, Args&&... args) { |
| 48 | if (ctx.stage == Stage::TessellationControl) { | 67 | if (ctx.stage == Stage::TessellationControl) { |
| @@ -74,6 +93,13 @@ std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) { | |||
| 74 | return OutputAccessChain(ctx, ctx.output_f32, info.id, index_id); | 93 | return OutputAccessChain(ctx, ctx.output_f32, info.id, index_id); |
| 75 | } | 94 | } |
| 76 | } | 95 | } |
| 96 | if (IsFixedFncTexture(attr)) { | ||
| 97 | const u32 index{FixedFncTextureAttributeIndex(attr)}; | ||
| 98 | const u32 element{FixedFncTextureAttributeElement(attr)}; | ||
| 99 | const Id element_id{ctx.Const(element)}; | ||
| 100 | return OutputAccessChain(ctx, ctx.output_f32, ctx.output_fixed_fnc_textures[index], | ||
| 101 | element_id); | ||
| 102 | } | ||
| 77 | switch (attr) { | 103 | switch (attr) { |
| 78 | case IR::Attribute::PointSize: | 104 | case IR::Attribute::PointSize: |
| 79 | return ctx.output_point_size; | 105 | return ctx.output_point_size; |
| @@ -85,6 +111,14 @@ std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) { | |||
| 85 | const Id element_id{ctx.Const(element)}; | 111 | const Id element_id{ctx.Const(element)}; |
| 86 | return OutputAccessChain(ctx, ctx.output_f32, ctx.output_position, element_id); | 112 | return OutputAccessChain(ctx, ctx.output_f32, ctx.output_position, element_id); |
| 87 | } | 113 | } |
| 114 | case IR::Attribute::ColorFrontDiffuseR: | ||
| 115 | case IR::Attribute::ColorFrontDiffuseG: | ||
| 116 | case IR::Attribute::ColorFrontDiffuseB: | ||
| 117 | case IR::Attribute::ColorFrontDiffuseA: { | ||
| 118 | const u32 element{static_cast<u32>(attr) % 4}; | ||
| 119 | const Id element_id{ctx.Const(element)}; | ||
| 120 | return OutputAccessChain(ctx, ctx.output_f32, ctx.output_front_color, element_id); | ||
| 121 | } | ||
| 88 | case IR::Attribute::ClipDistance0: | 122 | case IR::Attribute::ClipDistance0: |
| 89 | case IR::Attribute::ClipDistance1: | 123 | case IR::Attribute::ClipDistance1: |
| 90 | case IR::Attribute::ClipDistance2: | 124 | case IR::Attribute::ClipDistance2: |
| @@ -307,6 +341,12 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) { | |||
| 307 | const Id value{ctx.OpLoad(type->id, pointer)}; | 341 | const Id value{ctx.OpLoad(type->id, pointer)}; |
| 308 | return type->needs_cast ? ctx.OpBitcast(ctx.F32[1], value) : value; | 342 | return type->needs_cast ? ctx.OpBitcast(ctx.F32[1], value) : value; |
| 309 | } | 343 | } |
| 344 | if (IsFixedFncTexture(attr)) { | ||
| 345 | const u32 index{FixedFncTextureAttributeIndex(attr)}; | ||
| 346 | const Id attr_id{ctx.input_fixed_fnc_textures[index]}; | ||
| 347 | const Id attr_ptr{AttrPointer(ctx, ctx.input_f32, vertex, attr_id, ctx.Const(element))}; | ||
| 348 | return ctx.OpLoad(ctx.F32[1], attr_ptr); | ||
| 349 | } | ||
| 310 | switch (attr) { | 350 | switch (attr) { |
| 311 | case IR::Attribute::PrimitiveId: | 351 | case IR::Attribute::PrimitiveId: |
| 312 | return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.primitive_id)); | 352 | return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.primitive_id)); |
| @@ -316,6 +356,13 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) { | |||
| 316 | case IR::Attribute::PositionW: | 356 | case IR::Attribute::PositionW: |
| 317 | return ctx.OpLoad(ctx.F32[1], AttrPointer(ctx, ctx.input_f32, vertex, ctx.input_position, | 357 | return ctx.OpLoad(ctx.F32[1], AttrPointer(ctx, ctx.input_f32, vertex, ctx.input_position, |
| 318 | ctx.Const(element))); | 358 | ctx.Const(element))); |
| 359 | case IR::Attribute::ColorFrontDiffuseR: | ||
| 360 | case IR::Attribute::ColorFrontDiffuseG: | ||
| 361 | case IR::Attribute::ColorFrontDiffuseB: | ||
| 362 | case IR::Attribute::ColorFrontDiffuseA: { | ||
| 363 | return ctx.OpLoad(ctx.F32[1], AttrPointer(ctx, ctx.input_f32, vertex, ctx.input_front_color, | ||
| 364 | ctx.Const(element))); | ||
| 365 | } | ||
| 319 | case IR::Attribute::InstanceId: | 366 | case IR::Attribute::InstanceId: |
| 320 | if (ctx.profile.support_vertex_instance_id) { | 367 | if (ctx.profile.support_vertex_instance_id) { |
| 321 | return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.instance_id)); | 368 | return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.instance_id)); |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp index 78b1e1ba7..cef52c56e 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp | |||
| @@ -7,8 +7,13 @@ | |||
| 7 | 7 | ||
| 8 | namespace Shader::Backend::SPIRV { | 8 | namespace Shader::Backend::SPIRV { |
| 9 | namespace { | 9 | namespace { |
| 10 | Id GetThreadId(EmitContext& ctx) { | ||
| 11 | return ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id); | ||
| 12 | } | ||
| 13 | |||
| 10 | Id WarpExtract(EmitContext& ctx, Id value) { | 14 | Id WarpExtract(EmitContext& ctx, Id value) { |
| 11 | const Id local_index{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)}; | 15 | const Id thread_id{GetThreadId(ctx)}; |
| 16 | const Id local_index{ctx.OpShiftRightArithmetic(ctx.U32[1], thread_id, ctx.Const(5U))}; | ||
| 12 | return ctx.OpVectorExtractDynamic(ctx.U32[1], value, local_index); | 17 | return ctx.OpVectorExtractDynamic(ctx.U32[1], value, local_index); |
| 13 | } | 18 | } |
| 14 | 19 | ||
| @@ -48,10 +53,17 @@ Id SelectValue(EmitContext& ctx, Id in_range, Id value, Id src_thread_id) { | |||
| 48 | return ctx.OpSelect(ctx.U32[1], in_range, | 53 | return ctx.OpSelect(ctx.U32[1], in_range, |
| 49 | ctx.OpSubgroupReadInvocationKHR(ctx.U32[1], value, src_thread_id), value); | 54 | ctx.OpSubgroupReadInvocationKHR(ctx.U32[1], value, src_thread_id), value); |
| 50 | } | 55 | } |
| 56 | |||
| 57 | Id GetUpperClamp(EmitContext& ctx, Id invocation_id, Id clamp) { | ||
| 58 | const Id thirty_two{ctx.Const(32u)}; | ||
| 59 | const Id is_upper_partition{ctx.OpSGreaterThanEqual(ctx.U1, invocation_id, thirty_two)}; | ||
| 60 | const Id upper_clamp{ctx.OpIAdd(ctx.U32[1], thirty_two, clamp)}; | ||
| 61 | return ctx.OpSelect(ctx.U32[1], is_upper_partition, upper_clamp, clamp); | ||
| 62 | } | ||
| 51 | } // Anonymous namespace | 63 | } // Anonymous namespace |
| 52 | 64 | ||
| 53 | Id EmitLaneId(EmitContext& ctx) { | 65 | Id EmitLaneId(EmitContext& ctx) { |
| 54 | const Id id{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)}; | 66 | const Id id{GetThreadId(ctx)}; |
| 55 | if (!ctx.profile.warp_size_potentially_larger_than_guest) { | 67 | if (!ctx.profile.warp_size_potentially_larger_than_guest) { |
| 56 | return id; | 68 | return id; |
| 57 | } | 69 | } |
| @@ -123,7 +135,15 @@ Id EmitSubgroupGeMask(EmitContext& ctx) { | |||
| 123 | Id EmitShuffleIndex(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp, | 135 | Id EmitShuffleIndex(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp, |
| 124 | Id segmentation_mask) { | 136 | Id segmentation_mask) { |
| 125 | const Id not_seg_mask{ctx.OpNot(ctx.U32[1], segmentation_mask)}; | 137 | const Id not_seg_mask{ctx.OpNot(ctx.U32[1], segmentation_mask)}; |
| 126 | const Id thread_id{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)}; | 138 | const Id thread_id{GetThreadId(ctx)}; |
| 139 | if (ctx.profile.warp_size_potentially_larger_than_guest) { | ||
| 140 | const Id thirty_two{ctx.Const(32u)}; | ||
| 141 | const Id is_upper_partition{ctx.OpSGreaterThanEqual(ctx.U1, thread_id, thirty_two)}; | ||
| 142 | const Id upper_index{ctx.OpIAdd(ctx.U32[1], thirty_two, index)}; | ||
| 143 | const Id upper_clamp{ctx.OpIAdd(ctx.U32[1], thirty_two, clamp)}; | ||
| 144 | index = ctx.OpSelect(ctx.U32[1], is_upper_partition, upper_index, index); | ||
| 145 | clamp = ctx.OpSelect(ctx.U32[1], is_upper_partition, upper_clamp, clamp); | ||
| 146 | } | ||
| 127 | const Id min_thread_id{ComputeMinThreadId(ctx, thread_id, segmentation_mask)}; | 147 | const Id min_thread_id{ComputeMinThreadId(ctx, thread_id, segmentation_mask)}; |
| 128 | const Id max_thread_id{ComputeMaxThreadId(ctx, min_thread_id, clamp, not_seg_mask)}; | 148 | const Id max_thread_id{ComputeMaxThreadId(ctx, min_thread_id, clamp, not_seg_mask)}; |
| 129 | 149 | ||
| @@ -137,7 +157,10 @@ Id EmitShuffleIndex(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id cla | |||
| 137 | 157 | ||
| 138 | Id EmitShuffleUp(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp, | 158 | Id EmitShuffleUp(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp, |
| 139 | Id segmentation_mask) { | 159 | Id segmentation_mask) { |
| 140 | const Id thread_id{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)}; | 160 | const Id thread_id{GetThreadId(ctx)}; |
| 161 | if (ctx.profile.warp_size_potentially_larger_than_guest) { | ||
| 162 | clamp = GetUpperClamp(ctx, thread_id, clamp); | ||
| 163 | } | ||
| 141 | const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)}; | 164 | const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)}; |
| 142 | const Id src_thread_id{ctx.OpISub(ctx.U32[1], thread_id, index)}; | 165 | const Id src_thread_id{ctx.OpISub(ctx.U32[1], thread_id, index)}; |
| 143 | const Id in_range{ctx.OpSGreaterThanEqual(ctx.U1, src_thread_id, max_thread_id)}; | 166 | const Id in_range{ctx.OpSGreaterThanEqual(ctx.U1, src_thread_id, max_thread_id)}; |
| @@ -148,7 +171,10 @@ Id EmitShuffleUp(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp, | |||
| 148 | 171 | ||
| 149 | Id EmitShuffleDown(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp, | 172 | Id EmitShuffleDown(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp, |
| 150 | Id segmentation_mask) { | 173 | Id segmentation_mask) { |
| 151 | const Id thread_id{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)}; | 174 | const Id thread_id{GetThreadId(ctx)}; |
| 175 | if (ctx.profile.warp_size_potentially_larger_than_guest) { | ||
| 176 | clamp = GetUpperClamp(ctx, thread_id, clamp); | ||
| 177 | } | ||
| 152 | const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)}; | 178 | const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)}; |
| 153 | const Id src_thread_id{ctx.OpIAdd(ctx.U32[1], thread_id, index)}; | 179 | const Id src_thread_id{ctx.OpIAdd(ctx.U32[1], thread_id, index)}; |
| 154 | const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)}; | 180 | const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)}; |
| @@ -159,7 +185,10 @@ Id EmitShuffleDown(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clam | |||
| 159 | 185 | ||
| 160 | Id EmitShuffleButterfly(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp, | 186 | Id EmitShuffleButterfly(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp, |
| 161 | Id segmentation_mask) { | 187 | Id segmentation_mask) { |
| 162 | const Id thread_id{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)}; | 188 | const Id thread_id{GetThreadId(ctx)}; |
| 189 | if (ctx.profile.warp_size_potentially_larger_than_guest) { | ||
| 190 | clamp = GetUpperClamp(ctx, thread_id, clamp); | ||
| 191 | } | ||
| 163 | const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)}; | 192 | const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)}; |
| 164 | const Id src_thread_id{ctx.OpBitwiseXor(ctx.U32[1], thread_id, index)}; | 193 | const Id src_thread_id{ctx.OpBitwiseXor(ctx.U32[1], thread_id, index)}; |
| 165 | const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)}; | 194 | const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)}; |
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 2f6cdd216..269db21a5 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt | |||
| @@ -231,6 +231,7 @@ endif() | |||
| 231 | 231 | ||
| 232 | target_include_directories(video_core PRIVATE ${FFmpeg_INCLUDE_DIR}) | 232 | target_include_directories(video_core PRIVATE ${FFmpeg_INCLUDE_DIR}) |
| 233 | target_link_libraries(video_core PRIVATE ${FFmpeg_LIBRARIES}) | 233 | target_link_libraries(video_core PRIVATE ${FFmpeg_LIBRARIES}) |
| 234 | target_link_options(video_core PRIVATE ${FFmpeg_LDFLAGS}) | ||
| 234 | 235 | ||
| 235 | add_dependencies(video_core host_shaders) | 236 | add_dependencies(video_core host_shaders) |
| 236 | target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE}) | 237 | target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE}) |
diff --git a/src/video_core/command_classes/codecs/codec.cpp b/src/video_core/command_classes/codecs/codec.cpp index f798a0053..61966cbfe 100644 --- a/src/video_core/command_classes/codecs/codec.cpp +++ b/src/video_core/command_classes/codecs/codec.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include <fstream> | 5 | #include <fstream> |
| 6 | #include <vector> | 6 | #include <vector> |
| 7 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 8 | #include "common/settings.h" | ||
| 8 | #include "video_core/command_classes/codecs/codec.h" | 9 | #include "video_core/command_classes/codecs/codec.h" |
| 9 | #include "video_core/command_classes/codecs/h264.h" | 10 | #include "video_core/command_classes/codecs/h264.h" |
| 10 | #include "video_core/command_classes/codecs/vp9.h" | 11 | #include "video_core/command_classes/codecs/vp9.h" |
| @@ -16,108 +17,146 @@ extern "C" { | |||
| 16 | } | 17 | } |
| 17 | 18 | ||
| 18 | namespace Tegra { | 19 | namespace Tegra { |
| 19 | #if defined(LIBVA_FOUND) | ||
| 20 | // Hardware acceleration code from FFmpeg/doc/examples/hw_decode.c originally under MIT license | ||
| 21 | namespace { | 20 | namespace { |
| 22 | constexpr std::array<const char*, 2> VAAPI_DRIVERS = { | 21 | constexpr AVPixelFormat PREFERRED_GPU_FMT = AV_PIX_FMT_NV12; |
| 23 | "i915", | 22 | constexpr AVPixelFormat PREFERRED_CPU_FMT = AV_PIX_FMT_YUV420P; |
| 24 | "amdgpu", | 23 | |
| 25 | }; | 24 | void AVPacketDeleter(AVPacket* ptr) { |
| 25 | av_packet_free(&ptr); | ||
| 26 | } | ||
| 26 | 27 | ||
| 27 | AVPixelFormat GetHwFormat(AVCodecContext*, const AVPixelFormat* pix_fmts) { | 28 | using AVPacketPtr = std::unique_ptr<AVPacket, decltype(&AVPacketDeleter)>; |
| 29 | |||
| 30 | AVPixelFormat GetGpuFormat(AVCodecContext* av_codec_ctx, const AVPixelFormat* pix_fmts) { | ||
| 28 | for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) { | 31 | for (const AVPixelFormat* p = pix_fmts; *p != AV_PIX_FMT_NONE; ++p) { |
| 29 | if (*p == AV_PIX_FMT_VAAPI) { | 32 | if (*p == av_codec_ctx->pix_fmt) { |
| 30 | return AV_PIX_FMT_VAAPI; | 33 | return av_codec_ctx->pix_fmt; |
| 31 | } | 34 | } |
| 32 | } | 35 | } |
| 33 | LOG_INFO(Service_NVDRV, "Could not find compatible GPU AV format, falling back to CPU"); | 36 | LOG_INFO(Service_NVDRV, "Could not find compatible GPU AV format, falling back to CPU"); |
| 34 | return *pix_fmts; | 37 | av_buffer_unref(&av_codec_ctx->hw_device_ctx); |
| 38 | av_codec_ctx->pix_fmt = PREFERRED_CPU_FMT; | ||
| 39 | return PREFERRED_CPU_FMT; | ||
| 40 | } | ||
| 41 | } // namespace | ||
| 42 | |||
| 43 | void AVFrameDeleter(AVFrame* ptr) { | ||
| 44 | av_frame_free(&ptr); | ||
| 35 | } | 45 | } |
| 36 | 46 | ||
| 37 | bool CreateVaapiHwdevice(AVBufferRef** av_hw_device) { | 47 | Codec::Codec(GPU& gpu_, const NvdecCommon::NvdecRegisters& regs) |
| 48 | : gpu(gpu_), state{regs}, h264_decoder(std::make_unique<Decoder::H264>(gpu)), | ||
| 49 | vp9_decoder(std::make_unique<Decoder::VP9>(gpu)) {} | ||
| 50 | |||
| 51 | Codec::~Codec() { | ||
| 52 | if (!initialized) { | ||
| 53 | return; | ||
| 54 | } | ||
| 55 | // Free libav memory | ||
| 56 | avcodec_free_context(&av_codec_ctx); | ||
| 57 | av_buffer_unref(&av_gpu_decoder); | ||
| 58 | } | ||
| 59 | |||
| 60 | bool Codec::CreateGpuAvDevice() { | ||
| 61 | #if defined(LIBVA_FOUND) | ||
| 62 | static constexpr std::array<const char*, 3> VAAPI_DRIVERS = { | ||
| 63 | "i915", | ||
| 64 | "iHD", | ||
| 65 | "amdgpu", | ||
| 66 | }; | ||
| 38 | AVDictionary* hwdevice_options = nullptr; | 67 | AVDictionary* hwdevice_options = nullptr; |
| 39 | av_dict_set(&hwdevice_options, "connection_type", "drm", 0); | 68 | av_dict_set(&hwdevice_options, "connection_type", "drm", 0); |
| 40 | for (const auto& driver : VAAPI_DRIVERS) { | 69 | for (const auto& driver : VAAPI_DRIVERS) { |
| 41 | av_dict_set(&hwdevice_options, "kernel_driver", driver, 0); | 70 | 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, | 71 | const int hwdevice_error = av_hwdevice_ctx_create(&av_gpu_decoder, AV_HWDEVICE_TYPE_VAAPI, |
| 43 | nullptr, hwdevice_options, 0); | 72 | nullptr, hwdevice_options, 0); |
| 44 | if (hwdevice_error >= 0) { | 73 | if (hwdevice_error >= 0) { |
| 45 | LOG_INFO(Service_NVDRV, "Using VA-API with {}", driver); | 74 | LOG_INFO(Service_NVDRV, "Using VA-API with {}", driver); |
| 46 | av_dict_free(&hwdevice_options); | 75 | av_dict_free(&hwdevice_options); |
| 76 | av_codec_ctx->pix_fmt = AV_PIX_FMT_VAAPI; | ||
| 47 | return true; | 77 | return true; |
| 48 | } | 78 | } |
| 49 | LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed {}", hwdevice_error); | 79 | LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed {}", hwdevice_error); |
| 50 | } | 80 | } |
| 51 | LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed for all drivers"); | 81 | LOG_DEBUG(Service_NVDRV, "VA-API av_hwdevice_ctx_create failed for all drivers"); |
| 52 | av_dict_free(&hwdevice_options); | 82 | av_dict_free(&hwdevice_options); |
| 53 | return false; | ||
| 54 | } | ||
| 55 | } // namespace | ||
| 56 | #endif | 83 | #endif |
| 57 | 84 | static constexpr auto HW_CONFIG_METHOD = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX; | |
| 58 | void AVFrameDeleter(AVFrame* ptr) { | 85 | static constexpr std::array GPU_DECODER_TYPES{ |
| 59 | av_frame_free(&ptr); | 86 | AV_HWDEVICE_TYPE_CUDA, |
| 87 | #ifdef _WIN32 | ||
| 88 | AV_HWDEVICE_TYPE_D3D11VA, | ||
| 89 | #else | ||
| 90 | AV_HWDEVICE_TYPE_VDPAU, | ||
| 91 | #endif | ||
| 92 | }; | ||
| 93 | for (const auto& type : GPU_DECODER_TYPES) { | ||
| 94 | const int hwdevice_res = av_hwdevice_ctx_create(&av_gpu_decoder, type, nullptr, nullptr, 0); | ||
| 95 | if (hwdevice_res < 0) { | ||
| 96 | LOG_DEBUG(Service_NVDRV, "{} av_hwdevice_ctx_create failed {}", | ||
| 97 | av_hwdevice_get_type_name(type), hwdevice_res); | ||
| 98 | continue; | ||
| 99 | } | ||
| 100 | for (int i = 0;; i++) { | ||
| 101 | const AVCodecHWConfig* config = avcodec_get_hw_config(av_codec, i); | ||
| 102 | if (!config) { | ||
| 103 | LOG_DEBUG(Service_NVDRV, "{} decoder does not support device type {}.", | ||
| 104 | av_codec->name, av_hwdevice_get_type_name(type)); | ||
| 105 | break; | ||
| 106 | } | ||
| 107 | if (config->methods & HW_CONFIG_METHOD && config->device_type == type) { | ||
| 108 | av_codec_ctx->pix_fmt = config->pix_fmt; | ||
| 109 | LOG_INFO(Service_NVDRV, "Using {} GPU decoder", av_hwdevice_get_type_name(type)); | ||
| 110 | return true; | ||
| 111 | } | ||
| 112 | } | ||
| 113 | } | ||
| 114 | return false; | ||
| 60 | } | 115 | } |
| 61 | 116 | ||
| 62 | Codec::Codec(GPU& gpu_, const NvdecCommon::NvdecRegisters& regs) | 117 | void Codec::InitializeAvCodecContext() { |
| 63 | : gpu(gpu_), state{regs}, h264_decoder(std::make_unique<Decoder::H264>(gpu)), | 118 | av_codec_ctx = avcodec_alloc_context3(av_codec); |
| 64 | vp9_decoder(std::make_unique<Decoder::VP9>(gpu)) {} | 119 | av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0); |
| 65 | |||
| 66 | Codec::~Codec() { | ||
| 67 | if (!initialized) { | ||
| 68 | return; | ||
| 69 | } | ||
| 70 | // Free libav memory | ||
| 71 | avcodec_send_packet(av_codec_ctx, nullptr); | ||
| 72 | AVFrame* av_frame = av_frame_alloc(); | ||
| 73 | avcodec_receive_frame(av_codec_ctx, av_frame); | ||
| 74 | avcodec_flush_buffers(av_codec_ctx); | ||
| 75 | av_frame_free(&av_frame); | ||
| 76 | avcodec_close(av_codec_ctx); | ||
| 77 | av_buffer_unref(&av_hw_device); | ||
| 78 | } | 120 | } |
| 79 | 121 | ||
| 80 | void Codec::InitializeHwdec() { | 122 | void Codec::InitializeGpuDecoder() { |
| 81 | // Prioritize integrated GPU to mitigate bandwidth bottlenecks | 123 | if (!CreateGpuAvDevice()) { |
| 82 | #if defined(LIBVA_FOUND) | 124 | av_buffer_unref(&av_gpu_decoder); |
| 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; | 125 | return; |
| 89 | } | 126 | } |
| 90 | #endif | 127 | auto* hw_device_ctx = av_buffer_ref(av_gpu_decoder); |
| 91 | // TODO more GPU accelerated decoders | 128 | ASSERT_MSG(hw_device_ctx, "av_buffer_ref failed"); |
| 129 | av_codec_ctx->hw_device_ctx = hw_device_ctx; | ||
| 130 | av_codec_ctx->get_format = GetGpuFormat; | ||
| 92 | } | 131 | } |
| 93 | 132 | ||
| 94 | void Codec::Initialize() { | 133 | void Codec::Initialize() { |
| 95 | AVCodecID codec; | 134 | const AVCodecID codec = [&] { |
| 96 | switch (current_codec) { | 135 | switch (current_codec) { |
| 97 | case NvdecCommon::VideoCodec::H264: | 136 | case NvdecCommon::VideoCodec::H264: |
| 98 | codec = AV_CODEC_ID_H264; | 137 | return AV_CODEC_ID_H264; |
| 99 | break; | 138 | case NvdecCommon::VideoCodec::Vp9: |
| 100 | case NvdecCommon::VideoCodec::Vp9: | 139 | return AV_CODEC_ID_VP9; |
| 101 | codec = AV_CODEC_ID_VP9; | 140 | default: |
| 102 | break; | 141 | UNIMPLEMENTED_MSG("Unknown codec {}", current_codec); |
| 103 | default: | 142 | return AV_CODEC_ID_NONE; |
| 104 | UNIMPLEMENTED_MSG("Unknown codec {}", current_codec); | 143 | } |
| 144 | }(); | ||
| 145 | av_codec = avcodec_find_decoder(codec); | ||
| 146 | |||
| 147 | InitializeAvCodecContext(); | ||
| 148 | if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::GPU) { | ||
| 149 | InitializeGpuDecoder(); | ||
| 150 | } | ||
| 151 | if (const int res = avcodec_open2(av_codec_ctx, av_codec, nullptr); res < 0) { | ||
| 152 | LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed with result {}", res); | ||
| 153 | avcodec_free_context(&av_codec_ctx); | ||
| 154 | av_buffer_unref(&av_gpu_decoder); | ||
| 105 | return; | 155 | return; |
| 106 | } | 156 | } |
| 107 | av_codec = avcodec_find_decoder(codec); | ||
| 108 | av_codec_ctx = avcodec_alloc_context3(av_codec); | ||
| 109 | av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0); | ||
| 110 | InitializeHwdec(); | ||
| 111 | if (!av_codec_ctx->hw_device_ctx) { | 157 | if (!av_codec_ctx->hw_device_ctx) { |
| 112 | LOG_INFO(Service_NVDRV, "Using FFmpeg software decoding"); | 158 | LOG_INFO(Service_NVDRV, "Using FFmpeg software decoding"); |
| 113 | } | 159 | } |
| 114 | const auto av_error = avcodec_open2(av_codec_ctx, av_codec, nullptr); | ||
| 115 | if (av_error < 0) { | ||
| 116 | LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed."); | ||
| 117 | avcodec_close(av_codec_ctx); | ||
| 118 | av_buffer_unref(&av_hw_device); | ||
| 119 | return; | ||
| 120 | } | ||
| 121 | initialized = true; | 160 | initialized = true; |
| 122 | } | 161 | } |
| 123 | 162 | ||
| @@ -133,6 +172,9 @@ void Codec::Decode() { | |||
| 133 | if (is_first_frame) { | 172 | if (is_first_frame) { |
| 134 | Initialize(); | 173 | Initialize(); |
| 135 | } | 174 | } |
| 175 | if (!initialized) { | ||
| 176 | return; | ||
| 177 | } | ||
| 136 | bool vp9_hidden_frame = false; | 178 | bool vp9_hidden_frame = false; |
| 137 | std::vector<u8> frame_data; | 179 | std::vector<u8> frame_data; |
| 138 | if (current_codec == NvdecCommon::VideoCodec::H264) { | 180 | if (current_codec == NvdecCommon::VideoCodec::H264) { |
| @@ -141,50 +183,48 @@ void Codec::Decode() { | |||
| 141 | frame_data = vp9_decoder->ComposeFrameHeader(state); | 183 | frame_data = vp9_decoder->ComposeFrameHeader(state); |
| 142 | vp9_hidden_frame = vp9_decoder->WasFrameHidden(); | 184 | vp9_hidden_frame = vp9_decoder->WasFrameHidden(); |
| 143 | } | 185 | } |
| 144 | AVPacket packet{}; | 186 | AVPacketPtr packet{av_packet_alloc(), AVPacketDeleter}; |
| 145 | av_init_packet(&packet); | 187 | if (!packet) { |
| 146 | packet.data = frame_data.data(); | 188 | LOG_ERROR(Service_NVDRV, "av_packet_alloc failed"); |
| 147 | packet.size = static_cast<s32>(frame_data.size()); | 189 | return; |
| 148 | if (const int ret = avcodec_send_packet(av_codec_ctx, &packet); ret) { | 190 | } |
| 149 | LOG_DEBUG(Service_NVDRV, "avcodec_send_packet error {}", ret); | 191 | packet->data = frame_data.data(); |
| 192 | packet->size = static_cast<s32>(frame_data.size()); | ||
| 193 | if (const int res = avcodec_send_packet(av_codec_ctx, packet.get()); res != 0) { | ||
| 194 | LOG_DEBUG(Service_NVDRV, "avcodec_send_packet error {}", res); | ||
| 150 | return; | 195 | return; |
| 151 | } | 196 | } |
| 152 | // Only receive/store visible frames | 197 | // Only receive/store visible frames |
| 153 | if (vp9_hidden_frame) { | 198 | if (vp9_hidden_frame) { |
| 154 | return; | 199 | return; |
| 155 | } | 200 | } |
| 156 | AVFrame* hw_frame = av_frame_alloc(); | 201 | AVFramePtr initial_frame{av_frame_alloc(), AVFrameDeleter}; |
| 157 | AVFrame* sw_frame = hw_frame; | 202 | AVFramePtr final_frame{nullptr, AVFrameDeleter}; |
| 158 | ASSERT_MSG(hw_frame, "av_frame_alloc hw_frame failed"); | 203 | ASSERT_MSG(initial_frame, "av_frame_alloc initial_frame failed"); |
| 159 | if (const int ret = avcodec_receive_frame(av_codec_ctx, hw_frame); ret) { | 204 | if (const int ret = avcodec_receive_frame(av_codec_ctx, initial_frame.get()); ret) { |
| 160 | LOG_DEBUG(Service_NVDRV, "avcodec_receive_frame error {}", ret); | 205 | LOG_DEBUG(Service_NVDRV, "avcodec_receive_frame error {}", ret); |
| 161 | av_frame_free(&hw_frame); | ||
| 162 | return; | 206 | return; |
| 163 | } | 207 | } |
| 164 | if (!hw_frame->width || !hw_frame->height) { | 208 | if (initial_frame->width == 0 || initial_frame->height == 0) { |
| 165 | LOG_WARNING(Service_NVDRV, "Zero width or height in frame"); | 209 | LOG_WARNING(Service_NVDRV, "Zero width or height in frame"); |
| 166 | av_frame_free(&hw_frame); | ||
| 167 | return; | 210 | return; |
| 168 | } | 211 | } |
| 169 | #if defined(LIBVA_FOUND) | 212 | if (av_codec_ctx->hw_device_ctx) { |
| 170 | // Hardware acceleration code from FFmpeg/doc/examples/hw_decode.c under MIT license | 213 | final_frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter}; |
| 171 | if (hw_frame->format == AV_PIX_FMT_VAAPI) { | 214 | ASSERT_MSG(final_frame, "av_frame_alloc final_frame failed"); |
| 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 | 215 | // 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 | 216 | // because Intel drivers crash unless using AV_PIX_FMT_NV12 |
| 176 | sw_frame->format = AV_PIX_FMT_NV12; | 217 | final_frame->format = PREFERRED_GPU_FMT; |
| 177 | const int transfer_data_ret = av_hwframe_transfer_data(sw_frame, hw_frame, 0); | 218 | const int ret = av_hwframe_transfer_data(final_frame.get(), initial_frame.get(), 0); |
| 178 | ASSERT_MSG(!transfer_data_ret, "av_hwframe_transfer_data error {}", transfer_data_ret); | 219 | ASSERT_MSG(!ret, "av_hwframe_transfer_data error {}", ret); |
| 179 | av_frame_free(&hw_frame); | 220 | } else { |
| 221 | final_frame = std::move(initial_frame); | ||
| 180 | } | 222 | } |
| 181 | #endif | 223 | if (final_frame->format != PREFERRED_CPU_FMT && final_frame->format != PREFERRED_GPU_FMT) { |
| 182 | if (sw_frame->format != AV_PIX_FMT_YUV420P && sw_frame->format != AV_PIX_FMT_NV12) { | 224 | UNIMPLEMENTED_MSG("Unexpected video format: {}", final_frame->format); |
| 183 | UNIMPLEMENTED_MSG("Unexpected video format from host graphics: {}", sw_frame->format); | ||
| 184 | av_frame_free(&sw_frame); | ||
| 185 | return; | 225 | return; |
| 186 | } | 226 | } |
| 187 | av_frames.push(AVFramePtr{sw_frame, AVFrameDeleter}); | 227 | av_frames.push(std::move(final_frame)); |
| 188 | if (av_frames.size() > 10) { | 228 | if (av_frames.size() > 10) { |
| 189 | LOG_TRACE(Service_NVDRV, "av_frames.push overflow dropped frame"); | 229 | LOG_TRACE(Service_NVDRV, "av_frames.push overflow dropped frame"); |
| 190 | av_frames.pop(); | 230 | av_frames.pop(); |
diff --git a/src/video_core/command_classes/codecs/codec.h b/src/video_core/command_classes/codecs/codec.h index 71936203f..f9a80886f 100644 --- a/src/video_core/command_classes/codecs/codec.h +++ b/src/video_core/command_classes/codecs/codec.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <string_view> | ||
| 8 | #include <queue> | 9 | #include <queue> |
| 9 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 10 | #include "video_core/command_classes/nvdec_common.h" | 11 | #include "video_core/command_classes/nvdec_common.h" |
| @@ -50,18 +51,23 @@ public: | |||
| 50 | 51 | ||
| 51 | /// Returns the value of current_codec | 52 | /// Returns the value of current_codec |
| 52 | [[nodiscard]] NvdecCommon::VideoCodec GetCurrentCodec() const; | 53 | [[nodiscard]] NvdecCommon::VideoCodec GetCurrentCodec() const; |
| 54 | |||
| 53 | /// Return name of the current codec | 55 | /// Return name of the current codec |
| 54 | [[nodiscard]] std::string_view GetCurrentCodecName() const; | 56 | [[nodiscard]] std::string_view GetCurrentCodecName() const; |
| 55 | 57 | ||
| 56 | private: | 58 | private: |
| 57 | void InitializeHwdec(); | 59 | void InitializeAvCodecContext(); |
| 60 | |||
| 61 | void InitializeGpuDecoder(); | ||
| 62 | |||
| 63 | bool CreateGpuAvDevice(); | ||
| 58 | 64 | ||
| 59 | bool initialized{}; | 65 | bool initialized{}; |
| 60 | NvdecCommon::VideoCodec current_codec{NvdecCommon::VideoCodec::None}; | 66 | NvdecCommon::VideoCodec current_codec{NvdecCommon::VideoCodec::None}; |
| 61 | 67 | ||
| 62 | AVCodec* av_codec{nullptr}; | 68 | AVCodec* av_codec{nullptr}; |
| 63 | AVBufferRef* av_hw_device{nullptr}; | ||
| 64 | AVCodecContext* av_codec_ctx{nullptr}; | 69 | AVCodecContext* av_codec_ctx{nullptr}; |
| 70 | AVBufferRef* av_gpu_decoder{nullptr}; | ||
| 65 | 71 | ||
| 66 | GPU& gpu; | 72 | GPU& gpu; |
| 67 | const NvdecCommon::NvdecRegisters& state; | 73 | const NvdecCommon::NvdecRegisters& state; |
diff --git a/src/video_core/command_classes/codecs/h264.cpp b/src/video_core/command_classes/codecs/h264.cpp index 5fb6d45ee..51ee14c13 100644 --- a/src/video_core/command_classes/codecs/h264.cpp +++ b/src/video_core/command_classes/codecs/h264.cpp | |||
| @@ -95,7 +95,8 @@ const std::vector<u8>& H264::ComposeFrameHeader(const NvdecCommon::NvdecRegister | |||
| 95 | const s32 pic_height = context.h264_parameter_set.frame_height_in_map_units / | 95 | const s32 pic_height = context.h264_parameter_set.frame_height_in_map_units / |
| 96 | (context.h264_parameter_set.frame_mbs_only_flag ? 1 : 2); | 96 | (context.h264_parameter_set.frame_mbs_only_flag ? 1 : 2); |
| 97 | 97 | ||
| 98 | writer.WriteUe(16); | 98 | // TODO (ameerj): Where do we get this number, it seems to be particular for each stream |
| 99 | writer.WriteUe(6); // Max number of reference frames | ||
| 99 | writer.WriteBit(false); | 100 | writer.WriteBit(false); |
| 100 | writer.WriteUe(context.h264_parameter_set.pic_width_in_mbs - 1); | 101 | writer.WriteUe(context.h264_parameter_set.pic_width_in_mbs - 1); |
| 101 | writer.WriteUe(pic_height - 1); | 102 | writer.WriteUe(pic_height - 1); |
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 1aa43523a..7f4ca6282 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h | |||
| @@ -475,10 +475,10 @@ public: | |||
| 475 | 475 | ||
| 476 | // These values are used by Nouveau and some games. | 476 | // These values are used by Nouveau and some games. |
| 477 | AddGL = 0x8006, | 477 | AddGL = 0x8006, |
| 478 | SubtractGL = 0x8007, | 478 | MinGL = 0x8007, |
| 479 | ReverseSubtractGL = 0x8008, | 479 | MaxGL = 0x8008, |
| 480 | MinGL = 0x800a, | 480 | SubtractGL = 0x800a, |
| 481 | MaxGL = 0x800b | 481 | ReverseSubtractGL = 0x800b |
| 482 | }; | 482 | }; |
| 483 | 483 | ||
| 484 | enum class Factor : u32 { | 484 | enum class Factor : u32 { |
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index c60ed6453..dce00e829 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp | |||
| @@ -2,6 +2,8 @@ | |||
| 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 | |||
| 5 | #include "common/alignment.h" | 7 | #include "common/alignment.h" |
| 6 | #include "common/assert.h" | 8 | #include "common/assert.h" |
| 7 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index b0e14182e..02682bd76 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp | |||
| @@ -293,6 +293,8 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading, | |||
| 293 | }}; | 293 | }}; |
| 294 | LoadPipelines(stop_loading, shader_cache_filename, CACHE_VERSION, load_compute, load_graphics); | 294 | LoadPipelines(stop_loading, shader_cache_filename, CACHE_VERSION, load_compute, load_graphics); |
| 295 | 295 | ||
| 296 | LOG_INFO(Render_OpenGL, "Total Pipeline Count: {}", state.total); | ||
| 297 | |||
| 296 | std::unique_lock lock{state.mutex}; | 298 | std::unique_lock lock{state.mutex}; |
| 297 | callback(VideoCore::LoadCallbackStage::Build, 0, state.total); | 299 | callback(VideoCore::LoadCallbackStage::Build, 0, state.total); |
| 298 | state.has_loaded = true; | 300 | state.has_loaded = true; |
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 7c9b0d6db..9ff0a28cd 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp | |||
| @@ -164,7 +164,8 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | |||
| 164 | blit_screen.Recreate(); | 164 | blit_screen.Recreate(); |
| 165 | } | 165 | } |
| 166 | const VkSemaphore render_semaphore = blit_screen.DrawToSwapchain(*framebuffer, use_accelerated); | 166 | const VkSemaphore render_semaphore = blit_screen.DrawToSwapchain(*framebuffer, use_accelerated); |
| 167 | scheduler.Flush(render_semaphore); | 167 | const VkSemaphore present_semaphore = swapchain.CurrentPresentSemaphore(); |
| 168 | scheduler.Flush(render_semaphore, present_semaphore); | ||
| 168 | scheduler.WaitWorker(); | 169 | scheduler.WaitWorker(); |
| 169 | swapchain.Present(render_semaphore); | 170 | swapchain.Present(render_semaphore); |
| 170 | 171 | ||
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index cb0580182..888bc7392 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp | |||
| @@ -358,7 +358,7 @@ void VKBlitScreen::CreateDescriptorPool() { | |||
| 358 | void VKBlitScreen::CreateRenderPass() { | 358 | void VKBlitScreen::CreateRenderPass() { |
| 359 | const VkAttachmentDescription color_attachment{ | 359 | const VkAttachmentDescription color_attachment{ |
| 360 | .flags = 0, | 360 | .flags = 0, |
| 361 | .format = swapchain.GetImageFormat(), | 361 | .format = swapchain.GetImageViewFormat(), |
| 362 | .samples = VK_SAMPLE_COUNT_1_BIT, | 362 | .samples = VK_SAMPLE_COUNT_1_BIT, |
| 363 | .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, | 363 | .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, |
| 364 | .storeOp = VK_ATTACHMENT_STORE_OP_STORE, | 364 | .storeOp = VK_ATTACHMENT_STORE_OP_STORE, |
diff --git a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp index 8e77e4796..d87da2a34 100644 --- a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 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> | ||
| 5 | #include <mutex> | 6 | #include <mutex> |
| 6 | #include <span> | 7 | #include <span> |
| 7 | #include <vector> | 8 | #include <vector> |
| @@ -18,7 +19,6 @@ namespace Vulkan { | |||
| 18 | // Prefer small grow rates to avoid saturating the descriptor pool with barely used pipelines | 19 | // Prefer small grow rates to avoid saturating the descriptor pool with barely used pipelines |
| 19 | constexpr size_t SETS_GROW_RATE = 16; | 20 | constexpr size_t SETS_GROW_RATE = 16; |
| 20 | constexpr s32 SCORE_THRESHOLD = 3; | 21 | constexpr s32 SCORE_THRESHOLD = 3; |
| 21 | constexpr u32 SETS_PER_POOL = 64; | ||
| 22 | 22 | ||
| 23 | struct DescriptorBank { | 23 | struct DescriptorBank { |
| 24 | DescriptorBankInfo info; | 24 | DescriptorBankInfo info; |
| @@ -58,11 +58,12 @@ static DescriptorBankInfo MakeBankInfo(std::span<const Shader::Info> infos) { | |||
| 58 | static void AllocatePool(const Device& device, DescriptorBank& bank) { | 58 | static void AllocatePool(const Device& device, DescriptorBank& bank) { |
| 59 | std::array<VkDescriptorPoolSize, 6> pool_sizes; | 59 | std::array<VkDescriptorPoolSize, 6> pool_sizes; |
| 60 | size_t pool_cursor{}; | 60 | size_t pool_cursor{}; |
| 61 | const u32 sets_per_pool = device.GetSetsPerPool(); | ||
| 61 | const auto add = [&](VkDescriptorType type, u32 count) { | 62 | const auto add = [&](VkDescriptorType type, u32 count) { |
| 62 | if (count > 0) { | 63 | if (count > 0) { |
| 63 | pool_sizes[pool_cursor++] = { | 64 | pool_sizes[pool_cursor++] = { |
| 64 | .type = type, | 65 | .type = type, |
| 65 | .descriptorCount = count * SETS_PER_POOL, | 66 | .descriptorCount = count * sets_per_pool, |
| 66 | }; | 67 | }; |
| 67 | } | 68 | } |
| 68 | }; | 69 | }; |
| @@ -77,7 +78,7 @@ static void AllocatePool(const Device& device, DescriptorBank& bank) { | |||
| 77 | .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, | 78 | .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, |
| 78 | .pNext = nullptr, | 79 | .pNext = nullptr, |
| 79 | .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, | 80 | .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, |
| 80 | .maxSets = SETS_PER_POOL, | 81 | .maxSets = sets_per_pool, |
| 81 | .poolSizeCount = static_cast<u32>(pool_cursor), | 82 | .poolSizeCount = static_cast<u32>(pool_cursor), |
| 82 | .pPoolSizes = std::data(pool_sizes), | 83 | .pPoolSizes = std::data(pool_sizes), |
| 83 | })); | 84 | })); |
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 31bfbcb06..eb8b4e08b 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | |||
| @@ -447,6 +447,8 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading | |||
| 447 | VideoCommon::LoadPipelines(stop_loading, pipeline_cache_filename, CACHE_VERSION, load_compute, | 447 | VideoCommon::LoadPipelines(stop_loading, pipeline_cache_filename, CACHE_VERSION, load_compute, |
| 448 | load_graphics); | 448 | load_graphics); |
| 449 | 449 | ||
| 450 | LOG_INFO(Render_Vulkan, "Total Pipeline Count: {}", state.total); | ||
| 451 | |||
| 450 | std::unique_lock lock{state.mutex}; | 452 | std::unique_lock lock{state.mutex}; |
| 451 | callback(VideoCore::LoadCallbackStage::Build, 0, state.total); | 453 | callback(VideoCore::LoadCallbackStage::Build, 0, state.total); |
| 452 | state.has_loaded = true; | 454 | state.has_loaded = true; |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 3ac18ea54..3bcd6d6cc 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp | |||
| @@ -228,9 +228,7 @@ void RasterizerVulkan::Clear() { | |||
| 228 | }; | 228 | }; |
| 229 | 229 | ||
| 230 | const u32 color_attachment = regs.clear_buffers.RT; | 230 | const u32 color_attachment = regs.clear_buffers.RT; |
| 231 | const auto attachment_aspect_mask = framebuffer->ImageRanges()[color_attachment].aspectMask; | 231 | if (use_color && framebuffer->HasAspectColorBit(color_attachment)) { |
| 232 | const bool is_color_rt = (attachment_aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) != 0; | ||
| 233 | if (use_color && is_color_rt) { | ||
| 234 | VkClearValue clear_value; | 232 | VkClearValue clear_value; |
| 235 | std::memcpy(clear_value.color.float32, regs.clear_color, sizeof(regs.clear_color)); | 233 | std::memcpy(clear_value.color.float32, regs.clear_color, sizeof(regs.clear_color)); |
| 236 | 234 | ||
| @@ -248,12 +246,15 @@ void RasterizerVulkan::Clear() { | |||
| 248 | return; | 246 | return; |
| 249 | } | 247 | } |
| 250 | VkImageAspectFlags aspect_flags = 0; | 248 | VkImageAspectFlags aspect_flags = 0; |
| 251 | if (use_depth) { | 249 | if (use_depth && framebuffer->HasAspectDepthBit()) { |
| 252 | aspect_flags |= VK_IMAGE_ASPECT_DEPTH_BIT; | 250 | aspect_flags |= VK_IMAGE_ASPECT_DEPTH_BIT; |
| 253 | } | 251 | } |
| 254 | if (use_stencil) { | 252 | if (use_stencil && framebuffer->HasAspectStencilBit()) { |
| 255 | aspect_flags |= VK_IMAGE_ASPECT_STENCIL_BIT; | 253 | aspect_flags |= VK_IMAGE_ASPECT_STENCIL_BIT; |
| 256 | } | 254 | } |
| 255 | if (aspect_flags == 0) { | ||
| 256 | return; | ||
| 257 | } | ||
| 257 | scheduler.Record([clear_depth = regs.clear_depth, clear_stencil = regs.clear_stencil, | 258 | scheduler.Record([clear_depth = regs.clear_depth, clear_stencil = regs.clear_stencil, |
| 258 | clear_rect, aspect_flags](vk::CommandBuffer cmdbuf) { | 259 | clear_rect, aspect_flags](vk::CommandBuffer cmdbuf) { |
| 259 | VkClearAttachment attachment; | 260 | VkClearAttachment attachment; |
| @@ -764,12 +765,7 @@ void RasterizerVulkan::UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs) { | |||
| 764 | const Maxwell::StencilOp zpass = regs.stencil_front_op_zpass; | 765 | const Maxwell::StencilOp zpass = regs.stencil_front_op_zpass; |
| 765 | const Maxwell::ComparisonOp compare = regs.stencil_front_func_func; | 766 | const Maxwell::ComparisonOp compare = regs.stencil_front_func_func; |
| 766 | if (regs.stencil_two_side_enable) { | 767 | if (regs.stencil_two_side_enable) { |
| 767 | scheduler.Record([fail, zfail, zpass, compare](vk::CommandBuffer cmdbuf) { | 768 | // Separate stencil op per face |
| 768 | cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_FRONT_AND_BACK, MaxwellToVK::StencilOp(fail), | ||
| 769 | MaxwellToVK::StencilOp(zpass), MaxwellToVK::StencilOp(zfail), | ||
| 770 | MaxwellToVK::ComparisonOp(compare)); | ||
| 771 | }); | ||
| 772 | } else { | ||
| 773 | const Maxwell::StencilOp back_fail = regs.stencil_back_op_fail; | 769 | const Maxwell::StencilOp back_fail = regs.stencil_back_op_fail; |
| 774 | const Maxwell::StencilOp back_zfail = regs.stencil_back_op_zfail; | 770 | const Maxwell::StencilOp back_zfail = regs.stencil_back_op_zfail; |
| 775 | const Maxwell::StencilOp back_zpass = regs.stencil_back_op_zpass; | 771 | const Maxwell::StencilOp back_zpass = regs.stencil_back_op_zpass; |
| @@ -784,6 +780,13 @@ void RasterizerVulkan::UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs) { | |||
| 784 | MaxwellToVK::StencilOp(back_zfail), | 780 | MaxwellToVK::StencilOp(back_zfail), |
| 785 | MaxwellToVK::ComparisonOp(back_compare)); | 781 | MaxwellToVK::ComparisonOp(back_compare)); |
| 786 | }); | 782 | }); |
| 783 | } else { | ||
| 784 | // Front face defines the stencil op of both faces | ||
| 785 | scheduler.Record([fail, zfail, zpass, compare](vk::CommandBuffer cmdbuf) { | ||
| 786 | cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_FRONT_AND_BACK, MaxwellToVK::StencilOp(fail), | ||
| 787 | MaxwellToVK::StencilOp(zpass), MaxwellToVK::StencilOp(zfail), | ||
| 788 | MaxwellToVK::ComparisonOp(compare)); | ||
| 789 | }); | ||
| 787 | } | 790 | } |
| 788 | } | 791 | } |
| 789 | 792 | ||
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 4840962de..1d438787a 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp | |||
| @@ -55,14 +55,14 @@ VKScheduler::~VKScheduler() { | |||
| 55 | worker_thread.join(); | 55 | worker_thread.join(); |
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | void VKScheduler::Flush(VkSemaphore semaphore) { | 58 | void VKScheduler::Flush(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) { |
| 59 | SubmitExecution(semaphore); | 59 | SubmitExecution(signal_semaphore, wait_semaphore); |
| 60 | AllocateNewContext(); | 60 | AllocateNewContext(); |
| 61 | } | 61 | } |
| 62 | 62 | ||
| 63 | void VKScheduler::Finish(VkSemaphore semaphore) { | 63 | void VKScheduler::Finish(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) { |
| 64 | const u64 presubmit_tick = CurrentTick(); | 64 | const u64 presubmit_tick = CurrentTick(); |
| 65 | SubmitExecution(semaphore); | 65 | SubmitExecution(signal_semaphore, wait_semaphore); |
| 66 | WaitWorker(); | 66 | WaitWorker(); |
| 67 | Wait(presubmit_tick); | 67 | Wait(presubmit_tick); |
| 68 | AllocateNewContext(); | 68 | AllocateNewContext(); |
| @@ -171,37 +171,41 @@ void VKScheduler::AllocateWorkerCommandBuffer() { | |||
| 171 | }); | 171 | }); |
| 172 | } | 172 | } |
| 173 | 173 | ||
| 174 | void VKScheduler::SubmitExecution(VkSemaphore semaphore) { | 174 | void VKScheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) { |
| 175 | EndPendingOperations(); | 175 | EndPendingOperations(); |
| 176 | InvalidateState(); | 176 | InvalidateState(); |
| 177 | 177 | ||
| 178 | const u64 signal_value = master_semaphore->NextTick(); | 178 | const u64 signal_value = master_semaphore->NextTick(); |
| 179 | Record([semaphore, signal_value, this](vk::CommandBuffer cmdbuf) { | 179 | Record([signal_semaphore, wait_semaphore, signal_value, this](vk::CommandBuffer cmdbuf) { |
| 180 | cmdbuf.End(); | 180 | cmdbuf.End(); |
| 181 | |||
| 182 | const u32 num_signal_semaphores = semaphore ? 2U : 1U; | ||
| 183 | |||
| 184 | const u64 wait_value = signal_value - 1; | ||
| 185 | const VkPipelineStageFlags wait_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; | ||
| 186 | |||
| 187 | const VkSemaphore timeline_semaphore = master_semaphore->Handle(); | 181 | const VkSemaphore timeline_semaphore = master_semaphore->Handle(); |
| 182 | |||
| 183 | const u32 num_signal_semaphores = signal_semaphore ? 2U : 1U; | ||
| 188 | const std::array signal_values{signal_value, u64(0)}; | 184 | const std::array signal_values{signal_value, u64(0)}; |
| 189 | const std::array signal_semaphores{timeline_semaphore, semaphore}; | 185 | const std::array signal_semaphores{timeline_semaphore, signal_semaphore}; |
| 186 | |||
| 187 | const u32 num_wait_semaphores = wait_semaphore ? 2U : 1U; | ||
| 188 | const std::array wait_values{signal_value - 1, u64(1)}; | ||
| 189 | const std::array wait_semaphores{timeline_semaphore, wait_semaphore}; | ||
| 190 | static constexpr std::array<VkPipelineStageFlags, 2> wait_stage_masks{ | ||
| 191 | VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, | ||
| 192 | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, | ||
| 193 | }; | ||
| 190 | 194 | ||
| 191 | const VkTimelineSemaphoreSubmitInfoKHR timeline_si{ | 195 | const VkTimelineSemaphoreSubmitInfoKHR timeline_si{ |
| 192 | .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR, | 196 | .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR, |
| 193 | .pNext = nullptr, | 197 | .pNext = nullptr, |
| 194 | .waitSemaphoreValueCount = 1, | 198 | .waitSemaphoreValueCount = num_wait_semaphores, |
| 195 | .pWaitSemaphoreValues = &wait_value, | 199 | .pWaitSemaphoreValues = wait_values.data(), |
| 196 | .signalSemaphoreValueCount = num_signal_semaphores, | 200 | .signalSemaphoreValueCount = num_signal_semaphores, |
| 197 | .pSignalSemaphoreValues = signal_values.data(), | 201 | .pSignalSemaphoreValues = signal_values.data(), |
| 198 | }; | 202 | }; |
| 199 | const VkSubmitInfo submit_info{ | 203 | const VkSubmitInfo submit_info{ |
| 200 | .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, | 204 | .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, |
| 201 | .pNext = &timeline_si, | 205 | .pNext = &timeline_si, |
| 202 | .waitSemaphoreCount = 1, | 206 | .waitSemaphoreCount = num_wait_semaphores, |
| 203 | .pWaitSemaphores = &timeline_semaphore, | 207 | .pWaitSemaphores = wait_semaphores.data(), |
| 204 | .pWaitDstStageMask = &wait_stage_mask, | 208 | .pWaitDstStageMask = wait_stage_masks.data(), |
| 205 | .commandBufferCount = 1, | 209 | .commandBufferCount = 1, |
| 206 | .pCommandBuffers = cmdbuf.address(), | 210 | .pCommandBuffers = cmdbuf.address(), |
| 207 | .signalSemaphoreCount = num_signal_semaphores, | 211 | .signalSemaphoreCount = num_signal_semaphores, |
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index cf39a2363..759ed5a48 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h | |||
| @@ -34,10 +34,10 @@ public: | |||
| 34 | ~VKScheduler(); | 34 | ~VKScheduler(); |
| 35 | 35 | ||
| 36 | /// Sends the current execution context to the GPU. | 36 | /// Sends the current execution context to the GPU. |
| 37 | void Flush(VkSemaphore semaphore = nullptr); | 37 | void Flush(VkSemaphore signal_semaphore = nullptr, VkSemaphore wait_semaphore = nullptr); |
| 38 | 38 | ||
| 39 | /// Sends the current execution context to the GPU and waits for it to complete. | 39 | /// Sends the current execution context to the GPU and waits for it to complete. |
| 40 | void Finish(VkSemaphore semaphore = nullptr); | 40 | void Finish(VkSemaphore signal_semaphore = nullptr, VkSemaphore wait_semaphore = nullptr); |
| 41 | 41 | ||
| 42 | /// Waits for the worker thread to finish executing everything. After this function returns it's | 42 | /// Waits for the worker thread to finish executing everything. After this function returns it's |
| 43 | /// safe to touch worker resources. | 43 | /// safe to touch worker resources. |
| @@ -191,7 +191,7 @@ private: | |||
| 191 | 191 | ||
| 192 | void AllocateWorkerCommandBuffer(); | 192 | void AllocateWorkerCommandBuffer(); |
| 193 | 193 | ||
| 194 | void SubmitExecution(VkSemaphore semaphore); | 194 | void SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore); |
| 195 | 195 | ||
| 196 | void AllocateNewContext(); | 196 | void AllocateNewContext(); |
| 197 | 197 | ||
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h index 5f78f6950..d90935f52 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.h +++ b/src/video_core/renderer_vulkan/vk_state_tracker.h | |||
| @@ -110,10 +110,6 @@ public: | |||
| 110 | return Exchange(Dirty::DepthTestEnable, false); | 110 | return Exchange(Dirty::DepthTestEnable, false); |
| 111 | } | 111 | } |
| 112 | 112 | ||
| 113 | bool TouchDepthBoundsEnable() { | ||
| 114 | return Exchange(Dirty::DepthBoundsEnable, false); | ||
| 115 | } | ||
| 116 | |||
| 117 | bool TouchDepthWriteEnable() { | 113 | bool TouchDepthWriteEnable() { |
| 118 | return Exchange(Dirty::DepthWriteEnable, false); | 114 | return Exchange(Dirty::DepthWriteEnable, false); |
| 119 | } | 115 | } |
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index d990eefba..aadf03cb0 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp | |||
| @@ -20,16 +20,15 @@ namespace Vulkan { | |||
| 20 | 20 | ||
| 21 | namespace { | 21 | namespace { |
| 22 | 22 | ||
| 23 | VkSurfaceFormatKHR ChooseSwapSurfaceFormat(vk::Span<VkSurfaceFormatKHR> formats, bool srgb) { | 23 | VkSurfaceFormatKHR ChooseSwapSurfaceFormat(vk::Span<VkSurfaceFormatKHR> formats) { |
| 24 | if (formats.size() == 1 && formats[0].format == VK_FORMAT_UNDEFINED) { | 24 | if (formats.size() == 1 && formats[0].format == VK_FORMAT_UNDEFINED) { |
| 25 | VkSurfaceFormatKHR format; | 25 | VkSurfaceFormatKHR format; |
| 26 | format.format = VK_FORMAT_B8G8R8A8_UNORM; | 26 | format.format = VK_FORMAT_B8G8R8A8_UNORM; |
| 27 | format.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; | 27 | format.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; |
| 28 | return format; | 28 | return format; |
| 29 | } | 29 | } |
| 30 | const auto& found = std::find_if(formats.begin(), formats.end(), [srgb](const auto& format) { | 30 | const auto& found = std::find_if(formats.begin(), formats.end(), [](const auto& format) { |
| 31 | const auto request_format = srgb ? VK_FORMAT_B8G8R8A8_SRGB : VK_FORMAT_B8G8R8A8_UNORM; | 31 | return format.format == VK_FORMAT_B8G8R8A8_UNORM && |
| 32 | return format.format == request_format && | ||
| 33 | format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; | 32 | format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; |
| 34 | }); | 33 | }); |
| 35 | return found != formats.end() ? *found : formats[0]; | 34 | return found != formats.end() ? *found : formats[0]; |
| @@ -107,14 +106,12 @@ void VKSwapchain::AcquireNextImage() { | |||
| 107 | } | 106 | } |
| 108 | 107 | ||
| 109 | void VKSwapchain::Present(VkSemaphore render_semaphore) { | 108 | void VKSwapchain::Present(VkSemaphore render_semaphore) { |
| 110 | const VkSemaphore present_semaphore{*present_semaphores[frame_index]}; | ||
| 111 | const std::array<VkSemaphore, 2> semaphores{present_semaphore, render_semaphore}; | ||
| 112 | const auto present_queue{device.GetPresentQueue()}; | 109 | const auto present_queue{device.GetPresentQueue()}; |
| 113 | const VkPresentInfoKHR present_info{ | 110 | const VkPresentInfoKHR present_info{ |
| 114 | .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, | 111 | .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, |
| 115 | .pNext = nullptr, | 112 | .pNext = nullptr, |
| 116 | .waitSemaphoreCount = render_semaphore ? 2U : 1U, | 113 | .waitSemaphoreCount = render_semaphore ? 1U : 0U, |
| 117 | .pWaitSemaphores = semaphores.data(), | 114 | .pWaitSemaphores = &render_semaphore, |
| 118 | .swapchainCount = 1, | 115 | .swapchainCount = 1, |
| 119 | .pSwapchains = swapchain.address(), | 116 | .pSwapchains = swapchain.address(), |
| 120 | .pImageIndices = &image_index, | 117 | .pImageIndices = &image_index, |
| @@ -145,7 +142,7 @@ void VKSwapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, | |||
| 145 | const auto formats{physical_device.GetSurfaceFormatsKHR(surface)}; | 142 | const auto formats{physical_device.GetSurfaceFormatsKHR(surface)}; |
| 146 | const auto present_modes{physical_device.GetSurfacePresentModesKHR(surface)}; | 143 | const auto present_modes{physical_device.GetSurfacePresentModesKHR(surface)}; |
| 147 | 144 | ||
| 148 | const VkSurfaceFormatKHR surface_format{ChooseSwapSurfaceFormat(formats, srgb)}; | 145 | const VkSurfaceFormatKHR surface_format{ChooseSwapSurfaceFormat(formats)}; |
| 149 | const VkPresentModeKHR present_mode{ChooseSwapPresentMode(present_modes)}; | 146 | const VkPresentModeKHR present_mode{ChooseSwapPresentMode(present_modes)}; |
| 150 | 147 | ||
| 151 | u32 requested_image_count{capabilities.minImageCount + 1}; | 148 | u32 requested_image_count{capabilities.minImageCount + 1}; |
| @@ -180,6 +177,17 @@ void VKSwapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, | |||
| 180 | swapchain_ci.queueFamilyIndexCount = static_cast<u32>(queue_indices.size()); | 177 | swapchain_ci.queueFamilyIndexCount = static_cast<u32>(queue_indices.size()); |
| 181 | swapchain_ci.pQueueFamilyIndices = queue_indices.data(); | 178 | swapchain_ci.pQueueFamilyIndices = queue_indices.data(); |
| 182 | } | 179 | } |
| 180 | static constexpr std::array view_formats{VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_SRGB}; | ||
| 181 | VkImageFormatListCreateInfo format_list{ | ||
| 182 | .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR, | ||
| 183 | .pNext = nullptr, | ||
| 184 | .viewFormatCount = static_cast<u32>(view_formats.size()), | ||
| 185 | .pViewFormats = view_formats.data(), | ||
| 186 | }; | ||
| 187 | if (device.IsKhrSwapchainMutableFormatEnabled()) { | ||
| 188 | format_list.pNext = std::exchange(swapchain_ci.pNext, &format_list); | ||
| 189 | swapchain_ci.flags |= VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR; | ||
| 190 | } | ||
| 183 | // Request the size again to reduce the possibility of a TOCTOU race condition. | 191 | // Request the size again to reduce the possibility of a TOCTOU race condition. |
| 184 | const auto updated_capabilities = physical_device.GetSurfaceCapabilitiesKHR(surface); | 192 | const auto updated_capabilities = physical_device.GetSurfaceCapabilitiesKHR(surface); |
| 185 | swapchain_ci.imageExtent = ChooseSwapExtent(updated_capabilities, width, height); | 193 | swapchain_ci.imageExtent = ChooseSwapExtent(updated_capabilities, width, height); |
| @@ -191,7 +199,7 @@ void VKSwapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, | |||
| 191 | 199 | ||
| 192 | images = swapchain.GetImages(); | 200 | images = swapchain.GetImages(); |
| 193 | image_count = static_cast<u32>(images.size()); | 201 | image_count = static_cast<u32>(images.size()); |
| 194 | image_format = surface_format.format; | 202 | image_view_format = srgb ? VK_FORMAT_B8G8R8A8_SRGB : VK_FORMAT_B8G8R8A8_UNORM; |
| 195 | } | 203 | } |
| 196 | 204 | ||
| 197 | void VKSwapchain::CreateSemaphores() { | 205 | void VKSwapchain::CreateSemaphores() { |
| @@ -207,7 +215,7 @@ void VKSwapchain::CreateImageViews() { | |||
| 207 | .flags = 0, | 215 | .flags = 0, |
| 208 | .image = {}, | 216 | .image = {}, |
| 209 | .viewType = VK_IMAGE_VIEW_TYPE_2D, | 217 | .viewType = VK_IMAGE_VIEW_TYPE_2D, |
| 210 | .format = image_format, | 218 | .format = image_view_format, |
| 211 | .components = | 219 | .components = |
| 212 | { | 220 | { |
| 213 | .r = VK_COMPONENT_SWIZZLE_IDENTITY, | 221 | .r = VK_COMPONENT_SWIZZLE_IDENTITY, |
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.h b/src/video_core/renderer_vulkan/vk_swapchain.h index 35c2cdc14..5bce41e21 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.h +++ b/src/video_core/renderer_vulkan/vk_swapchain.h | |||
| @@ -68,8 +68,12 @@ public: | |||
| 68 | return *image_views[index]; | 68 | return *image_views[index]; |
| 69 | } | 69 | } |
| 70 | 70 | ||
| 71 | VkFormat GetImageFormat() const { | 71 | VkFormat GetImageViewFormat() const { |
| 72 | return image_format; | 72 | return image_view_format; |
| 73 | } | ||
| 74 | |||
| 75 | VkSemaphore CurrentPresentSemaphore() const { | ||
| 76 | return *present_semaphores[frame_index]; | ||
| 73 | } | 77 | } |
| 74 | 78 | ||
| 75 | private: | 79 | private: |
| @@ -96,7 +100,7 @@ private: | |||
| 96 | u32 image_index{}; | 100 | u32 image_index{}; |
| 97 | u32 frame_index{}; | 101 | u32 frame_index{}; |
| 98 | 102 | ||
| 99 | VkFormat image_format{}; | 103 | VkFormat image_view_format{}; |
| 100 | VkExtent2D extent{}; | 104 | VkExtent2D extent{}; |
| 101 | 105 | ||
| 102 | bool current_srgb{}; | 106 | bool current_srgb{}; |
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 8f4df7122..ff979a7ac 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp | |||
| @@ -1186,9 +1186,12 @@ Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM | |||
| 1186 | renderpass_key.depth_format = depth_buffer->format; | 1186 | renderpass_key.depth_format = depth_buffer->format; |
| 1187 | num_layers = std::max(num_layers, depth_buffer->range.extent.layers); | 1187 | num_layers = std::max(num_layers, depth_buffer->range.extent.layers); |
| 1188 | images[num_images] = depth_buffer->ImageHandle(); | 1188 | images[num_images] = depth_buffer->ImageHandle(); |
| 1189 | image_ranges[num_images] = MakeSubresourceRange(depth_buffer); | 1189 | const VkImageSubresourceRange subresource_range = MakeSubresourceRange(depth_buffer); |
| 1190 | image_ranges[num_images] = subresource_range; | ||
| 1190 | samples = depth_buffer->Samples(); | 1191 | samples = depth_buffer->Samples(); |
| 1191 | ++num_images; | 1192 | ++num_images; |
| 1193 | has_depth = (subresource_range.aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT) != 0; | ||
| 1194 | has_stencil = (subresource_range.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0; | ||
| 1192 | } else { | 1195 | } else { |
| 1193 | renderpass_key.depth_format = PixelFormat::Invalid; | 1196 | renderpass_key.depth_format = PixelFormat::Invalid; |
| 1194 | } | 1197 | } |
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 5fe6b7ba3..6d5a68bfe 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h | |||
| @@ -232,6 +232,18 @@ public: | |||
| 232 | return image_ranges; | 232 | return image_ranges; |
| 233 | } | 233 | } |
| 234 | 234 | ||
| 235 | [[nodiscard]] bool HasAspectColorBit(size_t index) const noexcept { | ||
| 236 | return (image_ranges.at(index).aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) != 0; | ||
| 237 | } | ||
| 238 | |||
| 239 | [[nodiscard]] bool HasAspectDepthBit() const noexcept { | ||
| 240 | return has_depth; | ||
| 241 | } | ||
| 242 | |||
| 243 | [[nodiscard]] bool HasAspectStencilBit() const noexcept { | ||
| 244 | return has_stencil; | ||
| 245 | } | ||
| 246 | |||
| 235 | private: | 247 | private: |
| 236 | vk::Framebuffer framebuffer; | 248 | vk::Framebuffer framebuffer; |
| 237 | VkRenderPass renderpass{}; | 249 | VkRenderPass renderpass{}; |
| @@ -241,6 +253,8 @@ private: | |||
| 241 | u32 num_images = 0; | 253 | u32 num_images = 0; |
| 242 | std::array<VkImage, 9> images{}; | 254 | std::array<VkImage, 9> images{}; |
| 243 | std::array<VkImageSubresourceRange, 9> image_ranges{}; | 255 | std::array<VkImageSubresourceRange, 9> image_ranges{}; |
| 256 | bool has_depth{}; | ||
| 257 | bool has_stencil{}; | ||
| 244 | }; | 258 | }; |
| 245 | 259 | ||
| 246 | struct TextureCacheParams { | 260 | struct TextureCacheParams { |
diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp index 8a4581c19..81a878bb2 100644 --- a/src/video_core/shader_environment.cpp +++ b/src/video_core/shader_environment.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 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> | ||
| 5 | #include <filesystem> | 6 | #include <filesystem> |
| 6 | #include <fstream> | 7 | #include <fstream> |
| 7 | #include <memory> | 8 | #include <memory> |
diff --git a/src/video_core/texture_cache/slot_vector.h b/src/video_core/texture_cache/slot_vector.h index 6180b8c0e..74cd3c9d8 100644 --- a/src/video_core/texture_cache/slot_vector.h +++ b/src/video_core/texture_cache/slot_vector.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 <bit> | 9 | #include <bit> |
| 9 | #include <concepts> | 10 | #include <concepts> |
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index 3b575db4d..cae543a51 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp | |||
| @@ -37,7 +37,8 @@ std::unique_ptr<VideoCore::RendererBase> CreateRenderer( | |||
| 37 | namespace VideoCore { | 37 | namespace VideoCore { |
| 38 | 38 | ||
| 39 | std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system) { | 39 | std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system) { |
| 40 | const bool use_nvdec = Settings::values.use_nvdec_emulation.GetValue(); | 40 | const auto nvdec_value = Settings::values.nvdec_emulation.GetValue(); |
| 41 | const bool use_nvdec = nvdec_value != Settings::NvdecEmulation::Off; | ||
| 41 | const bool use_async = Settings::values.use_asynchronous_gpu_emulation.GetValue(); | 42 | const bool use_async = Settings::values.use_asynchronous_gpu_emulation.GetValue(); |
| 42 | auto gpu = std::make_unique<Tegra::GPU>(system, use_async, use_nvdec); | 43 | auto gpu = std::make_unique<Tegra::GPU>(system, use_async, use_nvdec); |
| 43 | auto context = emu_window.CreateSharedContext(); | 44 | auto context = emu_window.CreateSharedContext(); |
diff --git a/src/video_core/vulkan_common/vulkan_debug_callback.cpp b/src/video_core/vulkan_common/vulkan_debug_callback.cpp index 0f60765bb..cf94e1d39 100644 --- a/src/video_core/vulkan_common/vulkan_debug_callback.cpp +++ b/src/video_core/vulkan_common/vulkan_debug_callback.cpp | |||
| @@ -16,6 +16,7 @@ VkBool32 Callback(VkDebugUtilsMessageSeverityFlagBitsEXT severity, | |||
| 16 | switch (static_cast<u32>(data->messageIdNumber)) { | 16 | switch (static_cast<u32>(data->messageIdNumber)) { |
| 17 | case 0x682a878au: // VUID-vkCmdBindVertexBuffers2EXT-pBuffers-parameter | 17 | case 0x682a878au: // VUID-vkCmdBindVertexBuffers2EXT-pBuffers-parameter |
| 18 | case 0x99fb7dfdu: // UNASSIGNED-RequiredParameter (vkCmdBindVertexBuffers2EXT pBuffers[0]) | 18 | case 0x99fb7dfdu: // UNASSIGNED-RequiredParameter (vkCmdBindVertexBuffers2EXT pBuffers[0]) |
| 19 | case 0xe8616bf2u: // Bound VkDescriptorSet 0x0[] was destroyed. Likely push_descriptor related | ||
| 19 | return VK_FALSE; | 20 | return VK_FALSE; |
| 20 | default: | 21 | default: |
| 21 | break; | 22 | break; |
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 86ca4be54..c2ec9f76a 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp | |||
| @@ -368,8 +368,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR | |||
| 368 | }; | 368 | }; |
| 369 | SetNext(next, demote); | 369 | SetNext(next, demote); |
| 370 | 370 | ||
| 371 | VkPhysicalDeviceFloat16Int8FeaturesKHR float16_int8; | ||
| 371 | if (is_int8_supported || is_float16_supported) { | 372 | if (is_int8_supported || is_float16_supported) { |
| 372 | VkPhysicalDeviceFloat16Int8FeaturesKHR float16_int8{ | 373 | float16_int8 = { |
| 373 | .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR, | 374 | .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR, |
| 374 | .pNext = nullptr, | 375 | .pNext = nullptr, |
| 375 | .shaderFloat16 = is_float16_supported, | 376 | .shaderFloat16 = is_float16_supported, |
| @@ -587,6 +588,26 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR | |||
| 587 | ext_extended_dynamic_state = false; | 588 | ext_extended_dynamic_state = false; |
| 588 | } | 589 | } |
| 589 | } | 590 | } |
| 591 | |||
| 592 | sets_per_pool = 64; | ||
| 593 | if (driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE) { | ||
| 594 | // AMD drivers need a higher amount of Sets per Pool in certain circunstances like in XC2. | ||
| 595 | sets_per_pool = 96; | ||
| 596 | } | ||
| 597 | |||
| 598 | const bool is_amd = driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || | ||
| 599 | driver_id == VK_DRIVER_ID_MESA_RADV || | ||
| 600 | driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE; | ||
| 601 | if (ext_sampler_filter_minmax && is_amd) { | ||
| 602 | // Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken. | ||
| 603 | if (!is_float16_supported) { | ||
| 604 | LOG_WARNING( | ||
| 605 | Render_Vulkan, | ||
| 606 | "Blacklisting AMD GCN4 and lower for VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME"); | ||
| 607 | ext_sampler_filter_minmax = false; | ||
| 608 | } | ||
| 609 | } | ||
| 610 | |||
| 590 | if (ext_vertex_input_dynamic_state && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) { | 611 | if (ext_vertex_input_dynamic_state && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) { |
| 591 | LOG_WARNING(Render_Vulkan, "Blacklisting Intel for VK_EXT_vertex_input_dynamic_state"); | 612 | LOG_WARNING(Render_Vulkan, "Blacklisting Intel for VK_EXT_vertex_input_dynamic_state"); |
| 592 | ext_vertex_input_dynamic_state = false; | 613 | ext_vertex_input_dynamic_state = false; |
| @@ -839,6 +860,8 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) { | |||
| 839 | bool has_khr_shader_float16_int8{}; | 860 | bool has_khr_shader_float16_int8{}; |
| 840 | bool has_khr_workgroup_memory_explicit_layout{}; | 861 | bool has_khr_workgroup_memory_explicit_layout{}; |
| 841 | bool has_khr_pipeline_executable_properties{}; | 862 | bool has_khr_pipeline_executable_properties{}; |
| 863 | bool has_khr_image_format_list{}; | ||
| 864 | bool has_khr_swapchain_mutable_format{}; | ||
| 842 | bool has_ext_subgroup_size_control{}; | 865 | bool has_ext_subgroup_size_control{}; |
| 843 | bool has_ext_transform_feedback{}; | 866 | bool has_ext_transform_feedback{}; |
| 844 | bool has_ext_custom_border_color{}; | 867 | bool has_ext_custom_border_color{}; |
| @@ -888,6 +911,9 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) { | |||
| 888 | test(has_ext_shader_atomic_int64, VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME, false); | 911 | test(has_ext_shader_atomic_int64, VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME, false); |
| 889 | test(has_khr_workgroup_memory_explicit_layout, | 912 | test(has_khr_workgroup_memory_explicit_layout, |
| 890 | VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME, false); | 913 | VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME, false); |
| 914 | test(has_khr_image_format_list, VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME, false); | ||
| 915 | test(has_khr_swapchain_mutable_format, VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME, | ||
| 916 | false); | ||
| 891 | test(has_ext_line_rasterization, VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME, false); | 917 | test(has_ext_line_rasterization, VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME, false); |
| 892 | if (Settings::values.enable_nsight_aftermath) { | 918 | if (Settings::values.enable_nsight_aftermath) { |
| 893 | test(nv_device_diagnostics_config, VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, | 919 | test(nv_device_diagnostics_config, VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, |
| @@ -1066,6 +1092,11 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) { | |||
| 1066 | khr_pipeline_executable_properties = true; | 1092 | khr_pipeline_executable_properties = true; |
| 1067 | } | 1093 | } |
| 1068 | } | 1094 | } |
| 1095 | if (has_khr_image_format_list && has_khr_swapchain_mutable_format) { | ||
| 1096 | extensions.push_back(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME); | ||
| 1097 | extensions.push_back(VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME); | ||
| 1098 | khr_swapchain_mutable_format = true; | ||
| 1099 | } | ||
| 1069 | if (khr_push_descriptor) { | 1100 | if (khr_push_descriptor) { |
| 1070 | VkPhysicalDevicePushDescriptorPropertiesKHR push_descriptor; | 1101 | VkPhysicalDevicePushDescriptorPropertiesKHR push_descriptor; |
| 1071 | push_descriptor.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR; | 1102 | 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 234d74129..bc180a32a 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h | |||
| @@ -224,6 +224,11 @@ public: | |||
| 224 | return khr_pipeline_executable_properties; | 224 | return khr_pipeline_executable_properties; |
| 225 | } | 225 | } |
| 226 | 226 | ||
| 227 | /// Returns true if VK_KHR_swapchain_mutable_format is enabled. | ||
| 228 | bool IsKhrSwapchainMutableFormatEnabled() const { | ||
| 229 | return khr_swapchain_mutable_format; | ||
| 230 | } | ||
| 231 | |||
| 227 | /// Returns true if the device supports VK_KHR_workgroup_memory_explicit_layout. | 232 | /// Returns true if the device supports VK_KHR_workgroup_memory_explicit_layout. |
| 228 | bool IsKhrWorkgroupMemoryExplicitLayoutSupported() const { | 233 | bool IsKhrWorkgroupMemoryExplicitLayoutSupported() const { |
| 229 | return khr_workgroup_memory_explicit_layout; | 234 | return khr_workgroup_memory_explicit_layout; |
| @@ -318,6 +323,10 @@ public: | |||
| 318 | return device_access_memory; | 323 | return device_access_memory; |
| 319 | } | 324 | } |
| 320 | 325 | ||
| 326 | u32 GetSetsPerPool() const { | ||
| 327 | return sets_per_pool; | ||
| 328 | } | ||
| 329 | |||
| 321 | private: | 330 | private: |
| 322 | /// Checks if the physical device is suitable. | 331 | /// Checks if the physical device is suitable. |
| 323 | void CheckSuitability(bool requires_swapchain) const; | 332 | void CheckSuitability(bool requires_swapchain) const; |
| @@ -371,6 +380,7 @@ private: | |||
| 371 | VkShaderStageFlags guest_warp_stages{}; ///< Stages where the guest warp size can be forced. | 380 | VkShaderStageFlags guest_warp_stages{}; ///< Stages where the guest warp size can be forced. |
| 372 | u64 device_access_memory{}; ///< Total size of device local memory in bytes. | 381 | u64 device_access_memory{}; ///< Total size of device local memory in bytes. |
| 373 | u32 max_push_descriptors{}; ///< Maximum number of push descriptors | 382 | u32 max_push_descriptors{}; ///< Maximum number of push descriptors |
| 383 | u32 sets_per_pool{}; ///< Sets per Description Pool | ||
| 374 | bool is_optimal_astc_supported{}; ///< Support for native ASTC. | 384 | bool is_optimal_astc_supported{}; ///< Support for native ASTC. |
| 375 | bool is_float16_supported{}; ///< Support for float16 arithmetic. | 385 | bool is_float16_supported{}; ///< Support for float16 arithmetic. |
| 376 | bool is_int8_supported{}; ///< Support for int8 arithmetic. | 386 | bool is_int8_supported{}; ///< Support for int8 arithmetic. |
| @@ -390,6 +400,7 @@ private: | |||
| 390 | bool khr_workgroup_memory_explicit_layout{}; ///< Support for explicit workgroup layouts. | 400 | bool khr_workgroup_memory_explicit_layout{}; ///< Support for explicit workgroup layouts. |
| 391 | bool khr_push_descriptor{}; ///< Support for VK_KHR_push_descritor. | 401 | bool khr_push_descriptor{}; ///< Support for VK_KHR_push_descritor. |
| 392 | bool khr_pipeline_executable_properties{}; ///< Support for executable properties. | 402 | bool khr_pipeline_executable_properties{}; ///< Support for executable properties. |
| 403 | bool khr_swapchain_mutable_format{}; ///< Support for VK_KHR_swapchain_mutable_format. | ||
| 393 | bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8. | 404 | bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8. |
| 394 | bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax. | 405 | bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax. |
| 395 | bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted. | 406 | bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted. |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 700c162ea..18271dee2 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -813,7 +813,7 @@ void Config::ReadRendererValues() { | |||
| 813 | ReadGlobalSetting(Settings::values.use_disk_shader_cache); | 813 | ReadGlobalSetting(Settings::values.use_disk_shader_cache); |
| 814 | ReadGlobalSetting(Settings::values.gpu_accuracy); | 814 | ReadGlobalSetting(Settings::values.gpu_accuracy); |
| 815 | ReadGlobalSetting(Settings::values.use_asynchronous_gpu_emulation); | 815 | ReadGlobalSetting(Settings::values.use_asynchronous_gpu_emulation); |
| 816 | ReadGlobalSetting(Settings::values.use_nvdec_emulation); | 816 | ReadGlobalSetting(Settings::values.nvdec_emulation); |
| 817 | ReadGlobalSetting(Settings::values.accelerate_astc); | 817 | ReadGlobalSetting(Settings::values.accelerate_astc); |
| 818 | ReadGlobalSetting(Settings::values.use_vsync); | 818 | ReadGlobalSetting(Settings::values.use_vsync); |
| 819 | ReadGlobalSetting(Settings::values.shader_backend); | 819 | ReadGlobalSetting(Settings::values.shader_backend); |
| @@ -1351,7 +1351,10 @@ void Config::SaveRendererValues() { | |||
| 1351 | static_cast<u32>(Settings::values.gpu_accuracy.GetDefault()), | 1351 | static_cast<u32>(Settings::values.gpu_accuracy.GetDefault()), |
| 1352 | Settings::values.gpu_accuracy.UsingGlobal()); | 1352 | Settings::values.gpu_accuracy.UsingGlobal()); |
| 1353 | WriteGlobalSetting(Settings::values.use_asynchronous_gpu_emulation); | 1353 | WriteGlobalSetting(Settings::values.use_asynchronous_gpu_emulation); |
| 1354 | WriteGlobalSetting(Settings::values.use_nvdec_emulation); | 1354 | WriteSetting(QString::fromStdString(Settings::values.nvdec_emulation.GetLabel()), |
| 1355 | static_cast<u32>(Settings::values.nvdec_emulation.GetValue(global)), | ||
| 1356 | static_cast<u32>(Settings::values.nvdec_emulation.GetDefault()), | ||
| 1357 | Settings::values.nvdec_emulation.UsingGlobal()); | ||
| 1355 | WriteGlobalSetting(Settings::values.accelerate_astc); | 1358 | WriteGlobalSetting(Settings::values.accelerate_astc); |
| 1356 | WriteGlobalSetting(Settings::values.use_vsync); | 1359 | WriteGlobalSetting(Settings::values.use_vsync); |
| 1357 | WriteSetting(QString::fromStdString(Settings::values.shader_backend.GetLabel()), | 1360 | WriteSetting(QString::fromStdString(Settings::values.shader_backend.GetLabel()), |
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index 9555f4498..4733227b6 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h | |||
| @@ -182,5 +182,6 @@ private: | |||
| 182 | Q_DECLARE_METATYPE(Settings::CPUAccuracy); | 182 | Q_DECLARE_METATYPE(Settings::CPUAccuracy); |
| 183 | Q_DECLARE_METATYPE(Settings::GPUAccuracy); | 183 | Q_DECLARE_METATYPE(Settings::GPUAccuracy); |
| 184 | Q_DECLARE_METATYPE(Settings::FullscreenMode); | 184 | Q_DECLARE_METATYPE(Settings::FullscreenMode); |
| 185 | Q_DECLARE_METATYPE(Settings::NvdecEmulation); | ||
| 185 | Q_DECLARE_METATYPE(Settings::RendererBackend); | 186 | Q_DECLARE_METATYPE(Settings::RendererBackend); |
| 186 | Q_DECLARE_METATYPE(Settings::ShaderBackend); | 187 | Q_DECLARE_METATYPE(Settings::ShaderBackend); |
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index 37e896258..c594164be 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp | |||
| @@ -88,24 +88,30 @@ void ConfigureGraphics::SetConfiguration() { | |||
| 88 | ui->api_widget->setEnabled(runtime_lock); | 88 | ui->api_widget->setEnabled(runtime_lock); |
| 89 | ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock); | 89 | ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock); |
| 90 | ui->use_disk_shader_cache->setEnabled(runtime_lock); | 90 | ui->use_disk_shader_cache->setEnabled(runtime_lock); |
| 91 | ui->use_nvdec_emulation->setEnabled(runtime_lock); | 91 | ui->nvdec_emulation_widget->setEnabled(runtime_lock); |
| 92 | ui->accelerate_astc->setEnabled(runtime_lock); | 92 | ui->accelerate_astc->setEnabled(runtime_lock); |
| 93 | ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue()); | 93 | ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue()); |
| 94 | ui->use_asynchronous_gpu_emulation->setChecked( | 94 | ui->use_asynchronous_gpu_emulation->setChecked( |
| 95 | Settings::values.use_asynchronous_gpu_emulation.GetValue()); | 95 | Settings::values.use_asynchronous_gpu_emulation.GetValue()); |
| 96 | ui->use_nvdec_emulation->setChecked(Settings::values.use_nvdec_emulation.GetValue()); | ||
| 97 | ui->accelerate_astc->setChecked(Settings::values.accelerate_astc.GetValue()); | 96 | ui->accelerate_astc->setChecked(Settings::values.accelerate_astc.GetValue()); |
| 98 | 97 | ||
| 99 | if (Settings::IsConfiguringGlobal()) { | 98 | if (Settings::IsConfiguringGlobal()) { |
| 100 | ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend.GetValue())); | 99 | ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend.GetValue())); |
| 101 | ui->fullscreen_mode_combobox->setCurrentIndex( | 100 | ui->fullscreen_mode_combobox->setCurrentIndex( |
| 102 | static_cast<int>(Settings::values.fullscreen_mode.GetValue())); | 101 | static_cast<int>(Settings::values.fullscreen_mode.GetValue())); |
| 102 | ui->nvdec_emulation->setCurrentIndex( | ||
| 103 | static_cast<int>(Settings::values.nvdec_emulation.GetValue())); | ||
| 103 | ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue()); | 104 | ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue()); |
| 104 | } else { | 105 | } else { |
| 105 | ConfigurationShared::SetPerGameSetting(ui->api, &Settings::values.renderer_backend); | 106 | ConfigurationShared::SetPerGameSetting(ui->api, &Settings::values.renderer_backend); |
| 106 | ConfigurationShared::SetHighlight(ui->api_widget, | 107 | ConfigurationShared::SetHighlight(ui->api_widget, |
| 107 | !Settings::values.renderer_backend.UsingGlobal()); | 108 | !Settings::values.renderer_backend.UsingGlobal()); |
| 108 | 109 | ||
| 110 | ConfigurationShared::SetPerGameSetting(ui->nvdec_emulation, | ||
| 111 | &Settings::values.nvdec_emulation); | ||
| 112 | ConfigurationShared::SetHighlight(ui->nvdec_emulation_widget, | ||
| 113 | !Settings::values.nvdec_emulation.UsingGlobal()); | ||
| 114 | |||
| 109 | ConfigurationShared::SetPerGameSetting(ui->fullscreen_mode_combobox, | 115 | ConfigurationShared::SetPerGameSetting(ui->fullscreen_mode_combobox, |
| 110 | &Settings::values.fullscreen_mode); | 116 | &Settings::values.fullscreen_mode); |
| 111 | ConfigurationShared::SetHighlight(ui->fullscreen_mode_label, | 117 | ConfigurationShared::SetHighlight(ui->fullscreen_mode_label, |
| @@ -137,8 +143,6 @@ void ConfigureGraphics::ApplyConfiguration() { | |||
| 137 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_gpu_emulation, | 143 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_gpu_emulation, |
| 138 | ui->use_asynchronous_gpu_emulation, | 144 | ui->use_asynchronous_gpu_emulation, |
| 139 | use_asynchronous_gpu_emulation); | 145 | use_asynchronous_gpu_emulation); |
| 140 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_nvdec_emulation, | ||
| 141 | ui->use_nvdec_emulation, use_nvdec_emulation); | ||
| 142 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.accelerate_astc, ui->accelerate_astc, | 146 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.accelerate_astc, ui->accelerate_astc, |
| 143 | accelerate_astc); | 147 | accelerate_astc); |
| 144 | 148 | ||
| @@ -147,6 +151,9 @@ void ConfigureGraphics::ApplyConfiguration() { | |||
| 147 | if (Settings::values.renderer_backend.UsingGlobal()) { | 151 | if (Settings::values.renderer_backend.UsingGlobal()) { |
| 148 | Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend()); | 152 | Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend()); |
| 149 | } | 153 | } |
| 154 | if (Settings::values.nvdec_emulation.UsingGlobal()) { | ||
| 155 | Settings::values.nvdec_emulation.SetValue(GetCurrentNvdecEmulation()); | ||
| 156 | } | ||
| 150 | if (Settings::values.shader_backend.UsingGlobal()) { | 157 | if (Settings::values.shader_backend.UsingGlobal()) { |
| 151 | Settings::values.shader_backend.SetValue(shader_backend); | 158 | Settings::values.shader_backend.SetValue(shader_backend); |
| 152 | } | 159 | } |
| @@ -180,6 +187,13 @@ void ConfigureGraphics::ApplyConfiguration() { | |||
| 180 | } | 187 | } |
| 181 | } | 188 | } |
| 182 | 189 | ||
| 190 | if (ui->nvdec_emulation->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { | ||
| 191 | Settings::values.nvdec_emulation.SetGlobal(true); | ||
| 192 | } else { | ||
| 193 | Settings::values.nvdec_emulation.SetGlobal(false); | ||
| 194 | Settings::values.nvdec_emulation.SetValue(GetCurrentNvdecEmulation()); | ||
| 195 | } | ||
| 196 | |||
| 183 | if (ui->bg_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { | 197 | if (ui->bg_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { |
| 184 | Settings::values.bg_red.SetGlobal(true); | 198 | Settings::values.bg_red.SetGlobal(true); |
| 185 | Settings::values.bg_green.SetGlobal(true); | 199 | Settings::values.bg_green.SetGlobal(true); |
| @@ -278,6 +292,20 @@ Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { | |||
| 278 | ConfigurationShared::USE_GLOBAL_OFFSET); | 292 | ConfigurationShared::USE_GLOBAL_OFFSET); |
| 279 | } | 293 | } |
| 280 | 294 | ||
| 295 | Settings::NvdecEmulation ConfigureGraphics::GetCurrentNvdecEmulation() const { | ||
| 296 | if (Settings::IsConfiguringGlobal()) { | ||
| 297 | return static_cast<Settings::NvdecEmulation>(ui->nvdec_emulation->currentIndex()); | ||
| 298 | } | ||
| 299 | |||
| 300 | if (ui->nvdec_emulation->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { | ||
| 301 | Settings::values.nvdec_emulation.SetGlobal(true); | ||
| 302 | return Settings::values.nvdec_emulation.GetValue(); | ||
| 303 | } | ||
| 304 | Settings::values.nvdec_emulation.SetGlobal(false); | ||
| 305 | return static_cast<Settings::NvdecEmulation>(ui->nvdec_emulation->currentIndex() - | ||
| 306 | ConfigurationShared::USE_GLOBAL_OFFSET); | ||
| 307 | } | ||
| 308 | |||
| 281 | void ConfigureGraphics::SetupPerGameUI() { | 309 | void ConfigureGraphics::SetupPerGameUI() { |
| 282 | if (Settings::IsConfiguringGlobal()) { | 310 | if (Settings::IsConfiguringGlobal()) { |
| 283 | ui->api->setEnabled(Settings::values.renderer_backend.UsingGlobal()); | 311 | ui->api->setEnabled(Settings::values.renderer_backend.UsingGlobal()); |
| @@ -286,7 +314,7 @@ void ConfigureGraphics::SetupPerGameUI() { | |||
| 286 | ui->aspect_ratio_combobox->setEnabled(Settings::values.aspect_ratio.UsingGlobal()); | 314 | ui->aspect_ratio_combobox->setEnabled(Settings::values.aspect_ratio.UsingGlobal()); |
| 287 | ui->use_asynchronous_gpu_emulation->setEnabled( | 315 | ui->use_asynchronous_gpu_emulation->setEnabled( |
| 288 | Settings::values.use_asynchronous_gpu_emulation.UsingGlobal()); | 316 | Settings::values.use_asynchronous_gpu_emulation.UsingGlobal()); |
| 289 | ui->use_nvdec_emulation->setEnabled(Settings::values.use_nvdec_emulation.UsingGlobal()); | 317 | ui->nvdec_emulation->setEnabled(Settings::values.nvdec_emulation.UsingGlobal()); |
| 290 | ui->accelerate_astc->setEnabled(Settings::values.accelerate_astc.UsingGlobal()); | 318 | ui->accelerate_astc->setEnabled(Settings::values.accelerate_astc.UsingGlobal()); |
| 291 | ui->use_disk_shader_cache->setEnabled(Settings::values.use_disk_shader_cache.UsingGlobal()); | 319 | ui->use_disk_shader_cache->setEnabled(Settings::values.use_disk_shader_cache.UsingGlobal()); |
| 292 | ui->bg_button->setEnabled(Settings::values.bg_red.UsingGlobal()); | 320 | ui->bg_button->setEnabled(Settings::values.bg_red.UsingGlobal()); |
| @@ -301,8 +329,6 @@ void ConfigureGraphics::SetupPerGameUI() { | |||
| 301 | 329 | ||
| 302 | ConfigurationShared::SetColoredTristate( | 330 | ConfigurationShared::SetColoredTristate( |
| 303 | ui->use_disk_shader_cache, Settings::values.use_disk_shader_cache, use_disk_shader_cache); | 331 | ui->use_disk_shader_cache, Settings::values.use_disk_shader_cache, use_disk_shader_cache); |
| 304 | ConfigurationShared::SetColoredTristate( | ||
| 305 | ui->use_nvdec_emulation, Settings::values.use_nvdec_emulation, use_nvdec_emulation); | ||
| 306 | ConfigurationShared::SetColoredTristate(ui->accelerate_astc, Settings::values.accelerate_astc, | 332 | ConfigurationShared::SetColoredTristate(ui->accelerate_astc, Settings::values.accelerate_astc, |
| 307 | accelerate_astc); | 333 | accelerate_astc); |
| 308 | ConfigurationShared::SetColoredTristate(ui->use_asynchronous_gpu_emulation, | 334 | ConfigurationShared::SetColoredTristate(ui->use_asynchronous_gpu_emulation, |
| @@ -316,4 +342,6 @@ void ConfigureGraphics::SetupPerGameUI() { | |||
| 316 | static_cast<int>(Settings::values.fullscreen_mode.GetValue(true))); | 342 | static_cast<int>(Settings::values.fullscreen_mode.GetValue(true))); |
| 317 | ConfigurationShared::InsertGlobalItem( | 343 | ConfigurationShared::InsertGlobalItem( |
| 318 | ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true))); | 344 | ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true))); |
| 345 | ConfigurationShared::InsertGlobalItem( | ||
| 346 | ui->nvdec_emulation, static_cast<int>(Settings::values.nvdec_emulation.GetValue(true))); | ||
| 319 | } | 347 | } |
diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h index c866b911b..7d7ac329d 100644 --- a/src/yuzu/configuration/configure_graphics.h +++ b/src/yuzu/configuration/configure_graphics.h | |||
| @@ -43,6 +43,7 @@ private: | |||
| 43 | void SetupPerGameUI(); | 43 | void SetupPerGameUI(); |
| 44 | 44 | ||
| 45 | Settings::RendererBackend GetCurrentGraphicsBackend() const; | 45 | Settings::RendererBackend GetCurrentGraphicsBackend() const; |
| 46 | Settings::NvdecEmulation GetCurrentNvdecEmulation() const; | ||
| 46 | 47 | ||
| 47 | std::unique_ptr<Ui::ConfigureGraphics> ui; | 48 | std::unique_ptr<Ui::ConfigureGraphics> ui; |
| 48 | QColor bg_color; | 49 | QColor bg_color; |
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index 099ddbb7c..1a12cfa4d 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui | |||
| @@ -156,7 +156,7 @@ | |||
| 156 | <item> | 156 | <item> |
| 157 | <widget class="QCheckBox" name="use_disk_shader_cache"> | 157 | <widget class="QCheckBox" name="use_disk_shader_cache"> |
| 158 | <property name="text"> | 158 | <property name="text"> |
| 159 | <string>Use disk shader cache</string> | 159 | <string>Use disk pipeline cache</string> |
| 160 | </property> | 160 | </property> |
| 161 | </widget> | 161 | </widget> |
| 162 | </item> | 162 | </item> |
| @@ -168,13 +168,6 @@ | |||
| 168 | </widget> | 168 | </widget> |
| 169 | </item> | 169 | </item> |
| 170 | <item> | 170 | <item> |
| 171 | <widget class="QCheckBox" name="use_nvdec_emulation"> | ||
| 172 | <property name="text"> | ||
| 173 | <string>Use NVDEC emulation</string> | ||
| 174 | </property> | ||
| 175 | </widget> | ||
| 176 | </item> | ||
| 177 | <item> | ||
| 178 | <widget class="QCheckBox" name="accelerate_astc"> | 171 | <widget class="QCheckBox" name="accelerate_astc"> |
| 179 | <property name="text"> | 172 | <property name="text"> |
| 180 | <string>Accelerate ASTC texture decoding</string> | 173 | <string>Accelerate ASTC texture decoding</string> |
| @@ -182,6 +175,50 @@ | |||
| 182 | </widget> | 175 | </widget> |
| 183 | </item> | 176 | </item> |
| 184 | <item> | 177 | <item> |
| 178 | <widget class="QWidget" name="nvdec_emulation_widget" native="true"> | ||
| 179 | <layout class="QHBoxLayout" name="nvdec_emulation_layout"> | ||
| 180 | <property name="leftMargin"> | ||
| 181 | <number>0</number> | ||
| 182 | </property> | ||
| 183 | <property name="topMargin"> | ||
| 184 | <number>0</number> | ||
| 185 | </property> | ||
| 186 | <property name="rightMargin"> | ||
| 187 | <number>0</number> | ||
| 188 | </property> | ||
| 189 | <property name="bottomMargin"> | ||
| 190 | <number>0</number> | ||
| 191 | </property> | ||
| 192 | <item> | ||
| 193 | <widget class="QLabel" name="nvdec_emulation_label"> | ||
| 194 | <property name="text"> | ||
| 195 | <string>NVDEC emulation:</string> | ||
| 196 | </property> | ||
| 197 | </widget> | ||
| 198 | </item> | ||
| 199 | <item> | ||
| 200 | <widget class="QComboBox" name="nvdec_emulation"> | ||
| 201 | <item> | ||
| 202 | <property name="text"> | ||
| 203 | <string>Disabled</string> | ||
| 204 | </property> | ||
| 205 | </item> | ||
| 206 | <item> | ||
| 207 | <property name="text"> | ||
| 208 | <string>CPU Decoding</string> | ||
| 209 | </property> | ||
| 210 | </item> | ||
| 211 | <item> | ||
| 212 | <property name="text"> | ||
| 213 | <string>GPU Decoding</string> | ||
| 214 | </property> | ||
| 215 | </item> | ||
| 216 | </widget> | ||
| 217 | </item> | ||
| 218 | </layout> | ||
| 219 | </widget> | ||
| 220 | </item> | ||
| 221 | <item> | ||
| 185 | <widget class="QWidget" name="fullscreen_mode_layout" native="true"> | 222 | <widget class="QWidget" name="fullscreen_mode_layout" native="true"> |
| 186 | <layout class="QHBoxLayout" name="horizontalLayout_1"> | 223 | <layout class="QHBoxLayout" name="horizontalLayout_1"> |
| 187 | <property name="leftMargin"> | 224 | <property name="leftMargin"> |
diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui index 5891f8299..b91abc2f0 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.ui +++ b/src/yuzu/configuration/configure_graphics_advanced.ui | |||
| @@ -82,7 +82,7 @@ | |||
| 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 (hack)</string> | 85 | <string>Use asynchronous shader building (Hack)</string> |
| 86 | </property> | 86 | </property> |
| 87 | </widget> | 87 | </widget> |
| 88 | </item> | 88 | </item> |
| @@ -92,7 +92,7 @@ | |||
| 92 | <string>Enables Fast GPU Time. This option will force most games to run at their highest native resolution.</string> | 92 | <string>Enables Fast GPU Time. This option will force most games to run at their highest native resolution.</string> |
| 93 | </property> | 93 | </property> |
| 94 | <property name="text"> | 94 | <property name="text"> |
| 95 | <string>Use Fast GPU Time (hack)</string> | 95 | <string>Use Fast GPU Time (Hack)</string> |
| 96 | </property> | 96 | </property> |
| 97 | </widget> | 97 | </widget> |
| 98 | </item> | 98 | </item> |
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index e97804220..f9d949e75 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp | |||
| @@ -515,16 +515,16 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri | |||
| 515 | QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location")); | 515 | QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location")); |
| 516 | QAction* open_mod_location = context_menu.addAction(tr("Open Mod Data Location")); | 516 | QAction* open_mod_location = context_menu.addAction(tr("Open Mod Data Location")); |
| 517 | QAction* open_transferable_shader_cache = | 517 | QAction* open_transferable_shader_cache = |
| 518 | context_menu.addAction(tr("Open Transferable Shader Cache")); | 518 | context_menu.addAction(tr("Open Transferable Pipeline Cache")); |
| 519 | context_menu.addSeparator(); | 519 | context_menu.addSeparator(); |
| 520 | QMenu* remove_menu = context_menu.addMenu(tr("Remove")); | 520 | QMenu* remove_menu = context_menu.addMenu(tr("Remove")); |
| 521 | QAction* remove_update = remove_menu->addAction(tr("Remove Installed Update")); | 521 | QAction* remove_update = remove_menu->addAction(tr("Remove Installed Update")); |
| 522 | QAction* remove_dlc = remove_menu->addAction(tr("Remove All Installed DLC")); | 522 | QAction* remove_dlc = remove_menu->addAction(tr("Remove All Installed DLC")); |
| 523 | QAction* remove_custom_config = remove_menu->addAction(tr("Remove Custom Configuration")); | 523 | QAction* remove_custom_config = remove_menu->addAction(tr("Remove Custom Configuration")); |
| 524 | QAction* remove_gl_shader_cache = remove_menu->addAction(tr("Remove OpenGL Shader Cache")); | 524 | QAction* remove_gl_shader_cache = remove_menu->addAction(tr("Remove OpenGL Pipeline Cache")); |
| 525 | QAction* remove_vk_shader_cache = remove_menu->addAction(tr("Remove Vulkan Shader Cache")); | 525 | QAction* remove_vk_shader_cache = remove_menu->addAction(tr("Remove Vulkan Pipeline Cache")); |
| 526 | remove_menu->addSeparator(); | 526 | remove_menu->addSeparator(); |
| 527 | QAction* remove_shader_cache = remove_menu->addAction(tr("Remove All Shader Caches")); | 527 | QAction* remove_shader_cache = remove_menu->addAction(tr("Remove All Pipeline Caches")); |
| 528 | QAction* remove_all_content = remove_menu->addAction(tr("Remove All Installed Contents")); | 528 | QAction* remove_all_content = remove_menu->addAction(tr("Remove All Installed Contents")); |
| 529 | QMenu* dump_romfs_menu = context_menu.addMenu(tr("Dump RomFS")); | 529 | QMenu* dump_romfs_menu = context_menu.addMenu(tr("Dump RomFS")); |
| 530 | QAction* dump_romfs = dump_romfs_menu->addAction(tr("Dump RomFS")); | 530 | QAction* dump_romfs = dump_romfs_menu->addAction(tr("Dump RomFS")); |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index e129707e9..f4e49001d 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -3175,12 +3175,11 @@ std::optional<u64> GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProv | |||
| 3175 | } | 3175 | } |
| 3176 | 3176 | ||
| 3177 | bool GMainWindow::ConfirmClose() { | 3177 | bool GMainWindow::ConfirmClose() { |
| 3178 | if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) | 3178 | if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) { |
| 3179 | return true; | 3179 | return true; |
| 3180 | 3180 | } | |
| 3181 | QMessageBox::StandardButton answer = | 3181 | const auto text = tr("Are you sure you want to close yuzu?"); |
| 3182 | QMessageBox::question(this, tr("yuzu"), tr("Are you sure you want to close yuzu?"), | 3182 | const auto answer = QMessageBox::question(this, tr("yuzu"), text); |
| 3183 | QMessageBox::Yes | QMessageBox::No, QMessageBox::No); | ||
| 3184 | return answer != QMessageBox::No; | 3183 | return answer != QMessageBox::No; |
| 3185 | } | 3184 | } |
| 3186 | 3185 | ||
| @@ -3262,14 +3261,13 @@ bool GMainWindow::ConfirmChangeGame() { | |||
| 3262 | } | 3261 | } |
| 3263 | 3262 | ||
| 3264 | bool GMainWindow::ConfirmForceLockedExit() { | 3263 | bool GMainWindow::ConfirmForceLockedExit() { |
| 3265 | if (emu_thread == nullptr) | 3264 | if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) { |
| 3266 | return true; | 3265 | return true; |
| 3266 | } | ||
| 3267 | const auto text = tr("The currently running application has requested yuzu to not exit.\n\n" | ||
| 3268 | "Would you like to bypass this and exit anyway?"); | ||
| 3267 | 3269 | ||
| 3268 | const auto answer = | 3270 | const auto answer = QMessageBox::question(this, tr("yuzu"), text); |
| 3269 | QMessageBox::question(this, tr("yuzu"), | ||
| 3270 | tr("The currently running application has requested yuzu to not " | ||
| 3271 | "exit.\n\nWould you like to bypass this and exit anyway?"), | ||
| 3272 | QMessageBox::Yes | QMessageBox::No, QMessageBox::No); | ||
| 3273 | return answer != QMessageBox::No; | 3271 | return answer != QMessageBox::No; |
| 3274 | } | 3272 | } |
| 3275 | 3273 | ||
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 757dd1ea0..891f7be6f 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp | |||
| @@ -465,7 +465,7 @@ void Config::ReadValues() { | |||
| 465 | ReadSetting("Renderer", Settings::values.disable_fps_limit); | 465 | ReadSetting("Renderer", Settings::values.disable_fps_limit); |
| 466 | ReadSetting("Renderer", Settings::values.shader_backend); | 466 | ReadSetting("Renderer", Settings::values.shader_backend); |
| 467 | ReadSetting("Renderer", Settings::values.use_asynchronous_shaders); | 467 | ReadSetting("Renderer", Settings::values.use_asynchronous_shaders); |
| 468 | ReadSetting("Renderer", Settings::values.use_nvdec_emulation); | 468 | ReadSetting("Renderer", Settings::values.nvdec_emulation); |
| 469 | ReadSetting("Renderer", Settings::values.accelerate_astc); | 469 | ReadSetting("Renderer", Settings::values.accelerate_astc); |
| 470 | ReadSetting("Renderer", Settings::values.use_fast_gpu_time); | 470 | ReadSetting("Renderer", Settings::values.use_fast_gpu_time); |
| 471 | 471 | ||
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index e02eceb99..72f3213fb 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h | |||
| @@ -261,9 +261,9 @@ shader_backend = | |||
| 261 | # 0 (default): Off, 1: On | 261 | # 0 (default): Off, 1: On |
| 262 | use_asynchronous_shaders = | 262 | use_asynchronous_shaders = |
| 263 | 263 | ||
| 264 | # Enable NVDEC emulation. | 264 | # NVDEC emulation. |
| 265 | # 0: Off, 1 (default): On | 265 | # 0: Disabled, 1: CPU Decoding, 2 (default): GPU Decoding |
| 266 | use_nvdec_emulation = | 266 | nvdec_emulation = |
| 267 | 267 | ||
| 268 | # Accelerate ASTC texture decoding. | 268 | # Accelerate ASTC texture decoding. |
| 269 | # 0: Off, 1 (default): On | 269 | # 0: Off, 1 (default): On |