Compare commits

..

100 Commits

Author SHA1 Message Date
David Marcec
967307d3be Fix style issues 2020-07-18 14:24:32 +10:00
David Marcec
4f473cda64 Drop settings namespace 2020-07-17 17:23:24 +10:00
David Marcec
85b591f6f0 Remove duplicate config 2020-07-17 14:26:18 +10:00
David Marcec
f48187449e Use conditional var 2020-07-17 14:26:17 +10:00
David Marcec
2ba195aa0d Drop max workers from 8->2 for testing 2020-07-17 14:26:15 +10:00
David Marcec
85d7a8f466 Rebase for per game settings 2020-07-17 14:26:14 +10:00
David Marcec
468bd9c1b0 async shaders 2020-07-17 14:24:57 +10:00
David
c783cf443e Merge pull request #4347 from lioncash/logging
settings: Make use of std::string_view over std::string for logging
2020-07-17 13:25:06 +10:00
David
92f37a229e Merge pull request #4371 from lioncash/cmake2
core/CMakeLists: Add missing physical_memory.h header file
2020-07-17 13:22:19 +10:00
David
adbf5ca50b Merge pull request #4357 from lioncash/unused4
kernel: Remove unused variables
2020-07-17 13:18:31 +10:00
David
69f8b6a53e Merge pull request #4358 from lioncash/unused5
kernel/thread: Remove unimplemented function prototype
2020-07-17 13:17:52 +10:00
David
0d10c863a5 Merge pull request #4367 from lioncash/inc2
constants: Add missing <array> include
2020-07-17 13:14:01 +10:00
David
9cca0c2f83 Merge pull request #4368 from lioncash/macro
macro: Resolve missing parameter in doxygen comment
2020-07-17 13:13:22 +10:00
David
3ce4edba64 Merge pull request #4370 from lioncash/simplify
macro_hle: Simplify shift expression in HLE_771BB18C62444DA0()
2020-07-17 13:13:05 +10:00
Lioncash
311f500753 core/CMakeLists: Add missing physical_memory.h header file
Allows this header file to show up in IDE CMake generators.
2020-07-16 22:56:31 -04:00
Lioncash
be6b7591d9 macro_hle: Simplify shift expression in HLE_771BB18C62444DA0()
Given the expression involves a 32-bit value, this simplifies down to
just: 0x3ffffff. This is likely a remnant from testing that was never
cleaned up.

Resolves a -Wshift-overflow warning.
2020-07-16 22:16:11 -04:00
Lioncash
502dbfb9eb macro: Resolve missing parameter in doxygen comment
Resolves a -Wdocumentation warning.
2020-07-16 21:54:42 -04:00
Lioncash
e07eb5b223 constants: Add missing <array> include
Eliminates reliance on an indirect include.
2020-07-16 21:43:20 -04:00
Rodrigo Locatti
39ae2deb28 Merge pull request #4363 from lioncash/mismatch
vk_texture_cache: Amend mismatched access masks and indices in UploadBuffer
2020-07-16 21:54:40 -03:00
bunnei
3bbf4462db Merge pull request #4292 from bunnei/mii-rewrite
hle: service: mii: Rewrite service to properly support creation of random and default miis.
2020-07-16 20:05:40 -04:00
Lioncash
169759e069 vk_texture_cache: Amend mismatched access masks and indices in UploadBuffer
Discovered while converting relevant parts of the codebase over to
designated initializers.
2020-07-16 19:45:46 -04:00
bunnei
267d483ed4 Merge pull request #4362 from lioncash/cast
vk_graphics_pipeline: Resolve narrowing warnings
2020-07-16 18:49:42 -04:00
Lioncash
fb563e75e9 vk_graphics_pipeline: Resolve narrowing warnings
For whatever reason, VK_TRUE and VK_FALSE aren't defined as having a
VkBool32 type, so we need to cast to it explicitly.
2020-07-16 18:13:49 -04:00
Rodrigo Locatti
104c523d3d Merge pull request #4327 from lioncash/desig2
address_space_info: Make use of designated initializers
2020-07-16 17:41:55 -03:00
Rodrigo Locatti
be68ee88c2 Merge pull request #4333 from lioncash/desig3
vk_graphics_pipeline: Make use of designated initializers where applicable
2020-07-16 17:41:45 -03:00
Rodrigo Locatti
b6d73ec9c2 Merge pull request #4332 from lioncash/vkdev
vk_device: Make use of designated initializers where applicable
2020-07-16 17:41:20 -03:00
bunnei
98b36625fa Merge pull request #4321 from lioncash/desig
vk_blit_screen: Make use of designated initializers where applicable
2020-07-16 14:55:36 -04:00
Lioncash
8bef49cde5 kernel/thread: Remove unimplemented function prototype
This isn't used, so it can be removed.
2020-07-16 14:32:46 -04:00
Lioncash
2bab07c367 kernel: Remove unused variables
Resolves some compiler warnings in the Linux build.
2020-07-16 14:17:50 -04:00
LC
d84d9a64b3 Merge pull request #4356 from lioncash/inc
cpu_manager: Minor tidying up/header inclusions
2020-07-16 14:16:57 -04:00
Lioncash
07d080ecc8 kernel: Add missing include 2020-07-16 13:51:51 -04:00
Lioncash
f0125b2be8 cpu_manager: Mark function getters as static
All these do are return std::function instances of static functions, so
these can be used without an instance of the CPU manager.
2020-07-16 13:30:56 -04:00
Lioncash
51546ce57e cpu_manager: Remove unused preemption_count variable
Shrinks the data structure by 8 bytes.
2020-07-16 13:24:25 -04:00
Lioncash
201514cb50 cpu_manager: Add missing includes
Previously this header was relying on indirect inclusions that are no
longer satisfied.
2020-07-16 13:22:58 -04:00
bunnei
a89dfc9183 Merge pull request #4261 from ameerj/gc-calibration
input_common: GC Controller save and compare against analog origin state
2020-07-16 11:40:09 -04:00
bunnei
2781201bfb Merge pull request #4337 from lat9nq/fix-per-game-async
main: Set async gpu properly after loading per-game setting
2020-07-16 11:33:51 -04:00
bunnei
9121d35e70 Merge pull request #4297 from FearlessTobi/skip-profile-select
main/profile_select: Don't prompt for profile selection when only one is available
2020-07-16 09:45:09 -04:00
David
0648e023ea Merge pull request #4346 from lioncash/thread
kernel/handle_table: Remove usages of the global system instance
2020-07-16 23:02:04 +10:00
David
815f30dc10 Merge pull request #4249 from Morph1984/delete-update-aoc-on-overwrite
registered_cache: Remove previous update/dlc if it exists on install
2020-07-16 20:36:22 +10:00
Morph
f66e3181dc Check for empty section0 and CNMT prior to install 2020-07-16 05:22:51 -04:00
bunnei
f26f53f35b Merge pull request #4328 from lioncash/unused-var3
memory_layout: Remove unused data member
2020-07-15 22:08:05 -04:00
Lioncash
af5a56ddc4 settings: Resolve a sign conversion warning within GetTimeZoneString()
A sign conversion warning was occurring due to an int < size_t
comparison.
2020-07-15 13:45:22 -04:00
Lioncash
73bb87c06b kernel/process: Move name and system context to the bottom of the member list
These aren't directly important or commonly used within the process, so
we can move these to the bottom to allow everything else to be more
likely to be within a cache line.
2020-07-15 13:40:18 -04:00
Lioncash
52e83f0d5c kernel/handle_table: Remove usages of the global system instance
Removes even more usages of the global system instance, trimming away
more dependencies on global variables and making them explicit in the
interface.
2020-07-15 13:40:15 -04:00
Lioncash
5dbf91d739 settings: Make use of std::string_view over std::string for logging
In all usages of LogSetting(), string literals are provided.
std::string_view is better suited here, as we won't churn a bunch of
string allocations every time the settings are logged out.

While we're at it, we can fold LogSetting() into LogSettings(), given
it's only ever used there.
2020-07-15 13:37:33 -04:00
Lioncash
4ad69ca96e kernel/thread: Remove global GetCurrentThread()
This is only used in one place, so we can fold it into the calling code,
eliminating a place for the global system instance to be used.
2020-07-15 13:28:05 -04:00
Morph
0ca7b8269a clang format 2020-07-15 13:27:04 -04:00
Morph
1bbc61f5f1 Use proper install result when overwriting files 2020-07-15 13:27:04 -04:00
Morph
8794e623d9 Remove global system instance and address feedback 2020-07-15 13:27:04 -04:00
Morph
a82fdea1ac registered_cache: Remove previous update/dlc if it exists on install
- This checks for and removes old updates or dlc based on title id. If a content meta nca exists within the registered cache, it will attempt to remove all the ncas associated with the content meta before installing a new update/dlc
2020-07-15 13:27:04 -04:00
Rodrigo Locatti
263200f982 Merge pull request #4342 from lioncash/endian
common/swap: Make use of std::endian
2020-07-14 18:49:07 -03:00
Lioncash
9f027b1af2 common/swap: Make use of std::endian
Allows removing a bunch of defines in favor of a two liner.
2020-07-14 16:26:54 -04:00
bunnei
666b37ad56 Merge pull request #4242 from ReinUsesLisp/maxwell-dma
maxwell_dma: Match official doc and support pitch->voxel copies
2020-07-14 14:04:16 -04:00
lat9nq
a683e42516 clang-format 2020-07-14 13:46:42 -04:00
lat9nq
6d1477f214 settings: Move settings sanitization to its own function
Creates a new function that can be expanded later to fix other settings that are known to cause emulation errors across executables.
2020-07-14 13:36:09 -04:00
Ameer
93fe982a0c Rebase to master 2020-07-14 13:04:02 -04:00
bunnei
e2730372b8 Merge pull request #4294 from MerryMage/cpu-opt-settings
configuration: Add settings to enable/disable specific CPU optimizations
2020-07-14 12:38:03 -04:00
bunnei
450cbcfee6 Merge pull request #4282 from Morph1984/fs-size
filesystem: Set various NAND partition sizes to their defaults
2020-07-14 12:16:42 -04:00
bunnei
bf9c010be5 Merge pull request #4338 from ameerj/disconnected-adapter
gcadapter: Fix crash if gc configured but adapter not connected
2020-07-14 12:01:43 -04:00
Ameer
ab65de2f96 Fix crash if gc configured but adapter not connected 2020-07-14 11:23:10 -04:00
bunnei
393cdb15f5 Merge pull request #4314 from lioncash/input-warn
gcadapter: Tidy up compiler warnings
2020-07-14 10:20:12 -04:00
bunnei
edb291b3be Merge pull request #4315 from lioncash/udp-warn
udp: Silence a C++20 deprecation warning
2020-07-14 09:33:16 -04:00
LC
6989fd65f3 Merge pull request #4335 from lat9nq/fix-set-per-game-multicore
configure_general: Explicitly guard use_multi_core when applying setting
2020-07-14 07:53:04 -04:00
lat9nq
8160e142e1 main: Set async gpu properly after loading per-game setting
Another error that got pass me and only noticed when I was doing the per-game settings UI rework. This prevents asynchronous GPU emulation from being disabled while multi core is enabled as a result of a poorly put together per-game config.
2020-07-14 01:02:10 -04:00
lat9nq
e02687ff47 configure_general: Explicitly guard use_multi_core when applying settings
This is likely an oversight during a rebase. Guards use_multi_core to be only set when the global value is in use. It should not make a difference given the current code base, but makes the code sensible.
2020-07-14 00:49:17 -04:00
Lioncash
0f8b977663 vk_device: Make use of designated initializers where applicable
Avoids redundant repetitions of variable names, and allows assignment
all in one statement.
2020-07-13 22:24:01 -04:00
LC
edb2caaae5 Merge pull request #4280 from jbeich/system-libusb
cmake: pass libusb include directory
2020-07-13 22:21:07 -04:00
Ameer
b284c43385 input_common: drop unused libusb.h include
Remnant of an early implementation.
2020-07-14 01:50:34 +00:00
Jan Beich
883fab2fff input_common: make libusb private to gc_adapter 2020-07-13 18:48:19 +00:00
Lioncash
f2f876e3ff memory_layout: Remove unused data member
This isn't used, so it can be removed entirely, shrinking the structure
size by 8 bytes.
2020-07-13 10:51:23 -04:00
Lioncash
ed0fe04b4f address_space_info: Use type alias to simplify code
We can define an alias for the index arrays and then just reuse it to
make the code nicer to read.
2020-07-13 10:42:52 -04:00
Lioncash
c3eb42de65 address_space_info: Make use of designated initializers
We can alter the structure so that we can use designated initializers in
the array, eliminating the comments that indicate their field names.
2020-07-13 10:42:49 -04:00
Lioncash
db6fbd5894 vk_blit_screen: Make use of designated initializers where applicable
Now that we make use of C++20, we can use designated initializers to
make things a little nicer to read.
2020-07-12 19:45:30 -04:00
Rodrigo Locatti
f1d8c83e1c Merge pull request #4318 from lioncash/cpp20
CMakeLists: Enable usage of C++20 on Linux
2020-07-12 19:39:09 -03:00
Lioncash
07632ad825 CMakeLists: Enable usage of C++20 on Linux
This also fixes building on Linux with C++20, so we can enable it across
the board for all OSes that we officially support.
2020-07-12 18:15:29 -04:00
Lioncash
8df93132cd udp: Silence a C++20 deprecation warning
C++20 deprecates using the = lambda capture to implicitly capture the
this pointer. Instead, we must specify it explicitly.
2020-07-12 15:49:42 -04:00
Lioncash
a1dddca4ab gc_poller: Mark GCButtonFactory::GetNextInput() as const
This doesn't modify class instance state.
2020-07-12 15:43:07 -04:00
Lioncash
839c91cd14 gc_poller: Get rid of undefined behavior in Create()
Ensures that the function always has returns in all control paths.
2020-07-12 15:41:35 -04:00
Lioncash
a8ba6dc3c9 gc_poller: Silence sign conversion warnings 2020-07-12 15:40:22 -04:00
Lioncash
32b6fc4062 gc_adapter: Remove deprecated usage of = in lambda captures
It's deprecated in C++20 to use = to capture the this pointer.

Instead, we can simply pass this as an argument to the thread
constructor.
2020-07-12 15:38:19 -04:00
Lioncash
9ce6ea648f gc_adapter: Silence sign conversion warnings 2020-07-12 15:36:27 -04:00
MerryMage
a67d00ef31 configure_cpu: Split optimization settings off into Debug tab 2020-07-12 19:32:32 +01:00
bunnei
e706501c8d hle: service: mii: Rewrite service to properly support creation of random and default miis. 2020-07-11 21:34:59 -04:00
MerryMage
da11a27f42 configure_cpu: Add tooltips 2020-07-11 16:38:38 +01:00
MerryMage
505aa3a4c1 configure_cpu: Show/Hide debugging options 2020-07-11 16:38:38 +01:00
FearlessTobi
a59ad9246b main/profile_select: Don't prompt for profile selection when only one is available 2020-07-11 16:08:34 +02:00
MerryMage
0193202964 configuration: Add settings to enable/disable specific CPU optimizations 2020-07-11 14:34:09 +01:00
Ameer
042c6602a0 Break out of scan loop if can't find adapter on first run 2020-07-10 11:07:43 -04:00
ReinUsesLisp
c574ab5aa1 video_core/textures: Add and use SwizzleSliceToVoxel, and minor style changes
Change GOB sizes from free-functions to constexpr constants.

Add SwizzleSliceToVoxel, a function that swizzles a 2D array of pixels
into a 3D texture and use it for 3D copies.
2020-07-10 04:09:32 -03:00
Morph
b24b463c87 bis_factory: Set User NAND free space to be 1 MiB less than total. 2020-07-10 00:37:39 -04:00
Morph
17242a8865 sdmc_factory: Set the SDMC total size to 1 TiB
We should not be limited by the SDMC's partition size, set this to 1 TiB. Hardware is limited to the max allowed by the MBR partition table which is 2 TiB.
2020-07-10 00:37:39 -04:00
Morph
0373ead96e bis_factory: Use hardware default NAND partition sizes
Sets the total space of user and system partitions to their hardware defaults.
Furthermore, return the total space as free space for the user partition to prevent it from reaching zero.
Some games like Bioshock 2 check for the available free space prior to save creation, and we should not be limited by arbitrary limits.
2020-07-10 00:37:39 -04:00
Morph
47e26d7bc7 settings: Remove storage size options 2020-07-10 00:37:39 -04:00
Jan Beich
48ff15602e cmake: pass libusb include directory as well
In file included from src/input_common/gcadapter/gc_adapter.cpp:8:
src/./input_common/gcadapter/gc_adapter.h:11:10: fatal error: 'libusb.h' file not found
 #include <libusb.h>
          ^~~~~~~~~~
2020-07-09 15:26:54 +00:00
Ameer
4489ea6f53 Rebase to master, fix merge conflicts 2020-07-08 21:15:49 -04:00
ReinUsesLisp
2a9d17b7e7 maxwell_dma: Rename registers to match official docs and reorder
Rename registers in the MaxwellDMA class to match Nvidia's official
documentation. This one can be found here:

https://github.com/NVIDIA/open-gpu-doc/blob/master/classes/dma-copy/clb0b5.h

While we are at it, reorganize the code in MaxwellDMA to be separated in
different functions.
2020-07-07 19:19:33 -03:00
Ameer
b57475887b Address PR feedback, fix axis button thresholding 2020-07-07 12:20:59 -04:00
Ameer
e3253b5f18 Brace the code! Fix compile error due to class member construction order 2020-07-06 23:01:57 -04:00
Ameer
86abff48e1 Recalibrate reconnected controllers 2020-07-06 22:09:07 -04:00
Ameer
7ad423923d Save origin state of GC controller analog features, compare against origin for input detection 2020-07-06 21:58:31 -04:00
94 changed files with 6018 additions and 2352 deletions

View File

@@ -118,15 +118,15 @@ message(STATUS "Target architecture: ${ARCHITECTURE}")
# Configure C++ standard
# ===========================
# boost asio's concept usage doesn't play nicely with some compilers yet.
add_definitions(-DBOOST_ASIO_DISABLE_CONCEPTS)
if (MSVC)
add_compile_options(/std:c++latest)
# cubeb and boost still make use of deprecated result_of.
add_definitions(-D_HAS_DEPRECATED_RESULT_OF)
# boost asio's concept usage doesn't play nicely with MSVC yet.
add_definitions(-DBOOST_ASIO_DISABLE_CONCEPTS)
else()
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
endif()
@@ -345,6 +345,7 @@ if(NOT APPLE)
endif()
if (NOT LIBUSB_FOUND)
add_subdirectory(externals/libusb)
set(LIBUSB_INCLUDE_DIR "")
set(LIBUSB_LIBRARIES usb)
endif()

View File

@@ -17,43 +17,14 @@
#pragma once
#include <type_traits>
#if defined(_MSC_VER)
#include <cstdlib>
#endif
#include <bit>
#include <cstring>
#include <type_traits>
#include "common/common_types.h"
// GCC
#ifdef __GNUC__
#if __BYTE_ORDER__ && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) && !defined(COMMON_LITTLE_ENDIAN)
#define COMMON_LITTLE_ENDIAN 1
#elif __BYTE_ORDER__ && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) && !defined(COMMON_BIG_ENDIAN)
#define COMMON_BIG_ENDIAN 1
#endif
// LLVM/clang
#elif defined(__clang__)
#if __LITTLE_ENDIAN__ && !defined(COMMON_LITTLE_ENDIAN)
#define COMMON_LITTLE_ENDIAN 1
#elif __BIG_ENDIAN__ && !defined(COMMON_BIG_ENDIAN)
#define COMMON_BIG_ENDIAN 1
#endif
// MSVC
#elif defined(_MSC_VER) && !defined(COMMON_BIG_ENDIAN) && !defined(COMMON_LITTLE_ENDIAN)
#define COMMON_LITTLE_ENDIAN 1
#endif
// Worst case, default to little endian.
#if !COMMON_BIG_ENDIAN && !COMMON_LITTLE_ENDIAN
#define COMMON_LITTLE_ENDIAN 1
#endif
namespace Common {
#ifdef _MSC_VER
@@ -675,17 +646,8 @@ struct AddEndian<T, SwapTag> {
};
// Alias LETag/BETag as KeepTag/SwapTag depending on the system
#if COMMON_LITTLE_ENDIAN
using LETag = KeepTag;
using BETag = SwapTag;
#else
using BETag = KeepTag;
using LETag = SwapTag;
#endif
using LETag = std::conditional_t<std::endian::native == std::endian::little, KeepTag, SwapTag>;
using BETag = std::conditional_t<std::endian::native == std::endian::big, KeepTag, SwapTag>;
// Aliases for LE types
using u16_le = AddEndian<u16, LETag>::type;

View File

@@ -185,6 +185,7 @@ add_library(core STATIC
hle/kernel/object.h
hle/kernel/physical_core.cpp
hle/kernel/physical_core.h
hle/kernel/physical_memory.h
hle/kernel/process.cpp
hle/kernel/process.h
hle/kernel/process_capability.cpp
@@ -398,10 +399,13 @@ add_library(core STATIC
hle/service/lm/manager.h
hle/service/mig/mig.cpp
hle/service/mig/mig.h
hle/service/mii/manager.cpp
hle/service/mii/manager.h
hle/service/mii/mii.cpp
hle/service/mii/mii.h
hle/service/mii/mii_manager.cpp
hle/service/mii/mii_manager.h
hle/service/mii/raw_data.cpp
hle/service/mii/raw_data.h
hle/service/mii/types.h
hle/service/mm/mm_u.cpp
hle/service/mm/mm_u.h
hle/service/ncm/ncm.cpp

View File

@@ -142,10 +142,32 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable&
// Timing
config.wall_clock_cntpct = uses_wall_clock;
// Optimizations
if (Settings::values.disable_cpu_opt) {
config.enable_optimizations = false;
config.enable_fast_dispatch = false;
// Safe optimizations
if (Settings::values.cpu_accuracy != Settings::CPUAccuracy::Accurate) {
if (!Settings::values.cpuopt_page_tables) {
config.page_table = nullptr;
}
if (!Settings::values.cpuopt_block_linking) {
config.optimizations &= ~Dynarmic::OptimizationFlag::BlockLinking;
}
if (!Settings::values.cpuopt_return_stack_buffer) {
config.optimizations &= ~Dynarmic::OptimizationFlag::ReturnStackBuffer;
}
if (!Settings::values.cpuopt_fast_dispatcher) {
config.optimizations &= ~Dynarmic::OptimizationFlag::FastDispatch;
}
if (!Settings::values.cpuopt_context_elimination) {
config.optimizations &= ~Dynarmic::OptimizationFlag::GetSetElimination;
}
if (!Settings::values.cpuopt_const_prop) {
config.optimizations &= ~Dynarmic::OptimizationFlag::ConstProp;
}
if (!Settings::values.cpuopt_misc_ir) {
config.optimizations &= ~Dynarmic::OptimizationFlag::MiscIROpt;
}
if (!Settings::values.cpuopt_reduce_misalign_checks) {
config.only_detect_misalignment_via_page_table_on_page_boundary = false;
}
}
return std::make_unique<Dynarmic::A32::Jit>(config);

View File

@@ -191,15 +191,37 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable&
// Unpredictable instructions
config.define_unpredictable_behaviour = true;
// Optimizations
if (Settings::values.disable_cpu_opt) {
config.enable_optimizations = false;
config.enable_fast_dispatch = false;
}
// Timing
config.wall_clock_cntpct = uses_wall_clock;
// Safe optimizations
if (Settings::values.cpu_accuracy != Settings::CPUAccuracy::Accurate) {
if (!Settings::values.cpuopt_page_tables) {
config.page_table = nullptr;
}
if (!Settings::values.cpuopt_block_linking) {
config.optimizations &= ~Dynarmic::OptimizationFlag::BlockLinking;
}
if (!Settings::values.cpuopt_return_stack_buffer) {
config.optimizations &= ~Dynarmic::OptimizationFlag::ReturnStackBuffer;
}
if (!Settings::values.cpuopt_fast_dispatcher) {
config.optimizations &= ~Dynarmic::OptimizationFlag::FastDispatch;
}
if (!Settings::values.cpuopt_context_elimination) {
config.optimizations &= ~Dynarmic::OptimizationFlag::GetSetElimination;
}
if (!Settings::values.cpuopt_const_prop) {
config.optimizations &= ~Dynarmic::OptimizationFlag::ConstProp;
}
if (!Settings::values.cpuopt_misc_ir) {
config.optimizations &= ~Dynarmic::OptimizationFlag::MiscIROpt;
}
if (!Settings::values.cpuopt_reduce_misalign_checks) {
config.only_detect_misalignment_via_page_table_on_page_boundary = false;
}
}
return std::make_shared<Dynarmic::A64::Jit>(config);
}

View File

@@ -4,6 +4,7 @@
#pragma once
#include <array>
#include "common/common_types.h"
// This is to consolidate system-wide constants that are used by multiple components of yuzu.

View File

@@ -9,6 +9,9 @@
#include <functional>
#include <memory>
#include <thread>
#include "common/fiber.h"
#include "common/thread.h"
#include "core/hardware_properties.h"
namespace Common {
@@ -46,9 +49,9 @@ public:
void Pause(bool paused);
std::function<void(void*)> GetGuestThreadStartFunc();
std::function<void(void*)> GetIdleThreadStartFunc();
std::function<void(void*)> GetSuspendThreadStartFunc();
static std::function<void(void*)> GetGuestThreadStartFunc();
static std::function<void(void*)> GetIdleThreadStartFunc();
static std::function<void(void*)> GetSuspendThreadStartFunc();
void* GetStartFuncParamater();
void PreemptSingleCore(bool from_running_enviroment = true);
@@ -97,7 +100,6 @@ private:
bool is_async_gpu{};
bool is_multicore{};
std::atomic<std::size_t> current_core{};
std::size_t preemption_count{};
std::size_t idle_count{};
static constexpr std::size_t max_cycle_runs = 5;

View File

@@ -12,6 +12,10 @@
namespace FileSys {
constexpr u64 NAND_USER_SIZE = 0x680000000; // 26624 MiB
constexpr u64 NAND_SYSTEM_SIZE = 0xA0000000; // 2560 MiB
constexpr u64 NAND_TOTAL_SIZE = 0x747C00000; // 29820 MiB
BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_, VirtualDir dump_root_)
: nand_root(std::move(nand_root_)), load_root(std::move(load_root_)),
dump_root(std::move(dump_root_)),
@@ -110,30 +114,29 @@ VirtualDir BISFactory::GetImageDirectory() const {
u64 BISFactory::GetSystemNANDFreeSpace() const {
const auto sys_dir = GetOrCreateDirectoryRelative(nand_root, "/system");
if (sys_dir == nullptr)
return 0;
if (sys_dir == nullptr) {
return GetSystemNANDTotalSpace();
}
return GetSystemNANDTotalSpace() - sys_dir->GetSize();
}
u64 BISFactory::GetSystemNANDTotalSpace() const {
return static_cast<u64>(Settings::values.nand_system_size);
return NAND_SYSTEM_SIZE;
}
u64 BISFactory::GetUserNANDFreeSpace() const {
const auto usr_dir = GetOrCreateDirectoryRelative(nand_root, "/user");
if (usr_dir == nullptr)
return 0;
return GetUserNANDTotalSpace() - usr_dir->GetSize();
// For some reason games such as BioShock 1 checks whether this is exactly 0x680000000 bytes.
// Set the free space to be 1 MiB less than the total as a workaround to this issue.
return GetUserNANDTotalSpace() - 0x100000;
}
u64 BISFactory::GetUserNANDTotalSpace() const {
return static_cast<u64>(Settings::values.nand_user_size);
return NAND_USER_SIZE;
}
u64 BISFactory::GetFullNANDTotalSpace() const {
return static_cast<u64>(Settings::values.nand_total_size);
return NAND_TOTAL_SIZE;
}
VirtualDir BISFactory::GetBCATDirectory(u64 title_id) const {

View File

@@ -547,6 +547,56 @@ InstallResult RegisteredCache::InstallEntry(const XCI& xci, bool overwrite_if_ex
return InstallEntry(*xci.GetSecurePartitionNSP(), overwrite_if_exists, copy);
}
bool RegisteredCache::RemoveExistingEntry(u64 title_id) {
const auto delete_nca = [this](const NcaID& id) {
const auto path = GetRelativePathFromNcaID(id, false, true, false);
if (dir->GetFileRelative(path) == nullptr) {
return false;
}
Core::Crypto::SHA256Hash hash{};
mbedtls_sha256_ret(id.data(), id.size(), hash.data(), 0);
const auto dirname = fmt::format("000000{:02X}", hash[0]);
const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname);
const auto res = dir2->DeleteFile(fmt::format("{}.nca", Common::HexToString(id, false)));
return res;
};
// If an entry exists in the registered cache, remove it
if (HasEntry(title_id, ContentRecordType::Meta)) {
LOG_INFO(Loader,
"Previously installed entry (v{}) for title_id={:016X} detected! "
"Attempting to remove...",
GetEntryVersion(title_id).value_or(0), title_id);
// Get all the ncas associated with the current CNMT and delete them
const auto meta_old_id =
GetNcaIDFromMetadata(title_id, ContentRecordType::Meta).value_or(NcaID{});
const auto program_id =
GetNcaIDFromMetadata(title_id, ContentRecordType::Program).value_or(NcaID{});
const auto data_id =
GetNcaIDFromMetadata(title_id, ContentRecordType::Data).value_or(NcaID{});
const auto control_id =
GetNcaIDFromMetadata(title_id, ContentRecordType::Control).value_or(NcaID{});
const auto html_id =
GetNcaIDFromMetadata(title_id, ContentRecordType::HtmlDocument).value_or(NcaID{});
const auto legal_id =
GetNcaIDFromMetadata(title_id, ContentRecordType::LegalInformation).value_or(NcaID{});
delete_nca(meta_old_id);
delete_nca(program_id);
delete_nca(data_id);
delete_nca(control_id);
delete_nca(html_id);
delete_nca(legal_id);
return true;
}
return false;
}
InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_exists,
const VfsCopyFunction& copy) {
const auto ncas = nsp.GetNCAsCollapsed();
@@ -560,31 +610,57 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex
return InstallResult::ErrorMetaFailed;
}
// Install Metadata File
const auto meta_id_raw = (*meta_iter)->GetName().substr(0, 32);
const auto meta_id = Common::HexStringToArray<16>(meta_id_raw);
const auto res = RawInstallNCA(**meta_iter, copy, overwrite_if_exists, meta_id);
if (res != InstallResult::Success)
return res;
if ((*meta_iter)->GetSubdirectories().empty()) {
LOG_ERROR(Loader,
"The file you are attempting to install does not contain a section0 within the "
"metadata NCA and is therefore malformed. Verify that the file is valid.");
return InstallResult::ErrorMetaFailed;
}
// Install all the other NCAs
const auto section0 = (*meta_iter)->GetSubdirectories()[0];
if (section0->GetFiles().empty()) {
LOG_ERROR(Loader,
"The file you are attempting to install does not contain a CNMT within the "
"metadata NCA and is therefore malformed. Verify that the file is valid.");
return InstallResult::ErrorMetaFailed;
}
const auto cnmt_file = section0->GetFiles()[0];
const CNMT cnmt(cnmt_file);
const auto title_id = cnmt.GetTitleID();
const auto result = RemoveExistingEntry(title_id);
// Install Metadata File
const auto res = RawInstallNCA(**meta_iter, copy, overwrite_if_exists, meta_id);
if (res != InstallResult::Success) {
return res;
}
// Install all the other NCAs
for (const auto& record : cnmt.GetContentRecords()) {
// Ignore DeltaFragments, they are not useful to us
if (record.type == ContentRecordType::DeltaFragment)
if (record.type == ContentRecordType::DeltaFragment) {
continue;
}
const auto nca = GetNCAFromNSPForID(nsp, record.nca_id);
if (nca == nullptr)
if (nca == nullptr) {
return InstallResult::ErrorCopyFailed;
}
const auto res2 = RawInstallNCA(*nca, copy, overwrite_if_exists, record.nca_id);
if (res2 != InstallResult::Success)
if (res2 != InstallResult::Success) {
return res2;
}
}
Refresh();
if (result) {
return InstallResult::OverwriteExisting;
}
return InstallResult::Success;
}
@@ -610,8 +686,9 @@ InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type,
mbedtls_sha256_ret(data.data(), data.size(), c_rec.hash.data(), 0);
memcpy(&c_rec.nca_id, &c_rec.hash, 16);
const CNMT new_cnmt(header, opt_header, {c_rec}, {});
if (!RawInstallYuzuMeta(new_cnmt))
if (!RawInstallYuzuMeta(new_cnmt)) {
return InstallResult::ErrorMetaFailed;
}
return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id);
}
@@ -649,8 +726,9 @@ InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFuncti
}
auto out = dir->CreateFileRelative(path);
if (out == nullptr)
if (out == nullptr) {
return InstallResult::ErrorCopyFailed;
}
return copy(in, out, VFS_RC_LARGE_COPY_BLOCK) ? InstallResult::Success
: InstallResult::ErrorCopyFailed;
}

View File

