Compare commits

..

48 Commits

Author SHA1 Message Date
Liam
e532b74e11 audio_core: remove explicitly defaulted and implicitly deleted constructors 2022-12-10 13:05:15 -05:00
liamwhite
f6e705737a Merge pull request #9412 from Saalvage/fix/trace-log-compilation
Fix compilation error
2022-12-09 17:03:19 -05:00
bunnei
66c4331de5 Merge pull request #9411 from Saalvage/fix/unlock-mutex
Correctly unlock mutex before its destruction
2022-12-09 10:51:19 -08:00
Salvage
c586ac9be2 Remove the lock entirely as per PR discussion
Correctly unlock mutex before its destruction

As per https://en.cppreference.com/w/cpp/thread/mutex/~mutex destroying a locked mutex is undefined behavior and MSVC++ decides to throw in this case

Swap out unique for scoped lock and readd comment
2022-12-09 16:39:59 +01:00
Salvage
0e265db873 Fix compilation error 2022-12-09 14:49:15 +01:00
Fernando S
41461514d6 Merge pull request #9401 from vonchenplus/draw_manager
video_core: Implement maxwell3d draw manager and split draw logic
2022-12-08 12:41:39 +01:00
Feng Chen
bf0b957c05 video_core: Implement maxwell3d draw manager and split draw logic 2022-12-08 10:12:19 +08:00
Morph
bfdd512787 Merge pull request #9365 from liamwhite/val
vulkan_common: quiet some validation errors
2022-12-06 21:08:14 -05:00
merry
e1f5f4bdea Merge pull request #9370 from liamwhite/break-unmapped
core: add option to break on unmapped access
2022-12-06 20:20:20 +00:00
Fernando S
08d4e7c7af Merge pull request #9393 from liamwhite/more-vulkan
vulkan_common: further initialization tweaks
2022-12-06 17:45:08 +01:00
liamwhite
4b7e73e0a6 Merge pull request #9392 from lioncash/reporter
reporter: Eliminate undefined behavior in SaveErrorReport
2022-12-06 11:27:00 -05:00
liamwhite
d8534ea140 Merge pull request #9390 from lioncash/keyboard
applets: Extract callback types into aliases
2022-12-06 11:26:53 -05:00
liamwhite
bbdb6d391e Merge pull request #9389 from lioncash/emumove
emulated_console/emulated_controller: std::move ParamPackage instances where applicable
2022-12-06 11:26:46 -05:00
liamwhite
a86af1b776 Merge pull request #9386 from lioncash/init
kernel: Ensure relevant class members are always initialized on construction
2022-12-06 11:26:38 -05:00
liamwhite
131ed37803 Merge pull request #9395 from abouvier/cmake-find
cmake: correct find modules
2022-12-06 09:58:55 -05:00
liamwhite
a225ba4cda Merge pull request #9391 from abouvier/cmake-sdl
cmake: use sdl2 imported target
2022-12-06 09:58:48 -05:00
liamwhite
e86e144a7c Merge pull request #9387 from abouvier/cmake-libusb
cmake: prefer system libusb
2022-12-06 09:58:37 -05:00
liamwhite
99494e77c3 Merge pull request #9394 from lioncash/translate
configure_graphics: Make SPIRV backend string translatable
2022-12-06 08:30:22 -05:00
Lioncash
dba84458be configure_graphics: Make SPIRV backend string translatable
The parenthetical needs to be translatable, like with GLASM
2022-12-06 00:13:07 -05:00
Alexandre Bouvier
bb3440f7c4 cmake: correct find modules 2022-12-06 05:32:09 +01:00
Alexandre Bouvier
f385175aa2 cmake: prefer system libusb 2022-12-06 05:30:40 +01:00
liamwhite
7c68f93bdf Merge pull request #9385 from Morph1984/dynarmic-ver
externals: Bump dynarmic to 6.4.0
2022-12-05 22:17:45 -05:00
liamwhite
04779b3d2a Merge pull request #9369 from german77/mifare
service: nfc: Implement mifare service
2022-12-05 22:17:32 -05:00
liamwhite
90145c424d Merge pull request #9360 from Kelebek1/R-E-S-P-E-C-T
Respect render mode override
2022-12-05 22:17:22 -05:00
Lioncash
eadc1ae1e7 reporter: Pass by const reference where applicable
Same behavior, but without memory churn.
2022-12-05 21:48:42 -05:00
Lioncash
e7f9f58fa4 reporter: Eliminate undefined behavior in SaveErrorReport
The optionals are unconditionally dereferenced when setting the custom
error text, and in a few cases this function is called using the default
value of the optionals.

This means we'd be dereferencing uninitialized storage.

Since they're used unconditionally, we can use value_or to set a default
when storage is uninitialized.
2022-12-05 21:31:34 -05:00
Alexandre Bouvier
e6ae720c33 cmake: use sdl2 imported target 2022-12-06 01:26:30 +01:00
Lioncash
fedd857054 applets/controller: Use aliases for callbacks 2022-12-05 19:06:04 -05:00
Lioncash
d8da9a2afd applets/error: Use aliases for callbacks 2022-12-05 19:06:04 -05:00
Lioncash
2b40cdf04f applets/mii_edit: Use aliases for callbacks 2022-12-05 19:06:04 -05:00
Lioncash
a84676c2aa applets/profile_select: Use aliases for callbacks
Deduplicates callback definitions and situates it in one place.
2022-12-05 19:06:04 -05:00
Lioncash
e26c86a6e7 applets/web_browser: Use aliases for callbacks
Deduplicates a lot of long callback declarations
2022-12-05 19:06:04 -05:00
Lioncash
9bbb77637e applets/software_keyboard: Use aliases for callbacks
Deduplicates really long std::function declarations to make the
interface nicer to read.
2022-12-05 19:06:01 -05:00
Lioncash
e4a16f50ef emulated_controller: Remove unused parameter in GetMappedDevices()
This isn't used, so it can be removed to make the function a little
nicer.
2022-12-05 18:27:18 -05:00
Lioncash
87543b9dea emulated_controller: Use std::move() in GetMappedDevices()
Avoids churning allocations in a loop.
2022-12-05 18:27:15 -05:00
Lioncash
7bf4b45349 emulated_console: Amend cast in SetTouch()
id is an int value, not a u32.
2022-12-05 18:15:23 -05:00
Lioncash
4255e30722 emulated_console: std::move() ParamPackages and callbacks where applicable 2022-12-05 18:15:19 -05:00
Lioncash
efa8711bf3 kernel/k_shared_memory: Ensure device_memory is always initialized 2022-12-05 15:27:57 -05:00
Lioncash
c3fd211b43 kernel/k_memory_block: Ensure members are always initialized 2022-12-05 15:27:47 -05:00
Lioncash
dcca650599 kernel/physical_core: Ensure is_interrupted is always initialized 2022-12-05 15:19:37 -05:00
Lioncash
dddc9bb8f1 kernel/thread: Ensure stack_top and argument are always initialized 2022-12-05 15:19:36 -05:00
Lioncash
4769d798f9 kernel/kernel: Ensure shutdown threads are always initialized 2022-12-05 15:19:36 -05:00
Morph
7381f873e9 externals: Bump dynarmic to 6.4.0
Uses the tagged, versioned release instead.
2022-12-05 13:08:00 -05:00
Narr the Reg
752236caad input_common: Allow mifare files 2022-12-04 20:02:48 -06:00
Liam
6072b22a0b core: add option to break on unmapped access 2022-12-02 08:25:45 -05:00
Narr the Reg
a4725bcb73 service: nfc: Implement mifare service 2022-12-01 20:43:59 -06:00
Liam
be05cb640c vulkan_common: quiet some validation errors 2022-11-30 19:18:05 -05:00
Kelebek1
a78372110c Respect render mode override 2022-11-30 01:43:36 +00:00
88 changed files with 1318 additions and 689 deletions

View File

@@ -31,8 +31,6 @@ CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" "${MSV
option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
option(YUZU_USE_BUNDLED_LIBUSB "Compile bundled libusb" OFF)
option(YUZU_USE_BUNDLED_FFMPEG "Download/Build bundled FFmpeg" "${WIN32}")
option(YUZU_USE_QT_MULTIMEDIA "Use QtMultimedia for Camera" OFF)
@@ -202,6 +200,7 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
find_package(enet 1.3)
find_package(fmt 9 REQUIRED)
find_package(inih)
find_package(libusb 1.0.24)
find_package(lz4 REQUIRED)
find_package(nlohmann_json 3.8 REQUIRED)
find_package(Opus 1.3)
@@ -214,7 +213,7 @@ if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64)
endif()
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
find_package(dynarmic 6.2.4)
find_package(dynarmic 6.4.0)
endif()
if (ENABLE_CUBEB)
@@ -433,23 +432,13 @@ if (ENABLE_SDL2)
set(SDL2_LIBRARY "${SDL2_PREFIX}/lib/x64/SDL2.lib" CACHE PATH "Path to SDL2 library")
set(SDL2_DLL_DIR "${SDL2_PREFIX}/lib/x64/" CACHE PATH "Path to SDL2.dll")
add_library(SDL2 INTERFACE)
target_link_libraries(SDL2 INTERFACE "${SDL2_LIBRARY}")
target_include_directories(SDL2 INTERFACE "${SDL2_INCLUDE_DIR}")
add_library(SDL2::SDL2 INTERFACE IMPORTED)
target_link_libraries(SDL2::SDL2 INTERFACE "${SDL2_LIBRARY}")
target_include_directories(SDL2::SDL2 INTERFACE "${SDL2_INCLUDE_DIR}")
elseif (YUZU_USE_EXTERNAL_SDL2)
message(STATUS "Using SDL2 from externals.")
else()
find_package(SDL2 2.0.18 REQUIRED)
# Some installations don't set SDL2_LIBRARIES
if("${SDL2_LIBRARIES}" STREQUAL "")
message(WARNING "SDL2_LIBRARIES wasn't set, manually setting to SDL2::SDL2")
set(SDL2_LIBRARIES "SDL2::SDL2")
endif()
include_directories(SYSTEM ${SDL2_INCLUDE_DIRS})
add_library(SDL2 INTERFACE)
target_link_libraries(SDL2 INTERFACE "${SDL2_LIBRARIES}")
endif()
endif()
@@ -461,26 +450,6 @@ if (TARGET Boost::boost)
add_library(boost ALIAS Boost::boost)
endif()
# Ensure libusb is properly configured (based on dolphin libusb include)
if(NOT YUZU_USE_BUNDLED_LIBUSB)
find_package(PkgConfig)
if (PKG_CONFIG_FOUND AND NOT CMAKE_SYSTEM_NAME MATCHES "DragonFly|FreeBSD")
pkg_check_modules(LIBUSB QUIET libusb-1.0>=1.0.24)
else()
find_package(LibUSB)
endif()
if (LIBUSB_FOUND)
add_library(usb INTERFACE)
target_include_directories(usb INTERFACE "${LIBUSB_INCLUDEDIR}" "${LIBUSB_INCLUDE_DIRS}")
target_link_directories(usb INTERFACE "${LIBUSB_LIBRARY_DIRS}")
target_link_libraries(usb INTERFACE "${LIBUSB_LIBRARIES}")
else()
message(WARNING "libusb not found, falling back to externals")
set(YUZU_USE_BUNDLED_LIBUSB ON)
endif()
endif()
# List of all FFmpeg components required
set(FFmpeg_COMPONENTS
avcodec

View File

@@ -45,8 +45,8 @@ if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "12" AND CMAKE_CXX_COMPILER
endif()
# libusb
if (NOT LIBUSB_FOUND OR YUZU_USE_BUNDLED_LIBUSB)
add_subdirectory(libusb)
if (NOT TARGET libusb::usb)
add_subdirectory(libusb EXCLUDE_FROM_ALL)
endif()
# SDL2

View File

@@ -1,44 +0,0 @@
# SPDX-FileCopyrightText: 2009 Michal Cihar <michal@cihar.com>
# SPDX-License-Identifier: GPL-2.0-or-later
# - Find libusb-1.0 library
# This module defines
# LIBUSB_INCLUDE_DIR, where to find bluetooth.h
# LIBUSB_LIBRARIES, the libraries needed to use libusb-1.0.
# LIBUSB_FOUND, If false, do not try to use libusb-1.0.
#
# vim: expandtab sw=4 ts=4 sts=4:
if(ANDROID)
set(LIBUSB_FOUND FALSE CACHE INTERNAL "libusb-1.0 found")
message(STATUS "libusb-1.0 not found.")
elseif (NOT LIBUSB_FOUND)
pkg_check_modules (LIBUSB_PKG libusb-1.0)
find_path(LIBUSB_INCLUDE_DIR NAMES libusb.h
PATHS
${LIBUSB_PKG_INCLUDE_DIRS}
/usr/include/libusb-1.0
/usr/include
/usr/local/include/libusb-1.0
/usr/local/include
)
find_library(LIBUSB_LIBRARIES NAMES usb-1.0 usb
PATHS
${LIBUSB_PKG_LIBRARY_DIRS}
/usr/lib
/usr/local/lib
)
if(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES)
set(LIBUSB_FOUND TRUE CACHE INTERNAL "libusb-1.0 found")
message(STATUS "Found libusb-1.0: ${LIBUSB_INCLUDE_DIR}, ${LIBUSB_LIBRARIES}")
else(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES)
set(LIBUSB_FOUND FALSE CACHE INTERNAL "libusb-1.0 found")
message(STATUS "libusb-1.0 not found.")
endif(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES)
mark_as_advanced(LIBUSB_INCLUDE_DIR LIBUSB_LIBRARIES)
endif ()

View File

@@ -1,19 +1,17 @@
# SPDX-FileCopyrightText: 2022 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
find_package(PkgConfig)
find_package(PkgConfig QUIET)
if (PKG_CONFIG_FOUND)
pkg_search_module(opus IMPORTED_TARGET GLOBAL opus)
if (opus_FOUND)
add_library(Opus::opus ALIAS PkgConfig::opus)
endif()
pkg_search_module(OPUS QUIET IMPORTED_TARGET opus)
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Opus
REQUIRED_VARS
opus_LINK_LIBRARIES
opus_FOUND
VERSION_VAR opus_VERSION
REQUIRED_VARS OPUS_LINK_LIBRARIES
VERSION_VAR OPUS_VERSION
)
if (Opus_FOUND AND NOT TARGET Opus::opus)
add_library(Opus::opus ALIAS PkgConfig::OPUS)
endif()

View File

@@ -4,10 +4,7 @@
find_package(PkgConfig QUIET)
if (PKG_CONFIG_FOUND)
pkg_search_module(ENET QUIET IMPORTED_TARGET GLOBAL libenet)
if (ENET_FOUND)
add_library(enet::enet ALIAS PkgConfig::ENET)
endif()
pkg_search_module(ENET QUIET IMPORTED_TARGET libenet)
endif()
include(FindPackageHandleStandardArgs)
@@ -15,3 +12,7 @@ find_package_handle_standard_args(enet
REQUIRED_VARS ENET_LINK_LIBRARIES
VERSION_VAR ENET_VERSION
)
if (enet_FOUND AND NOT TARGET enet::enet)
add_library(enet::enet ALIAS PkgConfig::ENET)
endif()

View File

@@ -10,13 +10,14 @@ if (httplib_FOUND)
else()
find_package(PkgConfig QUIET)
if (PKG_CONFIG_FOUND)
pkg_search_module(HTTPLIB QUIET IMPORTED_TARGET GLOBAL cpp-httplib)
if (HTTPLIB_FOUND)
add_library(httplib::httplib ALIAS PkgConfig::HTTPLIB)
endif()
pkg_search_module(HTTPLIB QUIET IMPORTED_TARGET cpp-httplib)
endif()
find_package_handle_standard_args(httplib
REQUIRED_VARS HTTPLIB_INCLUDEDIR
VERSION_VAR HTTPLIB_VERSION
)
endif()
if (httplib_FOUND AND NOT TARGET httplib::httplib)
add_library(httplib::httplib ALIAS PkgConfig::HTTPLIB)
endif()

View File