@@ -34,6 +34,7 @@ using VfsCopyFunction = std::function<bool(const VirtualFile&, const VirtualFile
enum class InstallResult {
Success,
OverwriteExisting,
ErrorAlreadyExists,
ErrorCopyFailed,
ErrorMetaFailed,
@@ -154,6 +155,9 @@ public:
std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
std::optional<u64> title_id = {}) const override;
// Removes an existing entry based on title id
bool RemoveExistingEntry(u64 title_id);
// Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure
// there is a meta NCA and all of them are accessible.
InstallResult InstallEntry(const XCI& xci, bool overwrite_if_exists = false,

View File

@@ -10,6 +10,8 @@
namespace FileSys {
constexpr u64 SDMC_TOTAL_SIZE = 0x10000000000; // 1 TiB
SDMCFactory::SDMCFactory(VirtualDir dir_)
: dir(std::move(dir_)), contents(std::make_unique<RegisteredCache>(
GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"),
@@ -46,7 +48,7 @@ u64 SDMCFactory::GetSDMCFreeSpace() const {
}
u64 SDMCFactory::GetSDMCTotalSpace() const {
return static_cast<u64>(Settings::values.sdmc_size);
return SDMC_TOTAL_SIZE;
}
} // namespace FileSys

View File

@@ -24,7 +24,6 @@ namespace Kernel {
// Wake up num_to_wake (or all) threads in a vector.
void AddressArbiter::WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads,
s32 num_to_wake) {
auto& time_manager = system.Kernel().TimeManager();
// Only process up to 'target' threads, unless 'target' is <= 0, in which case process
// them all.
std::size_t last = waiting_threads.size();

View File

@@ -8,7 +8,9 @@
#include "core/core.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
namespace Kernel {
@@ -22,7 +24,7 @@ constexpr u16 GetGeneration(Handle handle) {
}
} // Anonymous namespace
HandleTable::HandleTable() {
HandleTable::HandleTable(KernelCore& kernel) : kernel{kernel} {
Clear();
}
@@ -103,9 +105,9 @@ bool HandleTable::IsValid(Handle handle) const {
std::shared_ptr<Object> HandleTable::GetGeneric(Handle handle) const {
if (handle == CurrentThread) {
return SharedFrom(GetCurrentThread());
return SharedFrom(kernel.CurrentScheduler().GetCurrentThread());
} else if (handle == CurrentProcess) {
return SharedFrom(Core::System::GetInstance().CurrentProcess());
return SharedFrom(kernel.CurrentProcess());
}
if (!IsValid(handle)) {

View File

@@ -14,6 +14,8 @@
namespace Kernel {
class KernelCore;
enum KernelHandle : Handle {
InvalidHandle = 0,
CurrentThread = 0xFFFF8000,
@@ -48,7 +50,7 @@ public:
/// This is the maximum limit of handles allowed per process in Horizon
static constexpr std::size_t MAX_COUNT = 1024;
HandleTable();
explicit HandleTable(KernelCore& kernel);
~HandleTable();
/**
@@ -134,6 +136,9 @@ private:
/// Head of the free slots linked list.
u16 next_free_slot = 0;
/// Underlying kernel instance that this handle table operates under.
KernelCore& kernel;
};
} // namespace Kernel

View File

@@ -50,7 +50,8 @@ namespace Kernel {
struct KernelCore::Impl {
explicit Impl(Core::System& system, KernelCore& kernel)
: global_scheduler{kernel}, synchronization{system}, time_manager{system}, system{system} {}
: global_scheduler{kernel}, synchronization{system}, time_manager{system},
global_handle_table{kernel}, system{system} {}
void SetMulticore(bool is_multicore) {
this->is_multicore = is_multicore;
@@ -160,13 +161,14 @@ struct KernelCore::Impl {
void InitializeSuspendThreads() {
for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
std::string name = "Suspend Thread Id:" + std::to_string(i);
std::function<void(void*)> init_func =
system.GetCpuManager().GetSuspendThreadStartFunc();
std::function<void(void*)> init_func = Core::CpuManager::GetSuspendThreadStartFunc();
void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
ThreadType type =
const auto type =
static_cast<ThreadType>(THREADTYPE_KERNEL | THREADTYPE_HLE | THREADTYPE_SUSPEND);
auto thread_res = Thread::Create(system, type, name, 0, 0, 0, static_cast<u32>(i), 0,
nullptr, std::move(init_func), init_func_parameter);
auto thread_res =
Thread::Create(system, type, std::move(name), 0, 0, 0, static_cast<u32>(i), 0,
nullptr, std::move(init_func), init_func_parameter);
suspend_threads[i] = std::move(thread_res).Unwrap();
}
}
@@ -307,7 +309,7 @@ struct KernelCore::Impl {
// This is the kernel's handle table or supervisor handle table which
// stores all the objects in place.
Kernel::HandleTable global_handle_table;
HandleTable global_handle_table;
/// Map of named ports managed by the kernel, which can be retrieved using
/// the ConnectToPort SVC.

View File

@@ -9,6 +9,7 @@
#include <string>
#include <unordered_map>
#include <vector>
#include "core/arm/cpu_interrupt_handler.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/memory/memory_types.h"
#include "core/hle/kernel/object.h"

View File

@@ -29,40 +29,39 @@ enum : u64 {
// clang-format off
constexpr std::array<AddressSpaceInfo, 13> AddressSpaceInfos{{
{ 32 /*bit_width*/, Size_2_MB /*addr*/, Size_1_GB - Size_2_MB /*size*/, AddressSpaceInfo::Type::Is32Bit, },
{ 32 /*bit_width*/, Size_1_GB /*addr*/, Size_4_GB - Size_1_GB /*size*/, AddressSpaceInfo::Type::Small64Bit, },
{ 32 /*bit_width*/, Invalid /*addr*/, Size_1_GB /*size*/, AddressSpaceInfo::Type::Heap, },
{ 32 /*bit_width*/, Invalid /*addr*/, Size_1_GB /*size*/, AddressSpaceInfo::Type::Alias, },
{ 36 /*bit_width*/, Size_128_MB /*addr*/, Size_2_GB - Size_128_MB /*size*/, AddressSpaceInfo::Type::Is32Bit, },
{ 36 /*bit_width*/, Size_2_GB /*addr*/, Size_64_GB - Size_2_GB /*size*/, AddressSpaceInfo::Type::Small64Bit, },
{ 36 /*bit_width*/, Invalid /*addr*/, Size_6_GB /*size*/, AddressSpaceInfo::Type::Heap, },
{ 36 /*bit_width*/, Invalid /*addr*/, Size_6_GB /*size*/, AddressSpaceInfo::Type::Alias, },
{ 39 /*bit_width*/, Size_128_MB /*addr*/, Size_512_GB - Size_128_MB /*size*/, AddressSpaceInfo::Type::Large64Bit, },
{ 39 /*bit_width*/, Invalid /*addr*/, Size_64_GB /*size*/, AddressSpaceInfo::Type::Is32Bit },
{ 39 /*bit_width*/, Invalid /*addr*/, Size_6_GB /*size*/, AddressSpaceInfo::Type::Heap, },
{ 39 /*bit_width*/, Invalid /*addr*/, Size_64_GB /*size*/, AddressSpaceInfo::Type::Alias, },
{ 39 /*bit_width*/, Invalid /*addr*/, Size_2_GB /*size*/, AddressSpaceInfo::Type::Stack, },
{ .bit_width = 32, .address = Size_2_MB , .size = Size_1_GB - Size_2_MB , .type = AddressSpaceInfo::Type::Is32Bit, },
{ .bit_width = 32, .address = Size_1_GB , .size = Size_4_GB - Size_1_GB , .type = AddressSpaceInfo::Type::Small64Bit, },
{ .bit_width = 32, .address = Invalid , .size = Size_1_GB , .type = AddressSpaceInfo::Type::Heap, },
{ .bit_width = 32, .address = Invalid , .size = Size_1_GB , .type = AddressSpaceInfo::Type::Alias, },
{ .bit_width = 36, .address = Size_128_MB, .size = Size_2_GB - Size_128_MB, .type = AddressSpaceInfo::Type::Is32Bit, },
{ .bit_width = 36, .address = Size_2_GB , .size = Size_64_GB - Size_2_GB , .type = AddressSpaceInfo::Type::Small64Bit, },
{ .bit_width = 36, .address = Invalid , .size = Size_6_GB , .type = AddressSpaceInfo::Type::Heap, },
{ .bit_width = 36, .address = Invalid , .size = Size_6_GB , .type = AddressSpaceInfo::Type::Alias, },
{ .bit_width = 39, .address = Size_128_MB, .size = Size_512_GB - Size_128_MB, .type = AddressSpaceInfo::Type::Large64Bit, },
{ .bit_width = 39, .address = Invalid , .size = Size_64_GB , .type = AddressSpaceInfo::Type::Is32Bit },
{ .bit_width = 39, .address = Invalid , .size = Size_6_GB , .type = AddressSpaceInfo::Type::Heap, },
{ .bit_width = 39, .address = Invalid , .size = Size_64_GB , .type = AddressSpaceInfo::Type::Alias, },
{ .bit_width = 39, .address = Invalid , .size = Size_2_GB , .type = AddressSpaceInfo::Type::Stack, },
}};
// clang-format on
constexpr bool IsAllowedIndexForAddress(std::size_t index) {
return index < std::size(AddressSpaceInfos) && AddressSpaceInfos[index].GetAddress() != Invalid;
return index < AddressSpaceInfos.size() && AddressSpaceInfos[index].address != Invalid;
}
constexpr std::array<std::size_t, static_cast<std::size_t>(AddressSpaceInfo::Type::Count)>
AddressSpaceIndices32Bit{
0, 1, 0, 2, 0, 3,
};
using IndexArray = std::array<std::size_t, static_cast<std::size_t>(AddressSpaceInfo::Type::Count)>;
constexpr std::array<std::size_t, static_cast<std::size_t>(AddressSpaceInfo::Type::Count)>
AddressSpaceIndices36Bit{
4, 5, 4, 6, 4, 7,
};
constexpr IndexArray AddressSpaceIndices32Bit{
0, 1, 0, 2, 0, 3,
};
constexpr std::array<std::size_t, static_cast<std::size_t>(AddressSpaceInfo::Type::Count)>
AddressSpaceIndices39Bit{
9, 8, 8, 10, 12, 11,
};
constexpr IndexArray AddressSpaceIndices36Bit{
4, 5, 4, 6, 4, 7,
};
constexpr IndexArray AddressSpaceIndices39Bit{
9, 8, 8, 10, 12, 11,
};
constexpr bool IsAllowed32BitType(AddressSpaceInfo::Type type) {
return type < AddressSpaceInfo::Type::Count && type != AddressSpaceInfo::Type::Large64Bit &&
@@ -80,37 +79,37 @@ constexpr bool IsAllowed39BitType(AddressSpaceInfo::Type type) {
} // namespace
u64 AddressSpaceInfo::GetAddressSpaceStart(std::size_t width, AddressSpaceInfo::Type type) {
u64 AddressSpaceInfo::GetAddressSpaceStart(std::size_t width, Type type) {
const std::size_t index{static_cast<std::size_t>(type)};
switch (width) {
case 32:
ASSERT(IsAllowed32BitType(type));
ASSERT(IsAllowedIndexForAddress(AddressSpaceIndices32Bit[index]));
return AddressSpaceInfos[AddressSpaceIndices32Bit[index]].GetAddress();
return AddressSpaceInfos[AddressSpaceIndices32Bit[index]].address;
case 36:
ASSERT(IsAllowed36BitType(type));
ASSERT(IsAllowedIndexForAddress(AddressSpaceIndices36Bit[index]));
return AddressSpaceInfos[AddressSpaceIndices36Bit[index]].GetAddress();
return AddressSpaceInfos[AddressSpaceIndices36Bit[index]].address;
case 39:
ASSERT(IsAllowed39BitType(type));
ASSERT(IsAllowedIndexForAddress(AddressSpaceIndices39Bit[index]));
return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].GetAddress();
return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].address;
}
UNREACHABLE();
}
std::size_t AddressSpaceInfo::GetAddressSpaceSize(std::size_t width, AddressSpaceInfo::Type type) {
std::size_t AddressSpaceInfo::GetAddressSpaceSize(std::size_t width, Type type) {
const std::size_t index{static_cast<std::size_t>(type)};
switch (width) {
case 32:
ASSERT(IsAllowed32BitType(type));
return AddressSpaceInfos[AddressSpaceIndices32Bit[index]].GetSize();
return AddressSpaceInfos[AddressSpaceIndices32Bit[index]].size;
case 36:
ASSERT(IsAllowed36BitType(type));
return AddressSpaceInfos[AddressSpaceIndices36Bit[index]].GetSize();
return AddressSpaceInfos[AddressSpaceIndices36Bit[index]].size;
case 39:
ASSERT(IsAllowed39BitType(type));
return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].GetSize();
return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].size;
}
UNREACHABLE();
}

View File

@@ -11,8 +11,7 @@
namespace Kernel::Memory {
class AddressSpaceInfo final : NonCopyable {
public:
struct AddressSpaceInfo final {
enum class Type : u32 {
Is32Bit = 0,
Small64Bit = 1,
@@ -23,31 +22,13 @@ public:
Count,
};
private:
std::size_t bit_width{};
std::size_t addr{};
std::size_t size{};
Type type{};
public:
static u64 GetAddressSpaceStart(std::size_t width, Type type);
static std::size_t GetAddressSpaceSize(std::size_t width, Type type);
constexpr AddressSpaceInfo(std::size_t bit_width, std::size_t addr, std::size_t size, Type type)
: bit_width{bit_width}, addr{addr}, size{size}, type{type} {}
constexpr std::size_t GetWidth() const {
return bit_width;
}
constexpr std::size_t GetAddress() const {
return addr;
}
constexpr std::size_t GetSize() const {
return size;
}
constexpr Type GetType() const {
return type;
}
const std::size_t bit_width{};
const std::size_t address{};
const std::size_t size{};
const Type type{};
};
} // namespace Kernel::Memory

View File

@@ -66,8 +66,6 @@ private:
const MemoryRegion application;
const MemoryRegion applet;
const MemoryRegion system;
const PAddr start_address{};
};
} // namespace Kernel::Memory

View File

@@ -408,7 +408,7 @@ void Process::LoadModule(CodeSet code_set, VAddr base_addr) {
Process::Process(Core::System& system)
: SynchronizationObject{system.Kernel()}, page_table{std::make_unique<Memory::PageTable>(
system)},
address_arbiter{system}, mutex{system}, system{system} {}
handle_table{system.Kernel()}, address_arbiter{system}, mutex{system}, system{system} {}
Process::~Process() = default;

View File

@@ -382,12 +382,6 @@ private:
/// List of threads waiting for a condition variable
std::unordered_map<VAddr, std::list<std::shared_ptr<Thread>>> cond_var_threads;
/// System context
Core::System& system;
/// Name of this process
std::string name;
/// Address of the top of the main thread's stack
VAddr main_thread_stack_top{};
@@ -399,6 +393,12 @@ private:
/// Process total image size
std::size_t image_size{};
/// Name of this process
std::string name;
/// System context
Core::System& system;
};
} // namespace Kernel

View File

@@ -802,7 +802,7 @@ void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
void Scheduler::Initialize() {
std::string name = "Idle Thread Id:" + std::to_string(core_id);
std::function<void(void*)> init_func = system.GetCpuManager().GetIdleThreadStartFunc();
std::function<void(void*)> init_func = Core::CpuManager::GetIdleThreadStartFunc();
void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
ThreadType type = static_cast<ThreadType>(THREADTYPE_KERNEL | THREADTYPE_HLE | THREADTYPE_IDLE);
auto thread_res = Thread::Create(system, type, name, 0, 64, 0, static_cast<u32>(core_id), 0,

View File

@@ -458,9 +458,7 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr
return ERR_OUT_OF_RANGE;
}
auto* const thread = system.CurrentScheduler().GetCurrentThread();
auto& kernel = system.Kernel();
using ObjectPtr = Thread::ThreadSynchronizationObjects::value_type;
Thread::ThreadSynchronizationObjects objects(handle_count);
const auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
@@ -1750,9 +1748,9 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_
// Only process up to 'target' threads, unless 'target' is less equal 0, in which case process
// them all.
std::size_t last = waiting_threads.size();
if (target > 0)
if (target > 0) {
last = std::min(waiting_threads.size(), static_cast<std::size_t>(target));
auto& time_manager = kernel.TimeManager();
}
for (std::size_t index = 0; index < last; ++index) {
auto& thread = waiting_threads[index];
@@ -1763,7 +1761,6 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_
const std::size_t current_core = system.CurrentCoreIndex();
auto& monitor = system.Monitor();
auto& memory = system.Memory();
// Atomically read the value of the mutex.
u32 mutex_val = 0;

View File

@@ -19,7 +19,6 @@ Synchronization::Synchronization(Core::System& system) : system{system} {}
void Synchronization::SignalObject(SynchronizationObject& obj) const {
auto& kernel = system.Kernel();
SchedulerLock lock(kernel);
auto& time_manager = kernel.TimeManager();
if (obj.IsSignaled()) {
for (auto thread : obj.GetWaitingThreads()) {
if (thread->GetSchedulingStatus() == ThreadSchedStatus::Paused) {

View File

@@ -13,16 +13,8 @@
#include "common/logging/log.h"
#include "common/thread_queue_list.h"
#include "core/arm/arm_interface.h"
#ifdef ARCHITECTURE_x86_64
#include "core/arm/dynarmic/arm_dynarmic_32.h"
#include "core/arm/dynarmic/arm_dynarmic_64.h"
#endif
#include "core/arm/cpu_interrupt_handler.h"
#include "core/arm/exclusive_monitor.h"
#include "core/arm/unicorn/arm_unicorn.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/cpu_manager.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/errors.h"
@@ -36,6 +28,11 @@
#include "core/hle/result.h"
#include "core/memory.h"
#ifdef ARCHITECTURE_x86_64
#include "core/arm/dynarmic/arm_dynarmic_32.h"
#include "core/arm/dynarmic/arm_dynarmic_64.h"
#endif
namespace Kernel {
bool Thread::ShouldWait(const Thread* thread) const {
@@ -158,7 +155,7 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadTy
std::string name, VAddr entry_point, u32 priority,
u64 arg, s32 processor_id, VAddr stack_top,
Process* owner_process) {
std::function<void(void*)> init_func = system.GetCpuManager().GetGuestThreadStartFunc();
std::function<void(void*)> init_func = Core::CpuManager::GetGuestThreadStartFunc();
void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
return Create(system, type_flags, name, entry_point, priority, arg, processor_id, stack_top,
owner_process, std::move(init_func), init_func_parameter);
@@ -540,13 +537,4 @@ ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) {
return RESULT_SUCCESS;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Gets the current thread
*/
Thread* GetCurrentThread() {
return Core::System::GetInstance().CurrentScheduler().GetCurrentThread();
}
} // namespace Kernel

View File

@@ -583,8 +583,6 @@ private:
void SetCurrentPriority(u32 new_priority);
void AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core);
Common::SpinLock context_guard{};
ThreadContext32 context_32{};
ThreadContext64 context_64{};
@@ -680,9 +678,4 @@ private:
std::string name;
};
/**
* Gets the current thread
*/
Thread* GetCurrentThread();
} // namespace Kernel

View File

@@ -0,0 +1,483 @@
// Copyright 2020 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include <random>
#include "common/assert.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/mii/manager.h"
#include "core/hle/service/mii/raw_data.h"
#include "core/hle/service/mii/types.h"
namespace Service::Mii {
namespace {
constexpr ResultCode ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4};
constexpr std::size_t DefaultMiiCount{sizeof(RawData::DefaultMii) / sizeof(DefaultMii)};
constexpr MiiStoreData::Name DefaultMiiName{u'y', u'u', u'z', u'u'};
constexpr std::array<u8, 8> HairColorLookup{8, 1, 2, 3, 4, 5, 6, 7};
constexpr std::array<u8, 6> EyeColorLookup{8, 9, 10, 11, 12, 13};
constexpr std::array<u8, 5> MouthColorLookup{19, 20, 21, 22, 23};
constexpr std::array<u8, 7> GlassesColorLookup{8, 14, 15, 16, 17, 18, 0};
constexpr std::array<u8, 62> EyeRotateLookup{
{0x03, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x04,
0x04, 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x03, 0x04, 0x04,
0x04, 0x03, 0x03, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03,
0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x03, 0x04, 0x04}};
constexpr std::array<u8, 24> EyebrowRotateLookup{{0x06, 0x06, 0x05, 0x07, 0x06, 0x07, 0x06, 0x07,
0x04, 0x07, 0x06, 0x08, 0x05, 0x05, 0x06, 0x06,
0x07, 0x07, 0x06, 0x06, 0x05, 0x06, 0x07, 0x05}};
template <typename T, std::size_t SourceArraySize, std::size_t DestArraySize>
std::array<T, DestArraySize> ResizeArray(const std::array<T, SourceArraySize>& in) {
std::array<T, DestArraySize> out{};
std::memcpy(out.data(), in.data(), sizeof(T) * std::min(SourceArraySize, DestArraySize));
return out;
}
MiiInfo ConvertStoreDataToInfo(const MiiStoreData& data) {
MiiStoreBitFields bf;
std::memcpy(&bf, data.data.data.data(), sizeof(MiiStoreBitFields));
MiiInfo info{};
info.name = ResizeArray<char16_t, 10, 11>(data.data.name);
info.uuid = data.data.uuid;
info.font_region = static_cast<u8>(bf.font_region.Value());
info.favorite_color = static_cast<u8>(bf.favorite_color.Value());
info.gender = static_cast<u8>(bf.gender.Value());
info.height = static_cast<u8>(bf.height.Value());
info.build = static_cast<u8>(bf.build.Value());
info.type = static_cast<u8>(bf.type.Value());
info.region_move = static_cast<u8>(bf.region_move.Value());
info.faceline_type = static_cast<u8>(bf.faceline_type.Value());
info.faceline_color = static_cast<u8>(bf.faceline_color.Value());
info.faceline_wrinkle = static_cast<u8>(bf.faceline_wrinkle.Value());
info.faceline_make = static_cast<u8>(bf.faceline_makeup.Value());
info.hair_type = static_cast<u8>(bf.hair_type.Value());
info.hair_color = static_cast<u8>(bf.hair_color.Value());
info.hair_flip = static_cast<u8>(bf.hair_flip.Value());
info.eye_type = static_cast<u8>(bf.eye_type.Value());
info.eye_color = static_cast<u8>(bf.eye_color.Value());
info.eye_scale = static_cast<u8>(bf.eye_scale.Value());
info.eye_aspect = static_cast<u8>(bf.eye_aspect.Value());
info.eye_rotate = static_cast<u8>(bf.eye_rotate.Value());
info.eye_x = static_cast<u8>(bf.eye_x.Value());
info.eye_y = static_cast<u8>(bf.eye_y.Value());
info.eyebrow_type = static_cast<u8>(bf.eyebrow_type.Value());
info.eyebrow_color = static_cast<u8>(bf.eyebrow_color.Value());
info.eyebrow_scale = static_cast<u8>(bf.eyebrow_scale.Value());
info.eyebrow_aspect = static_cast<u8>(bf.eyebrow_aspect.Value());
info.eyebrow_rotate = static_cast<u8>(bf.eyebrow_rotate.Value());
info.eyebrow_x = static_cast<u8>(bf.eyebrow_x.Value());
info.eyebrow_y = static_cast<u8>(bf.eyebrow_y.Value() + 3);
info.nose_type = static_cast<u8>(bf.nose_type.Value());
info.nose_scale = static_cast<u8>(bf.nose_scale.Value());
info.nose_y = static_cast<u8>(bf.nose_y.Value());
info.mouth_type = static_cast<u8>(bf.mouth_type.Value());
info.mouth_color = static_cast<u8>(bf.mouth_color.Value());
info.mouth_scale = static_cast<u8>(bf.mouth_scale.Value());
info.mouth_aspect = static_cast<u8>(bf.mouth_aspect.Value());
info.mouth_y = static_cast<u8>(bf.mouth_y.Value());
info.beard_color = static_cast<u8>(bf.beard_color.Value());
info.beard_type = static_cast<u8>(bf.beard_type.Value());
info.mustache_type = static_cast<u8>(bf.mustache_type.Value());
info.mustache_scale = static_cast<u8>(bf.mustache_scale.Value());
info.mustache_y = static_cast<u8>(bf.mustache_y.Value());
info.glasses_type = static_cast<u8>(bf.glasses_type.Value());
info.glasses_color = static_cast<u8>(bf.glasses_color.Value());
info.glasses_scale = static_cast<u8>(bf.glasses_scale.Value());
info.glasses_y = static_cast<u8>(bf.glasses_y.Value());
info.mole_type = static_cast<u8>(bf.mole_type.Value());
info.mole_scale = static_cast<u8>(bf.mole_scale.Value());
info.mole_x = static_cast<u8>(bf.mole_x.Value());
info.mole_y = static_cast<u8>(bf.mole_y.Value());
return info;
}
u16 GenerateCrc16(const void* data, std::size_t size) {
s32 crc{};
for (int i = 0; i < size; i++) {
crc ^= reinterpret_cast<const u8*>(data)[i] << 8;
for (int j = 0; j < 8; j++) {
crc <<= 1;
if ((crc & 0x10000) != 0) {
crc = (crc ^ 0x1021) & 0xFFFF;
}
}
}
return Common::swap16(static_cast<u16>(crc));
}
Common::UUID GenerateValidUUID() {
auto uuid{Common::UUID::Generate()};
// Bit 7 must be set, and bit 6 unset for the UUID to be valid
uuid.uuid[1] &= 0xFFFFFFFFFFFFFF3FULL;
uuid.uuid[1] |= 0x0000000000000080ULL;
return uuid;
}
template <typename T>
T GetRandomValue(T min, T max) {
std::random_device device;
std::mt19937 gen(device());
std::uniform_int_distribution<u64> distribution(0, static_cast<u64>(max));
return static_cast<T>(distribution(gen));
}
template <typename T>
T GetRandomValue(T max) {
return GetRandomValue<T>({}, max);
}
template <typename T>
T GetArrayValue(const u8* data, std::size_t index) {
T result{};
std::memcpy(&result, &data[index * sizeof(T)], sizeof(T));
return result;
}
MiiStoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Common::UUID& user_id) {
MiiStoreBitFields bf{};
if (gender == Gender::All) {
gender = GetRandomValue<Gender>(Gender::Maximum);
}
bf.gender.Assign(gender);
bf.favorite_color.Assign(GetRandomValue<u8>(11));
bf.region_move.Assign(0);
bf.font_region.Assign(FontRegion::Standard);
bf.type.Assign(0);
bf.height.Assign(64);
bf.build.Assign(64);
if (age == Age::All) {
const auto temp{GetRandomValue<int>(10)};
if (temp >= 8) {
age = Age::Old;
} else if (temp >= 4) {
age = Age::Normal;
} else {
age = Age::Young;
}
}
if (race == Race::All) {
const auto temp{GetRandomValue<int>(10)};
if (temp >= 8) {
race = Race::Black;
} else if (temp >= 4) {
race = Race::White;
} else {
race = Race::Asian;
}
}
u32 axis_y{};
if (gender == Gender::Female && age == Age::Young) {
axis_y = GetRandomValue<u32>(3);
}
const std::size_t index{3 * static_cast<std::size_t>(age) +
9 * static_cast<std::size_t>(gender) + static_cast<std::size_t>(race)};
const auto faceline_type_info{
GetArrayValue<RandomMiiData4>(&RawData::RandomMiiFaceline[0], index)};
const auto faceline_color_info{GetArrayValue<RandomMiiData3>(
RawData::RandomMiiFacelineColor.data(),
3 * static_cast<std::size_t>(gender) + static_cast<std::size_t>(race))};
const auto faceline_wrinkle_info{
GetArrayValue<RandomMiiData4>(RawData::RandomMiiFacelineWrinkle.data(), index)};
const auto faceline_makeup_info{
GetArrayValue<RandomMiiData4>(RawData::RandomMiiFacelineMakeup.data(), index)};
const auto hair_type_info{
GetArrayValue<RandomMiiData4>(RawData::RandomMiiHairType.data(), index)};
const auto hair_color_info{GetArrayValue<RandomMiiData3>(RawData::RandomMiiHairColor.data(),
3 * static_cast<std::size_t>(race) +
static_cast<std::size_t>(age))};
const auto eye_type_info{
GetArrayValue<RandomMiiData4>(RawData::RandomMiiEyeType.data(), index)};
const auto eye_color_info{GetArrayValue<RandomMiiData2>(RawData::RandomMiiEyeColor.data(),
static_cast<std::size_t>(race))};
const auto eyebrow_type_info{
GetArrayValue<RandomMiiData4>(RawData::RandomMiiEyebrowType.data(), index)};
const auto nose_type_info{
GetArrayValue<RandomMiiData4>(RawData::RandomMiiNoseType.data(), index)};
const auto mouth_type_info{
GetArrayValue<RandomMiiData4>(RawData::RandomMiiMouthType.data(), index)};
const auto glasses_type_info{GetArrayValue<RandomMiiData2>(RawData::RandomMiiGlassType.data(),
static_cast<std::size_t>(age))};
bf.faceline_type.Assign(
faceline_type_info.values[GetRandomValue<std::size_t>(faceline_type_info.values_count)]);
bf.faceline_color.Assign(
faceline_color_info.values[GetRandomValue<std::size_t>(faceline_color_info.values_count)]);
bf.faceline_wrinkle.Assign(
faceline_wrinkle_info
.values[GetRandomValue<std::size_t>(faceline_wrinkle_info.values_count)]);
bf.faceline_makeup.Assign(
faceline_makeup_info
.values[GetRandomValue<std::size_t>(faceline_makeup_info.values_count)]);
bf.hair_type.Assign(
hair_type_info.values[GetRandomValue<std::size_t>(hair_type_info.values_count)]);
bf.hair_color.Assign(
HairColorLookup[hair_color_info
.values[GetRandomValue<std::size_t>(hair_color_info.values_count)]]);
bf.hair_flip.Assign(GetRandomValue<HairFlip>(HairFlip::Maximum));
bf.eye_type.Assign(
eye_type_info.values[GetRandomValue<std::size_t>(eye_type_info.values_count)]);
const auto eye_rotate_1{gender != Gender::Male ? 4 : 2};
const auto eye_rotate_2{gender != Gender::Male ? 3 : 4};
const auto eye_rotate_offset{32 - EyeRotateLookup[eye_rotate_1] + eye_rotate_2};
const auto eye_rotate{32 - EyeRotateLookup[bf.eye_type]};
bf.eye_color.Assign(
EyeColorLookup[eye_color_info
.values[GetRandomValue<std::size_t>(eye_color_info.values_count)]]);
bf.eye_scale.Assign(4);
bf.eye_aspect.Assign(3);
bf.eye_rotate.Assign(eye_rotate_offset - eye_rotate);
bf.eye_x.Assign(2);
bf.eye_y.Assign(axis_y + 12);
bf.eyebrow_type.Assign(
eyebrow_type_info.values[GetRandomValue<std::size_t>(eyebrow_type_info.values_count)]);
const auto eyebrow_rotate_1{race == Race::Asian ? 6 : 0};
const auto eyebrow_y{race == Race::Asian ? 9 : 10};
const auto eyebrow_rotate_offset{32 - EyebrowRotateLookup[eyebrow_rotate_1] + 6};
const auto eyebrow_rotate{
32 - EyebrowRotateLookup[static_cast<std::size_t>(bf.eyebrow_type.Value())]};
bf.eyebrow_color.Assign(bf.hair_color);
bf.eyebrow_scale.Assign(4);
bf.eyebrow_aspect.Assign(3);
bf.eyebrow_rotate.Assign(eyebrow_rotate_offset - eyebrow_rotate);
bf.eyebrow_x.Assign(2);
bf.eyebrow_y.Assign(axis_y + eyebrow_y);
const auto nose_scale{gender == Gender::Female ? 3 : 4};
bf.nose_type.Assign(
nose_type_info.values[GetRandomValue<std::size_t>(nose_type_info.values_count)]);
bf.nose_scale.Assign(nose_scale);
bf.nose_y.Assign(axis_y + 9);
const auto mouth_color{gender == Gender::Female ? GetRandomValue<int>(4) : 0};
bf.mouth_type.Assign(
mouth_type_info.values[GetRandomValue<std::size_t>(mouth_type_info.values_count)]);
bf.mouth_color.Assign(MouthColorLookup[mouth_color]);
bf.mouth_scale.Assign(4);
bf.mouth_aspect.Assign(3);
bf.mouth_y.Assign(axis_y + 13);
bf.beard_color.Assign(bf.hair_color);
bf.mustache_scale.Assign(4);
if (gender == Gender::Male && age != Age::Young && GetRandomValue<int>(10) < 2) {
const auto mustache_and_beard_flag{
GetRandomValue<BeardAndMustacheFlag>(BeardAndMustacheFlag::All)};
auto beard_type{BeardType::None};
auto mustache_type{MustacheType::None};
if ((mustache_and_beard_flag & BeardAndMustacheFlag::Beard) ==
BeardAndMustacheFlag::Beard) {
beard_type = GetRandomValue<BeardType>(BeardType::Beard1, BeardType::Beard5);
}
if ((mustache_and_beard_flag & BeardAndMustacheFlag::Mustache) ==
BeardAndMustacheFlag::Mustache) {
mustache_type =
GetRandomValue<MustacheType>(MustacheType::Mustache1, MustacheType::Mustache5);
}
bf.mustache_type.Assign(mustache_type);
bf.beard_type.Assign(beard_type);
bf.mustache_y.Assign(10);
} else {
bf.mustache_type.Assign(MustacheType::None);
bf.beard_type.Assign(BeardType::None);
bf.mustache_y.Assign(axis_y + 10);
}
const auto glasses_type_start{GetRandomValue<std::size_t>(100)};
u8 glasses_type{};
while (glasses_type_start < glasses_type_info.values[glasses_type]) {
if (++glasses_type >= glasses_type_info.values_count) {
UNREACHABLE();
break;
}
}
bf.glasses_type.Assign(glasses_type);
bf.glasses_color.Assign(GlassesColorLookup[0]);
bf.glasses_scale.Assign(4);
bf.glasses_y.Assign(axis_y + 10);
bf.mole_type.Assign(0);
bf.mole_scale.Assign(4);
bf.mole_x.Assign(2);
bf.mole_y.Assign(20);
return {DefaultMiiName, bf, user_id};
}
MiiStoreData BuildDefaultStoreData(const DefaultMii& info, const Common::UUID& user_id) {
MiiStoreBitFields bf{};
bf.font_region.Assign(info.font_region);
bf.favorite_color.Assign(info.favorite_color);
bf.gender.Assign(info.gender);
bf.height.Assign(info.height);
bf.build.Assign(info.weight);
bf.type.Assign(info.type);
bf.region_move.Assign(info.region);
bf.faceline_type.Assign(info.face_type);
bf.faceline_color.Assign(info.face_color);
bf.faceline_wrinkle.Assign(info.face_wrinkle);
bf.faceline_makeup.Assign(info.face_makeup);
bf.hair_type.Assign(info.hair_type);
bf.hair_color.Assign(HairColorLookup[info.hair_color]);
bf.hair_flip.Assign(static_cast<HairFlip>(info.hair_flip));
bf.eye_type.Assign(info.eye_type);
bf.eye_color.Assign(EyeColorLookup[info.eye_color]);
bf.eye_scale.Assign(info.eye_scale);
bf.eye_aspect.Assign(info.eye_aspect);
bf.eye_rotate.Assign(info.eye_rotate);
bf.eye_x.Assign(info.eye_x);
bf.eye_y.Assign(info.eye_y);
bf.eyebrow_type.Assign(info.eyebrow_type);
bf.eyebrow_color.Assign(HairColorLookup[info.eyebrow_color]);
bf.eyebrow_scale.Assign(info.eyebrow_scale);
bf.eyebrow_aspect.Assign(info.eyebrow_aspect);
bf.eyebrow_rotate.Assign(info.eyebrow_rotate);
bf.eyebrow_x.Assign(info.eyebrow_x);
bf.eyebrow_y.Assign(info.eyebrow_y - 3);
bf.nose_type.Assign(info.nose_type);
bf.nose_scale.Assign(info.nose_scale);
bf.nose_y.Assign(info.nose_y);
bf.mouth_type.Assign(info.mouth_type);
bf.mouth_color.Assign(MouthColorLookup[info.mouth_color]);
bf.mouth_scale.Assign(info.mouth_scale);
bf.mouth_aspect.Assign(info.mouth_aspect);
bf.mouth_y.Assign(info.mouth_y);
bf.beard_color.Assign(HairColorLookup[info.beard_color]);
bf.beard_type.Assign(static_cast<BeardType>(info.beard_type));
bf.mustache_type.Assign(static_cast<MustacheType>(info.mustache_type));
bf.mustache_scale.Assign(info.mustache_scale);
bf.mustache_y.Assign(info.mustache_y);
bf.glasses_type.Assign(info.glasses_type);
bf.glasses_color.Assign(GlassesColorLookup[info.glasses_color]);
bf.glasses_scale.Assign(info.glasses_scale);
bf.glasses_y.Assign(info.glasses_y);
bf.mole_type.Assign(info.mole_type);
bf.mole_scale.Assign(info.mole_scale);
bf.mole_x.Assign(info.mole_x);
bf.mole_y.Assign(info.mole_y);
return {DefaultMiiName, bf, user_id};
}
} // namespace
MiiStoreData::MiiStoreData() = default;
MiiStoreData::MiiStoreData(const MiiStoreData::Name& name, const MiiStoreBitFields& bit_fields,
const Common::UUID& user_id) {
data.name = name;
data.uuid = GenerateValidUUID();
std::memcpy(data.data.data(), &bit_fields, sizeof(MiiStoreBitFields));
data_crc = GenerateCrc16(data.data.data(), sizeof(data));
device_crc = GenerateCrc16(&user_id, sizeof(Common::UUID));
}
MiiManager::MiiManager() : user_id{Service::Account::ProfileManager().GetLastOpenedUser()} {}
bool MiiManager::CheckAndResetUpdateCounter(SourceFlag source_flag, u64& current_update_counter) {
if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
return false;
}
const bool result{current_update_counter != update_counter};
current_update_counter = update_counter;
return result;
}
bool MiiManager::IsFullDatabase() const {
// TODO(bunnei): We don't implement the Mii database, so it cannot be full
return false;
}
u32 MiiManager::GetCount(SourceFlag source_flag) const {
u32 count{};
if ((source_flag & SourceFlag::Database) != SourceFlag::None) {
// TODO(bunnei): We don't implement the Mii database, but when we do, update this
count += 0;
}
if ((source_flag & SourceFlag::Default) != SourceFlag::None) {
count += DefaultMiiCount;
}
return count;
}
ResultVal<MiiInfo> MiiManager::UpdateLatest([[maybe_unused]] const MiiInfo& info,
SourceFlag source_flag) {
if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
return ERROR_CANNOT_FIND_ENTRY;
}
// TODO(bunnei): We don't implement the Mii database, so we can't have an entry
return ERROR_CANNOT_FIND_ENTRY;
}
MiiInfo MiiManager::BuildRandom(Age age, Gender gender, Race race) {
return ConvertStoreDataToInfo(BuildRandomStoreData(age, gender, race, user_id));
}
MiiInfo MiiManager::BuildDefault(std::size_t index) {
return ConvertStoreDataToInfo(BuildDefaultStoreData(
GetArrayValue<DefaultMii>(RawData::DefaultMii.data(), index), user_id));
}
ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_flag) {
std::vector<MiiInfoElement> result;
if ((source_flag & SourceFlag::Default) == SourceFlag::None) {
return MakeResult(std::move(result));
}
for (std::size_t index = 0; index < DefaultMiiCount; index++) {
result.emplace_back(BuildDefault(index), Source::Default);
}
return MakeResult(std::move(result));
}
ResultCode MiiManager::GetIndex([[maybe_unused]] const MiiInfo& info, u32& index) {
constexpr u32 INVALID_INDEX{0xFFFFFFFF};
index = INVALID_INDEX;
// TODO(bunnei): We don't implement the Mii database, so we can't have an index
return ERROR_CANNOT_FIND_ENTRY;
}
} // namespace Service::Mii

View File

@@ -0,0 +1,331 @@
// Copyright 2020 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/uuid.h"
#include "core/hle/result.h"
#include "core/hle/service/mii/types.h"
namespace Service::Mii {
enum class Source : u32 {
Database = 0,
Default = 1,
Account = 2,
Friend = 3,
};
enum class SourceFlag : u32 {
None = 0,
Database = 1 << 0,
Default = 1 << 1,
};
DECLARE_ENUM_FLAG_OPERATORS(SourceFlag);
struct MiiInfo {
Common::UUID uuid{Common::INVALID_UUID};
std::array<char16_t, 11> name{};
u8 font_region{};
u8 favorite_color{};
u8 gender{};
u8 height{};
u8 build{};
u8 type{};
u8 region_move{};
u8 faceline_type{};
u8 faceline_color{};
u8 faceline_wrinkle{};
u8 faceline_make{};
u8 hair_type{};
u8 hair_color{};
u8 hair_flip{};
u8 eye_type{};
u8 eye_color{};
u8 eye_scale{};
u8 eye_aspect{};
u8 eye_rotate{};
u8 eye_x{};
u8 eye_y{};
u8 eyebrow_type{};
u8 eyebrow_color{};
u8 eyebrow_scale{};
u8 eyebrow_aspect{};
u8 eyebrow_rotate{};
u8 eyebrow_x{};
u8 eyebrow_y{};
u8 nose_type{};
u8 nose_scale{};
u8 nose_y{};
u8 mouth_type{};
u8 mouth_color{};
u8 mouth_scale{};
u8 mouth_aspect{};
u8 mouth_y{};
u8 beard_color{};
u8 beard_type{};
u8 mustache_type{};
u8 mustache_scale{};
u8 mustache_y{};
u8 glasses_type{};
u8 glasses_color{};
u8 glasses_scale{};
u8 glasses_y{};
u8 mole_type{};
u8 mole_scale{};
u8 mole_x{};
u8 mole_y{};
INSERT_PADDING_BYTES(1);
std::u16string Name() const;
};
static_assert(sizeof(MiiInfo) == 0x58, "MiiInfo has incorrect size.");
static_assert(std::has_unique_object_representations_v<MiiInfo>,
"All bits of MiiInfo must contribute to its value.");
#pragma pack(push, 4)
struct MiiInfoElement {
MiiInfoElement(const MiiInfo& info, Source source) : info{info}, source{source} {}
MiiInfo info{};
Source source{};
};
static_assert(sizeof(MiiInfoElement) == 0x5c, "MiiInfoElement has incorrect size.");
struct MiiStoreBitFields {
union {
u32 word_0{};
BitField<0, 8, u32> hair_type;
BitField<8, 7, u32> height;
BitField<15, 1, u32> mole_type;
BitField<16, 7, u32> build;
BitField<23, 1, HairFlip> hair_flip;
BitField<24, 7, u32> hair_color;
BitField<31, 1, u32> type;
};
union {
u32 word_1{};
BitField<0, 7, u32> eye_color;
BitField<7, 1, Gender> gender;
BitField<8, 7, u32> eyebrow_color;
BitField<16, 7, u32> mouth_color;
BitField<24, 7, u32> beard_color;
};
union {
u32 word_2{};
BitField<0, 7, u32> glasses_color;
BitField<8, 6, u32> eye_type;
BitField<14, 2, u32> region_move;
BitField<16, 6, u32> mouth_type;
BitField<22, 2, FontRegion> font_region;
BitField<24, 5, u32> eye_y;
BitField<29, 3, u32> glasses_scale;
};
union {
u32 word_3{};
BitField<0, 5, u32> eyebrow_type;
BitField<5, 3, MustacheType> mustache_type;
BitField<8, 5, u32> nose_type;
BitField<13, 3, BeardType> beard_type;
BitField<16, 5, u32> nose_y;
BitField<21, 3, u32> mouth_aspect;
BitField<24, 5, u32> mouth_y;
BitField<29, 3, u32> eyebrow_aspect;
};
union {
u32 word_4{};
BitField<0, 5, u32> mustache_y;
BitField<5, 3, u32> eye_rotate;
BitField<8, 5, u32> glasses_y;
BitField<13, 3, u32> eye_aspect;
BitField<16, 5, u32> mole_x;
BitField<21, 3, u32> eye_scale;
BitField<24, 5, u32> mole_y;
};
union {
u32 word_5{};
BitField<0, 5, u32> glasses_type;
BitField<8, 4, u32> favorite_color;
BitField<12, 4, u32> faceline_type;
BitField<16, 4, u32> faceline_color;
BitField<20, 4, u32> faceline_wrinkle;
BitField<24, 4, u32> faceline_makeup;
BitField<28, 4, u32> eye_x;
};
union {
u32 word_6{};
BitField<0, 4, u32> eyebrow_scale;
BitField<4, 4, u32> eyebrow_rotate;
BitField<8, 4, u32> eyebrow_x;
BitField<12, 4, u32> eyebrow_y;
BitField<16, 4, u32> nose_scale;
BitField<20, 4, u32> mouth_scale;
BitField<24, 4, u32> mustache_scale;
BitField<28, 4, u32> mole_scale;
};
};
static_assert(sizeof(MiiStoreBitFields) == 0x1c, "MiiStoreBitFields has incorrect size.");
static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>,
"MiiStoreBitFields is not trivially copyable.");
struct MiiStoreData {
using Name = std::array<char16_t, 10>;
MiiStoreData();
MiiStoreData(const Name& name, const MiiStoreBitFields& bit_fields,
const Common::UUID& user_id);
// This corresponds to the above structure MiiStoreBitFields. I did it like this because the
// BitField<> type makes this (and any thing that contains it) not trivially copyable, which is
// not suitable for our uses.
struct {
std::array<u8, 0x1C> data{};
static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size.");
Name name{};
Common::UUID uuid{Common::INVALID_UUID};
} data;
u16 data_crc{};
u16 device_crc{};
};
static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size.");
struct MiiStoreDataElement {
MiiStoreData data{};
Source source{};
};
static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size.");
struct MiiDatabase {
u32 magic{}; // 'NFDB'
std::array<MiiStoreData, 0x64> miis{};
INSERT_PADDING_BYTES(1);
u8 count{};
u16 crc{};
};
static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size.");
struct RandomMiiValues {
std::array<u8, 0xbc> values{};
};
static_assert(sizeof(RandomMiiValues) == 0xbc, "RandomMiiValues has incorrect size.");
struct RandomMiiData4 {
Gender gender{};
Age age{};
Race race{};
u32 values_count{};
std::array<u8, 0xbc> values{};
};
static_assert(sizeof(RandomMiiData4) == 0xcc, "RandomMiiData4 has incorrect size.");
struct RandomMiiData3 {
u32 arg_1;
u32 arg_2;
u32 values_count;
std::array<u8, 0xbc> values{};
};
static_assert(sizeof(RandomMiiData3) == 0xc8, "RandomMiiData3 has incorrect size.");
struct RandomMiiData2 {
u32 arg_1;
u32 values_count;
std::array<u8, 0xbc> values{};
};
static_assert(sizeof(RandomMiiData2) == 0xc4, "RandomMiiData2 has incorrect size.");
struct DefaultMii {
u32 face_type{};
u32 face_color{};
u32 face_wrinkle{};
u32 face_makeup{};
u32 hair_type{};
u32 hair_color{};
u32 hair_flip{};
u32 eye_type{};
u32 eye_color{};
u32 eye_scale{};
u32 eye_aspect{};
u32 eye_rotate{};
u32 eye_x{};
u32 eye_y{};
u32 eyebrow_type{};
u32 eyebrow_color{};
u32 eyebrow_scale{};
u32 eyebrow_aspect{};
u32 eyebrow_rotate{};
u32 eyebrow_x{};
u32 eyebrow_y{};
u32 nose_type{};
u32 nose_scale{};
u32 nose_y{};
u32 mouth_type{};
u32 mouth_color{};
u32 mouth_scale{};
u32 mouth_aspect{};
u32 mouth_y{};
u32 mustache_type{};
u32 beard_type{};
u32 beard_color{};
u32 mustache_scale{};
u32 mustache_y{};
u32 glasses_type{};
u32 glasses_color{};
u32 glasses_scale{};
u32 glasses_y{};
u32 mole_type{};
u32 mole_scale{};
u32 mole_x{};
u32 mole_y{};
u32 height{};
u32 weight{};
Gender gender{};
u32 favorite_color{};
u32 region{};
FontRegion font_region{};
u32 type{};
INSERT_PADDING_WORDS(5);
};
static_assert(sizeof(DefaultMii) == 0xd8, "MiiStoreData has incorrect size.");
#pragma pack(pop)
// The Mii manager is responsible for loading and storing the Miis to the database in NAND along
// with providing an easy interface for HLE emulation of the mii service.
class MiiManager {
public:
MiiManager();
bool CheckAndResetUpdateCounter(SourceFlag source_flag, u64& current_update_counter);
bool IsFullDatabase() const;
u32 GetCount(SourceFlag source_flag) const;
ResultVal<MiiInfo> UpdateLatest(const MiiInfo& info, SourceFlag source_flag);
MiiInfo BuildRandom(Age age, Gender gender, Race race);
MiiInfo BuildDefault(std::size_t index);
ResultVal<std::vector<MiiInfoElement>> GetDefault(SourceFlag source_flag);
ResultCode GetIndex(const MiiInfo& info, u32& index);
private:
const Common::UUID user_id;
u64 update_counter{};
};
}; // namespace Service::Mii

View File

@@ -4,22 +4,17 @@
#include <memory>
#include <fmt/ostream.h>
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/mii/manager.h"
#include "core/hle/service/mii/mii.h"
#include "core/hle/service/mii/mii_manager.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
namespace Service::Mii {
constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::Mii, 1};
constexpr ResultCode ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4};
constexpr ResultCode ERROR_NOT_IN_TEST_MODE{ErrorModule::Mii, 99};
class IDatabaseService final : public ServiceFramework<IDatabaseService> {
public:
@@ -31,19 +26,19 @@ public:
{2, &IDatabaseService::GetCount, "GetCount"},
{3, &IDatabaseService::Get, "Get"},
{4, &IDatabaseService::Get1, "Get1"},
{5, nullptr, "UpdateLatest"},
{5, &IDatabaseService::UpdateLatest, "UpdateLatest"},
{6, &IDatabaseService::BuildRandom, "BuildRandom"},
{7, &IDatabaseService::BuildDefault, "BuildDefault"},
{8, &IDatabaseService::Get2, "Get2"},
{9, &IDatabaseService::Get3, "Get3"},
{8, nullptr, "Get2"},
{9, nullptr, "Get3"},
{10, nullptr, "UpdateLatest1"},
{11, &IDatabaseService::FindIndex, "FindIndex"},
{12, &IDatabaseService::Move, "Move"},
{13, &IDatabaseService::AddOrReplace, "AddOrReplace"},
{14, &IDatabaseService::Delete, "Delete"},
{15, &IDatabaseService::DestroyFile, "DestroyFile"},
{16, &IDatabaseService::DeleteFile, "DeleteFile"},
{17, &IDatabaseService::Format, "Format"},
{11, nullptr, "FindIndex"},
{12, nullptr, "Move"},
{13, nullptr, "AddOrReplace"},
{14, nullptr, "Delete"},
{15, nullptr, "DestroyFile"},
{16, nullptr, "DeleteFile"},
{17, nullptr, "Format"},
{18, nullptr, "Import"},
{19, nullptr, "Export"},
{20, nullptr, "IsBrokenDatabaseWithClearFlag"},
@@ -59,31 +54,26 @@ public:
}
private:
template <typename OutType>
std::vector<u8> SerializeArray(OutType (MiiManager::*getter)(u32) const, u32 offset,
u32 requested_size, u32& read_size) {
read_size = std::min(requested_size, db.Size() - offset);
std::vector<u8> out(read_size * sizeof(OutType));
for (u32 i = 0; i < read_size; ++i) {
const auto obj = (db.*getter)(offset + i);
std::memcpy(out.data() + i * sizeof(OutType), &obj, sizeof(OutType));
template <typename T>
std::vector<u8> SerializeArray(const std::vector<T>& values) {
std::vector<u8> out(values.size() * sizeof(T));
std::size_t offset{};
for (const auto& value : values) {
std::memcpy(out.data() + offset, &value, sizeof(T));
offset += sizeof(T);
}
return out;
}
void IsUpdated(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto source{rp.PopRaw<Source>()};
const auto source_flag{rp.PopRaw<SourceFlag>()};
LOG_DEBUG(Service_Mii, "called with source={}", source);
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(db.CheckUpdatedFlag());
db.ResetUpdatedFlag();
rb.Push(manager.CheckAndResetUpdateCounter(source_flag, current_update_counter));
}
void IsFullDatabase(Kernel::HLERequestContext& ctx) {
@@ -91,93 +81,126 @@ private:
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(db.Full());
rb.Push(manager.IsFullDatabase());
}
void GetCount(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto source{rp.PopRaw<Source>()};
const auto source_flag{rp.PopRaw<SourceFlag>()};
LOG_DEBUG(Service_Mii, "called with source={}", source);
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(db.Size());
rb.Push<u32>(manager.GetCount(source_flag));
}
// Gets Miis from database at offset and index in format MiiInfoElement
void Get(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto size{rp.PopRaw<u32>()};
const auto source{rp.PopRaw<Source>()};
const auto source_flag{rp.PopRaw<SourceFlag>()};
LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size,
offsets[0], source);
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
u32 read_size{};
ctx.WriteBuffer(SerializeArray(&MiiManager::GetInfoElement, offsets[0], size, read_size));
offsets[0] += read_size;
const auto result{manager.GetDefault(source_flag)};
if (result.Failed()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result.Code());
return;
}
if (result->size() > 0) {
ctx.WriteBuffer(SerializeArray(*result));
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(read_size);
rb.Push<u32>(static_cast<u32>(result->size()));
}
// Gets Miis from database at offset and index in format MiiInfo
void Get1(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto size{rp.PopRaw<u32>()};
const auto source{rp.PopRaw<Source>()};
const auto source_flag{rp.PopRaw<SourceFlag>()};
LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size,
offsets[1], source);
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
u32 read_size{};
ctx.WriteBuffer(SerializeArray(&MiiManager::GetInfo, offsets[1], size, read_size));
offsets[1] += read_size;
const auto result{manager.GetDefault(source_flag)};
if (result.Failed()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result.Code());
return;
}
std::vector<MiiInfo> values;
for (const auto& element : *result) {
values.emplace_back(element.info);
}
ctx.WriteBuffer(SerializeArray(values));
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(read_size);
rb.Push<u32>(static_cast<u32>(result->size()));
}
void UpdateLatest(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto info{rp.PopRaw<MiiInfo>()};
const auto source_flag{rp.PopRaw<SourceFlag>()};
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
const auto result{manager.UpdateLatest(info, source_flag)};
if (result.Failed()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result.Code());
return;
}
IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<MiiInfo>(*result);
}
void BuildRandom(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto [unknown1, unknown2, unknown3] = rp.PopRaw<RandomParameters>();
if (unknown1 > 3) {
const auto age{rp.PopRaw<Age>()};
const auto gender{rp.PopRaw<Gender>()};
const auto race{rp.PopRaw<Race>()};
LOG_DEBUG(Service_Mii, "called with age={}, gender={}, race={}", age, gender, race);
if (age > Age::All) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ARGUMENT);
LOG_ERROR(Service_Mii, "Invalid unknown1 value: {}", unknown1);
LOG_ERROR(Service_Mii, "invalid age={}", age);
return;
}
if (unknown2 > 2) {
if (gender > Gender::All) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ARGUMENT);
LOG_ERROR(Service_Mii, "Invalid unknown2 value: {}", unknown2);
LOG_ERROR(Service_Mii, "invalid gender={}", gender);
return;
}
if (unknown3 > 3) {
if (race > Race::All) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ARGUMENT);
LOG_ERROR(Service_Mii, "Invalid unknown3 value: {}", unknown3);
LOG_ERROR(Service_Mii, "invalid race={}", race);
return;
}
LOG_DEBUG(Service_Mii, "called with param_1={:08X}, param_2={:08X}, param_3={:08X}",
unknown1, unknown2, unknown3);
const auto info = db.CreateRandom({unknown1, unknown2, unknown3});
IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<MiiInfo>(info);
rb.PushRaw<MiiInfo>(manager.BuildRandom(age, gender, race));
}
void BuildDefault(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto index{rp.PopRaw<u32>()};
const auto index{rp.Pop<u32>()};
LOG_DEBUG(Service_Mii, "called with index={}", index);
if (index > 5) {
LOG_ERROR(Service_Mii, "invalid argument, index cannot be greater than 5 but is {:08X}",
@@ -187,168 +210,20 @@ private:
return;
}
LOG_DEBUG(Service_Mii, "called with index={:08X}", index);
const auto info = db.CreateDefault(index);
IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<MiiInfo>(info);
}
// Gets Miis from database at offset and index in format MiiStoreDataElement
void Get2(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto size{rp.PopRaw<u32>()};
const auto source{rp.PopRaw<Source>()};
LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size,
offsets[2], source);
u32 read_size{};
ctx.WriteBuffer(
SerializeArray(&MiiManager::GetStoreDataElement, offsets[2], size, read_size));
offsets[2] += read_size;
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(read_size);
}
// Gets Miis from database at offset and index in format MiiStoreData
void Get3(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto size{rp.PopRaw<u32>()};
const auto source{rp.PopRaw<Source>()};
LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size,
offsets[3], source);
u32 read_size{};
ctx.WriteBuffer(SerializeArray(&MiiManager::GetStoreData, offsets[3], size, read_size));
offsets[3] += read_size;
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(read_size);
}
void FindIndex(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto uuid{rp.PopRaw<Common::UUID>()};
const auto unknown{rp.PopRaw<bool>()};
LOG_DEBUG(Service_Mii, "called with uuid={}, unknown={}", uuid.FormatSwitch(), unknown);
IPC::ResponseBuilder rb{ctx, 3};
const auto index = db.IndexOf(uuid);
if (index > MAX_MIIS) {
// TODO(DarkLordZach): Find a better error code
rb.Push(RESULT_UNKNOWN);
rb.Push(index);
} else {
rb.Push(RESULT_SUCCESS);
rb.Push(index);
}
}
void Move(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto uuid{rp.PopRaw<Common::UUID>()};
const auto index{rp.PopRaw<s32>()};
if (index < 0) {
LOG_ERROR(Service_Mii, "Index cannot be negative but is {:08X}!", index);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ARGUMENT);
return;
}
LOG_DEBUG(Service_Mii, "called with uuid={}, index={:08X}", uuid.FormatSwitch(), index);
const auto success = db.Move(uuid, index);
IPC::ResponseBuilder rb{ctx, 2};
// TODO(DarkLordZach): Find a better error code
rb.Push(success ? RESULT_SUCCESS : RESULT_UNKNOWN);
}
void AddOrReplace(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto data{rp.PopRaw<MiiStoreData>()};
LOG_DEBUG(Service_Mii, "called with Mii data uuid={}, name={}", data.uuid.FormatSwitch(),
Common::UTF16ToUTF8(data.Name()));
const auto success = db.AddOrReplace(data);
IPC::ResponseBuilder rb{ctx, 2};
// TODO(DarkLordZach): Find a better error code
rb.Push(success ? RESULT_SUCCESS : RESULT_UNKNOWN);
}
void Delete(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto uuid{rp.PopRaw<Common::UUID>()};
LOG_DEBUG(Service_Mii, "called with uuid={}", uuid.FormatSwitch());
const auto success = db.Remove(uuid);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(success ? RESULT_SUCCESS : ERROR_CANNOT_FIND_ENTRY);
}
void DestroyFile(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Mii, "called");
if (!db.IsTestModeEnabled()) {
LOG_ERROR(Service_Mii, "Database is not in test mode -- cannot destory database file.");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_NOT_IN_TEST_MODE);
return;
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(db.DestroyFile());
}
void DeleteFile(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Mii, "called");
if (!db.IsTestModeEnabled()) {
LOG_ERROR(Service_Mii, "Database is not in test mode -- cannot delete database file.");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_NOT_IN_TEST_MODE);
return;
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(db.DeleteFile());
}
void Format(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Mii, "called");
db.Clear();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<MiiInfo>(manager.BuildDefault(index));
}
void GetIndex(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto info{rp.PopRaw<MiiInfo>()};
LOG_DEBUG(Service_Mii, "called with Mii info uuid={}, name={}", info.uuid.FormatSwitch(),
Common::UTF16ToUTF8(info.Name()));
LOG_DEBUG(Service_Mii, "called");
const auto index = db.IndexOf(info);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
u32 index{};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(manager.GetIndex(info, index));
rb.Push(index);
}
@@ -364,12 +239,14 @@ private:
rb.Push(RESULT_SUCCESS);
}
MiiManager db;
constexpr bool IsInterfaceVersionSupported(u32 interface_version) const {
return current_interface_version >= interface_version;
}
u32 current_interface_version = 0;
MiiManager manager;
// Last read offsets of Get functions
std::array<u32, 4> offsets{};
u32 current_interface_version{};
u64 current_update_counter{};
};
class MiiDBModule final : public ServiceFramework<MiiDBModule> {

View File

@@ -1,420 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <cstring>
#include "common/assert.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/hle/service/mii/mii_manager.h"
namespace Service::Mii {
namespace {
constexpr char MII_SAVE_DATABASE_PATH[] = "/system/save/8000000000000030/MiiDatabase.dat";
constexpr std::array<char16_t, 11> DEFAULT_MII_NAME = {u'y', u'u', u'z', u'u', u'\0'};
// This value was retrieved from HW test
constexpr MiiStoreData DEFAULT_MII = {
{
0x21, 0x40, 0x40, 0x01, 0x08, 0x01, 0x13, 0x08, 0x08, 0x02, 0x17, 0x8C, 0x06, 0x01,
0x69, 0x6D, 0x8A, 0x6A, 0x82, 0x14, 0x00, 0x00, 0x00, 0x20, 0x64, 0x72, 0x44, 0x44,
},
{'y', 'u', 'z', 'u', '\0'},
Common::UUID{1, 0},
0,
0,
};
// Default values taken from multiple real databases
const MiiDatabase DEFAULT_MII_DATABASE{Common::MakeMagic('N', 'F', 'D', 'B'), {}, {1}, 0, 0};
constexpr std::array<const char*, 4> SOURCE_NAMES{
"Database",
"Default",
"Account",
"Friend",
};
template <typename T, std::size_t SourceArraySize, std::size_t DestArraySize>
std::array<T, DestArraySize> ResizeArray(const std::array<T, SourceArraySize>& in) {
std::array<T, DestArraySize> out{};
std::memcpy(out.data(), in.data(), sizeof(T) * std::min(SourceArraySize, DestArraySize));
return out;
}
MiiInfo ConvertStoreDataToInfo(const MiiStoreData& data) {
MiiStoreBitFields bf{};
std::memcpy(&bf, data.data.data(), sizeof(MiiStoreBitFields));
return {
data.uuid,
ResizeArray<char16_t, 10, 11>(data.name),
static_cast<u8>(bf.font_region.Value()),
static_cast<u8>(bf.favorite_color.Value()),
static_cast<u8>(bf.gender.Value()),
static_cast<u8>(bf.height.Value()),
static_cast<u8>(bf.weight.Value()),
static_cast<u8>(bf.mii_type.Value()),
static_cast<u8>(bf.mii_region.Value()),
static_cast<u8>(bf.face_type.Value()),
static_cast<u8>(bf.face_color.Value()),
static_cast<u8>(bf.face_wrinkle.Value()),
static_cast<u8>(bf.face_makeup.Value()),
static_cast<u8>(bf.hair_type.Value()),
static_cast<u8>(bf.hair_color.Value()),
static_cast<bool>(bf.hair_flip.Value()),
static_cast<u8>(bf.eye_type.Value()),
static_cast<u8>(bf.eye_color.Value()),
static_cast<u8>(bf.eye_scale.Value()),
static_cast<u8>(bf.eye_aspect.Value()),
static_cast<u8>(bf.eye_rotate.Value()),
static_cast<u8>(bf.eye_x.Value()),
static_cast<u8>(bf.eye_y.Value()),
static_cast<u8>(bf.eyebrow_type.Value()),
static_cast<u8>(bf.eyebrow_color.Value()),
static_cast<u8>(bf.eyebrow_scale.Value()),
static_cast<u8>(bf.eyebrow_aspect.Value()),
static_cast<u8>(bf.eyebrow_rotate.Value()),
static_cast<u8>(bf.eyebrow_x.Value()),
static_cast<u8>(bf.eyebrow_y.Value()),
static_cast<u8>(bf.nose_type.Value()),
static_cast<u8>(bf.nose_scale.Value()),
static_cast<u8>(bf.nose_y.Value()),
static_cast<u8>(bf.mouth_type.Value()),
static_cast<u8>(bf.mouth_color.Value()),
static_cast<u8>(bf.mouth_scale.Value()),
static_cast<u8>(bf.mouth_aspect.Value()),
static_cast<u8>(bf.mouth_y.Value()),
static_cast<u8>(bf.facial_hair_color.Value()),
static_cast<u8>(bf.beard_type.Value()),
static_cast<u8>(bf.mustache_type.Value()),
static_cast<u8>(bf.mustache_scale.Value()),
static_cast<u8>(bf.mustache_y.Value()),
static_cast<u8>(bf.glasses_type.Value()),
static_cast<u8>(bf.glasses_color.Value()),
static_cast<u8>(bf.glasses_scale.Value()),
static_cast<u8>(bf.glasses_y.Value()),
static_cast<u8>(bf.mole_type.Value()),
static_cast<u8>(bf.mole_scale.Value()),
static_cast<u8>(bf.mole_x.Value()),
static_cast<u8>(bf.mole_y.Value()),
0x00,
};
}
MiiStoreData ConvertInfoToStoreData(const MiiInfo& info) {
MiiStoreData out{};
out.name = ResizeArray<char16_t, 11, 10>(info.name);
out.uuid = info.uuid;
MiiStoreBitFields bf{};
bf.hair_type.Assign(info.hair_type);
bf.mole_type.Assign(info.mole_type);
bf.height.Assign(info.height);
bf.hair_flip.Assign(info.hair_flip);
bf.weight.Assign(info.weight);
bf.hair_color.Assign(info.hair_color);
bf.gender.Assign(info.gender);
bf.eye_color.Assign(info.eye_color);
bf.eyebrow_color.Assign(info.eyebrow_color);
bf.mouth_color.Assign(info.mouth_color);
bf.facial_hair_color.Assign(info.facial_hair_color);
bf.mii_type.Assign(info.mii_type);
bf.glasses_color.Assign(info.glasses_color);
bf.font_region.Assign(info.font_region);
bf.eye_type.Assign(info.eye_type);
bf.mii_region.Assign(info.mii_region);
bf.mouth_type.Assign(info.mouth_type);
bf.glasses_scale.Assign(info.glasses_scale);
bf.eye_y.Assign(info.eye_y);
bf.mustache_type.Assign(info.mustache_type);
bf.eyebrow_type.Assign(info.eyebrow_type);
bf.beard_type.Assign(info.beard_type);
bf.nose_type.Assign(info.nose_type);
bf.mouth_aspect.Assign(info.mouth_aspect_ratio);
bf.nose_y.Assign(info.nose_y);
bf.eyebrow_aspect.Assign(info.eyebrow_aspect_ratio);
bf.mouth_y.Assign(info.mouth_y);
bf.eye_rotate.Assign(info.eye_rotate);
bf.mustache_y.Assign(info.mustache_y);
bf.eye_aspect.Assign(info.eye_aspect_ratio);
bf.glasses_y.Assign(info.glasses_y);
bf.eye_scale.Assign(info.eye_scale);
bf.mole_x.Assign(info.mole_x);
bf.mole_y.Assign(info.mole_y);
bf.glasses_type.Assign(info.glasses_type);
bf.face_type.Assign(info.face_type);
bf.favorite_color.Assign(info.favorite_color);
bf.face_wrinkle.Assign(info.face_wrinkle);
bf.face_color.Assign(info.face_color);
bf.eye_x.Assign(info.eye_x);
bf.face_makeup.Assign(info.face_makeup);
bf.eyebrow_rotate.Assign(info.eyebrow_rotate);
bf.eyebrow_scale.Assign(info.eyebrow_scale);
bf.eyebrow_y.Assign(info.eyebrow_y);
bf.eyebrow_x.Assign(info.eyebrow_x);
bf.mouth_scale.Assign(info.mouth_scale);
bf.nose_scale.Assign(info.nose_scale);
bf.mole_scale.Assign(info.mole_scale);
bf.mustache_scale.Assign(info.mustache_scale);
std::memcpy(out.data.data(), &bf, sizeof(MiiStoreBitFields));
return out;
}
} // namespace
std::ostream& operator<<(std::ostream& os, Source source) {
if (static_cast<std::size_t>(source) >= SOURCE_NAMES.size()) {
return os << "[UNKNOWN SOURCE]";
}
os << SOURCE_NAMES.at(static_cast<std::size_t>(source));
return os;
}
std::u16string MiiInfo::Name() const {
return Common::UTF16StringFromFixedZeroTerminatedBuffer(name.data(), name.size());
}
bool operator==(const MiiInfo& lhs, const MiiInfo& rhs) {
return std::memcmp(&lhs, &rhs, sizeof(MiiInfo)) == 0;
}
bool operator!=(const MiiInfo& lhs, const MiiInfo& rhs) {
return !operator==(lhs, rhs);
}
std::u16string MiiStoreData::Name() const {
return Common::UTF16StringFromFixedZeroTerminatedBuffer(name.data(), name.size());
}
MiiManager::MiiManager() = default;
MiiManager::~MiiManager() = default;
MiiInfo MiiManager::CreateRandom(RandomParameters params) {
LOG_WARNING(Service_Mii,
"(STUBBED) called with params={:08X}{:08X}{:08X}, returning default Mii",
params.unknown_1, params.unknown_2, params.unknown_3);
return ConvertStoreDataToInfo(CreateMiiWithUniqueUUID());
}
MiiInfo MiiManager::CreateDefault(u32 index) {
const auto new_mii = CreateMiiWithUniqueUUID();
database.miis.at(index) = new_mii;
EnsureDatabasePartition();
return ConvertStoreDataToInfo(new_mii);
}
bool MiiManager::CheckUpdatedFlag() const {
return updated_flag;
}
void MiiManager::ResetUpdatedFlag() {
updated_flag = false;
}
bool MiiManager::IsTestModeEnabled() const {
return is_test_mode_enabled;
}
bool MiiManager::Empty() const {
return Size() == 0;
}
bool MiiManager::Full() const {
return Size() == MAX_MIIS;
}
void MiiManager::Clear() {
updated_flag = true;
std::fill(database.miis.begin(), database.miis.end(), MiiStoreData{});
}
u32 MiiManager::Size() const {
return static_cast<u32>(std::count_if(database.miis.begin(), database.miis.end(),
[](const MiiStoreData& elem) { return elem.uuid; }));
}
MiiInfo MiiManager::GetInfo(u32 index) const {
return ConvertStoreDataToInfo(GetStoreData(index));
}
MiiInfoElement MiiManager::GetInfoElement(u32 index) const {
return {GetInfo(index), Source::Database};
}
MiiStoreData MiiManager::GetStoreData(u32 index) const {
return database.miis.at(index);
}
MiiStoreDataElement MiiManager::GetStoreDataElement(u32 index) const {
return {GetStoreData(index), Source::Database};
}
bool MiiManager::Remove(Common::UUID uuid) {
const auto iter = std::find_if(database.miis.begin(), database.miis.end(),
[uuid](const MiiStoreData& elem) { return elem.uuid == uuid; });
if (iter == database.miis.end())
return false;
updated_flag = true;
*iter = MiiStoreData{};
EnsureDatabasePartition();
return true;
}
u32 MiiManager::IndexOf(Common::UUID uuid) const {
const auto iter = std::find_if(database.miis.begin(), database.miis.end(),
[uuid](const MiiStoreData& elem) { return elem.uuid == uuid; });
if (iter == database.miis.end())
return INVALID_INDEX;
return static_cast<u32>(std::distance(database.miis.begin(), iter));
}
u32 MiiManager::IndexOf(const MiiInfo& info) const {
const auto iter =
std::find_if(database.miis.begin(), database.miis.end(), [&info](const MiiStoreData& elem) {
return ConvertStoreDataToInfo(elem) == info;
});
if (iter == database.miis.end())
return INVALID_INDEX;
return static_cast<u32>(std::distance(database.miis.begin(), iter));
}
bool MiiManager::Move(Common::UUID uuid, u32 new_index) {
const auto index = IndexOf(uuid);
if (index == INVALID_INDEX || new_index >= MAX_MIIS)
return false;
updated_flag = true;
const auto moving = database.miis[index];
const auto replacing = database.miis[new_index];
if (replacing.uuid) {
database.miis[index] = replacing;
database.miis[new_index] = moving;
} else {
database.miis[index] = MiiStoreData{};
database.miis[new_index] = moving;
}
EnsureDatabasePartition();
return true;
}
bool MiiManager::AddOrReplace(const MiiStoreData& data) {
const auto index = IndexOf(data.uuid);
updated_flag = true;
if (index == INVALID_INDEX) {
const auto size = Size();
if (size == MAX_MIIS)
return false;
database.miis[size] = data;
} else {
database.miis[index] = data;
}
return true;
}
bool MiiManager::DestroyFile() {
database = DEFAULT_MII_DATABASE;
updated_flag = false;
return DeleteFile();
}
bool MiiManager::DeleteFile() {
const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH;
return FileUtil::Exists(path) && FileUtil::Delete(path);
}
void MiiManager::WriteToFile() {
const auto raw_path =
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000030";
if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path))
FileUtil::Delete(raw_path);
const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH;
if (!FileUtil::CreateFullPath(path)) {
LOG_WARNING(Service_Mii,
"Failed to create full path of MiiDatabase.dat. Create the directory "
"nand/system/save/8000000000000030 to mitigate this "
"issue.");
return;
}
FileUtil::IOFile save(path, "wb");
if (!save.IsOpen()) {
LOG_WARNING(Service_Mii, "Failed to write save data to file... No changes to user data "
"made in current session will be saved.");
return;
}
save.Resize(sizeof(MiiDatabase));
if (save.WriteBytes(&database, sizeof(MiiDatabase)) != sizeof(MiiDatabase)) {
LOG_WARNING(Service_Mii, "Failed to write all data to save file... Data may be malformed "
"and/or regenerated on next run.");
save.Resize(0);
}
}
void MiiManager::ReadFromFile() {
FileUtil::IOFile save(
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH, "rb");
if (!save.IsOpen()) {
LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new "
"blank Mii database with no Miis.");
std::memcpy(&database, &DEFAULT_MII_DATABASE, sizeof(MiiDatabase));
return;
}
if (save.ReadBytes(&database, sizeof(MiiDatabase)) != sizeof(MiiDatabase)) {
LOG_WARNING(Service_ACC, "MiiDatabase.dat is smaller than expected... Generating new blank "
"Mii database with no Miis.");
std::memcpy(&database, &DEFAULT_MII_DATABASE, sizeof(MiiDatabase));
return;
}
EnsureDatabasePartition();
}
MiiStoreData MiiManager::CreateMiiWithUniqueUUID() const {
auto new_mii = DEFAULT_MII;
do {
new_mii.uuid = Common::UUID::Generate();
} while (IndexOf(new_mii.uuid) != INVALID_INDEX);
return new_mii;
}
void MiiManager::EnsureDatabasePartition() {
std::stable_partition(database.miis.begin(), database.miis.end(),
[](const MiiStoreData& elem) { return elem.uuid; });
}
} // namespace Service::Mii

View File

@@ -1,273 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/uuid.h"
namespace Service::Mii {
constexpr std::size_t MAX_MIIS{100};
constexpr u32 INVALID_INDEX{0xFFFFFFFF};
struct RandomParameters {
u32 unknown_1{};
u32 unknown_2{};
u32 unknown_3{};
};
static_assert(sizeof(RandomParameters) == 0xC, "RandomParameters has incorrect size.");
enum class Source : u32 {
Database = 0,
Default = 1,
Account = 2,
Friend = 3,
};
std::ostream& operator<<(std::ostream& os, Source source);
struct MiiInfo {
Common::UUID uuid{Common::INVALID_UUID};
std::array<char16_t, 11> name{};
u8 font_region{};
u8 favorite_color{};
u8 gender{};
u8 height{};
u8 weight{};
u8 mii_type{};
u8 mii_region{};
u8 face_type{};
u8 face_color{};
u8 face_wrinkle{};
u8 face_makeup{};
u8 hair_type{};
u8 hair_color{};
bool hair_flip{};
u8 eye_type{};
u8 eye_color{};
u8 eye_scale{};
u8 eye_aspect_ratio{};
u8 eye_rotate{};
u8 eye_x{};
u8 eye_y{};
u8 eyebrow_type{};
u8 eyebrow_color{};
u8 eyebrow_scale{};
u8 eyebrow_aspect_ratio{};
u8 eyebrow_rotate{};
u8 eyebrow_x{};
u8 eyebrow_y{};
u8 nose_type{};
u8 nose_scale{};
u8 nose_y{};
u8 mouth_type{};
u8 mouth_color{};
u8 mouth_scale{};
u8 mouth_aspect_ratio{};
u8 mouth_y{};
u8 facial_hair_color{};
u8 beard_type{};
u8 mustache_type{};
u8 mustache_scale{};
u8 mustache_y{};
u8 glasses_type{};
u8 glasses_color{};
u8 glasses_scale{};
u8 glasses_y{};
u8 mole_type{};
u8 mole_scale{};
u8 mole_x{};
u8 mole_y{};
INSERT_PADDING_BYTES(1);
std::u16string Name() const;
};
static_assert(sizeof(MiiInfo) == 0x58, "MiiInfo has incorrect size.");
static_assert(std::has_unique_object_representations_v<MiiInfo>,
"All bits of MiiInfo must contribute to its value.");
bool operator==(const MiiInfo& lhs, const MiiInfo& rhs);
bool operator!=(const MiiInfo& lhs, const MiiInfo& rhs);
#pragma pack(push, 4)
struct MiiInfoElement {
MiiInfo info{};
Source source{};
};
static_assert(sizeof(MiiInfoElement) == 0x5C, "MiiInfoElement has incorrect size.");
struct MiiStoreBitFields {
union {
u32 word_0{};
BitField<24, 8, u32> hair_type;
BitField<23, 1, u32> mole_type;
BitField<16, 7, u32> height;
BitField<15, 1, u32> hair_flip;
BitField<8, 7, u32> weight;
BitField<0, 7, u32> hair_color;
};
union {
u32 word_1{};
BitField<31, 1, u32> gender;
BitField<24, 7, u32> eye_color;
BitField<16, 7, u32> eyebrow_color;
BitField<8, 7, u32> mouth_color;
BitField<0, 7, u32> facial_hair_color;
};
union {
u32 word_2{};
BitField<31, 1, u32> mii_type;
BitField<24, 7, u32> glasses_color;
BitField<22, 2, u32> font_region;
BitField<16, 6, u32> eye_type;
BitField<14, 2, u32> mii_region;
BitField<8, 6, u32> mouth_type;
BitField<5, 3, u32> glasses_scale;
BitField<0, 5, u32> eye_y;
};
union {
u32 word_3{};
BitField<29, 3, u32> mustache_type;
BitField<24, 5, u32> eyebrow_type;
BitField<21, 3, u32> beard_type;
BitField<16, 5, u32> nose_type;
BitField<13, 3, u32> mouth_aspect;
BitField<8, 5, u32> nose_y;
BitField<5, 3, u32> eyebrow_aspect;
BitField<0, 5, u32> mouth_y;
};
union {
u32 word_4{};
BitField<29, 3, u32> eye_rotate;
BitField<24, 5, u32> mustache_y;
BitField<21, 3, u32> eye_aspect;
BitField<16, 5, u32> glasses_y;
BitField<13, 3, u32> eye_scale;
BitField<8, 5, u32> mole_x;
BitField<0, 5, u32> mole_y;
};
union {
u32 word_5{};
BitField<24, 5, u32> glasses_type;
BitField<20, 4, u32> face_type;
BitField<16, 4, u32> favorite_color;
BitField<12, 4, u32> face_wrinkle;
BitField<8, 4, u32> face_color;
BitField<4, 4, u32> eye_x;
BitField<0, 4, u32> face_makeup;
};
union {
u32 word_6{};
BitField<28, 4, u32> eyebrow_rotate;
BitField<24, 4, u32> eyebrow_scale;
BitField<20, 4, u32> eyebrow_y;
BitField<16, 4, u32> eyebrow_x;
BitField<12, 4, u32> mouth_scale;
BitField<8, 4, u32> nose_scale;
BitField<4, 4, u32> mole_scale;
BitField<0, 4, u32> mustache_scale;
};
};
static_assert(sizeof(MiiStoreBitFields) == 0x1C, "MiiStoreBitFields has incorrect size.");
static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>,
"MiiStoreBitFields is not trivially copyable.");
struct MiiStoreData {
// This corresponds to the above structure MiiStoreBitFields. I did it like this because the
// BitField<> type makes this (and any thing that contains it) not trivially copyable, which is
// not suitable for our uses.
std::array<u8, 0x1C> data{};
static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size.");
std::array<char16_t, 10> name{};
Common::UUID uuid{Common::INVALID_UUID};
u16 crc_1{};
u16 crc_2{};
std::u16string Name() const;
};
static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size.");
struct MiiStoreDataElement {
MiiStoreData data{};
Source source{};
};
static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size.");
struct MiiDatabase {
u32 magic{}; // 'NFDB'
std::array<MiiStoreData, MAX_MIIS> miis{};
INSERT_PADDING_BYTES(1);
u8 count{};
u16 crc{};
};
static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size.");
#pragma pack(pop)
// The Mii manager is responsible for loading and storing the Miis to the database in NAND along
// with providing an easy interface for HLE emulation of the mii service.
class MiiManager {
public:
MiiManager();
~MiiManager();
MiiInfo CreateRandom(RandomParameters params);
MiiInfo CreateDefault(u32 index);
bool CheckUpdatedFlag() const;
void ResetUpdatedFlag();
bool IsTestModeEnabled() const;
bool Empty() const;
bool Full() const;
void Clear();
u32 Size() const;
MiiInfo GetInfo(u32 index) const;
MiiInfoElement GetInfoElement(u32 index) const;
MiiStoreData GetStoreData(u32 index) const;
MiiStoreDataElement GetStoreDataElement(u32 index) const;
bool Remove(Common::UUID uuid);
u32 IndexOf(Common::UUID uuid) const;
u32 IndexOf(const MiiInfo& info) const;
bool Move(Common::UUID uuid, u32 new_index);
bool AddOrReplace(const MiiStoreData& data);
bool DestroyFile();
bool DeleteFile();
private:
void WriteToFile();
void ReadFromFile();
MiiStoreData CreateMiiWithUniqueUUID() const;
void EnsureDatabasePartition();
MiiDatabase database;
bool updated_flag{};
bool is_test_mode_enabled{};
};
}; // namespace Service::Mii

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,27 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include "common/common_types.h"
namespace Service::Mii::RawData {
extern const std::array<u8, 1728> DefaultMii;
extern const std::array<u8, 3672> RandomMiiFaceline;
extern const std::array<u8, 1200> RandomMiiFacelineColor;
extern const std::array<u8, 3672> RandomMiiFacelineWrinkle;
extern const std::array<u8, 3672> RandomMiiFacelineMakeup;
extern const std::array<u8, 3672> RandomMiiHairType;
extern const std::array<u8, 1800> RandomMiiHairColor;
extern const std::array<u8, 3672> RandomMiiEyeType;
extern const std::array<u8, 588> RandomMiiEyeColor;
extern const std::array<u8, 3672> RandomMiiEyebrowType;
extern const std::array<u8, 3672> RandomMiiNoseType;
extern const std::array<u8, 3672> RandomMiiMouthType;
extern const std::array<u8, 588> RandomMiiGlassType;
} // namespace Service::Mii::RawData