@@ -4,10 +4,7 @@
find_package(PkgConfig QUIET)
if (PKG_CONFIG_FOUND)
pkg_search_module(INIREADER QUIET IMPORTED_TARGET GLOBAL INIReader)
if (INIREADER_FOUND)
add_library(inih::INIReader ALIAS PkgConfig::INIREADER)
endif()
pkg_search_module(INIREADER QUIET IMPORTED_TARGET INIReader)
endif()
include(FindPackageHandleStandardArgs)
@@ -15,3 +12,7 @@ find_package_handle_standard_args(inih
REQUIRED_VARS INIREADER_LINK_LIBRARIES
VERSION_VAR INIREADER_VERSION
)
if (inih_FOUND AND NOT TARGET inih::INIReader)
add_library(inih::INIReader ALIAS PkgConfig::INIREADER)
endif()

18
externals/find-modules/Findlibusb.cmake vendored Normal file
View File

@@ -0,0 +1,18 @@
# SPDX-FileCopyrightText: 2022 Alexandre Bouvier <contact@amb.tf>
#
# SPDX-License-Identifier: GPL-3.0-or-later
find_package(PkgConfig QUIET)
if (PKG_CONFIG_FOUND)
pkg_search_module(LIBUSB QUIET IMPORTED_TARGET libusb-1.0)
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(libusb
REQUIRED_VARS LIBUSB_LINK_LIBRARIES
VERSION_VAR LIBUSB_VERSION
)
if (libusb_FOUND AND NOT TARGET libusb::usb)
add_library(libusb::usb ALIAS PkgConfig::LIBUSB)
endif()

View File

@@ -6,25 +6,23 @@ include(FindPackageHandleStandardArgs)
find_package(lz4 QUIET CONFIG)
if (lz4_FOUND)
find_package_handle_standard_args(lz4 CONFIG_MODE)
if (NOT TARGET lz4::lz4)
if (TARGET LZ4::lz4_shared)
set_target_properties(LZ4::lz4_shared PROPERTIES IMPORTED_GLOBAL TRUE)
add_library(lz4::lz4 ALIAS LZ4::lz4_shared)
else()
set_target_properties(LZ4::lz4_static PROPERTIES IMPORTED_GLOBAL TRUE)
add_library(lz4::lz4 ALIAS LZ4::lz4_static)
endif()
endif()
else()
find_package(PkgConfig QUIET)
if (PKG_CONFIG_FOUND)
pkg_search_module(liblz4 QUIET IMPORTED_TARGET GLOBAL liblz4)
if (liblz4_FOUND)
add_library(lz4::lz4 ALIAS PkgConfig::liblz4)
endif()
pkg_search_module(LZ4 QUIET IMPORTED_TARGET liblz4)
endif()
find_package_handle_standard_args(lz4
REQUIRED_VARS liblz4_LINK_LIBRARIES
VERSION_VAR liblz4_VERSION
REQUIRED_VARS LZ4_LINK_LIBRARIES
VERSION_VAR LZ4_VERSION
)
endif()
if (lz4_FOUND AND NOT TARGET lz4::lz4)
if (TARGET LZ4::lz4_shared)
add_library(lz4::lz4 ALIAS LZ4::lz4_shared)
elseif (TARGET LZ4::lz4_static)
add_library(lz4::lz4 ALIAS LZ4::lz4_static)
else()
add_library(lz4::lz4 ALIAS PkgConfig::LZ4)
endif()
endif()

View File

@@ -6,25 +6,23 @@ include(FindPackageHandleStandardArgs)
find_package(zstd QUIET CONFIG)
if (zstd_FOUND)
find_package_handle_standard_args(zstd CONFIG_MODE)
if (NOT TARGET zstd::zstd)
if (TARGET zstd::libzstd_shared)
set_target_properties(zstd::libzstd_shared PROPERTIES IMPORTED_GLOBAL TRUE)
add_library(zstd::zstd ALIAS zstd::libzstd_shared)
else()
set_target_properties(zstd::libzstd_static PROPERTIES IMPORTED_GLOBAL TRUE)
add_library(zstd::zstd ALIAS zstd::libzstd_static)
endif()
endif()
else()
find_package(PkgConfig QUIET)
if (PKG_CONFIG_FOUND)
pkg_search_module(libzstd QUIET IMPORTED_TARGET GLOBAL libzstd)
if (libzstd_FOUND)
add_library(zstd::zstd ALIAS PkgConfig::libzstd)
endif()
pkg_search_module(ZSTD QUIET IMPORTED_TARGET libzstd)
endif()
find_package_handle_standard_args(zstd
REQUIRED_VARS libzstd_LINK_LIBRARIES
VERSION_VAR libzstd_VERSION
REQUIRED_VARS ZSTD_LINK_LIBRARIES
VERSION_VAR ZSTD_VERSION
)
endif()
if (zstd_FOUND AND NOT TARGET zstd::zstd)
if (TARGET zstd::libzstd_shared)
add_library(zstd::zstd ALIAS zstd::libzstd_shared)
elseif (TARGET zstd::libzstd_static)
add_library(zstd::zstd ALIAS zstd::libzstd_static)
else()
add_library(zstd::zstd ALIAS PkgConfig::ZSTD)
endif()
endif()

View File

@@ -273,3 +273,5 @@ else() # MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
configure_file(config.h.in config.h)
endif() # MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
add_library(libusb::usb ALIAS usb)

View File

@@ -227,11 +227,7 @@ if(ENABLE_CUBEB)
target_compile_definitions(audio_core PRIVATE -DHAVE_CUBEB=1)
endif()
if(ENABLE_SDL2)
if (YUZU_USE_EXTERNAL_SDL2)
target_link_libraries(audio_core PRIVATE SDL2-static)
else()
target_link_libraries(audio_core PRIVATE SDL2)
endif()
target_link_libraries(audio_core PRIVATE SDL2::SDL2)
target_compile_definitions(audio_core PRIVATE HAVE_SDL2)
endif()

View File

@@ -16,7 +16,6 @@ class CommandGenerator;
*/
class DetailAspect {
public:
DetailAspect() = default;
DetailAspect(CommandGenerator& command_generator, PerformanceEntryType entry_type, s32 node_id,
PerformanceDetailType detail_type);

View File

@@ -16,7 +16,6 @@ class CommandGenerator;
*/
class EntryAspect {
public:
EntryAspect() = default;
EntryAspect(CommandGenerator& command_generator, PerformanceEntryType type, s32 node_id);
/// Command generator the command will be generated into

View File

@@ -400,6 +400,7 @@ struct Values {
Setting<bool> cpuopt_fastmem{true, "cpuopt_fastmem"};
Setting<bool> cpuopt_fastmem_exclusives{true, "cpuopt_fastmem_exclusives"};
Setting<bool> cpuopt_recompile_exclusives{true, "cpuopt_recompile_exclusives"};
Setting<bool> cpuopt_ignore_memory_aborts{true, "cpuopt_ignore_memory_aborts"};
SwitchableSetting<bool> cpuopt_unsafe_unfuse_fma{true, "cpuopt_unsafe_unfuse_fma"};
SwitchableSetting<bool> cpuopt_unsafe_reduce_fp_error{true, "cpuopt_unsafe_reduce_fp_error"};

View File

@@ -528,6 +528,8 @@ add_library(core STATIC
hle/service/mnpp/mnpp_app.h
hle/service/ncm/ncm.cpp
hle/service/ncm/ncm.h
hle/service/nfc/mifare_user.cpp
hle/service/nfc/mifare_user.h
hle/service/nfc/nfc.cpp
hle/service/nfc/nfc.h
hle/service/nfc/nfc_device.cpp

View File

@@ -145,11 +145,15 @@ void ARM_Interface::Run() {
// Notify the debugger and go to sleep if a breakpoint was hit,
// or if the thread is unable to continue for any reason.
if (Has(hr, breakpoint) || Has(hr, no_execute)) {
RewindBreakpointInstruction();
if (!Has(hr, no_execute)) {
RewindBreakpointInstruction();
}
if (system.DebuggerEnabled()) {
system.GetDebugger().NotifyThreadStopped(current_thread);
} else {
LogBacktrace();
}
current_thread->RequestSuspend(Kernel::SuspendType::Debug);
current_thread->RequestSuspend(SuspendType::Debug);
break;
}

View File

@@ -29,7 +29,9 @@ class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
public:
explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_)
: parent{parent_},
memory(parent.system.Memory()), debugger_enabled{parent.system.DebuggerEnabled()} {}
memory(parent.system.Memory()), debugger_enabled{parent.system.DebuggerEnabled()},
check_memory_access{debugger_enabled ||
!Settings::values.cpuopt_ignore_memory_aborts.GetValue()} {}
u8 MemoryRead8(u32 vaddr) override {
CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
@@ -154,6 +156,17 @@ public:
}
bool CheckMemoryAccess(VAddr addr, u64 size, Kernel::DebugWatchpointType type) {
if (!check_memory_access) {
return true;
}
if (!memory.IsValidVirtualAddressRange(addr, size)) {
LOG_CRITICAL(Core_ARM, "Stopping execution due to unmapped memory access at {:#x}",
addr);
parent.jit.load()->HaltExecution(ARM_Interface::no_execute);
return false;
}
if (!debugger_enabled) {
return true;
}
@@ -181,7 +194,8 @@ public:
ARM_Dynarmic_32& parent;
Core::Memory::Memory& memory;
std::size_t num_interpreted_instructions{};
bool debugger_enabled{};
const bool debugger_enabled{};
const bool check_memory_access{};
static constexpr u64 minimum_run_cycles = 10000U;
};
@@ -264,6 +278,9 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
if (!Settings::values.cpuopt_recompile_exclusives) {
config.recompile_on_exclusive_fastmem_failure = false;
}
if (!Settings::values.cpuopt_ignore_memory_aborts) {
config.check_halt_on_memory_access = true;
}
} else {
// Unsafe optimizations
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {

View File

@@ -29,7 +29,9 @@ class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
public:
explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_)
: parent{parent_},
memory(parent.system.Memory()), debugger_enabled{parent.system.DebuggerEnabled()} {}
memory(parent.system.Memory()), debugger_enabled{parent.system.DebuggerEnabled()},
check_memory_access{debugger_enabled ||
!Settings::values.cpuopt_ignore_memory_aborts.GetValue()} {}
u8 MemoryRead8(u64 vaddr) override {
CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
@@ -198,6 +200,17 @@ public:
}
bool CheckMemoryAccess(VAddr addr, u64 size, Kernel::DebugWatchpointType type) {
if (!check_memory_access) {
return true;
}
if (!memory.IsValidVirtualAddressRange(addr, size)) {
LOG_CRITICAL(Core_ARM, "Stopping execution due to unmapped memory access at {:#x}",
addr);
parent.jit.load()->HaltExecution(ARM_Interface::no_execute);
return false;
}
if (!debugger_enabled) {
return true;
}
@@ -226,7 +239,8 @@ public:
Core::Memory::Memory& memory;
u64 tpidrro_el0 = 0;
u64 tpidr_el0 = 0;
bool debugger_enabled{};
const bool debugger_enabled{};
const bool check_memory_access{};
static constexpr u64 minimum_run_cycles = 10000U;
};
@@ -323,6 +337,9 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
if (!Settings::values.cpuopt_recompile_exclusives) {
config.recompile_on_exclusive_fastmem_failure = false;
}
if (!Settings::values.cpuopt_ignore_memory_aborts) {
config.check_halt_on_memory_access = true;
}
} else {
// Unsafe optimizations
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {

View File

@@ -16,7 +16,7 @@ DefaultControllerApplet::DefaultControllerApplet(HID::HIDCore& hid_core_) : hid_
DefaultControllerApplet::~DefaultControllerApplet() = default;
void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callback,
void DefaultControllerApplet::ReconfigureControllers(ReconfigureCallback callback,
const ControllerParameters& parameters) const {
LOG_INFO(Service_HID, "called, deducing the best configuration based on the given parameters!");

View File

@@ -36,9 +36,11 @@ struct ControllerParameters {
class ControllerApplet {
public:
using ReconfigureCallback = std::function<void()>;
virtual ~ControllerApplet();
virtual void ReconfigureControllers(std::function<void()> callback,
virtual void ReconfigureControllers(ReconfigureCallback callback,
const ControllerParameters& parameters) const = 0;
};
@@ -47,7 +49,7 @@ public:
explicit DefaultControllerApplet(HID::HIDCore& hid_core_);
~DefaultControllerApplet() override;
void ReconfigureControllers(std::function<void()> callback,
void ReconfigureControllers(ReconfigureCallback callback,
const ControllerParameters& parameters) const override;
private:

View File

@@ -8,13 +8,13 @@ namespace Core::Frontend {
ErrorApplet::~ErrorApplet() = default;
void DefaultErrorApplet::ShowError(Result error, std::function<void()> finished) const {
void DefaultErrorApplet::ShowError(Result error, FinishedCallback finished) const {
LOG_CRITICAL(Service_Fatal, "Application requested error display: {:04}-{:04} (raw={:08X})",
error.module.Value(), error.description.Value(), error.raw);
}
void DefaultErrorApplet::ShowErrorWithTimestamp(Result error, std::chrono::seconds time,
std::function<void()> finished) const {
FinishedCallback finished) const {
LOG_CRITICAL(
Service_Fatal,
"Application requested error display: {:04X}-{:04X} (raw={:08X}) with timestamp={:016X}",
@@ -23,7 +23,7 @@ void DefaultErrorApplet::ShowErrorWithTimestamp(Result error, std::chrono::secon
void DefaultErrorApplet::ShowCustomErrorText(Result error, std::string main_text,
std::string detail_text,
std::function<void()> finished) const {
FinishedCallback finished) const {
LOG_CRITICAL(Service_Fatal,
"Application requested custom error with error_code={:04X}-{:04X} (raw={:08X})",
error.module.Value(), error.description.Value(), error.raw);

View File

@@ -12,25 +12,27 @@ namespace Core::Frontend {
class ErrorApplet {
public:
using FinishedCallback = std::function<void()>;
virtual ~ErrorApplet();
virtual void ShowError(Result error, std::function<void()> finished) const = 0;
virtual void ShowError(Result error, FinishedCallback finished) const = 0;
virtual void ShowErrorWithTimestamp(Result error, std::chrono::seconds time,
std::function<void()> finished) const = 0;
FinishedCallback finished) const = 0;
virtual void ShowCustomErrorText(Result error, std::string dialog_text,
std::string fullscreen_text,
std::function<void()> finished) const = 0;
FinishedCallback finished) const = 0;
};
class DefaultErrorApplet final : public ErrorApplet {
public:
void ShowError(Result error, std::function<void()> finished) const override;
void ShowError(Result error, FinishedCallback finished) const override;
void ShowErrorWithTimestamp(Result error, std::chrono::seconds time,
std::function<void()> finished) const override;
FinishedCallback finished) const override;
void ShowCustomErrorText(Result error, std::string main_text, std::string detail_text,
std::function<void()> finished) const override;
FinishedCallback finished) const override;
};
} // namespace Core::Frontend

View File

@@ -8,7 +8,7 @@ namespace Core::Frontend {
MiiEditApplet::~MiiEditApplet() = default;
void DefaultMiiEditApplet::ShowMiiEdit(const std::function<void()>& callback) const {
void DefaultMiiEditApplet::ShowMiiEdit(const MiiEditCallback& callback) const {
LOG_WARNING(Service_AM, "(STUBBED) called");
callback();

View File

@@ -9,14 +9,16 @@ namespace Core::Frontend {
class MiiEditApplet {
public:
using MiiEditCallback = std::function<void()>;
virtual ~MiiEditApplet();
virtual void ShowMiiEdit(const std::function<void()>& callback) const = 0;
virtual void ShowMiiEdit(const MiiEditCallback& callback) const = 0;
};
class DefaultMiiEditApplet final : public MiiEditApplet {
public:
void ShowMiiEdit(const std::function<void()>& callback) const override;
void ShowMiiEdit(const MiiEditCallback& callback) const override;
};
} // namespace Core::Frontend

View File

@@ -9,8 +9,7 @@ namespace Core::Frontend {
ProfileSelectApplet::~ProfileSelectApplet() = default;
void DefaultProfileSelectApplet::SelectProfile(
std::function<void(std::optional<Common::UUID>)> callback) const {
void DefaultProfileSelectApplet::SelectProfile(SelectProfileCallback callback) const {
Service::Account::ProfileManager manager;
callback(manager.GetUser(Settings::values.current_user.GetValue()).value_or(Common::UUID{}));
LOG_INFO(Service_ACC, "called, selecting current user instead of prompting...");

View File

@@ -11,14 +11,16 @@ namespace Core::Frontend {
class ProfileSelectApplet {
public:
using SelectProfileCallback = std::function<void(std::optional<Common::UUID>)>;
virtual ~ProfileSelectApplet();
virtual void SelectProfile(std::function<void(std::optional<Common::UUID>)> callback) const = 0;
virtual void SelectProfile(SelectProfileCallback callback) const = 0;
};
class DefaultProfileSelectApplet final : public ProfileSelectApplet {
public:
void SelectProfile(std::function<void(std::optional<Common::UUID>)> callback) const override;
void SelectProfile(SelectProfileCallback callback) const override;
};
} // namespace Core::Frontend

View File

@@ -15,10 +15,7 @@ DefaultSoftwareKeyboardApplet::~DefaultSoftwareKeyboardApplet() = default;
void DefaultSoftwareKeyboardApplet::InitializeKeyboard(
bool is_inline, KeyboardInitializeParameters initialize_parameters,
std::function<void(Service::AM::Applets::SwkbdResult, std::u16string, bool)>
submit_normal_callback_,
std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>
submit_inline_callback_) {
SubmitNormalCallback submit_normal_callback_, SubmitInlineCallback submit_inline_callback_) {
if (is_inline) {
LOG_WARNING(
Service_AM,

View File

@@ -54,14 +54,17 @@ struct InlineTextParameters {
class SoftwareKeyboardApplet {
public:
using SubmitInlineCallback =
std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>;
using SubmitNormalCallback =
std::function<void(Service::AM::Applets::SwkbdResult, std::u16string, bool)>;
virtual ~SoftwareKeyboardApplet();
virtual void InitializeKeyboard(
bool is_inline, KeyboardInitializeParameters initialize_parameters,
std::function<void(Service::AM::Applets::SwkbdResult, std::u16string, bool)>
submit_normal_callback_,
std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>
submit_inline_callback_) = 0;
virtual void InitializeKeyboard(bool is_inline,
KeyboardInitializeParameters initialize_parameters,
SubmitNormalCallback submit_normal_callback_,
SubmitInlineCallback submit_inline_callback_) = 0;
virtual void ShowNormalKeyboard() const = 0;
@@ -81,12 +84,9 @@ class DefaultSoftwareKeyboardApplet final : public SoftwareKeyboardApplet {
public:
~DefaultSoftwareKeyboardApplet() override;
void InitializeKeyboard(
bool is_inline, KeyboardInitializeParameters initialize_parameters,
std::function<void(Service::AM::Applets::SwkbdResult, std::u16string, bool)>
submit_normal_callback_,
std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>
submit_inline_callback_) override;
void InitializeKeyboard(bool is_inline, KeyboardInitializeParameters initialize_parameters,
SubmitNormalCallback submit_normal_callback_,
SubmitInlineCallback submit_inline_callback_) override;
void ShowNormalKeyboard() const override;
@@ -105,12 +105,10 @@ private:
void SubmitNormalText(std::u16string text) const;
void SubmitInlineText(std::u16string_view text) const;
KeyboardInitializeParameters parameters;
KeyboardInitializeParameters parameters{};
mutable std::function<void(Service::AM::Applets::SwkbdResult, std::u16string, bool)>
submit_normal_callback;
mutable std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>
submit_inline_callback;
mutable SubmitNormalCallback submit_normal_callback;
mutable SubmitInlineCallback submit_inline_callback;
};
} // namespace Core::Frontend

View File

@@ -10,18 +10,17 @@ WebBrowserApplet::~WebBrowserApplet() = default;
DefaultWebBrowserApplet::~DefaultWebBrowserApplet() = default;
void DefaultWebBrowserApplet::OpenLocalWebPage(
const std::string& local_url, std::function<void()> extract_romfs_callback,
std::function<void(Service::AM::Applets::WebExitReason, std::string)> callback) const {
void DefaultWebBrowserApplet::OpenLocalWebPage(const std::string& local_url,
ExtractROMFSCallback extract_romfs_callback,
OpenWebPageCallback callback) const {
LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to open local web page at {}",
local_url);
callback(Service::AM::Applets::WebExitReason::WindowClosed, "http://localhost/");
}
void DefaultWebBrowserApplet::OpenExternalWebPage(
const std::string& external_url,
std::function<void(Service::AM::Applets::WebExitReason, std::string)> callback) const {
void DefaultWebBrowserApplet::OpenExternalWebPage(const std::string& external_url,
OpenWebPageCallback callback) const {
LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to open external web page at {}",
external_url);

View File

@@ -11,29 +11,29 @@ namespace Core::Frontend {
class WebBrowserApplet {
public:
using ExtractROMFSCallback = std::function<void()>;
using OpenWebPageCallback =
std::function<void(Service::AM::Applets::WebExitReason, std::string)>;
virtual ~WebBrowserApplet();
virtual void OpenLocalWebPage(
const std::string& local_url, std::function<void()> extract_romfs_callback,
std::function<void(Service::AM::Applets::WebExitReason, std::string)> callback) const = 0;
virtual void OpenLocalWebPage(const std::string& local_url,
ExtractROMFSCallback extract_romfs_callback,
OpenWebPageCallback callback) const = 0;
virtual void OpenExternalWebPage(
const std::string& external_url,
std::function<void(Service::AM::Applets::WebExitReason, std::string)> callback) const = 0;
virtual void OpenExternalWebPage(const std::string& external_url,
OpenWebPageCallback callback) const = 0;
};
class DefaultWebBrowserApplet final : public WebBrowserApplet {
public:
~DefaultWebBrowserApplet() override;
void OpenLocalWebPage(const std::string& local_url,
std::function<void()> extract_romfs_callback,
std::function<void(Service::AM::Applets::WebExitReason, std::string)>
callback) const override;
void OpenLocalWebPage(const std::string& local_url, ExtractROMFSCallback extract_romfs_callback,
OpenWebPageCallback callback) const override;
void OpenExternalWebPage(const std::string& external_url,
std::function<void(Service::AM::Applets::WebExitReason, std::string)>
callback) const override;
OpenWebPageCallback callback) const override;
};
} // namespace Core::Frontend

View File

@@ -37,7 +37,7 @@ void EmulatedConsole::SetTouchParams() {
touchscreen_param.Set("axis_x", i * 2);
touchscreen_param.Set("axis_y", (i * 2) + 1);
touchscreen_param.Set("button", i);
touch_params[index++] = touchscreen_param;
touch_params[index++] = std::move(touchscreen_param);
}
const auto button_index =
@@ -59,7 +59,7 @@ void EmulatedConsole::SetTouchParams() {
touch_button_params.Set("button", params.Serialize());
touch_button_params.Set("x", x);
touch_button_params.Set("y", y);
touch_params[index] = touch_button_params;
touch_params[index] = std::move(touch_button_params);
index++;
}
}
@@ -131,7 +131,7 @@ Common::ParamPackage EmulatedConsole::GetMotionParam() const {
}
void EmulatedConsole::SetMotionParam(Common::ParamPackage param) {
motion_params = param;
motion_params = std::move(param);
ReloadInput();
}
@@ -199,7 +199,7 @@ void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, st
if (is_new_input) {
touch_value.pressed.value = true;
touch_value.id = static_cast<u32>(index);
touch_value.id = static_cast<int>(index);
}
touch_value.x = touch_input.x;
@@ -284,7 +284,7 @@ void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) {
int EmulatedConsole::SetCallback(ConsoleUpdateCallback update_callback) {
std::scoped_lock lock{callback_mutex};
callback_list.insert_or_assign(last_callback_key, update_callback);
callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
return last_callback_key++;
}

View File

@@ -424,15 +424,14 @@ void EmulatedController::RestoreConfig() {
ReloadFromSettings();
}
std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices(
EmulatedDeviceIndex device_index) const {
std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices() const {
std::vector<Common::ParamPackage> devices;
for (const auto& param : button_params) {
if (!param.Has("engine")) {
continue;
}
const auto devices_it = std::find_if(
devices.begin(), devices.end(), [param](const Common::ParamPackage param_) {
devices.begin(), devices.end(), [&param](const Common::ParamPackage& param_) {
return param.Get("engine", "") == param_.Get("engine", "") &&
param.Get("guid", "") == param_.Get("guid", "") &&
param.Get("port", 0) == param_.Get("port", 0) &&
@@ -441,12 +440,12 @@ std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices(
if (devices_it != devices.end()) {
continue;
}
Common::ParamPackage device{};
auto& device = devices.emplace_back();
device.Set("engine", param.Get("engine", ""));
device.Set("guid", param.Get("guid", ""));
device.Set("port", param.Get("port", 0));
device.Set("pad", param.Get("pad", 0));
devices.push_back(device);
}
for (const auto& param : stick_params) {
@@ -457,7 +456,7 @@ std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices(
continue;
}
const auto devices_it = std::find_if(
devices.begin(), devices.end(), [param](const Common::ParamPackage param_) {
devices.begin(), devices.end(), [&param](const Common::ParamPackage& param_) {
return param.Get("engine", "") == param_.Get("engine", "") &&
param.Get("guid", "") == param_.Get("guid", "") &&
param.Get("port", 0) == param_.Get("port", 0) &&
@@ -466,12 +465,12 @@ std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices(
if (devices_it != devices.end()) {
continue;
}
Common::ParamPackage device{};
auto& device = devices.emplace_back();
device.Set("engine", param.Get("engine", ""));
device.Set("guid", param.Get("guid", ""));
device.Set("port", param.Get("port", 0));
device.Set("pad", param.Get("pad", 0));
devices.push_back(device);
}
return devices;
}

View File

@@ -244,7 +244,7 @@ public:
void RestoreConfig();
/// Returns a vector of mapped devices from the mapped button and stick parameters
std::vector<Common::ParamPackage> GetMappedDevices(EmulatedDeviceIndex device_index) const;
std::vector<Common::ParamPackage> GetMappedDevices() const;
// Returns the current mapped button device
Common::ParamPackage GetButtonParam(std::size_t index) const;

View File

@@ -280,18 +280,19 @@ struct KMemoryInfo {
class KMemoryBlock : public Common::IntrusiveRedBlackTreeBaseNode<KMemoryBlock> {
private:
u16 m_device_disable_merge_left_count;
u16 m_device_disable_merge_right_count;
VAddr m_address;
size_t m_num_pages;
KMemoryState m_memory_state;
u16 m_ipc_lock_count;
u16 m_device_use_count;
u16 m_ipc_disable_merge_count;
KMemoryPermission m_permission;
KMemoryPermission m_original_permission;
KMemoryAttribute m_attribute;
KMemoryBlockDisableMergeAttribute m_disable_merge_attribute;
u16 m_device_disable_merge_left_count{};
u16 m_device_disable_merge_right_count{};
VAddr m_address{};
size_t m_num_pages{};
KMemoryState m_memory_state{KMemoryState::None};
u16 m_ipc_lock_count{};
u16 m_device_use_count{};
u16 m_ipc_disable_merge_count{};
KMemoryPermission m_permission{KMemoryPermission::None};
KMemoryPermission m_original_permission{KMemoryPermission::None};
KMemoryAttribute m_attribute{KMemoryAttribute::None};
KMemoryBlockDisableMergeAttribute m_disable_merge_attribute{
KMemoryBlockDisableMergeAttribute::None};
public:
static constexpr int Compare(const KMemoryBlock& lhs, const KMemoryBlock& rhs) {
@@ -367,12 +368,8 @@ public:
constexpr KMemoryBlock(VAddr addr, size_t np, KMemoryState ms, KMemoryPermission p,
KMemoryAttribute attr)
: Common::IntrusiveRedBlackTreeBaseNode<KMemoryBlock>(),
m_device_disable_merge_left_count(), m_device_disable_merge_right_count(),
m_address(addr), m_num_pages(np), m_memory_state(ms), m_ipc_lock_count(0),
m_device_use_count(0), m_ipc_disable_merge_count(), m_permission(p),
m_original_permission(KMemoryPermission::None), m_attribute(attr),
m_disable_merge_attribute() {}
: Common::IntrusiveRedBlackTreeBaseNode<KMemoryBlock>(), m_address(addr), m_num_pages(np),
m_memory_state(ms), m_permission(p), m_attribute(attr) {}
constexpr void Initialize(VAddr addr, size_t np, KMemoryState ms, KMemoryPermission p,
KMemoryAttribute attr) {

View File

@@ -3,6 +3,7 @@
#pragma once
#include <array>
#include <functional>
#include "common/common_funcs.h"
@@ -17,9 +18,9 @@ public:
static constexpr size_t MaxBlocks = 2;
private:
KMemoryBlock* m_blocks[MaxBlocks];
size_t m_index;
KMemoryBlockSlabManager* m_slab_manager;
std::array<KMemoryBlock*, MaxBlocks> m_blocks{};
size_t m_index{MaxBlocks};
KMemoryBlockSlabManager* m_slab_manager{};
private:
Result Initialize(size_t num_blocks) {
@@ -41,7 +42,7 @@ private:
public:
KMemoryBlockManagerUpdateAllocator(Result* out_result, KMemoryBlockSlabManager* sm,
size_t num_blocks = MaxBlocks)
: m_blocks(), m_index(MaxBlocks), m_slab_manager(sm) {
: m_slab_manager(sm) {
*out_result = this->Initialize(num_blocks);
}

View File

@@ -74,7 +74,7 @@ public:
static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
private:
Core::DeviceMemory* device_memory;
Core::DeviceMemory* device_memory{};
KProcess* owner_process{};
KPageGroup page_list;
Svc::MemoryPermission owner_permission{};

View File

@@ -784,8 +784,8 @@ private:
std::vector<KSynchronizationObject*> wait_objects_for_debugging;
VAddr mutex_wait_address_for_debugging{};
ThreadWaitReasonForDebugging wait_reason_for_debugging{};
uintptr_t argument;
VAddr stack_top;
uintptr_t argument{};
VAddr stack_top{};
public:
using ConditionVariableThreadTreeType = ConditionVariableThreadTree;

View File

@@ -891,7 +891,7 @@ struct KernelCore::Impl {
Common::ThreadWorker service_threads_manager;
Common::Barrier service_thread_barrier;
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> shutdown_threads;
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> shutdown_threads{};
std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{};
bool is_multicore{};

View File

@@ -85,7 +85,7 @@ private:
std::mutex guard;
std::condition_variable on_interrupt;
std::unique_ptr<Core::ARM_Interface> arm_interface;
bool is_interrupted;
bool is_interrupted{};
};
} // namespace Kernel

View File

@@ -163,9 +163,6 @@ ServiceThread::Impl::~Impl() {
m_wakeup_event->Signal();
m_host_thread.join();
// Lock mutex.
m_session_mutex.lock();
// Close all remaining sessions.
for (const auto& [server_session, manager] : m_sessions) {
server_session->Close();

View File

@@ -0,0 +1,400 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hid/hid_types.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/nfc/mifare_user.h"
#include "core/hle/service/nfc/nfc_device.h"
#include "core/hle/service/nfc/nfc_result.h"
namespace Service::NFC {
MFIUser::MFIUser(Core::System& system_)
: ServiceFramework{system_, "NFC::MFIUser"}, service_context{system_, service_name} {
static const FunctionInfo functions[] = {
{0, &MFIUser::Initialize, "Initialize"},
{1, &MFIUser::Finalize, "Finalize"},
{2, &MFIUser::ListDevices, "ListDevices"},
{3, &MFIUser::StartDetection, "StartDetection"},
{4, &MFIUser::StopDetection, "StopDetection"},
{5, &MFIUser::Read, "Read"},
{6, &MFIUser::Write, "Write"},
{7, &MFIUser::GetTagInfo, "GetTagInfo"},
{8, &MFIUser::GetActivateEventHandle, "GetActivateEventHandle"},
{9, &MFIUser::GetDeactivateEventHandle, "GetDeactivateEventHandle"},
{10, &MFIUser::GetState, "GetState"},
{11, &MFIUser::GetDeviceState, "GetDeviceState"},
{12, &MFIUser::GetNpadId, "GetNpadId"},
{13, &MFIUser::GetAvailabilityChangeEventHandle, "GetAvailabilityChangeEventHandle"},
};
RegisterHandlers(functions);
availability_change_event = service_context.CreateEvent("MFIUser:AvailabilityChangeEvent");
for (u32 device_index = 0; device_index < 10; device_index++) {
devices[device_index] =
std::make_shared<NfcDevice>(Core::HID::IndexToNpadIdType(device_index), system,
service_context, availability_change_event);
}
}
MFIUser ::~MFIUser() {
availability_change_event->Close();
}
void MFIUser::Initialize(Kernel::HLERequestContext& ctx) {
LOG_INFO(Service_NFC, "called");
state = State::Initialized;
for (auto& device : devices) {
device->Initialize();
}
IPC::ResponseBuilder rb{ctx, 2, 0};
rb.Push(ResultSuccess);
}
void MFIUser::Finalize(Kernel::HLERequestContext& ctx) {
LOG_INFO(Service_NFC, "called");
state = State::NonInitialized;
for (auto& device : devices) {
device->Finalize();
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void MFIUser::ListDevices(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
if (!ctx.CanWriteBuffer()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareInvalidArgument);
return;
}
if (ctx.GetWriteBufferSize() == 0) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareInvalidArgument);
return;
}
std::vector<u64> nfp_devices;
const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements<u64>();
for (const auto& device : devices) {
if (nfp_devices.size() >= max_allowed_devices) {
continue;
}
if (device->GetCurrentState() != NFP::DeviceState::Unavailable) {
nfp_devices.push_back(device->GetHandle());
}
}
if (nfp_devices.empty()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
ctx.WriteBuffer(nfp_devices);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(static_cast<s32>(nfp_devices.size()));
}
void MFIUser::StartDetection(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
const auto result = device.value()->StartDetection(NFP::TagProtocol::All);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void MFIUser::StopDetection(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
const auto result = device.value()->StopDetection();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void MFIUser::Read(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto buffer{ctx.ReadBuffer()};
const auto number_of_commands{ctx.GetReadBufferNumElements<NFP::MifareReadBlockParameter>()};
std::vector<NFP::MifareReadBlockParameter> read_commands(number_of_commands);
memcpy(read_commands.data(), buffer.data(),
number_of_commands * sizeof(NFP::MifareReadBlockParameter));
LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, read_commands_size={}",
device_handle, number_of_commands);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
Result result = ResultSuccess;
std::vector<NFP::MifareReadBlockData> out_data(number_of_commands);
for (std::size_t i = 0; i < number_of_commands; i++) {
result = device.value()->MifareRead(read_commands[i], out_data[i]);
if (result.IsError()) {
break;
}
}
ctx.WriteBuffer(out_data);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void MFIUser::Write(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto buffer{ctx.ReadBuffer()};
const auto number_of_commands{ctx.GetReadBufferNumElements<NFP::MifareWriteBlockParameter>()};
std::vector<NFP::MifareWriteBlockParameter> write_commands(number_of_commands);
memcpy(write_commands.data(), buffer.data(),
number_of_commands * sizeof(NFP::MifareWriteBlockParameter));
LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, write_commands_size={}",
device_handle, number_of_commands);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
Result result = ResultSuccess;
std::vector<NFP::MifareReadBlockData> out_data(number_of_commands);
for (std::size_t i = 0; i < number_of_commands; i++) {
result = device.value()->MifareWrite(write_commands[i]);
if (result.IsError()) {
break;
}
}
if (result.IsSuccess()) {
result = device.value()->Flush();
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void MFIUser::GetTagInfo(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
NFP::TagInfo tag_info{};
const auto result = device.value()->GetTagInfo(tag_info, true);
ctx.WriteBuffer(tag_info);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void MFIUser::GetActivateEventHandle(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(device.value()->GetActivateEvent());
}
void MFIUser::GetDeactivateEventHandle(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(device.value()->GetDeactivateEvent());
}
void MFIUser::GetState(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(state);
}
void MFIUser::GetDeviceState(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(device.value()->GetCurrentState());
}
void MFIUser::GetNpadId(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(device.value()->GetNpadId());
}
void MFIUser::GetAvailabilityChangeEventHandle(Kernel::HLERequestContext& ctx) {
LOG_INFO(Service_NFC, "called");
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(availability_change_event->GetReadableEvent());
}
std::optional<std::shared_ptr<NfcDevice>> MFIUser::GetNfcDevice(u64 handle) {
for (auto& device : devices) {
if (device->GetHandle() == handle) {
return device;
}
}
return std::nullopt;
}
} // namespace Service::NFC

View File

@@ -0,0 +1,52 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <memory>
#include <optional>
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
namespace Service::NFC {
class NfcDevice;
class MFIUser final : public ServiceFramework<MFIUser> {
public:
explicit MFIUser(Core::System& system_);
~MFIUser();
private:
enum class State : u32 {
NonInitialized,
Initialized,
};
void Initialize(Kernel::HLERequestContext& ctx);
void Finalize(Kernel::HLERequestContext& ctx);
void ListDevices(Kernel::HLERequestContext& ctx);
void StartDetection(Kernel::HLERequestContext& ctx);
void StopDetection(Kernel::HLERequestContext& ctx);
void Read(Kernel::HLERequestContext& ctx);
void Write(Kernel::HLERequestContext& ctx);
void GetTagInfo(Kernel::HLERequestContext& ctx);
void GetActivateEventHandle(Kernel::HLERequestContext& ctx);
void GetDeactivateEventHandle(Kernel::HLERequestContext& ctx);
void GetState(Kernel::HLERequestContext& ctx);
void GetDeviceState(Kernel::HLERequestContext& ctx);
void GetNpadId(Kernel::HLERequestContext& ctx);
void GetAvailabilityChangeEventHandle(Kernel::HLERequestContext& ctx);
std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle);
KernelHelpers::ServiceContext service_context;
std::array<std::shared_ptr<NfcDevice>, 10> devices{};
State state{State::NonInitialized};
Kernel::KEvent* availability_change_event;
};
} // namespace Service::NFC

View File

@@ -6,6 +6,7 @@
#include "common/logging/log.h"
#include "common/settings.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/nfc/mifare_user.h"
#include "core/hle/service/nfc/nfc.h"
#include "core/hle/service/nfc/nfc_user.h"
#include "core/hle/service/service.h"
@@ -50,32 +51,6 @@ private:
}
};
class MFIUser final : public ServiceFramework<MFIUser> {
public:
explicit MFIUser(Core::System& system_) : ServiceFramework{system_, "NFC::MFIUser"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "Initialize"},
{1, nullptr, "Finalize"},
{2, nullptr, "ListDevices"},
{3, nullptr, "StartDetection"},
{4, nullptr, "StopDetection"},
{5, nullptr, "Read"},
{6, nullptr, "Write"},
{7, nullptr, "GetTagInfo"},
{8, nullptr, "GetActivateEventHandle"},
{9, nullptr, "GetDeactivateEventHandle"},
{10, nullptr, "GetState"},
{11, nullptr, "GetDeviceState"},
{12, nullptr, "GetNpadId"},
{13, nullptr, "GetAvailabilityChangeEventHandle"},
};
// clang-format on
RegisterHandlers(functions);
}
};
class NFC_MF_U final : public ServiceFramework<NFC_MF_U> {
public:
explicit NFC_MF_U(Core::System& system_) : ServiceFramework{system_, "nfc:mf:u"} {

View File

@@ -77,11 +77,13 @@ bool NfcDevice::LoadNfcTag(std::span<const u8> data) {
return false;
}
if (data.size() != sizeof(NFP::EncryptedNTAG215File)) {
if (data.size() < sizeof(NFP::EncryptedNTAG215File)) {
LOG_ERROR(Service_NFC, "Not an amiibo, size={}", data.size());
return false;
}
tag_data.resize(data.size());
memcpy(tag_data.data(), data.data(), data.size());
memcpy(&encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File));
device_state = NFP::DeviceState::TagFound;
@@ -121,7 +123,7 @@ void NfcDevice::Finalize() {
device_state = NFP::DeviceState::Unavailable;
}
Result NfcDevice::StartDetection(s32 protocol_) {
Result NfcDevice::StartDetection(NFP::TagProtocol allowed_protocol) {
if (device_state != NFP::DeviceState::Initialized &&
device_state != NFP::DeviceState::TagRemoved) {
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
@@ -134,7 +136,7 @@ Result NfcDevice::StartDetection(s32 protocol_) {
}
device_state = NFP::DeviceState::SearchingForTag;
protocol = protocol_;
allowed_protocols = allowed_protocol;
return ResultSuccess;
}
@@ -160,7 +162,7 @@ Result NfcDevice::StopDetection() {
return WrongDeviceState;
}
Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info) const {
Result NfcDevice::Flush() {
if (device_state != NFP::DeviceState::TagFound &&
device_state != NFP::DeviceState::TagMounted) {
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
@@ -170,6 +172,34 @@ Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info) const {
return WrongDeviceState;
}
if (!npad_device->WriteNfc(tag_data)) {
LOG_ERROR(Service_NFP, "Error writing to file");
return MifareReadError;
}
return ResultSuccess;
}
Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const {
if (device_state != NFP::DeviceState::TagFound &&
device_state != NFP::DeviceState::TagMounted) {
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
if (device_state == NFP::DeviceState::TagRemoved) {
return TagRemoved;
}
return WrongDeviceState;
}
if (is_mifare) {
tag_info = {
.uuid = encrypted_tag_data.uuid.uid,
.uuid_length = static_cast<u8>(encrypted_tag_data.uuid.uid.size()),
.protocol = NFP::TagProtocol::TypeA,
.tag_type = NFP::TagType::Type4,
};
return ResultSuccess;
}
// Protocol and tag type may change here
tag_info = {
.uuid = encrypted_tag_data.uuid.uid,
@@ -181,6 +211,52 @@ Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info) const {
return ResultSuccess;
}
Result NfcDevice::MifareRead(const NFP::MifareReadBlockParameter& parameter,
NFP::MifareReadBlockData& read_block_data) {
const std::size_t sector_index = parameter.sector_number * sizeof(NFP::DataBlock);
read_block_data.sector_number = parameter.sector_number;
if (device_state != NFP::DeviceState::TagFound &&
device_state != NFP::DeviceState::TagMounted) {
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
if (device_state == NFP::DeviceState::TagRemoved) {
return TagRemoved;
}
return WrongDeviceState;
}
if (tag_data.size() < sector_index + sizeof(NFP::DataBlock)) {
return MifareReadError;
}
// TODO: Use parameter.sector_key to read encrypted data
memcpy(read_block_data.data.data(), tag_data.data() + sector_index, sizeof(NFP::DataBlock));
return ResultSuccess;
}
Result NfcDevice::MifareWrite(const NFP::MifareWriteBlockParameter& parameter) {
const std::size_t sector_index = parameter.sector_number * sizeof(NFP::DataBlock);
if (device_state != NFP::DeviceState::TagFound &&
device_state != NFP::DeviceState::TagMounted) {
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
if (device_state == NFP::DeviceState::TagRemoved) {
return TagRemoved;
}
return WrongDeviceState;
}
if (tag_data.size() < sector_index + sizeof(NFP::DataBlock)) {
return MifareReadError;
}
// TODO: Use parameter.sector_key to encrypt the data
memcpy(tag_data.data() + sector_index, parameter.data.data(), sizeof(NFP::DataBlock));
return ResultSuccess;
}
u64 NfcDevice::GetHandle() const {
// Generate a handle based of the npad id
return static_cast<u64>(npad_id);

View File

@@ -34,10 +34,16 @@ public:
void Initialize();
void Finalize();
Result StartDetection(s32 protocol_);
Result StartDetection(NFP::TagProtocol allowed_protocol);
Result StopDetection();
Result Flush();
Result GetTagInfo(NFP::TagInfo& tag_info) const;
Result GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const;
Result MifareRead(const NFP::MifareReadBlockParameter& parameter,
NFP::MifareReadBlockData& read_block_data);
Result MifareWrite(const NFP::MifareWriteBlockParameter& parameter);
u64 GetHandle() const;
NFP::DeviceState GetCurrentState() const;
@@ -61,10 +67,11 @@ private:
Kernel::KEvent* deactivate_event = nullptr;
Kernel::KEvent* availability_change_event = nullptr;
s32 protocol{};
NFP::TagProtocol allowed_protocols{};
NFP::DeviceState device_state{NFP::DeviceState::Unavailable};
NFP::EncryptedNTAG215File encrypted_tag_data{};
std::vector<u8> tag_data{};
};
} // namespace Service::NFC

View File

@@ -12,6 +12,12 @@ constexpr Result InvalidArgument(ErrorModule::NFC, 65);
constexpr Result WrongDeviceState(ErrorModule::NFC, 73);
constexpr Result NfcDisabled(ErrorModule::NFC, 80);
constexpr Result TagRemoved(ErrorModule::NFC, 97);
constexpr Result CorruptedData(ErrorModule::NFC, 144);
constexpr Result MifareDeviceNotFound(ErrorModule::NFCMifare, 64);
constexpr Result MifareInvalidArgument(ErrorModule::NFCMifare, 65);
constexpr Result MifareWrongDeviceState(ErrorModule::NFCMifare, 73);
constexpr Result MifareNfcDisabled(ErrorModule::NFCMifare, 80);
constexpr Result MifareTagRemoved(ErrorModule::NFCMifare, 97);
constexpr Result MifareReadError(ErrorModule::NFCMifare, 288);
} // namespace Service::NFC

View File

@@ -201,7 +201,7 @@ void IUser::AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) {
void IUser::StartDetection(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto nfp_protocol{rp.Pop<s32>()};
const auto nfp_protocol{rp.PopEnum<NFP::TagProtocol>()};
LOG_INFO(Service_NFC, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol);
if (state == State::NonInitialized) {
@@ -267,7 +267,7 @@ void IUser::GetTagInfo(Kernel::HLERequestContext& ctx) {
}
NFP::TagInfo tag_info{};
const auto result = device.value()->GetTagInfo(tag_info);
const auto result = device.value()->GetTagInfo(tag_info, false);
ctx.WriteBuffer(tag_info);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);

View File

@@ -106,11 +106,24 @@ enum class CabinetMode : u8 {
StartFormatter,
};
enum class MifareCmd : u8 {
AuthA = 0x60,
AuthB = 0x61,
Read = 0x30,
Write = 0xA0,
Transfer = 0xB0,
Decrement = 0xC0,
Increment = 0xC1,
Store = 0xC2
};
using UniqueSerialNumber = std::array<u8, 7>;
using LockBytes = std::array<u8, 2>;
using HashData = std::array<u8, 0x20>;
using ApplicationArea = std::array<u8, 0xD8>;
using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>;
using DataBlock = std::array<u8, 0x10>;
using KeyData = std::array<u8, 0x6>;
struct TagUuid {
UniqueSerialNumber uid;
@@ -323,4 +336,37 @@ struct RegisterInfo {
};
static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size");
struct SectorKey {
MifareCmd command;
u8 unknown; // Usually 1
INSERT_PADDING_BYTES(0x6);
KeyData sector_key;
INSERT_PADDING_BYTES(0x2);
};
static_assert(sizeof(SectorKey) == 0x10, "SectorKey is an invalid size");
struct MifareReadBlockParameter {
u8 sector_number;
INSERT_PADDING_BYTES(0x7);
SectorKey sector_key;
};
static_assert(sizeof(MifareReadBlockParameter) == 0x18,
"MifareReadBlockParameter is an invalid size");
struct MifareReadBlockData {
DataBlock data;
u8 sector_number;
INSERT_PADDING_BYTES(0x7);
};
static_assert(sizeof(MifareReadBlockData) == 0x18, "MifareReadBlockData is an invalid size");
struct MifareWriteBlockParameter {
DataBlock data;
u8 sector_number;
INSERT_PADDING_BYTES(0x7);
SectorKey sector_key;
};
static_assert(sizeof(MifareWriteBlockParameter) == 0x28,
"MifareWriteBlockParameter is an invalid size");
} // namespace Service::NFP

View File

@@ -38,7 +38,7 @@ std::string GetTimestamp() {
using namespace nlohmann;
void SaveToFile(json json, const std::filesystem::path& filename) {
void SaveToFile(const json& json, const std::filesystem::path& filename) {
if (!Common::FS::CreateParentDirs(filename)) {
LOG_ERROR(Core, "Failed to create path for '{}' to save report!",
Common::FS::PathToUTF8String(filename));
@@ -81,8 +81,8 @@ json GetReportCommonData(u64 title_id, Result result, const std::string& timesta
}
json GetProcessorStateData(const std::string& architecture, u64 entry_point, u64 sp, u64 pc,
u64 pstate, std::array<u64, 31> registers,
std::optional<std::array<u64, 32>> backtrace = {}) {
u64 pstate, const std::array<u64, 31>& registers,
const std::optional<std::array<u64, 32>>& backtrace = {}) {
auto out = json{
{"entry_point", fmt::format("{:016X}", entry_point)},
{"sp", fmt::format("{:016X}", sp)},
@@ -224,11 +224,11 @@ void Reporter::SaveCrashReport(u64 title_id, Result result, u64 set_flags, u64 e
out["processor_state"] = std::move(proc_out);
SaveToFile(std::move(out), GetPath("crash_report", title_id, timestamp));
SaveToFile(out, GetPath("crash_report", title_id, timestamp));
}
void Reporter::SaveSvcBreakReport(u32 type, bool signal_debugger, u64 info1, u64 info2,
std::optional<std::vector<u8>> resolved_buffer) const {
const std::optional<std::vector<u8>>& resolved_buffer) const {
if (!IsReportingEnabled()) {
return;
}
@@ -250,7 +250,7 @@ void Reporter::SaveSvcBreakReport(u32 type, bool signal_debugger, u64 info1, u64
out["svc_break"] = std::move(break_out);
SaveToFile(std::move(out), GetPath("svc_break_report", title_id, timestamp));
SaveToFile(out, GetPath("svc_break_report", title_id, timestamp));
}
void Reporter::SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u32 command_id,
@@ -271,13 +271,13 @@ void Reporter::SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u
out["function"] = std::move(function_out);
SaveToFile(std::move(out), GetPath("unimpl_func_report", title_id, timestamp));
SaveToFile(out, GetPath("unimpl_func_report", title_id, timestamp));
}
void Reporter::SaveUnimplementedAppletReport(
u32 applet_id, u32 common_args_version, u32 library_version, u32 theme_color,
bool startup_sound, u64 system_tick, std::vector<std::vector<u8>> normal_channel,
std::vector<std::vector<u8>> interactive_channel) const {
bool startup_sound, u64 system_tick, const std::vector<std::vector<u8>>& normal_channel,
const std::vector<std::vector<u8>>& interactive_channel) const {
if (!IsReportingEnabled()) {
return;
}
@@ -308,10 +308,11 @@ void Reporter::SaveUnimplementedAppletReport(
out["applet_normal_data"] = std::move(normal_out);
out["applet_interactive_data"] = std::move(interactive_out);
SaveToFile(std::move(out), GetPath("unimpl_applet_report", title_id, timestamp));
SaveToFile(out, GetPath("unimpl_applet_report", title_id, timestamp));
}
void Reporter::SavePlayReport(PlayReportType type, u64 title_id, std::vector<std::vector<u8>> data,
void Reporter::SavePlayReport(PlayReportType type, u64 title_id,
const std::vector<std::vector<u8>>& data,
std::optional<u64> process_id, std::optional<u128> user_id) const {
if (!IsReportingEnabled()) {
return;
@@ -335,12 +336,12 @@ void Reporter::SavePlayReport(PlayReportType type, u64 title_id, std::vector<std
out["play_report_type"] = fmt::format("{:02}", static_cast<u8>(type));
out["play_report_data"] = std::move(data_out);
SaveToFile(std::move(out), GetPath("play_report", title_id, timestamp));
SaveToFile(out, GetPath("play_report", title_id, timestamp));
}
void Reporter::SaveErrorReport(u64 title_id, Result result,
std::optional<std::string> custom_text_main,
std::optional<std::string> custom_text_detail) const {
const std::optional<std::string>& custom_text_main,
const std::optional<std::string>& custom_text_detail) const {
if (!IsReportingEnabled()) {
return;
}
@@ -354,11 +355,11 @@ void Reporter::SaveErrorReport(u64 title_id, Result result,
out["backtrace"] = GetBacktraceData(system);
out["error_custom_text"] = {
{"main", *custom_text_main},
{"detail", *custom_text_detail},
{"main", custom_text_main.value_or("")},
{"detail", custom_text_detail.value_or("")},
};
SaveToFile(std::move(out), GetPath("error_report", title_id, timestamp));
SaveToFile(out, GetPath("error_report", title_id, timestamp));
}
void Reporter::SaveFSAccessLog(std::string_view log_message) const {

View File

@@ -36,7 +36,7 @@ public:
// Used by syscall svcBreak
void SaveSvcBreakReport(u32 type, bool signal_debugger, u64 info1, u64 info2,
std::optional<std::vector<u8>> resolved_buffer = {}) const;
const std::optional<std::vector<u8>>& resolved_buffer = {}) const;
// Used by HLE service handler
void SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u32 command_id,
@@ -44,10 +44,10 @@ public:
const std::string& service_name) const;
// Used by stub applet implementation
void SaveUnimplementedAppletReport(u32 applet_id, u32 common_args_version, u32 library_version,
u32 theme_color, bool startup_sound, u64 system_tick,
std::vector<std::vector<u8>> normal_channel,
std::vector<std::vector<u8>> interactive_channel) const;
void SaveUnimplementedAppletReport(
u32 applet_id, u32 common_args_version, u32 library_version, u32 theme_color,
bool startup_sound, u64 system_tick, const std::vector<std::vector<u8>>& normal_channel,
const std::vector<std::vector<u8>>& interactive_channel) const;
enum class PlayReportType {
Old,
@@ -56,13 +56,13 @@ public:
System,
};
void SavePlayReport(PlayReportType type, u64 title_id, std::vector<std::vector<u8>> data,
void SavePlayReport(PlayReportType type, u64 title_id, const std::vector<std::vector<u8>>& data,
std::optional<u64> process_id = {}, std::optional<u128> user_id = {}) const;
// Used by error applet
void SaveErrorReport(u64 title_id, Result result,
std::optional<std::string> custom_text_main = {},
std::optional<std::string> custom_text_detail = {}) const;
const std::optional<std::string>& custom_text_main = {},
const std::optional<std::string>& custom_text_detail = {}) const;
void SaveFSAccessLog(std::string_view log_message) const;

View File

@@ -56,18 +56,12 @@ if (ENABLE_SDL2)
drivers/sdl_driver.cpp
drivers/sdl_driver.h
)
if (YUZU_USE_EXTERNAL_SDL2)
target_link_libraries(input_common PRIVATE SDL2-static)
else()
target_link_libraries(input_common PRIVATE SDL2)
endif()
target_link_libraries(input_common PRIVATE SDL2::SDL2)
target_compile_definitions(input_common PRIVATE HAVE_SDL2)
endif()
target_link_libraries(input_common PRIVATE usb)
create_target_directory_groups(input_common)
target_link_libraries(input_common PUBLIC core PRIVATE common Boost::boost)
target_link_libraries(input_common PUBLIC core PRIVATE common Boost::boost libusb::usb)
if (YUZU_USE_PRECOMPILED_HEADERS)
target_precompile_headers(input_common PRIVATE precompiled_headers.h)

View File

@@ -47,20 +47,20 @@ Common::Input::NfcState VirtualAmiibo::SupportsNfc(
Common::Input::NfcState VirtualAmiibo::WriteNfcData(
[[maybe_unused]] const PadIdentifier& identifier_, const std::vector<u8>& data) {
const Common::FS::IOFile amiibo_file{file_path, Common::FS::FileAccessMode::ReadWrite,
Common::FS::FileType::BinaryFile};
const Common::FS::IOFile nfc_file{file_path, Common::FS::FileAccessMode::ReadWrite,
Common::FS::FileType::BinaryFile};
if (!amiibo_file.IsOpen()) {
if (!nfc_file.IsOpen()) {
LOG_ERROR(Core, "Amiibo is already on use");
return Common::Input::NfcState::WriteFailed;
}
if (!amiibo_file.Write(data)) {
if (!nfc_file.Write(data)) {
LOG_ERROR(Service_NFP, "Error writting to file");
return Common::Input::NfcState::WriteFailed;
}
amiibo_data = data;
nfc_data = data;
return Common::Input::NfcState::Success;
}
@@ -70,32 +70,44 @@ VirtualAmiibo::State VirtualAmiibo::GetCurrentState() const {
}
VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) {
const Common::FS::IOFile amiibo_file{filename, Common::FS::FileAccessMode::Read,
Common::FS::FileType::BinaryFile};
const Common::FS::IOFile nfc_file{filename, Common::FS::FileAccessMode::Read,
Common::FS::FileType::BinaryFile};
if (state != State::WaitingForAmiibo) {
return Info::WrongDeviceState;
}
if (!amiibo_file.IsOpen()) {
if (!nfc_file.IsOpen()) {
return Info::UnableToLoad;
}
amiibo_data.resize(amiibo_size);
if (amiibo_file.Read(amiibo_data) < amiibo_size_without_password) {
switch (nfc_file.GetSize()) {
case AmiiboSize:
case AmiiboSizeWithoutPassword:
nfc_data.resize(AmiiboSize);
if (nfc_file.Read(nfc_data) < AmiiboSizeWithoutPassword) {
return Info::NotAnAmiibo;
}
break;
case MifareSize:
nfc_data.resize(MifareSize);
if (nfc_file.Read(nfc_data) < MifareSize) {
return Info::NotAnAmiibo;
}
break;
default:
return Info::NotAnAmiibo;
}
file_path = filename;
state = State::AmiiboIsOpen;
SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, amiibo_data});
SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, nfc_data});
return Info::Success;
}
VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() {
if (state == State::AmiiboIsOpen) {
SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, amiibo_data});
SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, nfc_data});
return Info::Success;
}

View File

@@ -53,12 +53,13 @@ public:
std::string GetLastFilePath() const;
private:
static constexpr std::size_t amiibo_size = 0x21C;
static constexpr std::size_t amiibo_size_without_password = amiibo_size - 0x8;
static constexpr std::size_t AmiiboSize = 0x21C;
static constexpr std::size_t AmiiboSizeWithoutPassword = AmiiboSize - 0x8;
static constexpr std::size_t MifareSize = 0x400;
std::string file_path{};
State state{State::Initialized};
std::vector<u8> amiibo_data;
std::vector<u8> nfc_data;
Common::Input::PollingMode polling_mode{Common::Input::PollingMode::Pasive};
};
} // namespace InputCommon

View File

@@ -33,6 +33,8 @@ add_library(video_core STATIC
engines/sw_blitter/converter.cpp
engines/sw_blitter/converter.h
engines/const_buffer_info.h
engines/draw_manager.cpp
engines/draw_manager.h
engines/engine_interface.h
engines/engine_upload.cpp
engines/engine_upload.h

View File

@@ -26,6 +26,7 @@
#include "video_core/control/channel_state_cache.h"
#include "video_core/delayed_destruction_ring.h"
#include "video_core/dirty_flags.h"
#include "video_core/engines/draw_manager.h"
#include "video_core/engines/kepler_compute.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/memory_manager.h"
@@ -664,9 +665,10 @@ void BufferCache<P>::BindHostGeometryBuffers(bool is_indexed) {
if (is_indexed) {
BindHostIndexBuffer();
} else if constexpr (!HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) {
const auto& regs = maxwell3d->regs;
if (regs.draw.topology == Maxwell::PrimitiveTopology::Quads) {
runtime.BindQuadArrayIndexBuffer(regs.vertex_buffer.first, regs.vertex_buffer.count);
const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
if (draw_state.topology == Maxwell::PrimitiveTopology::Quads) {
runtime.BindQuadArrayIndexBuffer(draw_state.vertex_buffer.first,
draw_state.vertex_buffer.count);
}
}
BindHostVertexBuffers();
@@ -993,28 +995,29 @@ void BufferCache<P>::BindHostIndexBuffer() {
TouchBuffer(buffer, index_buffer.buffer_id);
const u32 offset = buffer.Offset(index_buffer.cpu_addr);
const u32 size = index_buffer.size;
if (maxwell3d->inline_index_draw_indexes.size()) {
const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
if (!draw_state.inline_index_draw_indexes.empty()) {
if constexpr (USE_MEMORY_MAPS) {
auto upload_staging = runtime.UploadStagingBuffer(size);
std::array<BufferCopy, 1> copies{
{BufferCopy{.src_offset = upload_staging.offset, .dst_offset = 0, .size = size}}};
std::memcpy(upload_staging.mapped_span.data(),
maxwell3d->inline_index_draw_indexes.data(), size);
draw_state.inline_index_draw_indexes.data(), size);
runtime.CopyBuffer(buffer, upload_staging.buffer, copies);
} else {
buffer.ImmediateUpload(0, maxwell3d->inline_index_draw_indexes);
buffer.ImmediateUpload(0, draw_state.inline_index_draw_indexes);
}
} else {
SynchronizeBuffer(buffer, index_buffer.cpu_addr, size);
}
if constexpr (HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) {
const u32 new_offset = offset + maxwell3d->regs.index_buffer.first *
maxwell3d->regs.index_buffer.FormatSizeInBytes();
const u32 new_offset =
offset + draw_state.index_buffer.first * draw_state.index_buffer.FormatSizeInBytes();
runtime.BindIndexBuffer(buffer, new_offset, size);
} else {
runtime.BindIndexBuffer(maxwell3d->regs.draw.topology, maxwell3d->regs.index_buffer.format,
maxwell3d->regs.index_buffer.first,
maxwell3d->regs.index_buffer.count, buffer, offset, size);
runtime.BindIndexBuffer(draw_state.topology, draw_state.index_buffer.format,
draw_state.index_buffer.first, draw_state.index_buffer.count,
buffer, offset, size);
}
}
@@ -1282,15 +1285,16 @@ template <class P>
void BufferCache<P>::UpdateIndexBuffer() {
// We have to check for the dirty flags and index count
// The index count is currently changed without updating the dirty flags
const auto& index_array = maxwell3d->regs.index_buffer;
const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
const auto& index_array = draw_state.index_buffer;
auto& flags = maxwell3d->dirty.flags;
if (!flags[Dirty::IndexBuffer] && last_index_count == index_array.count) {
return;
}
flags[Dirty::IndexBuffer] = false;
last_index_count = index_array.count;
if (maxwell3d->inline_index_draw_indexes.size()) {
auto inline_index_size = static_cast<u32>(maxwell3d->inline_index_draw_indexes.size());
if (!draw_state.inline_index_draw_indexes.empty()) {
auto inline_index_size = static_cast<u32>(draw_state.inline_index_draw_indexes.size());
index_buffer = Binding{
.cpu_addr = 0,
.size = inline_index_size,

View File

@@ -0,0 +1,191 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "video_core/dirty_flags.h"
#include "video_core/engines/draw_manager.h"
#include "video_core/rasterizer_interface.h"
namespace Tegra::Engines {
DrawManager::DrawManager(Maxwell3D* maxwell3d_) : maxwell3d(maxwell3d_) {}
void DrawManager::ProcessMethodCall(u32 method, u32 argument) {
const auto& regs{maxwell3d->regs};
switch (method) {
case MAXWELL3D_REG_INDEX(clear_surface):
return Clear(1);
case MAXWELL3D_REG_INDEX(draw.begin):
return DrawBegin();
case MAXWELL3D_REG_INDEX(draw.end):
return DrawEnd();
case MAXWELL3D_REG_INDEX(vertex_buffer.first):
case MAXWELL3D_REG_INDEX(vertex_buffer.count):
case MAXWELL3D_REG_INDEX(index_buffer.first):
break;
case MAXWELL3D_REG_INDEX(index_buffer.count):
draw_state.draw_indexed = true;
break;
case MAXWELL3D_REG_INDEX(index_buffer32_subsequent):
case MAXWELL3D_REG_INDEX(index_buffer16_subsequent):
case MAXWELL3D_REG_INDEX(index_buffer8_subsequent):
draw_state.instance_count++;
[[fallthrough]];
case MAXWELL3D_REG_INDEX(index_buffer32_first):
case MAXWELL3D_REG_INDEX(index_buffer16_first):
case MAXWELL3D_REG_INDEX(index_buffer8_first):
return DrawIndexSmall(argument);
case MAXWELL3D_REG_INDEX(draw_inline_index):
SetInlineIndexBuffer(argument);
break;
case MAXWELL3D_REG_INDEX(inline_index_2x16.even):
SetInlineIndexBuffer(regs.inline_index_2x16.even);
SetInlineIndexBuffer(regs.inline_index_2x16.odd);
break;
case MAXWELL3D_REG_INDEX(inline_index_4x8.index0):
SetInlineIndexBuffer(regs.inline_index_4x8.index0);
SetInlineIndexBuffer(regs.inline_index_4x8.index1);
SetInlineIndexBuffer(regs.inline_index_4x8.index2);
SetInlineIndexBuffer(regs.inline_index_4x8.index3);
break;
case MAXWELL3D_REG_INDEX(topology_override):
use_topology_override = true;
break;
default:
break;
}
}
void DrawManager::Clear(u32 layer_count) {
maxwell3d->rasterizer->Clear(layer_count);
}
void DrawManager::DrawDeferred() {
if (draw_state.draw_mode != DrawMode::Instance || draw_state.instance_count == 0)
return;
DrawEnd(draw_state.instance_count + 1, true);
draw_state.instance_count = 0;
}
void DrawManager::DrawArray(PrimitiveTopology topology, u32 vertex_first, u32 vertex_count,
u32 base_instance, u32 num_instances) {
draw_state.topology = topology;
draw_state.vertex_buffer.first = vertex_first;
draw_state.vertex_buffer.count = vertex_count;
draw_state.base_instance = base_instance;
ProcessDraw(false, num_instances);
}
void DrawManager::DrawIndex(PrimitiveTopology topology, u32 index_first, u32 index_count,
u32 base_index, u32 base_instance, u32 num_instances) {
const auto& regs{maxwell3d->regs};
draw_state.topology = topology;
draw_state.index_buffer = regs.index_buffer;
draw_state.index_buffer.first = index_first;
draw_state.index_buffer.count = index_count;
draw_state.base_index = base_index;
draw_state.base_instance = base_instance;
ProcessDraw(true, num_instances);
}
void DrawManager::SetInlineIndexBuffer(u32 index) {
draw_state.inline_index_draw_indexes.push_back(static_cast<u8>(index & 0x000000ff));
draw_state.inline_index_draw_indexes.push_back(static_cast<u8>((index & 0x0000ff00) >> 8));
draw_state.inline_index_draw_indexes.push_back(static_cast<u8>((index & 0x00ff0000) >> 16));
draw_state.inline_index_draw_indexes.push_back(static_cast<u8>((index & 0xff000000) >> 24));
draw_state.draw_mode = DrawMode::InlineIndex;
}
void DrawManager::DrawBegin() {
const auto& regs{maxwell3d->regs};
auto reset_instance_count = regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::First;
auto increment_instance_count =
regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent;
if (reset_instance_count) {
DrawDeferred();
draw_state.instance_count = 0;
draw_state.draw_mode = DrawMode::General;
} else if (increment_instance_count) {
draw_state.instance_count++;
draw_state.draw_mode = DrawMode::Instance;
}
draw_state.topology = regs.draw.topology;
}
void DrawManager::DrawEnd(u32 instance_count, bool force_draw) {
const auto& regs{maxwell3d->regs};
switch (draw_state.draw_mode) {
case DrawMode::Instance:
if (!force_draw)
break;
[[fallthrough]];
case DrawMode::General:
draw_state.base_instance = regs.global_base_instance_index;
draw_state.base_index = regs.global_base_vertex_index;
if (draw_state.draw_indexed) {
draw_state.index_buffer = regs.index_buffer;
ProcessDraw(true, instance_count);
} else {
draw_state.vertex_buffer = regs.vertex_buffer;
ProcessDraw(false, instance_count);
}
draw_state.draw_indexed = false;
break;
case DrawMode::InlineIndex:
draw_state.base_instance = regs.global_base_instance_index;
draw_state.base_index = regs.global_base_vertex_index;
draw_state.index_buffer = regs.index_buffer;
draw_state.index_buffer.count =
static_cast<u32>(draw_state.inline_index_draw_indexes.size() / 4);
draw_state.index_buffer.format = Maxwell3D::Regs::IndexFormat::UnsignedInt;
ProcessDraw(true, instance_count);
draw_state.inline_index_draw_indexes.clear();
break;
}
}
void DrawManager::DrawIndexSmall(u32 argument) {
const auto& regs{maxwell3d->regs};
IndexBufferSmall index_small_params{argument};
draw_state.base_instance = regs.global_base_instance_index;
draw_state.base_index = regs.global_base_vertex_index;
draw_state.index_buffer = regs.index_buffer;
draw_state.index_buffer.first = index_small_params.first;
draw_state.index_buffer.count = index_small_params.count;
draw_state.topology = index_small_params.topology;
maxwell3d->dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
ProcessDraw(true, 1);
}
void DrawManager::ProcessTopologyOverride() {
if (!use_topology_override)
return;
const auto& regs{maxwell3d->regs};
switch (regs.topology_override) {
case PrimitiveTopologyOverride::None:
break;
case PrimitiveTopologyOverride::Points:
draw_state.topology = PrimitiveTopology::Points;
break;
case PrimitiveTopologyOverride::Lines:
draw_state.topology = PrimitiveTopology::Lines;
break;
case PrimitiveTopologyOverride::LineStrip:
draw_state.topology = PrimitiveTopology::LineStrip;
break;
default:
draw_state.topology = static_cast<PrimitiveTopology>(regs.topology_override);
break;
}
}
void DrawManager::ProcessDraw(bool draw_indexed, u32 instance_count) {
LOG_TRACE(HW_GPU, "called, topology={}, count={}", draw_state.topology,
draw_indexed ? draw_state.index_buffer.count : draw_state.vertex_buffer.count);
ProcessTopologyOverride();
if (maxwell3d->ShouldExecute())
maxwell3d->rasterizer->Draw(draw_indexed, instance_count);
}
} // namespace Tegra::Engines

View File

@@ -0,0 +1,69 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
#include "video_core/engines/maxwell_3d.h"
namespace VideoCore {
class RasterizerInterface;
}
namespace Tegra::Engines {
using PrimitiveTopology = Maxwell3D::Regs::PrimitiveTopology;
using PrimitiveTopologyOverride = Maxwell3D::Regs::PrimitiveTopologyOverride;
using IndexBuffer = Maxwell3D::Regs::IndexBuffer;
using VertexBuffer = Maxwell3D::Regs::VertexBuffer;
using IndexBufferSmall = Maxwell3D::Regs::IndexBufferSmall;
class DrawManager {
public:
enum class DrawMode : u32 { General = 0, Instance, InlineIndex };
struct State {
PrimitiveTopology topology{};
DrawMode draw_mode{};
bool draw_indexed{};
u32 base_index{};
VertexBuffer vertex_buffer;
IndexBuffer index_buffer;
u32 base_instance{};
u32 instance_count{};
std::vector<u8> inline_index_draw_indexes;
};
explicit DrawManager(Maxwell3D* maxwell_3d);
void ProcessMethodCall(u32 method, u32 argument);
void Clear(u32 layer_count);
void DrawDeferred();
void DrawArray(PrimitiveTopology topology, u32 vertex_first, u32 vertex_count,
u32 base_instance, u32 num_instances);
void DrawIndex(PrimitiveTopology topology, u32 index_first, u32 index_count, u32 base_index,
u32 base_instance, u32 num_instances);
const State& GetDrawState() const {
return draw_state;
}
private:
void SetInlineIndexBuffer(u32 index);
void DrawBegin();
void DrawEnd(u32 instance_count = 1, bool force_draw = false);
void DrawIndexSmall(u32 argument);
void ProcessTopologyOverride();
void ProcessDraw(bool draw_indexed, u32 instance_count);
Maxwell3D* maxwell3d{};
State draw_state{};
bool use_topology_override{};
};
} // namespace Tegra::Engines

View File

@@ -7,6 +7,7 @@
#include "core/core.h"
#include "core/core_timing.h"
#include "video_core/dirty_flags.h"
#include "video_core/engines/draw_manager.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/gpu.h"
#include "video_core/memory_manager.h"
@@ -21,8 +22,10 @@ using VideoCore::QueryType;
constexpr u32 MacroRegistersStart = 0xE00;
Maxwell3D::Maxwell3D(Core::System& system_, MemoryManager& memory_manager_)
: system{system_}, memory_manager{memory_manager_}, macro_engine{GetMacroEngine(*this)},
upload_state{memory_manager, regs.upload} {
: draw_manager{std::make_unique<DrawManager>(this)}, system{system_},
memory_manager{memory_manager_}, macro_engine{GetMacroEngine(*this)}, upload_state{
memory_manager,
regs.upload} {
dirty.flags.flip();
InitializeRegisterDefaults();
}
@@ -116,16 +119,6 @@ void Maxwell3D::InitializeRegisterDefaults() {
regs.polygon_mode_front = Maxwell3D::Regs::PolygonMode::Fill;
shadow_state = regs;
draw_command[MAXWELL3D_REG_INDEX(draw.end)] = true;
draw_command[MAXWELL3D_REG_INDEX(draw.begin)] = true;
draw_command[MAXWELL3D_REG_INDEX(vertex_buffer.first)] = true;
draw_command[MAXWELL3D_REG_INDEX(vertex_buffer.count)] = true;
draw_command[MAXWELL3D_REG_INDEX(index_buffer.first)] = true;
draw_command[MAXWELL3D_REG_INDEX(index_buffer.count)] = true;
draw_command[MAXWELL3D_REG_INDEX(draw_inline_index)] = true;
draw_command[MAXWELL3D_REG_INDEX(inline_index_2x16.even)] = true;
draw_command[MAXWELL3D_REG_INDEX(inline_index_4x8.index0)] = true;
}
void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) {
@@ -213,29 +206,6 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume
return ProcessCBBind(3);
case MAXWELL3D_REG_INDEX(bind_groups[4].raw_config):
return ProcessCBBind(4);
case MAXWELL3D_REG_INDEX(index_buffer32_first):
regs.index_buffer.count = regs.index_buffer32_first.count;
regs.index_buffer.first = regs.index_buffer32_first.first;
dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
draw_indexed = true;
return ProcessDraw();
case MAXWELL3D_REG_INDEX(index_buffer16_first):
regs.index_buffer.count = regs.index_buffer16_first.count;
regs.index_buffer.first = regs.index_buffer16_first.first;
dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
draw_indexed = true;
return ProcessDraw();
case MAXWELL3D_REG_INDEX(index_buffer8_first):
regs.index_buffer.count = regs.index_buffer8_first.count;
regs.index_buffer.first = regs.index_buffer8_first.first;
dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
draw_indexed = true;
return ProcessDraw();
case MAXWELL3D_REG_INDEX(topology_override):
use_topology_override = true;
return;
case MAXWELL3D_REG_INDEX(clear_surface):
return ProcessClearBuffers(1);
case MAXWELL3D_REG_INDEX(report_semaphore.query):
return ProcessQueryGet();
case MAXWELL3D_REG_INDEX(render_enable.mode):
@@ -254,6 +224,9 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume
return rasterizer->FragmentBarrier();
case MAXWELL3D_REG_INDEX(tiled_cache_barrier):
return rasterizer->TiledCacheBarrier();
default:
draw_manager->ProcessMethodCall(method, argument);
break;
}
}
@@ -268,7 +241,7 @@ void Maxwell3D::CallMacroMethod(u32 method, const std::vector<u32>& parameters)
// Execute the current macro.
macro_engine->Execute(macro_positions[entry], parameters);
ProcessDeferredDraw();
draw_manager->DrawDeferred();
}
void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
@@ -291,62 +264,7 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
const u32 argument = ProcessShadowRam(method, method_argument);
ProcessDirtyRegisters(method, argument);
if (draw_command[method]) {
regs.reg_array[method] = method_argument;
deferred_draw_method.push_back(method);
auto update_inline_index = [&](const u32 index) {
inline_index_draw_indexes.push_back(static_cast<u8>(index & 0x000000ff));
inline_index_draw_indexes.push_back(static_cast<u8>((index & 0x0000ff00) >> 8));
inline_index_draw_indexes.push_back(static_cast<u8>((index & 0x00ff0000) >> 16));
inline_index_draw_indexes.push_back(static_cast<u8>((index & 0xff000000) >> 24));
draw_mode = DrawMode::InlineIndex;
};
switch (method) {
case MAXWELL3D_REG_INDEX(draw.begin): {
draw_mode =
(regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent) ||
(regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Unchanged)
? DrawMode::Instance
: DrawMode::General;
break;
}
case MAXWELL3D_REG_INDEX(draw.end):
switch (draw_mode) {
case DrawMode::General:
ProcessDraw();
break;
case DrawMode::InlineIndex:
regs.index_buffer.count = static_cast<u32>(inline_index_draw_indexes.size() / 4);
regs.index_buffer.format = Regs::IndexFormat::UnsignedInt;
draw_indexed = true;
ProcessDraw();
inline_index_draw_indexes.clear();
break;
case DrawMode::Instance:
break;
}
break;
case MAXWELL3D_REG_INDEX(index_buffer.count):
draw_indexed = true;
break;
case MAXWELL3D_REG_INDEX(draw_inline_index):
update_inline_index(method_argument);
break;
case MAXWELL3D_REG_INDEX(inline_index_2x16.even):
update_inline_index(regs.inline_index_2x16.even);
update_inline_index(regs.inline_index_2x16.odd);
break;
case MAXWELL3D_REG_INDEX(inline_index_4x8.index0):
update_inline_index(regs.inline_index_4x8.index0);
update_inline_index(regs.inline_index_4x8.index1);
update_inline_index(regs.inline_index_4x8.index2);
update_inline_index(regs.inline_index_4x8.index3);
break;
}
} else {
ProcessDeferredDraw();
ProcessMethodCall(method, argument, method_argument, is_last_call);
}
ProcessMethodCall(method, argument, method_argument, is_last_call);
}
void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
@@ -387,35 +305,6 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
}
}
void Maxwell3D::ProcessTopologyOverride() {
using PrimitiveTopology = Maxwell3D::Regs::PrimitiveTopology;
using PrimitiveTopologyOverride = Maxwell3D::Regs::PrimitiveTopologyOverride;
PrimitiveTopology topology{};
switch (regs.topology_override) {
case PrimitiveTopologyOverride::None:
topology = regs.draw.topology;
break;
case PrimitiveTopologyOverride::Points:
topology = PrimitiveTopology::Points;
break;
case PrimitiveTopologyOverride::Lines:
topology = PrimitiveTopology::Lines;
break;
case PrimitiveTopologyOverride::LineStrip:
topology = PrimitiveTopology::LineStrip;
break;
default:
topology = static_cast<PrimitiveTopology>(regs.topology_override);
break;
}
if (use_topology_override) {
regs.draw.topology.Assign(topology);
}
}
void Maxwell3D::ProcessMacroUpload(u32 data) {
macro_engine->AddCode(regs.load_mme.instruction_ptr++, data);
}
@@ -493,41 +382,51 @@ void Maxwell3D::ProcessQueryGet() {
void Maxwell3D::ProcessQueryCondition() {
const GPUVAddr condition_address{regs.render_enable.Address()};
switch (regs.render_enable.mode) {
case Regs::RenderEnable::Mode::True: {
switch (regs.render_enable_override) {
case Regs::RenderEnable::Override::AlwaysRender:
execute_on = true;
break;
}
case Regs::RenderEnable::Mode::False: {
case Regs::RenderEnable::Override::NeverRender:
execute_on = false;
break;
}
case Regs::RenderEnable::Mode::Conditional: {
Regs::ReportSemaphore::Compare cmp;
memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp));
execute_on = cmp.initial_sequence != 0U && cmp.initial_mode != 0U;
case Regs::RenderEnable::Override::UseRenderEnable:
switch (regs.render_enable.mode) {
case Regs::RenderEnable::Mode::True: {
execute_on = true;
break;
}
case Regs::RenderEnable::Mode::False: {
execute_on = false;
break;
}
case Regs::RenderEnable::Mode::Conditional: {
Regs::ReportSemaphore::Compare cmp;
memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp));
execute_on = cmp.initial_sequence != 0U && cmp.initial_mode != 0U;
break;
}
case Regs::RenderEnable::Mode::IfEqual: {
Regs::ReportSemaphore::Compare cmp;
memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp));
execute_on = cmp.initial_sequence == cmp.current_sequence &&
cmp.initial_mode == cmp.current_mode;
break;
}
case Regs::RenderEnable::Mode::IfNotEqual: {
Regs::ReportSemaphore::Compare cmp;
memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp));
execute_on = cmp.initial_sequence != cmp.current_sequence ||
cmp.initial_mode != cmp.current_mode;
break;
}
default: {
UNIMPLEMENTED_MSG("Uninplemented Condition Mode!");
execute_on = true;
break;
}
}
break;
}
case Regs::RenderEnable::Mode::IfEqual: {
Regs::ReportSemaphore::Compare cmp;
memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp));
execute_on =
cmp.initial_sequence == cmp.current_sequence && cmp.initial_mode == cmp.current_mode;
break;
}
case Regs::RenderEnable::Mode::IfNotEqual: {
Regs::ReportSemaphore::Compare cmp;
memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp));
execute_on =
cmp.initial_sequence != cmp.current_sequence || cmp.initial_mode != cmp.current_mode;
break;
}
default: {
UNIMPLEMENTED_MSG("Uninplemented Condition Mode!");
execute_on = true;
break;
}
}
}
void Maxwell3D::ProcessCounterReset() {
@@ -625,44 +524,4 @@ u32 Maxwell3D::GetRegisterValue(u32 method) const {
return regs.reg_array[method];
}
void Maxwell3D::ProcessClearBuffers(u32 layer_count) {
rasterizer->Clear(layer_count);
}
void Maxwell3D::ProcessDraw(u32 instance_count) {
LOG_TRACE(HW_GPU, "called, topology={}, count={}", regs.draw.topology.Value(),
draw_indexed ? regs.index_buffer.count : regs.vertex_buffer.count);
ProcessTopologyOverride();
if (ShouldExecute()) {
rasterizer->Draw(draw_indexed, instance_count);
}
draw_indexed = false;
deferred_draw_method.clear();
}
void Maxwell3D::ProcessDeferredDraw() {
if (draw_mode != DrawMode::Instance || deferred_draw_method.empty()) {
return;
}
const auto method_count = deferred_draw_method.size();
u32 instance_count = 1;
u32 vertex_buffer_count = 0;
u32 index_buffer_count = 0;
for (size_t index = 0; index < method_count; ++index) {
const u32 method = deferred_draw_method[index];
if (method == MAXWELL3D_REG_INDEX(vertex_buffer.count)) {
instance_count = ++vertex_buffer_count;
} else if (method == MAXWELL3D_REG_INDEX(index_buffer.count)) {
instance_count = ++index_buffer_count;
}
}
ASSERT_MSG(!(vertex_buffer_count && index_buffer_count), "Instance both indexed and direct?");
ProcessDraw(instance_count);
}
} // namespace Tegra::Engines

View File

@@ -37,6 +37,8 @@ class RasterizerInterface;
namespace Tegra::Engines {
class DrawManager;
/**
* This Engine is known as GF100_3D. Documentation can be found in:
* https://github.com/NVIDIA/open-gpu-doc/blob/master/classes/3d/clb197.h
@@ -2223,6 +2225,7 @@ public:
struct IndexBufferSmall {
union {
u32 raw;
BitField<0, 16, u32> first;
BitField<16, 12, u32> count;
BitField<28, 4, PrimitiveTopology> topology;
@@ -3061,10 +3064,8 @@ public:
Tables tables{};
} dirty;
std::vector<u8> inline_index_draw_indexes;
/// Handles a write to the CLEAR_BUFFERS register.
void ProcessClearBuffers(u32 layer_count);
std::unique_ptr<DrawManager> draw_manager;
friend class DrawManager;
private:
void InitializeRegisterDefaults();
@@ -3122,15 +3123,6 @@ private:
/// Handles a write to the CB_BIND register.
void ProcessCBBind(size_t stage_index);
/// Handles use of topology overrides (e.g., to avoid using a topology assigned from a macro)
void ProcessTopologyOverride();
/// Handles deferred draw(e.g., instance draw).
void ProcessDeferredDraw();
/// Handles a draw.
void ProcessDraw(u32 instance_count = 1);
/// Returns a query's value or an empty object if the value will be deferred through a cache.
std::optional<u64> GetQueryResult();
@@ -3153,13 +3145,6 @@ private:
Upload::State upload_state;
bool execute_on{true};
bool use_topology_override{false};
std::array<bool, Regs::NUM_REGS> draw_command{};
std::vector<u32> deferred_draw_method;
enum class DrawMode : u32 { General = 0, Instance, InlineIndex };
DrawMode draw_mode{DrawMode::General};
bool draw_indexed{};
};
#define ASSERT_REG_POSITION(field_name, position) \

View File

@@ -5,6 +5,7 @@
#include <vector>
#include "common/scope_exit.h"
#include "video_core/dirty_flags.h"
#include "video_core/engines/draw_manager.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/macro/macro.h"
#include "video_core/macro/macro_hle.h"
@@ -18,57 +19,33 @@ using HLEFunction = void (*)(Engines::Maxwell3D& maxwell3d, const std::vector<u3
// HLE'd functions
void HLE_771BB18C62444DA0(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) {
const u32 instance_count = parameters[2] & maxwell3d.GetRegisterValue(0xD1B);
maxwell3d.regs.draw.topology.Assign(
static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0] & 0x3ffffff));
maxwell3d.regs.global_base_instance_index = parameters[5];
maxwell3d.regs.global_base_vertex_index = parameters[3];
maxwell3d.regs.index_buffer.count = parameters[1];
maxwell3d.regs.index_buffer.first = parameters[4];
if (maxwell3d.ShouldExecute()) {
maxwell3d.Rasterizer().Draw(true, instance_count);
}
maxwell3d.regs.index_buffer.count = 0;
maxwell3d.draw_manager->DrawIndex(
static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0] & 0x3ffffff),
parameters[4], parameters[1], parameters[3], parameters[5], instance_count);
}
void HLE_0D61FC9FAAC9FCAD(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) {
const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]);
maxwell3d.regs.vertex_buffer.first = parameters[3];
maxwell3d.regs.vertex_buffer.count = parameters[1];
maxwell3d.regs.global_base_instance_index = parameters[4];
maxwell3d.regs.draw.topology.Assign(
static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0]));
if (maxwell3d.ShouldExecute()) {
maxwell3d.Rasterizer().Draw(false, instance_count);
}
maxwell3d.regs.vertex_buffer.count = 0;
maxwell3d.draw_manager->DrawArray(
static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0]),
parameters[3], parameters[1], parameters[4], instance_count);
}
void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) {
const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]);
const u32 element_base = parameters[4];
const u32 base_instance = parameters[5];
maxwell3d.regs.index_buffer.first = parameters[3];
maxwell3d.regs.vertex_id_base = element_base;
maxwell3d.regs.index_buffer.count = parameters[1];
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
maxwell3d.regs.global_base_vertex_index = element_base;
maxwell3d.regs.global_base_instance_index = base_instance;
maxwell3d.CallMethod(0x8e3, 0x640, true);
maxwell3d.CallMethod(0x8e4, element_base, true);
maxwell3d.CallMethod(0x8e5, base_instance, true);
maxwell3d.regs.draw.topology.Assign(
static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0]));
if (maxwell3d.ShouldExecute()) {
maxwell3d.Rasterizer().Draw(true, instance_count);
}
maxwell3d.draw_manager->DrawIndex(
static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0]),
parameters[3], parameters[1], element_base, base_instance, instance_count);
maxwell3d.regs.vertex_id_base = 0x0;
maxwell3d.regs.index_buffer.count = 0;
maxwell3d.regs.global_base_vertex_index = 0x0;
maxwell3d.regs.global_base_instance_index = 0x0;
maxwell3d.CallMethod(0x8e3, 0x640, true);
maxwell3d.CallMethod(0x8e4, 0x0, true);
maxwell3d.CallMethod(0x8e5, 0x0, true);
@@ -79,9 +56,6 @@ void HLE_3F5E74B9C9A50164(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
SCOPE_EXIT({
// Clean everything.
maxwell3d.regs.vertex_id_base = 0x0;
maxwell3d.regs.index_buffer.count = 0;
maxwell3d.regs.global_base_vertex_index = 0x0;
maxwell3d.regs.global_base_instance_index = 0x0;
maxwell3d.CallMethod(0x8e3, 0x640, true);
maxwell3d.CallMethod(0x8e4, 0x0, true);
maxwell3d.CallMethod(0x8e5, 0x0, true);
@@ -93,9 +67,6 @@ void HLE_3F5E74B9C9A50164(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
// Nothing to do.
return;
}
const auto topology =
static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[2]);
maxwell3d.regs.draw.topology.Assign(topology);
const u32 padding = parameters[3];
const std::size_t max_draws = parameters[4];
@@ -106,23 +77,17 @@ void HLE_3F5E74B9C9A50164(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
for (std::size_t index = first_draw; index < last_draw; index++) {
const std::size_t base = index * indirect_words + 5;
const u32 num_vertices = parameters[base];
const u32 instance_count = parameters[base + 1];
const u32 first_index = parameters[base + 2];
const u32 base_vertex = parameters[base + 3];
const u32 base_instance = parameters[base + 4];
maxwell3d.regs.index_buffer.first = first_index;
maxwell3d.regs.vertex_id_base = base_vertex;
maxwell3d.regs.index_buffer.count = num_vertices;
maxwell3d.regs.global_base_vertex_index = base_vertex;
maxwell3d.regs.global_base_instance_index = base_instance;
maxwell3d.CallMethod(0x8e3, 0x640, true);
maxwell3d.CallMethod(0x8e4, base_vertex, true);
maxwell3d.CallMethod(0x8e5, base_instance, true);
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
if (maxwell3d.ShouldExecute()) {
maxwell3d.Rasterizer().Draw(true, instance_count);
}
maxwell3d.draw_manager->DrawIndex(
static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[2]),
parameters[base + 2], parameters[base], base_vertex, base_instance,
parameters[base + 1]);
}
}
@@ -136,7 +101,7 @@ void HLE_EAD26C3E2109B06B(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
ASSERT(clear_params.layer == 0);
maxwell3d.regs.clear_surface.raw = clear_params.raw;
maxwell3d.ProcessClearBuffers(num_layers);
maxwell3d.draw_manager->Clear(num_layers);
}
constexpr std::array<std::pair<u64, HLEFunction>, 5> hle_funcs{{

View File

@@ -224,16 +224,18 @@ void RasterizerOpenGL::Draw(bool is_indexed, u32 instance_count) {
SyncState();
const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(maxwell3d->regs.draw.topology);
const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(draw_state.topology);
BeginTransformFeedback(pipeline, primitive_mode);
const GLuint base_instance = static_cast<GLuint>(maxwell3d->regs.global_base_instance_index);
const GLuint base_instance = static_cast<GLuint>(draw_state.base_instance);
const GLsizei num_instances = static_cast<GLsizei>(instance_count);
if (is_indexed) {
const GLint base_vertex = static_cast<GLint>(maxwell3d->regs.global_base_vertex_index);
const GLsizei num_vertices = static_cast<GLsizei>(maxwell3d->regs.index_buffer.count);
const GLint base_vertex = static_cast<GLint>(draw_state.base_index);
const GLsizei num_vertices = static_cast<GLsizei>(draw_state.index_buffer.count);
const GLvoid* const offset = buffer_cache_runtime.IndexOffset();
const GLenum format = MaxwellToGL::IndexFormat(maxwell3d->regs.index_buffer.format);
const GLenum format = MaxwellToGL::IndexFormat(draw_state.index_buffer.format);
if (num_instances == 1 && base_instance == 0 && base_vertex == 0) {
glDrawElements(primitive_mode, num_vertices, format, offset);
} else if (num_instances == 1 && base_instance == 0) {
@@ -252,8 +254,8 @@ void RasterizerOpenGL::Draw(bool is_indexed, u32 instance_count) {
base_instance);
}
} else {
const GLint base_vertex = static_cast<GLint>(maxwell3d->regs.vertex_buffer.first);
const GLsizei num_vertices = static_cast<GLsizei>(maxwell3d->regs.vertex_buffer.count);
const GLint base_vertex = static_cast<GLint>(draw_state.vertex_buffer.first);
const GLsizei num_vertices = static_cast<GLsizei>(draw_state.vertex_buffer.count);
if (num_instances == 1 && base_instance == 0) {
glDrawArrays(primitive_mode, base_vertex, num_vertices);
} else if (base_instance == 0) {

View File

@@ -22,6 +22,7 @@
#include "shader_recompiler/frontend/maxwell/control_flow.h"
#include "shader_recompiler/frontend/maxwell/translate_program.h"
#include "shader_recompiler/profile.h"
#include "video_core/engines/draw_manager.h"
#include "video_core/engines/kepler_compute.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/memory_manager.h"
@@ -327,7 +328,7 @@ GraphicsPipeline* ShaderCache::CurrentGraphicsPipeline() {
const auto& regs{maxwell3d->regs};
graphics_key.raw = 0;
graphics_key.early_z.Assign(regs.mandated_early_z != 0 ? 1 : 0);
graphics_key.gs_input_topology.Assign(regs.draw.topology.Value());
graphics_key.gs_input_topology.Assign(maxwell3d->draw_manager->GetDrawState().topology);
graphics_key.tessellation_primitive.Assign(regs.tessellation.params.domain_type.Value());
graphics_key.tessellation_spacing.Assign(regs.tessellation.params.spacing.Value());
graphics_key.tessellation_clockwise.Assign(
@@ -371,7 +372,8 @@ GraphicsPipeline* ShaderCache::BuiltPipeline(GraphicsPipeline* pipeline) const n
// If games are using a small index count, we can assume these are full screen quads.
// Usually these shaders are only used once for building textures so we can assume they
// can't be built async
if (maxwell3d->regs.index_buffer.count <= 6 || maxwell3d->regs.vertex_buffer.count <= 6) {
const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
if (draw_state.index_buffer.count <= 6 || draw_state.vertex_buffer.count <= 6) {
return pipeline;
}
return nullptr;

View File

@@ -8,6 +8,7 @@
#include "common/cityhash.h"
#include "common/common_types.h"
#include "common/polyfill_ranges.h"
#include "video_core/engines/draw_manager.h"
#include "video_core/renderer_vulkan/fixed_pipeline_state.h"
#include "video_core/renderer_vulkan/vk_state_tracker.h"
@@ -50,12 +51,13 @@ void RefreshXfbState(VideoCommon::TransformFeedbackState& state, const Maxwell&
void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d,
bool has_extended_dynamic_state, bool has_dynamic_vertex_input) {
const Maxwell& regs = maxwell3d.regs;
const auto topology_ = maxwell3d.draw_manager->GetDrawState().topology;
const std::array enabled_lut{
regs.polygon_offset_point_enable,
regs.polygon_offset_line_enable,
regs.polygon_offset_fill_enable,
};
const u32 topology_index = static_cast<u32>(regs.draw.topology.Value());
const u32 topology_index = static_cast<u32>(topology_);
raw1 = 0;
extended_dynamic_state.Assign(has_extended_dynamic_state ? 1 : 0);
@@ -78,7 +80,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d,
Maxwell::Tessellation::OutputPrimitives::Triangles_CW);
logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0);
logic_op.Assign(PackLogicOp(regs.logic_op.op));
topology.Assign(regs.draw.topology);
topology.Assign(topology_);
msaa_mode.Assign(regs.anti_alias_samples_mode);
raw2 = 0;

View File

@@ -507,7 +507,8 @@ GraphicsPipeline* PipelineCache::BuiltPipeline(GraphicsPipeline* pipeline) const
// If games are using a small index count, we can assume these are full screen quads.
// Usually these shaders are only used once for building textures so we can assume they
// can't be built async
if (maxwell3d->regs.index_buffer.count <= 6 || maxwell3d->regs.vertex_buffer.count <= 6) {
const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
if (draw_state.index_buffer.count <= 6 || draw_state.vertex_buffer.count <= 6) {
return pipeline;
}
return nullptr;

View File

@@ -12,6 +12,7 @@
#include "common/scope_exit.h"
#include "common/settings.h"
#include "video_core/control/channel_state.h"
#include "video_core/engines/draw_manager.h"
#include "video_core/engines/kepler_compute.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/renderer_vulkan/blit_image.h"
@@ -36,6 +37,7 @@
namespace Vulkan {
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
using MaxwellDrawState = Tegra::Engines::DrawManager::State;
using VideoCommon::ImageViewId;
using VideoCommon::ImageViewType;
@@ -127,16 +129,16 @@ VkRect2D GetScissorState(const Maxwell& regs, size_t index, u32 up_scale = 1, u3
return scissor;
}
DrawParams MakeDrawParams(const Maxwell& regs, u32 num_instances, bool is_indexed) {
DrawParams MakeDrawParams(const MaxwellDrawState& draw_state, u32 num_instances, bool is_indexed) {
DrawParams params{
.base_instance = regs.global_base_instance_index,
.base_instance = draw_state.base_instance,
.num_instances = num_instances,
.base_vertex = is_indexed ? regs.global_base_vertex_index : regs.vertex_buffer.first,
.num_vertices = is_indexed ? regs.index_buffer.count : regs.vertex_buffer.count,
.first_index = is_indexed ? regs.index_buffer.first : 0,
.base_vertex = is_indexed ? draw_state.base_index : draw_state.vertex_buffer.first,
.num_vertices = is_indexed ? draw_state.index_buffer.count : draw_state.vertex_buffer.count,
.first_index = is_indexed ? draw_state.index_buffer.first : 0,
.is_indexed = is_indexed,
};
if (regs.draw.topology == Maxwell::PrimitiveTopology::Quads) {
if (draw_state.topology == Maxwell::PrimitiveTopology::Quads) {
// 6 triangle vertices per quad, base vertex is part of the index
// See BindQuadArrayIndexBuffer for more details
params.num_vertices = (params.num_vertices / 4) * 6;
@@ -195,9 +197,9 @@ void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
UpdateDynamicStates();
const auto& regs{maxwell3d->regs};
const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
const u32 num_instances{instance_count};
const DrawParams draw_params{MakeDrawParams(regs, num_instances, is_indexed)};
const DrawParams draw_params{MakeDrawParams(draw_state, num_instances, is_indexed)};
scheduler.Record([draw_params](vk::CommandBuffer cmdbuf) {
if (draw_params.is_indexed) {
cmdbuf.DrawIndexed(draw_params.num_vertices, draw_params.num_instances,

View File

@@ -16,6 +16,8 @@ VkBool32 Callback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
case 0x682a878au: // VUID-vkCmdBindVertexBuffers2EXT-pBuffers-parameter
case 0x99fb7dfdu: // UNASSIGNED-RequiredParameter (vkCmdBindVertexBuffers2EXT pBuffers[0])
case 0xe8616bf2u: // Bound VkDescriptorSet 0x0[] was destroyed. Likely push_descriptor related
case 0x1608dec0u: // Image layout in vkUpdateDescriptorSet doesn't match descriptor use
case 0x55362756u: // Descriptor binding and framebuffer attachment overlap
return VK_FALSE;
default:
break;

View File

@@ -1119,7 +1119,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
test(has_ext_transform_feedback, VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME, false);
test(has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, false);
test(has_ext_extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, false);
test(has_ext_subgroup_size_control, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, false);
test(has_ext_subgroup_size_control, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, true);
test(has_ext_provoking_vertex, VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME, false);
test(has_ext_vertex_input_dynamic_state, VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME,
false);

View File

@@ -391,11 +391,7 @@ if (YUZU_USE_BUNDLED_QT AND QT_VERSION VERSION_LESS 6)
endif()
if (ENABLE_SDL2)
if (YUZU_USE_EXTERNAL_SDL2)
target_link_libraries(yuzu PRIVATE SDL2-static)
else()
target_link_libraries(yuzu PRIVATE SDL2)
endif()
target_link_libraries(yuzu PRIVATE SDL2::SDL2)
target_compile_definitions(yuzu PRIVATE HAVE_SDL2)
endif()

View File

@@ -685,7 +685,7 @@ QtControllerSelector::QtControllerSelector(GMainWindow& parent) {
QtControllerSelector::~QtControllerSelector() = default;
void QtControllerSelector::ReconfigureControllers(
std::function<void()> callback_, const Core::Frontend::ControllerParameters& parameters) const {
ReconfigureCallback callback_, const Core::Frontend::ControllerParameters& parameters) const {
callback = std::move(callback_);
emit MainWindowReconfigureControllers(parameters);
}

View File

@@ -157,7 +157,7 @@ public:
~QtControllerSelector() override;
void ReconfigureControllers(
std::function<void()> callback_,
ReconfigureCallback callback_,
const Core::Frontend::ControllerParameters& parameters) const override;
signals:
@@ -167,5 +167,5 @@ signals:
private:
void MainWindowReconfigureFinished();
mutable std::function<void()> callback;
mutable ReconfigureCallback callback;
};

View File

@@ -14,7 +14,7 @@ QtErrorDisplay::QtErrorDisplay(GMainWindow& parent) {
QtErrorDisplay::~QtErrorDisplay() = default;
void QtErrorDisplay::ShowError(Result error, std::function<void()> finished) const {
void QtErrorDisplay::ShowError(Result error, FinishedCallback finished) const {
callback = std::move(finished);
emit MainWindowDisplayError(
tr("Error Code: %1-%2 (0x%3)")
@@ -25,7 +25,7 @@ void QtErrorDisplay::ShowError(Result error, std::function<void()> finished) con
}
void QtErrorDisplay::ShowErrorWithTimestamp(Result error, std::chrono::seconds time,
std::function<void()> finished) const {
FinishedCallback finished) const {
callback = std::move(finished);
const QDateTime date_time = QDateTime::fromSecsSinceEpoch(time.count());
@@ -42,7 +42,7 @@ void QtErrorDisplay::ShowErrorWithTimestamp(Result error, std::chrono::seconds t
void QtErrorDisplay::ShowCustomErrorText(Result error, std::string dialog_text,
std::string fullscreen_text,
std::function<void()> finished) const {
FinishedCallback finished) const {
callback = std::move(finished);
emit MainWindowDisplayError(
tr("Error Code: %1-%2 (0x%3)")

View File

@@ -16,11 +16,11 @@ public:
explicit QtErrorDisplay(GMainWindow& parent);
~QtErrorDisplay() override;
void ShowError(Result error, std::function<void()> finished) const override;
void ShowError(Result error, FinishedCallback finished) const override;
void ShowErrorWithTimestamp(Result error, std::chrono::seconds time,
std::function<void()> finished) const override;
FinishedCallback finished) const override;
void ShowCustomErrorText(Result error, std::string dialog_text, std::string fullscreen_text,
std::function<void()> finished) const override;
FinishedCallback finished) const override;
signals:
void MainWindowDisplayError(QString error_code, QString error_text) const;
@@ -28,5 +28,5 @@ signals:
private:
void MainWindowFinishedError();
mutable std::function<void()> callback;
mutable FinishedCallback callback;
};

View File

@@ -163,8 +163,7 @@ QtProfileSelector::QtProfileSelector(GMainWindow& parent) {
QtProfileSelector::~QtProfileSelector() = default;
void QtProfileSelector::SelectProfile(
std::function<void(std::optional<Common::UUID>)> callback_) const {
void QtProfileSelector::SelectProfile(SelectProfileCallback callback_) const {
callback = std::move(callback_);
emit MainWindowSelectProfile();
}

View File

@@ -65,7 +65,7 @@ public:
explicit QtProfileSelector(GMainWindow& parent);
~QtProfileSelector() override;
void SelectProfile(std::function<void(std::optional<Common::UUID>)> callback_) const override;
void SelectProfile(SelectProfileCallback callback_) const override;
signals:
void MainWindowSelectProfile() const;
@@ -73,5 +73,5 @@ signals:
private:
void MainWindowFinishedSelection(std::optional<Common::UUID> uuid);
mutable std::function<void(std::optional<Common::UUID>)> callback;
mutable SelectProfileCallback callback;
};

View File

@@ -1566,10 +1566,7 @@ QtSoftwareKeyboard::~QtSoftwareKeyboard() = default;
void QtSoftwareKeyboard::InitializeKeyboard(
bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters,
std::function<void(Service::AM::Applets::SwkbdResult, std::u16string, bool)>
submit_normal_callback_,
std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>
submit_inline_callback_) {
SubmitNormalCallback submit_normal_callback_, SubmitInlineCallback submit_inline_callback_) {
if (is_inline) {
submit_inline_callback = std::move(submit_inline_callback_);
} else {

View File

@@ -233,12 +233,10 @@ public:
explicit QtSoftwareKeyboard(GMainWindow& parent);
~QtSoftwareKeyboard() override;
void InitializeKeyboard(
bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters,
std::function<void(Service::AM::Applets::SwkbdResult, std::u16string, bool)>
submit_normal_callback_,
std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>
submit_inline_callback_) override;
void InitializeKeyboard(bool is_inline,
Core::Frontend::KeyboardInitializeParameters initialize_parameters,
SubmitNormalCallback submit_normal_callback_,
SubmitInlineCallback submit_inline_callback_) override;
void ShowNormalKeyboard() const override;
@@ -279,8 +277,6 @@ private:
void SubmitInlineText(Service::AM::Applets::SwkbdReplyType reply_type,
std::u16string submitted_text, s32 cursor_position) const;
mutable std::function<void(Service::AM::Applets::SwkbdResult, std::u16string, bool)>
submit_normal_callback;
mutable std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>
submit_inline_callback;
mutable SubmitNormalCallback submit_normal_callback;
mutable SubmitInlineCallback submit_inline_callback;
};

View File

@@ -401,9 +401,9 @@ QtWebBrowser::QtWebBrowser(GMainWindow& main_window) {
QtWebBrowser::~QtWebBrowser() = default;
void QtWebBrowser::OpenLocalWebPage(
const std::string& local_url, std::function<void()> extract_romfs_callback_,
std::function<void(Service::AM::Applets::WebExitReason, std::string)> callback_) const {
void QtWebBrowser::OpenLocalWebPage(const std::string& local_url,
ExtractROMFSCallback extract_romfs_callback_,
OpenWebPageCallback callback_) const {
extract_romfs_callback = std::move(extract_romfs_callback_);
callback = std::move(callback_);
@@ -416,9 +416,8 @@ void QtWebBrowser::OpenLocalWebPage(
}
}
void QtWebBrowser::OpenExternalWebPage(
const std::string& external_url,
std::function<void(Service::AM::Applets::WebExitReason, std::string)> callback_) const {
void QtWebBrowser::OpenExternalWebPage(const std::string& external_url,
OpenWebPageCallback callback_) const {
callback = std::move(callback_);
const auto index = external_url.find('?');

View File

@@ -197,13 +197,11 @@ public:
~QtWebBrowser() override;
void OpenLocalWebPage(const std::string& local_url,
std::function<void()> extract_romfs_callback_,
std::function<void(Service::AM::Applets::WebExitReason, std::string)>
callback_) const override;
ExtractROMFSCallback extract_romfs_callback_,
OpenWebPageCallback callback_) const override;
void OpenExternalWebPage(const std::string& external_url,
std::function<void(Service::AM::Applets::WebExitReason, std::string)>
callback_) const override;
OpenWebPageCallback callback_) const override;
signals:
void MainWindowOpenWebPage(const std::string& main_url, const std::string& additional_args,
@@ -215,7 +213,6 @@ private:
void MainWindowWebBrowserClosed(Service::AM::Applets::WebExitReason exit_reason,
std::string last_url);
mutable std::function<void()> extract_romfs_callback;
mutable std::function<void(Service::AM::Applets::WebExitReason, std::string)> callback;
mutable ExtractROMFSCallback extract_romfs_callback;
mutable OpenWebPageCallback callback;
};

View File

@@ -679,6 +679,7 @@ void Config::ReadCpuValues() {
ReadBasicSetting(Settings::values.cpuopt_fastmem);
ReadBasicSetting(Settings::values.cpuopt_fastmem_exclusives);
ReadBasicSetting(Settings::values.cpuopt_recompile_exclusives);
ReadBasicSetting(Settings::values.cpuopt_ignore_memory_aborts);
}
qt_config->endGroup();
@@ -1291,6 +1292,7 @@ void Config::SaveCpuValues() {
WriteBasicSetting(Settings::values.cpuopt_fastmem);
WriteBasicSetting(Settings::values.cpuopt_fastmem_exclusives);
WriteBasicSetting(Settings::values.cpuopt_recompile_exclusives);
WriteBasicSetting(Settings::values.cpuopt_ignore_memory_aborts);
}
qt_config->endGroup();

View File

@@ -45,6 +45,9 @@ void ConfigureCpuDebug::SetConfiguration() {
ui->cpuopt_recompile_exclusives->setEnabled(runtime_lock);
ui->cpuopt_recompile_exclusives->setChecked(
Settings::values.cpuopt_recompile_exclusives.GetValue());
ui->cpuopt_ignore_memory_aborts->setEnabled(runtime_lock);
ui->cpuopt_ignore_memory_aborts->setChecked(
Settings::values.cpuopt_ignore_memory_aborts.GetValue());
}
void ConfigureCpuDebug::ApplyConfiguration() {
@@ -59,6 +62,7 @@ void ConfigureCpuDebug::ApplyConfiguration() {
Settings::values.cpuopt_fastmem = ui->cpuopt_fastmem->isChecked();
Settings::values.cpuopt_fastmem_exclusives = ui->cpuopt_fastmem_exclusives->isChecked();
Settings::values.cpuopt_recompile_exclusives = ui->cpuopt_recompile_exclusives->isChecked();
Settings::values.cpuopt_ignore_memory_aborts = ui->cpuopt_ignore_memory_aborts->isChecked();
}
void ConfigureCpuDebug::changeEvent(QEvent* event) {

View File

@@ -175,6 +175,19 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cpuopt_ignore_memory_aborts">
<property name="toolTip">
<string>
&lt;div style=&quot;white-space: nowrap&quot;&gt;This optimization speeds up memory accesses by allowing invalid memory accesses to succeed.&lt;/div&gt;
&lt;div style=&quot;white-space: nowrap&quot;&gt;Enabling it reduces the overhead of all memory accesses and has no impact on programs that don't access invalid memory.&lt;/div&gt;
</string>
</property>
<property name="text">
<string>Enable fallbacks for invalid memory accesses</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View File

@@ -31,7 +31,7 @@ ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* paren
ui->backend->addItem(QStringLiteral("GLSL"));
ui->backend->addItem(tr("GLASM (Assembly Shaders, NVIDIA Only)"));
ui->backend->addItem(QStringLiteral("SPIR-V (Experimental, Mesa Only)"));
ui->backend->addItem(tr("SPIR-V (Experimental, Mesa Only)"));
SetupPerGameUI();

View File

@@ -855,8 +855,7 @@ void ConfigureInputPlayer::UpdateInputDeviceCombobox() {
return;
}
const auto devices =
emulated_controller->GetMappedDevices(Core::HID::EmulatedDeviceIndex::AllDevices);
const auto devices = emulated_controller->GetMappedDevices();
UpdateInputDevices();
if (devices.empty()) {

View File

@@ -43,13 +43,7 @@ target_link_libraries(yuzu-cmd PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
create_resource("../../dist/yuzu.bmp" "yuzu_cmd/yuzu_icon.h" "yuzu_icon")
target_include_directories(yuzu-cmd PRIVATE ${RESOURCES_DIR})
target_link_libraries(yuzu-cmd PRIVATE Vulkan::Headers)
if (YUZU_USE_EXTERNAL_SDL2)
target_link_libraries(yuzu-cmd PRIVATE SDL2-static)
else()
target_link_libraries(yuzu-cmd PRIVATE SDL2)
endif()
target_link_libraries(yuzu-cmd PRIVATE SDL2::SDL2 Vulkan::Headers)
if(UNIX AND NOT APPLE)
install(TARGETS yuzu-cmd)

View File

@@ -286,6 +286,7 @@ void Config::ReadValues() {
ReadSetting("Cpu", Settings::values.cpuopt_fastmem);
ReadSetting("Cpu", Settings::values.cpuopt_fastmem_exclusives);
ReadSetting("Cpu", Settings::values.cpuopt_recompile_exclusives);
ReadSetting("Cpu", Settings::values.cpuopt_ignore_memory_aborts);
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_unfuse_fma);
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_reduce_fp_error);
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_standard_fpcr);

View File

@@ -208,6 +208,10 @@ cpuopt_fastmem_exclusives =
# 0: Disabled, 1 (default): Enabled
cpuopt_recompile_exclusives =
# Enable optimization to ignore invalid memory accesses (faster guest memory access)
# 0: Disabled, 1 (default): Enabled
cpuopt_ignore_memory_aborts =
# Enable unfuse FMA (improve performance on CPUs without FMA)
# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
# 0: Disabled, 1 (default): Enabled