View File

@@ -0,0 +1,67 @@
// Copyright 2020 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_funcs.h"
#include "common/common_types.h"
namespace Service::Mii {
enum class Age : u32 {
Young,
Normal,
Old,
All,
};
enum class BeardType : u32 {
None,
Beard1,
Beard2,
Beard3,
Beard4,
Beard5,
};
enum class BeardAndMustacheFlag : u32 { Beard = 1, Mustache, All = Beard | Mustache };
DECLARE_ENUM_FLAG_OPERATORS(BeardAndMustacheFlag);
enum class FontRegion : u32 {
Standard,
China,
Korea,
Taiwan,
};
enum class Gender : u32 {
Male,
Female,
All,
Maximum = Female,
};
enum class HairFlip : u32 {
Left,
Right,
Maximum = Right,
};
enum class MustacheType : u32 {
None,
Mustache1,
Mustache2,
Mustache3,
Mustache4,
Mustache5,
};
enum class Race : u32 {
Black,
White,
Asian,
All,
};
} // namespace Service::Mii

View File

@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <string_view>
#include "common/file_util.h"
#include "core/core.h"
#include "core/gdbstub/gdbstub.h"
@@ -65,18 +67,18 @@ Values values = {};
bool configuring_global = true;
std::string GetTimeZoneString() {
static constexpr std::array<const char*, 46> timezones{{
static constexpr std::array timezones{
"auto", "default", "CET", "CST6CDT", "Cuba", "EET", "Egypt", "Eire",
"EST", "EST5EDT", "GB", "GB-Eire", "GMT", "GMT+0", "GMT-0", "GMT0",
"Greenwich", "Hongkong", "HST", "Iceland", "Iran", "Israel", "Jamaica", "Japan",
"Kwajalein", "Libya", "MET", "MST", "MST7MDT", "Navajo", "NZ", "NZ-CHAT",
"Poland", "Portugal", "PRC", "PST8PDT", "ROC", "ROK", "Singapore", "Turkey",
"UCT", "Universal", "UTC", "W-SU", "WET", "Zulu",
}};
};
ASSERT(Settings::values.time_zone_index.GetValue() < timezones.size());
return timezones[Settings::values.time_zone_index.GetValue()];
const auto time_zone_index = static_cast<std::size_t>(values.time_zone_index.GetValue());
ASSERT(time_zone_index < timezones.size());
return timezones[time_zone_index];
}
void Apply() {
@@ -91,41 +93,41 @@ void Apply() {
Service::HID::ReloadInputDevices();
}
template <typename T>
void LogSetting(const std::string& name, const T& value) {
LOG_INFO(Config, "{}: {}", name, value);
}
void LogSettings() {
const auto log_setting = [](std::string_view name, const auto& value) {
LOG_INFO(Config, "{}: {}", name, value);
};
LOG_INFO(Config, "yuzu Configuration:");
LogSetting("Controls_UseDockedMode", Settings::values.use_docked_mode);
LogSetting("System_RngSeed", Settings::values.rng_seed.GetValue().value_or(0));
LogSetting("System_CurrentUser", Settings::values.current_user);
LogSetting("System_LanguageIndex", Settings::values.language_index.GetValue());
LogSetting("System_RegionIndex", Settings::values.region_index.GetValue());
LogSetting("System_TimeZoneIndex", Settings::values.time_zone_index.GetValue());
LogSetting("Core_UseMultiCore", Settings::values.use_multi_core.GetValue());
LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor.GetValue());
LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit.GetValue());
LogSetting("Renderer_FrameLimit", Settings::values.frame_limit.GetValue());
LogSetting("Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache.GetValue());
LogSetting("Renderer_GPUAccuracyLevel", Settings::values.gpu_accuracy.GetValue());
LogSetting("Renderer_UseAsynchronousGpuEmulation",
Settings::values.use_asynchronous_gpu_emulation.GetValue());
LogSetting("Renderer_UseVsync", Settings::values.use_vsync.GetValue());
LogSetting("Renderer_UseAssemblyShaders", Settings::values.use_assembly_shaders.GetValue());
LogSetting("Renderer_AnisotropicFilteringLevel", Settings::values.max_anisotropy.GetValue());
LogSetting("Audio_OutputEngine", Settings::values.sink_id);
LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching.GetValue());
LogSetting("Audio_OutputDevice", Settings::values.audio_device_id);
LogSetting("DataStorage_UseVirtualSd", Settings::values.use_virtual_sd);
LogSetting("DataStorage_NandDir", FileUtil::GetUserPath(FileUtil::UserPath::NANDDir));
LogSetting("DataStorage_SdmcDir", FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir));
LogSetting("Debugging_UseGdbstub", Settings::values.use_gdbstub);
LogSetting("Debugging_GdbstubPort", Settings::values.gdbstub_port);
LogSetting("Debugging_ProgramArgs", Settings::values.program_args);
LogSetting("Services_BCATBackend", Settings::values.bcat_backend);
LogSetting("Services_BCATBoxcatLocal", Settings::values.bcat_boxcat_local);
log_setting("Controls_UseDockedMode", values.use_docked_mode);
log_setting("System_RngSeed", values.rng_seed.GetValue().value_or(0));
log_setting("System_CurrentUser", values.current_user);
log_setting("System_LanguageIndex", values.language_index.GetValue());
log_setting("System_RegionIndex", values.region_index.GetValue());
log_setting("System_TimeZoneIndex", values.time_zone_index.GetValue());
log_setting("Core_UseMultiCore", values.use_multi_core.GetValue());
log_setting("Renderer_UseResolutionFactor", values.resolution_factor.GetValue());
log_setting("Renderer_UseFrameLimit", values.use_frame_limit.GetValue());
log_setting("Renderer_FrameLimit", values.frame_limit.GetValue());
log_setting("Renderer_UseDiskShaderCache", values.use_disk_shader_cache.GetValue());
log_setting("Renderer_GPUAccuracyLevel", values.gpu_accuracy.GetValue());
log_setting("Renderer_UseAsynchronousGpuEmulation",
values.use_asynchronous_gpu_emulation.GetValue());
log_setting("Renderer_UseVsync", values.use_vsync.GetValue());
log_setting("Renderer_UseAssemblyShaders", values.use_assembly_shaders.GetValue());
log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue());
log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue());
log_setting("Audio_OutputEngine", values.sink_id);
log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue());
log_setting("Audio_OutputDevice", values.audio_device_id);
log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd);
log_setting("DataStorage_NandDir", FileUtil::GetUserPath(FileUtil::UserPath::NANDDir));
log_setting("DataStorage_SdmcDir", FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir));
log_setting("Debugging_UseGdbstub", values.use_gdbstub);
log_setting("Debugging_GdbstubPort", values.gdbstub_port);
log_setting("Debugging_ProgramArgs", values.program_args);
log_setting("Services_BCATBackend", values.bcat_backend);
log_setting("Services_BCATBoxcatLocal", values.bcat_boxcat_local);
}
float Volume() {
@@ -169,6 +171,7 @@ void RestoreGlobalState() {
values.use_asynchronous_gpu_emulation.SetGlobal(true);
values.use_vsync.SetGlobal(true);
values.use_assembly_shaders.SetGlobal(true);
values.use_asynchronous_shaders.SetGlobal(true);
values.use_fast_gpu_time.SetGlobal(true);
values.force_30fps_mode.SetGlobal(true);
values.bg_red.SetGlobal(true);
@@ -184,4 +187,9 @@ void RestoreGlobalState() {
values.sound_index.SetGlobal(true);
}
void Sanitize() {
values.use_asynchronous_gpu_emulation.SetValue(
values.use_asynchronous_gpu_emulation.GetValue() || values.use_multi_core.GetValue());
}
} // namespace Settings

View File

@@ -346,31 +346,6 @@ struct TouchscreenInput {
u32 rotation_angle;
};
enum class NANDTotalSize : u64 {
S29_1GB = 0x747C00000ULL,
};
enum class NANDUserSize : u64 {
S26GB = 0x680000000ULL,
};
enum class NANDSystemSize : u64 {
S2_5GB = 0xA0000000,
};
enum class SDMCSize : u64 {
S1GB = 0x40000000,
S2GB = 0x80000000,
S4GB = 0x100000000ULL,
S8GB = 0x200000000ULL,
S16GB = 0x400000000ULL,
S32GB = 0x800000000ULL,
S64GB = 0x1000000000ULL,
S128GB = 0x2000000000ULL,
S256GB = 0x4000000000ULL,
S1TB = 0x10000000000ULL,
};
enum class RendererBackend {
OpenGL = 0,
Vulkan = 1,
@@ -382,6 +357,11 @@ enum class GPUAccuracy : u32 {
Extreme = 2,
};
enum class CPUAccuracy {
Accurate = 0,
DebugMode = 1,
};
extern bool configuring_global;
template <typename Type>
@@ -427,6 +407,18 @@ struct Values {
// Core
Setting<bool> use_multi_core;
// Cpu
CPUAccuracy cpu_accuracy;
bool cpuopt_page_tables;
bool cpuopt_block_linking;
bool cpuopt_return_stack_buffer;
bool cpuopt_fast_dispatcher;
bool cpuopt_context_elimination;
bool cpuopt_const_prop;
bool cpuopt_misc_ir;
bool cpuopt_reduce_misalign_checks;
// Renderer
Setting<RendererBackend> renderer_backend;
bool renderer_debug;
@@ -442,6 +434,7 @@ struct Values {
Setting<bool> use_asynchronous_gpu_emulation;
Setting<bool> use_vsync;
Setting<bool> use_assembly_shaders;
Setting<bool> use_asynchronous_shaders;
Setting<bool> force_30fps_mode;
Setting<bool> use_fast_gpu_time;
@@ -491,10 +484,6 @@ struct Values {
bool gamecard_inserted;
bool gamecard_current_game;
std::string gamecard_path;
NANDTotalSize nand_total_size;
NANDSystemSize nand_system_size;
NANDUserSize nand_user_size;
SDMCSize sdmc_size;
// Debugging
bool record_frame_times;
@@ -505,7 +494,6 @@ struct Values {
bool dump_nso;
bool reporting_services;
bool quest_flag;
bool disable_cpu_opt;
bool disable_macro_jit;
// Misceallaneous
@@ -539,4 +527,7 @@ void LogSettings();
// Restore the global state of all applicable settings in the Values struct
void RestoreGlobalState();
// Fixes settings that are known to cause issues with the emulator
void Sanitize();
} // namespace Settings

View File

@@ -207,6 +207,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync.GetValue());
AddField(field_type, "Renderer_UseAssemblyShaders",
Settings::values.use_assembly_shaders.GetValue());
AddField(field_type, "Renderer_UseAsynchronousShaders",
Settings::values.use_asynchronous_shaders.GetValue());
AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode);
}

View File

@@ -30,7 +30,8 @@ if(SDL2_FOUND)
target_compile_definitions(input_common PRIVATE HAVE_SDL2)
endif()
target_link_libraries(input_common PUBLIC ${LIBUSB_LIBRARIES})
target_include_directories(input_common SYSTEM PRIVATE ${LIBUSB_INCLUDE_DIR})
target_link_libraries(input_common PRIVATE ${LIBUSB_LIBRARIES})
create_target_directory_groups(input_common)
target_link_libraries(input_common PUBLIC core PRIVATE common Boost::boost)

View File

@@ -4,6 +4,7 @@
#include <chrono>
#include <thread>
#include <libusb.h>
#include "common/logging/log.h"
#include "input_common/gcadapter/gc_adapter.h"
@@ -24,6 +25,7 @@ Adapter::Adapter() {
LOG_INFO(Input, "GC Adapter Initialization started");
current_status = NO_ADAPTER_DETECTED;
get_origin.fill(true);
const int init_res = libusb_init(&libusb_ctx);
if (init_res == LIBUSB_SUCCESS) {
@@ -33,15 +35,10 @@ Adapter::Adapter() {
}
}
GCPadStatus Adapter::GetPadStatus(int port, const std::array<u8, 37>& adapter_payload) {
GCPadStatus Adapter::GetPadStatus(std::size_t port, const std::array<u8, 37>& adapter_payload) {
GCPadStatus pad = {};
bool get_origin = false;
ControllerTypes type = ControllerTypes(adapter_payload[1 + (9 * port)] >> 4);
if (type != ControllerTypes::None) {
get_origin = true;
}
adapter_controllers_status[port] = type;
static constexpr std::array<PadButton, 8> b1_buttons{
@@ -57,6 +54,11 @@ GCPadStatus Adapter::GetPadStatus(int port, const std::array<u8, 37>& adapter_pa
PadButton::PAD_TRIGGER_L,
};
if (adapter_controllers_status[port] == ControllerTypes::None && !get_origin[port]) {
// Controller may have been disconnected, recalibrate if reconnected.
get_origin[port] = true;
}
if (adapter_controllers_status[port] != ControllerTypes::None) {
const u8 b1 = adapter_payload[1 + (9 * port) + 1];
const u8 b2 = adapter_payload[1 + (9 * port) + 2];
@@ -73,16 +75,22 @@ GCPadStatus Adapter::GetPadStatus(int port, const std::array<u8, 37>& adapter_pa
}
}
if (get_origin) {
pad.button |= PAD_GET_ORIGIN;
}
pad.stick_x = adapter_payload[1 + (9 * port) + 3];
pad.stick_y = adapter_payload[1 + (9 * port) + 4];
pad.substick_x = adapter_payload[1 + (9 * port) + 5];
pad.substick_y = adapter_payload[1 + (9 * port) + 6];
pad.trigger_left = adapter_payload[1 + (9 * port) + 7];
pad.trigger_right = adapter_payload[1 + (9 * port) + 8];
if (get_origin[port]) {
origin_status[port].stick_x = pad.stick_x;
origin_status[port].stick_y = pad.stick_y;
origin_status[port].substick_x = pad.substick_x;
origin_status[port].substick_y = pad.substick_y;
origin_status[port].trigger_left = pad.trigger_left;
origin_status[port].trigger_right = pad.trigger_right;
get_origin[port] = false;
}
}
return pad;
}
@@ -131,31 +139,31 @@ void Adapter::Read() {
for (std::size_t port = 0; port < pads.size(); ++port) {
pads[port] = GetPadStatus(port, adapter_payload_copy);
if (DeviceConnected(port) && configuring) {
if (pads[port].button != PAD_GET_ORIGIN) {
if (pads[port].button != 0) {
pad_queue[port].Push(pads[port]);
}
// Accounting for a threshold here because of some controller variance
if (pads[port].stick_x > pads[port].MAIN_STICK_CENTER_X + pads[port].THRESHOLD ||
pads[port].stick_x < pads[port].MAIN_STICK_CENTER_X - pads[port].THRESHOLD) {
if (pads[port].stick_x > origin_status[port].stick_x + pads[port].THRESHOLD ||
pads[port].stick_x < origin_status[port].stick_x - pads[port].THRESHOLD) {
pads[port].axis = GCAdapter::PadAxes::StickX;
pads[port].axis_value = pads[port].stick_x;
pad_queue[port].Push(pads[port]);
}
if (pads[port].stick_y > pads[port].MAIN_STICK_CENTER_Y + pads[port].THRESHOLD ||
pads[port].stick_y < pads[port].MAIN_STICK_CENTER_Y - pads[port].THRESHOLD) {
if (pads[port].stick_y > origin_status[port].stick_y + pads[port].THRESHOLD ||
pads[port].stick_y < origin_status[port].stick_y - pads[port].THRESHOLD) {
pads[port].axis = GCAdapter::PadAxes::StickY;
pads[port].axis_value = pads[port].stick_y;
pad_queue[port].Push(pads[port]);
}
if (pads[port].substick_x > pads[port].C_STICK_CENTER_X + pads[port].THRESHOLD ||
pads[port].substick_x < pads[port].C_STICK_CENTER_X - pads[port].THRESHOLD) {
if (pads[port].substick_x > origin_status[port].substick_x + pads[port].THRESHOLD ||
pads[port].substick_x < origin_status[port].substick_x - pads[port].THRESHOLD) {
pads[port].axis = GCAdapter::PadAxes::SubstickX;
pads[port].axis_value = pads[port].substick_x;
pad_queue[port].Push(pads[port]);
}
if (pads[port].substick_y > pads[port].C_STICK_CENTER_Y + pads[port].THRESHOLD ||
pads[port].substick_y < pads[port].C_STICK_CENTER_Y - pads[port].THRESHOLD) {
if (pads[port].substick_y > origin_status[port].substick_y + pads[port].THRESHOLD ||
pads[port].substick_y < origin_status[port].substick_y - pads[port].THRESHOLD) {
pads[port].axis = GCAdapter::PadAxes::SubstickY;
pads[port].axis_value = pads[port].substick_y;
pad_queue[port].Push(pads[port]);
@@ -198,7 +206,7 @@ void Adapter::StartScanThread() {
}
detect_thread_running = true;
detect_thread = std::thread([=] { ScanThreadFunc(); });
detect_thread = std::thread(&Adapter::ScanThreadFunc, this);
}
void Adapter::StopScanThread() {
@@ -227,7 +235,7 @@ void Adapter::Setup() {
}
if (devices != nullptr) {
for (std::size_t index = 0; index < device_count; ++index) {
for (std::size_t index = 0; index < static_cast<std::size_t>(device_count); ++index) {
if (CheckDeviceAccess(devices[index])) {
// GC Adapter found and accessible, registering it
GetGCEndpoint(devices[index]);
@@ -236,6 +244,9 @@ void Adapter::Setup() {
}
libusb_free_device_list(devices, 1);
}
// Break out of the ScanThreadFunc() loop that is constantly looking for the device
// Assumes user has GC adapter plugged in before launch to use the adapter
detect_thread_running = false;
}
bool Adapter::CheckDeviceAccess(libusb_device* device) {
@@ -344,6 +355,7 @@ void Adapter::Reset() {
adapter_input_thread.join();
adapter_controllers_status.fill(ControllerTypes::None);
get_origin.fill(true);
current_status = NO_ADAPTER_DETECTED;
if (usb_adapter_handle) {
@@ -357,15 +369,16 @@ void Adapter::Reset() {
}
}
bool Adapter::DeviceConnected(int port) {
bool Adapter::DeviceConnected(std::size_t port) {
return adapter_controllers_status[port] != ControllerTypes::None;
}
void Adapter::ResetDeviceType(int port) {
void Adapter::ResetDeviceType(std::size_t port) {
adapter_controllers_status[port] = ControllerTypes::None;
}
void Adapter::BeginConfiguration() {
get_origin.fill(true);
for (auto& pq : pad_queue) {
pq.Clear();
}
@@ -395,4 +408,25 @@ const std::array<GCState, 4>& Adapter::GetPadState() const {
return state;
}
int Adapter::GetOriginValue(int port, int axis) const {
const auto& status = origin_status[port];
switch (static_cast<PadAxes>(axis)) {
case PadAxes::StickX:
return status.stick_x;
case PadAxes::StickY:
return status.stick_y;
case PadAxes::SubstickX:
return status.substick_x;
case PadAxes::SubstickY:
return status.substick_y;
case PadAxes::TriggerLeft:
return status.trigger_left;
case PadAxes::TriggerRight:
return status.trigger_right;
default:
return 0;
}
}
} // namespace GCAdapter

View File

@@ -8,17 +8,14 @@
#include <mutex>
#include <thread>
#include <unordered_map>
#include <libusb.h>
#include "common/common_types.h"
#include "common/threadsafe_queue.h"
namespace GCAdapter {
struct libusb_context;
struct libusb_device;
struct libusb_device_handle;
enum {
PAD_USE_ORIGIN = 0x0080,
PAD_GET_ORIGIN = 0x2000,
PAD_ERR_STATUS = 0x8000,
};
namespace GCAdapter {
enum class PadButton {
PAD_BUTTON_LEFT = 0x0001,
@@ -97,14 +94,19 @@ public:
void BeginConfiguration();
void EndConfiguration();
/// Returns true if there is a device connected to port
bool DeviceConnected(std::size_t port);
std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue();
const std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue() const;
std::array<GCState, 4>& GetPadState();
const std::array<GCState, 4>& GetPadState() const;
int GetOriginValue(int port, int axis) const;
private:
GCPadStatus GetPadStatus(int port, const std::array<u8, 37>& adapter_payload);
GCPadStatus GetPadStatus(std::size_t port, const std::array<u8, 37>& adapter_payload);
void PadToState(const GCPadStatus& pad, GCState& state);
@@ -116,11 +118,8 @@ private:
/// Stop scanning for the adapter
void StopScanThread();
/// Returns true if there is a device connected to port
bool DeviceConnected(int port);
/// Resets status of device connected to port
void ResetDeviceType(int port);
void ResetDeviceType(std::size_t port);
/// Returns true if we successfully gain access to GC Adapter
bool CheckDeviceAccess(libusb_device* device);
@@ -156,6 +155,8 @@ private:
std::array<Common::SPSCQueue<GCPadStatus>, 4> pad_queue;
std::array<GCState, 4> state;
std::array<bool, 4> get_origin;
std::array<GCPadStatus, 4> origin_status;
};
} // namespace GCAdapter

View File

@@ -6,6 +6,7 @@
#include <list>
#include <mutex>
#include <utility>
#include "common/assert.h"
#include "common/threadsafe_queue.h"
#include "input_common/gcadapter/gc_adapter.h"
#include "input_common/gcadapter/gc_poller.h"
@@ -20,7 +21,10 @@ public:
~GCButton() override;
bool GetStatus() const override {
return gcadapter->GetPadState()[port].buttons.at(button);
if (gcadapter->DeviceConnected(port)) {
return gcadapter->GetPadState()[port].buttons.at(button);
}
return false;
}
private:
@@ -34,22 +38,20 @@ public:
explicit GCAxisButton(int port_, int axis_, float threshold_, bool trigger_if_greater_,
GCAdapter::Adapter* adapter)
: port(port_), axis(axis_), threshold(threshold_), trigger_if_greater(trigger_if_greater_),
gcadapter(adapter) {
// L/R triggers range is only in positive direction beginning near 0
// 0.0 threshold equates to near half trigger press, but threshold accounts for variability.
if (axis > 3) {
threshold *= -0.5;
}
}
gcadapter(adapter), origin_value(adapter->GetOriginValue(port_, axis_)) {}
bool GetStatus() const override {
const float axis_value = (gcadapter->GetPadState()[port].axes.at(axis) - 128.0f) / 128.0f;
if (trigger_if_greater) {
// TODO: Might be worthwile to set a slider for the trigger threshold. It is currently
// always set to 0.5 in configure_input_player.cpp ZL/ZR HandleClick
return axis_value > threshold;
if (gcadapter->DeviceConnected(port)) {
const float current_axis_value = gcadapter->GetPadState()[port].axes.at(axis);
const float axis_value = (current_axis_value - origin_value) / 128.0f;
if (trigger_if_greater) {
// TODO: Might be worthwile to set a slider for the trigger threshold. It is
// currently always set to 0.5 in configure_input_player.cpp ZL/ZR HandleClick
return axis_value > threshold;
}
return axis_value < -threshold;
}
return axis_value < -threshold;
return false;
}
private:
@@ -58,6 +60,7 @@ private:
float threshold;
bool trigger_if_greater;
GCAdapter::Adapter* gcadapter;
const float origin_value;
};
GCButtonFactory::GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_)
@@ -94,9 +97,12 @@ std::unique_ptr<Input::ButtonDevice> GCButtonFactory::Create(const Common::Param
return std::make_unique<GCAxisButton>(port, axis, threshold, trigger_if_greater,
adapter.get());
}
UNREACHABLE();
return nullptr;
}
Common::ParamPackage GCButtonFactory::GetNextInput() {
Common::ParamPackage GCButtonFactory::GetNextInput() const {
Common::ParamPackage params;
GCAdapter::GCPadStatus pad;
auto& queue = adapter->GetPadQueue();
@@ -144,14 +150,20 @@ void GCButtonFactory::EndConfiguration() {
class GCAnalog final : public Input::AnalogDevice {
public:
GCAnalog(int port_, int axis_x_, int axis_y_, float deadzone_, GCAdapter::Adapter* adapter)
: port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter) {}
: port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter),
origin_value_x(adapter->GetOriginValue(port_, axis_x_)),
origin_value_y(adapter->GetOriginValue(port_, axis_y_)) {}
float GetAxis(int axis) const {
std::lock_guard lock{mutex};
// division is not by a perfect 128 to account for some variance in center location
// e.g. my device idled at 131 in X, 120 in Y, and full range of motion was in range
// [20-230]
return (gcadapter->GetPadState()[port].axes.at(axis) - 128.0f) / 95.0f;
if (gcadapter->DeviceConnected(port)) {
std::lock_guard lock{mutex};
const auto origin_value = axis % 2 == 0 ? origin_value_x : origin_value_y;
// division is not by a perfect 128 to account for some variance in center location
// e.g. my device idled at 131 in X, 120 in Y, and full range of motion was in range
// [20-230]
return (gcadapter->GetPadState()[port].axes.at(axis) - origin_value) / 95.0f;
}
return 0.0f;
}
std::pair<float, float> GetAnalog(int axis_x, int axis_y) const {
@@ -201,8 +213,10 @@ private:
const int axis_x;
const int axis_y;
const float deadzone;
mutable std::mutex mutex;
GCAdapter::Adapter* gcadapter;
const float origin_value_x;
const float origin_value_y;
mutable std::mutex mutex;
};
/// An analog device factory that creates analog devices from GC Adapter
@@ -249,7 +263,7 @@ Common::ParamPackage GCAnalogFactory::GetNextInput() {
const u8 axis = static_cast<u8>(pad.axis);
if (analog_x_axis == -1) {
analog_x_axis = axis;
controller_number = port;
controller_number = static_cast<int>(port);
} else if (analog_y_axis == -1 && analog_x_axis != axis && controller_number == port) {
analog_y_axis = axis;
}

View File

@@ -25,7 +25,7 @@ public:
*/
std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override;
Common::ParamPackage GetNextInput();
Common::ParamPackage GetNextInput() const;
/// For device input configuration/polling
void BeginConfiguration();

View File

@@ -4,7 +4,6 @@
#include <memory>
#include <thread>
#include <libusb.h>
#include "common/param_package.h"
#include "input_common/analog_from_button.h"
#include "input_common/gcadapter/gc_adapter.h"

View File

@@ -234,7 +234,7 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
std::function<void(Status)> status_callback,
std::function<void(u16, u16, u16, u16)> data_callback) {
std::thread([=] {
std::thread([=, this] {
constexpr u16 CALIBRATION_THRESHOLD = 100;
u16 min_x{UINT16_MAX};

View File

@@ -98,6 +98,8 @@ add_library(video_core STATIC
sampler_cache.cpp
sampler_cache.h
shader_cache.h
shader_notify.cpp
shader_notify.h
shader/decode/arithmetic.cpp
shader/decode/arithmetic_immediate.cpp
shader/decode/bfe.cpp
@@ -128,6 +130,8 @@ add_library(video_core STATIC
shader/decode/other.cpp
shader/ast.cpp
shader/ast.h
shader/async_shaders.cpp
shader/async_shaders.h
shader/compiler_settings.cpp
shader/compiler_settings.h
shader/control_flow.cpp

View File

@@ -14,50 +14,45 @@
namespace Tegra::Engines {
using namespace Texture;
MaxwellDMA::MaxwellDMA(Core::System& system, MemoryManager& memory_manager)
: system{system}, memory_manager{memory_manager} {}
void MaxwellDMA::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
ASSERT_MSG(method < Regs::NUM_REGS,
"Invalid MaxwellDMA register, increase the size of the Regs structure");
ASSERT_MSG(method < NUM_REGS, "Invalid MaxwellDMA register");
regs.reg_array[method] = method_argument;
#define MAXWELLDMA_REG_INDEX(field_name) \
(offsetof(Tegra::Engines::MaxwellDMA::Regs, field_name) / sizeof(u32))
switch (method) {
case MAXWELLDMA_REG_INDEX(exec): {
HandleCopy();
break;
if (method == offsetof(Regs, launch_dma) / sizeof(u32)) {
Launch();
}
}
#undef MAXWELLDMA_REG_INDEX
}
void MaxwellDMA::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
u32 methods_pending) {
for (std::size_t i = 0; i < amount; i++) {
for (size_t i = 0; i < amount; ++i) {
CallMethod(method, base_start[i], methods_pending - static_cast<u32>(i) <= 1);
}
}
void MaxwellDMA::HandleCopy() {
LOG_TRACE(HW_GPU, "Requested a DMA copy");
const GPUVAddr source = regs.src_address.Address();
const GPUVAddr dest = regs.dst_address.Address();
void MaxwellDMA::Launch() {
LOG_TRACE(Render_OpenGL, "DMA copy 0x{:x} -> 0x{:x}", static_cast<GPUVAddr>(regs.offset_in),
static_cast<GPUVAddr>(regs.offset_out));
// TODO(Subv): Perform more research and implement all features of this engine.
ASSERT(regs.exec.enable_swizzle == 0);
ASSERT(regs.exec.query_mode == Regs::QueryMode::None);
ASSERT(regs.exec.query_intr == Regs::QueryIntr::None);
ASSERT(regs.exec.copy_mode == Regs::CopyMode::Unk2);
ASSERT(regs.dst_params.pos_x == 0);
ASSERT(regs.dst_params.pos_y == 0);
const LaunchDMA& launch = regs.launch_dma;
ASSERT(launch.remap_enable == 0);
ASSERT(launch.semaphore_type == LaunchDMA::SemaphoreType::NONE);
ASSERT(launch.interrupt_type == LaunchDMA::InterruptType::NONE);
ASSERT(launch.data_transfer_type == LaunchDMA::DataTransferType::NON_PIPELINED);
ASSERT(regs.dst_params.origin.x == 0);
ASSERT(regs.dst_params.origin.y == 0);
if (!regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
const bool is_src_pitch = launch.src_memory_layout == LaunchDMA::MemoryLayout::PITCH;
const bool is_dst_pitch = launch.dst_memory_layout == LaunchDMA::MemoryLayout::PITCH;
if (!is_src_pitch && !is_dst_pitch) {
// If both the source and the destination are in block layout, assert.
UNREACHABLE_MSG("Tiled->Tiled DMA transfers are not yet implemented");
return;
@@ -66,144 +61,161 @@ void MaxwellDMA::HandleCopy() {
// All copies here update the main memory, so mark all rasterizer states as invalid.
system.GPU().Maxwell3D().OnMemoryWrite();
if (regs.exec.is_dst_linear && regs.exec.is_src_linear) {
// When the enable_2d bit is disabled, the copy is performed as if we were copying a 1D
// buffer of length `x_count`, otherwise we copy a 2D image of dimensions (x_count,
// y_count).
if (!regs.exec.enable_2d) {
memory_manager.CopyBlock(dest, source, regs.x_count);
return;
}
// If both the source and the destination are in linear layout, perform a line-by-line
// copy. We're going to take a subrect of size (x_count, y_count) from the source
// rectangle. There is no need to manually flush/invalidate the regions because
// CopyBlock does that for us.
for (u32 line = 0; line < regs.y_count; ++line) {
const GPUVAddr source_line = source + line * regs.src_pitch;
const GPUVAddr dest_line = dest + line * regs.dst_pitch;
memory_manager.CopyBlock(dest_line, source_line, regs.x_count);
}
return;
}
ASSERT(regs.exec.enable_2d == 1);
if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
ASSERT(regs.src_params.BlockDepth() == 0);
// Optimized path for micro copies.
if (regs.dst_pitch * regs.y_count < Texture::GetGOBSize() && regs.dst_pitch <= 64) {
const u32 bytes_per_pixel = regs.dst_pitch / regs.x_count;
const std::size_t src_size = Texture::GetGOBSize();
const std::size_t dst_size = regs.dst_pitch * regs.y_count;
u32 pos_x = regs.src_params.pos_x;
u32 pos_y = regs.src_params.pos_y;
const u64 offset =
Texture::GetGOBOffset(regs.src_params.size_x, regs.src_params.size_y, pos_x, pos_y,
regs.src_params.BlockDepth(), bytes_per_pixel);
const u32 x_in_gob = 64 / bytes_per_pixel;
pos_x = pos_x % x_in_gob;
pos_y = pos_y % 8;
if (read_buffer.size() < src_size) {
read_buffer.resize(src_size);
}
if (write_buffer.size() < dst_size) {
write_buffer.resize(dst_size);
}
if (Settings::IsGPULevelExtreme()) {
memory_manager.ReadBlock(source + offset, read_buffer.data(), src_size);
memory_manager.ReadBlock(dest, write_buffer.data(), dst_size);
} else {
memory_manager.ReadBlockUnsafe(source + offset, read_buffer.data(), src_size);
memory_manager.ReadBlockUnsafe(dest, write_buffer.data(), dst_size);
}
Texture::UnswizzleSubrect(regs.x_count, regs.y_count, regs.dst_pitch,
regs.src_params.size_x, bytes_per_pixel, read_buffer.data(),
write_buffer.data(), regs.src_params.BlockHeight(), pos_x,
pos_y);
memory_manager.WriteBlock(dest, write_buffer.data(), dst_size);
return;
}
// If the input is tiled and the output is linear, deswizzle the input and copy it over.
const u32 bytes_per_pixel = regs.dst_pitch / regs.x_count;
const std::size_t src_size = Texture::CalculateSize(
true, bytes_per_pixel, regs.src_params.size_x, regs.src_params.size_y,
regs.src_params.size_z, regs.src_params.BlockHeight(), regs.src_params.BlockDepth());
const std::size_t src_layer_size = Texture::CalculateSize(
true, bytes_per_pixel, regs.src_params.size_x, regs.src_params.size_y, 1,
regs.src_params.BlockHeight(), regs.src_params.BlockDepth());
const std::size_t dst_size = regs.dst_pitch * regs.y_count;
if (read_buffer.size() < src_size) {
read_buffer.resize(src_size);
}
if (write_buffer.size() < dst_size) {
write_buffer.resize(dst_size);
}
if (Settings::IsGPULevelExtreme()) {
memory_manager.ReadBlock(source, read_buffer.data(), src_size);
memory_manager.ReadBlock(dest, write_buffer.data(), dst_size);
} else {
memory_manager.ReadBlockUnsafe(source, read_buffer.data(), src_size);
memory_manager.ReadBlockUnsafe(dest, write_buffer.data(), dst_size);
}
Texture::UnswizzleSubrect(
regs.x_count, regs.y_count, regs.dst_pitch, regs.src_params.size_x, bytes_per_pixel,
read_buffer.data() + src_layer_size * regs.src_params.pos_z, write_buffer.data(),
regs.src_params.BlockHeight(), regs.src_params.pos_x, regs.src_params.pos_y);
memory_manager.WriteBlock(dest, write_buffer.data(), dst_size);
if (is_src_pitch && is_dst_pitch) {
CopyPitchToPitch();
} else {
ASSERT(regs.dst_params.BlockDepth() == 0);
ASSERT(launch.multi_line_enable == 1);
const u32 bytes_per_pixel = regs.src_pitch / regs.x_count;
const std::size_t dst_size = Texture::CalculateSize(
true, bytes_per_pixel, regs.dst_params.size_x, regs.dst_params.size_y,
regs.dst_params.size_z, regs.dst_params.BlockHeight(), regs.dst_params.BlockDepth());
const std::size_t dst_layer_size = Texture::CalculateSize(
true, bytes_per_pixel, regs.dst_params.size_x, regs.dst_params.size_y, 1,
regs.dst_params.BlockHeight(), regs.dst_params.BlockDepth());
const std::size_t src_size = regs.src_pitch * regs.y_count;
if (read_buffer.size() < src_size) {
read_buffer.resize(src_size);
}
if (write_buffer.size() < dst_size) {
write_buffer.resize(dst_size);
}
if (Settings::IsGPULevelExtreme()) {
memory_manager.ReadBlock(source, read_buffer.data(), src_size);
memory_manager.ReadBlock(dest, write_buffer.data(), dst_size);
if (!is_src_pitch && is_dst_pitch) {
CopyBlockLinearToPitch();
} else {
memory_manager.ReadBlockUnsafe(source, read_buffer.data(), src_size);
memory_manager.ReadBlockUnsafe(dest, write_buffer.data(), dst_size);
CopyPitchToBlockLinear();
}
// If the input is linear and the output is tiled, swizzle the input and copy it over.
Texture::SwizzleSubrect(
regs.x_count, regs.y_count, regs.src_pitch, regs.dst_params.size_x, bytes_per_pixel,
write_buffer.data() + dst_layer_size * regs.dst_params.pos_z, read_buffer.data(),
regs.dst_params.BlockHeight(), regs.dst_params.pos_x, regs.dst_params.pos_y);
memory_manager.WriteBlock(dest, write_buffer.data(), dst_size);
}
}
void MaxwellDMA::CopyPitchToPitch() {
// When `multi_line_enable` bit is disabled the copy is performed as if we were copying a 1D
// buffer of length `line_length_in`.
// Otherwise we copy a 2D image of dimensions (line_length_in, line_count).
if (!regs.launch_dma.multi_line_enable) {
memory_manager.CopyBlock(regs.offset_out, regs.offset_in, regs.line_length_in);
return;
}
// Perform a line-by-line copy.
// We're going to take a subrect of size (line_length_in, line_count) from the source rectangle.
// There is no need to manually flush/invalidate the regions because CopyBlock does that for us.
for (u32 line = 0; line < regs.line_count; ++line) {
const GPUVAddr source_line = regs.offset_in + static_cast<size_t>(line) * regs.pitch_in;
const GPUVAddr dest_line = regs.offset_out + static_cast<size_t>(line) * regs.pitch_out;
memory_manager.CopyBlock(dest_line, source_line, regs.line_length_in);
}
}
void MaxwellDMA::CopyBlockLinearToPitch() {
ASSERT(regs.src_params.block_size.depth == 0);
// Optimized path for micro copies.
const size_t dst_size = static_cast<size_t>(regs.pitch_out) * regs.line_count;
if (dst_size < GOB_SIZE && regs.pitch_out <= GOB_SIZE_X) {
FastCopyBlockLinearToPitch();
return;
}
// Deswizzle the input and copy it over.
const u32 bytes_per_pixel = regs.pitch_out / regs.line_length_in;
const Parameters& src_params = regs.src_params;
const u32 width = src_params.width;
const u32 height = src_params.height;
const u32 depth = src_params.depth;
const u32 block_height = src_params.block_size.height;
const u32 block_depth = src_params.block_size.depth;
const size_t src_size =
CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth);
const size_t src_layer_size =
CalculateSize(true, bytes_per_pixel, width, height, 1, block_height, block_depth);
if (read_buffer.size() < src_size) {
read_buffer.resize(src_size);
}
if (write_buffer.size() < dst_size) {
write_buffer.resize(dst_size);
}
if (Settings::IsGPULevelExtreme()) {
memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size);
memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
} else {
memory_manager.ReadBlockUnsafe(regs.offset_in, read_buffer.data(), src_size);
memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size);
}
UnswizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_out, width, bytes_per_pixel,
read_buffer.data() + src_layer_size * src_params.layer, write_buffer.data(),
block_height, src_params.origin.x, src_params.origin.y);
memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size);
}
void MaxwellDMA::CopyPitchToBlockLinear() {
const auto& dst_params = regs.dst_params;
const u32 bytes_per_pixel = regs.pitch_in / regs.line_length_in;
const u32 width = dst_params.width;
const u32 height = dst_params.height;
const u32 depth = dst_params.depth;
const u32 block_height = dst_params.block_size.height;
const u32 block_depth = dst_params.block_size.depth;
const size_t dst_size =
CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth);
const size_t dst_layer_size =
CalculateSize(true, bytes_per_pixel, width, height, 1, block_height, block_depth);
const size_t src_size = static_cast<size_t>(regs.pitch_in) * regs.line_count;
if (read_buffer.size() < src_size) {
read_buffer.resize(src_size);
}
if (write_buffer.size() < dst_size) {
write_buffer.resize(dst_size);
}
if (Settings::IsGPULevelExtreme()) {
memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size);
memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
} else {
memory_manager.ReadBlockUnsafe(regs.offset_in, read_buffer.data(), src_size);
memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size);
}
// If the input is linear and the output is tiled, swizzle the input and copy it over.
if (regs.dst_params.block_size.depth > 0) {
ASSERT(dst_params.layer == 0);
SwizzleSliceToVoxel(regs.line_length_in, regs.line_count, regs.pitch_in, width, height,
bytes_per_pixel, block_height, block_depth, dst_params.origin.x,
dst_params.origin.y, write_buffer.data(), read_buffer.data());
} else {
SwizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_in, width, bytes_per_pixel,
write_buffer.data() + dst_layer_size * dst_params.layer, read_buffer.data(),
block_height, dst_params.origin.x, dst_params.origin.y);
}
memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size);
}
void MaxwellDMA::FastCopyBlockLinearToPitch() {
const u32 bytes_per_pixel = regs.pitch_out / regs.line_length_in;
const size_t src_size = GOB_SIZE;
const size_t dst_size = static_cast<size_t>(regs.pitch_out) * regs.line_count;
u32 pos_x = regs.src_params.origin.x;
u32 pos_y = regs.src_params.origin.y;
const u64 offset = GetGOBOffset(regs.src_params.width, regs.src_params.height, pos_x, pos_y,
regs.src_params.block_size.height, bytes_per_pixel);
const u32 x_in_gob = 64 / bytes_per_pixel;
pos_x = pos_x % x_in_gob;
pos_y = pos_y % 8;
if (read_buffer.size() < src_size) {
read_buffer.resize(src_size);
}
if (write_buffer.size() < dst_size) {
write_buffer.resize(dst_size);
}
if (Settings::IsGPULevelExtreme()) {
memory_manager.ReadBlock(regs.offset_in + offset, read_buffer.data(), src_size);
memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
} else {
memory_manager.ReadBlockUnsafe(regs.offset_in + offset, read_buffer.data(), src_size);
memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size);
}
UnswizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_out, regs.src_params.width,
bytes_per_pixel, read_buffer.data(), write_buffer.data(),
regs.src_params.block_size.height, pos_x, pos_y);
memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size);
}
} // namespace Tegra::Engines

View File

@@ -24,12 +24,167 @@ class MemoryManager;
namespace Tegra::Engines {
/**
* This Engine is known as GK104_Copy. Documentation can be found in:
* This engine is known as gk104_copy. Documentation can be found in:
* https://github.com/NVIDIA/open-gpu-doc/blob/master/classes/dma-copy/clb0b5.h
* https://github.com/envytools/envytools/blob/master/rnndb/fifo/gk104_copy.xml
*/
class MaxwellDMA final : public EngineInterface {
public:
struct PackedGPUVAddr {
u32 upper;
u32 lower;
constexpr operator GPUVAddr() const noexcept {
return (static_cast<GPUVAddr>(upper & 0xff) << 32) | lower;
}
};
union BlockSize {
BitField<0, 4, u32> width;
BitField<4, 4, u32> height;
BitField<8, 4, u32> depth;
BitField<12, 4, u32> gob_height;
};
static_assert(sizeof(BlockSize) == 4);
union Origin {
BitField<0, 16, u32> x;
BitField<16, 16, u32> y;
};
static_assert(sizeof(Origin) == 4);
struct Parameters {
BlockSize block_size;
u32 width;
u32 height;
u32 depth;
u32 layer;
Origin origin;
};
static_assert(sizeof(Parameters) == 24);
struct Semaphore {
PackedGPUVAddr address;
u32 payload;
};
static_assert(sizeof(Semaphore) == 12);
struct RenderEnable {
enum class Mode : u32 {
FALSE = 0,
TRUE = 1,
CONDITIONAL = 2,
RENDER_IF_EQUAL = 3,
RENDER_IF_NOT_EQUAL = 4,
};
PackedGPUVAddr address;
BitField<0, 3, Mode> mode;
};
static_assert(sizeof(RenderEnable) == 12);
enum class PhysModeTarget : u32 {
LOCAL_FB = 0,
COHERENT_SYSMEM = 1,
NONCOHERENT_SYSMEM = 2,
};
using PhysMode = BitField<0, 2, PhysModeTarget>;
union LaunchDMA {
enum class DataTransferType : u32 {
NONE = 0,
PIPELINED = 1,
NON_PIPELINED = 2,
};
enum class SemaphoreType : u32 {
NONE = 0,
RELEASE_ONE_WORD_SEMAPHORE = 1,
RELEASE_FOUR_WORD_SEMAPHORE = 2,
};
enum class InterruptType : u32 {
NONE = 0,
BLOCKING = 1,
NON_BLOCKING = 2,
};
enum class MemoryLayout : u32 {
BLOCKLINEAR = 0,
PITCH = 1,
};
enum class Type : u32 {
VIRTUAL = 0,
PHYSICAL = 1,
};
enum class SemaphoreReduction : u32 {
IMIN = 0,
IMAX = 1,
IXOR = 2,
IAND = 3,
IOR = 4,
IADD = 5,
INC = 6,
DEC = 7,
FADD = 0xA,
};
enum class SemaphoreReductionSign : u32 {
SIGNED = 0,
UNSIGNED = 1,
};
enum class BypassL2 : u32 {
USE_PTE_SETTING = 0,
FORCE_VOLATILE = 1,
};
BitField<0, 2, DataTransferType> data_transfer_type;
BitField<2, 1, u32> flush_enable;
BitField<3, 2, SemaphoreType> semaphore_type;
BitField<5, 2, InterruptType> interrupt_type;
BitField<7, 1, MemoryLayout> src_memory_layout;
BitField<8, 1, MemoryLayout> dst_memory_layout;
BitField<9, 1, u32> multi_line_enable;
BitField<10, 1, u32> remap_enable;
BitField<11, 1, u32> rmwdisable;
BitField<12, 1, Type> src_type;
BitField<13, 1, Type> dst_type;
BitField<14, 4, SemaphoreReduction> semaphore_reduction;
BitField<18, 1, SemaphoreReductionSign> semaphore_reduction_sign;
BitField<19, 1, u32> reduction_enable;
BitField<20, 1, BypassL2> bypass_l2;
};
static_assert(sizeof(LaunchDMA) == 4);
struct RemapConst {
enum Swizzle : u32 {
SRC_X = 0,
SRC_Y = 1,
SRC_Z = 2,
SRC_W = 3,
CONST_A = 4,
CONST_B = 5,
NO_WRITE = 6,
};
PackedGPUVAddr address;
union {
BitField<0, 3, Swizzle> dst_x;
BitField<4, 3, Swizzle> dst_y;
BitField<8, 3, Swizzle> dst_z;
BitField<12, 3, Swizzle> dst_w;
BitField<16, 2, u32> component_size_minus_one;
BitField<20, 2, u32> num_src_components_minus_one;
BitField<24, 2, u32> num_dst_components_minus_one;
};
};
static_assert(sizeof(RemapConst) == 12);
explicit MaxwellDMA(Core::System& system, MemoryManager& memory_manager);
~MaxwellDMA() = default;
@@ -40,144 +195,19 @@ public:
void CallMultiMethod(u32 method, const u32* base_start, u32 amount,
u32 methods_pending) override;
struct Regs {
static constexpr std::size_t NUM_REGS = 0x1D6;
struct Parameters {
union {
BitField<0, 4, u32> block_depth;
BitField<4, 4, u32> block_height;
BitField<8, 4, u32> block_width;
};
u32 size_x;
u32 size_y;
u32 size_z;
u32 pos_z;
union {
BitField<0, 16, u32> pos_x;
BitField<16, 16, u32> pos_y;
};
u32 BlockHeight() const {
return block_height.Value();
}
u32 BlockDepth() const {
return block_depth.Value();
}
};
static_assert(sizeof(Parameters) == 24, "Parameters has wrong size");
enum class ComponentMode : u32 {
Src0 = 0,
Src1 = 1,
Src2 = 2,
Src3 = 3,
Const0 = 4,
Const1 = 5,
Zero = 6,
};
enum class CopyMode : u32 {
None = 0,
Unk1 = 1,
Unk2 = 2,
};
enum class QueryMode : u32 {
None = 0,
Short = 1,
Long = 2,
};
enum class QueryIntr : u32 {
None = 0,
Block = 1,
NonBlock = 2,
};
union {
struct {
INSERT_UNION_PADDING_WORDS(0xC0);
struct {
union {
BitField<0, 2, CopyMode> copy_mode;
BitField<2, 1, u32> flush;
BitField<3, 2, QueryMode> query_mode;
BitField<5, 2, QueryIntr> query_intr;
BitField<7, 1, u32> is_src_linear;
BitField<8, 1, u32> is_dst_linear;
BitField<9, 1, u32> enable_2d;
BitField<10, 1, u32> enable_swizzle;
};
} exec;
INSERT_UNION_PADDING_WORDS(0x3F);
struct {
u32 address_high;
u32 address_low;
GPUVAddr Address() const {
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
address_low);
}
} src_address;
struct {
u32 address_high;
u32 address_low;
GPUVAddr Address() const {
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
address_low);
}
} dst_address;
u32 src_pitch;
u32 dst_pitch;
u32 x_count;
u32 y_count;
INSERT_UNION_PADDING_WORDS(0xB8);
u32 const0;
u32 const1;
union {
BitField<0, 4, ComponentMode> component0;
BitField<4, 4, ComponentMode> component1;
BitField<8, 4, ComponentMode> component2;
BitField<12, 4, ComponentMode> component3;
BitField<16, 2, u32> component_size;
BitField<20, 3, u32> src_num_components;
BitField<24, 3, u32> dst_num_components;
u32 SrcBytePerPixel() const {
return src_num_components.Value() * component_size.Value();
}
u32 DstBytePerPixel() const {
return dst_num_components.Value() * component_size.Value();
}
} swizzle_config;
Parameters dst_params;
INSERT_UNION_PADDING_WORDS(1);
Parameters src_params;
INSERT_UNION_PADDING_WORDS(0x13);
};
std::array<u32, NUM_REGS> reg_array;
};
} regs{};
private:
/// Performs the copy from the source buffer to the destination buffer as configured in the
/// registers.
void Launch();
void CopyPitchToPitch();
void CopyBlockLinearToPitch();
void CopyPitchToBlockLinear();
void FastCopyBlockLinearToPitch();
Core::System& system;
MemoryManager& memory_manager;
@@ -185,28 +215,58 @@ private:
std::vector<u8> read_buffer;
std::vector<u8> write_buffer;
/// Performs the copy from the source buffer to the destination buffer as configured in the
/// registers.
void HandleCopy();
};
static constexpr std::size_t NUM_REGS = 0x800;
struct Regs {
union {
struct {
u32 reserved[0x40];
u32 nop;
u32 reserved01[0xf];
u32 pm_trigger;
u32 reserved02[0x3f];
Semaphore semaphore;
u32 reserved03[0x2];
RenderEnable render_enable;
PhysMode src_phys_mode;
PhysMode dst_phys_mode;
u32 reserved04[0x26];
LaunchDMA launch_dma;
u32 reserved05[0x3f];
PackedGPUVAddr offset_in;
PackedGPUVAddr offset_out;
u32 pitch_in;
u32 pitch_out;
u32 line_length_in;
u32 line_count;
u32 reserved06[0xb8];
RemapConst remap_const;
Parameters dst_params;
u32 reserved07[0x1];
Parameters src_params;
u32 reserved08[0x275];
u32 pm_trigger_end;
u32 reserved09[0x3ba];
};
std::array<u32, NUM_REGS> reg_array;
};
} regs{};
#define ASSERT_REG_POSITION(field_name, position) \
static_assert(offsetof(MaxwellDMA::Regs, field_name) == position * 4, \
"Field " #field_name " has invalid position")
ASSERT_REG_POSITION(exec, 0xC0);
ASSERT_REG_POSITION(src_address, 0x100);
ASSERT_REG_POSITION(dst_address, 0x102);
ASSERT_REG_POSITION(src_pitch, 0x104);
ASSERT_REG_POSITION(dst_pitch, 0x105);
ASSERT_REG_POSITION(x_count, 0x106);
ASSERT_REG_POSITION(y_count, 0x107);
ASSERT_REG_POSITION(const0, 0x1C0);
ASSERT_REG_POSITION(const1, 0x1C1);
ASSERT_REG_POSITION(swizzle_config, 0x1C2);
ASSERT_REG_POSITION(dst_params, 0x1C3);
ASSERT_REG_POSITION(src_params, 0x1CA);
ASSERT_REG_POSITION(launch_dma, 0xC0);
ASSERT_REG_POSITION(offset_in, 0x100);
ASSERT_REG_POSITION(offset_out, 0x102);
ASSERT_REG_POSITION(pitch_in, 0x104);
ASSERT_REG_POSITION(pitch_out, 0x105);
ASSERT_REG_POSITION(line_length_in, 0x106);
ASSERT_REG_POSITION(line_count, 0x107);
ASSERT_REG_POSITION(remap_const, 0x1C0);
ASSERT_REG_POSITION(dst_params, 0x1C3);
ASSERT_REG_POSITION(src_params, 0x1CA);
#undef ASSERT_REG_POSITION
};
} // namespace Tegra::Engines

View File

@@ -20,6 +20,7 @@
#include "video_core/gpu.h"
#include "video_core/memory_manager.h"
#include "video_core/renderer_base.h"
#include "video_core/shader_notify.h"
#include "video_core/video_core.h"
namespace Tegra {
@@ -36,6 +37,7 @@ GPU::GPU(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& render
kepler_compute = std::make_unique<Engines::KeplerCompute>(system, rasterizer, *memory_manager);
maxwell_dma = std::make_unique<Engines::MaxwellDMA>(system, *memory_manager);
kepler_memory = std::make_unique<Engines::KeplerMemory>(system, *memory_manager);
shader_notify = std::make_unique<VideoCore::ShaderNotify>();
}
GPU::~GPU() = default;

View File

@@ -33,6 +33,7 @@ class System;
namespace VideoCore {
class RendererBase;
class ShaderNotify;
} // namespace VideoCore
namespace Tegra {
@@ -207,6 +208,14 @@ public:
return *renderer;
}
VideoCore::ShaderNotify& ShaderNotify() {
return *shader_notify;
}
const VideoCore::ShaderNotify& ShaderNotify() const {
return *shader_notify;
}
// Waits for the GPU to finish working
virtual void WaitIdle() const = 0;
@@ -347,6 +356,8 @@ private:
std::unique_ptr<Engines::MaxwellDMA> maxwell_dma;
/// Inline memory engine
std::unique_ptr<Engines::KeplerMemory> kepler_memory;
/// Shader build notifier
std::unique_ptr<VideoCore::ShaderNotify> shader_notify;
std::array<std::atomic<u32>, Service::Nvidia::MaxSyncPoints> syncpoints{};

View File

@@ -103,8 +103,9 @@ public:
virtual ~CachedMacro() = default;
/**
* Executes the macro code with the specified input parameters.
* @param code The macro byte code to execute
*
* @param parameters The parameters of the macro
* @param method The method to execute
*/
virtual void Execute(const std::vector<u32>& parameters, u32 method) = 0;
};

View File

@@ -17,8 +17,7 @@ static void HLE_771BB18C62444DA0(Engines::Maxwell3D& maxwell3d,
const u32 instance_count = parameters[2] & maxwell3d.GetRegisterValue(0xD1B);
maxwell3d.regs.draw.topology.Assign(
static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0] &
~(0x3ffffff << 26)));
static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0] & 0x3ffffff));
maxwell3d.regs.vb_base_instance = parameters[5];
maxwell3d.mme_draw.instance_count = instance_count;
maxwell3d.regs.vb_element_base = parameters[3];

View File

@@ -233,6 +233,8 @@ Device::Device()
GLAD_GL_NV_gpu_program5 && GLAD_GL_NV_compute_program5 &&
GLAD_GL_NV_transform_feedback && GLAD_GL_NV_transform_feedback2;
use_asynchronous_shaders = Settings::values.use_asynchronous_shaders.GetValue();
LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi);
LOG_INFO(Render_OpenGL, "Renderer_ComponentIndexingBug: {}", has_component_indexing_bug);
LOG_INFO(Render_OpenGL, "Renderer_PreciseBug: {}", has_precise_bug);

View File

@@ -104,6 +104,10 @@ public:
return use_assembly_shaders;
}
bool UseAsynchronousShaders() const {
return use_asynchronous_shaders;
}
private:
static bool TestVariableAoffi();
static bool TestPreciseBug();
@@ -127,6 +131,7 @@ private:
bool has_fast_buffer_sub_data{};
bool has_nv_viewport_array2{};
bool use_assembly_shaders{};
bool use_asynchronous_shaders{};
};
} // namespace OpenGL

View File

@@ -149,7 +149,8 @@ RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWind
shader_cache{*this, system, emu_window, device}, query_cache{system, *this},
buffer_cache{*this, system, device, STREAM_BUFFER_SIZE},
fence_manager{system, *this, texture_cache, buffer_cache, query_cache}, system{system},
screen_info{info}, program_manager{program_manager}, state_tracker{state_tracker} {
screen_info{info}, program_manager{program_manager}, state_tracker{state_tracker},
async_shaders{emu_window} {
CheckExtensions();
unified_uniform_buffer.Create();
@@ -162,6 +163,23 @@ RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWind
nullptr, 0);
}
}
if (device.UseAsynchronousShaders()) {
// Max worker threads we should allow
constexpr auto MAX_THREADS = 2u;
// Amount of threads we should reserve for other parts of yuzu
constexpr auto RESERVED_THREADS = 6u;
// Get the amount of threads we can use(this can return zero)
const auto cpu_thread_count =
std::max(RESERVED_THREADS, std::thread::hardware_concurrency());
// Deduce how many "extra" threads we have to use.
const auto max_threads_unused = cpu_thread_count - RESERVED_THREADS;
// Always allow at least 1 thread regardless of our settings
const auto max_worker_count = std::max(1u, max_threads_unused);
// Don't use more than MAX_THREADS
const auto worker_count = std::min(max_worker_count, MAX_THREADS);
async_shaders.AllocateWorkers(worker_count);
}
}
RasterizerOpenGL::~RasterizerOpenGL() {
@@ -336,7 +354,7 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
continue;
}
Shader* const shader = shader_cache.GetStageProgram(program);
Shader* shader = shader_cache.GetStageProgram(program, async_shaders);
if (device.UseAssemblyShaders()) {
// Check for ARB limitation. We only have 16 SSBOs per context state. To workaround this
@@ -353,7 +371,7 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
SetupDrawTextures(stage, shader);
SetupDrawImages(stage, shader);
const GLuint program_handle = shader->GetHandle();
const GLuint program_handle = shader->IsBuilt() ? shader->GetHandle() : 0;
switch (program) {
case Maxwell::ShaderProgram::VertexA:
case Maxwell::ShaderProgram::VertexB:

View File

@@ -33,6 +33,7 @@
#include "video_core/renderer_opengl/gl_state_tracker.h"
#include "video_core/renderer_opengl/gl_texture_cache.h"
#include "video_core/renderer_opengl/utils.h"
#include "video_core/shader/async_shaders.h"
#include "video_core/textures/texture.h"
namespace Core {
@@ -91,6 +92,14 @@ public:
return num_queued_commands > 0;
}
VideoCommon::Shader::AsyncShaders& GetAsyncShaders() {
return async_shaders;
}
const VideoCommon::Shader::AsyncShaders& GetAsyncShaders() const {
return async_shaders;
}
private:
/// Configures the color and depth framebuffer states.
void ConfigureFramebuffers();
@@ -242,6 +251,7 @@ private:
ScreenInfo& screen_info;
ProgramManager& program_manager;
StateTracker& state_tracker;
VideoCommon::Shader::AsyncShaders async_shaders;
static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024;

View File

@@ -177,6 +177,12 @@ public:
Release();
}
OGLAssemblyProgram& operator=(OGLAssemblyProgram&& o) noexcept {
Release();
handle = std::exchange(o.handle, 0);
return *this;
}
/// Deletes the internal OpenGL resource
void Release();

View File

@@ -31,6 +31,7 @@
#include "video_core/shader/registry.h"
#include "video_core/shader/shader_ir.h"
#include "video_core/shader_cache.h"
#include "video_core/shader_notify.h"
namespace OpenGL {
@@ -140,9 +141,24 @@ std::shared_ptr<Registry> MakeRegistry(const ShaderDiskCacheEntry& entry) {
return registry;
}
std::unordered_set<GLenum> GetSupportedFormats() {
GLint num_formats;
glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &num_formats);
std::vector<GLint> formats(num_formats);
glGetIntegerv(GL_PROGRAM_BINARY_FORMATS, formats.data());
std::unordered_set<GLenum> supported_formats;
for (const GLint format : formats) {
supported_formats.insert(static_cast<GLenum>(format));
}
return supported_formats;
}
} // Anonymous namespace
ProgramSharedPtr BuildShader(const Device& device, ShaderType shader_type, u64 unique_identifier,
const ShaderIR& ir, const Registry& registry,
bool hint_retrievable = false) {
const ShaderIR& ir, const Registry& registry, bool hint_retrievable) {
const std::string shader_id = MakeShaderID(unique_identifier, shader_type);
LOG_INFO(Render_OpenGL, "{}", shader_id);
@@ -181,30 +197,17 @@ ProgramSharedPtr BuildShader(const Device& device, ShaderType shader_type, u64 u
return program;
}
std::unordered_set<GLenum> GetSupportedFormats() {
GLint num_formats;
glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &num_formats);
std::vector<GLint> formats(num_formats);
glGetIntegerv(GL_PROGRAM_BINARY_FORMATS, formats.data());
std::unordered_set<GLenum> supported_formats;
for (const GLint format : formats) {
supported_formats.insert(static_cast<GLenum>(format));
}
return supported_formats;
}
} // Anonymous namespace
Shader::Shader(std::shared_ptr<VideoCommon::Shader::Registry> registry_, ShaderEntries entries_,
ProgramSharedPtr program_)
: registry{std::move(registry_)}, entries{std::move(entries_)}, program{std::move(program_)} {
ProgramSharedPtr program_, bool is_built)
: registry{std::move(registry_)}, entries{std::move(entries_)}, program{std::move(program_)},
is_built(is_built) {
handle = program->assembly_program.handle;
if (handle == 0) {
handle = program->source_program.handle;
}
ASSERT(handle != 0);
if (is_built) {
ASSERT(handle != 0);
}
}
Shader::~Shader() = default;
@@ -214,42 +217,82 @@ GLuint Shader::GetHandle() const {
return handle;
}
std::unique_ptr<Shader> Shader::CreateStageFromMemory(const ShaderParameters& params,
Maxwell::ShaderProgram program_type,
ProgramCode code, ProgramCode code_b) {
bool Shader::IsBuilt() const {
return is_built;
}
void Shader::AsyncOpenGLBuilt(OGLProgram new_program) {
program->source_program = std::move(new_program);
handle = program->source_program.handle;
is_built = true;
}
void Shader::AsyncGLASMBuilt(OGLAssemblyProgram new_program) {
program->assembly_program = std::move(new_program);
handle = program->assembly_program.handle;
is_built = true;
}
std::unique_ptr<Shader> Shader::CreateStageFromMemory(
const ShaderParameters& params, Maxwell::ShaderProgram program_type, ProgramCode code,
ProgramCode code_b, VideoCommon::Shader::AsyncShaders& async_shaders, VAddr cpu_addr) {
const auto shader_type = GetShaderType(program_type);
const std::size_t size_in_bytes = code.size() * sizeof(u64);
auto registry = std::make_shared<Registry>(shader_type, params.system.GPU().Maxwell3D());
const ShaderIR ir(code, STAGE_MAIN_OFFSET, COMPILER_SETTINGS, *registry);
// TODO(Rodrigo): Handle VertexA shaders
// std::optional<ShaderIR> ir_b;
// if (!code_b.empty()) {
// ir_b.emplace(code_b, STAGE_MAIN_OFFSET);
// }
auto program = BuildShader(params.device, shader_type, params.unique_identifier, ir, *registry);
auto& gpu = params.system.GPU();
gpu.ShaderNotify().MarkSharderBuilding();
ShaderDiskCacheEntry entry;
entry.type = shader_type;
entry.code = std::move(code);
entry.code_b = std::move(code_b);
entry.unique_identifier = params.unique_identifier;
entry.bound_buffer = registry->GetBoundBuffer();
entry.graphics_info = registry->GetGraphicsInfo();
entry.keys = registry->GetKeys();
entry.bound_samplers = registry->GetBoundSamplers();
entry.bindless_samplers = registry->GetBindlessSamplers();
params.disk_cache.SaveEntry(std::move(entry));
auto registry = std::make_shared<Registry>(shader_type, gpu.Maxwell3D());
if (!async_shaders.IsShaderAsync(params.system.GPU()) ||
!params.device.UseAsynchronousShaders()) {
const ShaderIR ir(code, STAGE_MAIN_OFFSET, COMPILER_SETTINGS, *registry);
// TODO(Rodrigo): Handle VertexA shaders
// std::optional<ShaderIR> ir_b;
// if (!code_b.empty()) {
// ir_b.emplace(code_b, STAGE_MAIN_OFFSET);
// }
auto program =
BuildShader(params.device, shader_type, params.unique_identifier, ir, *registry);
ShaderDiskCacheEntry entry;
entry.type = shader_type;
entry.code = std::move(code);
entry.code_b = std::move(code_b);
entry.unique_identifier = params.unique_identifier;
entry.bound_buffer = registry->GetBoundBuffer();
entry.graphics_info = registry->GetGraphicsInfo();
entry.keys = registry->GetKeys();
entry.bound_samplers = registry->GetBoundSamplers();
entry.bindless_samplers = registry->GetBindlessSamplers();
params.disk_cache.SaveEntry(std::move(entry));
return std::unique_ptr<Shader>(new Shader(
std::move(registry), MakeEntries(params.device, ir, shader_type), std::move(program)));
gpu.ShaderNotify().MarkShaderComplete();
return std::unique_ptr<Shader>(new Shader(std::move(registry),
MakeEntries(params.device, ir, shader_type),
std::move(program), true));
} else {
// Required for entries
const ShaderIR ir(code, STAGE_MAIN_OFFSET, COMPILER_SETTINGS, *registry);
auto entries = MakeEntries(params.device, ir, shader_type);
async_shaders.QueueOpenGLShader(params.device, shader_type, params.unique_identifier,
std::move(code), std::move(code_b), STAGE_MAIN_OFFSET,
COMPILER_SETTINGS, *registry, cpu_addr);
auto program = std::make_shared<ProgramHandle>();
return std::unique_ptr<Shader>(
new Shader(std::move(registry), std::move(entries), std::move(program), false));
}
}
std::unique_ptr<Shader> Shader::CreateKernelFromMemory(const ShaderParameters& params,
ProgramCode code) {
const std::size_t size_in_bytes = code.size() * sizeof(u64);
auto& engine = params.system.GPU().KeplerCompute();
auto& gpu = params.system.GPU();
gpu.ShaderNotify().MarkSharderBuilding();
auto& engine = gpu.KeplerCompute();
auto registry = std::make_shared<Registry>(ShaderType::Compute, engine);
const ShaderIR ir(code, KERNEL_MAIN_OFFSET, COMPILER_SETTINGS, *registry);
const u64 uid = params.unique_identifier;
@@ -266,6 +309,8 @@ std::unique_ptr<Shader> Shader::CreateKernelFromMemory(const ShaderParameters& p
entry.bindless_samplers = registry->GetBindlessSamplers();
params.disk_cache.SaveEntry(std::move(entry));
gpu.ShaderNotify().MarkShaderComplete();
return std::unique_ptr<Shader>(new Shader(std::move(registry),
MakeEntries(params.device, ir, ShaderType::Compute),
std::move(program)));
@@ -436,14 +481,51 @@ ProgramSharedPtr ShaderCacheOpenGL::GeneratePrecompiledProgram(
return program;
}
Shader* ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
Shader* ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program,
VideoCommon::Shader::AsyncShaders& async_shaders) {
if (!system.GPU().Maxwell3D().dirty.flags[Dirty::Shaders]) {
return last_shaders[static_cast<std::size_t>(program)];
auto* last_shader = last_shaders[static_cast<std::size_t>(program)];
if (last_shader->IsBuilt()) {
return last_shader;
}
}
auto& memory_manager{system.GPU().MemoryManager()};
const GPUVAddr address{GetShaderAddress(system, program)};
if (device.UseAsynchronousShaders() && async_shaders.HasCompletedWork()) {
auto completed_work = async_shaders.GetCompletedWork();
for (auto& work : completed_work) {
Shader* shader = TryGet(work.cpu_address);
auto& gpu = system.GPU();
gpu.ShaderNotify().MarkShaderComplete();
if (shader == nullptr) {
continue;
}
using namespace VideoCommon::Shader;
if (work.backend == AsyncShaders::Backend::OpenGL) {
shader->AsyncOpenGLBuilt(std::move(work.program.opengl));
} else if (work.backend == AsyncShaders::Backend::GLASM) {
shader->AsyncGLASMBuilt(std::move(work.program.glasm));
}
ShaderDiskCacheEntry entry;
entry.type = work.shader_type;
entry.code = std::move(work.code);
entry.code_b = std::move(work.code_b);
entry.unique_identifier = work.uid;
auto& registry = shader->GetRegistry();
entry.bound_buffer = registry.GetBoundBuffer();
entry.graphics_info = registry.GetGraphicsInfo();
entry.keys = registry.GetKeys();
entry.bound_samplers = registry.GetBoundSamplers();
entry.bindless_samplers = registry.GetBindlessSamplers();
disk_cache.SaveEntry(std::move(entry));
}
}
// Look up shader in the cache based on address
const auto cpu_addr{memory_manager.GpuToCpuAddress(address)};
if (Shader* const shader{cpu_addr ? TryGet(*cpu_addr) : null_shader.get()}) {
@@ -471,7 +553,8 @@ Shader* ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
std::unique_ptr<Shader> shader;
const auto found = runtime_cache.find(unique_identifier);
if (found == runtime_cache.end()) {
shader = Shader::CreateStageFromMemory(params, program, std::move(code), std::move(code_b));
shader = Shader::CreateStageFromMemory(params, program, std::move(code), std::move(code_b),
async_shaders, cpu_addr.value_or(0));
} else {
shader = Shader::CreateFromCache(params, found->second);
}

View File

@@ -33,6 +33,10 @@ namespace Core::Frontend {
class EmuWindow;
}
namespace VideoCommon::Shader {
class AsyncShaders;
}
namespace OpenGL {
class Device;
@@ -61,6 +65,11 @@ struct ShaderParameters {
u64 unique_identifier;
};
ProgramSharedPtr BuildShader(const Device& device, Tegra::Engines::ShaderType shader_type,
u64 unique_identifier, const VideoCommon::Shader::ShaderIR& ir,
const VideoCommon::Shader::Registry& registry,
bool hint_retrievable = false);
class Shader final {
public:
~Shader();
@@ -68,15 +77,28 @@ public:
/// Gets the GL program handle for the shader
GLuint GetHandle() const;
bool IsBuilt() const;
/// Gets the shader entries for the shader
const ShaderEntries& GetEntries() const {
return entries;
}
static std::unique_ptr<Shader> CreateStageFromMemory(const ShaderParameters& params,
Maxwell::ShaderProgram program_type,
ProgramCode program_code,
ProgramCode program_code_b);
const VideoCommon::Shader::Registry& GetRegistry() const {
return *registry;
}
/// Mark a OpenGL shader as built
void AsyncOpenGLBuilt(OGLProgram new_program);
/// Mark a GLASM shader as built
void AsyncGLASMBuilt(OGLAssemblyProgram new_program);
static std::unique_ptr<Shader> CreateStageFromMemory(
const ShaderParameters& params, Maxwell::ShaderProgram program_type,
ProgramCode program_code, ProgramCode program_code_b,
VideoCommon::Shader::AsyncShaders& async_shaders, VAddr cpu_addr);
static std::unique_ptr<Shader> CreateKernelFromMemory(const ShaderParameters& params,
ProgramCode code);
@@ -85,12 +107,13 @@ public:
private:
explicit Shader(std::shared_ptr<VideoCommon::Shader::Registry> registry, ShaderEntries entries,
ProgramSharedPtr program);
ProgramSharedPtr program, bool is_built = true);
std::shared_ptr<VideoCommon::Shader::Registry> registry;
ShaderEntries entries;
ProgramSharedPtr program;
GLuint handle = 0;
bool is_built{};
};
class ShaderCacheOpenGL final : public VideoCommon::ShaderCache<Shader> {
@@ -104,7 +127,8 @@ public:
const VideoCore::DiskResourceLoadCallback& callback);
/// Gets the current specified shader stage program
Shader* GetStageProgram(Maxwell::ShaderProgram program);
Shader* GetStageProgram(Maxwell::ShaderProgram program,
VideoCommon::Shader::AsyncShaders& async_shaders);
/// Gets a compute kernel in the passed address
Shader* GetComputeKernel(GPUVAddr code_addr);

View File

@@ -141,24 +141,28 @@ struct ScreenRectVertex {
std::array<f32, 2> tex_coord;
static VkVertexInputBindingDescription GetDescription() {
VkVertexInputBindingDescription description;
description.binding = 0;
description.stride = sizeof(ScreenRectVertex);
description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
return description;
return {
.binding = 0,
.stride = sizeof(ScreenRectVertex),
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX,
};
}
static std::array<VkVertexInputAttributeDescription, 2> GetAttributes() {
std::array<VkVertexInputAttributeDescription, 2> attributes;
attributes[0].location = 0;
attributes[0].binding = 0;
attributes[0].format = VK_FORMAT_R32G32_SFLOAT;
attributes[0].offset = offsetof(ScreenRectVertex, position);
attributes[1].location = 1;
attributes[1].binding = 0;
attributes[1].format = VK_FORMAT_R32G32_SFLOAT;
attributes[1].offset = offsetof(ScreenRectVertex, tex_coord);
return attributes;
return {{
{
.location = 0,
.binding = 0,
.format = VK_FORMAT_R32G32_SFLOAT,
.offset = offsetof(ScreenRectVertex, position),
},
{
.location = 1,
.binding = 0,
.format = VK_FORMAT_R32G32_SFLOAT,
.offset = offsetof(ScreenRectVertex, tex_coord),
},
}};
}
};
@@ -267,20 +271,25 @@ std::tuple<VKFence&, VkSemaphore> VKBlitScreen::Draw(const Tegra::FramebufferCon
blit_image->Transition(0, 1, 0, 1, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
VkBufferImageCopy copy;
copy.bufferOffset = image_offset;
copy.bufferRowLength = 0;
copy.bufferImageHeight = 0;
copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copy.imageSubresource.mipLevel = 0;
copy.imageSubresource.baseArrayLayer = 0;
copy.imageSubresource.layerCount = 1;
copy.imageOffset.x = 0;
copy.imageOffset.y = 0;
copy.imageOffset.z = 0;
copy.imageExtent.width = framebuffer.width;
copy.imageExtent.height = framebuffer.height;
copy.imageExtent.depth = 1;
const VkBufferImageCopy copy{
.bufferOffset = image_offset,
.bufferRowLength = 0,
.bufferImageHeight = 0,
.imageSubresource =
{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.imageOffset = {.x = 0, .y = 0, .z = 0},
.imageExtent =
{
.width = framebuffer.width,
.height = framebuffer.height,
.depth = 1,
},
};
scheduler.Record(
[buffer = *buffer, image = *blit_image->GetHandle(), copy](vk::CommandBuffer cmdbuf) {
cmdbuf.CopyBufferToImage(buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy);
@@ -295,11 +304,9 @@ std::tuple<VKFence&, VkSemaphore> VKBlitScreen::Draw(const Tegra::FramebufferCon
descriptor_set = descriptor_sets[image_index], buffer = *buffer,
size = swapchain.GetSize(), pipeline = *pipeline,
layout = *pipeline_layout](vk::CommandBuffer cmdbuf) {
VkClearValue clear_color;
clear_color.color.float32[0] = 0.0f;
clear_color.color.float32[1] = 0.0f;
clear_color.color.float32[2] = 0.0f;
clear_color.color.float32[3] = 0.0f;
const VkClearValue clear_color{
.color = {.float32 = {0.0f, 0.0f, 0.0f, 0.0f}},
};
VkRenderPassBeginInfo renderpass_bi;
renderpass_bi.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
@@ -379,93 +386,109 @@ void VKBlitScreen::CreateSemaphores() {
}
void VKBlitScreen::CreateDescriptorPool() {
std::array<VkDescriptorPoolSize, 2> pool_sizes;
pool_sizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
pool_sizes[0].descriptorCount = static_cast<u32>(image_count);
pool_sizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
pool_sizes[1].descriptorCount = static_cast<u32>(image_count);
const std::array<VkDescriptorPoolSize, 2> pool_sizes{{
{
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = static_cast<u32>(image_count),
},
{
.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = static_cast<u32>(image_count),
},
}};
VkDescriptorPoolCreateInfo ci;
ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
ci.pNext = nullptr;
ci.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
ci.maxSets = static_cast<u32>(image_count);
ci.poolSizeCount = static_cast<u32>(pool_sizes.size());
ci.pPoolSizes = pool_sizes.data();
const VkDescriptorPoolCreateInfo ci{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.pNext = nullptr,
.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
.maxSets = static_cast<u32>(image_count),
.poolSizeCount = static_cast<u32>(pool_sizes.size()),
.pPoolSizes = pool_sizes.data(),
};
descriptor_pool = device.GetLogical().CreateDescriptorPool(ci);
}
void VKBlitScreen::CreateRenderPass() {
VkAttachmentDescription color_attachment;
color_attachment.flags = 0;
color_attachment.format = swapchain.GetImageFormat();
color_attachment.samples = VK_SAMPLE_COUNT_1_BIT;
color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
color_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
color_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
color_attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
const VkAttachmentDescription color_attachment{
.flags = 0,
.format = swapchain.GetImageFormat(),
.samples = VK_SAMPLE_COUNT_1_BIT,
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
};
VkAttachmentReference color_attachment_ref;
color_attachment_ref.attachment = 0;
color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
const VkAttachmentReference color_attachment_ref{
.attachment = 0,
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
};
VkSubpassDescription subpass_description;
subpass_description.flags = 0;
subpass_description.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass_description.inputAttachmentCount = 0;
subpass_description.pInputAttachments = nullptr;
subpass_description.colorAttachmentCount = 1;
subpass_description.pColorAttachments = &color_attachment_ref;
subpass_description.pResolveAttachments = nullptr;
subpass_description.pDepthStencilAttachment = nullptr;
subpass_description.preserveAttachmentCount = 0;
subpass_description.pPreserveAttachments = nullptr;
const VkSubpassDescription subpass_description{
.flags = 0,
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
.inputAttachmentCount = 0,
.pInputAttachments = nullptr,
.colorAttachmentCount = 1,
.pColorAttachments = &color_attachment_ref,
.pResolveAttachments = nullptr,
.pDepthStencilAttachment = nullptr,
.preserveAttachmentCount = 0,
.pPreserveAttachments = nullptr,
};
VkSubpassDependency dependency;
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
dependency.dstSubpass = 0;
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.srcAccessMask = 0;
dependency.dstAccessMask =
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependency.dependencyFlags = 0;
const VkSubpassDependency dependency{
.srcSubpass = VK_SUBPASS_EXTERNAL,
.dstSubpass = 0,
.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
.srcAccessMask = 0,
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
.dependencyFlags = 0,
};
VkRenderPassCreateInfo renderpass_ci;
renderpass_ci.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderpass_ci.pNext = nullptr;
renderpass_ci.flags = 0;
renderpass_ci.attachmentCount = 1;
renderpass_ci.pAttachments = &color_attachment;
renderpass_ci.subpassCount = 1;
renderpass_ci.pSubpasses = &subpass_description;
renderpass_ci.dependencyCount = 1;
renderpass_ci.pDependencies = &dependency;
const VkRenderPassCreateInfo renderpass_ci{
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.attachmentCount = 1,
.pAttachments = &color_attachment,
.subpassCount = 1,
.pSubpasses = &subpass_description,
.dependencyCount = 1,
.pDependencies = &dependency,
};
renderpass = device.GetLogical().CreateRenderPass(renderpass_ci);
}
void VKBlitScreen::CreateDescriptorSetLayout() {
std::array<VkDescriptorSetLayoutBinding, 2> layout_bindings;
layout_bindings[0].binding = 0;
layout_bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
layout_bindings[0].descriptorCount = 1;
layout_bindings[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
layout_bindings[0].pImmutableSamplers = nullptr;
layout_bindings[1].binding = 1;
layout_bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
layout_bindings[1].descriptorCount = 1;
layout_bindings[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
layout_bindings[1].pImmutableSamplers = nullptr;
const std::array<VkDescriptorSetLayoutBinding, 2> layout_bindings{{
{
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
.pImmutableSamplers = nullptr,
},
{
.binding = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
.pImmutableSamplers = nullptr,
},
}};
VkDescriptorSetLayoutCreateInfo ci;
ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
ci.pNext = nullptr;
ci.flags = 0;
ci.bindingCount = static_cast<u32>(layout_bindings.size());
ci.pBindings = layout_bindings.data();
const VkDescriptorSetLayoutCreateInfo ci{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.bindingCount = static_cast<u32>(layout_bindings.size()),
.pBindings = layout_bindings.data(),
};
descriptor_set_layout = device.GetLogical().CreateDescriptorSetLayout(ci);
}
@@ -473,175 +496,192 @@ void VKBlitScreen::CreateDescriptorSetLayout() {
void VKBlitScreen::CreateDescriptorSets() {
const std::vector layouts(image_count, *descriptor_set_layout);
VkDescriptorSetAllocateInfo ai;
ai.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
ai.pNext = nullptr;
ai.descriptorPool = *descriptor_pool;
ai.descriptorSetCount = static_cast<u32>(image_count);
ai.pSetLayouts = layouts.data();
const VkDescriptorSetAllocateInfo ai{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.pNext = nullptr,
.descriptorPool = *descriptor_pool,
.descriptorSetCount = static_cast<u32>(image_count),
.pSetLayouts = layouts.data(),
};
descriptor_sets = descriptor_pool.Allocate(ai);
}
void VKBlitScreen::CreatePipelineLayout() {
VkPipelineLayoutCreateInfo ci;
ci.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
ci.pNext = nullptr;
ci.flags = 0;
ci.setLayoutCount = 1;
ci.pSetLayouts = descriptor_set_layout.address();
ci.pushConstantRangeCount = 0;
ci.pPushConstantRanges = nullptr;
const VkPipelineLayoutCreateInfo ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.setLayoutCount = 1,
.pSetLayouts = descriptor_set_layout.address(),
.pushConstantRangeCount = 0,
.pPushConstantRanges = nullptr,
};
pipeline_layout = device.GetLogical().CreatePipelineLayout(ci);
}
void VKBlitScreen::CreateGraphicsPipeline() {
std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages;
shader_stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shader_stages[0].pNext = nullptr;
shader_stages[0].flags = 0;
shader_stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
shader_stages[0].module = *vertex_shader;
shader_stages[0].pName = "main";
shader_stages[0].pSpecializationInfo = nullptr;
shader_stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shader_stages[1].pNext = nullptr;
shader_stages[1].flags = 0;
shader_stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
shader_stages[1].module = *fragment_shader;
shader_stages[1].pName = "main";
shader_stages[1].pSpecializationInfo = nullptr;
const std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages{{
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.stage = VK_SHADER_STAGE_VERTEX_BIT,
.module = *vertex_shader,
.pName = "main",
.pSpecializationInfo = nullptr,
},
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
.module = *fragment_shader,
.pName = "main",
.pSpecializationInfo = nullptr,
},
}};
const auto vertex_binding_description = ScreenRectVertex::GetDescription();
const auto vertex_attrs_description = ScreenRectVertex::GetAttributes();
VkPipelineVertexInputStateCreateInfo vertex_input_ci;
vertex_input_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertex_input_ci.pNext = nullptr;
vertex_input_ci.flags = 0;
vertex_input_ci.vertexBindingDescriptionCount = 1;
vertex_input_ci.pVertexBindingDescriptions = &vertex_binding_description;
vertex_input_ci.vertexAttributeDescriptionCount = u32{vertex_attrs_description.size()};
vertex_input_ci.pVertexAttributeDescriptions = vertex_attrs_description.data();
const VkPipelineVertexInputStateCreateInfo vertex_input_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.vertexBindingDescriptionCount = 1,
.pVertexBindingDescriptions = &vertex_binding_description,
.vertexAttributeDescriptionCount = u32{vertex_attrs_description.size()},
.pVertexAttributeDescriptions = vertex_attrs_description.data(),
};
VkPipelineInputAssemblyStateCreateInfo input_assembly_ci;
input_assembly_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
input_assembly_ci.pNext = nullptr;
input_assembly_ci.flags = 0;
input_assembly_ci.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
input_assembly_ci.primitiveRestartEnable = VK_FALSE;
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
.primitiveRestartEnable = VK_FALSE,
};
VkPipelineViewportStateCreateInfo viewport_state_ci;
viewport_state_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewport_state_ci.pNext = nullptr;
viewport_state_ci.flags = 0;
viewport_state_ci.viewportCount = 1;
viewport_state_ci.pViewports = nullptr;
viewport_state_ci.scissorCount = 1;
viewport_state_ci.pScissors = nullptr;
const VkPipelineViewportStateCreateInfo viewport_state_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.viewportCount = 1,
.pViewports = nullptr,
.scissorCount = 1,
.pScissors = nullptr,
};
VkPipelineRasterizationStateCreateInfo rasterization_ci;
rasterization_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterization_ci.pNext = nullptr;
rasterization_ci.flags = 0;
rasterization_ci.depthClampEnable = VK_FALSE;
rasterization_ci.rasterizerDiscardEnable = VK_FALSE;
rasterization_ci.polygonMode = VK_POLYGON_MODE_FILL;
rasterization_ci.cullMode = VK_CULL_MODE_NONE;
rasterization_ci.frontFace = VK_FRONT_FACE_CLOCKWISE;
rasterization_ci.depthBiasEnable = VK_FALSE;
rasterization_ci.depthBiasConstantFactor = 0.0f;
rasterization_ci.depthBiasClamp = 0.0f;
rasterization_ci.depthBiasSlopeFactor = 0.0f;
rasterization_ci.lineWidth = 1.0f;
const VkPipelineRasterizationStateCreateInfo rasterization_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.depthClampEnable = VK_FALSE,
.rasterizerDiscardEnable = VK_FALSE,
.polygonMode = VK_POLYGON_MODE_FILL,
.cullMode = VK_CULL_MODE_NONE,
.frontFace = VK_FRONT_FACE_CLOCKWISE,
.depthBiasEnable = VK_FALSE,
.depthBiasConstantFactor = 0.0f,
.depthBiasClamp = 0.0f,
.depthBiasSlopeFactor = 0.0f,
.lineWidth = 1.0f,
};
VkPipelineMultisampleStateCreateInfo multisampling_ci;
multisampling_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampling_ci.pNext = nullptr;
multisampling_ci.flags = 0;
multisampling_ci.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
multisampling_ci.sampleShadingEnable = VK_FALSE;
multisampling_ci.minSampleShading = 0.0f;
multisampling_ci.pSampleMask = nullptr;
multisampling_ci.alphaToCoverageEnable = VK_FALSE;
multisampling_ci.alphaToOneEnable = VK_FALSE;
const VkPipelineMultisampleStateCreateInfo multisampling_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
.sampleShadingEnable = VK_FALSE,
.minSampleShading = 0.0f,
.pSampleMask = nullptr,
.alphaToCoverageEnable = VK_FALSE,
.alphaToOneEnable = VK_FALSE,
};
VkPipelineColorBlendAttachmentState color_blend_attachment;
color_blend_attachment.blendEnable = VK_FALSE;
color_blend_attachment.srcColorBlendFactor = VK_BLEND_FACTOR_ZERO;
color_blend_attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
color_blend_attachment.colorBlendOp = VK_BLEND_OP_ADD;
color_blend_attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
color_blend_attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
color_blend_attachment.alphaBlendOp = VK_BLEND_OP_ADD;
color_blend_attachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
const VkPipelineColorBlendAttachmentState color_blend_attachment{
.blendEnable = VK_FALSE,
.srcColorBlendFactor = VK_BLEND_FACTOR_ZERO,
.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
.colorBlendOp = VK_BLEND_OP_ADD,
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
.alphaBlendOp = VK_BLEND_OP_ADD,
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
};
VkPipelineColorBlendStateCreateInfo color_blend_ci;
color_blend_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
color_blend_ci.flags = 0;
color_blend_ci.pNext = nullptr;
color_blend_ci.logicOpEnable = VK_FALSE;
color_blend_ci.logicOp = VK_LOGIC_OP_COPY;
color_blend_ci.attachmentCount = 1;
color_blend_ci.pAttachments = &color_blend_attachment;
color_blend_ci.blendConstants[0] = 0.0f;
color_blend_ci.blendConstants[1] = 0.0f;
color_blend_ci.blendConstants[2] = 0.0f;
color_blend_ci.blendConstants[3] = 0.0f;
const VkPipelineColorBlendStateCreateInfo color_blend_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.logicOpEnable = VK_FALSE,
.logicOp = VK_LOGIC_OP_COPY,
.attachmentCount = 1,
.pAttachments = &color_blend_attachment,
.blendConstants = {0.0f, 0.0f, 0.0f, 0.0f},
};
static constexpr std::array dynamic_states = {VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR};
VkPipelineDynamicStateCreateInfo dynamic_state_ci;
dynamic_state_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamic_state_ci.pNext = nullptr;
dynamic_state_ci.flags = 0;
dynamic_state_ci.dynamicStateCount = static_cast<u32>(dynamic_states.size());
dynamic_state_ci.pDynamicStates = dynamic_states.data();
static constexpr std::array dynamic_states{
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR,
};
const VkPipelineDynamicStateCreateInfo dynamic_state_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.dynamicStateCount = static_cast<u32>(dynamic_states.size()),
.pDynamicStates = dynamic_states.data(),
};
VkGraphicsPipelineCreateInfo pipeline_ci;
pipeline_ci.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipeline_ci.pNext = nullptr;
pipeline_ci.flags = 0;
pipeline_ci.stageCount = static_cast<u32>(shader_stages.size());
pipeline_ci.pStages = shader_stages.data();
pipeline_ci.pVertexInputState = &vertex_input_ci;
pipeline_ci.pInputAssemblyState = &input_assembly_ci;
pipeline_ci.pTessellationState = nullptr;
pipeline_ci.pViewportState = &viewport_state_ci;
pipeline_ci.pRasterizationState = &rasterization_ci;
pipeline_ci.pMultisampleState = &multisampling_ci;
pipeline_ci.pDepthStencilState = nullptr;
pipeline_ci.pColorBlendState = &color_blend_ci;
pipeline_ci.pDynamicState = &dynamic_state_ci;
pipeline_ci.layout = *pipeline_layout;
pipeline_ci.renderPass = *renderpass;
pipeline_ci.subpass = 0;
pipeline_ci.basePipelineHandle = 0;
pipeline_ci.basePipelineIndex = 0;
const VkGraphicsPipelineCreateInfo pipeline_ci{
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.stageCount = static_cast<u32>(shader_stages.size()),
.pStages = shader_stages.data(),
.pVertexInputState = &vertex_input_ci,
.pInputAssemblyState = &input_assembly_ci,
.pTessellationState = nullptr,
.pViewportState = &viewport_state_ci,
.pRasterizationState = &rasterization_ci,
.pMultisampleState = &multisampling_ci,
.pDepthStencilState = nullptr,
.pColorBlendState = &color_blend_ci,
.pDynamicState = &dynamic_state_ci,
.layout = *pipeline_layout,
.renderPass = *renderpass,
.subpass = 0,
.basePipelineHandle = 0,
.basePipelineIndex = 0,
};
pipeline = device.GetLogical().CreateGraphicsPipeline(pipeline_ci);
}
void VKBlitScreen::CreateSampler() {
VkSamplerCreateInfo ci;
ci.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
ci.pNext = nullptr;
ci.flags = 0;
ci.magFilter = VK_FILTER_LINEAR;
ci.minFilter = VK_FILTER_NEAREST;
ci.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
ci.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
ci.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
ci.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
ci.mipLodBias = 0.0f;
ci.anisotropyEnable = VK_FALSE;
ci.maxAnisotropy = 0.0f;
ci.compareEnable = VK_FALSE;
ci.compareOp = VK_COMPARE_OP_NEVER;
ci.minLod = 0.0f;
ci.maxLod = 0.0f;
ci.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK;
ci.unnormalizedCoordinates = VK_FALSE;
const VkSamplerCreateInfo ci{
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.magFilter = VK_FILTER_LINEAR,
.minFilter = VK_FILTER_NEAREST,
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
.mipLodBias = 0.0f,
.anisotropyEnable = VK_FALSE,
.maxAnisotropy = 0.0f,
.compareEnable = VK_FALSE,
.compareOp = VK_COMPARE_OP_NEVER,
.minLod = 0.0f,
.maxLod = 0.0f,
.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK,
.unnormalizedCoordinates = VK_FALSE,
};
sampler = device.GetLogical().CreateSampler(ci);
}
@@ -650,15 +690,16 @@ void VKBlitScreen::CreateFramebuffers() {
const VkExtent2D size{swapchain.GetSize()};
framebuffers.resize(image_count);
VkFramebufferCreateInfo ci;
ci.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
ci.pNext = nullptr;
ci.flags = 0;
ci.renderPass = *renderpass;
ci.attachmentCount = 1;
ci.width = size.width;
ci.height = size.height;
ci.layers = 1;
VkFramebufferCreateInfo ci{
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.renderPass = *renderpass,
.attachmentCount = 1,
.width = size.width,
.height = size.height,
.layers = 1,
};
for (std::size_t i = 0; i < image_count; ++i) {
const VkImageView image_view{swapchain.GetImageViewIndex(i)};
@@ -678,16 +719,17 @@ void VKBlitScreen::ReleaseRawImages() {
}
void VKBlitScreen::CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer) {
VkBufferCreateInfo ci;
ci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
ci.pNext = nullptr;
ci.flags = 0;
ci.size = CalculateBufferSize(framebuffer);
ci.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
ci.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
ci.queueFamilyIndexCount = 0;
ci.pQueueFamilyIndices = nullptr;
const VkBufferCreateInfo ci{
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.size = CalculateBufferSize(framebuffer),
.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
};
buffer = device.GetLogical().CreateBuffer(ci);
buffer_commit = memory_manager.Commit(buffer, true);
@@ -697,24 +739,28 @@ void VKBlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer)
raw_images.resize(image_count);
raw_buffer_commits.resize(image_count);
VkImageCreateInfo ci;
ci.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
ci.pNext = nullptr;
ci.flags = 0;
ci.imageType = VK_IMAGE_TYPE_2D;
ci.format = GetFormat(framebuffer);
ci.extent.width = framebuffer.width;
ci.extent.height = framebuffer.height;
ci.extent.depth = 1;
ci.mipLevels = 1;
ci.arrayLayers = 1;
ci.samples = VK_SAMPLE_COUNT_1_BIT;
ci.tiling = VK_IMAGE_TILING_LINEAR;
ci.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
ci.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
ci.queueFamilyIndexCount = 0;
ci.pQueueFamilyIndices = nullptr;
ci.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
const VkImageCreateInfo ci{
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.imageType = VK_IMAGE_TYPE_2D,
.format = GetFormat(framebuffer),
.extent =
{
.width = framebuffer.width,
.height = framebuffer.height,
.depth = 1,
},
.mipLevels = 1,
.arrayLayers = 1,
.samples = VK_SAMPLE_COUNT_1_BIT,
.tiling = VK_IMAGE_TILING_LINEAR,
.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
};
for (std::size_t i = 0; i < image_count; ++i) {
raw_images[i] = std::make_unique<VKImage>(device, scheduler, ci, VK_IMAGE_ASPECT_COLOR_BIT);
@@ -723,39 +769,43 @@ void VKBlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer)
}
void VKBlitScreen::UpdateDescriptorSet(std::size_t image_index, VkImageView image_view) const {
VkDescriptorBufferInfo buffer_info;
buffer_info.buffer = *buffer;
buffer_info.offset = offsetof(BufferData, uniform);
buffer_info.range = sizeof(BufferData::uniform);
const VkDescriptorBufferInfo buffer_info{
.buffer = *buffer,
.offset = offsetof(BufferData, uniform),
.range = sizeof(BufferData::uniform),
};
VkWriteDescriptorSet ubo_write;
ubo_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
ubo_write.pNext = nullptr;
ubo_write.dstSet = descriptor_sets[image_index];
ubo_write.dstBinding = 0;
ubo_write.dstArrayElement = 0;
ubo_write.descriptorCount = 1;
ubo_write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
ubo_write.pImageInfo = nullptr;
ubo_write.pBufferInfo = &buffer_info;
ubo_write.pTexelBufferView = nullptr;
const VkWriteDescriptorSet ubo_write{
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.pNext = nullptr,
.dstSet = descriptor_sets[image_index],
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.pImageInfo = nullptr,
.pBufferInfo = &buffer_info,
.pTexelBufferView = nullptr,
};
VkDescriptorImageInfo image_info;
image_info.sampler = *sampler;
image_info.imageView = image_view;
image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
const VkDescriptorImageInfo image_info{
.sampler = *sampler,
.imageView = image_view,
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
};
VkWriteDescriptorSet sampler_write;
sampler_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
sampler_write.pNext = nullptr;
sampler_write.dstSet = descriptor_sets[image_index];
sampler_write.dstBinding = 1;
sampler_write.dstArrayElement = 0;
sampler_write.descriptorCount = 1;
sampler_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
sampler_write.pImageInfo = &image_info;
sampler_write.pBufferInfo = nullptr;
sampler_write.pTexelBufferView = nullptr;
const VkWriteDescriptorSet sampler_write{
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.pNext = nullptr,
.dstSet = descriptor_sets[image_index],
.dstBinding = 1,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.pImageInfo = &image_info,
.pBufferInfo = nullptr,
.pTexelBufferView = nullptr,
};
device.GetLogical().UpdateDescriptorSets(std::array{ubo_write, sampler_write}, {});
}

View File

@@ -22,14 +22,21 @@ namespace {
namespace Alternatives {
constexpr std::array Depth24UnormS8_UINT = {VK_FORMAT_D32_SFLOAT_S8_UINT,
VK_FORMAT_D16_UNORM_S8_UINT, VkFormat{}};
constexpr std::array Depth16UnormS8_UINT = {VK_FORMAT_D24_UNORM_S8_UINT,
VK_FORMAT_D32_SFLOAT_S8_UINT, VkFormat{}};
constexpr std::array Depth24UnormS8_UINT{
VK_FORMAT_D32_SFLOAT_S8_UINT,
VK_FORMAT_D16_UNORM_S8_UINT,
VkFormat{},
};
constexpr std::array Depth16UnormS8_UINT{
VK_FORMAT_D24_UNORM_S8_UINT,
VK_FORMAT_D32_SFLOAT_S8_UINT,
VkFormat{},
};
} // namespace Alternatives
constexpr std::array REQUIRED_EXTENSIONS = {
constexpr std::array REQUIRED_EXTENSIONS{
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
VK_KHR_16BIT_STORAGE_EXTENSION_NAME,
VK_KHR_8BIT_STORAGE_EXTENSION_NAME,
@@ -169,97 +176,104 @@ bool VKDevice::Create() {
const auto queue_cis = GetDeviceQueueCreateInfos();
const std::vector extensions = LoadExtensions();
VkPhysicalDeviceFeatures2 features2;
features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
features2.pNext = nullptr;
VkPhysicalDeviceFeatures2 features2{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
.pNext = nullptr,
};
const void* first_next = &features2;
void** next = &features2.pNext;
auto& features = features2.features;
features.robustBufferAccess = false;
features.fullDrawIndexUint32 = false;
features.imageCubeArray = false;
features.independentBlend = true;
features.geometryShader = true;
features.tessellationShader = true;
features.sampleRateShading = false;
features.dualSrcBlend = false;
features.logicOp = false;
features.multiDrawIndirect = false;
features.drawIndirectFirstInstance = false;
features.depthClamp = true;
features.depthBiasClamp = true;
features.fillModeNonSolid = false;
features.depthBounds = false;
features.wideLines = false;
features.largePoints = true;
features.alphaToOne = false;
features.multiViewport = true;
features.samplerAnisotropy = true;
features.textureCompressionETC2 = false;
features.textureCompressionASTC_LDR = is_optimal_astc_supported;
features.textureCompressionBC = false;
features.occlusionQueryPrecise = true;
features.pipelineStatisticsQuery = false;
features.vertexPipelineStoresAndAtomics = true;
features.fragmentStoresAndAtomics = true;
features.shaderTessellationAndGeometryPointSize = false;
features.shaderImageGatherExtended = true;
features.shaderStorageImageExtendedFormats = false;
features.shaderStorageImageMultisample = false;
features.shaderStorageImageReadWithoutFormat = is_formatless_image_load_supported;
features.shaderStorageImageWriteWithoutFormat = true;
features.shaderUniformBufferArrayDynamicIndexing = false;
features.shaderSampledImageArrayDynamicIndexing = false;
features.shaderStorageBufferArrayDynamicIndexing = false;
features.shaderStorageImageArrayDynamicIndexing = false;
features.shaderClipDistance = false;
features.shaderCullDistance = false;
features.shaderFloat64 = false;
features.shaderInt64 = false;
features.shaderInt16 = false;
features.shaderResourceResidency = false;
features.shaderResourceMinLod = false;
features.sparseBinding = false;
features.sparseResidencyBuffer = false;
features.sparseResidencyImage2D = false;
features.sparseResidencyImage3D = false;
features.sparseResidency2Samples = false;
features.sparseResidency4Samples = false;
features.sparseResidency8Samples = false;
features.sparseResidency16Samples = false;
features.sparseResidencyAliased = false;
features.variableMultisampleRate = false;
features.inheritedQueries = false;
features2.features = {
.robustBufferAccess = false,
.fullDrawIndexUint32 = false,
.imageCubeArray = false,
.independentBlend = true,
.geometryShader = true,
.tessellationShader = true,
.sampleRateShading = false,
.dualSrcBlend = false,
.logicOp = false,
.multiDrawIndirect = false,
.drawIndirectFirstInstance = false,
.depthClamp = true,
.depthBiasClamp = true,
.fillModeNonSolid = false,
.depthBounds = false,
.wideLines = false,
.largePoints = true,
.alphaToOne = false,
.multiViewport = true,
.samplerAnisotropy = true,
.textureCompressionETC2 = false,
.textureCompressionASTC_LDR = is_optimal_astc_supported,
.textureCompressionBC = false,
.occlusionQueryPrecise = true,
.pipelineStatisticsQuery = false,
.vertexPipelineStoresAndAtomics = true,
.fragmentStoresAndAtomics = true,
.shaderTessellationAndGeometryPointSize = false,
.shaderImageGatherExtended = true,
.shaderStorageImageExtendedFormats = false,
.shaderStorageImageMultisample = false,
.shaderStorageImageReadWithoutFormat = is_formatless_image_load_supported,
.shaderStorageImageWriteWithoutFormat = true,
.shaderUniformBufferArrayDynamicIndexing = false,
.shaderSampledImageArrayDynamicIndexing = false,
.shaderStorageBufferArrayDynamicIndexing = false,
.shaderStorageImageArrayDynamicIndexing = false,
.shaderClipDistance = false,
.shaderCullDistance = false,
.shaderFloat64 = false,
.shaderInt64 = false,
.shaderInt16 = false,
.shaderResourceResidency = false,
.shaderResourceMinLod = false,
.sparseBinding = false,
.sparseResidencyBuffer = false,
.sparseResidencyImage2D = false,
.sparseResidencyImage3D = false,
.sparseResidency2Samples = false,
.sparseResidency4Samples = false,
.sparseResidency8Samples = false,
.sparseResidency16Samples = false,
.sparseResidencyAliased = false,
.variableMultisampleRate = false,
.inheritedQueries = false,
};
VkPhysicalDevice16BitStorageFeaturesKHR bit16_storage;
bit16_storage.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR;
bit16_storage.pNext = nullptr;
bit16_storage.storageBuffer16BitAccess = false;
bit16_storage.uniformAndStorageBuffer16BitAccess = true;
bit16_storage.storagePushConstant16 = false;
bit16_storage.storageInputOutput16 = false;
VkPhysicalDevice16BitStorageFeaturesKHR bit16_storage{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR,
.pNext = nullptr,
.storageBuffer16BitAccess = false,
.uniformAndStorageBuffer16BitAccess = true,
.storagePushConstant16 = false,
.storageInputOutput16 = false,
};
SetNext(next, bit16_storage);
VkPhysicalDevice8BitStorageFeaturesKHR bit8_storage;
bit8_storage.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES_KHR;
bit8_storage.pNext = nullptr;
bit8_storage.storageBuffer8BitAccess = false;
bit8_storage.uniformAndStorageBuffer8BitAccess = true;
bit8_storage.storagePushConstant8 = false;
VkPhysicalDevice8BitStorageFeaturesKHR bit8_storage{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES_KHR,
.pNext = nullptr,
.storageBuffer8BitAccess = false,
.uniformAndStorageBuffer8BitAccess = true,
.storagePushConstant8 = false,
};
SetNext(next, bit8_storage);
VkPhysicalDeviceHostQueryResetFeaturesEXT host_query_reset;
host_query_reset.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES_EXT;
host_query_reset.hostQueryReset = true;
VkPhysicalDeviceHostQueryResetFeaturesEXT host_query_reset{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES_EXT,
.hostQueryReset = true,
};
SetNext(next, host_query_reset);
VkPhysicalDeviceFloat16Int8FeaturesKHR float16_int8;
if (is_float16_supported) {
float16_int8.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR;
float16_int8.pNext = nullptr;
float16_int8.shaderFloat16 = true;
float16_int8.shaderInt8 = false;
float16_int8 = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR,
.pNext = nullptr,
.shaderFloat16 = true,
.shaderInt8 = false,
};
SetNext(next, float16_int8);
} else {
LOG_INFO(Render_Vulkan, "Device doesn't support float16 natively");
@@ -271,10 +285,11 @@ bool VKDevice::Create() {
VkPhysicalDeviceUniformBufferStandardLayoutFeaturesKHR std430_layout;
if (khr_uniform_buffer_standard_layout) {
std430_layout.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES_KHR;
std430_layout.pNext = nullptr;
std430_layout.uniformBufferStandardLayout = true;
std430_layout = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES_KHR,
.pNext = nullptr,
.uniformBufferStandardLayout = true,
};
SetNext(next, std430_layout);
} else {
LOG_INFO(Render_Vulkan, "Device doesn't support packed UBOs");
@@ -282,9 +297,11 @@ bool VKDevice::Create() {
VkPhysicalDeviceIndexTypeUint8FeaturesEXT index_type_uint8;
if (ext_index_type_uint8) {
index_type_uint8.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES_EXT;
index_type_uint8.pNext = nullptr;
index_type_uint8.indexTypeUint8 = true;
index_type_uint8 = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES_EXT,
.pNext = nullptr,
.indexTypeUint8 = true,
};
SetNext(next, index_type_uint8);
} else {
LOG_INFO(Render_Vulkan, "Device doesn't support uint8 indexes");
@@ -292,11 +309,12 @@ bool VKDevice::Create() {
VkPhysicalDeviceTransformFeedbackFeaturesEXT transform_feedback;
if (ext_transform_feedback) {
transform_feedback.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT;
transform_feedback.pNext = nullptr;
transform_feedback.transformFeedback = true;
transform_feedback.geometryStreams = true;
transform_feedback = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT,
.pNext = nullptr,
.transformFeedback = true,
.geometryStreams = true,
};
SetNext(next, transform_feedback);
} else {
LOG_INFO(Render_Vulkan, "Device doesn't support transform feedbacks");
@@ -304,10 +322,12 @@ bool VKDevice::Create() {
VkPhysicalDeviceCustomBorderColorFeaturesEXT custom_border;
if (ext_custom_border_color) {
custom_border.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT;
custom_border.pNext = nullptr;
custom_border.customBorderColors = VK_TRUE;
custom_border.customBorderColorWithoutFormat = VK_TRUE;
custom_border = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT,
.pNext = nullptr,
.customBorderColors = VK_TRUE,
.customBorderColorWithoutFormat = VK_TRUE,
};
SetNext(next, custom_border);
} else {
LOG_INFO(Render_Vulkan, "Device doesn't support custom border colors");
@@ -315,9 +335,11 @@ bool VKDevice::Create() {
VkPhysicalDeviceExtendedDynamicStateFeaturesEXT dynamic_state;
if (ext_extended_dynamic_state) {
dynamic_state.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT;
dynamic_state.pNext = nullptr;
dynamic_state.extendedDynamicState = VK_TRUE;
dynamic_state = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT,
.pNext = nullptr,
.extendedDynamicState = VK_TRUE,
};
SetNext(next, dynamic_state);
} else {
LOG_INFO(Render_Vulkan, "Device doesn't support extended dynamic state");
@@ -331,11 +353,13 @@ bool VKDevice::Create() {
if (nv_device_diagnostics_config) {
nsight_aftermath_tracker.Initialize();
diagnostics_nv.sType = VK_STRUCTURE_TYPE_DEVICE_DIAGNOSTICS_CONFIG_CREATE_INFO_NV;
diagnostics_nv.pNext = &features2;
diagnostics_nv.flags = VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_SHADER_DEBUG_INFO_BIT_NV |
VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_RESOURCE_TRACKING_BIT_NV |
VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_AUTOMATIC_CHECKPOINTS_BIT_NV;
diagnostics_nv = {
.sType = VK_STRUCTURE_TYPE_DEVICE_DIAGNOSTICS_CONFIG_CREATE_INFO_NV,
.pNext = &features2,
.flags = VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_SHADER_DEBUG_INFO_BIT_NV |
VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_RESOURCE_TRACKING_BIT_NV |
VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_AUTOMATIC_CHECKPOINTS_BIT_NV,
};
first_next = &diagnostics_nv;
}
@@ -704,13 +728,15 @@ void VKDevice::SetupFeatures() {
}
void VKDevice::CollectTelemetryParameters() {
VkPhysicalDeviceDriverPropertiesKHR driver;
driver.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR;
driver.pNext = nullptr;
VkPhysicalDeviceDriverPropertiesKHR driver{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR,
.pNext = nullptr,
};
VkPhysicalDeviceProperties2KHR properties;
properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR;
properties.pNext = &driver;
VkPhysicalDeviceProperties2KHR properties{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR,
.pNext = &driver,
};
physical.GetProperties2KHR(properties);
driver_id = driver.driverID;
@@ -719,24 +745,26 @@ void VKDevice::CollectTelemetryParameters() {
const std::vector extensions = physical.EnumerateDeviceExtensionProperties();
reported_extensions.reserve(std::size(extensions));
for (const auto& extension : extensions) {
reported_extensions.push_back(extension.extensionName);
reported_extensions.emplace_back(extension.extensionName);
}
}
std::vector<VkDeviceQueueCreateInfo> VKDevice::GetDeviceQueueCreateInfos() const {
static constexpr float QUEUE_PRIORITY = 1.0f;
std::unordered_set<u32> unique_queue_families = {graphics_family, present_family};
std::unordered_set<u32> unique_queue_families{graphics_family, present_family};
std::vector<VkDeviceQueueCreateInfo> queue_cis;
queue_cis.reserve(unique_queue_families.size());
for (const u32 queue_family : unique_queue_families) {
VkDeviceQueueCreateInfo& ci = queue_cis.emplace_back();
ci.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
ci.pNext = nullptr;
ci.flags = 0;
ci.queueFamilyIndex = queue_family;
ci.queueCount = 1;
ci.pQueuePriorities = &QUEUE_PRIORITY;
queue_cis.push_back({
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.queueFamilyIndex = queue_family,
.queueCount = 1,
.pQueuePriorities = &QUEUE_PRIORITY,
});
}
return queue_cis;

View File

@@ -307,8 +307,10 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.depthClampEnable = state.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE,
.rasterizerDiscardEnable = state.rasterize_enable == 0 ? VK_TRUE : VK_FALSE,
.depthClampEnable =
static_cast<VkBool32>(state.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE),
.rasterizerDiscardEnable =
static_cast<VkBool32>(state.rasterize_enable == 0 ? VK_TRUE : VK_FALSE),
.polygonMode = VK_POLYGON_MODE_FILL,
.cullMode =
dynamic.cull_enable ? MaxwellToVK::CullFace(dynamic.CullFace()) : VK_CULL_MODE_NONE,

View File

@@ -281,12 +281,10 @@ void CachedSurface::UploadBuffer(const std::vector<u8>& staging_buffer) {
VkBufferMemoryBarrier barrier;
barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
barrier.pNext = nullptr;
barrier.srcAccessMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
barrier.dstAccessMask = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
barrier.srcQueueFamilyIndex = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstQueueFamilyIndex = VK_ACCESS_SHADER_READ_BIT;
barrier.srcQueueFamilyIndex = 0;
barrier.dstQueueFamilyIndex = 0;
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; // They'll be ignored anyway
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.buffer = dst_buffer;
barrier.offset = 0;
barrier.size = size;

View File

@@ -0,0 +1,181 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <chrono>
#include <condition_variable>
#include <mutex>
#include <thread>
#include <vector>
#include "video_core/engines/maxwell_3d.h"
#include "video_core/renderer_base.h"
#include "video_core/renderer_opengl/gl_shader_cache.h"
#include "video_core/shader/async_shaders.h"
namespace VideoCommon::Shader {
AsyncShaders::AsyncShaders(Core::Frontend::EmuWindow& emu_window) : emu_window(emu_window) {}
AsyncShaders::~AsyncShaders() {
KillWorkers();
}
void AsyncShaders::AllocateWorkers(std::size_t num_workers) {
// If we're already have workers queued or don't want to queue workers, ignore
if (num_workers == worker_threads.size() || num_workers == 0) {
return;
}
// If workers already exist, clear them
if (!worker_threads.empty()) {
FreeWorkers();
}
// Create workers
for (std::size_t i = 0; i < num_workers; i++) {
context_list.push_back(emu_window.CreateSharedContext());
worker_threads.push_back(std::move(
std::thread(&AsyncShaders::ShaderCompilerThread, this, context_list[i].get())));
}
}
void AsyncShaders::FreeWorkers() {
// Mark all threads to quit
is_thread_exiting.store(true);
cv.notify_all();
for (auto& thread : worker_threads) {
thread.join();
}
// Clear our shared contexts
context_list.clear();
// Clear our worker threads
worker_threads.clear();
}
void AsyncShaders::KillWorkers() {
is_thread_exiting.store(true);
for (auto& thread : worker_threads) {
thread.detach();
}
// Clear our shared contexts
context_list.clear();
// Clear our worker threads
worker_threads.clear();
}
bool AsyncShaders::HasWorkQueued() {
return !pending_queue.empty();
}
bool AsyncShaders::HasCompletedWork() {
std::shared_lock lock{completed_mutex};
return !finished_work.empty();
}
bool AsyncShaders::IsShaderAsync(const Tegra::GPU& gpu) const {
const auto& regs = gpu.Maxwell3D().regs;
// If something is using depth, we can assume that games are not rendering anything which will
// be used one time.
if (regs.zeta_enable) {
return true;
}
// 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 (regs.index_array.count <= 6 || regs.vertex_buffer.count <= 6) {
return false;
}
return true;
}
std::vector<AsyncShaders::Result> AsyncShaders::GetCompletedWork() {
std::vector<AsyncShaders::Result> results;
{
std::unique_lock lock{completed_mutex};
results.assign(std::make_move_iterator(finished_work.begin()),
std::make_move_iterator(finished_work.end()));
finished_work.clear();
}
return results;
}
void AsyncShaders::QueueOpenGLShader(const OpenGL::Device& device,
Tegra::Engines::ShaderType shader_type, u64 uid,
std::vector<u64> code, std::vector<u64> code_b,
u32 main_offset,
VideoCommon::Shader::CompilerSettings compiler_settings,
const VideoCommon::Shader::Registry& registry,
VAddr cpu_addr) {
WorkerParams params{device.UseAssemblyShaders() ? AsyncShaders::Backend::GLASM
: AsyncShaders::Backend::OpenGL,
device,
shader_type,
uid,
std::move(code),
std::move(code_b),
main_offset,
compiler_settings,
registry,
cpu_addr};
std::unique_lock lock(queue_mutex);
pending_queue.push_back(std::move(params));
cv.notify_one();
}
void AsyncShaders::ShaderCompilerThread(Core::Frontend::GraphicsContext* context) {
using namespace std::chrono_literals;
while (!is_thread_exiting.load(std::memory_order_relaxed)) {
std::unique_lock lock{queue_mutex};
cv.wait(lock, [this] { return HasWorkQueued() || is_thread_exiting; });
if (is_thread_exiting) {
return;
}
// Partial lock to allow all threads to read at the same time
if (!HasWorkQueued()) {
continue;
}
// Another thread beat us, just unlock and wait for the next load
if (pending_queue.empty()) {
continue;
}
// Pull work from queue
WorkerParams work = std::move(pending_queue.front());
pending_queue.pop_front();
lock.unlock();
if (work.backend == AsyncShaders::Backend::OpenGL ||
work.backend == AsyncShaders::Backend::GLASM) {
const ShaderIR ir(work.code, work.main_offset, work.compiler_settings, work.registry);
const auto scope = context->Acquire();
auto program =
OpenGL::BuildShader(work.device, work.shader_type, work.uid, ir, work.registry);
Result result{};
result.backend = work.backend;
result.cpu_address = work.cpu_address;
result.uid = work.uid;
result.code = std::move(work.code);
result.code_b = std::move(work.code_b);
result.shader_type = work.shader_type;
if (work.backend == AsyncShaders::Backend::OpenGL) {
result.program.opengl = std::move(program->source_program);
} else if (work.backend == AsyncShaders::Backend::GLASM) {
result.program.glasm = std::move(program->assembly_program);
}
{
std::unique_lock complete_lock(completed_mutex);
finished_work.push_back(std::move(result));
}
}
}
}
} // namespace VideoCommon::Shader

View File

@@ -0,0 +1,109 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <condition_variable>
#include <deque>
#include <memory>
#include <shared_mutex>
#include <thread>
#include "common/bit_field.h"
#include "common/common_types.h"
#include "video_core/renderer_opengl/gl_device.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
namespace Core::Frontend {
class EmuWindow;
class GraphicsContext;
} // namespace Core::Frontend
namespace Tegra {
class GPU;
}
namespace VideoCommon::Shader {
class AsyncShaders {
public:
enum class Backend {
OpenGL,
GLASM,
};
struct ResultPrograms {
OpenGL::OGLProgram opengl;
OpenGL::OGLAssemblyProgram glasm;
};
struct Result {
u64 uid;
VAddr cpu_address;
Backend backend;
ResultPrograms program;
std::vector<u64> code;
std::vector<u64> code_b;
Tegra::Engines::ShaderType shader_type;
};
explicit AsyncShaders(Core::Frontend::EmuWindow& emu_window);
~AsyncShaders();
/// Start up shader worker threads
void AllocateWorkers(std::size_t num_workers);
/// Clear the shader queue and kill all worker threads
void FreeWorkers();
// Force end all threads
void KillWorkers();
/// Check to see if any shaders have actually been compiled
bool HasCompletedWork();
/// Deduce if a shader can be build on another thread of MUST be built in sync. We cannot build
/// every shader async as some shaders are only built and executed once. We try to "guess" which
/// shader would be used only once
bool IsShaderAsync(const Tegra::GPU& gpu) const;
/// Pulls completed compiled shaders
std::vector<Result> GetCompletedWork();
void QueueOpenGLShader(const OpenGL::Device& device, Tegra::Engines::ShaderType shader_type,
u64 uid, std::vector<u64> code, std::vector<u64> code_b, u32 main_offset,
VideoCommon::Shader::CompilerSettings compiler_settings,
const VideoCommon::Shader::Registry& registry, VAddr cpu_addr);
private:
void ShaderCompilerThread(Core::Frontend::GraphicsContext* context);
/// Check our worker queue to see if we have any work queued already
bool HasWorkQueued();
struct WorkerParams {
AsyncShaders::Backend backend;
OpenGL::Device device;
Tegra::Engines::ShaderType shader_type;
u64 uid;
std::vector<u64> code;
std::vector<u64> code_b;
u32 main_offset;
VideoCommon::Shader::CompilerSettings compiler_settings;
VideoCommon::Shader::Registry registry;
VAddr cpu_address;
};
std::condition_variable cv;
std::mutex queue_mutex;
std::shared_mutex completed_mutex;
std::atomic<bool> is_thread_exiting{};
std::vector<std::unique_ptr<Core::Frontend::GraphicsContext>> context_list;
std::vector<std::thread> worker_threads;
std::deque<WorkerParams> pending_queue;
std::vector<AsyncShaders::Result> finished_work;
Core::Frontend::EmuWindow& emu_window;
};
} // namespace VideoCommon::Shader

View File

@@ -0,0 +1,42 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "video_core/shader_notify.h"
using namespace std::chrono_literals;
namespace VideoCore {
namespace {
constexpr auto UPDATE_TICK = 32ms;
}
ShaderNotify::ShaderNotify() = default;
ShaderNotify::~ShaderNotify() = default;
std::size_t ShaderNotify::GetShadersBuilding() {
const auto now = std::chrono::high_resolution_clock::now();
const auto diff = now - last_update;
if (diff > UPDATE_TICK) {
std::shared_lock lock(mutex);
last_updated_count = accurate_count;
}
return last_updated_count;
}
std::size_t ShaderNotify::GetShadersBuildingAccurate() {
std::shared_lock lock{mutex};
return accurate_count;
}
void ShaderNotify::MarkShaderComplete() {
std::unique_lock lock{mutex};
accurate_count--;
}
void ShaderNotify::MarkSharderBuilding() {
std::unique_lock lock{mutex};
accurate_count++;
}
} // namespace VideoCore

View File

@@ -0,0 +1,29 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <chrono>
#include <shared_mutex>
#include "common/common_types.h"
namespace VideoCore {
class ShaderNotify {
public:
ShaderNotify();
~ShaderNotify();
std::size_t GetShadersBuilding();
std::size_t GetShadersBuildingAccurate();
void MarkShaderComplete();
void MarkSharderBuilding();
private:
std::size_t last_updated_count{};
std::size_t accurate_count{};
std::shared_mutex mutex;
std::chrono::high_resolution_clock::time_point last_update{};
};
} // namespace VideoCore

View File

@@ -343,8 +343,7 @@ std::size_t SurfaceParams::GetLayerSize(bool as_host_size, bool uncompressed) co
size += GetInnerMipmapMemorySize(level, as_host_size, uncompressed);
}
if (is_tiled && is_layered) {
return Common::AlignBits(size,
Tegra::Texture::GetGOBSizeShift() + block_height + block_depth);
return Common::AlignBits(size, Tegra::Texture::GOB_SIZE_SHIFT + block_height + block_depth);
}
return size;
}
@@ -418,7 +417,7 @@ std::tuple<u32, u32, u32> SurfaceParams::GetBlockOffsetXYZ(u32 offset) const {
const u32 block_size = GetBlockSize();
const u32 block_index = offset / block_size;
const u32 gob_offset = offset % block_size;
const u32 gob_index = gob_offset / static_cast<u32>(Tegra::Texture::GetGOBSize());
const u32 gob_index = gob_offset / static_cast<u32>(Tegra::Texture::GOB_SIZE);
const u32 x_gob_pixels = 64U / GetBytesPerPixel();
const u32 x_block_pixels = x_gob_pixels << block_width;
const u32 y_block_pixels = 8U << block_height;

View File

@@ -204,7 +204,7 @@ public:
static std::size_t AlignLayered(const std::size_t out_size, const u32 block_height,
const u32 block_depth) {
return Common::AlignBits(out_size,
Tegra::Texture::GetGOBSizeShift() + block_height + block_depth);
Tegra::Texture::GOB_SIZE_SHIFT + block_height + block_depth);
}
/// Converts a width from a type of surface into another. This helps represent the

View File

@@ -6,6 +6,7 @@
#include <cstring>
#include "common/alignment.h"
#include "common/assert.h"
#include "common/bit_util.h"
#include "video_core/gpu.h"
#include "video_core/textures/decoders.h"
#include "video_core/textures/texture.h"
@@ -37,20 +38,10 @@ struct alignas(64) SwizzleTable {
std::array<std::array<u16, M>, N> values{};
};
constexpr u32 gob_size_x_shift = 6;
constexpr u32 gob_size_y_shift = 3;
constexpr u32 gob_size_z_shift = 0;
constexpr u32 gob_size_shift = gob_size_x_shift + gob_size_y_shift + gob_size_z_shift;
constexpr u32 FAST_SWIZZLE_ALIGN = 16;
constexpr u32 gob_size_x = 1U << gob_size_x_shift;
constexpr u32 gob_size_y = 1U << gob_size_y_shift;
constexpr u32 gob_size_z = 1U << gob_size_z_shift;
constexpr u32 gob_size = 1U << gob_size_shift;
constexpr u32 fast_swizzle_align = 16;
constexpr auto legacy_swizzle_table = SwizzleTable<gob_size_y, gob_size_x, gob_size_z>();
constexpr auto fast_swizzle_table = SwizzleTable<gob_size_y, 4, fast_swizzle_align>();
constexpr auto LEGACY_SWIZZLE_TABLE = SwizzleTable<GOB_SIZE_X, GOB_SIZE_X, GOB_SIZE_Z>();
constexpr auto FAST_SWIZZLE_TABLE = SwizzleTable<GOB_SIZE_Y, 4, FAST_SWIZZLE_ALIGN>();
/**
* This function manages ALL the GOBs(Group of Bytes) Inside a single block.
@@ -69,17 +60,17 @@ void PreciseProcessBlock(u8* const swizzled_data, u8* const unswizzled_data, con
u32 y_address = z_address;
u32 pixel_base = layer_z * z + y_start * stride_x;
for (u32 y = y_start; y < y_end; y++) {
const auto& table = legacy_swizzle_table[y % gob_size_y];
const auto& table = LEGACY_SWIZZLE_TABLE[y % GOB_SIZE_Y];
for (u32 x = x_start; x < x_end; x++) {
const u32 swizzle_offset{y_address + table[x * bytes_per_pixel % gob_size_x]};
const u32 swizzle_offset{y_address + table[x * bytes_per_pixel % GOB_SIZE_X]};
const u32 pixel_index{x * out_bytes_per_pixel + pixel_base};
data_ptrs[unswizzle] = swizzled_data + swizzle_offset;
data_ptrs[!unswizzle] = unswizzled_data + pixel_index;
std::memcpy(data_ptrs[0], data_ptrs[1], bytes_per_pixel);
}
pixel_base += stride_x;
if ((y + 1) % gob_size_y == 0)
y_address += gob_size;
if ((y + 1) % GOB_SIZE_Y == 0)
y_address += GOB_SIZE;
}
z_address += xy_block_size;
}
@@ -104,18 +95,18 @@ void FastProcessBlock(u8* const swizzled_data, u8* const unswizzled_data, const
u32 y_address = z_address;
u32 pixel_base = layer_z * z + y_start * stride_x;
for (u32 y = y_start; y < y_end; y++) {
const auto& table = fast_swizzle_table[y % gob_size_y];
for (u32 xb = x_startb; xb < x_endb; xb += fast_swizzle_align) {
const u32 swizzle_offset{y_address + table[(xb / fast_swizzle_align) % 4]};
const auto& table = FAST_SWIZZLE_TABLE[y % GOB_SIZE_Y];
for (u32 xb = x_startb; xb < x_endb; xb += FAST_SWIZZLE_ALIGN) {
const u32 swizzle_offset{y_address + table[(xb / FAST_SWIZZLE_ALIGN) % 4]};
const u32 out_x = xb * out_bytes_per_pixel / bytes_per_pixel;
const u32 pixel_index{out_x + pixel_base};
data_ptrs[unswizzle ? 1 : 0] = swizzled_data + swizzle_offset;
data_ptrs[unswizzle ? 0 : 1] = unswizzled_data + pixel_index;
std::memcpy(data_ptrs[0], data_ptrs[1], fast_swizzle_align);
std::memcpy(data_ptrs[0], data_ptrs[1], FAST_SWIZZLE_ALIGN);
}
pixel_base += stride_x;
if ((y + 1) % gob_size_y == 0)
y_address += gob_size;
if ((y + 1) % GOB_SIZE_Y == 0)
y_address += GOB_SIZE;
}
z_address += xy_block_size;
}
@@ -138,9 +129,9 @@ void SwizzledData(u8* const swizzled_data, u8* const unswizzled_data, const bool
auto div_ceil = [](const u32 x, const u32 y) { return ((x + y - 1) / y); };
const u32 stride_x = width * out_bytes_per_pixel;
const u32 layer_z = height * stride_x;
const u32 gob_elements_x = gob_size_x / bytes_per_pixel;
constexpr u32 gob_elements_y = gob_size_y;
constexpr u32 gob_elements_z = gob_size_z;
const u32 gob_elements_x = GOB_SIZE_X / bytes_per_pixel;
constexpr u32 gob_elements_y = GOB_SIZE_Y;
constexpr u32 gob_elements_z = GOB_SIZE_Z;
const u32 block_x_elements = gob_elements_x;
const u32 block_y_elements = gob_elements_y * block_height;
const u32 block_z_elements = gob_elements_z * block_depth;
@@ -148,7 +139,7 @@ void SwizzledData(u8* const swizzled_data, u8* const unswizzled_data, const bool
const u32 blocks_on_x = div_ceil(aligned_width, block_x_elements);
const u32 blocks_on_y = div_ceil(height, block_y_elements);
const u32 blocks_on_z = div_ceil(depth, block_z_elements);
const u32 xy_block_size = gob_size * block_height;
const u32 xy_block_size = GOB_SIZE * block_height;
const u32 block_size = xy_block_size * block_depth;
u32 tile_offset = 0;
for (u32 zb = 0; zb < blocks_on_z; zb++) {
@@ -182,7 +173,7 @@ void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel,
bool unswizzle, u32 block_height, u32 block_depth, u32 width_spacing) {
const u32 block_height_size{1U << block_height};
const u32 block_depth_size{1U << block_depth};
if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % fast_swizzle_align == 0) {
if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % FAST_SWIZZLE_ALIGN == 0) {
SwizzledData<true>(swizzled_data, unswizzled_data, unswizzle, width, height, depth,
bytes_per_pixel, out_bytes_per_pixel, block_height_size,
block_depth_size, width_spacing);
@@ -259,25 +250,26 @@ std::vector<u8> UnswizzleTexture(u8* address, u32 tile_size_x, u32 tile_size_y,
}
void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width,
u32 bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data,
u32 bytes_per_pixel, u8* swizzled_data, const u8* unswizzled_data,
u32 block_height_bit, u32 offset_x, u32 offset_y) {
const u32 block_height = 1U << block_height_bit;
const u32 image_width_in_gobs{(swizzled_width * bytes_per_pixel + (gob_size_x - 1)) /
gob_size_x};
const u32 image_width_in_gobs =
(swizzled_width * bytes_per_pixel + (GOB_SIZE_X - 1)) / GOB_SIZE_X;
for (u32 line = 0; line < subrect_height; ++line) {
const u32 dst_y = line + offset_y;
const u32 gob_address_y =
(dst_y / (gob_size_y * block_height)) * gob_size * block_height * image_width_in_gobs +
((dst_y % (gob_size_y * block_height)) / gob_size_y) * gob_size;
const auto& table = legacy_swizzle_table[dst_y % gob_size_y];
(dst_y / (GOB_SIZE_Y * block_height)) * GOB_SIZE * block_height * image_width_in_gobs +
((dst_y % (GOB_SIZE_Y * block_height)) / GOB_SIZE_Y) * GOB_SIZE;
const auto& table = LEGACY_SWIZZLE_TABLE[dst_y % GOB_SIZE_Y];
for (u32 x = 0; x < subrect_width; ++x) {
const u32 dst_x = x + offset_x;
const u32 gob_address =
gob_address_y + (dst_x * bytes_per_pixel / gob_size_x) * gob_size * block_height;
const u32 swizzled_offset = gob_address + table[(dst_x * bytes_per_pixel) % gob_size_x];
u8* source_line = unswizzled_data + line * source_pitch + x * bytes_per_pixel;
u8* dest_addr = swizzled_data + swizzled_offset;
gob_address_y + (dst_x * bytes_per_pixel / GOB_SIZE_X) * GOB_SIZE * block_height;
const u32 swizzled_offset = gob_address + table[(dst_x * bytes_per_pixel) % GOB_SIZE_X];
const u32 unswizzled_offset = line * source_pitch + x * bytes_per_pixel;
const u8* const source_line = unswizzled_data + unswizzled_offset;
u8* const dest_addr = swizzled_data + swizzled_offset;
std::memcpy(dest_addr, source_line, bytes_per_pixel);
}
}
@@ -289,14 +281,15 @@ void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32
const u32 block_height = 1U << block_height_bit;
for (u32 line = 0; line < subrect_height; ++line) {
const u32 y2 = line + offset_y;
const u32 gob_address_y = (y2 / (gob_size_y * block_height)) * gob_size * block_height +
((y2 % (gob_size_y * block_height)) / gob_size_y) * gob_size;
const auto& table = legacy_swizzle_table[y2 % gob_size_y];
const u32 gob_address_y = (y2 / (GOB_SIZE_Y * block_height)) * GOB_SIZE * block_height +
((y2 % (GOB_SIZE_Y * block_height)) / GOB_SIZE_Y) * GOB_SIZE;
const auto& table = LEGACY_SWIZZLE_TABLE[y2 % GOB_SIZE_Y];
for (u32 x = 0; x < subrect_width; ++x) {
const u32 x2 = (x + offset_x) * bytes_per_pixel;
const u32 gob_address = gob_address_y + (x2 / gob_size_x) * gob_size * block_height;
const u32 swizzled_offset = gob_address + table[x2 % gob_size_x];
u8* dest_line = unswizzled_data + line * dest_pitch + x * bytes_per_pixel;
const u32 gob_address = gob_address_y + (x2 / GOB_SIZE_X) * GOB_SIZE * block_height;
const u32 swizzled_offset = gob_address + table[x2 % GOB_SIZE_X];
const u32 unswizzled_offset = line * dest_pitch + x * bytes_per_pixel;
u8* dest_line = unswizzled_data + unswizzled_offset;
u8* source_addr = swizzled_data + swizzled_offset;
std::memcpy(dest_line, source_addr, bytes_per_pixel);
@@ -304,21 +297,48 @@ void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32
}
}
void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 height,
u32 bytes_per_pixel, u32 block_height, u32 block_depth, u32 origin_x,
u32 origin_y, u8* output, const u8* input) {
UNIMPLEMENTED_IF(origin_x > 0);
UNIMPLEMENTED_IF(origin_y > 0);
const u32 stride = width * bytes_per_pixel;
const u32 gobs_in_x = (stride + GOB_SIZE_X - 1) / GOB_SIZE_X;
const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth);
const u32 block_height_mask = (1U << block_height) - 1;
const u32 x_shift = Common::CountTrailingZeroes32(GOB_SIZE << (block_height + block_depth));
for (u32 line = 0; line < line_count; ++line) {
const auto& table = LEGACY_SWIZZLE_TABLE[line % GOB_SIZE_Y];
const u32 block_y = line / GOB_SIZE_Y;
const u32 dst_offset_y =
(block_y >> block_height) * block_size + (block_y & block_height_mask) * GOB_SIZE;
for (u32 x = 0; x < line_length_in; ++x) {
const u32 dst_offset =
((x / GOB_SIZE_X) << x_shift) + dst_offset_y + table[x % GOB_SIZE_X];
const u32 src_offset = x * bytes_per_pixel + line * pitch;
std::memcpy(output + dst_offset, input + src_offset, bytes_per_pixel);
}
}
}
void SwizzleKepler(const u32 width, const u32 height, const u32 dst_x, const u32 dst_y,
const u32 block_height_bit, const std::size_t copy_size, const u8* source_data,
u8* swizzle_data) {
const u32 block_height = 1U << block_height_bit;
const u32 image_width_in_gobs{(width + gob_size_x - 1) / gob_size_x};
const u32 image_width_in_gobs{(width + GOB_SIZE_X - 1) / GOB_SIZE_X};
std::size_t count = 0;
for (std::size_t y = dst_y; y < height && count < copy_size; ++y) {
const std::size_t gob_address_y =
(y / (gob_size_y * block_height)) * gob_size * block_height * image_width_in_gobs +
((y % (gob_size_y * block_height)) / gob_size_y) * gob_size;
const auto& table = legacy_swizzle_table[y % gob_size_y];
(y / (GOB_SIZE_Y * block_height)) * GOB_SIZE * block_height * image_width_in_gobs +
((y % (GOB_SIZE_Y * block_height)) / GOB_SIZE_Y) * GOB_SIZE;
const auto& table = LEGACY_SWIZZLE_TABLE[y % GOB_SIZE_Y];
for (std::size_t x = dst_x; x < width && count < copy_size; ++x) {
const std::size_t gob_address =
gob_address_y + (x / gob_size_x) * gob_size * block_height;
const std::size_t swizzled_offset = gob_address + table[x % gob_size_x];
gob_address_y + (x / GOB_SIZE_X) * GOB_SIZE * block_height;
const std::size_t swizzled_offset = gob_address + table[x % GOB_SIZE_X];
const u8* source_line = source_data + count;
u8* dest_addr = swizzle_data + swizzled_offset;
count++;
@@ -373,9 +393,9 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
u32 block_height, u32 block_depth) {
if (tiled) {
const u32 aligned_width = Common::AlignBits(width * bytes_per_pixel, gob_size_x_shift);
const u32 aligned_height = Common::AlignBits(height, gob_size_y_shift + block_height);
const u32 aligned_depth = Common::AlignBits(depth, gob_size_z_shift + block_depth);
const u32 aligned_width = Common::AlignBits(width * bytes_per_pixel, GOB_SIZE_X_SHIFT);
const u32 aligned_height = Common::AlignBits(height, GOB_SIZE_Y_SHIFT + block_height);
const u32 aligned_depth = Common::AlignBits(depth, GOB_SIZE_Z_SHIFT + block_depth);
return aligned_width * aligned_height * aligned_depth;
} else {
return width * height * depth * bytes_per_pixel;
@@ -386,14 +406,14 @@ u64 GetGOBOffset(u32 width, u32 height, u32 dst_x, u32 dst_y, u32 block_height,
u32 bytes_per_pixel) {
auto div_ceil = [](const u32 x, const u32 y) { return ((x + y - 1) / y); };
const u32 gobs_in_block = 1 << block_height;
const u32 y_blocks = gob_size_y << block_height;
const u32 x_per_gob = gob_size_x / bytes_per_pixel;
const u32 y_blocks = GOB_SIZE_Y << block_height;
const u32 x_per_gob = GOB_SIZE_X / bytes_per_pixel;
const u32 x_blocks = div_ceil(width, x_per_gob);
const u32 block_size = gob_size * gobs_in_block;
const u32 block_size = GOB_SIZE * gobs_in_block;
const u32 stride = block_size * x_blocks;
const u32 base = (dst_y / y_blocks) * stride + (dst_x / x_per_gob) * block_size;
const u32 relative_y = dst_y % y_blocks;
return base + (relative_y / gob_size_y) * gob_size;
return base + (relative_y / GOB_SIZE_Y) * GOB_SIZE;
}
} // namespace Tegra::Texture

View File

@@ -10,15 +10,15 @@
namespace Tegra::Texture {
// GOBSize constant. Calculated by 64 bytes in x multiplied by 8 y coords, represents
// an small rect of (64/bytes_per_pixel)X8.
inline std::size_t GetGOBSize() {
return 512;
}
constexpr u32 GOB_SIZE_X = 64;
constexpr u32 GOB_SIZE_Y = 8;
constexpr u32 GOB_SIZE_Z = 1;
constexpr u32 GOB_SIZE = GOB_SIZE_X * GOB_SIZE_Y * GOB_SIZE_Z;
inline std::size_t GetGOBSizeShift() {
return 9;
}
constexpr std::size_t GOB_SIZE_X_SHIFT = 6;
constexpr std::size_t GOB_SIZE_Y_SHIFT = 3;
constexpr std::size_t GOB_SIZE_Z_SHIFT = 0;
constexpr std::size_t GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT + GOB_SIZE_Z_SHIFT;
/// Unswizzles a swizzled texture without changing its format.
void UnswizzleTexture(u8* unswizzled_data, u8* address, u32 tile_size_x, u32 tile_size_y,
@@ -48,14 +48,32 @@ std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height
/// Copies an untiled subrectangle into a tiled surface.
void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width,
u32 bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, u32 block_height,
u32 offset_x, u32 offset_y);
u32 bytes_per_pixel, u8* swizzled_data, const u8* unswizzled_data,
u32 block_height_bit, u32 offset_x, u32 offset_y);
/// Copies a tiled subrectangle into a linear surface.
void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width,
u32 bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, u32 block_height,
u32 offset_x, u32 offset_y);
/// @brief Swizzles a 2D array of pixels into a 3D texture
/// @param line_length_in Number of pixels per line
/// @param line_count Number of lines
/// @param pitch Number of bytes per line
/// @param width Width of the swizzled texture
/// @param height Height of the swizzled texture
/// @param bytes_per_pixel Number of bytes used per pixel
/// @param block_height Block height shift
/// @param block_depth Block depth shift
/// @param origin_x Column offset in pixels of the swizzled texture
/// @param origin_y Row offset in pixels of the swizzled texture
/// @param output Pointer to the pixels of the swizzled texture
/// @param input Pointer to the 2D array of pixels used as input
/// @pre input and output points to an array large enough to hold the number of bytes used
void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 height,
u32 bytes_per_pixel, u32 block_height, u32 block_depth, u32 origin_x,
u32 origin_y, u8* output, const u8* input);
void SwizzleKepler(u32 width, u32 height, u32 dst_x, u32 dst_y, u32 block_height,
std::size_t copy_size, const u8* source_data, u8* swizzle_data);

View File

@@ -30,6 +30,12 @@ add_executable(yuzu
configuration/configure_audio.cpp
configuration/configure_audio.h
configuration/configure_audio.ui
configuration/configure_cpu.cpp
configuration/configure_cpu.h
configuration/configure_cpu.ui
configuration/configure_cpu_debug.cpp
configuration/configure_cpu_debug.h
configuration/configure_cpu_debug.ui
configuration/configure_debug.cpp
configuration/configure_debug.h
configuration/configure_debug.ui

View File

@@ -505,22 +505,6 @@ void Config::ReadDataStorageValues() {
ReadSetting(QStringLiteral("gamecard_current_game"), false).toBool();
Settings::values.gamecard_path =
ReadSetting(QStringLiteral("gamecard_path"), QStringLiteral("")).toString().toStdString();
Settings::values.nand_total_size = static_cast<Settings::NANDTotalSize>(
ReadSetting(QStringLiteral("nand_total_size"),
QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDTotalSize::S29_1GB)))
.toULongLong());
Settings::values.nand_user_size = static_cast<Settings::NANDUserSize>(
ReadSetting(QStringLiteral("nand_user_size"),
QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDUserSize::S26GB)))
.toULongLong());
Settings::values.nand_system_size = static_cast<Settings::NANDSystemSize>(
ReadSetting(QStringLiteral("nand_system_size"),
QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDSystemSize::S2_5GB)))
.toULongLong());
Settings::values.sdmc_size = static_cast<Settings::SDMCSize>(
ReadSetting(QStringLiteral("sdmc_size"),
QVariant::fromValue<u64>(static_cast<u64>(Settings::SDMCSize::S16GB)))
.toULongLong());
qt_config->endGroup();
}
@@ -540,8 +524,6 @@ void Config::ReadDebuggingValues() {
Settings::values.reporting_services =
ReadSetting(QStringLiteral("reporting_services"), false).toBool();
Settings::values.quest_flag = ReadSetting(QStringLiteral("quest_flag"), false).toBool();
Settings::values.disable_cpu_opt =
ReadSetting(QStringLiteral("disable_cpu_opt"), false).toBool();
Settings::values.disable_macro_jit =
ReadSetting(QStringLiteral("disable_macro_jit"), false).toBool();
@@ -633,6 +615,34 @@ void Config::ReadPathValues() {
qt_config->endGroup();
}
void Config::ReadCpuValues() {
qt_config->beginGroup(QStringLiteral("Cpu"));
if (global) {
Settings::values.cpu_accuracy = static_cast<Settings::CPUAccuracy>(
ReadSetting(QStringLiteral("cpu_accuracy"), 0).toInt());
Settings::values.cpuopt_page_tables =
ReadSetting(QStringLiteral("cpuopt_page_tables"), true).toBool();
Settings::values.cpuopt_block_linking =
ReadSetting(QStringLiteral("cpuopt_block_linking"), true).toBool();
Settings::values.cpuopt_return_stack_buffer =
ReadSetting(QStringLiteral("cpuopt_return_stack_buffer"), true).toBool();
Settings::values.cpuopt_fast_dispatcher =
ReadSetting(QStringLiteral("cpuopt_fast_dispatcher"), true).toBool();
Settings::values.cpuopt_context_elimination =
ReadSetting(QStringLiteral("cpuopt_context_elimination"), true).toBool();
Settings::values.cpuopt_const_prop =
ReadSetting(QStringLiteral("cpuopt_const_prop"), true).toBool();
Settings::values.cpuopt_misc_ir =
ReadSetting(QStringLiteral("cpuopt_misc_ir"), true).toBool();
Settings::values.cpuopt_reduce_misalign_checks =
ReadSetting(QStringLiteral("cpuopt_reduce_misalign_checks"), true).toBool();
}
qt_config->endGroup();
}
void Config::ReadRendererValues() {
qt_config->beginGroup(QStringLiteral("Renderer"));
@@ -651,6 +661,8 @@ void Config::ReadRendererValues() {
ReadSettingGlobal(Settings::values.use_vsync, QStringLiteral("use_vsync"), true);
ReadSettingGlobal(Settings::values.use_assembly_shaders, QStringLiteral("use_assembly_shaders"),
false);
ReadSettingGlobal(Settings::values.use_asynchronous_shaders,
QStringLiteral("use_asynchronous_shaders"), false);
ReadSettingGlobal(Settings::values.use_fast_gpu_time, QStringLiteral("use_fast_gpu_time"),
true);
ReadSettingGlobal(Settings::values.force_30fps_mode, QStringLiteral("force_30fps_mode"), false);
@@ -829,6 +841,7 @@ void Config::ReadValues() {
ReadMiscellaneousValues();
}
ReadCoreValues();
ReadCpuValues();
ReadRendererValues();
ReadAudioValues();
ReadSystemValues();
@@ -929,6 +942,7 @@ void Config::SaveValues() {
SaveMiscellaneousValues();
}
SaveCoreValues();
SaveCpuValues();
SaveRendererValues();
SaveAudioValues();
SaveSystemValues();
@@ -1006,18 +1020,7 @@ void Config::SaveDataStorageValues() {
false);
WriteSetting(QStringLiteral("gamecard_path"),
QString::fromStdString(Settings::values.gamecard_path), QStringLiteral(""));
WriteSetting(QStringLiteral("nand_total_size"),
QVariant::fromValue<u64>(static_cast<u64>(Settings::values.nand_total_size)),
QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDTotalSize::S29_1GB)));
WriteSetting(QStringLiteral("nand_user_size"),
QVariant::fromValue<u64>(static_cast<u64>(Settings::values.nand_user_size)),
QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDUserSize::S26GB)));
WriteSetting(QStringLiteral("nand_system_size"),
QVariant::fromValue<u64>(static_cast<u64>(Settings::values.nand_system_size)),
QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDSystemSize::S2_5GB)));
WriteSetting(QStringLiteral("sdmc_size"),
QVariant::fromValue<u64>(static_cast<u64>(Settings::values.sdmc_size)),
QVariant::fromValue<u64>(static_cast<u64>(Settings::SDMCSize::S16GB)));
qt_config->endGroup();
}
@@ -1033,7 +1036,6 @@ void Config::SaveDebuggingValues() {
WriteSetting(QStringLiteral("dump_exefs"), Settings::values.dump_exefs, false);
WriteSetting(QStringLiteral("dump_nso"), Settings::values.dump_nso, false);
WriteSetting(QStringLiteral("quest_flag"), Settings::values.quest_flag, false);
WriteSetting(QStringLiteral("disable_cpu_opt"), Settings::values.disable_cpu_opt, false);
WriteSetting(QStringLiteral("disable_macro_jit"), Settings::values.disable_macro_jit, false);
qt_config->endGroup();
@@ -1097,6 +1099,32 @@ void Config::SavePathValues() {
qt_config->endGroup();
}
void Config::SaveCpuValues() {
qt_config->beginGroup(QStringLiteral("Cpu"));
if (global) {
WriteSetting(QStringLiteral("cpu_accuracy"),
static_cast<int>(Settings::values.cpu_accuracy), 0);
WriteSetting(QStringLiteral("cpuopt_page_tables"), Settings::values.cpuopt_page_tables,
true);
WriteSetting(QStringLiteral("cpuopt_block_linking"), Settings::values.cpuopt_block_linking,
true);
WriteSetting(QStringLiteral("cpuopt_return_stack_buffer"),
Settings::values.cpuopt_return_stack_buffer, true);
WriteSetting(QStringLiteral("cpuopt_fast_dispatcher"),
Settings::values.cpuopt_fast_dispatcher, true);
WriteSetting(QStringLiteral("cpuopt_context_elimination"),
Settings::values.cpuopt_context_elimination, true);
WriteSetting(QStringLiteral("cpuopt_const_prop"), Settings::values.cpuopt_const_prop, true);
WriteSetting(QStringLiteral("cpuopt_misc_ir"), Settings::values.cpuopt_misc_ir, true);
WriteSetting(QStringLiteral("cpuopt_reduce_misalign_checks"),
Settings::values.cpuopt_reduce_misalign_checks, true);
}
qt_config->endGroup();
}
void Config::SaveRendererValues() {
qt_config->beginGroup(QStringLiteral("Renderer"));
@@ -1119,6 +1147,8 @@ void Config::SaveRendererValues() {
WriteSettingGlobal(QStringLiteral("use_vsync"), Settings::values.use_vsync, true);
WriteSettingGlobal(QStringLiteral("use_assembly_shaders"),
Settings::values.use_assembly_shaders, false);
WriteSettingGlobal(QStringLiteral("use_asynchronous_shaders"),
Settings::values.use_asynchronous_shaders, false);
WriteSettingGlobal(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time,
true);
WriteSettingGlobal(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode,
@@ -1342,11 +1372,13 @@ void Config::WriteSettingGlobal(const QString& name, const QVariant& value, bool
void Config::Reload() {
ReadValues();
Settings::Sanitize();
// To apply default value changes
SaveValues();
Settings::Apply();
}
void Config::Save() {
Settings::Sanitize();
SaveValues();
}

View File

@@ -49,6 +49,7 @@ private:
void ReadDisabledAddOnValues();
void ReadMiscellaneousValues();
void ReadPathValues();
void ReadCpuValues();
void ReadRendererValues();
void ReadShortcutValues();
void ReadSystemValues();
@@ -73,6 +74,7 @@ private:
void SaveDisabledAddOnValues();
void SaveMiscellaneousValues();
void SavePathValues();
void SaveCpuValues();
void SaveRendererValues();
void SaveShortcutValues();
void SaveSystemValues();

View File

@@ -78,6 +78,16 @@
<string>Hotkeys</string>
</attribute>
</widget>
<widget class="ConfigureCpu" name="cpuTab">
<attribute name="title">
<string>CPU</string>
</attribute>
</widget>
<widget class="ConfigureCpuDebug" name="cpuDebugTab">
<attribute name="title">
<string>Debug</string>
</attribute>
</widget>
<widget class="ConfigureGraphics" name="graphicsTab">
<attribute name="title">
<string>Graphics</string>
@@ -158,6 +168,18 @@
<header>configuration/configure_debug.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ConfigureCpu</class>
<extends>QWidget</extends>
<header>configuration/configure_cpu.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ConfigureCpuDebug</class>
<extends>QWidget</extends>
<header>configuration/configure_cpu_debug.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ConfigureGraphics</class>
<extends>QWidget</extends>

View File

@@ -0,0 +1,61 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <QComboBox>
#include <QMessageBox>
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/settings.h"
#include "ui_configure_cpu.h"
#include "yuzu/configuration/configure_cpu.h"
ConfigureCpu::ConfigureCpu(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureCpu) {
ui->setupUi(this);
SetConfiguration();
connect(ui->accuracy, qOverload<int>(&QComboBox::activated), this,
&ConfigureCpu::AccuracyUpdated);
}
ConfigureCpu::~ConfigureCpu() = default;
void ConfigureCpu::SetConfiguration() {
const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
ui->accuracy->setEnabled(runtime_lock);
ui->accuracy->setCurrentIndex(static_cast<int>(Settings::values.cpu_accuracy));
}
void ConfigureCpu::AccuracyUpdated(int index) {
if (static_cast<Settings::CPUAccuracy>(index) == Settings::CPUAccuracy::DebugMode) {
const auto result = QMessageBox::warning(this, tr("Setting CPU to Debug Mode"),
tr("CPU Debug Mode is only intended for developer "
"use. Are you sure you want to enable this?"),
QMessageBox::Yes | QMessageBox::No);
if (result == QMessageBox::No) {
ui->accuracy->setCurrentIndex(static_cast<int>(Settings::CPUAccuracy::Accurate));
return;
}
}
}
void ConfigureCpu::ApplyConfiguration() {
Settings::values.cpu_accuracy =
static_cast<Settings::CPUAccuracy>(ui->accuracy->currentIndex());
}
void ConfigureCpu::changeEvent(QEvent* event) {
if (event->type() == QEvent::LanguageChange) {
RetranslateUI();
}
QWidget::changeEvent(event);
}
void ConfigureCpu::RetranslateUI() {
ui->retranslateUi(this);
}

View File

@@ -0,0 +1,33 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <QWidget>
#include "core/settings.h"
namespace Ui {
class ConfigureCpu;
}
class ConfigureCpu : public QWidget {
Q_OBJECT
public:
explicit ConfigureCpu(QWidget* parent = nullptr);
~ConfigureCpu() override;
void ApplyConfiguration();
private:
void changeEvent(QEvent* event) override;
void RetranslateUI();
void AccuracyUpdated(int index);
void SetConfiguration();
std::unique_ptr<Ui::ConfigureCpu> ui;
};

View File

@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConfigureCpu</class>
<widget class="QWidget" name="ConfigureCpu">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>321</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout">
<item>
<layout class="QVBoxLayout">
<item>
<widget class="QGroupBox">
<property name="title">
<string>General</string>
</property>
<layout class="QVBoxLayout">
<item>
<layout class="QHBoxLayout">
<item>
<widget class="QLabel">
<property name="text">
<string>Accuracy:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="accuracy">
<item>
<property name="text">
<string>Accurate</string>
</property>
</item>
<item>
<property name="text">
<string>Enable Debug Mode</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel">
<property name="wordWrap">
<bool>1</bool>
</property>
<property name="text">
<string>We recommend setting accuracy to "Accurate".</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_disable_info">
<property name="text">
<string>CPU settings are available only when game is not running.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,65 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <QComboBox>
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/settings.h"
#include "ui_configure_cpu_debug.h"
#include "yuzu/configuration/configure_cpu_debug.h"
ConfigureCpuDebug::ConfigureCpuDebug(QWidget* parent)
: QWidget(parent), ui(new Ui::ConfigureCpuDebug) {
ui->setupUi(this);
SetConfiguration();
}
ConfigureCpuDebug::~ConfigureCpuDebug() = default;
void ConfigureCpuDebug::SetConfiguration() {
const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
ui->cpuopt_page_tables->setEnabled(runtime_lock);
ui->cpuopt_page_tables->setChecked(Settings::values.cpuopt_page_tables);
ui->cpuopt_block_linking->setEnabled(runtime_lock);
ui->cpuopt_block_linking->setChecked(Settings::values.cpuopt_block_linking);
ui->cpuopt_return_stack_buffer->setEnabled(runtime_lock);
ui->cpuopt_return_stack_buffer->setChecked(Settings::values.cpuopt_return_stack_buffer);
ui->cpuopt_fast_dispatcher->setEnabled(runtime_lock);
ui->cpuopt_fast_dispatcher->setChecked(Settings::values.cpuopt_fast_dispatcher);
ui->cpuopt_context_elimination->setEnabled(runtime_lock);
ui->cpuopt_context_elimination->setChecked(Settings::values.cpuopt_context_elimination);
ui->cpuopt_const_prop->setEnabled(runtime_lock);
ui->cpuopt_const_prop->setChecked(Settings::values.cpuopt_const_prop);
ui->cpuopt_misc_ir->setEnabled(runtime_lock);
ui->cpuopt_misc_ir->setChecked(Settings::values.cpuopt_misc_ir);
ui->cpuopt_reduce_misalign_checks->setEnabled(runtime_lock);
ui->cpuopt_reduce_misalign_checks->setChecked(Settings::values.cpuopt_reduce_misalign_checks);
}
void ConfigureCpuDebug::ApplyConfiguration() {
Settings::values.cpuopt_page_tables = ui->cpuopt_page_tables->isChecked();
Settings::values.cpuopt_block_linking = ui->cpuopt_block_linking->isChecked();
Settings::values.cpuopt_return_stack_buffer = ui->cpuopt_return_stack_buffer->isChecked();
Settings::values.cpuopt_fast_dispatcher = ui->cpuopt_fast_dispatcher->isChecked();
Settings::values.cpuopt_context_elimination = ui->cpuopt_context_elimination->isChecked();
Settings::values.cpuopt_const_prop = ui->cpuopt_const_prop->isChecked();
Settings::values.cpuopt_misc_ir = ui->cpuopt_misc_ir->isChecked();
Settings::values.cpuopt_reduce_misalign_checks = ui->cpuopt_reduce_misalign_checks->isChecked();
}
void ConfigureCpuDebug::changeEvent(QEvent* event) {
if (event->type() == QEvent::LanguageChange) {
RetranslateUI();
}
QWidget::changeEvent(event);
}
void ConfigureCpuDebug::RetranslateUI() {
ui->retranslateUi(this);
}

View File

@@ -0,0 +1,31 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <QWidget>
#include "core/settings.h"
namespace Ui {
class ConfigureCpuDebug;
}
class ConfigureCpuDebug : public QWidget {
Q_OBJECT
public:
explicit ConfigureCpuDebug(QWidget* parent = nullptr);
~ConfigureCpuDebug() override;
void ApplyConfiguration();
private:
void changeEvent(QEvent* event) override;
void RetranslateUI();
void SetConfiguration();
std::unique_ptr<Ui::ConfigureCpuDebug> ui;
};

View File

@@ -0,0 +1,174 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConfigureCpuDebug</class>
<widget class="QWidget" name="ConfigureCpuDebug">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>321</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout">
<item>
<layout class="QVBoxLayout">
<item>
<widget class="QGroupBox">
<property name="title">
<string>Toggle CPU Optimizations</string>
</property>
<layout class="QVBoxLayout">
<item>
<widget class="QLabel">
<property name="wordWrap">
<bool>1</bool>
</property>
<property name="text">
<string>
&lt;div&gt;
&lt;b&gt;For debugging only.&lt;/b&gt;
&lt;br&gt;
If you're not sure what these do, keep all of these enabled.
&lt;br&gt;
These settings only take effect when CPU Accuracy is "Debug Mode".
&lt;/div&gt;
</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cpuopt_page_tables">
<property name="text">
<string>Enable inline page tables</string>
</property>
<property name="toolTip">
<string>
&lt;div style="white-space: nowrap"&gt;This optimization speeds up memory accesses by the guest program.&lt;/div&gt;
&lt;div style="white-space: nowrap"&gt;Enabling it inlines accesses to PageTable::pointers into emitted code.&lt;/div&gt;
&lt;div style="white-space: nowrap"&gt;Disabling this forces all memory accesses to go through the Memory::Read/Memory::Write functions.&lt;/div&gt;
</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cpuopt_block_linking">
<property name="text">
<string>Enable block linking</string>
</property>
<property name="toolTip">
<string>
&lt;div&gt;This optimization avoids dispatcher lookups by allowing emitted basic blocks to jump directly to other basic blocks if the destination PC is static.&lt;/div&gt;
</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cpuopt_return_stack_buffer">
<property name="text">
<string>Enable return stack buffer</string>
</property>
<property name="toolTip">
<string>
&lt;div&gt;This optimization avoids dispatcher lookups by keeping track potential return addresses of BL instructions. This approximates what happens with a return stack buffer on a real CPU.&lt;/div&gt;
</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cpuopt_fast_dispatcher">
<property name="text">
<string>Enable fast dispatcher</string>
</property>
<property name="toolTip">
<string>
&lt;div&gt;Enable a two-tiered dispatch system. A faster dispatcher written in assembly has a small MRU cache of jump destinations is used first. If that fails, dispatch falls back to the slower C++ dispatcher.&lt;/div&gt;
</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cpuopt_context_elimination">
<property name="text">
<string>Enable context elimination</string>
</property>
<property name="toolTip">
<string>
&lt;div&gt;Enables an IR optimization that reduces unnecessary accesses to the CPU context structure.&lt;/div&gt;
</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cpuopt_const_prop">
<property name="text">
<string>Enable constant propagation</string>
</property>
<property name="toolTip">
<string>
&lt;div&gt;Enables IR optimizations that involve constant propagation.&lt;/div&gt;
</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cpuopt_misc_ir">
<property name="text">
<string>Enable miscellaneous optimizations</string>
</property>
<property name="toolTip">
<string>
&lt;div&gt;Enables miscellaneous IR optimizations.&lt;/div&gt;
</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cpuopt_reduce_misalign_checks">
<property name="text">
<string>Enable misalignment check reduction</string>
</property>
<property name="toolTip">
<string>
&lt;div style="white-space: nowrap"&gt;When enabled, a misalignment is only triggered when an access crosses a page boundary.&lt;/div&gt;
&lt;div style="white-space: nowrap"&gt;When disabled, a misalignment is triggered on all misaligned accesses.&lt;/div&gt;
</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_disable_info">
<property name="text">
<string>CPU settings are available only when game is not running.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -36,7 +36,6 @@ void ConfigureDebug::SetConfiguration() {
ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args));
ui->reporting_services->setChecked(Settings::values.reporting_services);
ui->quest_flag->setChecked(Settings::values.quest_flag);
ui->disable_cpu_opt->setChecked(Settings::values.disable_cpu_opt);
ui->enable_graphics_debugging->setEnabled(!Core::System::GetInstance().IsPoweredOn());
ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug);
ui->disable_macro_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn());
@@ -51,7 +50,6 @@ void ConfigureDebug::ApplyConfiguration() {
Settings::values.program_args = ui->homebrew_args_edit->text().toStdString();
Settings::values.reporting_services = ui->reporting_services->isChecked();
Settings::values.quest_flag = ui->quest_flag->isChecked();
Settings::values.disable_cpu_opt = ui->disable_cpu_opt->isChecked();
Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked();
Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked();
Debugger::ToggleConsole();

View File

@@ -228,13 +228,6 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="disable_cpu_opt">
<property name="text">
<string>Disable CPU JIT optimizations</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View File

@@ -42,6 +42,8 @@ void ConfigureDialog::ApplyConfiguration() {
ui->filesystemTab->applyConfiguration();
ui->inputTab->ApplyConfiguration();
ui->hotkeysTab->ApplyConfiguration(registry);
ui->cpuTab->ApplyConfiguration();
ui->cpuDebugTab->ApplyConfiguration();
ui->graphicsTab->ApplyConfiguration();
ui->graphicsAdvancedTab->ApplyConfiguration();
ui->audioTab->ApplyConfiguration();
@@ -76,9 +78,10 @@ void ConfigureDialog::RetranslateUI() {
Q_DECLARE_METATYPE(QList<QWidget*>);
void ConfigureDialog::PopulateSelectionList() {
const std::array<std::pair<QString, QList<QWidget*>>, 5> items{
const std::array<std::pair<QString, QList<QWidget*>>, 6> items{
{{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->uiTab}},
{tr("System"), {ui->systemTab, ui->profileManagerTab, ui->serviceTab, ui->filesystemTab}},
{tr("CPU"), {ui->cpuTab, ui->cpuDebugTab}},
{tr("Graphics"), {ui->graphicsTab, ui->graphicsAdvancedTab}},
{tr("Audio"), {ui->audioTab}},
{tr("Controls"), {ui->inputTab, ui->hotkeysTab}}},
@@ -107,6 +110,8 @@ void ConfigureDialog::UpdateVisibleTabs() {
{ui->profileManagerTab, tr("Profiles")},
{ui->inputTab, tr("Input")},
{ui->hotkeysTab, tr("Hotkeys")},
{ui->cpuTab, tr("CPU")},
{ui->cpuDebugTab, tr("Debug")},
{ui->graphicsTab, tr("Graphics")},
{ui->graphicsAdvancedTab, tr("Advanced")},
{ui->audioTab, tr("Audio")},

View File

@@ -11,19 +11,6 @@
#include "yuzu/configuration/configure_filesystem.h"
#include "yuzu/uisettings.h"
namespace {
template <typename T>
void SetComboBoxFromData(QComboBox* combo_box, T data) {
const auto index = combo_box->findData(QVariant::fromValue(static_cast<u64>(data)));
if (index >= combo_box->count() || index < 0)
return;
combo_box->setCurrentIndex(index);
}
} // Anonymous namespace
ConfigureFilesystem::ConfigureFilesystem(QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureFilesystem>()) {
ui->setupUi(this);
@@ -73,11 +60,6 @@ void ConfigureFilesystem::setConfiguration() {
ui->cache_game_list->setChecked(UISettings::values.cache_game_list);
SetComboBoxFromData(ui->nand_size, Settings::values.nand_total_size);
SetComboBoxFromData(ui->usrnand_size, Settings::values.nand_user_size);
SetComboBoxFromData(ui->sysnand_size, Settings::values.nand_system_size);
SetComboBoxFromData(ui->sdmc_size, Settings::values.sdmc_size);
UpdateEnabledControls();
}
@@ -98,15 +80,6 @@ void ConfigureFilesystem::applyConfiguration() {
Settings::values.dump_nso = ui->dump_nso->isChecked();
UISettings::values.cache_game_list = ui->cache_game_list->isChecked();
Settings::values.nand_total_size = static_cast<Settings::NANDTotalSize>(
ui->nand_size->itemData(ui->nand_size->currentIndex()).toULongLong());
Settings::values.nand_system_size = static_cast<Settings::NANDSystemSize>(
ui->nand_size->itemData(ui->sysnand_size->currentIndex()).toULongLong());
Settings::values.nand_user_size = static_cast<Settings::NANDUserSize>(
ui->nand_size->itemData(ui->usrnand_size->currentIndex()).toULongLong());
Settings::values.sdmc_size = static_cast<Settings::SDMCSize>(
ui->nand_size->itemData(ui->sdmc_size->currentIndex()).toULongLong());
}
void ConfigureFilesystem::SetDirectory(DirectoryTarget target, QLineEdit* edit) {

View File

@@ -115,127 +115,6 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Storage Sizes</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="3" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>SD Card</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>System NAND</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="sysnand_size">
<item>
<property name="text">
<string>2.5 GB</string>
</property>
</item>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="sdmc_size">
<property name="currentText">
<string>32 GB</string>
</property>
<item>
<property name="text">
<string>1 GB</string>
</property>
</item>
<item>
<property name="text">
<string>2 GB</string>
</property>
</item>
<item>
<property name="text">
<string>4 GB</string>
</property>
</item>
<item>
<property name="text">
<string>8 GB</string>
</property>
</item>
<item>
<property name="text">
<string>16 GB</string>
</property>
</item>
<item>
<property name="text">
<string>32 GB</string>
</property>
</item>
<item>
<property name="text">
<string>64 GB</string>
</property>
</item>
<item>
<property name="text">
<string>128 GB</string>
</property>
</item>
<item>
<property name="text">
<string>256 GB</string>
</property>
</item>
<item>
<property name="text">
<string>1 TB</string>
</property>
</item>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="usrnand_size">
<item>
<property name="text">
<string>26 GB</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>User NAND</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>NAND</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="nand_size">
<item>
<property name="text">
<string>29.1 GB</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">

View File

@@ -65,6 +65,8 @@ void ConfigureGeneral::ApplyConfiguration() {
Settings::values.use_frame_limit.SetValue(ui->toggle_frame_limit->checkState() ==
Qt::Checked);
Settings::values.frame_limit.SetValue(ui->frame_limit->value());
}
if (Settings::values.use_multi_core.UsingGlobal()) {
Settings::values.use_multi_core.SetValue(ui->use_multi_core->isChecked());
}
} else {

View File

@@ -24,6 +24,7 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
ui->use_vsync->setEnabled(runtime_lock);
ui->use_assembly_shaders->setEnabled(runtime_lock);
ui->use_asynchronous_shaders->setEnabled(runtime_lock);
ui->force_30fps_mode->setEnabled(runtime_lock);
ui->anisotropic_filtering_combobox->setEnabled(runtime_lock);
@@ -32,6 +33,8 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
static_cast<int>(Settings::values.gpu_accuracy.GetValue()));
ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue());
ui->use_assembly_shaders->setChecked(Settings::values.use_assembly_shaders.GetValue());
ui->use_asynchronous_shaders->setChecked(
Settings::values.use_asynchronous_shaders.GetValue());
ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue());
ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode.GetValue());
ui->anisotropic_filtering_combobox->setCurrentIndex(
@@ -41,6 +44,10 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
ConfigurationShared::SetPerGameSetting(ui->use_vsync, &Settings::values.use_vsync);
ConfigurationShared::SetPerGameSetting(ui->use_assembly_shaders,
&Settings::values.use_assembly_shaders);
ConfigurationShared::SetPerGameSetting(ui->use_asynchronous_shaders,
&Settings::values.use_asynchronous_shaders);
ConfigurationShared::SetPerGameSetting(ui->use_asynchronous_shaders,
&Settings::values.use_asynchronous_shaders);
ConfigurationShared::SetPerGameSetting(ui->use_fast_gpu_time,
&Settings::values.use_fast_gpu_time);
ConfigurationShared::SetPerGameSetting(ui->force_30fps_mode,
@@ -67,6 +74,14 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
if (Settings::values.use_assembly_shaders.UsingGlobal()) {
Settings::values.use_assembly_shaders.SetValue(ui->use_assembly_shaders->isChecked());
}
if (Settings::values.use_asynchronous_shaders.UsingGlobal()) {
Settings::values.use_asynchronous_shaders.SetValue(
ui->use_asynchronous_shaders->isChecked());
}
if (Settings::values.use_asynchronous_shaders.UsingGlobal()) {
Settings::values.use_asynchronous_shaders.SetValue(
ui->use_asynchronous_shaders->isChecked());
}
if (Settings::values.use_fast_gpu_time.UsingGlobal()) {
Settings::values.use_fast_gpu_time.SetValue(ui->use_fast_gpu_time->isChecked());
}
@@ -83,6 +98,10 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->use_vsync);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_assembly_shaders,
ui->use_assembly_shaders);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_shaders,
ui->use_asynchronous_shaders);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_shaders,
ui->use_asynchronous_shaders);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time,
ui->use_fast_gpu_time);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.force_30fps_mode,
@@ -117,6 +136,8 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal());
ui->use_vsync->setEnabled(Settings::values.use_vsync.UsingGlobal());
ui->use_assembly_shaders->setEnabled(Settings::values.use_assembly_shaders.UsingGlobal());
ui->use_asynchronous_shaders->setEnabled(
Settings::values.use_asynchronous_shaders.UsingGlobal());
ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal());
ui->force_30fps_mode->setEnabled(Settings::values.force_30fps_mode.UsingGlobal());
ui->anisotropic_filtering_combobox->setEnabled(
@@ -128,6 +149,7 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
ConfigurationShared::InsertGlobalItem(ui->gpu_accuracy);
ui->use_vsync->setTristate(true);
ui->use_assembly_shaders->setTristate(true);
ui->use_asynchronous_shaders->setTristate(true);
ui->use_fast_gpu_time->setTristate(true);
ui->force_30fps_mode->setTristate(true);
ConfigurationShared::InsertGlobalItem(ui->anisotropic_filtering_combobox);

View File

@@ -72,6 +72,16 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="use_asynchronous_shaders">
<property name="toolTip">
<string>Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental.</string>
</property>
<property name="text">
<string>Use asynchronous shader building (experimental, OpenGL or Assembly shaders only)</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="force_30fps_mode">
<property name="text">

View File

@@ -94,6 +94,8 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include "core/perf_stats.h"
#include "core/settings.h"
#include "core/telemetry_session.h"
#include "video_core/gpu.h"
#include "video_core/shader_notify.h"
#include "yuzu/about_dialog.h"
#include "yuzu/bootmanager.h"
#include "yuzu/compatdb.h"
@@ -279,17 +281,21 @@ GMainWindow::~GMainWindow() {
}
void GMainWindow::ProfileSelectorSelectProfile() {
QtProfileSelectionDialog dialog(this);
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
dialog.setWindowModality(Qt::WindowModal);
if (dialog.exec() == QDialog::Rejected) {
emit ProfileSelectorFinishedSelection(std::nullopt);
return;
const Service::Account::ProfileManager manager;
int index = 0;
if (manager.GetUserCount() != 1) {
QtProfileSelectionDialog dialog(this);
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
dialog.setWindowModality(Qt::WindowModal);
if (dialog.exec() == QDialog::Rejected) {
emit ProfileSelectorFinishedSelection(std::nullopt);
return;
}
index = dialog.GetIndex();
}
Service::Account::ProfileManager manager;
const auto uuid = manager.GetUser(static_cast<std::size_t>(dialog.GetIndex()));
const auto uuid = manager.GetUser(static_cast<std::size_t>(index));
if (!uuid.has_value()) {
emit ProfileSelectorFinishedSelection(std::nullopt);
return;
@@ -494,6 +500,8 @@ void GMainWindow::InitializeWidgets() {
message_label->setAlignment(Qt::AlignLeft);
statusBar()->addPermanentWidget(message_label, 1);
shader_building_label = new QLabel();
shader_building_label->setToolTip(tr("The amount of shaders currently being built"));
emu_speed_label = new QLabel();
emu_speed_label->setToolTip(
tr("Current emulation speed. Values higher or lower than 100% "
@@ -506,7 +514,8 @@ void GMainWindow::InitializeWidgets() {
tr("Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For "
"full-speed emulation this should be at most 16.67 ms."));
for (auto& label : {emu_speed_label, game_fps_label, emu_frametime_label}) {
for (auto& label :
{shader_building_label, emu_speed_label, game_fps_label, emu_frametime_label}) {
label->setVisible(false);
label->setFrameStyle(QFrame::NoFrame);
label->setContentsMargins(4, 0, 4, 0);
@@ -1172,6 +1181,7 @@ void GMainWindow::ShutdownGame() {
// Disable status bar updates
status_bar_update_timer.stop();
shader_building_label->setVisible(false);
emu_speed_label->setVisible(false);
game_fps_label->setVisible(false);
emu_frametime_label->setVisible(false);
@@ -1755,7 +1765,7 @@ InstallResult GMainWindow::InstallNSPXCI(const QString& filename) {
*nsp, true, qt_raw_copy);
if (res == FileSys::InstallResult::Success) {
return InstallResult::Success;
} else if (res == FileSys::InstallResult::ErrorAlreadyExists) {
} else if (res == FileSys::InstallResult::OverwriteExisting) {
return InstallResult::Overwrite;
} else {
return InstallResult::Failure;
@@ -1842,7 +1852,7 @@ InstallResult GMainWindow::InstallNCA(const QString& filename) {
if (res == FileSys::InstallResult::Success) {
return InstallResult::Success;
} else if (res == FileSys::InstallResult::ErrorAlreadyExists) {
} else if (res == FileSys::InstallResult::OverwriteExisting) {
return InstallResult::Overwrite;
} else {
return InstallResult::Failure;
@@ -2182,6 +2192,17 @@ void GMainWindow::UpdateStatusBar() {
}
auto results = Core::System::GetInstance().GetAndResetPerfStats();
auto& shader_notify = Core::System::GetInstance().GPU().ShaderNotify();
const auto shaders_building = shader_notify.GetShadersBuilding();
if (shaders_building != 0) {
shader_building_label->setText(
tr("Building: %1 shader").arg(shaders_building) +
(shaders_building != 1 ? QString::fromStdString("s") : QString::fromStdString("")));
shader_building_label->setVisible(true);
} else {
shader_building_label->setVisible(false);
}
if (Settings::values.use_frame_limit.GetValue()) {
emu_speed_label->setText(tr("Speed: %1% / %2%")
@@ -2311,9 +2332,12 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
if (behavior == ReinitializeKeyBehavior::Warning) {
const auto res = QMessageBox::information(
this, tr("Confirm Key Rederivation"),
tr("You are about to force rederive all of your keys. \nIf you do not know what this "
"means or what you are doing, \nthis is a potentially destructive action. \nPlease "
"make sure this is what you want \nand optionally make backups.\n\nThis will delete "
tr("You are about to force rederive all of your keys. \nIf you do not know what "
"this "
"means or what you are doing, \nthis is a potentially destructive action. "
"\nPlease "
"make sure this is what you want \nand optionally make backups.\n\nThis will "
"delete "
"your autogenerated key files and re-run the key derivation module."),
QMessageBox::StandardButtons{QMessageBox::Ok, QMessageBox::Cancel});
@@ -2624,8 +2648,8 @@ int main(int argc, char* argv[]) {
#ifdef __APPLE__
// If you start a bundle (binary) on OSX without the Terminal, the working directory is "/".
// But since we require the working directory to be the executable path for the location of the
// user folder in the Qt Frontend, we need to cd into that working directory
// But since we require the working directory to be the executable path for the location of
// the user folder in the Qt Frontend, we need to cd into that working directory
const std::string bin_path = FileUtil::GetBundleDirectory() + DIR_SEP + "..";
chdir(bin_path.c_str());
#endif

View File

@@ -248,6 +248,7 @@ private:
// Status bar elements
QLabel* message_label = nullptr;
QLabel* shader_building_label = nullptr;
QLabel* emu_speed_label = nullptr;
QLabel* game_fps_label = nullptr;
QLabel* emu_frametime_label = nullptr;

View File

@@ -335,15 +335,6 @@ void Config::ReadValues() {
Settings::values.gamecard_current_game =
sdl2_config->GetBoolean("Data Storage", "gamecard_current_game", false);
Settings::values.gamecard_path = sdl2_config->Get("Data Storage", "gamecard_path", "");
Settings::values.nand_total_size = static_cast<Settings::NANDTotalSize>(sdl2_config->GetInteger(
"Data Storage", "nand_total_size", static_cast<long>(Settings::NANDTotalSize::S29_1GB)));
Settings::values.nand_user_size = static_cast<Settings::NANDUserSize>(sdl2_config->GetInteger(
"Data Storage", "nand_user_size", static_cast<long>(Settings::NANDUserSize::S26GB)));
Settings::values.nand_system_size = static_cast<Settings::NANDSystemSize>(
sdl2_config->GetInteger("Data Storage", "nand_system_size",
static_cast<long>(Settings::NANDSystemSize::S2_5GB)));
Settings::values.sdmc_size = static_cast<Settings::SDMCSize>(sdl2_config->GetInteger(
"Data Storage", "sdmc_size", static_cast<long>(Settings::SDMCSize::S16GB)));
// System
Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false);
@@ -403,6 +394,10 @@ void Config::ReadValues() {
static_cast<u16>(sdl2_config->GetInteger("Renderer", "use_vsync", 1)));
Settings::values.use_assembly_shaders.SetValue(
sdl2_config->GetBoolean("Renderer", "use_assembly_shaders", false));
Settings::values.use_asynchronous_shaders.SetValue(
sdl2_config->GetBoolean("Renderer", "use_asynchronous_shaders", false));
Settings::values.use_asynchronous_shaders.SetValue(
sdl2_config->GetBoolean("Renderer", "use_asynchronous_shaders", false));
Settings::values.use_fast_gpu_time.SetValue(
sdl2_config->GetBoolean("Renderer", "use_fast_gpu_time", true));
@@ -437,8 +432,6 @@ void Config::ReadValues() {
Settings::values.reporting_services =
sdl2_config->GetBoolean("Debugging", "reporting_services", false);
Settings::values.quest_flag = sdl2_config->GetBoolean("Debugging", "quest_flag", false);
Settings::values.disable_cpu_opt =
sdl2_config->GetBoolean("Debugging", "disable_cpu_opt", false);
Settings::values.disable_macro_jit =
sdl2_config->GetBoolean("Debugging", "disable_macro_jit", false);

View File

@@ -97,6 +97,39 @@ udp_pad_index=
# 0 (default): Disabled, 1: Enabled
use_multi_core=
[Cpu]
# Enable inline page tables optimization (faster guest memory access)
# 0: Disabled, 1 (default): Enabled
cpuopt_page_tables =
# Enable block linking CPU optimization (reduce block dispatcher use during predictable jumps)
# 0: Disabled, 1 (default): Enabled
cpuopt_block_linking =
# Enable return stack buffer CPU optimization (reduce block dispatcher use during predictable returns)
# 0: Disabled, 1 (default): Enabled
cpuopt_return_stack_buffer =
# Enable fast dispatcher CPU optimization (use a two-tiered dispatcher architecture)
# 0: Disabled, 1 (default): Enabled
cpuopt_fast_dispatcher =
# Enable context elimination CPU Optimization (reduce host memory use for guest context)
# 0: Disabled, 1 (default): Enabled
cpuopt_context_elimination =
# Enable constant propagation CPU optimization (basic IR optimization)
# 0: Disabled, 1 (default): Enabled
cpuopt_const_prop =
# Enable miscellaneous CPU optimizations (basic IR optimization)
# 0: Disabled, 1 (default): Enabled
cpuopt_misc_ir =
# Enable reduction of memory misalignment checks (reduce memory fallbacks for misaligned access)
# 0: Disabled, 1 (default): Enabled
cpuopt_reduce_misalign_checks =
[Renderer]
# Which backend API to use.
# 0 (default): OpenGL, 1: Vulkan
@@ -133,6 +166,10 @@ use_vsync =
# 0 (default): Off, 1: On
use_assembly_shaders =
# Whether to allow asynchronous shader building.
# 0 (default): Off, 1: On
use_asynchronous_shaders =
# Turns on the frame limiter, which will limit frames output to the target game speed
# 0: Off, 1: On (default)
use_frame_limit =
@@ -283,9 +320,6 @@ dump_nso=false
# Determines whether or not yuzu will report to the game that the emulated console is in Kiosk Mode
# false: Retail/Normal Mode (default), true: Kiosk Mode
quest_flag =
# Determines whether or not JIT CPU optimizations are enabled
# false: Optimizations Enabled, true: Optimizations Disabled
disable_cpu_opt =
# Enables/Disables the macro JIT compiler
disable_macro_jit=false

View File

@@ -12,6 +12,39 @@ const char* sdl2_config_file = R"(
# 0 (default): Disabled, 1: Enabled
use_multi_core=
[Cpu]
# Enable inline page tables optimization (faster guest memory access)
# 0: Disabled, 1 (default): Enabled
cpuopt_page_tables =
# Enable block linking CPU optimization (reduce block dispatcher use during predictable jumps)
# 0: Disabled, 1 (default): Enabled
cpuopt_block_linking =
# Enable return stack buffer CPU optimization (reduce block dispatcher use during predictable returns)
# 0: Disabled, 1 (default): Enabled
cpuopt_return_stack_buffer =
# Enable fast dispatcher CPU optimization (use a two-tiered dispatcher architecture)
# 0: Disabled, 1 (default): Enabled
cpuopt_fast_dispatcher =
# Enable context elimination CPU Optimization (reduce host memory use for guest context)
# 0: Disabled, 1 (default): Enabled
cpuopt_context_elimination =
# Enable constant propagation CPU optimization (basic IR optimization)
# 0: Disabled, 1 (default): Enabled
cpuopt_const_prop =
# Enable miscellaneous CPU optimizations (basic IR optimization)
# 0: Disabled, 1 (default): Enabled
cpuopt_misc_ir =
# Enable reduction of memory misalignment checks (reduce memory fallbacks for misaligned access)
# 0: Disabled, 1 (default): Enabled
cpuopt_reduce_misalign_checks =
[Renderer]
# Whether to use software or hardware rendering.
# 0: Software, 1 (default): Hardware