Compare commits

..

93 Commits

Author SHA1 Message Date
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
Rodrigo Locatti
b2305dcee0 Merge pull request #4317 from lioncash/boost
CMakeLists: Correct boost asio disabling define name
2020-07-12 19:13:10 -03:00
Lioncash
b971b82275 CMakeLists: Correct boost asio disabling define name
Previously the name of the define was missing the BOOST_ prefix.
2020-07-12 17:17:51 -04:00
Rodrigo Locatti
84b5804834 Merge pull request #4316 from lioncash/cmake-concept
CMakeLists: Disable concepts in boost asio
2020-07-12 17:18:20 -03:00
Lioncash
539675b21a CMakeLists: Disable concepts in boost asio 2020-07-12 15:56:44 -04:00
Fernando Sahmkow
739d90ee66 Merge pull request #4265 from Morph1984/file-rename
vfs_real: Fix MoveFile
2020-07-12 13:00:09 -04:00
LC
ed89bcc767 Merge pull request #4290 from lioncash/latest
CMakeLists: Make use of /std:c++latest on MSVC
2020-07-12 12:25:10 -04:00
bunnei
f1aabc21ee Merge pull request #3385 from Morph1984/batch-install
frontend: Add support to batch install files to NAND
2020-07-12 12:20:56 -04:00
Tobias
80a0f2a118 common/alignment: Fix compilation errors (#4303) 2020-07-12 16:45:49 +02:00
LC
6001538139 Merge pull request #4312 from Morph1984/fix-discord-invite
Fix Discord invite link
2020-07-12 10:17:45 -04:00
Morph
63cc4e417f Fix Discord invite link 2020-07-12 10:16:07 -04:00
bunnei
e60733aad3 Merge pull request #4275 from CrazyMax/desired_language
AM: fix GetDesiredLanguage:
2020-07-12 01:45:08 -04:00
bunnei
5fb27f83cf Merge pull request #4289 from ReinUsesLisp/dynfix
vk_rasterizer: Pass <pSizes> to CmdBindVertexBuffers2EXT
2020-07-11 23:44:03 -04:00
bunnei
f16a94fb39 Merge pull request #4295 from MerryMage/macOS-libusb
CMakeLists: Do not search for system libusb on macOS
2020-07-11 22:47:52 -04:00
bunnei
e5abf11186 Merge pull request #4305 from yuzu-emu/revert-4300-port-5441
Revert "Port citra-emu/citra#5441: "Common: remove a mod from AlignUp""
2020-07-11 19:28:18 -04:00
bunnei
1074c87f18 Revert "Port citra-emu/citra#5441: "Common: remove a mod from AlignUp"" 2020-07-11 19:28:09 -04:00
bunnei
7a051c4973 Merge pull request #4300 from FearlessTobi/port-5441
Port citra-emu/citra#5441: "Common: remove a mod from AlignUp"
2020-07-11 14:54:34 -04:00
Marshall Mohror
a4306b9e56 Common: remove a mod from AlignUp (#5441)
In cases where the size is not a known constant when inlining, AlignUp<std::size_t> currently generates two 64-bit div instructions.
This generates one div and a cmov which is significantly cheaper.
2020-07-11 18:39:00 +02:00
MerryMage
6744e7ea4a CMakeLists: Do not search for system libusb on macOS 2020-07-11 14:37:34 +01:00
Lioncash
fb0fefc75c CMakeLists: Make use of /std:c++latest on MSVC
Provides the buildbot with one builder that is always tracking the
latest version of the C++ standard, allowing us to progressively rectify
our code and amend any differences between standards over time instead
of waiting for a complete standard change, potentially breaking a lot of
code all at once.
2020-07-11 04:45:40 -04:00
bunnei
a0ee597b19 Merge pull request #4203 from VolcaEM/services
service: Update function tables
2020-07-11 00:02:36 -04:00
bunnei
a45a57641f Merge pull request #4250 from Morph1984/key-writing
KeyManager: Prevent writing of invalid keys
2020-07-10 22:45:18 -04:00
ReinUsesLisp
fca26980a2 vk_rasterizer: Pass <pSizes> to CmdBindVertexBuffers2EXT
This has been fixed in Nvidia's public beta driver 451.74. The previous
beta driver will be broken, people using these will have to update.
2020-07-10 18:15:32 -03:00
bunnei
995067538d Merge pull request #4221 from jbeich/unused-qt-opengl
cmake: drop dependency on QtOpenGL
2020-07-10 15:18:04 -04:00
bunnei
01a1adfb0c Merge pull request #4287 from lioncash/fmt7
CMakeLists: Update fmt to 7.0.1
2020-07-10 13:02:21 -04:00
Lioncash
798c1b457d CMakeLists: Update fmt to 7.0.1
Keeps the package up to date with the latest major release of fmt.

This version brings in quite a bit of improvements, such as code size
reduction, etc.
2020-07-10 04:51:03 -04:00
David
d7a2dc4cea Merge pull request #4286 from lioncash/format
CMakeLists: Use lower-case command names
2020-07-10 17:07:10 +10:00
Lioncash
d39b457566 CMakeLists: Use lower-case command names
Our convention uses lower-case command names, so this is just a
consistency change.
2020-07-10 01:18:40 -04:00
Morph
755506d404 vfs_real: Fix MoveFile
The file wasn't closed prior to being renamed / moved, throwing an error that states "The process cannot access the file because it is being used by another process." Fix this by closing the file prior to a rename / move operation.

Fixes saving in Luigi's Mansion 3 and KATANA KAMI: A Way of the Samurai Story.
2020-07-10 00:39:23 -04:00
Morph
7351ca8c75 KeyManager: Prevent writing of invalid keys
If the keys are zero, don't write them to the autogenerated file.
2020-07-10 00:39:00 -04:00
Morph
75a01475d1 Add additional empty check for the QStringList returned by the InstallDialog 2020-07-10 00:38:29 -04:00
Morph
6d8d7ebc66 Update the install and progress dialogs
- Remove the overwrite files checkbox, it will always overwrite
- The progressbar now reflects the progress in terms of data transferred.
2020-07-10 00:38:28 -04:00
Morph
7f4d96d873 Refactor batch installing files
Key issues fixed:
- Progress dialog showing up as white/hanging/getting stuck/unresponsive.

Key changes:
- Progress dialog now shows progress as a function of all files instead of per nca within a file.
- Overwrite existing files will overwrite all files in the selection.
2020-07-10 00:38:28 -04:00
Morph
4c269e5ced Add support for batch install to NAND
This adds support to batch install files to NAND
2020-07-10 00:38:28 -04:00
David
0974533c96 Merge pull request #4285 from ogniK5377/fmt-fix
cmake: Fix libfmt linking errors
2020-07-10 14:34:09 +10:00
David
df27813e76 Merge pull request #4284 from ogniK5377/libusb-fix
cmake: Fix libusb builds breaking
2020-07-10 14:33:55 +10:00
David Marcec
bd2feb6873 cmake: Fix libfmt linking errors 2020-07-10 14:04:06 +10:00
David Marcec
48aa076049 cmake: Fix libusb builds breaking 2020-07-10 14:02:44 +10:00
Rodrigo Locatti
e73c53fad1 Merge pull request #4283 from lat9nq/fix-linux-nvidia-vulkan
vk_stream_buffer: Prevent Vulkan crash in Linux on recent NVIDIA driver
2020-07-10 00:18:44 -03:00
lat9nq
63d23835ef configuration: implement per-game configurations (#4098)
* Switch game settings to use a pointer

In order to add full per-game settings, we need to be able to tell yuzu to switch
to using either the global or game configuration. Using a pointer makes it easier
to switch.

* configuration: add new UI without changing existing funcitonality

The new UI also adds General, System, Graphics, Advanced Graphics,
and Audio tabs, but as yet they do nothing. This commit keeps yuzu
to the same functionality as originally branched.

* configuration: Rename files

These weren't included in the last commit. Now they are.

* configuration: setup global configuration checkbox

Global config checkbox now enables/disables the appropriate tabs in the game
properties dialog. The use global configuration setting is now saved to the
config, defaulting to true. This also addresses some changes requested in the PR.

* configuration: swap to per-game config memory for properties dialog

Does not set memory going in-game. Swaps to game values when opening the
properties dialog, then swaps back when closing it. Uses a `memcpy` to swap.
Also implements saving config files, limited to certain groups of configurations
so as to not risk setting unsafe configurations.

* configuration: change config interfaces to use config-specific pointers

When a game is booted, we need to be able to open the configuration dialogs
without changing the settings pointer in the game's emualtion. A new pointer
specific to just the configuration dialogs can be used to separate changes
to just those config dialogs without affecting the emulation.

* configuration: boot a game using per-game settings

Swaps values where needed to boot a game.

* configuration: user correct config during emulation

Creates a new pointer specifically for modifying the configuration while
emulation is in progress. Both the regular configuration dialog and the game
properties dialog now use the pointer Settings::config_values to focus edits to
the correct struct.

* settings: split Settings::values into two different structs

By splitting the settings into two mutually exclusive structs, it becomes easier,
as a developer, to determine how to use the Settings structs after per-game
configurations is merged. Other benefits include only duplicating the required
settings in memory.

* settings: move use_docked_mode to Controls group

`use_docked_mode` is set in the input settings and cannot be accessed from the
system settings. Grouping it with system settings causes it to be saved with
per-game settings, which may make transferring configs more difficult later on,
especially since docked mode cannot be set from within the game properties
dialog.

* configuration: Fix the other yuzu executables and a regression

In main.cpp, we have to get the title ID before the ROM is loaded, else the
renderer will reflect only the global settings and now the user's game specific
settings.

* settings: use a template to duplicate memory for each setting

Replaces the type of each variable in the Settings::Values struct with a new
class that allows basic data reading and writing. The new struct
Settings::Setting duplicates the data in memory and can manage global overrides
per each setting.

* configuration: correct add-ons config and swap settings when apropriate

Any add-ons interaction happens directly through the global values struct.
Swapping bewteen structs now also includes copying the necessary global configs
that cannot be changed nor saved in per-game settings. General and System config
menus now update based on whether it is viewing the global or per-game settings.

* settings: restore old values struct

No longer needed with the Settings::Setting class template.

* configuration: implement hierarchical game properties dialog

This sets the apropriate global or local data in each setting.

* clang format

* clang format take 2

can the docker container save this?

* address comments and style issues

* config: read and write settings with global awareness

Adds new functions to read and write settings while keeping the global state in
focus. Files now generated per-game are much smaller since often they only need
address the global state.

* settings: restore global state when necessary

Upon closing a game or the game properties dialog, we need to restore all global
settings to the original global state so that we can properly open the
configuration dialog or boot a different game.

* configuration: guard setting values incorrectly

This disables setting values while a game is running if the setting is
overwritten by a per game setting.

* config: don't write local settings in the global config

Simple guards to prevent writing the wrong settings in the wrong files.

* configuration: add comments, assume less, and clang format

No longer assumes that a disabled UI element means the global state is turned
off, instead opting to directly answer that question. Still however assumes a
game is running if it is in that state.

* configuration: fix a logic error

Should not be negated

* restore settings' global state regardless of accept/cancel

Fixes loading a properties dialog and causing the global config dialog to show
local settings.

* fix more logic errors

Fixed the frame limit would set the global setting from the game properties
dialog. Also strengthened the Settings::Setting member variables and simplified
the logic in config reading (ReadSettingGlobal).

* fix another logic error

In my efforts to guard RestoreGlobalState, I accidentally negated the IsPowered
condition.

* configure_audio: set toggle_stretched_audio to tristate

* fixed custom rtc and rng seed overwriting the global value

* clang format

* rebased

* clang format take 4

* address my own review

Basically revert unintended changes

* settings: literal instead of casting

"No need to cast, use 1U instead"
Thanks, Morph!

Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com>

* Revert "settings: literal instead of casting
"

This reverts commit 95e992a87c898f3e882ffdb415bb0ef9f80f613f.

* main: fix status buttons reporting wrong settings after stop emulation

* settings: Log UseDockedMode in the Controls group

This should have happened when use_docked_mode was moved over to the controls group
internally. This just reflects this in the log.

* main: load settings if the file has a title id

In other words, don't exit if the loader has trouble getting a title id.

* use a zero

* settings: initalize resolution factor with constructor instead of casting

* Revert "settings: initalize resolution factor with constructor instead of casting"

This reverts commit 54c35ecb46a29953842614620f9b7de1aa9d5dc8.

* configure_graphics: guard device selector when Vulkan is global

Prevents the user from editing the device selector if Vulkan is the global
renderer backend. Also resets the vulkan_device variable when the users
switches back-and-forth between global and Vulkan.

* address reviewer concerns

Changes function variables to const wherever they don't need to be changed. Sets Settings::Setting to final as it should not be inherited from. Sets ConfigurationShared::use_global_text to static.

Co-Authored-By: VolcaEM <volcaem@users.noreply.github.com>

* main: load per-game settings after LoadROM

This prevents `Restart Emulation` from restoring the global settings *after* the per-game settings were applied. Thanks to BSoDGamingYT for finding this bug.

* Revert "main: load per-game settings after LoadROM"

This reverts commit 9d0d48c52d2dcf3bfb1806cc8fa7d5a271a8a804.

* main: only restore global settings when necessary

Loading the per-game settings cannot happen after the ROM is loaded, so we have to specify when to restore the global state. Again thanks to BSoD for finding the bug.

* configuration_shared: address reviewer concerns except operator overrides

Dropping operator override usage in next commit.

Co-Authored-By: LC <lioncash@users.noreply.github.com>

* settings: Drop operator overrides from Setting template

Requires using GetValue and SetValue explicitly. Also reverts a change that broke title ID formatting in the game properties dialog.

* complete rebase

* configuration_shared: translate "Use global configuration"

Uses ConfigurePerGame to do so, since its usage, at least as of now, corresponds with ConfigurationShared.

* configure_per_game: address reviewer concern

As far as I understand, it prevents the program from unnecessarily copying strings.

Co-Authored-By: LC <lioncash@users.noreply.github.com>

Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com>
Co-authored-by: VolcaEM <volcaem@users.noreply.github.com>
Co-authored-by: LC <lioncash@users.noreply.github.com>
2020-07-09 22:42:09 -04:00
bunnei
0f60ccdde2 Merge pull request #4281 from RealJohnGalt/linkfix2
cmake: fix fmt linking when found
2020-07-09 22:11:38 -04:00
bunnei
f9e748b721 Merge pull request #4248 from Morph1984/CreateManagedDisplaySeparableLayer
AM/ISelfController: Stub CreateManagedDisplaySeparableLayer
2020-07-09 22:09:42 -04:00
lat9nq
1c7d106aac vk_stream_buffer: set allocable_size to 9 MiB
This solves the crash on Linux systems running the current Linux Long Lived branch nVidia driver.
2020-07-09 21:28:32 -04:00
bunnei
83b1b259bd Merge pull request #4202 from ReinUsesLisp/scoped-lock
core_timing,scheduler: Use std::scoped_lock when possible
2020-07-09 15:33:41 -04:00
John Galt
95cd2e17ab cmake: fix fmt linking when found
This is a new attempt at #4206 that shouldn't break windows builds.

If someone else could test on windows, it would be much appreciated.
Previously, the build bot passed but the actual builds failed.
2020-07-09 11:35:28 -04:00
bunnei
77bdd09aa2 Merge pull request #4219 from ogniK5377/audio-timing
audio: Improving audio timing for multicore/single core
2020-07-08 22:07:17 -04:00
bunnei
5311b562aa Merge pull request #4266 from jbeich/freebsd
gcadapter: unbreak build on FreeBSD
2020-07-08 16:30:26 -04:00
bunnei
9c2773409a Merge pull request #4255 from lioncash/copy
configure_graphics: Prevent unnecessary string copies in UpdateDeviceComboBox()
2020-07-08 16:28:10 -04:00
bunnei
713e204a8a Merge pull request #4276 from ameerj/master
input_common: Add more libusb error checks for GC Adapter
2020-07-08 15:34:37 -04:00
Ameer
3c7a115afe Address comments for better clarity/signed dev count 2020-07-08 14:18:54 -04:00
Ameer
ec13746e4d Add more libusb error checks 2020-07-08 13:19:33 -04:00
CrazyMax
cf76769026 AM: fix GetDesiredLanguage:
try to get a control metadata from application update when is failed to get from the basic version.

Tested on Kirby Star Allies
2020-07-08 19:45:06 +03:00
bunnei
4e2464a713 Merge pull request #4243 from CrazyMax/display_version
AM: fix GetDisplayVersion
2020-07-08 10:46:56 -04:00
bunnei
2a87ddcce2 Merge pull request #4245 from MerryMage/page-table-race
memory: Race-condition in pagetables.
2020-07-08 10:44:33 -04:00
merry
dff8f1f9f8 Merge pull request #4270 from MerryMage/rm-pragma-once
cpu_interrupt_handler: Remove #pragma once from .cpp file
2020-07-07 23:13:55 +01:00
MerryMage
e7c77b90c4 cpu_interrupt_handler: Remove #pragma once from .cpp file 2020-07-07 22:47:00 +01:00
bunnei
6ae0b83442 Merge pull request #4198 from FearlessTobi/port-5364
Port citra-emu/citra#5364: ".github: add a new issue template"
2020-07-07 15:02:06 -04:00
bunnei
35f7740b6c Merge pull request #4150 from ReinUsesLisp/dynamic-state-impl
vulkan: Use VK_EXT_extended_dynamic_state when available
2020-07-07 10:58:09 -04:00
Jan Beich
2af7e40ce6 cmake: unbreak system libusb support
Reference libusb doesn't support DragonFly and FreeBSD because those
ship a different libusb implementation (supports 0.1, 1.0, 2.0 API).

ld: error: undefined symbol: usbi_backend
>>> referenced by core.c
>>>               core.c.o:(usbi_alloc_device) in archive externals/libusb/libusb.a

ld: error: undefined symbol: usbi_backend
>>> referenced by core.c
>>>               core.c.o:(usbi_alloc_device) in archive externals/libusb/libusb.a

ld: error: undefined symbol: usbi_backend
>>> referenced by core.c
>>>               core.c.o:(libusb_has_capability) in archive externals/libusb/libusb.a

ld: error: undefined symbol: usbi_backend
>>> referenced by core.c
>>>               core.c.o:(libusb_has_capability) in archive externals/libusb/libusb.a

ld: error: undefined symbol: usbi_backend
>>> referenced by core.c
>>>               core.c.o:(libusb_has_capability) in archive externals/libusb/libusb.a

ld: error: undefined symbol: usbi_backend
>>> referenced by core.c
>>>               core.c.o:(usbi_connect_device) in archive externals/libusb/libusb.a

ld: error: undefined symbol: usbi_backend
>>> referenced by core.c
>>>               core.c.o:(usbi_disconnect_device) in archive externals/libusb/libusb.a

ld: error: undefined symbol: usbi_backend
>>> referenced by core.c
>>>               core.c.o:(libusb_get_device_list) in archive externals/libusb/libusb.a

ld: error: undefined symbol: usbi_backend
>>> referenced by core.c
>>>               core.c.o:(libusb_get_device_list) in archive externals/libusb/libusb.a

ld: error: undefined symbol: usbi_backend
>>> referenced by core.c
>>>               core.c.o:(libusb_unref_device) in archive externals/libusb/libusb.a

ld: error: undefined symbol: usbi_backend
>>> referenced by core.c
>>>               core.c.o:(libusb_unref_device) in archive externals/libusb/libusb.a

ld: error: undefined symbol: usbi_write
>>> referenced by core.c
>>>               core.c.o:(usbi_signal_event) in archive externals/libusb/libusb.a

ld: error: undefined symbol: usbi_read
>>> referenced by core.c
>>>               core.c.o:(usbi_clear_event) in archive externals/libusb/libusb.a

ld: error: undefined symbol: usbi_backend
>>> referenced by core.c
>>>               core.c.o:(libusb_wrap_sys_device) in archive externals/libusb/libusb.a

ld: error: undefined symbol: usbi_backend
>>> referenced by core.c
>>>               core.c.o:(libusb_wrap_sys_device) in archive externals/libusb/libusb.a

ld: error: undefined symbol: usbi_backend
>>> referenced by core.c
>>>               core.c.o:(libusb_wrap_sys_device) in archive externals/libusb/libusb.a

ld: error: undefined symbol: usbi_backend
>>> referenced by core.c
>>>               core.c.o:(libusb_open) in archive externals/libusb/libusb.a

ld: error: undefined symbol: usbi_backend
>>> referenced by core.c
>>>               core.c.o:(libusb_open) in archive externals/libusb/libusb.a

ld: error: undefined symbol: usbi_read
>>> referenced by core.c
>>>               core.c.o:(libusb_close) in archive externals/libusb/libusb.a

ld: error: undefined symbol: usbi_write
>>> referenced by core.c
>>>               core.c.o:(libusb_close) in archive externals/libusb/libusb.a

ld: error: too many errors emitted, stopping now (use -error-limit=0 to see all errors)
c++: error: linker command failed with exit code 1 (use -v to see invocation)
2020-07-07 10:13:18 +00:00
Jan Beich
1db10b8f4c input_common/gcadapter: add missing C++11 header required by libc++
In file included from src/input_common/gcadapter/gc_adapter.cpp:8:
src/./input_common/gcadapter/gc_adapter.h:77:10: error: no template named 'unordered_map' in namespace 'std'
    std::unordered_map<int, bool> buttons;
    ~~~~~^
src/./input_common/gcadapter/gc_adapter.h:78:10: error: no template named 'unordered_map' in namespace 'std'
    std::unordered_map<int, u16> axes;
    ~~~~~^
2020-07-07 10:02:03 +00:00
bunnei
155c8ba04c Merge pull request #4258 from ameerj/master
configuration: Fix hotkeys that open file dialog in windowed mode
2020-07-06 23:09:52 -04:00
Ameer
40cf9288e9 change shortcut context for other hotkeys with file open dialog 2020-07-06 19:45:57 -04:00
CrazyMax
7bd3558c64 GetDisplayVersion should return a null-terminated version string.
also, in case of failed to get of the basic version, we will try get it from application update.
2020-07-07 02:04:24 +03:00
Ameer
b700d657d7 Revert inadvertent dynarmic update 2020-07-06 18:57:02 -04:00
Ameer
fa4d6df4c5 Fix ss crash on game menu, fix ss on windowed mode 2020-07-06 17:30:28 -04:00
Lioncash
4f678284e2 configure_graphics: Make use of qOverload in signals/slots
While we're in the same area, we can make use of qOverload to tidy up
some function pointer casts.
2020-07-06 09:07:21 -04:00
Lioncash
be06b21f7b configure_graphics: Prevent unnecessary string copies in UpdateDeviceComboBox()
Unlikely to impact performance at all, but this is essentially a "free"
transformation, so why not?
2020-07-06 09:07:03 -04:00
MerryMage
76bd617b2d memory: Set page-table pointers before setting attribute = Memory 2020-07-05 12:54:19 +01:00
Morph
c0dc8f9d25 AM/ISelfController: Stub CreateManagedDisplaySeparableLayer
Stub this by sending 1 layer id instead of 2 as yuzu does not support multiple layers per display.
No adverse side effects have been observed.
- Used by Animal Crossing: New Horizons Update 1.3.0
2020-07-05 06:26:21 -04:00
Fernando Sahmkow
52882a93a5 Merge pull request #4194 from ReinUsesLisp/fix-shader-cache
shader_cache: Fix use-after-free and orphan invalidation cache entries
2020-07-04 20:49:00 -04:00
bunnei
9f8e17cb18 Merge pull request #4137 from ameerj/master
GC Adapter Implementation
2020-07-04 10:05:59 -04:00
VolcaEM
3f910efb40 Rename two functions in NS
- Rename "GetShellEvent" to "GetShellEventHandle"
- Rename "LaunchApplicationFromHost" to "LaunchApplication"
2020-07-02 09:02:55 +02:00
VolcaEM
38b585a309 Rename GetApplicationArea2 to GetApplicationAreaSize 2020-07-02 08:58:51 +02:00
Jan Beich
b1b1ed7597 cmake: stop linking against QGL after c6a0ab9792 2020-07-01 22:11:39 +00:00
ReinUsesLisp
f6cb128eac shader_cache: Fix use-after-free and orphan invalidation cache entries
This fixes some cases where entries could have been removed multiple
times reading freed memory. To address this issue this commit removes
duplicates from entries marked for removal and sorts out the removal
process to fix another use-after-free situation.

Another issue fixed in this commit is orphan invalidation cache entries.
Previously only the entries that were invalidated in the current
operations had its entries removed. This led to more use-after-free
situations when these entries were actually invalidated but referenced
an object that didn't exist.
2020-07-01 18:16:53 -03:00
David Marcec
28aa697528 Don't handle cycles late if stretcher is active
Timestretcher seems to be broken
2020-07-01 16:59:42 +10:00
David Marcec
24c2930012 audio: Improving audio timing for multicore/single core
Fixes the issue with needing the timestretcher for multicore.
2020-07-01 16:52:02 +10:00
VolcaEM
86946ea13c Remove duplicate functions 2020-06-29 04:22:38 +02:00
VolcaEM
f3630a0713 Use decimal instead of hexadecimal
Co-authored-by: David <25727384+ogniK5377@users.noreply.github.com>
2020-06-29 04:21:10 +02:00
VolcaEM
a0c499aef7 Fix typo 2020-06-29 04:12:36 +02:00
VolcaEM
f2eead3b5b Clang-format 2020-06-29 04:09:38 +02:00
VolcaEM
6a0010d0c6 service: Update function tables 2020-06-29 04:01:34 +02:00
ReinUsesLisp
8562b516c0 core_timing,scheduler: Use std::scoped_lock when possible
Simplifies the cognitive load of procedures using locks and makes locks
safe against exceptions.
2020-06-28 21:42:57 -03:00
tgsm
9ff3401133 .github: add a new issue template 2020-06-28 20:46:46 +02:00
ReinUsesLisp
9d55e5586f vk_rasterizer: Use nullptr for <pSizes> in CmdBindVertexBuffers2EXT
Disable this temporarily.
2020-06-26 20:57:22 -03:00
ReinUsesLisp
8584a77eb2 vk_pipeline_cache: Avoid hashing and comparing dynamic state when possible
With extended dynamic states, some bytes don't have to be collected from
the pipeline key, hence we can avoid hashing and comparing them on
lookups.
2020-06-26 20:57:22 -03:00
ReinUsesLisp
1a84209418 vulkan/fixed_pipeline_state: Move state out of individual structures 2020-06-26 20:57:22 -03:00
ReinUsesLisp
c94b398f14 vk_rasterizer: Use VK_EXT_extended_dynamic_state 2020-06-26 20:57:22 -03:00
ReinUsesLisp
a6db8e5f4d renderer_vulkan/wrapper: Add VK_EXT_extended_dynamic_state functions 2020-06-26 20:55:15 -03:00
ReinUsesLisp
c387a72c76 fixed_pipeline_state: Add requirements for VK_EXT_extended_dynamic_state
This moves dynamic state present in VK_EXT_extended_dynamic_state to a
separate structure in FixedPipelineState. This is structure is at the
bottom allowing us to hash and memcmp only when the extension is not
supported.
2020-06-26 20:55:15 -03:00
ReinUsesLisp
7527402a46 vk_device: Enable VK_EXT_extended_dynamic_state when available 2020-06-26 20:55:15 -03:00
119 changed files with 3877 additions and 1861 deletions

View File

@@ -1,4 +1,13 @@
<!--
---
name: Bug Report / Feature Request
about: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with yuzu or you are requesting a feature you believe would make yuzu better.
title: ''
labels: ''
assignees: ''
---
<!---
Please keep in mind yuzu is EXPERIMENTAL SOFTWARE.
Please read the FAQ:

8
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: yuzu Discord
url: https://discord.com/invite/u77vRWY
about: If you are experiencing an issue with yuzu, and you need tech support, or if you have a general question, try asking in the official yuzu Discord linked here. Piracy is not allowed.
- name: Community forums
url: https://community.citra-emu.org
about: This is an alternative place for tech support, however helpers there are not as active.

View File

@@ -118,8 +118,17 @@ message(STATUS "Target architecture: ${ARCHITECTURE}")
# Configure C++ standard
# ===========================
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 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)
else()
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
endif()
# Output binaries to bin/
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
@@ -151,7 +160,7 @@ macro(yuzu_find_packages)
# Cmake Pkg Prefix Version Conan Pkg
"Boost 1.71 boost/1.72.0"
"Catch2 2.11 catch2/2.11.0"
"fmt 6.2 fmt/6.2.0"
"fmt 7.0 fmt/7.0.1"
# can't use until https://github.com/bincrafters/community/issues/1173
#"libzip 1.5 libzip/1.5.2@bincrafters/stable"
"lz4 1.8 lz4/1.9.2"
@@ -211,7 +220,7 @@ if(ENABLE_QT)
set(QT_PREFIX_HINT HINTS "${QT_PREFIX}")
endif()
find_package(Qt5 5.9 COMPONENTS Widgets OpenGL ${QT_PREFIX_HINT})
find_package(Qt5 5.9 COMPONENTS Widgets ${QT_PREFIX_HINT})
if (YUZU_USE_QT_WEB_ENGINE)
find_package(Qt5 COMPONENTS WebEngineCore WebEngineWidgets)
endif()
@@ -287,7 +296,7 @@ if (CONAN_REQUIRED_LIBS)
if(ENABLE_QT)
list(APPEND CMAKE_MODULE_PATH "${CONAN_QT_ROOT_RELEASE}")
list(APPEND CMAKE_PREFIX_PATH "${CONAN_QT_ROOT_RELEASE}")
find_package(Qt5 5.9 REQUIRED COMPONENTS Widgets OpenGL)
find_package(Qt5 5.9 REQUIRED COMPONENTS Widgets)
if (YUZU_USE_QT_WEB_ENGINE)
find_package(Qt5 REQUIRED COMPONENTS WebEngineCore WebEngineWidgets)
endif()
@@ -330,10 +339,14 @@ elseif(SDL2_FOUND)
endif()
# Ensure libusb is properly configured (based on dolphin libusb include)
find_package(LibUSB)
add_subdirectory(externals/libusb)
set(LIBUSB_LIBRARIES usb)
if(NOT APPLE)
include(FindPkgConfig)
find_package(LibUSB)
endif()
if (NOT LIBUSB_FOUND)
add_subdirectory(externals/libusb)
set(LIBUSB_LIBRARIES usb)
endif()
# Prefer the -pthread flag on Linux.
set(THREADS_PREFER_PTHREAD_FLAG ON)

View File

@@ -15,7 +15,6 @@ function(copy_yuzu_Qt5_deps target_dir)
icuuc*.dll
Qt5Core$<$<CONFIG:Debug>:d>.*
Qt5Gui$<$<CONFIG:Debug>:d>.*
Qt5OpenGL$<$<CONFIG:Debug>:d>.*
Qt5Widgets$<$<CONFIG:Debug>:d>.*
)

View File

@@ -2,7 +2,7 @@ yuzu emulator
=============
[![Travis CI Build Status](https://travis-ci.com/yuzu-emu/yuzu.svg?branch=master)](https://travis-ci.com/yuzu-emu/yuzu)
[![Azure Mainline CI Build Status](https://dev.azure.com/yuzu-emu/yuzu/_apis/build/status/yuzu%20mainline?branchName=master)](https://dev.azure.com/yuzu-emu/yuzu/)
[![Discord](https://img.shields.io/discord/398318088170242053?color=%237289DA&label=yuzu&logo=discord&logoColor=white)](https://discord.gg/XQV6dn9)
[![Discord](https://img.shields.io/discord/398318088170242053?color=%237289DA&label=yuzu&logo=discord&logoColor=white)](https://discord.com/invite/u77vRWY)
yuzu is an experimental open-source emulator for the Nintendo Switch from the creators of [Citra](https://citra-emu.org/).
@@ -16,7 +16,7 @@ yuzu is licensed under the GPLv2 (or any later version). Refer to the license.tx
Check out our [website](https://yuzu-emu.org/)!
For development discussion, please join us on [Discord](https://discord.gg/XQV6dn9).
For development discussion, please join us on [Discord](https://discord.com/invite/u77vRWY).
### Development

43
externals/find-modules/FindLibUSB.cmake vendored Normal file
View File

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

View File

@@ -193,7 +193,7 @@ long CubebSinkStream::DataCallback(cubeb_stream* stream, void* user_data, const
const std::size_t samples_to_write = num_channels * num_frames;
std::size_t samples_written;
if (Settings::values.enable_audio_stretching) {
if (Settings::values.enable_audio_stretching.GetValue()) {
const std::vector<s16> in{impl->queue.Pop()};
const std::size_t num_in{in.size() / num_channels};
s16* const out{reinterpret_cast<s16*>(buffer)};

View File

@@ -38,7 +38,7 @@ Stream::Stream(Core::Timing::CoreTiming& core_timing, u32 sample_rate, Format fo
sink_stream{sink_stream}, core_timing{core_timing}, name{std::move(name_)} {
release_event = Core::Timing::CreateEvent(
name, [this](u64 userdata, s64 cycles_late) { ReleaseActiveBuffer(); });
name, [this](u64 userdata, s64 cycles_late) { ReleaseActiveBuffer(cycles_late); });
}
void Stream::Play() {
@@ -66,15 +66,6 @@ s64 Stream::GetBufferReleaseNS(const Buffer& buffer) const {
return ns.count();
}
s64 Stream::GetBufferReleaseNSHostTiming(const Buffer& buffer) const {
const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
/// DSP signals before playing the last sample, in HLE we emulate this in this way
s64 base_samples = std::max<s64>(static_cast<s64>(num_samples) - 1, 0);
const auto ns =
std::chrono::nanoseconds((static_cast<u64>(base_samples) * 1000000000ULL) / sample_rate);
return ns.count();
}
static void VolumeAdjustSamples(std::vector<s16>& samples, float game_volume) {
const float volume{std::clamp(Settings::Volume() - (1.0f - game_volume), 0.0f, 1.0f)};
@@ -89,7 +80,7 @@ static void VolumeAdjustSamples(std::vector<s16>& samples, float game_volume) {
}
}
void Stream::PlayNextBuffer() {
void Stream::PlayNextBuffer(s64 cycles_late) {
if (!IsPlaying()) {
// Ensure we are in playing state before playing the next buffer
sink_stream.Flush();
@@ -114,18 +105,17 @@ void Stream::PlayNextBuffer() {
sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
if (core_timing.IsHostTiming()) {
core_timing.ScheduleEvent(GetBufferReleaseNSHostTiming(*active_buffer), release_event, {});
} else {
core_timing.ScheduleEvent(GetBufferReleaseNS(*active_buffer), release_event, {});
}
core_timing.ScheduleEvent(
GetBufferReleaseNS(*active_buffer) -
(Settings::values.enable_audio_stretching.GetValue() ? 0 : cycles_late),
release_event, {});
}
void Stream::ReleaseActiveBuffer() {
void Stream::ReleaseActiveBuffer(s64 cycles_late) {
ASSERT(active_buffer);
released_buffers.push(std::move(active_buffer));
release_callback();
PlayNextBuffer();
PlayNextBuffer(cycles_late);
}
bool Stream::QueueBuffer(BufferPtr&& buffer) {

View File

@@ -90,10 +90,10 @@ public:
private:
/// Plays the next queued buffer in the audio stream, starting playback if necessary
void PlayNextBuffer();
void PlayNextBuffer(s64 cycles_late = 0);
/// Releases the actively playing buffer, signalling that it has been completed
void ReleaseActiveBuffer();
void ReleaseActiveBuffer(s64 cycles_late = 0);
/// Gets the number of core cycles when the specified buffer will be released
s64 GetBufferReleaseNS(const Buffer& buffer) const;

View File

@@ -11,7 +11,9 @@ namespace Common {
template <typename T>
constexpr T AlignUp(T value, std::size_t size) {
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
return static_cast<T>(value + (size - value % size) % size);
auto mod{static_cast<T>(value % size)};
value -= mod;
return static_cast<T>(mod == T{0} ? value : value + size);
}
template <typename T>

View File

@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/thread.h"
#include "core/arm/cpu_interrupt_handler.h"

View File

@@ -147,8 +147,8 @@ struct System::Impl {
device_memory = std::make_unique<Core::DeviceMemory>(system);
is_multicore = Settings::values.use_multi_core;
is_async_gpu = is_multicore || Settings::values.use_asynchronous_gpu_emulation;
is_multicore = Settings::values.use_multi_core.GetValue();
is_async_gpu = is_multicore || Settings::values.use_asynchronous_gpu_emulation.GetValue();
kernel.SetMulticore(is_multicore);
cpu_manager.SetMulticore(is_multicore);
@@ -162,7 +162,7 @@ struct System::Impl {
const auto current_time = std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch());
Settings::values.custom_rtc_differential =
Settings::values.custom_rtc.value_or(current_time) - current_time;
Settings::values.custom_rtc.GetValue().value_or(current_time) - current_time;
// Create a default fs if one doesn't already exist.
if (virtual_filesystem == nullptr)

View File

@@ -172,7 +172,7 @@ void CoreTiming::ClearPendingEvents() {
}
void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
basic_lock.lock();
std::scoped_lock lock{basic_lock};
const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
return e.type.lock().get() == event_type.get();
@@ -183,12 +183,10 @@ void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
event_queue.erase(itr, event_queue.end());
std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
}
basic_lock.unlock();
}
std::optional<s64> CoreTiming::Advance() {
std::scoped_lock advance_scope{advance_lock};
std::scoped_lock basic_scope{basic_lock};
std::scoped_lock lock{advance_lock, basic_lock};
global_timer = GetGlobalTimeNs().count();
while (!event_queue.empty() && event_queue.front().time <= global_timer) {

View File

@@ -695,8 +695,9 @@ void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname,
}
void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
if (s128_keys.find({id, field1, field2}) != s128_keys.end())
if (s128_keys.find({id, field1, field2}) != s128_keys.end() || key == Key128{}) {
return;
}
if (id == S128KeyType::Titlekey) {
Key128 rights_id;
std::memcpy(rights_id.data(), &field2, sizeof(u64));
@@ -716,8 +717,9 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
return std::tie(elem.second.type, elem.second.field1, elem.second.field2) ==
std::tie(id, field1, field2);
});
if (iter2 != s128_file_id.end())
if (iter2 != s128_file_id.end()) {
WriteKeyToFile(category, iter2->first, key);
}
// Variable cases
if (id == S128KeyType::KeyArea) {
@@ -745,16 +747,18 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
}
void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
if (s256_keys.find({id, field1, field2}) != s256_keys.end())
if (s256_keys.find({id, field1, field2}) != s256_keys.end() || key == Key256{}) {
return;
}
const auto iter = std::find_if(
s256_file_id.begin(), s256_file_id.end(),
[&id, &field1, &field2](const std::pair<std::string, KeyIndex<S256KeyType>> elem) {
return std::tie(elem.second.type, elem.second.field1, elem.second.field2) ==
std::tie(id, field1, field2);
});
if (iter != s256_file_id.end())
if (iter != s256_file_id.end()) {
WriteKeyToFile(KeyCategory::Standard, iter->first, key);
}
s256_keys[{id, field1, field2}] = key;
}

View File

@@ -112,19 +112,26 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_
const auto new_path =
FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path))
return nullptr;
if (cache.find(old_path) != cache.end()) {
auto cached = cache[old_path];
if (!cached.expired()) {
auto file = cached.lock();
file->Open(new_path, "r+b");
cache.erase(old_path);
cache[new_path] = file;
auto file = cache[old_path].lock();
if (!cache[old_path].expired()) {
file->Close();
}
if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path)) {
return nullptr;
}
cache.erase(old_path);
file->Open(new_path, "r+b");
cache[new_path] = file;
} else {
UNREACHABLE();
return nullptr;
}
return OpenFile(new_path, Mode::ReadWrite);
}

View File

@@ -29,7 +29,7 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height) {
const float window_aspect_ratio = static_cast<float>(height) / width;
const float emulation_aspect_ratio = EmulationAspectRatio(
static_cast<AspectRatio>(Settings::values.aspect_ratio), window_aspect_ratio);
static_cast<AspectRatio>(Settings::values.aspect_ratio.GetValue()), window_aspect_ratio);
const Common::Rectangle<u32> screen_window_area{0, 0, width, height};
Common::Rectangle<u32> screen = MaxRectangle(screen_window_area, emulation_aspect_ratio);

View File

@@ -123,7 +123,7 @@ std::shared_ptr<Process> Process::Create(Core::System& system, std::string name,
: kernel.CreateNewUserProcessID();
process->capabilities.InitializeForMetadatalessProcess();
std::mt19937 rng(Settings::values.rng_seed.value_or(0));
std::mt19937 rng(Settings::values.rng_seed.GetValue().value_or(0));
std::uniform_int_distribution<u64> distribution;
std::generate(process->random_entropy.begin(), process->random_entropy.end(),
[&] { return distribution(rng); });

View File

@@ -6,6 +6,7 @@
// licensed under GPLv2 or later under exception provided by the author.
#include <algorithm>
#include <mutex>
#include <set>
#include <unordered_set>
#include <utility>
@@ -31,22 +32,20 @@ GlobalScheduler::GlobalScheduler(KernelCore& kernel) : kernel{kernel} {}
GlobalScheduler::~GlobalScheduler() = default;
void GlobalScheduler::AddThread(std::shared_ptr<Thread> thread) {
global_list_guard.lock();
std::scoped_lock lock{global_list_guard};
thread_list.push_back(std::move(thread));
global_list_guard.unlock();
}
void GlobalScheduler::RemoveThread(std::shared_ptr<Thread> thread) {
global_list_guard.lock();
std::scoped_lock lock{global_list_guard};
thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
thread_list.end());
global_list_guard.unlock();
}
u32 GlobalScheduler::SelectThreads() {
ASSERT(is_locked);
const auto update_thread = [](Thread* thread, Scheduler& sched) {
sched.guard.lock();
std::scoped_lock lock{sched.guard};
if (thread != sched.selected_thread_set.get()) {
if (thread == nullptr) {
++sched.idle_selection_count;
@@ -57,7 +56,6 @@ u32 GlobalScheduler::SelectThreads() {
sched.is_context_switch_pending || (sched.selected_thread_set != sched.current_thread);
sched.is_context_switch_pending = reschedule_pending;
std::atomic_thread_fence(std::memory_order_seq_cst);
sched.guard.unlock();
return reschedule_pending;
};
if (!is_reselection_pending.load()) {
@@ -757,11 +755,12 @@ void Scheduler::OnSwitch(void* this_scheduler) {
void Scheduler::SwitchToCurrent() {
while (true) {
guard.lock();
selected_thread = selected_thread_set;
current_thread = selected_thread;
is_context_switch_pending = false;
guard.unlock();
{
std::scoped_lock lock{guard};
selected_thread = selected_thread_set;
current_thread = selected_thread;
is_context_switch_pending = false;
}
while (!is_context_switch_pending) {
if (current_thread != nullptr && !current_thread->IsHLEThread()) {
current_thread->context_guard.lock();

View File

@@ -10,6 +10,7 @@
#include "core/core.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/savedata_factory.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/kernel.h"
@@ -271,7 +272,7 @@ ISelfController::ISelfController(Core::System& system,
{41, nullptr, "IsSystemBufferSharingEnabled"},
{42, nullptr, "GetSystemSharedLayerHandle"},
{43, nullptr, "GetSystemSharedBufferHandle"},
{44, nullptr, "CreateManagedDisplaySeparableLayer"},
{44, &ISelfController::CreateManagedDisplaySeparableLayer, "CreateManagedDisplaySeparableLayer"},
{45, nullptr, "SetManagedDisplayLayerSeparationMode"},
{50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"},
{51, nullptr, "ApproveToDisplay"},
@@ -461,6 +462,24 @@ void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx)
rb.Push(*layer_id);
}
void ISelfController::CreateManagedDisplaySeparableLayer(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
// TODO(Subv): Find out how AM determines the display to use, for now just
// create the layer in the Default display.
// This calls nn::vi::CreateRecordingLayer() which creates another layer.
// Currently we do not support more than 1 layer per display, output 1 layer id for now.
// Outputting 1 layer id instead of the expected 2 has not been observed to cause any adverse
// side effects.
// TODO: Support multiple layers
const auto display_id = nvflinger->OpenDisplay("Default");
const auto layer_id = nvflinger->CreateLayer(*display_id);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push(*layer_id);
}
void ISelfController::SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
@@ -730,14 +749,14 @@ void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext&
if (Settings::values.use_docked_mode) {
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) *
static_cast<u32>(Settings::values.resolution_factor));
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) *
static_cast<u32>(Settings::values.resolution_factor));
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
} else {
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) *
static_cast<u32>(Settings::values.resolution_factor));
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight) *
static_cast<u32>(Settings::values.resolution_factor));
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
}
}
@@ -1353,14 +1372,25 @@ void IApplicationFunctions::GetDisplayVersion(Kernel::HLERequestContext& ctx) {
std::array<u8, 0x10> version_string{};
FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()};
const auto res = pm.GetControlMetadata();
const auto res = [this] {
const auto title_id = system.CurrentProcess()->GetTitleID();
FileSys::PatchManager pm{title_id};
auto res = pm.GetControlMetadata();
if (res.first != nullptr) {
return res;
}
FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id)};
return pm_update.GetControlMetadata();
}();
if (res.first != nullptr) {
const auto& version = res.first->GetVersionString();
std::copy(version.begin(), version.end(), version_string.begin());
} else {
constexpr u128 default_version = {1, 0};
std::memcpy(version_string.data(), default_version.data(), sizeof(u128));
constexpr char default_version[]{"1.0.0"};
std::memcpy(version_string.data(), default_version, sizeof(default_version));
}
IPC::ResponseBuilder rb{ctx, 6};
@@ -1377,7 +1407,19 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) {
u32 supported_languages = 0;
FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()};
const auto res = pm.GetControlMetadata();
const auto res = [this] {
const auto title_id = system.CurrentProcess()->GetTitleID();
FileSys::PatchManager pm{title_id};
auto res = pm.GetControlMetadata();
if (res.first != nullptr) {
return res;
}
FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id)};
return pm_update.GetControlMetadata();
}();
if (res.first != nullptr) {
supported_languages = res.first->GetSupportedLanguages();
}

View File

@@ -140,6 +140,7 @@ private:
void SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx);
void SetAlbumImageOrientation(Kernel::HLERequestContext& ctx);
void CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx);
void CreateManagedDisplaySeparableLayer(Kernel::HLERequestContext& ctx);
void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx);
void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);

View File

@@ -121,11 +121,83 @@ public:
{39, nullptr, "PrepareShutdown"},
{40, nullptr, "ListApplyDeltaTask"},
{41, nullptr, "ClearNotEnoughSpaceStateOfApplyDeltaTask"},
{42, nullptr, "Unknown1"},
{43, nullptr, "Unknown2"},
{44, nullptr, "Unknown3"},
{45, nullptr, "Unknown4"},
{46, nullptr, "Unknown5"},
{42, nullptr, "Unknown42"},
{43, nullptr, "Unknown43"},
{44, nullptr, "Unknown44"},
{45, nullptr, "Unknown45"},
{46, nullptr, "Unknown46"},
{47, nullptr, "Unknown47"},
{48, nullptr, "Unknown48"},
{49, nullptr, "Unknown49"},
{50, nullptr, "Unknown50"},
{51, nullptr, "Unknown51"},
{52, nullptr, "Unknown52"},
{53, nullptr, "Unknown53"},
{54, nullptr, "Unknown54"},
{55, nullptr, "Unknown55"},
{56, nullptr, "Unknown56"},
{57, nullptr, "Unknown57"},
{58, nullptr, "Unknown58"},
{59, nullptr, "Unknown59"},
{60, nullptr, "Unknown60"},
{61, nullptr, "Unknown61"},
{62, nullptr, "Unknown62"},
{63, nullptr, "Unknown63"},
{64, nullptr, "Unknown64"},
{65, nullptr, "Unknown65"},
{66, nullptr, "Unknown66"},
{67, nullptr, "Unknown67"},
{68, nullptr, "Unknown68"},
{69, nullptr, "Unknown69"},
{70, nullptr, "Unknown70"},
{71, nullptr, "Unknown71"},
{72, nullptr, "Unknown72"},
{73, nullptr, "Unknown73"},
{74, nullptr, "Unknown74"},
{75, nullptr, "Unknown75"},
{76, nullptr, "Unknown76"},
{77, nullptr, "Unknown77"},
{78, nullptr, "Unknown78"},
{79, nullptr, "Unknown79"},
{80, nullptr, "Unknown80"},
{81, nullptr, "Unknown81"},
{82, nullptr, "Unknown82"},
{83, nullptr, "Unknown83"},
{84, nullptr, "Unknown84"},
{85, nullptr, "Unknown85"},
{86, nullptr, "Unknown86"},
{87, nullptr, "Unknown87"},
{88, nullptr, "Unknown88"},
{89, nullptr, "Unknown89"},
{90, nullptr, "Unknown90"},
{91, nullptr, "Unknown91"},
{92, nullptr, "Unknown92"},
{93, nullptr, "Unknown93"},
{94, nullptr, "Unknown94"},
{95, nullptr, "Unknown95"},
{96, nullptr, "Unknown96"},
{97, nullptr, "Unknown97"},
{98, nullptr, "Unknown98"},
{99, nullptr, "Unknown99"},
{100, nullptr, "Unknown100"},
{101, nullptr, "Unknown101"},
{102, nullptr, "Unknown102"},
{103, nullptr, "Unknown103"},
{104, nullptr, "Unknown104"},
{105, nullptr, "Unknown105"},
{106, nullptr, "Unknown106"},
{107, nullptr, "Unknown107"},
{108, nullptr, "Unknown108"},
{109, nullptr, "Unknown109"},
{110, nullptr, "Unknown110"},
{111, nullptr, "Unknown111"},
{112, nullptr, "Unknown112"},
{113, nullptr, "Unknown113"},
{114, nullptr, "Unknown114"},
{115, nullptr, "Unknown115"},
{116, nullptr, "Unknown116"},
{117, nullptr, "Unknown117"},
{118, nullptr, "Unknown118"},
};
// clang-format on
@@ -142,6 +214,7 @@ public:
{1, nullptr, "RefreshDebugAvailability"},
{2, nullptr, "ClearDebugResponse"},
{3, nullptr, "RegisterDebugResponse"},
{4, nullptr, "IsLargeResourceAvailable"},
};
// clang-format on
@@ -164,6 +237,8 @@ public:
static const FunctionInfo functions[] = {
{0, nullptr, "RequestDeviceAuthenticationToken"},
{1, nullptr, "RequestCachedDeviceAuthenticationToken"},
{2, nullptr, "RequestEdgeToken"},
{3, nullptr, "RequestCachedEdgeToken"},
{100, nullptr, "RequestRegisterDeviceAccount"},
{101, nullptr, "RequestUnregisterDeviceAccount"},
{102, nullptr, "RequestDeviceAccountStatus"},
@@ -181,7 +256,8 @@ public:
{305, nullptr, "RequestCreateVirtualAccount"},
{306, nullptr, "RequestDeviceLinkStatus"},
{400, nullptr, "GetAccountByVirtualAccount"},
{500, nullptr, "RequestSyncTicket"},
{401, nullptr, "GetVirtualAccount"},
{500, nullptr, "RequestSyncTicketLegacy"},
{501, nullptr, "RequestDownloadTicket"},
{502, nullptr, "RequestDownloadTicketForPrepurchasedContents"},
{503, nullptr, "RequestSyncTicket"},

View File

@@ -30,6 +30,7 @@ public:
{23, nullptr, "DestroyToken"},
{24, nullptr, "DestroyTokenWithApplicationId"},
{25, nullptr, "QueryIsTokenValid"},
{26, nullptr, "ListenToMyApplicationId"},
{31, nullptr, "UploadTokenToBaaS"},
{32, nullptr, "DestroyTokenForBaaS"},
{33, nullptr, "CreateTokenForBaaS"},

View File

@@ -104,7 +104,7 @@ IApplicationManagerInterface::IApplicationManagerInterface()
{94, nullptr, "LaunchApplication"},
{95, nullptr, "GetApplicationLaunchInfo"},
{96, nullptr, "AcquireApplicationLaunchInfo"},
{97, nullptr, "GetMainApplicationProgramIndex2"},
{97, nullptr, "GetMainApplicationProgramIndexByApplicationLaunchInfo"},
{98, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
{99, nullptr, "LaunchDevMenu"},
{100, nullptr, "ResetToFactorySettings"},
@@ -254,7 +254,7 @@ IApplicationManagerInterface::IApplicationManagerInterface()
{2170, nullptr, "GetRightsEnvironmentStatus"},
{2171, nullptr, "GetRightsEnvironmentStatusChangedEvent"},
{2180, nullptr, "RequestExtendRightsInRightsEnvironment"},
{2181, nullptr, "GetLastResultOfExtendRightsInRightsEnvironment"},
{2181, nullptr, "GetResultOfExtendRightsInRightsEnvironment"},
{2182, nullptr, "SetActiveRightsContextUsingStateToRightsEnvironment"},
{2190, nullptr, "GetRightsEnvironmentHandleForApplication"},
{2199, nullptr, "GetRightsEnvironmentCountForDebug"},
@@ -366,7 +366,8 @@ ResultVal<u8> IApplicationManagerInterface::GetApplicationDesiredLanguage(
LOG_DEBUG(Service_NS, "called with supported_languages={:08X}", supported_languages);
// Get language code from settings
const auto language_code = Set::GetLanguageCodeFromIndex(Settings::values.language_index);
const auto language_code =
Set::GetLanguageCodeFromIndex(Settings::values.language_index.GetValue());
// Convert to application language, get priority list
const auto application_language = ConvertToApplicationLanguage(language_code);
@@ -445,8 +446,8 @@ IApplicationVersionInterface::IApplicationVersionInterface()
IApplicationVersionInterface::~IApplicationVersionInterface() = default;
IContentManagerInterface::IContentManagerInterface()
: ServiceFramework{"IContentManagerInterface"} {
IContentManagementInterface::IContentManagementInterface()
: ServiceFramework{"IContentManagementInterface"} {
// clang-format off
static const FunctionInfo functions[] = {
{11, nullptr, "CalculateApplicationOccupiedSize"},
@@ -463,7 +464,7 @@ IContentManagerInterface::IContentManagerInterface()
RegisterHandlers(functions);
}
IContentManagerInterface::~IContentManagerInterface() = default;
IContentManagementInterface::~IContentManagementInterface() = default;
IDocumentInterface::IDocumentInterface() : ServiceFramework{"IDocumentInterface"} {
// clang-format off
@@ -545,7 +546,7 @@ NS::NS(const char* name) : ServiceFramework{name} {
{7995, &NS::PushInterface<IAccountProxyInterface>, "GetAccountProxyInterface"},
{7996, &NS::PushInterface<IApplicationManagerInterface>, "GetApplicationManagerInterface"},
{7997, &NS::PushInterface<IDownloadTaskInterface>, "GetDownloadTaskInterface"},
{7998, &NS::PushInterface<IContentManagerInterface>, "GetContentManagementInterface"},
{7998, &NS::PushInterface<IContentManagementInterface>, "GetContentManagementInterface"},
{7999, &NS::PushInterface<IDocumentInterface>, "GetDocumentInterface"},
};
// clang-format on
@@ -572,9 +573,9 @@ public:
{6, nullptr, "TerminateApplication"},
{7, nullptr, "PrepareLaunchProgramFromHost"},
{8, nullptr, "LaunchApplication"},
{9, nullptr, "LaunchApplicationWithStorageId"},
{10, nullptr, "TerminateApplication2"},
{11, nullptr, "GetRunningApplicationProcessId"},
{9, nullptr, "LaunchApplicationWithStorageIdForDevelop"},
{10, nullptr, "IsSystemMemoryResourceLimitBoosted"},
{11, nullptr, "GetRunningApplicationProcessIdForDevelop"},
{12, nullptr, "SetCurrentApplicationRightsEnvironmentCanBeActive"},
{13, nullptr, "CreateApplicationResourceForDevelop"},
{14, nullptr, "IsPreomiaForDevelop"},
@@ -636,6 +637,10 @@ public:
{9, nullptr, "GetSystemUpdateNotificationEventForContentDelivery"},
{10, nullptr, "NotifySystemUpdateForContentDelivery"},
{11, nullptr, "PrepareShutdown"},
{12, nullptr, "Unknown12"},
{13, nullptr, "Unknown13"},
{14, nullptr, "Unknown14"},
{15, nullptr, "Unknown15"},
{16, nullptr, "DestroySystemUpdateTask"},
{17, nullptr, "RequestSendSystemUpdate"},
{18, nullptr, "GetSendSystemUpdateProgress"},

View File

@@ -40,10 +40,10 @@ public:
~IApplicationVersionInterface() override;
};
class IContentManagerInterface final : public ServiceFramework<IContentManagerInterface> {
class IContentManagementInterface final : public ServiceFramework<IContentManagementInterface> {
public:
explicit IContentManagerInterface();
~IContentManagerInterface() override;
explicit IContentManagementInterface();
~IContentManagementInterface() override;
};
class IDocumentInterface final : public ServiceFramework<IDocumentInterface> {

View File

@@ -163,7 +163,7 @@ PL_U::PL_U(Core::System& system)
{5, &PL_U::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"},
{6, nullptr, "GetSharedFontInOrderOfPriorityForSystem"},
{100, nullptr, "RequestApplicationFunctionAuthorization"},
{101, nullptr, "RequestApplicationFunctionAuthorizationForSystem"},
{101, nullptr, "RequestApplicationFunctionAuthorizationByProcessId"},
{102, nullptr, "RequestApplicationFunctionAuthorizationByApplicationId"},
{1000, nullptr, "LoadNgWordDataForPlatformRegionChina"},
{1001, nullptr, "GetNgWordDataSizeForPlatformRegionChina"},

View File

@@ -144,7 +144,7 @@ void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
}
}
void NVDRV::SetClientPID(Kernel::HLERequestContext& ctx) {
void NVDRV::SetAruid(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
pid = rp.Pop<u64>();
LOG_WARNING(Service_NVDRV, "(STUBBED) called, pid=0x{:X}", pid);
@@ -154,7 +154,7 @@ void NVDRV::SetClientPID(Kernel::HLERequestContext& ctx) {
rb.Push<u32>(0);
}
void NVDRV::FinishInitialize(Kernel::HLERequestContext& ctx) {
void NVDRV::SetGraphicsFirmwareMemoryMarginEnabled(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
@@ -187,13 +187,14 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name)
{4, &NVDRV::QueryEvent, "QueryEvent"},
{5, nullptr, "MapSharedMem"},
{6, &NVDRV::GetStatus, "GetStatus"},
{7, nullptr, "ForceSetClientPID"},
{8, &NVDRV::SetClientPID, "SetClientPID"},
{7, nullptr, "SetAruidForTest"},
{8, &NVDRV::SetAruid, "SetAruid"},
{9, &NVDRV::DumpGraphicsMemoryInfo, "DumpGraphicsMemoryInfo"},
{10, nullptr, "InitializeDevtools"},
{11, &NVDRV::Ioctl2, "Ioctl2"},
{12, &NVDRV::Ioctl3, "Ioctl3"},
{13, &NVDRV::FinishInitialize, "FinishInitialize"},
{13, &NVDRV::SetGraphicsFirmwareMemoryMarginEnabled,
"SetGraphicsFirmwareMemoryMarginEnabled"},
};
RegisterHandlers(functions);
}

View File

@@ -29,8 +29,8 @@ private:
void Close(Kernel::HLERequestContext& ctx);
void Initialize(Kernel::HLERequestContext& ctx);
void QueryEvent(Kernel::HLERequestContext& ctx);
void SetClientPID(Kernel::HLERequestContext& ctx);
void FinishInitialize(Kernel::HLERequestContext& ctx);
void SetAruid(Kernel::HLERequestContext& ctx);
void SetGraphicsFirmwareMemoryMarginEnabled(Kernel::HLERequestContext& ctx);
void GetStatus(Kernel::HLERequestContext& ctx);
void DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx);
void IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version);

View File

@@ -10,19 +10,19 @@ namespace Service::Nvidia {
NVMEMP::NVMEMP() : ServiceFramework("nvmemp") {
static const FunctionInfo functions[] = {
{0, &NVMEMP::Cmd0, "Cmd0"},
{1, &NVMEMP::Cmd1, "Cmd1"},
{0, &NVMEMP::Open, "Open"},
{1, &NVMEMP::GetAruid, "GetAruid"},
};
RegisterHandlers(functions);
}
NVMEMP::~NVMEMP() = default;
void NVMEMP::Cmd0(Kernel::HLERequestContext& ctx) {
void NVMEMP::Open(Kernel::HLERequestContext& ctx) {
UNIMPLEMENTED();
}
void NVMEMP::Cmd1(Kernel::HLERequestContext& ctx) {
void NVMEMP::GetAruid(Kernel::HLERequestContext& ctx) {
UNIMPLEMENTED();
}

View File

@@ -14,8 +14,8 @@ public:
~NVMEMP() override;
private:
void Cmd0(Kernel::HLERequestContext& ctx);
void Cmd1(Kernel::HLERequestContext& ctx);
void Open(Kernel::HLERequestContext& ctx);
void GetAruid(Kernel::HLERequestContext& ctx);
};
} // namespace Service::Nvidia

View File

@@ -36,6 +36,9 @@ public:
{18, nullptr, "ReleaseIrq"},
{19, nullptr, "SetIrqEnable"},
{20, nullptr, "SetAspmEnable"},
{21, nullptr, "SetResetUponResumeEnable"},
{22, nullptr, "Unknown22"},
{23, nullptr, "Unknown23"},
};
// clang-format on

View File

@@ -42,6 +42,9 @@ public:
{24, nullptr, "GetModuleStateTable"},
{25, nullptr, "GetPowerDomainStateTable"},
{26, nullptr, "GetFuseInfo"},
{27, nullptr, "GetDramId"},
{28, nullptr, "IsPoweredOn"},
{29, nullptr, "GetVoltage"},
};
// clang-format on

View File

@@ -78,13 +78,13 @@ public:
: ServiceFramework{"pm:dmnt"}, kernel(kernel) {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetDebugProcesses"},
{1, nullptr, "StartDebugProcess"},
{2, &DebugMonitor::GetTitlePid, "GetTitlePid"},
{3, nullptr, "EnableDebugForTitleId"},
{4, &DebugMonitor::GetApplicationPid, "GetApplicationPid"},
{5, nullptr, "EnableDebugForApplication"},
{6, nullptr, "DisableDebug"},
{0, nullptr, "GetJitDebugProcessIdList"},
{1, nullptr, "StartProcess"},
{2, &DebugMonitor::GetProcessId, "GetProcessId"},
{3, nullptr, "HookToCreateProcess"},
{4, &DebugMonitor::GetApplicationProcessId, "GetApplicationProcessId"},
{5, nullptr, "HookToCreateApplicationProgress"},
{6, nullptr, "ClearHook"},
};
// clang-format on
@@ -92,7 +92,7 @@ public:
}
private:
void GetTitlePid(Kernel::HLERequestContext& ctx) {
void GetProcessId(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto title_id = rp.PopRaw<u64>();
@@ -114,7 +114,7 @@ private:
rb.Push((*process)->GetProcessID());
}
void GetApplicationPid(Kernel::HLERequestContext& ctx) {
void GetApplicationProcessId(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_PM, "called");
GetApplicationPidGeneric(ctx, kernel.GetProcessList());
}
@@ -163,15 +163,15 @@ public:
: ServiceFramework{"pm:shell"}, kernel(kernel) {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "LaunchProcess"},
{1, nullptr, "TerminateProcessByPid"},
{2, nullptr, "TerminateProcessByTitleId"},
{3, nullptr, "GetProcessEventWaiter"},
{4, nullptr, "GetProcessEventType"},
{0, nullptr, "LaunchProgram"},
{1, nullptr, "TerminateProcess"},
{2, nullptr, "TerminateProgram"},
{3, nullptr, "GetProcessEventHandle"},
{4, nullptr, "GetProcessEventInfo"},
{5, nullptr, "NotifyBootFinished"},
{6, &Shell::GetApplicationPid, "GetApplicationPid"},
{6, &Shell::GetApplicationProcessIdForShell, "GetApplicationProcessIdForShell"},
{7, nullptr, "BoostSystemMemoryResourceLimit"},
{8, nullptr, "EnableAdditionalSystemThreads"},
{8, nullptr, "BoostApplicationThreadResourceLimit"},
{9, nullptr, "GetBootFinishedEventHandle"},
};
// clang-format on
@@ -180,7 +180,7 @@ public:
}
private:
void GetApplicationPid(Kernel::HLERequestContext& ctx) {
void GetApplicationProcessIdForShell(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_PM, "called");
GetApplicationPidGeneric(ctx, kernel.GetProcessList());
}

View File

@@ -42,6 +42,11 @@ public:
{40101, nullptr, "SetUserAgreementCheckEnabled"},
{50100, nullptr, "ReadAllApplicationReportFiles"},
{90100, nullptr, "ReadAllReportFiles"},
{90101, nullptr, "Unknown90101"},
{90102, nullptr, "Unknown90102"},
{90200, nullptr, "GetStatistics"},
{90201, nullptr, "GetThroughputHistory"},
{90300, nullptr, "GetLastUploadError"},
};
// clang-format on

View File

@@ -24,6 +24,8 @@ public:
{4, nullptr, "Cancel"},
{5, nullptr, "PrintModuleInformation"},
{6, nullptr, "GetModuleInformation"},
{10, nullptr, "Unknown10"},
{11, nullptr, "Unknown11"},
};
// clang-format on

View File

@@ -35,6 +35,7 @@ public:
{15, nullptr, "GetBatteryAgePercentage"},
{16, nullptr, "GetBatteryChargeInfoEvent"},
{17, nullptr, "GetBatteryChargeInfoFields"},
{18, nullptr, "GetBatteryChargeCalibratedEvent"},
};
// clang-format on

View File

@@ -91,7 +91,7 @@ void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t m
}
void GetKeyCodeMapImpl(Kernel::HLERequestContext& ctx) {
const auto language_code = available_language_codes[Settings::values.language_index];
const auto language_code = available_language_codes[Settings::values.language_index.GetValue()];
const auto key_code =
std::find_if(language_to_layout.cbegin(), language_to_layout.cend(),
[=](const auto& element) { return element.first == language_code; });
@@ -99,7 +99,7 @@ void GetKeyCodeMapImpl(Kernel::HLERequestContext& ctx) {
if (key_code == language_to_layout.cend()) {
LOG_ERROR(Service_SET,
"Could not find keyboard layout for language index {}, defaulting to English us",
Settings::values.language_index);
Settings::values.language_index.GetValue());
} else {
layout = key_code->second;
}
@@ -163,11 +163,11 @@ void SET::GetQuestFlag(Kernel::HLERequestContext& ctx) {
}
void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index);
LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index.GetValue());
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.PushEnum(available_language_codes[Settings::values.language_index]);
rb.PushEnum(available_language_codes[Settings::values.language_index.GetValue()]);
}
void SET::GetRegionCode(Kernel::HLERequestContext& ctx) {
@@ -175,7 +175,7 @@ void SET::GetRegionCode(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(Settings::values.region_index);
rb.Push(Settings::values.region_index.GetValue());
}
void SET::GetKeyCodeMap(Kernel::HLERequestContext& ctx) {

View File

@@ -12,7 +12,7 @@
namespace Service::SM {
void Controller::ConvertSessionToDomain(Kernel::HLERequestContext& ctx) {
void Controller::ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx) {
ASSERT_MSG(ctx.Session()->IsSession(), "Session is already a domain");
LOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetObjectId());
ctx.Session()->ConvertToDomain();
@@ -22,7 +22,7 @@ void Controller::ConvertSessionToDomain(Kernel::HLERequestContext& ctx) {
rb.Push<u32>(1); // Converted sessions start with 1 request handler
}
void Controller::DuplicateSession(Kernel::HLERequestContext& ctx) {
void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) {
// TODO(bunnei): This is just creating a new handle to the same Session. I assume this is wrong
// and that we probably want to actually make an entirely new Session, but we still need to
// verify this on hardware.
@@ -33,10 +33,10 @@ void Controller::DuplicateSession(Kernel::HLERequestContext& ctx) {
rb.PushMoveObjects(ctx.Session()->GetParent()->Client());
}
void Controller::DuplicateSessionEx(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called, using DuplicateSession");
void Controller::CloneCurrentObjectEx(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called, using CloneCurrentObject");
DuplicateSession(ctx);
CloneCurrentObject(ctx);
}
void Controller::QueryPointerBufferSize(Kernel::HLERequestContext& ctx) {
@@ -47,13 +47,14 @@ void Controller::QueryPointerBufferSize(Kernel::HLERequestContext& ctx) {
rb.Push<u16>(0x1000);
}
// https://switchbrew.org/wiki/IPC_Marshalling
Controller::Controller() : ServiceFramework("IpcController") {
static const FunctionInfo functions[] = {
{0x00000000, &Controller::ConvertSessionToDomain, "ConvertSessionToDomain"},
{0x00000001, nullptr, "ConvertDomainToSession"},
{0x00000002, &Controller::DuplicateSession, "DuplicateSession"},
{0x00000003, &Controller::QueryPointerBufferSize, "QueryPointerBufferSize"},
{0x00000004, &Controller::DuplicateSessionEx, "DuplicateSessionEx"},
{0, &Controller::ConvertCurrentObjectToDomain, "ConvertCurrentObjectToDomain"},
{1, nullptr, "CopyFromCurrentDomain"},
{2, &Controller::CloneCurrentObject, "CloneCurrentObject"},
{3, &Controller::QueryPointerBufferSize, "QueryPointerBufferSize"},
{4, &Controller::CloneCurrentObjectEx, "CloneCurrentObjectEx"},
};
RegisterHandlers(functions);
}

View File

@@ -14,9 +14,9 @@ public:
~Controller() override;
private:
void ConvertSessionToDomain(Kernel::HLERequestContext& ctx);
void DuplicateSession(Kernel::HLERequestContext& ctx);
void DuplicateSessionEx(Kernel::HLERequestContext& ctx);
void ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx);
void CloneCurrentObject(Kernel::HLERequestContext& ctx);
void CloneCurrentObjectEx(Kernel::HLERequestContext& ctx);
void QueryPointerBufferSize(Kernel::HLERequestContext& ctx);
};

View File

@@ -14,6 +14,7 @@ NSD::NSD(const char* name) : ServiceFramework(name) {
{12, nullptr, "GetDeviceId"},
{13, nullptr, "DeleteSettings"},
{14, nullptr, "ImportSettings"},
{15, nullptr, "SetChangeEnvironmentIdentifierDisabled"},
{20, nullptr, "Resolve"},
{21, nullptr, "ResolveEx"},
{30, nullptr, "GetNasServiceSetting"},
@@ -28,6 +29,11 @@ NSD::NSD(const char* name) : ServiceFramework(name) {
{60, nullptr, "ReadSaveDataFromFsForTest"},
{61, nullptr, "WriteSaveDataToFsForTest"},
{62, nullptr, "DeleteSaveDataOfFsForTest"},
{63, nullptr, "IsChangeEnvironmentIdentifierDisabled"},
{64, nullptr, "SetWithoutDomainExchangeFqdns"},
{100, nullptr, "GetApplicationServerEnvironmentType"},
{101, nullptr, "SetApplicationServerEnvironmentType"},
{102, nullptr, "DeleteApplicationServerEnvironmentType"},
};
// clang-format on

View File

@@ -7,7 +7,7 @@
namespace Service::Sockets {
void SFDNSRES::GetAddrInfo(Kernel::HLERequestContext& ctx) {
void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) {
struct Parameters {
u8 use_nsd_resolve;
u32 unknown;
@@ -29,15 +29,20 @@ SFDNSRES::SFDNSRES() : ServiceFramework("sfdnsres") {
static const FunctionInfo functions[] = {
{0, nullptr, "SetDnsAddressesPrivate"},
{1, nullptr, "GetDnsAddressPrivate"},
{2, nullptr, "GetHostByName"},
{3, nullptr, "GetHostByAddr"},
{4, nullptr, "GetHostStringError"},
{5, nullptr, "GetGaiStringError"},
{6, &SFDNSRES::GetAddrInfo, "GetAddrInfo"},
{7, nullptr, "GetNameInfo"},
{8, nullptr, "RequestCancelHandle"},
{9, nullptr, "CancelSocketCall"},
{11, nullptr, "ClearDnsIpServerAddressArray"},
{2, nullptr, "GetHostByNameRequest"},
{3, nullptr, "GetHostByAddrRequest"},
{4, nullptr, "GetHostStringErrorRequest"},
{5, nullptr, "GetGaiStringErrorRequest"},
{6, &SFDNSRES::GetAddrInfoRequest, "GetAddrInfoRequest"},
{7, nullptr, "GetNameInfoRequest"},
{8, nullptr, "RequestCancelHandleRequest"},
{9, nullptr, "CancelRequest"},
{10, nullptr, "GetHostByNameRequestWithOptions"},
{11, nullptr, "GetHostByAddrRequestWithOptions"},
{12, nullptr, "GetAddrInfoRequestWithOptions"},
{13, nullptr, "GetNameInfoRequestWithOptions"},
{14, nullptr, "ResolverSetOptionRequest"},
{15, nullptr, "ResolverGetOptionRequest"},
};
RegisterHandlers(functions);
}

View File

@@ -15,7 +15,7 @@ public:
~SFDNSRES() override;
private:
void GetAddrInfo(Kernel::HLERequestContext& ctx);
void GetAddrInfoRequest(Kernel::HLERequestContext& ctx);
};
} // namespace Service::Sockets

View File

@@ -19,7 +19,7 @@ namespace Service::SPL {
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
: ServiceFramework(name), module(std::move(module)),
rng(Settings::values.rng_seed.value_or(std::time(nullptr))) {}
rng(Settings::values.rng_seed.GetValue().value_or(std::time(nullptr))) {}
Module::Interface::~Interface() = default;

View File

@@ -9,35 +9,36 @@ namespace Service::SPL {
SPL::SPL(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "spl:") {
static const FunctionInfo functions[] = {
{0, nullptr, "GetConfig"},
{1, nullptr, "UserExpMod"},
{1, nullptr, "ModularExponentiate"},
{2, nullptr, "GenerateAesKek"},
{3, nullptr, "LoadAesKey"},
{4, nullptr, "GenerateAesKey"},
{5, nullptr, "SetConfig"},
{7, &SPL::GetRandomBytes, "GetRandomBytes"},
{9, nullptr, "LoadSecureExpModKey"},
{10, nullptr, "SecureExpMod"},
{9, nullptr, "ImportLotusKey"},
{10, nullptr, "DecryptLotusMessage"},
{11, nullptr, "IsDevelopment"},
{12, nullptr, "GenerateSpecificAesKey"},
{13, nullptr, "DecryptPrivk"},
{13, nullptr, "DecryptDeviceUniqueData"},
{14, nullptr, "DecryptAesKey"},
{15, nullptr, "DecryptAesCtr"},
{15, nullptr, "CryptAesCtr"},
{16, nullptr, "ComputeCmac"},
{17, nullptr, "LoadRsaOaepKey"},
{18, nullptr, "UnwrapRsaOaepWrappedTitleKey"},
{17, nullptr, "ImportEsKey"},
{18, nullptr, "UnwrapTitleKey"},
{19, nullptr, "LoadTitleKey"},
{20, nullptr, "UnwrapAesWrappedTitleKey"},
{21, nullptr, "LockAesEngine"},
{22, nullptr, "UnlockAesEngine"},
{23, nullptr, "GetSplWaitEvent"},
{24, nullptr, "SetSharedData"},
{25, nullptr, "GetSharedData"},
{26, nullptr, "ImportSslRsaKey"},
{27, nullptr, "SecureExpModWithSslKey"},
{28, nullptr, "ImportEsRsaKey"},
{29, nullptr, "SecureExpModWithEsKey"},
{30, nullptr, "EncryptManuRsaKeyForImport"},
{31, nullptr, "GetPackage2Hash"},
{20, nullptr, "PrepareEsCommonKey"},
{21, nullptr, "AllocateAesKeyslot"},
{22, nullptr, "DeallocateAesKeySlot"},
{23, nullptr, "GetAesKeyslotAvailableEvent"},
{24, nullptr, "SetBootReason"},
{25, nullptr, "GetBootReason"},
{26, nullptr, "DecryptAndStoreSslClientCertKey"},
{27, nullptr, "ModularExponentiateWithSslClientCertKey"},
{28, nullptr, "DecryptAndStoreDrmDeviceCertKey"},
{29, nullptr, "ModularExponentiateWithDrmDeviceCertKey"},
{30, nullptr, "ReencryptDeviceUniqueData "},
{31, nullptr, "PrepareEsArchiveKey"}, // This is also GetPackage2Hash?
{32, nullptr, "LoadPreparedAesKey"},
};
RegisterHandlers(functions);
}

View File

@@ -90,6 +90,13 @@ public:
: ServiceFramework("ISteadyClock"), clock_core{clock_core}, system{system} {
static const FunctionInfo functions[] = {
{0, &ISteadyClock::GetCurrentTimePoint, "GetCurrentTimePoint"},
{2, nullptr, "GetTestOffset"},
{3, nullptr, "SetTestOffset"},
{100, nullptr, "GetRtcValue"},
{101, nullptr, "IsRtcResetDetected"},
{102, nullptr, "GetSetupResultValue"},
{200, nullptr, "GetInternalOffset"},
{201, nullptr, "SetInternalOffset"},
};
RegisterHandlers(functions);
}

View File

@@ -20,7 +20,7 @@ public:
static const FunctionInfo functions[] = {
{0, nullptr, "GetDsEndpoint"},
{1, nullptr, "GetSetupEvent"},
{2, nullptr, "Unknown"},
{2, nullptr, "Unknown2"},
{3, nullptr, "EnableInterface"},
{4, nullptr, "DisableInterface"},
{5, nullptr, "CtrlInPostBufferAsync"},
@@ -55,6 +55,7 @@ public:
{9, nullptr, "SetBinaryObjectStore"},
{10, nullptr, "Enable"},
{11, nullptr, "Disable"},
{12, nullptr, "Unknown12"},
};
// clang-format on
@@ -69,13 +70,13 @@ public:
static const FunctionInfo functions[] = {
{0, nullptr, "Open"},
{1, nullptr, "Close"},
{2, nullptr, "Unknown1"},
{2, nullptr, "Unknown2"},
{3, nullptr, "Populate"},
{4, nullptr, "PostBufferAsync"},
{5, nullptr, "GetXferReport"},
{6, nullptr, "PostBufferMultiAsync"},
{7, nullptr, "Unknown3"},
{8, nullptr, "Unknown4"},
{7, nullptr, "Unknown7"},
{8, nullptr, "Unknown8"},
};
// clang-format on
@@ -88,13 +89,13 @@ public:
explicit IClientIfSession() : ServiceFramework{"IClientIfSession"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "Unknown1"},
{0, nullptr, "Unknown0"},
{1, nullptr, "SetInterface"},
{2, nullptr, "GetInterface"},
{3, nullptr, "GetAlternateInterface"},
{4, nullptr, "GetCurrentFrame"},
{5, nullptr, "CtrlXferAsync"},
{6, nullptr, "Unknown2"},
{6, nullptr, "Unknown6"},
{7, nullptr, "GetCtrlXferReport"},
{8, nullptr, "ResetDevice"},
{9, nullptr, "OpenUsbEp"},
@@ -118,7 +119,7 @@ public:
{5, nullptr, "DestroyInterfaceAvailableEvent"},
{6, nullptr, "GetInterfaceStateChangeEvent"},
{7, nullptr, "AcquireUsbIf"},
{8, nullptr, "Unknown1"},
{8, nullptr, "Unknown8"},
};
// clang-format on
@@ -179,8 +180,8 @@ public:
{4, nullptr, "GetFwRevision"},
{5, nullptr, "GetManufacturerId"},
{6, nullptr, "GetDeviceId"},
{7, nullptr, "Unknown1"},
{8, nullptr, "Unknown2"},
{7, nullptr, "Unknown7"},
{8, nullptr, "Unknown8"},
};
// clang-format on
@@ -215,12 +216,12 @@ public:
explicit USB_PM() : ServiceFramework{"usb:pm"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "Unknown1"},
{1, nullptr, "Unknown2"},
{2, nullptr, "Unknown3"},
{3, nullptr, "Unknown4"},
{4, nullptr, "Unknown5"},
{5, nullptr, "Unknown6"},
{0, nullptr, "Unknown0"},
{1, nullptr, "Unknown1"},
{2, nullptr, "Unknown2"},
{3, nullptr, "Unknown3"},
{4, nullptr, "Unknown4"},
{5, nullptr, "Unknown5"},
};
// clang-format on

View File

@@ -519,9 +519,9 @@ private:
IGBPConnectRequestParcel request{ctx.ReadBuffer()};
IGBPConnectResponseParcel response{
static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedWidth) *
Settings::values.resolution_factor),
Settings::values.resolution_factor.GetValue()),
static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedHeight) *
Settings::values.resolution_factor)};
Settings::values.resolution_factor.GetValue())};
ctx.WriteBuffer(response.Serialize());
break;
}
@@ -700,6 +700,7 @@ public:
{3215, nullptr, "SetDisplayGamma"},
{3216, nullptr, "GetDisplayCmuLuma"},
{3217, nullptr, "SetDisplayCmuLuma"},
{6013, nullptr, "GetLayerPresentationSubmissionTimestamps"},
{8225, nullptr, "GetSharedBufferMemoryHandleId"},
{8250, nullptr, "OpenSharedLayer"},
{8251, nullptr, "CloseSharedLayer"},
@@ -748,14 +749,14 @@ private:
if (Settings::values.use_docked_mode) {
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) *
static_cast<u32>(Settings::values.resolution_factor));
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) *
static_cast<u32>(Settings::values.resolution_factor));
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
} else {
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) *
static_cast<u32>(Settings::values.resolution_factor));
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight) *
static_cast<u32>(Settings::values.resolution_factor));
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
}
rb.PushRaw<float>(60.0f); // This wouldn't seem to be correct for 30 fps games.
@@ -785,6 +786,7 @@ public:
{2300, nullptr, "AcquireLayerTexturePresentingEvent"},
{2301, nullptr, "ReleaseLayerTexturePresentingEvent"},
{2302, nullptr, "GetDisplayHotplugEvent"},
{2303, nullptr, "GetDisplayModeChangedEvent"},
{2402, nullptr, "GetDisplayHotplugState"},
{2501, nullptr, "GetCompositorErrorInfo"},
{2601, nullptr, "GetDisplayErrorEvent"},
@@ -1029,9 +1031,9 @@ private:
// between docked and undocked dimensions. We take the liberty of applying
// the resolution scaling factor here.
rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth) *
static_cast<u32>(Settings::values.resolution_factor));
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
rb.Push(static_cast<u64>(DisplayResolution::UndockedHeight) *
static_cast<u32>(Settings::values.resolution_factor));
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
}
void SetLayerScalingMode(Kernel::HLERequestContext& ctx) {
@@ -1064,8 +1066,8 @@ private:
LOG_WARNING(Service_VI, "(STUBBED) called");
DisplayInfo display_info;
display_info.width *= static_cast<u64>(Settings::values.resolution_factor);
display_info.height *= static_cast<u64>(Settings::values.resolution_factor);
display_info.width *= static_cast<u64>(Settings::values.resolution_factor.GetValue());
display_info.height *= static_cast<u64>(Settings::values.resolution_factor.GetValue());
ctx.WriteBuffer(&display_info, sizeof(DisplayInfo));
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);

View File

@@ -12,6 +12,7 @@ VI_U::VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
: ServiceFramework{"vi:u"}, nv_flinger{std::move(nv_flinger)} {
static const FunctionInfo functions[] = {
{0, &VI_U::GetDisplayService, "GetDisplayService"},
{1, nullptr, "GetDisplayServiceWithProxyNameExchange"},
};
RegisterHandlers(functions);
}

View File

@@ -15,34 +15,37 @@ public:
explicit WLANInfra() : ServiceFramework{"wlan:inf"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "Unknown1"},
{1, nullptr, "Unknown2"},
{0, nullptr, "OpenMode"},
{1, nullptr, "CloseMode"},
{2, nullptr, "GetMacAddress"},
{3, nullptr, "StartScan"},
{4, nullptr, "StopScan"},
{5, nullptr, "Connect"},
{6, nullptr, "CancelConnect"},
{7, nullptr, "Disconnect"},
{8, nullptr, "Unknown3"},
{9, nullptr, "Unknown4"},
{8, nullptr, "GetConnectionEvent"},
{9, nullptr, "GetConnectionStatus"},
{10, nullptr, "GetState"},
{11, nullptr, "GetScanResult"},
{12, nullptr, "GetRssi"},
{13, nullptr, "ChangeRxAntenna"},
{14, nullptr, "Unknown5"},
{15, nullptr, "Unknown6"},
{14, nullptr, "GetFwVersion"},
{15, nullptr, "RequestSleep"},
{16, nullptr, "RequestWakeUp"},
{17, nullptr, "RequestIfUpDown"},
{18, nullptr, "Unknown7"},
{19, nullptr, "Unknown8"},
{20, nullptr, "Unknown9"},
{21, nullptr, "Unknown10"},
{22, nullptr, "Unknown11"},
{23, nullptr, "Unknown12"},
{24, nullptr, "Unknown13"},
{25, nullptr, "Unknown14"},
{26, nullptr, "Unknown15"},
{27, nullptr, "Unknown16"},
{18, nullptr, "Unknown18"},
{19, nullptr, "Unknown19"},
{20, nullptr, "Unknown20"},
{21, nullptr, "Unknown21"},
{22, nullptr, "Unknown22"},
{23, nullptr, "Unknown23"},
{24, nullptr, "Unknown24"},
{25, nullptr, "Unknown25"},
{26, nullptr, "Unknown26"},
{27, nullptr, "Unknown27"},
{28, nullptr, "Unknown28"},
{29, nullptr, "Unknown29"},
{30, nullptr, "Unknown30"},
};
// clang-format on
@@ -55,12 +58,12 @@ public:
explicit WLANLocal() : ServiceFramework{"wlan:lcl"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "Unknown1"},
{1, nullptr, "Unknown2"},
{2, nullptr, "Unknown3"},
{3, nullptr, "Unknown4"},
{4, nullptr, "Unknown5"},
{5, nullptr, "Unknown6"},
{0, nullptr, "Unknown0"},
{1, nullptr, "Unknown1"},
{2, nullptr, "Unknown2"},
{3, nullptr, "Unknown3"},
{4, nullptr, "Unknown4"},
{5, nullptr, "Unknown5"},
{6, nullptr, "GetMacAddress"},
{7, nullptr, "CreateBss"},
{8, nullptr, "DestroyBss"},
@@ -72,38 +75,42 @@ public:
{14, nullptr, "CancelJoin"},
{15, nullptr, "Disconnect"},
{16, nullptr, "SetBeaconLostCount"},
{17, nullptr, "Unknown7"},
{18, nullptr, "Unknown8"},
{19, nullptr, "Unknown9"},
{17, nullptr, "Unknown17"},
{18, nullptr, "Unknown18"},
{19, nullptr, "Unknown19"},
{20, nullptr, "GetBssIndicationEvent"},
{21, nullptr, "GetBssIndicationInfo"},
{22, nullptr, "GetState"},
{23, nullptr, "GetAllowedChannels"},
{24, nullptr, "AddIe"},
{25, nullptr, "DeleteIe"},
{26, nullptr, "Unknown10"},
{27, nullptr, "Unknown11"},
{26, nullptr, "Unknown26"},
{27, nullptr, "Unknown27"},
{28, nullptr, "CreateRxEntry"},
{29, nullptr, "DeleteRxEntry"},
{30, nullptr, "Unknown12"},
{31, nullptr, "Unknown13"},
{30, nullptr, "Unknown30"},
{31, nullptr, "Unknown31"},
{32, nullptr, "AddMatchingDataToRxEntry"},
{33, nullptr, "RemoveMatchingDataFromRxEntry"},
{34, nullptr, "GetScanResult"},
{35, nullptr, "Unknown14"},
{35, nullptr, "Unknown35"},
{36, nullptr, "SetActionFrameWithBeacon"},
{37, nullptr, "CancelActionFrameWithBeacon"},
{38, nullptr, "CreateRxEntryForActionFrame"},
{39, nullptr, "DeleteRxEntryForActionFrame"},
{40, nullptr, "Unknown15"},
{41, nullptr, "Unknown16"},
{40, nullptr, "Unknown40"},
{41, nullptr, "Unknown41"},
{42, nullptr, "CancelGetActionFrame"},
{43, nullptr, "GetRssi"},
{44, nullptr, "Unknown17"},
{45, nullptr, "Unknown18"},
{46, nullptr, "Unknown19"},
{47, nullptr, "Unknown20"},
{48, nullptr, "Unknown21"},
{44, nullptr, "Unknown44"},
{45, nullptr, "Unknown45"},
{46, nullptr, "Unknown46"},
{47, nullptr, "Unknown47"},
{48, nullptr, "Unknown48"},
{49, nullptr, "Unknown49"},
{50, nullptr, "Unknown50"},
{51, nullptr, "Unknown51"},
{52, nullptr, "Unknown52"},
};
// clang-format on
@@ -142,18 +149,19 @@ public:
explicit WLANSocketManager() : ServiceFramework{"wlan:soc"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "Unknown1"},
{1, nullptr, "Unknown2"},
{2, nullptr, "Unknown3"},
{3, nullptr, "Unknown4"},
{4, nullptr, "Unknown5"},
{5, nullptr, "Unknown6"},
{0, nullptr, "Unknown0"},
{1, nullptr, "Unknown1"},
{2, nullptr, "Unknown2"},
{3, nullptr, "Unknown3"},
{4, nullptr, "Unknown4"},
{5, nullptr, "Unknown5"},
{6, nullptr, "GetMacAddress"},
{7, nullptr, "SwitchTsfTimerFunction"},
{8, nullptr, "Unknown7"},
{9, nullptr, "Unknown8"},
{10, nullptr, "Unknown9"},
{11, nullptr, "Unknown10"},
{8, nullptr, "Unknown8"},
{9, nullptr, "Unknown9"},
{10, nullptr, "Unknown10"},
{11, nullptr, "Unknown11"},
{12, nullptr, "Unknown12"},
};
// clang-format on

View File

@@ -548,9 +548,9 @@ struct Memory::Impl {
// longer exist, and we should just leave the pagetable entry blank.
page_type = Common::PageType::Unmapped;
} else {
page_type = Common::PageType::Memory;
current_page_table->pointers[vaddr >> PAGE_BITS] =
pointer - (vaddr & ~PAGE_MASK);
page_type = Common::PageType::Memory;
}
break;
}
@@ -591,9 +591,12 @@ struct Memory::Impl {
base + page_table.pointers.size());
if (!target) {
ASSERT_MSG(type != Common::PageType::Memory,
"Mapping memory page without a pointer @ {:016x}", base * PAGE_SIZE);
while (base != end) {
page_table.pointers[base] = nullptr;
page_table.attributes[base] = type;
page_table.pointers[base] = nullptr;
page_table.backing_addr[base] = 0;
base += 1;

View File

@@ -119,13 +119,14 @@ double PerfStats::GetLastFrameTimeScale() {
}
void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) {
if (!Settings::values.use_frame_limit || Settings::values.use_multi_core) {
if (!Settings::values.use_frame_limit.GetValue() ||
Settings::values.use_multi_core.GetValue()) {
return;
}
auto now = Clock::now();
const double sleep_scale = Settings::values.frame_limit / 100.0;
const double sleep_scale = Settings::values.frame_limit.GetValue() / 100.0;
// Max lag caused by slow frames. Shouldn't be more than the length of a frame at the current
// speed percent or it will clamp too much and prevent this from properly limiting to that

View File

@@ -62,6 +62,7 @@ const std::array<const char*, NumMouseButtons> mapping = {{
}
Values values = {};
bool configuring_global = true;
std::string GetTimeZoneString() {
static constexpr std::array<const char*, 46> timezones{{
@@ -73,9 +74,9 @@ std::string GetTimeZoneString() {
"UCT", "Universal", "UTC", "W-SU", "WET", "Zulu",
}};
ASSERT(Settings::values.time_zone_index < timezones.size());
ASSERT(Settings::values.time_zone_index.GetValue() < timezones.size());
return timezones[Settings::values.time_zone_index];
return timezones[Settings::values.time_zone_index.GetValue()];
}
void Apply() {
@@ -97,25 +98,25 @@ void LogSetting(const std::string& name, const T& value) {
void LogSettings() {
LOG_INFO(Config, "yuzu Configuration:");
LogSetting("System_UseDockedMode", Settings::values.use_docked_mode);
LogSetting("System_RngSeed", Settings::values.rng_seed.value_or(0));
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);
LogSetting("System_RegionIndex", Settings::values.region_index);
LogSetting("System_TimeZoneIndex", Settings::values.time_zone_index);
LogSetting("Core_UseMultiCore", Settings::values.use_multi_core);
LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor);
LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit);
LogSetting("Renderer_FrameLimit", Settings::values.frame_limit);
LogSetting("Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache);
LogSetting("Renderer_GPUAccuracyLevel", Settings::values.gpu_accuracy);
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);
LogSetting("Renderer_UseVsync", Settings::values.use_vsync);
LogSetting("Renderer_UseAssemblyShaders", Settings::values.use_assembly_shaders);
LogSetting("Renderer_AnisotropicFilteringLevel", Settings::values.max_anisotropy);
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);
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));
@@ -131,15 +132,56 @@ float Volume() {
if (values.audio_muted) {
return 0.0f;
}
return values.volume;
return values.volume.GetValue();
}
bool IsGPULevelExtreme() {
return values.gpu_accuracy == GPUAccuracy::Extreme;
return values.gpu_accuracy.GetValue() == GPUAccuracy::Extreme;
}
bool IsGPULevelHigh() {
return values.gpu_accuracy == GPUAccuracy::Extreme || values.gpu_accuracy == GPUAccuracy::High;
return values.gpu_accuracy.GetValue() == GPUAccuracy::Extreme ||
values.gpu_accuracy.GetValue() == GPUAccuracy::High;
}
void RestoreGlobalState() {
// If a game is running, DO NOT restore the global settings state
if (Core::System::GetInstance().IsPoweredOn()) {
return;
}
// Audio
values.enable_audio_stretching.SetGlobal(true);
values.volume.SetGlobal(true);
// Core
values.use_multi_core.SetGlobal(true);
// Renderer
values.renderer_backend.SetGlobal(true);
values.vulkan_device.SetGlobal(true);
values.aspect_ratio.SetGlobal(true);
values.max_anisotropy.SetGlobal(true);
values.use_frame_limit.SetGlobal(true);
values.frame_limit.SetGlobal(true);
values.use_disk_shader_cache.SetGlobal(true);
values.gpu_accuracy.SetGlobal(true);
values.use_asynchronous_gpu_emulation.SetGlobal(true);
values.use_vsync.SetGlobal(true);
values.use_assembly_shaders.SetGlobal(true);
values.use_fast_gpu_time.SetGlobal(true);
values.force_30fps_mode.SetGlobal(true);
values.bg_red.SetGlobal(true);
values.bg_green.SetGlobal(true);
values.bg_blue.SetGlobal(true);
// System
values.language_index.SetGlobal(true);
values.region_index.SetGlobal(true);
values.time_zone_index.SetGlobal(true);
values.rng_seed.SetGlobal(true);
values.custom_rtc.SetGlobal(true);
values.sound_index.SetGlobal(true);
}
} // namespace Settings

View File

@@ -382,20 +382,85 @@ enum class GPUAccuracy : u32 {
Extreme = 2,
};
extern bool configuring_global;
template <typename Type>
class Setting final {
public:
Setting() = default;
explicit Setting(Type val) : global{val} {}
~Setting() = default;
void SetGlobal(bool to_global) {
use_global = to_global;
}
bool UsingGlobal() const {
return use_global;
}
Type GetValue(bool need_global = false) const {
if (use_global || need_global) {
return global;
}
return local;
}
void SetValue(const Type& value) {
if (use_global) {
global = value;
} else {
local = value;
}
}
private:
bool use_global = true;
Type global{};
Type local{};
};
struct Values {
// Audio
std::string audio_device_id;
std::string sink_id;
bool audio_muted;
Setting<bool> enable_audio_stretching;
Setting<float> volume;
// Core
Setting<bool> use_multi_core;
// Renderer
Setting<RendererBackend> renderer_backend;
bool renderer_debug;
Setting<int> vulkan_device;
Setting<u16> resolution_factor = Setting(static_cast<u16>(1));
Setting<int> aspect_ratio;
Setting<int> max_anisotropy;
Setting<bool> use_frame_limit;
Setting<u16> frame_limit;
Setting<bool> use_disk_shader_cache;
Setting<GPUAccuracy> gpu_accuracy;
Setting<bool> use_asynchronous_gpu_emulation;
Setting<bool> use_vsync;
Setting<bool> use_assembly_shaders;
Setting<bool> force_30fps_mode;
Setting<bool> use_fast_gpu_time;
Setting<float> bg_red;
Setting<float> bg_green;
Setting<float> bg_blue;
// System
bool use_docked_mode;
std::optional<u32> rng_seed;
Setting<std::optional<u32>> rng_seed;
// Measured in seconds since epoch
std::optional<std::chrono::seconds> custom_rtc;
Setting<std::optional<std::chrono::seconds>> custom_rtc;
// Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc`
std::chrono::seconds custom_rtc_differential;
s32 current_user;
s32 language_index;
s32 region_index;
s32 time_zone_index;
s32 sound_index;
Setting<s32> language_index;
Setting<s32> region_index;
Setting<s32> time_zone_index;
Setting<s32> sound_index;
// Controls
std::array<PlayerInput, 10> players;
@@ -419,8 +484,7 @@ struct Values {
u16 udp_input_port;
u8 udp_pad_index;
// Core
bool use_multi_core;
bool use_docked_mode;
// Data Storage
bool use_virtual_sd;
@@ -432,39 +496,6 @@ struct Values {
NANDUserSize nand_user_size;
SDMCSize sdmc_size;
// Renderer
RendererBackend renderer_backend;
bool renderer_debug;
int vulkan_device;
u16 resolution_factor{1};
int aspect_ratio;
int max_anisotropy;
bool use_frame_limit;
u16 frame_limit;
bool use_disk_shader_cache;
GPUAccuracy gpu_accuracy;
bool use_asynchronous_gpu_emulation;
bool use_vsync;
bool use_assembly_shaders;
bool force_30fps_mode;
bool use_fast_gpu_time;
float bg_red;
float bg_green;
float bg_blue;
std::string log_filter;
bool use_dev_keys;
// Audio
bool audio_muted;
std::string sink_id;
bool enable_audio_stretching;
std::string audio_device_id;
float volume;
// Debugging
bool record_frame_times;
bool use_gdbstub;
@@ -477,7 +508,11 @@ struct Values {
bool disable_cpu_opt;
bool disable_macro_jit;
// BCAT
// Misceallaneous
std::string log_filter;
bool use_dev_keys;
// Services
std::string bcat_backend;
bool bcat_boxcat_local;
@@ -501,4 +536,7 @@ std::string GetTimeZoneString();
void Apply();
void LogSettings();
// Restore the global state of all applicable settings in the Values struct
void RestoreGlobalState();
} // namespace Settings

View File

@@ -189,19 +189,24 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
// Log user configuration information
constexpr auto field_type = Telemetry::FieldType::UserConfig;
AddField(field_type, "Audio_SinkId", Settings::values.sink_id);
AddField(field_type, "Audio_EnableAudioStretching", Settings::values.enable_audio_stretching);
AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core);
AddField(field_type, "Renderer_Backend", TranslateRenderer(Settings::values.renderer_backend));
AddField(field_type, "Renderer_ResolutionFactor", Settings::values.resolution_factor);
AddField(field_type, "Renderer_UseFrameLimit", Settings::values.use_frame_limit);
AddField(field_type, "Renderer_FrameLimit", Settings::values.frame_limit);
AddField(field_type, "Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache);
AddField(field_type, "Audio_EnableAudioStretching",
Settings::values.enable_audio_stretching.GetValue());
AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core.GetValue());
AddField(field_type, "Renderer_Backend",
TranslateRenderer(Settings::values.renderer_backend.GetValue()));
AddField(field_type, "Renderer_ResolutionFactor",
Settings::values.resolution_factor.GetValue());
AddField(field_type, "Renderer_UseFrameLimit", Settings::values.use_frame_limit.GetValue());
AddField(field_type, "Renderer_FrameLimit", Settings::values.frame_limit.GetValue());
AddField(field_type, "Renderer_UseDiskShaderCache",
Settings::values.use_disk_shader_cache.GetValue());
AddField(field_type, "Renderer_GPUAccuracyLevel",
TranslateGPUAccuracyLevel(Settings::values.gpu_accuracy));
TranslateGPUAccuracyLevel(Settings::values.gpu_accuracy.GetValue()));
AddField(field_type, "Renderer_UseAsynchronousGpuEmulation",
Settings::values.use_asynchronous_gpu_emulation);
AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync);
AddField(field_type, "Renderer_UseAssemblyShaders", Settings::values.use_assembly_shaders);
Settings::values.use_asynchronous_gpu_emulation.GetValue());
AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync.GetValue());
AddField(field_type, "Renderer_UseAssemblyShaders",
Settings::values.use_assembly_shaders.GetValue());
AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode);
}

View File

@@ -24,9 +24,13 @@ Adapter::Adapter() {
LOG_INFO(Input, "GC Adapter Initialization started");
current_status = NO_ADAPTER_DETECTED;
libusb_init(&libusb_ctx);
StartScanThread();
const int init_res = libusb_init(&libusb_ctx);
if (init_res == LIBUSB_SUCCESS) {
StartScanThread();
} else {
LOG_ERROR(Input, "libusb could not be initialized. failed with error = {}", init_res);
}
}
GCPadStatus Adapter::GetPadStatus(int port, const std::array<u8, 37>& adapter_payload) {
@@ -211,17 +215,26 @@ void Adapter::Setup() {
adapter_controllers_status.fill(ControllerTypes::None);
// pointer to list of connected usb devices
libusb_device** devices;
libusb_device** devices{};
// populate the list of devices, get the count
const std::size_t device_count = libusb_get_device_list(libusb_ctx, &devices);
const ssize_t device_count = libusb_get_device_list(libusb_ctx, &devices);
if (device_count < 0) {
LOG_ERROR(Input, "libusb_get_device_list failed with error: {}", device_count);
detect_thread_running = false; // Stop the loop constantly checking for gc adapter
// TODO: For hotplug+gc adapter checkbox implementation, revert this.
return;
}
for (std::size_t index = 0; index < device_count; ++index) {
if (CheckDeviceAccess(devices[index])) {
// GC Adapter found and accessible, registering it
GetGCEndpoint(devices[index]);
break;
if (devices != nullptr) {
for (std::size_t index = 0; index < device_count; ++index) {
if (CheckDeviceAccess(devices[index])) {
// GC Adapter found and accessible, registering it
GetGCEndpoint(devices[index]);
break;
}
}
libusb_free_device_list(devices, 1);
}
}
@@ -279,7 +292,13 @@ bool Adapter::CheckDeviceAccess(libusb_device* device) {
void Adapter::GetGCEndpoint(libusb_device* device) {
libusb_config_descriptor* config = nullptr;
libusb_get_config_descriptor(device, 0, &config);
const int config_descriptor_return = libusb_get_config_descriptor(device, 0, &config);
if (config_descriptor_return != LIBUSB_SUCCESS) {
LOG_ERROR(Input, "libusb_get_config_descriptor failed with error = {}",
config_descriptor_return);
return;
}
for (u8 ic = 0; ic < config->bNumInterfaces; ic++) {
const libusb_interface* interfaceContainer = &config->interface[ic];
for (int i = 0; i < interfaceContainer->num_altsetting; i++) {

View File

@@ -7,6 +7,7 @@
#include <functional>
#include <mutex>
#include <thread>
#include <unordered_map>
#include <libusb.h>
#include "common/common_types.h"
#include "common/threadsafe_queue.h"

View File

@@ -96,7 +96,8 @@ public:
}
if (is_written) {
map->MarkAsModified(true, GetModifiedTicks());
if (Settings::IsGPULevelHigh() && Settings::values.use_asynchronous_gpu_emulation) {
if (Settings::IsGPULevelHigh() &&
Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
MarkForAsyncFlush(map);
}
if (!map->is_written) {
@@ -369,7 +370,8 @@ private:
}
if (modified_inheritance) {
map->MarkAsModified(true, GetModifiedTicks());
if (Settings::IsGPULevelHigh() && Settings::values.use_asynchronous_gpu_emulation) {
if (Settings::IsGPULevelHigh() &&
Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
MarkForAsyncFlush(map);
}
}

View File

@@ -157,7 +157,7 @@ u64 GPU::GetTicks() const {
constexpr u64 gpu_ticks_den = 625;
u64 nanoseconds = system.CoreTiming().GetGlobalTimeNs().count();
if (Settings::values.use_fast_gpu_time) {
if (Settings::values.use_fast_gpu_time.GetValue()) {
nanoseconds /= 256;
}
const u64 nanoseconds_num = nanoseconds / gpu_ticks_den;

View File

@@ -132,7 +132,7 @@ public:
}
query->BindCounter(Stream(type).Current(), timestamp);
if (Settings::values.use_asynchronous_gpu_emulation) {
if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
AsyncFlushQuery(cpu_addr);
}
}

View File

@@ -18,7 +18,7 @@ RendererBase::~RendererBase() = default;
void RendererBase::RefreshBaseSettings() {
UpdateCurrentFramebufferLayout();
renderer_settings.use_framelimiter = Settings::values.use_frame_limit;
renderer_settings.use_framelimiter = Settings::values.use_frame_limit.GetValue();
renderer_settings.set_background_color = true;
}

View File

@@ -229,15 +229,15 @@ Device::Device()
// uniform buffers as "push constants"
has_fast_buffer_sub_data = is_nvidia && !disable_fast_buffer_sub_data;
use_assembly_shaders = Settings::values.use_assembly_shaders && GLAD_GL_NV_gpu_program5 &&
GLAD_GL_NV_compute_program5 && GLAD_GL_NV_transform_feedback &&
GLAD_GL_NV_transform_feedback2;
use_assembly_shaders = Settings::values.use_assembly_shaders.GetValue() &&
GLAD_GL_NV_gpu_program5 && GLAD_GL_NV_compute_program5 &&
GLAD_GL_NV_transform_feedback && GLAD_GL_NV_transform_feedback2;
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);
if (Settings::values.use_assembly_shaders && !use_assembly_shaders) {
if (Settings::values.use_assembly_shaders.GetValue() && !use_assembly_shaders) {
LOG_ERROR(Render_OpenGL, "Assembly shaders enabled but not supported");
}
}

View File

@@ -213,7 +213,7 @@ ShaderDiskCacheOpenGL::~ShaderDiskCacheOpenGL() = default;
std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTransferable() {
// Skip games without title id
const bool has_title_id = system.CurrentProcess()->GetTitleID() != 0;
if (!Settings::values.use_disk_shader_cache || !has_title_id) {
if (!Settings::values.use_disk_shader_cache.GetValue() || !has_title_id) {
return {};
}

View File

@@ -455,8 +455,8 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color
void RendererOpenGL::InitOpenGLObjects() {
frame_mailbox = std::make_unique<FrameMailbox>();
glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
0.0f);
glClearColor(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(),
Settings::values.bg_blue.GetValue(), 0.0f);
// Create shader programs
OGLShader vertex_shader;
@@ -561,8 +561,8 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
if (renderer_settings.set_background_color) {
// Update background color before drawing
glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
0.0f);
glClearColor(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(),
Settings::values.bg_blue.GetValue(), 0.0f);
}
// Set projection matrix

View File

@@ -39,52 +39,18 @@ constexpr std::array POLYGON_OFFSET_ENABLE_LUT = {
} // Anonymous namespace
void FixedPipelineState::DepthStencil::Fill(const Maxwell& regs) noexcept {
raw = 0;
front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op_fail));
front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op_zfail));
front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op_zpass));
front.test_func.Assign(PackComparisonOp(regs.stencil_front_func_func));
if (regs.stencil_two_side_enable) {
back.action_stencil_fail.Assign(PackStencilOp(regs.stencil_back_op_fail));
back.action_depth_fail.Assign(PackStencilOp(regs.stencil_back_op_zfail));
back.action_depth_pass.Assign(PackStencilOp(regs.stencil_back_op_zpass));
back.test_func.Assign(PackComparisonOp(regs.stencil_back_func_func));
} else {
back.action_stencil_fail.Assign(front.action_stencil_fail);
back.action_depth_fail.Assign(front.action_depth_fail);
back.action_depth_pass.Assign(front.action_depth_pass);
back.test_func.Assign(front.test_func);
}
depth_test_enable.Assign(regs.depth_test_enable);
depth_write_enable.Assign(regs.depth_write_enabled);
depth_bounds_enable.Assign(regs.depth_bounds_enable);
stencil_enable.Assign(regs.stencil_enable);
depth_test_func.Assign(PackComparisonOp(regs.depth_test_func));
}
void FixedPipelineState::Rasterizer::Fill(const Maxwell& regs) noexcept {
void FixedPipelineState::Fill(const Maxwell& regs, bool has_extended_dynamic_state) {
const auto& clip = regs.view_volume_clip_control;
const std::array enabled_lut = {regs.polygon_offset_point_enable,
regs.polygon_offset_line_enable,
regs.polygon_offset_fill_enable};
const u32 topology_index = static_cast<u32>(regs.draw.topology.Value());
u32 packed_front_face = PackFrontFace(regs.front_face);
if (regs.screen_y_control.triangle_rast_flip != 0) {
// Flip front face
packed_front_face = 1 - packed_front_face;
}
raw = 0;
topology.Assign(topology_index);
primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0);
cull_enable.Assign(regs.cull_test_enabled != 0 ? 1 : 0);
depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0);
depth_clamp_disabled.Assign(regs.view_volume_clip_control.depth_clamp_disabled.Value());
ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0);
cull_face.Assign(PackCullFace(regs.cull_face));
front_face.Assign(packed_front_face);
polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front));
patch_control_points_minus_one.Assign(regs.patch_vertices - 1);
tessellation_primitive.Assign(static_cast<u32>(regs.tess_mode.prim.Value()));
@@ -93,19 +59,37 @@ void FixedPipelineState::Rasterizer::Fill(const Maxwell& regs) noexcept {
logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0);
logic_op.Assign(PackLogicOp(regs.logic_op.operation));
rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0);
std::memcpy(&point_size, &regs.point_size, sizeof(point_size)); // TODO: C++20 std::bit_cast
}
void FixedPipelineState::ColorBlending::Fill(const Maxwell& regs) noexcept {
std::memcpy(&point_size, &regs.point_size, sizeof(point_size)); // TODO: C++20 std::bit_cast
for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
binding_divisors[index] =
regs.instanced_arrays.IsInstancingEnabled(index) ? regs.vertex_array[index].divisor : 0;
}
for (std::size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
const auto& input = regs.vertex_attrib_format[index];
auto& attribute = attributes[index];
attribute.raw = 0;
attribute.enabled.Assign(input.IsConstant() ? 0 : 1);
attribute.buffer.Assign(input.buffer);
attribute.offset.Assign(input.offset);
attribute.type.Assign(static_cast<u32>(input.type.Value()));
attribute.size.Assign(static_cast<u32>(input.size.Value()));
}
for (std::size_t index = 0; index < std::size(attachments); ++index) {
attachments[index].Fill(regs, index);
}
}
void FixedPipelineState::ViewportSwizzles::Fill(const Maxwell& regs) noexcept {
const auto& transform = regs.viewport_transform;
std::transform(transform.begin(), transform.end(), swizzles.begin(),
std::transform(transform.begin(), transform.end(), viewport_swizzles.begin(),
[](const auto& viewport) { return static_cast<u16>(viewport.swizzle.raw); });
if (!has_extended_dynamic_state) {
no_extended_dynamic_state.Assign(1);
dynamic_state.Fill(regs);
}
}
void FixedPipelineState::BlendingAttachment::Fill(const Maxwell& regs, std::size_t index) {
@@ -147,20 +131,57 @@ void FixedPipelineState::BlendingAttachment::Fill(const Maxwell& regs, std::size
enable.Assign(1);
}
void FixedPipelineState::Fill(const Maxwell& regs) {
rasterizer.Fill(regs);
depth_stencil.Fill(regs);
color_blending.Fill(regs);
viewport_swizzles.Fill(regs);
void FixedPipelineState::DynamicState::Fill(const Maxwell& regs) {
const u32 topology_index = static_cast<u32>(regs.draw.topology.Value());
u32 packed_front_face = PackFrontFace(regs.front_face);
if (regs.screen_y_control.triangle_rast_flip != 0) {
// Flip front face
packed_front_face = 1 - packed_front_face;
}
raw1 = 0;
raw2 = 0;
front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op_fail));
front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op_zfail));
front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op_zpass));
front.test_func.Assign(PackComparisonOp(regs.stencil_front_func_func));
if (regs.stencil_two_side_enable) {
back.action_stencil_fail.Assign(PackStencilOp(regs.stencil_back_op_fail));
back.action_depth_fail.Assign(PackStencilOp(regs.stencil_back_op_zfail));
back.action_depth_pass.Assign(PackStencilOp(regs.stencil_back_op_zpass));
back.test_func.Assign(PackComparisonOp(regs.stencil_back_func_func));
} else {
back.action_stencil_fail.Assign(front.action_stencil_fail);
back.action_depth_fail.Assign(front.action_depth_fail);
back.action_depth_pass.Assign(front.action_depth_pass);
back.test_func.Assign(front.test_func);
}
stencil_enable.Assign(regs.stencil_enable);
depth_write_enable.Assign(regs.depth_write_enabled);
depth_bounds_enable.Assign(regs.depth_bounds_enable);
depth_test_enable.Assign(regs.depth_test_enable);
front_face.Assign(packed_front_face);
depth_test_func.Assign(PackComparisonOp(regs.depth_test_func));
topology.Assign(topology_index);
cull_face.Assign(PackCullFace(regs.cull_face));
cull_enable.Assign(regs.cull_test_enabled != 0 ? 1 : 0);
for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
const auto& input = regs.vertex_array[index];
VertexBinding& binding = vertex_bindings[index];
binding.raw = 0;
binding.enabled.Assign(input.IsEnabled() ? 1 : 0);
binding.stride.Assign(static_cast<u16>(input.stride.Value()));
}
}
std::size_t FixedPipelineState::Hash() const noexcept {
const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), sizeof *this);
const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), Size());
return static_cast<std::size_t>(hash);
}
bool FixedPipelineState::operator==(const FixedPipelineState& rhs) const noexcept {
return std::memcmp(this, &rhs, sizeof *this) == 0;
return std::memcmp(this, &rhs, Size()) == 0;
}
u32 FixedPipelineState::PackComparisonOp(Maxwell::ComparisonOp op) noexcept {

View File

@@ -60,14 +60,6 @@ struct FixedPipelineState {
void Fill(const Maxwell& regs, std::size_t index);
std::size_t Hash() const noexcept;
bool operator==(const BlendingAttachment& rhs) const noexcept;
bool operator!=(const BlendingAttachment& rhs) const noexcept {
return !operator==(rhs);
}
constexpr std::array<bool, 4> Mask() const noexcept {
return {mask_r != 0, mask_g != 0, mask_b != 0, mask_a != 0};
}
@@ -97,83 +89,77 @@ struct FixedPipelineState {
}
};
struct VertexInput {
union Binding {
u16 raw;
BitField<0, 1, u16> enabled;
BitField<1, 12, u16> stride;
};
union VertexAttribute {
u32 raw;
BitField<0, 1, u32> enabled;
BitField<1, 5, u32> buffer;
BitField<6, 14, u32> offset;
BitField<20, 3, u32> type;
BitField<23, 6, u32> size;
union Attribute {
u32 raw;
BitField<0, 1, u32> enabled;
BitField<1, 5, u32> buffer;
BitField<6, 14, u32> offset;
BitField<20, 3, u32> type;
BitField<23, 6, u32> size;
constexpr Maxwell::VertexAttribute::Type Type() const noexcept {
return static_cast<Maxwell::VertexAttribute::Type>(type.Value());
}
constexpr Maxwell::VertexAttribute::Size Size() const noexcept {
return static_cast<Maxwell::VertexAttribute::Size>(size.Value());
}
};
std::array<Binding, Maxwell::NumVertexArrays> bindings;
std::array<u32, Maxwell::NumVertexArrays> binding_divisors;
std::array<Attribute, Maxwell::NumVertexAttributes> attributes;
void SetBinding(std::size_t index, bool enabled, u32 stride, u32 divisor) noexcept {
auto& binding = bindings[index];
binding.raw = 0;
binding.enabled.Assign(enabled ? 1 : 0);
binding.stride.Assign(static_cast<u16>(stride));
binding_divisors[index] = divisor;
constexpr Maxwell::VertexAttribute::Type Type() const noexcept {
return static_cast<Maxwell::VertexAttribute::Type>(type.Value());
}
void SetAttribute(std::size_t index, bool enabled, u32 buffer, u32 offset,
Maxwell::VertexAttribute::Type type,
Maxwell::VertexAttribute::Size size) noexcept {
auto& attribute = attributes[index];
attribute.raw = 0;
attribute.enabled.Assign(enabled ? 1 : 0);
attribute.buffer.Assign(buffer);
attribute.offset.Assign(offset);
attribute.type.Assign(static_cast<u32>(type));
attribute.size.Assign(static_cast<u32>(size));
constexpr Maxwell::VertexAttribute::Size Size() const noexcept {
return static_cast<Maxwell::VertexAttribute::Size>(size.Value());
}
};
struct Rasterizer {
template <std::size_t Position>
union StencilFace {
BitField<Position + 0, 3, u32> action_stencil_fail;
BitField<Position + 3, 3, u32> action_depth_fail;
BitField<Position + 6, 3, u32> action_depth_pass;
BitField<Position + 9, 3, u32> test_func;
Maxwell::StencilOp ActionStencilFail() const noexcept {
return UnpackStencilOp(action_stencil_fail);
}
Maxwell::StencilOp ActionDepthFail() const noexcept {
return UnpackStencilOp(action_depth_fail);
}
Maxwell::StencilOp ActionDepthPass() const noexcept {
return UnpackStencilOp(action_depth_pass);
}
Maxwell::ComparisonOp TestFunc() const noexcept {
return UnpackComparisonOp(test_func);
}
};
union VertexBinding {
u16 raw;
BitField<0, 12, u16> stride;
BitField<12, 1, u16> enabled;
};
struct DynamicState {
union {
u32 raw;
BitField<0, 4, u32> topology;
BitField<4, 1, u32> primitive_restart_enable;
BitField<5, 1, u32> cull_enable;
BitField<6, 1, u32> depth_bias_enable;
BitField<7, 1, u32> depth_clamp_disabled;
BitField<8, 1, u32> ndc_minus_one_to_one;
BitField<9, 2, u32> cull_face;
BitField<11, 1, u32> front_face;
BitField<12, 2, u32> polygon_mode;
BitField<14, 5, u32> patch_control_points_minus_one;
BitField<19, 2, u32> tessellation_primitive;
BitField<21, 2, u32> tessellation_spacing;
BitField<23, 1, u32> tessellation_clockwise;
BitField<24, 1, u32> logic_op_enable;
BitField<25, 4, u32> logic_op;
BitField<29, 1, u32> rasterize_enable;
u32 raw1;
StencilFace<0> front;
StencilFace<12> back;
BitField<24, 1, u32> stencil_enable;
BitField<25, 1, u32> depth_write_enable;
BitField<26, 1, u32> depth_bounds_enable;
BitField<27, 1, u32> depth_test_enable;
BitField<28, 1, u32> front_face;
BitField<29, 3, u32> depth_test_func;
};
union {
u32 raw2;
BitField<0, 4, u32> topology;
BitField<4, 2, u32> cull_face;
BitField<6, 1, u32> cull_enable;
};
std::array<VertexBinding, Maxwell::NumVertexArrays> vertex_bindings;
// TODO(Rodrigo): Move this to push constants
u32 point_size;
void Fill(const Maxwell& regs);
void Fill(const Maxwell& regs) noexcept;
constexpr Maxwell::PrimitiveTopology Topology() const noexcept {
return static_cast<Maxwell::PrimitiveTopology>(topology.Value());
Maxwell::ComparisonOp DepthTestFunc() const noexcept {
return UnpackComparisonOp(depth_test_func);
}
Maxwell::CullFace CullFace() const noexcept {
@@ -183,70 +169,36 @@ struct FixedPipelineState {
Maxwell::FrontFace FrontFace() const noexcept {
return UnpackFrontFace(front_face.Value());
}
};
struct DepthStencil {
template <std::size_t Position>
union StencilFace {
BitField<Position + 0, 3, u32> action_stencil_fail;
BitField<Position + 3, 3, u32> action_depth_fail;
BitField<Position + 6, 3, u32> action_depth_pass;
BitField<Position + 9, 3, u32> test_func;
Maxwell::StencilOp ActionStencilFail() const noexcept {
return UnpackStencilOp(action_stencil_fail);
}
Maxwell::StencilOp ActionDepthFail() const noexcept {
return UnpackStencilOp(action_depth_fail);
}
Maxwell::StencilOp ActionDepthPass() const noexcept {
return UnpackStencilOp(action_depth_pass);
}
Maxwell::ComparisonOp TestFunc() const noexcept {
return UnpackComparisonOp(test_func);
}
};
union {
u32 raw;
StencilFace<0> front;
StencilFace<12> back;
BitField<24, 1, u32> depth_test_enable;
BitField<25, 1, u32> depth_write_enable;
BitField<26, 1, u32> depth_bounds_enable;
BitField<27, 1, u32> stencil_enable;
BitField<28, 3, u32> depth_test_func;
};
void Fill(const Maxwell& regs) noexcept;
Maxwell::ComparisonOp DepthTestFunc() const noexcept {
return UnpackComparisonOp(depth_test_func);
constexpr Maxwell::PrimitiveTopology Topology() const noexcept {
return static_cast<Maxwell::PrimitiveTopology>(topology.Value());
}
};
struct ColorBlending {
std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments;
void Fill(const Maxwell& regs) noexcept;
union {
u32 raw;
BitField<0, 1, u32> no_extended_dynamic_state;
BitField<2, 1, u32> primitive_restart_enable;
BitField<3, 1, u32> depth_bias_enable;
BitField<4, 1, u32> depth_clamp_disabled;
BitField<5, 1, u32> ndc_minus_one_to_one;
BitField<6, 2, u32> polygon_mode;
BitField<8, 5, u32> patch_control_points_minus_one;
BitField<13, 2, u32> tessellation_primitive;
BitField<15, 2, u32> tessellation_spacing;
BitField<17, 1, u32> tessellation_clockwise;
BitField<18, 1, u32> logic_op_enable;
BitField<19, 4, u32> logic_op;
BitField<23, 1, u32> rasterize_enable;
};
u32 point_size;
std::array<u32, Maxwell::NumVertexArrays> binding_divisors;
std::array<VertexAttribute, Maxwell::NumVertexAttributes> attributes;
std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments;
std::array<u16, Maxwell::NumViewports> viewport_swizzles;
DynamicState dynamic_state;
struct ViewportSwizzles {
std::array<u16, Maxwell::NumViewports> swizzles;
void Fill(const Maxwell& regs) noexcept;
};
VertexInput vertex_input;
Rasterizer rasterizer;
DepthStencil depth_stencil;
ColorBlending color_blending;
ViewportSwizzles viewport_swizzles;
void Fill(const Maxwell& regs);
void Fill(const Maxwell& regs, bool has_extended_dynamic_state);
std::size_t Hash() const noexcept;
@@ -255,6 +207,11 @@ struct FixedPipelineState {
bool operator!=(const FixedPipelineState& rhs) const noexcept {
return !operator==(rhs);
}
std::size_t Size() const noexcept {
const std::size_t total_size = sizeof *this;
return total_size - (no_extended_dynamic_state != 0 ? 0 : sizeof(DynamicState));
}
};
static_assert(std::has_unique_object_representations_v<FixedPipelineState>);
static_assert(std::is_trivially_copyable_v<FixedPipelineState>);

View File

@@ -409,7 +409,7 @@ bool RendererVulkan::PickDevices() {
return false;
}
const s32 device_index = Settings::values.vulkan_device;
const s32 device_index = Settings::values.vulkan_device.GetValue();
if (device_index < 0 || device_index >= static_cast<s32>(devices->size())) {
LOG_ERROR(Render_Vulkan, "Invalid device index {}!", device_index);
return false;

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

@@ -313,6 +313,16 @@ bool VKDevice::Create() {
LOG_INFO(Render_Vulkan, "Device doesn't support custom border colors");
}
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;
SetNext(next, dynamic_state);
} else {
LOG_INFO(Render_Vulkan, "Device doesn't support extended dynamic state");
}
if (!ext_depth_range_unrestricted) {
LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted");
}
@@ -541,6 +551,7 @@ std::vector<const char*> VKDevice::LoadExtensions() {
bool has_ext_subgroup_size_control{};
bool has_ext_transform_feedback{};
bool has_ext_custom_border_color{};
bool has_ext_extended_dynamic_state{};
for (const auto& extension : physical.EnumerateDeviceExtensionProperties()) {
Test(extension, nv_viewport_swizzle, VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME, true);
Test(extension, khr_uniform_buffer_standard_layout,
@@ -558,6 +569,8 @@ std::vector<const char*> VKDevice::LoadExtensions() {
false);
Test(extension, has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME,
false);
Test(extension, has_ext_extended_dynamic_state,
VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, false);
if (Settings::values.renderer_debug) {
Test(extension, nv_device_diagnostics_config,
VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, true);
@@ -643,6 +656,19 @@ std::vector<const char*> VKDevice::LoadExtensions() {
}
}
if (has_ext_extended_dynamic_state) {
VkPhysicalDeviceExtendedDynamicStateFeaturesEXT dynamic_state;
dynamic_state.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT;
dynamic_state.pNext = nullptr;
features.pNext = &dynamic_state;
physical.GetFeatures2KHR(features);
if (dynamic_state.extendedDynamicState) {
extensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
ext_extended_dynamic_state = true;
}
}
return extensions;
}

View File

@@ -182,6 +182,11 @@ public:
return ext_custom_border_color;
}
/// Returns true if the device supports VK_EXT_extended_dynamic_state.
bool IsExtExtendedDynamicStateSupported() const {
return ext_extended_dynamic_state;
}
/// Returns the vendor name reported from Vulkan.
std::string_view GetVendorName() const {
return vendor_name;
@@ -239,6 +244,7 @@ private:
bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer.
bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback.
bool ext_custom_border_color{}; ///< Support for VK_EXT_custom_border_color.
bool ext_extended_dynamic_state{}; ///< Support for VK_EXT_extended_dynamic_state.
bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config.
// Telemetry parameters

View File

@@ -176,20 +176,32 @@ std::vector<vk::ShaderModule> VKGraphicsPipeline::CreateShaderModules(
vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpass_params,
const SPIRVProgram& program) const {
const auto& vi = fixed_state.vertex_input;
const auto& ds = fixed_state.depth_stencil;
const auto& cd = fixed_state.color_blending;
const auto& rs = fixed_state.rasterizer;
const auto& viewport_swizzles = fixed_state.viewport_swizzles.swizzles;
const auto& state = fixed_state;
const auto& viewport_swizzles = state.viewport_swizzles;
FixedPipelineState::DynamicState dynamic;
if (device.IsExtExtendedDynamicStateSupported()) {
// Insert dummy values, as long as they are valid they don't matter as extended dynamic
// state is ignored
dynamic.raw1 = 0;
dynamic.raw2 = 0;
for (FixedPipelineState::VertexBinding& binding : dynamic.vertex_bindings) {
// Enable all vertex bindings
binding.raw = 0;
binding.enabled.Assign(1);
}
} else {
dynamic = state.dynamic_state;
}
std::vector<VkVertexInputBindingDescription> vertex_bindings;
std::vector<VkVertexInputBindingDivisorDescriptionEXT> vertex_binding_divisors;
for (std::size_t index = 0; index < std::size(vi.bindings); ++index) {
const auto& binding = vi.bindings[index];
for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
const auto& binding = dynamic.vertex_bindings[index];
if (!binding.enabled) {
continue;
}
const bool instanced = vi.binding_divisors[index] != 0;
const bool instanced = state.binding_divisors[index] != 0;
const auto rate = instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX;
auto& vertex_binding = vertex_bindings.emplace_back();
@@ -200,14 +212,14 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
if (instanced) {
auto& binding_divisor = vertex_binding_divisors.emplace_back();
binding_divisor.binding = static_cast<u32>(index);
binding_divisor.divisor = vi.binding_divisors[index];
binding_divisor.divisor = state.binding_divisors[index];
}
}
std::vector<VkVertexInputAttributeDescription> vertex_attributes;
const auto& input_attributes = program[0]->entries.attributes;
for (std::size_t index = 0; index < std::size(vi.attributes); ++index) {
const auto& attribute = vi.attributes[index];
for (std::size_t index = 0; index < state.attributes.size(); ++index) {
const auto& attribute = state.attributes[index];
if (!attribute.enabled) {
continue;
}
@@ -244,15 +256,15 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
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 = MaxwellToVK::PrimitiveTopology(device, rs.Topology());
input_assembly_ci.topology = MaxwellToVK::PrimitiveTopology(device, dynamic.Topology());
input_assembly_ci.primitiveRestartEnable =
rs.primitive_restart_enable != 0 && SupportsPrimitiveRestart(input_assembly_ci.topology);
state.primitive_restart_enable != 0 && SupportsPrimitiveRestart(input_assembly_ci.topology);
VkPipelineTessellationStateCreateInfo tessellation_ci;
tessellation_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
tessellation_ci.pNext = nullptr;
tessellation_ci.flags = 0;
tessellation_ci.patchControlPoints = rs.patch_control_points_minus_one.Value() + 1;
tessellation_ci.patchControlPoints = state.patch_control_points_minus_one.Value() + 1;
VkPipelineViewportStateCreateInfo viewport_ci;
viewport_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
@@ -280,13 +292,13 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
rasterization_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterization_ci.pNext = nullptr;
rasterization_ci.flags = 0;
rasterization_ci.depthClampEnable = rs.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE;
rasterization_ci.rasterizerDiscardEnable = rs.rasterize_enable == 0 ? VK_TRUE : VK_FALSE;
rasterization_ci.depthClampEnable = state.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE;
rasterization_ci.rasterizerDiscardEnable = state.rasterize_enable == 0 ? VK_TRUE : VK_FALSE;
rasterization_ci.polygonMode = VK_POLYGON_MODE_FILL;
rasterization_ci.cullMode =
rs.cull_enable ? MaxwellToVK::CullFace(rs.CullFace()) : VK_CULL_MODE_NONE;
rasterization_ci.frontFace = MaxwellToVK::FrontFace(rs.FrontFace());
rasterization_ci.depthBiasEnable = rs.depth_bias_enable;
dynamic.cull_enable ? MaxwellToVK::CullFace(dynamic.CullFace()) : VK_CULL_MODE_NONE;
rasterization_ci.frontFace = MaxwellToVK::FrontFace(dynamic.FrontFace());
rasterization_ci.depthBiasEnable = state.depth_bias_enable;
rasterization_ci.depthBiasConstantFactor = 0.0f;
rasterization_ci.depthBiasClamp = 0.0f;
rasterization_ci.depthBiasSlopeFactor = 0.0f;
@@ -307,14 +319,15 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
depth_stencil_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
depth_stencil_ci.pNext = nullptr;
depth_stencil_ci.flags = 0;
depth_stencil_ci.depthTestEnable = ds.depth_test_enable;
depth_stencil_ci.depthWriteEnable = ds.depth_write_enable;
depth_stencil_ci.depthCompareOp =
ds.depth_test_enable ? MaxwellToVK::ComparisonOp(ds.DepthTestFunc()) : VK_COMPARE_OP_ALWAYS;
depth_stencil_ci.depthBoundsTestEnable = ds.depth_bounds_enable;
depth_stencil_ci.stencilTestEnable = ds.stencil_enable;
depth_stencil_ci.front = GetStencilFaceState(ds.front);
depth_stencil_ci.back = GetStencilFaceState(ds.back);
depth_stencil_ci.depthTestEnable = dynamic.depth_test_enable;
depth_stencil_ci.depthWriteEnable = dynamic.depth_write_enable;
depth_stencil_ci.depthCompareOp = dynamic.depth_test_enable
? MaxwellToVK::ComparisonOp(dynamic.DepthTestFunc())
: VK_COMPARE_OP_ALWAYS;
depth_stencil_ci.depthBoundsTestEnable = dynamic.depth_bounds_enable;
depth_stencil_ci.stencilTestEnable = dynamic.stencil_enable;
depth_stencil_ci.front = GetStencilFaceState(dynamic.front);
depth_stencil_ci.back = GetStencilFaceState(dynamic.back);
depth_stencil_ci.minDepthBounds = 0.0f;
depth_stencil_ci.maxDepthBounds = 0.0f;
@@ -324,7 +337,7 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
static constexpr std::array COMPONENT_TABLE = {
VK_COLOR_COMPONENT_R_BIT, VK_COLOR_COMPONENT_G_BIT, VK_COLOR_COMPONENT_B_BIT,
VK_COLOR_COMPONENT_A_BIT};
const auto& blend = cd.attachments[index];
const auto& blend = state.attachments[index];
VkColorComponentFlags color_components = 0;
for (std::size_t i = 0; i < COMPONENT_TABLE.size(); ++i) {
@@ -354,11 +367,27 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
color_blend_ci.pAttachments = cb_attachments.data();
std::memset(color_blend_ci.blendConstants, 0, sizeof(color_blend_ci.blendConstants));
static constexpr std::array dynamic_states = {
std::vector dynamic_states = {
VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR,
VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_BLEND_CONSTANTS,
VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK,
VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE};
VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE,
};
if (device.IsExtExtendedDynamicStateSupported()) {
static constexpr std::array extended = {
VK_DYNAMIC_STATE_CULL_MODE_EXT,
VK_DYNAMIC_STATE_FRONT_FACE_EXT,
VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY_EXT,
VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT,
VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT,
VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT,
VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT,
VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE_EXT,
VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT,
VK_DYNAMIC_STATE_STENCIL_OP_EXT,
};
dynamic_states.insert(dynamic_states.end(), extended.begin(), extended.end());
}
VkPipelineDynamicStateCreateInfo dynamic_state_ci;
dynamic_state_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;

View File

@@ -116,12 +116,12 @@ u32 FillDescriptorLayout(const ShaderEntries& entries,
} // Anonymous namespace
std::size_t GraphicsPipelineCacheKey::Hash() const noexcept {
const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), sizeof *this);
const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), Size());
return static_cast<std::size_t>(hash);
}
bool GraphicsPipelineCacheKey::operator==(const GraphicsPipelineCacheKey& rhs) const noexcept {
return std::memcmp(&rhs, this, sizeof *this) == 0;
return std::memcmp(&rhs, this, Size()) == 0;
}
std::size_t ComputePipelineCacheKey::Hash() const noexcept {
@@ -312,18 +312,19 @@ VKPipelineCache::DecompileShaders(const GraphicsPipelineCacheKey& key) {
const auto& gpu = system.GPU().Maxwell3D();
Specialization specialization;
if (fixed_state.rasterizer.Topology() == Maxwell::PrimitiveTopology::Points) {
if (fixed_state.dynamic_state.Topology() == Maxwell::PrimitiveTopology::Points ||
device.IsExtExtendedDynamicStateSupported()) {
float point_size;
std::memcpy(&point_size, &fixed_state.rasterizer.point_size, sizeof(float));
std::memcpy(&point_size, &fixed_state.point_size, sizeof(float));
specialization.point_size = point_size;
ASSERT(point_size != 0.0f);
}
for (std::size_t i = 0; i < Maxwell::NumVertexAttributes; ++i) {
const auto& attribute = fixed_state.vertex_input.attributes[i];
const auto& attribute = fixed_state.attributes[i];
specialization.enabled_attributes[i] = attribute.enabled.Value() != 0;
specialization.attribute_types[i] = attribute.Type();
}
specialization.ndc_minus_one_to_one = fixed_state.rasterizer.ndc_minus_one_to_one;
specialization.ndc_minus_one_to_one = fixed_state.ndc_minus_one_to_one;
SPIRVProgram program;
std::vector<VkDescriptorSetLayoutBinding> bindings;

View File

@@ -44,10 +44,10 @@ class VKUpdateDescriptorQueue;
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
struct GraphicsPipelineCacheKey {
FixedPipelineState fixed_state;
RenderPassParams renderpass_params;
u32 padding;
std::array<GPUVAddr, Maxwell::MaxShaderProgram> shaders;
u64 padding; // This is necessary for unique object representations
FixedPipelineState fixed_state;
std::size_t Hash() const noexcept;
@@ -56,6 +56,10 @@ struct GraphicsPipelineCacheKey {
bool operator!=(const GraphicsPipelineCacheKey& rhs) const noexcept {
return !operator==(rhs);
}
std::size_t Size() const noexcept {
return sizeof(renderpass_params) + sizeof(padding) + sizeof(shaders) + fixed_state.Size();
}
};
static_assert(std::has_unique_object_representations_v<GraphicsPipelineCacheKey>);
static_assert(std::is_trivially_copyable_v<GraphicsPipelineCacheKey>);

View File

@@ -186,13 +186,22 @@ bool HasToPreserveDepthContents(bool is_clear, const Maxwell& regs) {
scissor.max_y < regs.zeta_height;
}
template <std::size_t N>
std::array<VkDeviceSize, N> ExpandStrides(const std::array<u16, N>& strides) {
std::array<VkDeviceSize, N> expanded;
std::copy(strides.begin(), strides.end(), expanded.begin());
return expanded;
}
} // Anonymous namespace
class BufferBindings final {
public:
void AddVertexBinding(VkBuffer buffer, VkDeviceSize offset) {
void AddVertexBinding(VkBuffer buffer, VkDeviceSize offset, VkDeviceSize size, u32 stride) {
vertex.buffers[vertex.num_buffers] = buffer;
vertex.offsets[vertex.num_buffers] = offset;
vertex.sizes[vertex.num_buffers] = size;
vertex.strides[vertex.num_buffers] = static_cast<u16>(stride);
++vertex.num_buffers;
}
@@ -202,76 +211,76 @@ public:
index.type = type;
}
void Bind(VKScheduler& scheduler) const {
void Bind(const VKDevice& device, VKScheduler& scheduler) const {
// Use this large switch case to avoid dispatching more memory in the record lambda than
// what we need. It looks horrible, but it's the best we can do on standard C++.
switch (vertex.num_buffers) {
case 0:
return BindStatic<0>(scheduler);
return BindStatic<0>(device, scheduler);
case 1:
return BindStatic<1>(scheduler);
return BindStatic<1>(device, scheduler);
case 2:
return BindStatic<2>(scheduler);
return BindStatic<2>(device, scheduler);
case 3:
return BindStatic<3>(scheduler);
return BindStatic<3>(device, scheduler);
case 4:
return BindStatic<4>(scheduler);
return BindStatic<4>(device, scheduler);
case 5:
return BindStatic<5>(scheduler);
return BindStatic<5>(device, scheduler);
case 6:
return BindStatic<6>(scheduler);
return BindStatic<6>(device, scheduler);
case 7:
return BindStatic<7>(scheduler);
return BindStatic<7>(device, scheduler);
case 8:
return BindStatic<8>(scheduler);
return BindStatic<8>(device, scheduler);
case 9:
return BindStatic<9>(scheduler);
return BindStatic<9>(device, scheduler);
case 10:
return BindStatic<10>(scheduler);
return BindStatic<10>(device, scheduler);
case 11:
return BindStatic<11>(scheduler);
return BindStatic<11>(device, scheduler);
case 12:
return BindStatic<12>(scheduler);
return BindStatic<12>(device, scheduler);
case 13:
return BindStatic<13>(scheduler);
return BindStatic<13>(device, scheduler);
case 14:
return BindStatic<14>(scheduler);
return BindStatic<14>(device, scheduler);
case 15:
return BindStatic<15>(scheduler);
return BindStatic<15>(device, scheduler);
case 16:
return BindStatic<16>(scheduler);
return BindStatic<16>(device, scheduler);
case 17:
return BindStatic<17>(scheduler);
return BindStatic<17>(device, scheduler);
case 18:
return BindStatic<18>(scheduler);
return BindStatic<18>(device, scheduler);
case 19:
return BindStatic<19>(scheduler);
return BindStatic<19>(device, scheduler);
case 20:
return BindStatic<20>(scheduler);
return BindStatic<20>(device, scheduler);
case 21:
return BindStatic<21>(scheduler);
return BindStatic<21>(device, scheduler);
case 22:
return BindStatic<22>(scheduler);
return BindStatic<22>(device, scheduler);
case 23:
return BindStatic<23>(scheduler);
return BindStatic<23>(device, scheduler);
case 24:
return BindStatic<24>(scheduler);
return BindStatic<24>(device, scheduler);
case 25:
return BindStatic<25>(scheduler);
return BindStatic<25>(device, scheduler);
case 26:
return BindStatic<26>(scheduler);
return BindStatic<26>(device, scheduler);
case 27:
return BindStatic<27>(scheduler);
return BindStatic<27>(device, scheduler);
case 28:
return BindStatic<28>(scheduler);
return BindStatic<28>(device, scheduler);
case 29:
return BindStatic<29>(scheduler);
return BindStatic<29>(device, scheduler);
case 30:
return BindStatic<30>(scheduler);
return BindStatic<30>(device, scheduler);
case 31:
return BindStatic<31>(scheduler);
return BindStatic<31>(device, scheduler);
case 32:
return BindStatic<32>(scheduler);
return BindStatic<32>(device, scheduler);
}
UNREACHABLE();
}
@@ -282,6 +291,8 @@ private:
std::size_t num_buffers = 0;
std::array<VkBuffer, Maxwell::NumVertexArrays> buffers;
std::array<VkDeviceSize, Maxwell::NumVertexArrays> offsets;
std::array<VkDeviceSize, Maxwell::NumVertexArrays> sizes;
std::array<u16, Maxwell::NumVertexArrays> strides;
} vertex;
struct {
@@ -291,15 +302,23 @@ private:
} index;
template <std::size_t N>
void BindStatic(VKScheduler& scheduler) const {
if (index.buffer) {
BindStatic<N, true>(scheduler);
void BindStatic(const VKDevice& device, VKScheduler& scheduler) const {
if (device.IsExtExtendedDynamicStateSupported()) {
if (index.buffer) {
BindStatic<N, true, true>(scheduler);
} else {
BindStatic<N, false, true>(scheduler);
}
} else {
BindStatic<N, false>(scheduler);
if (index.buffer) {
BindStatic<N, true, false>(scheduler);
} else {
BindStatic<N, false, false>(scheduler);
}
}
}
template <std::size_t N, bool is_indexed>
template <std::size_t N, bool is_indexed, bool has_extended_dynamic_state>
void BindStatic(VKScheduler& scheduler) const {
static_assert(N <= Maxwell::NumVertexArrays);
if constexpr (N == 0) {
@@ -311,6 +330,31 @@ private:
std::copy(vertex.buffers.begin(), vertex.buffers.begin() + N, buffers.begin());
std::copy(vertex.offsets.begin(), vertex.offsets.begin() + N, offsets.begin());
if constexpr (has_extended_dynamic_state) {
// With extended dynamic states we can specify the length and stride of a vertex buffer
std::array<VkDeviceSize, N> sizes;
std::array<u16, N> strides;
std::copy(vertex.sizes.begin(), vertex.sizes.begin() + N, sizes.begin());
std::copy(vertex.strides.begin(), vertex.strides.begin() + N, strides.begin());
if constexpr (is_indexed) {
scheduler.Record(
[buffers, offsets, sizes, strides, index = index](vk::CommandBuffer cmdbuf) {
cmdbuf.BindIndexBuffer(index.buffer, index.offset, index.type);
cmdbuf.BindVertexBuffers2EXT(0, static_cast<u32>(N), buffers.data(),
offsets.data(), sizes.data(),
ExpandStrides(strides).data());
});
} else {
scheduler.Record([buffers, offsets, sizes, strides](vk::CommandBuffer cmdbuf) {
cmdbuf.BindVertexBuffers2EXT(0, static_cast<u32>(N), buffers.data(),
offsets.data(), sizes.data(),
ExpandStrides(strides).data());
});
}
return;
}
if constexpr (is_indexed) {
// Indexed draw
scheduler.Record([buffers, offsets, index = index](vk::CommandBuffer cmdbuf) {
@@ -369,7 +413,7 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
const auto& gpu = system.GPU().Maxwell3D();
GraphicsPipelineCacheKey key;
key.fixed_state.Fill(gpu.regs);
key.fixed_state.Fill(gpu.regs, device.IsExtExtendedDynamicStateSupported());
buffer_cache.Map(CalculateGraphicsStreamBufferSize(is_indexed));
@@ -402,7 +446,7 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
UpdateDynamicStates();
buffer_bindings.Bind(scheduler);
buffer_bindings.Bind(device, scheduler);
BeginTransformFeedback();
@@ -822,7 +866,7 @@ RasterizerVulkan::DrawParameters RasterizerVulkan::SetupGeometry(FixedPipelineSt
const auto& gpu = system.GPU().Maxwell3D();
const auto& regs = gpu.regs;
SetupVertexArrays(fixed_state.vertex_input, buffer_bindings);
SetupVertexArrays(buffer_bindings);
const u32 base_instance = regs.vb_base_instance;
const u32 num_instances = is_instanced ? gpu.mme_draw.instance_count : 1;
@@ -893,6 +937,17 @@ void RasterizerVulkan::UpdateDynamicStates() {
UpdateBlendConstants(regs);
UpdateDepthBounds(regs);
UpdateStencilFaces(regs);
if (device.IsExtExtendedDynamicStateSupported()) {
UpdateCullMode(regs);
UpdateDepthBoundsTestEnable(regs);
UpdateDepthTestEnable(regs);
UpdateDepthWriteEnable(regs);
UpdateDepthCompareOp(regs);
UpdateFrontFace(regs);
UpdatePrimitiveTopology(regs);
UpdateStencilOp(regs);
UpdateStencilTestEnable(regs);
}
}
void RasterizerVulkan::BeginTransformFeedback() {
@@ -940,41 +995,25 @@ void RasterizerVulkan::EndTransformFeedback() {
[](vk::CommandBuffer cmdbuf) { cmdbuf.EndTransformFeedbackEXT(0, 0, nullptr, nullptr); });
}
void RasterizerVulkan::SetupVertexArrays(FixedPipelineState::VertexInput& vertex_input,
BufferBindings& buffer_bindings) {
void RasterizerVulkan::SetupVertexArrays(BufferBindings& buffer_bindings) {
const auto& regs = system.GPU().Maxwell3D().regs;
for (std::size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
const auto& attrib = regs.vertex_attrib_format[index];
if (attrib.IsConstant()) {
vertex_input.SetAttribute(index, false, 0, 0, {}, {});
continue;
}
vertex_input.SetAttribute(index, true, attrib.buffer, attrib.offset, attrib.type.Value(),
attrib.size.Value());
}
for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
const auto& vertex_array = regs.vertex_array[index];
if (!vertex_array.IsEnabled()) {
vertex_input.SetBinding(index, false, 0, 0);
continue;
}
vertex_input.SetBinding(
index, true, vertex_array.stride,
regs.instanced_arrays.IsInstancingEnabled(index) ? vertex_array.divisor : 0);
const GPUVAddr start{vertex_array.StartAddress()};
const GPUVAddr end{regs.vertex_array_limit[index].LimitAddress()};
ASSERT(end >= start);
const std::size_t size{end - start};
const std::size_t size = end - start;
if (size == 0) {
buffer_bindings.AddVertexBinding(DefaultBuffer(), 0);
buffer_bindings.AddVertexBinding(DefaultBuffer(), 0, DEFAULT_BUFFER_SIZE, 0);
continue;
}
const auto info = buffer_cache.UploadMemory(start, size);
buffer_bindings.AddVertexBinding(info.handle, info.offset);
buffer_bindings.AddVertexBinding(info.handle, info.offset, size, vertex_array.stride);
}
}
@@ -1326,6 +1365,117 @@ void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs)
}
}
void RasterizerVulkan::UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs) {
if (!state_tracker.TouchCullMode()) {
return;
}
scheduler.Record(
[enabled = regs.cull_test_enabled, cull_face = regs.cull_face](vk::CommandBuffer cmdbuf) {
cmdbuf.SetCullModeEXT(enabled ? MaxwellToVK::CullFace(cull_face) : VK_CULL_MODE_NONE);
});
}
void RasterizerVulkan::UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
if (!state_tracker.TouchDepthBoundsTestEnable()) {
return;
}
scheduler.Record([enable = regs.depth_bounds_enable](vk::CommandBuffer cmdbuf) {
cmdbuf.SetDepthBoundsTestEnableEXT(enable);
});
}
void RasterizerVulkan::UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
if (!state_tracker.TouchDepthTestEnable()) {
return;
}
scheduler.Record([enable = regs.depth_test_enable](vk::CommandBuffer cmdbuf) {
cmdbuf.SetDepthTestEnableEXT(enable);
});
}
void RasterizerVulkan::UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
if (!state_tracker.TouchDepthWriteEnable()) {
return;
}
scheduler.Record([enable = regs.depth_write_enabled](vk::CommandBuffer cmdbuf) {
cmdbuf.SetDepthWriteEnableEXT(enable);
});
}
void RasterizerVulkan::UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs) {
if (!state_tracker.TouchDepthCompareOp()) {
return;
}
scheduler.Record([func = regs.depth_test_func](vk::CommandBuffer cmdbuf) {
cmdbuf.SetDepthCompareOpEXT(MaxwellToVK::ComparisonOp(func));
});
}
void RasterizerVulkan::UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs) {
if (!state_tracker.TouchFrontFace()) {
return;
}
VkFrontFace front_face = MaxwellToVK::FrontFace(regs.front_face);
if (regs.screen_y_control.triangle_rast_flip != 0) {
front_face = front_face == VK_FRONT_FACE_CLOCKWISE ? VK_FRONT_FACE_COUNTER_CLOCKWISE
: VK_FRONT_FACE_CLOCKWISE;
}
scheduler.Record(
[front_face](vk::CommandBuffer cmdbuf) { cmdbuf.SetFrontFaceEXT(front_face); });
}
void RasterizerVulkan::UpdatePrimitiveTopology(Tegra::Engines::Maxwell3D::Regs& regs) {
if (!state_tracker.TouchPrimitiveTopology()) {
return;
}
const Maxwell::PrimitiveTopology primitive_topology = regs.draw.topology.Value();
scheduler.Record([this, primitive_topology](vk::CommandBuffer cmdbuf) {
cmdbuf.SetPrimitiveTopologyEXT(MaxwellToVK::PrimitiveTopology(device, primitive_topology));
});
}
void RasterizerVulkan::UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs) {
if (!state_tracker.TouchStencilOp()) {
return;
}
const Maxwell::StencilOp fail = regs.stencil_front_op_fail;
const Maxwell::StencilOp zfail = regs.stencil_front_op_zfail;
const Maxwell::StencilOp zpass = regs.stencil_front_op_zpass;
const Maxwell::ComparisonOp compare = regs.stencil_front_func_func;
if (regs.stencil_two_side_enable) {
scheduler.Record([fail, zfail, zpass, compare](vk::CommandBuffer cmdbuf) {
cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_FRONT_AND_BACK, MaxwellToVK::StencilOp(fail),
MaxwellToVK::StencilOp(zpass), MaxwellToVK::StencilOp(zfail),
MaxwellToVK::ComparisonOp(compare));
});
} else {
const Maxwell::StencilOp back_fail = regs.stencil_back_op_fail;
const Maxwell::StencilOp back_zfail = regs.stencil_back_op_zfail;
const Maxwell::StencilOp back_zpass = regs.stencil_back_op_zpass;
const Maxwell::ComparisonOp back_compare = regs.stencil_back_func_func;
scheduler.Record([fail, zfail, zpass, compare, back_fail, back_zfail, back_zpass,
back_compare](vk::CommandBuffer cmdbuf) {
cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_FRONT_BIT, MaxwellToVK::StencilOp(fail),
MaxwellToVK::StencilOp(zpass), MaxwellToVK::StencilOp(zfail),
MaxwellToVK::ComparisonOp(compare));
cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_BACK_BIT, MaxwellToVK::StencilOp(back_fail),
MaxwellToVK::StencilOp(back_zpass),
MaxwellToVK::StencilOp(back_zfail),
MaxwellToVK::ComparisonOp(back_compare));
});
}
}
void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
if (!state_tracker.TouchStencilTestEnable()) {
return;
}
scheduler.Record([enable = regs.stencil_enable](vk::CommandBuffer cmdbuf) {
cmdbuf.SetStencilTestEnableEXT(enable);
});
}
std::size_t RasterizerVulkan::CalculateGraphicsStreamBufferSize(bool is_indexed) const {
std::size_t size = CalculateVertexArraysSize();
if (is_indexed) {

View File

@@ -185,8 +185,7 @@ private:
bool WalkAttachmentOverlaps(const CachedSurfaceView& attachment);
void SetupVertexArrays(FixedPipelineState::VertexInput& vertex_input,
BufferBindings& buffer_bindings);
void SetupVertexArrays(BufferBindings& buffer_bindings);
void SetupIndexBuffer(BufferBindings& buffer_bindings, DrawParameters& params, bool is_indexed);
@@ -246,6 +245,16 @@ private:
void UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdatePrimitiveTopology(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
std::size_t CalculateGraphicsStreamBufferSize(bool is_indexed) const;
std::size_t CalculateComputeStreamBufferSize() const;

View File

@@ -36,6 +36,15 @@ Flags MakeInvalidationFlags() {
flags[BlendConstants] = true;
flags[DepthBounds] = true;
flags[StencilProperties] = true;
flags[CullMode] = true;
flags[DepthBoundsEnable] = true;
flags[DepthTestEnable] = true;
flags[DepthWriteEnable] = true;
flags[DepthCompareOp] = true;
flags[FrontFace] = true;
flags[PrimitiveTopology] = true;
flags[StencilOp] = true;
flags[StencilTestEnable] = true;
return flags;
}
@@ -75,6 +84,57 @@ void SetupDirtyStencilProperties(Tables& tables) {
table[OFF(stencil_back_func_mask)] = StencilProperties;
}
void SetupDirtyCullMode(Tables& tables) {
auto& table = tables[0];
table[OFF(cull_face)] = CullMode;
table[OFF(cull_test_enabled)] = CullMode;
}
void SetupDirtyDepthBoundsEnable(Tables& tables) {
tables[0][OFF(depth_bounds_enable)] = DepthBoundsEnable;
}
void SetupDirtyDepthTestEnable(Tables& tables) {
tables[0][OFF(depth_test_enable)] = DepthTestEnable;
}
void SetupDirtyDepthWriteEnable(Tables& tables) {
tables[0][OFF(depth_write_enabled)] = DepthWriteEnable;
}
void SetupDirtyDepthCompareOp(Tables& tables) {
tables[0][OFF(depth_test_func)] = DepthCompareOp;
}
void SetupDirtyFrontFace(Tables& tables) {
auto& table = tables[0];
table[OFF(front_face)] = FrontFace;
table[OFF(screen_y_control)] = FrontFace;
}
void SetupDirtyPrimitiveTopology(Tables& tables) {
tables[0][OFF(draw.topology)] = PrimitiveTopology;
}
void SetupDirtyStencilOp(Tables& tables) {
auto& table = tables[0];
table[OFF(stencil_front_op_fail)] = StencilOp;
table[OFF(stencil_front_op_zfail)] = StencilOp;
table[OFF(stencil_front_op_zpass)] = StencilOp;
table[OFF(stencil_front_func_func)] = StencilOp;
table[OFF(stencil_back_op_fail)] = StencilOp;
table[OFF(stencil_back_op_zfail)] = StencilOp;
table[OFF(stencil_back_op_zpass)] = StencilOp;
table[OFF(stencil_back_func_func)] = StencilOp;
// Table 0 is used by StencilProperties
tables[1][OFF(stencil_two_side_enable)] = StencilOp;
}
void SetupDirtyStencilTestEnable(Tables& tables) {
tables[0][OFF(stencil_enable)] = StencilTestEnable;
}
} // Anonymous namespace
StateTracker::StateTracker(Core::System& system)
@@ -90,6 +150,14 @@ void StateTracker::Initialize() {
SetupDirtyBlendConstants(tables);
SetupDirtyDepthBounds(tables);
SetupDirtyStencilProperties(tables);
SetupDirtyCullMode(tables);
SetupDirtyDepthBoundsEnable(tables);
SetupDirtyDepthTestEnable(tables);
SetupDirtyDepthWriteEnable(tables);
SetupDirtyDepthCompareOp(tables);
SetupDirtyFrontFace(tables);
SetupDirtyPrimitiveTopology(tables);
SetupDirtyStencilOp(tables);
}
void StateTracker::InvalidateCommandBufferState() {

View File

@@ -26,6 +26,16 @@ enum : u8 {
DepthBounds,
StencilProperties,
CullMode,
DepthBoundsEnable,
DepthTestEnable,
DepthWriteEnable,
DepthCompareOp,
FrontFace,
PrimitiveTopology,
StencilOp,
StencilTestEnable,
Last
};
static_assert(Last <= std::numeric_limits<u8>::max());
@@ -64,6 +74,46 @@ public:
return Exchange(Dirty::StencilProperties, false);
}
bool TouchCullMode() {
return Exchange(Dirty::CullMode, false);
}
bool TouchDepthBoundsTestEnable() {
return Exchange(Dirty::DepthBoundsEnable, false);
}
bool TouchDepthTestEnable() {
return Exchange(Dirty::DepthTestEnable, false);
}
bool TouchDepthBoundsEnable() {
return Exchange(Dirty::DepthBoundsEnable, false);
}
bool TouchDepthWriteEnable() {
return Exchange(Dirty::DepthWriteEnable, false);
}
bool TouchDepthCompareOp() {
return Exchange(Dirty::DepthCompareOp, false);
}
bool TouchFrontFace() {
return Exchange(Dirty::FrontFace, false);
}
bool TouchPrimitiveTopology() {
return Exchange(Dirty::PrimitiveTopology, false);
}
bool TouchStencilOp() {
return Exchange(Dirty::StencilOp, false);
}
bool TouchStencilTestEnable() {
return Exchange(Dirty::StencilTestEnable, false);
}
private:
bool Exchange(std::size_t id, bool new_value) const noexcept {
auto& flags = system.GPU().Maxwell3D().dirty.flags;

View File

@@ -121,7 +121,7 @@ void VKStreamBuffer::CreateBuffers(VkBufferUsageFlags usage) {
// Substract from the preferred heap size some bytes to avoid getting out of memory.
const VkDeviceSize heap_size = memory_properties.memoryHeaps[preferred_heap].size;
const VkDeviceSize allocable_size = heap_size - 4 * 1024 * 1024;
const VkDeviceSize allocable_size = heap_size - 9 * 1024 * 1024;
VkBufferCreateInfo buffer_ci;
buffer_ci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;

View File

@@ -88,6 +88,16 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
X(vkCmdSetStencilWriteMask);
X(vkCmdSetViewport);
X(vkCmdWaitEvents);
X(vkCmdBindVertexBuffers2EXT);
X(vkCmdSetCullModeEXT);
X(vkCmdSetDepthBoundsTestEnableEXT);
X(vkCmdSetDepthCompareOpEXT);
X(vkCmdSetDepthTestEnableEXT);
X(vkCmdSetDepthWriteEnableEXT);
X(vkCmdSetFrontFaceEXT);
X(vkCmdSetPrimitiveTopologyEXT);
X(vkCmdSetStencilOpEXT);
X(vkCmdSetStencilTestEnableEXT);
X(vkCreateBuffer);
X(vkCreateBufferView);
X(vkCreateCommandPool);

View File

@@ -207,6 +207,16 @@ struct DeviceDispatch : public InstanceDispatch {
PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask;
PFN_vkCmdSetViewport vkCmdSetViewport;
PFN_vkCmdWaitEvents vkCmdWaitEvents;
PFN_vkCmdBindVertexBuffers2EXT vkCmdBindVertexBuffers2EXT;
PFN_vkCmdSetCullModeEXT vkCmdSetCullModeEXT;
PFN_vkCmdSetDepthBoundsTestEnableEXT vkCmdSetDepthBoundsTestEnableEXT;
PFN_vkCmdSetDepthCompareOpEXT vkCmdSetDepthCompareOpEXT;
PFN_vkCmdSetDepthTestEnableEXT vkCmdSetDepthTestEnableEXT;
PFN_vkCmdSetDepthWriteEnableEXT vkCmdSetDepthWriteEnableEXT;
PFN_vkCmdSetFrontFaceEXT vkCmdSetFrontFaceEXT;
PFN_vkCmdSetPrimitiveTopologyEXT vkCmdSetPrimitiveTopologyEXT;
PFN_vkCmdSetStencilOpEXT vkCmdSetStencilOpEXT;
PFN_vkCmdSetStencilTestEnableEXT vkCmdSetStencilTestEnableEXT;
PFN_vkCreateBuffer vkCreateBuffer;
PFN_vkCreateBufferView vkCreateBufferView;
PFN_vkCreateCommandPool vkCreateCommandPool;
@@ -969,6 +979,50 @@ public:
buffer_barriers.data(), image_barriers.size(), image_barriers.data());
}
void BindVertexBuffers2EXT(u32 first_binding, u32 binding_count, const VkBuffer* buffers,
const VkDeviceSize* offsets, const VkDeviceSize* sizes,
const VkDeviceSize* strides) const noexcept {
dld->vkCmdBindVertexBuffers2EXT(handle, first_binding, binding_count, buffers, offsets,
sizes, strides);
}
void SetCullModeEXT(VkCullModeFlags cull_mode) const noexcept {
dld->vkCmdSetCullModeEXT(handle, cull_mode);
}
void SetDepthBoundsTestEnableEXT(bool enable) const noexcept {
dld->vkCmdSetDepthBoundsTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
}
void SetDepthCompareOpEXT(VkCompareOp compare_op) const noexcept {
dld->vkCmdSetDepthCompareOpEXT(handle, compare_op);
}
void SetDepthTestEnableEXT(bool enable) const noexcept {
dld->vkCmdSetDepthTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
}
void SetDepthWriteEnableEXT(bool enable) const noexcept {
dld->vkCmdSetDepthWriteEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
}
void SetFrontFaceEXT(VkFrontFace front_face) const noexcept {
dld->vkCmdSetFrontFaceEXT(handle, front_face);
}
void SetPrimitiveTopologyEXT(VkPrimitiveTopology primitive_topology) const noexcept {
dld->vkCmdSetPrimitiveTopologyEXT(handle, primitive_topology);
}
void SetStencilOpEXT(VkStencilFaceFlags face_mask, VkStencilOp fail_op, VkStencilOp pass_op,
VkStencilOp depth_fail_op, VkCompareOp compare_op) const noexcept {
dld->vkCmdSetStencilOpEXT(handle, face_mask, fail_op, pass_op, depth_fail_op, compare_op);
}
void SetStencilTestEnableEXT(bool enable) const noexcept {
dld->vkCmdSetStencilTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
}
void BindTransformFeedbackBuffersEXT(u32 first, u32 count, const VkBuffer* buffers,
const VkDeviceSize* offsets,
const VkDeviceSize* sizes) const noexcept {

View File

@@ -20,6 +20,7 @@ namespace VideoCommon {
template <class T>
class ShaderCache {
static constexpr u64 PAGE_BITS = 14;
static constexpr u64 PAGE_SIZE = u64(1) << PAGE_BITS;
struct Entry {
VAddr addr_start;
@@ -87,8 +88,8 @@ protected:
const VAddr addr_end = addr + size;
Entry* const entry = NewEntry(addr, addr_end, data.get());
const u64 page_end = addr_end >> PAGE_BITS;
for (u64 page = addr >> PAGE_BITS; page <= page_end; ++page) {
const u64 page_end = (addr_end + PAGE_SIZE - 1) >> PAGE_BITS;
for (u64 page = addr >> PAGE_BITS; page < page_end; ++page) {
invalidation_cache[page].push_back(entry);
}
@@ -108,20 +109,13 @@ private:
/// @pre invalidation_mutex is locked
void InvalidatePagesInRegion(VAddr addr, std::size_t size) {
const VAddr addr_end = addr + size;
const u64 page_end = addr_end >> PAGE_BITS;
for (u64 page = addr >> PAGE_BITS; page <= page_end; ++page) {
const auto it = invalidation_cache.find(page);
const u64 page_end = (addr_end + PAGE_SIZE - 1) >> PAGE_BITS;
for (u64 page = addr >> PAGE_BITS; page < page_end; ++page) {
auto it = invalidation_cache.find(page);
if (it == invalidation_cache.end()) {
continue;
}
std::vector<Entry*>& entries = it->second;
InvalidatePageEntries(entries, addr, addr_end);
// If there's nothing else in this page, remove it to avoid overpopulating the hash map.
if (entries.empty()) {
invalidation_cache.erase(it);
}
InvalidatePageEntries(it->second, addr, addr_end);
}
}
@@ -131,15 +125,22 @@ private:
if (marked_for_removal.empty()) {
return;
}
std::scoped_lock lock{lookup_mutex};
// Remove duplicates
std::sort(marked_for_removal.begin(), marked_for_removal.end());
marked_for_removal.erase(std::unique(marked_for_removal.begin(), marked_for_removal.end()),
marked_for_removal.end());
std::vector<T*> removed_shaders;
removed_shaders.reserve(marked_for_removal.size());
std::scoped_lock lock{lookup_mutex};
for (Entry* const entry : marked_for_removal) {
if (lookup_cache.erase(entry->addr_start) > 0) {
removed_shaders.push_back(entry->data);
}
removed_shaders.push_back(entry->data);
const auto it = lookup_cache.find(entry->addr_start);
ASSERT(it != lookup_cache.end());
lookup_cache.erase(it);
}
marked_for_removal.clear();
@@ -154,17 +155,33 @@ private:
/// @param addr_end Non-inclusive end address of the invalidation
/// @pre invalidation_mutex is locked
void InvalidatePageEntries(std::vector<Entry*>& entries, VAddr addr, VAddr addr_end) {
auto it = entries.begin();
while (it != entries.end()) {
Entry* const entry = *it;
std::size_t index = 0;
while (index < entries.size()) {
Entry* const entry = entries[index];
if (!entry->Overlaps(addr, addr_end)) {
++it;
++index;
continue;
}
UnmarkMemory(entry);
marked_for_removal.push_back(entry);
it = entries.erase(it);
UnmarkMemory(entry);
RemoveEntryFromInvalidationCache(entry);
marked_for_removal.push_back(entry);
}
}
/// @brief Removes all references to an entry in the invalidation cache
/// @param entry Entry to remove from the invalidation cache
/// @pre invalidation_mutex is locked
void RemoveEntryFromInvalidationCache(const Entry* entry) {
const u64 page_end = (entry->addr_end + PAGE_SIZE - 1) >> PAGE_BITS;
for (u64 page = entry->addr_start >> PAGE_BITS; page < page_end; ++page) {
const auto entries_it = invalidation_cache.find(page);
ASSERT(entries_it != invalidation_cache.end());
std::vector<Entry*>& entries = entries_it->second;
const auto entry_it = std::find(entries.begin(), entries.end(), entry);
ASSERT(entry_it != entries.end());
entries.erase(entry_it);
}
}
@@ -182,16 +199,11 @@ private:
}
/// @brief Removes a vector of shaders from a list
/// @param removed_shaders Shaders to be removed from the storage, it can contain duplicates
/// @param removed_shaders Shaders to be removed from the storage
/// @pre invalidation_mutex is locked
/// @pre lookup_mutex is locked
void RemoveShadersFromStorage(std::vector<T*> removed_shaders) {
// Remove duplicates
std::sort(removed_shaders.begin(), removed_shaders.end());
removed_shaders.erase(std::unique(removed_shaders.begin(), removed_shaders.end()),
removed_shaders.end());
// Now that there are no duplicates, we can notify removals
// Notify removals
for (T* const shader : removed_shaders) {
OnShaderRemoval(shader);
}

View File

@@ -249,7 +249,7 @@ public:
auto& surface = render_targets[index].target;
surface->MarkAsRenderTarget(false, NO_RT);
const auto& cr_params = surface->GetSurfaceParams();
if (!cr_params.is_tiled && Settings::values.use_asynchronous_gpu_emulation) {
if (!cr_params.is_tiled && Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
AsyncFlushSurface(surface);
}
}

View File

@@ -48,7 +48,7 @@ constexpr std::array<float, 256> SRGB_CONVERSION_LUT = {
};
unsigned SettingsMinimumAnisotropy() noexcept {
switch (static_cast<Anisotropy>(Settings::values.max_anisotropy)) {
switch (static_cast<Anisotropy>(Settings::values.max_anisotropy.GetValue())) {
default:
case Anisotropy::Default:
return 1U;

View File

@@ -19,7 +19,7 @@ namespace {
std::unique_ptr<VideoCore::RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window,
Core::System& system,
Core::Frontend::GraphicsContext& context) {
switch (Settings::values.renderer_backend) {
switch (Settings::values.renderer_backend.GetValue()) {
case Settings::RendererBackend::OpenGL:
return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system, context);
#ifdef HAS_VULKAN
@@ -42,7 +42,7 @@ std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Cor
return nullptr;
}
if (Settings::values.use_asynchronous_gpu_emulation) {
if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
return std::make_unique<VideoCommon::GPUAsynch>(system, std::move(renderer),
std::move(context));
}
@@ -51,8 +51,8 @@ std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Cor
u16 GetResolutionScaleFactor(const RendererBase& renderer) {
return static_cast<u16>(
Settings::values.resolution_factor != 0
? Settings::values.resolution_factor
Settings::values.resolution_factor.GetValue() != 0
? Settings::values.resolution_factor.GetValue()
: renderer.GetRenderWindow().GetFramebufferLayout().GetScalingRatio());
}

View File

@@ -24,6 +24,8 @@ add_executable(yuzu
compatibility_list.h
configuration/config.cpp
configuration/config.h
configuration/configuration_shared.cpp
configuration/configuration_shared.h
configuration/configure.ui
configuration/configure_audio.cpp
configuration/configure_audio.h
@@ -60,9 +62,12 @@ add_executable(yuzu
configuration/configure_mouse_advanced.cpp
configuration/configure_mouse_advanced.h
configuration/configure_mouse_advanced.ui
configuration/configure_per_general.cpp
configuration/configure_per_general.h
configuration/configure_per_general.ui
configuration/configure_per_game.cpp
configuration/configure_per_game.h
configuration/configure_per_game.ui
configuration/configure_per_game_addons.cpp
configuration/configure_per_game_addons.h
configuration/configure_per_game_addons.ui
configuration/configure_profile_manager.cpp
configuration/configure_profile_manager.h
configuration/configure_profile_manager.ui
@@ -93,11 +98,13 @@ add_executable(yuzu
game_list_p.h
game_list_worker.cpp
game_list_worker.h
hotkeys.cpp
hotkeys.h
install_dialog.cpp
install_dialog.h
loading_screen.cpp
loading_screen.h
loading_screen.ui
hotkeys.cpp
hotkeys.h
main.cpp
main.h
main.ui
@@ -147,7 +154,7 @@ endif()
create_target_directory_groups(yuzu)
target_link_libraries(yuzu PRIVATE common core input_common video_core)
target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::OpenGL Qt5::Widgets)
target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::Widgets)
target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
if (ENABLE_VULKAN AND NOT WIN32)

View File

@@ -145,7 +145,7 @@ public:
// disable vsync for any shared contexts
auto format = share_context->format();
format.setSwapInterval(main_surface ? Settings::values.use_vsync : 0);
format.setSwapInterval(main_surface ? Settings::values.use_vsync.GetValue() : 0);
context = std::make_unique<QOpenGLContext>();
context->setShareContext(share_context);
@@ -495,7 +495,7 @@ void GRenderWindow::resizeEvent(QResizeEvent* event) {
std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const {
#ifdef HAS_OPENGL
if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) {
if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL) {
auto c = static_cast<OpenGLSharedContext*>(main_context.get());
// Bind the shared contexts to the main surface in case the backend wants to take over
// presentation
@@ -511,7 +511,7 @@ bool GRenderWindow::InitRenderTarget() {
first_frame = false;
switch (Settings::values.renderer_backend) {
switch (Settings::values.renderer_backend.GetValue()) {
case Settings::RendererBackend::OpenGL:
if (!InitializeOpenGL()) {
return false;
@@ -538,7 +538,7 @@ bool GRenderWindow::InitRenderTarget() {
OnFramebufferSizeChanged();
BackupGeometry();
if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) {
if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL) {
if (!LoadOpenGL()) {
return false;
}

View File

@@ -13,17 +13,20 @@
#include "input_common/udp/client.h"
#include "yuzu/configuration/config.h"
Config::Config() {
Config::Config(const std::string& config_file, bool is_global) {
// TODO: Don't hardcode the path; let the frontend decide where to put the config files.
qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini";
qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + config_file;
FileUtil::CreateFullPath(qt_config_loc);
qt_config =
std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat);
global = is_global;
Reload();
}
Config::~Config() {
Save();
if (global) {
Save();
}
}
const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = {
@@ -212,7 +215,7 @@ const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> Config::default
// UISetting::values.shortcuts, which is alphabetically ordered.
// clang-format off
const std::array<UISettings::Shortcut, 16> Config::default_hotkeys{{
{QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::ApplicationShortcut}},
{QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::WidgetWithChildrenShortcut}},
{QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::ApplicationShortcut}},
{QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}},
{QStringLiteral("Decrease Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("-"), Qt::ApplicationShortcut}},
@@ -220,8 +223,8 @@ const std::array<UISettings::Shortcut, 16> Config::default_hotkeys{{
{QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), Qt::WindowShortcut}},
{QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), Qt::WindowShortcut}},
{QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}},
{QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::ApplicationShortcut}},
{QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WindowShortcut}},
{QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::WidgetWithChildrenShortcut}},
{QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WidgetWithChildrenShortcut}},
{QStringLiteral("Mute Audio"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), Qt::WindowShortcut}},
{QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}},
{QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}},
@@ -402,16 +405,19 @@ void Config::ApplyDefaultProfileIfInputInvalid() {
void Config::ReadAudioValues() {
qt_config->beginGroup(QStringLiteral("Audio"));
Settings::values.sink_id = ReadSetting(QStringLiteral("output_engine"), QStringLiteral("auto"))
.toString()
.toStdString();
Settings::values.enable_audio_stretching =
ReadSetting(QStringLiteral("enable_audio_stretching"), true).toBool();
Settings::values.audio_device_id =
ReadSetting(QStringLiteral("output_device"), QStringLiteral("auto"))
.toString()
.toStdString();
Settings::values.volume = ReadSetting(QStringLiteral("volume"), 1).toFloat();
if (global) {
Settings::values.sink_id =
ReadSetting(QStringLiteral("output_engine"), QStringLiteral("auto"))
.toString()
.toStdString();
Settings::values.audio_device_id =
ReadSetting(QStringLiteral("output_device"), QStringLiteral("auto"))
.toString()
.toStdString();
}
ReadSettingGlobal(Settings::values.enable_audio_stretching,
QStringLiteral("enable_audio_stretching"), true);
ReadSettingGlobal(Settings::values.volume, QStringLiteral("volume"), 1);
qt_config->endGroup();
}
@@ -440,6 +446,8 @@ void Config::ReadControlValues() {
.toInt());
Settings::values.udp_pad_index =
static_cast<u8>(ReadSetting(QStringLiteral("udp_pad_index"), 0).toUInt());
Settings::values.use_docked_mode =
ReadSetting(QStringLiteral("use_docked_mode"), false).toBool();
qt_config->endGroup();
}
@@ -447,7 +455,7 @@ void Config::ReadControlValues() {
void Config::ReadCoreValues() {
qt_config->beginGroup(QStringLiteral("Core"));
Settings::values.use_multi_core = ReadSetting(QStringLiteral("use_multi_core"), false).toBool();
ReadSettingGlobal(Settings::values.use_multi_core, QStringLiteral("use_multi_core"), false);
qt_config->endGroup();
}
@@ -628,32 +636,28 @@ void Config::ReadPathValues() {
void Config::ReadRendererValues() {
qt_config->beginGroup(QStringLiteral("Renderer"));
Settings::values.renderer_backend =
static_cast<Settings::RendererBackend>(ReadSetting(QStringLiteral("backend"), 0).toInt());
Settings::values.renderer_debug = ReadSetting(QStringLiteral("debug"), false).toBool();
Settings::values.vulkan_device = ReadSetting(QStringLiteral("vulkan_device"), 0).toInt();
Settings::values.aspect_ratio = ReadSetting(QStringLiteral("aspect_ratio"), 0).toInt();
Settings::values.max_anisotropy = ReadSetting(QStringLiteral("max_anisotropy"), 0).toInt();
Settings::values.use_frame_limit =
ReadSetting(QStringLiteral("use_frame_limit"), true).toBool();
Settings::values.frame_limit = ReadSetting(QStringLiteral("frame_limit"), 100).toUInt();
Settings::values.use_disk_shader_cache =
ReadSetting(QStringLiteral("use_disk_shader_cache"), true).toBool();
const int gpu_accuracy_level = ReadSetting(QStringLiteral("gpu_accuracy"), 0).toInt();
Settings::values.gpu_accuracy = static_cast<Settings::GPUAccuracy>(gpu_accuracy_level);
Settings::values.use_asynchronous_gpu_emulation =
ReadSetting(QStringLiteral("use_asynchronous_gpu_emulation"), false).toBool();
Settings::values.use_vsync = ReadSetting(QStringLiteral("use_vsync"), true).toBool();
Settings::values.use_assembly_shaders =
ReadSetting(QStringLiteral("use_assembly_shaders"), false).toBool();
Settings::values.use_fast_gpu_time =
ReadSetting(QStringLiteral("use_fast_gpu_time"), true).toBool();
Settings::values.force_30fps_mode =
ReadSetting(QStringLiteral("force_30fps_mode"), false).toBool();
ReadSettingGlobal(Settings::values.renderer_backend, QStringLiteral("backend"), 0);
ReadSettingGlobal(Settings::values.renderer_debug, QStringLiteral("debug"), false);
ReadSettingGlobal(Settings::values.vulkan_device, QStringLiteral("vulkan_device"), 0);
ReadSettingGlobal(Settings::values.aspect_ratio, QStringLiteral("aspect_ratio"), 0);
ReadSettingGlobal(Settings::values.max_anisotropy, QStringLiteral("max_anisotropy"), 0);
ReadSettingGlobal(Settings::values.use_frame_limit, QStringLiteral("use_frame_limit"), true);
ReadSettingGlobal(Settings::values.frame_limit, QStringLiteral("frame_limit"), 100);
ReadSettingGlobal(Settings::values.use_disk_shader_cache,
QStringLiteral("use_disk_shader_cache"), true);
ReadSettingGlobal(Settings::values.gpu_accuracy, QStringLiteral("gpu_accuracy"), 0);
ReadSettingGlobal(Settings::values.use_asynchronous_gpu_emulation,
QStringLiteral("use_asynchronous_gpu_emulation"), false);
ReadSettingGlobal(Settings::values.use_vsync, QStringLiteral("use_vsync"), true);
ReadSettingGlobal(Settings::values.use_assembly_shaders, QStringLiteral("use_assembly_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);
Settings::values.bg_red = ReadSetting(QStringLiteral("bg_red"), 0.0).toFloat();
Settings::values.bg_green = ReadSetting(QStringLiteral("bg_green"), 0.0).toFloat();
Settings::values.bg_blue = ReadSetting(QStringLiteral("bg_blue"), 0.0).toFloat();
ReadSettingGlobal(Settings::values.bg_red, QStringLiteral("bg_red"), 0.0);
ReadSettingGlobal(Settings::values.bg_green, QStringLiteral("bg_green"), 0.0);
ReadSettingGlobal(Settings::values.bg_blue, QStringLiteral("bg_blue"), 0.0);
qt_config->endGroup();
}
@@ -665,11 +669,13 @@ void Config::ReadShortcutValues() {
const auto& [keyseq, context] = shortcut;
qt_config->beginGroup(group);
qt_config->beginGroup(name);
// No longer using ReadSetting for shortcut.second as it innacurately returns a value of 1
// for WidgetWithChildrenShortcut which is a value of 3. Needed to fix shortcuts the open
// a file dialog in windowed mode
UISettings::values.shortcuts.push_back(
{name,
group,
{ReadSetting(QStringLiteral("KeySeq"), keyseq).toString(),
ReadSetting(QStringLiteral("Context"), context).toInt()}});
{ReadSetting(QStringLiteral("KeySeq"), keyseq).toString(), shortcut.second}});
qt_config->endGroup();
qt_config->endGroup();
}
@@ -680,35 +686,45 @@ void Config::ReadShortcutValues() {
void Config::ReadSystemValues() {
qt_config->beginGroup(QStringLiteral("System"));
Settings::values.use_docked_mode =
ReadSetting(QStringLiteral("use_docked_mode"), false).toBool();
ReadSettingGlobal(Settings::values.current_user, QStringLiteral("current_user"), 0);
Settings::values.current_user =
std::clamp<int>(Settings::values.current_user, 0, Service::Account::MAX_USERS - 1);
Settings::values.current_user = std::clamp<int>(
ReadSetting(QStringLiteral("current_user"), 0).toInt(), 0, Service::Account::MAX_USERS - 1);
ReadSettingGlobal(Settings::values.language_index, QStringLiteral("language_index"), 1);
Settings::values.language_index = ReadSetting(QStringLiteral("language_index"), 1).toInt();
ReadSettingGlobal(Settings::values.region_index, QStringLiteral("region_index"), 1);
Settings::values.region_index = ReadSetting(QStringLiteral("region_index"), 1).toInt();
ReadSettingGlobal(Settings::values.time_zone_index, QStringLiteral("time_zone_index"), 0);
Settings::values.time_zone_index = ReadSetting(QStringLiteral("time_zone_index"), 0).toInt();
const auto rng_seed_enabled = ReadSetting(QStringLiteral("rng_seed_enabled"), false).toBool();
if (rng_seed_enabled) {
Settings::values.rng_seed = ReadSetting(QStringLiteral("rng_seed"), 0).toULongLong();
} else {
Settings::values.rng_seed = std::nullopt;
bool rng_seed_enabled;
ReadSettingGlobal(rng_seed_enabled, QStringLiteral("rng_seed_enabled"), false);
bool rng_seed_global =
global || qt_config->value(QStringLiteral("rng_seed/use_global"), true).toBool();
Settings::values.rng_seed.SetGlobal(rng_seed_global);
if (global || !rng_seed_global) {
if (rng_seed_enabled) {
Settings::values.rng_seed.SetValue(
ReadSetting(QStringLiteral("rng_seed"), 0).toULongLong());
} else {
Settings::values.rng_seed.SetValue(std::nullopt);
}
}
const auto custom_rtc_enabled =
ReadSetting(QStringLiteral("custom_rtc_enabled"), false).toBool();
if (custom_rtc_enabled) {
Settings::values.custom_rtc =
std::chrono::seconds(ReadSetting(QStringLiteral("custom_rtc"), 0).toULongLong());
} else {
Settings::values.custom_rtc = std::nullopt;
bool custom_rtc_enabled;
ReadSettingGlobal(custom_rtc_enabled, QStringLiteral("custom_rtc_enabled"), false);
bool custom_rtc_global =
global || qt_config->value(QStringLiteral("custom_rtc/use_global"), true).toBool();
Settings::values.custom_rtc.SetGlobal(custom_rtc_global);
if (global || !custom_rtc_global) {
if (custom_rtc_enabled) {
Settings::values.custom_rtc.SetValue(
std::chrono::seconds(ReadSetting(QStringLiteral("custom_rtc"), 0).toULongLong()));
} else {
Settings::values.custom_rtc.SetValue(std::nullopt);
}
}
Settings::values.sound_index = ReadSetting(QStringLiteral("sound_index"), 1).toInt();
ReadSettingGlobal(Settings::values.sound_index, QStringLiteral("sound_index"), 1);
qt_config->endGroup();
}
@@ -802,18 +818,20 @@ void Config::ReadWebServiceValues() {
}
void Config::ReadValues() {
ReadControlValues();
if (global) {
ReadControlValues();
ReadDataStorageValues();
ReadDebuggingValues();
ReadDisabledAddOnValues();
ReadServiceValues();
ReadUIValues();
ReadWebServiceValues();
ReadMiscellaneousValues();
}
ReadCoreValues();
ReadRendererValues();
ReadAudioValues();
ReadDataStorageValues();
ReadSystemValues();
ReadMiscellaneousValues();
ReadDebuggingValues();
ReadWebServiceValues();
ReadServiceValues();
ReadDisabledAddOnValues();
ReadUIValues();
}
void Config::SavePlayerValues() {
@@ -900,30 +918,35 @@ void Config::SaveTouchscreenValues() {
}
void Config::SaveValues() {
SaveControlValues();
if (global) {
SaveControlValues();
SaveDataStorageValues();
SaveDebuggingValues();
SaveDisabledAddOnValues();
SaveServiceValues();
SaveUIValues();
SaveWebServiceValues();
SaveMiscellaneousValues();
}
SaveCoreValues();
SaveRendererValues();
SaveAudioValues();
SaveDataStorageValues();
SaveSystemValues();
SaveMiscellaneousValues();
SaveDebuggingValues();
SaveWebServiceValues();
SaveServiceValues();
SaveDisabledAddOnValues();
SaveUIValues();
}
void Config::SaveAudioValues() {
qt_config->beginGroup(QStringLiteral("Audio"));
WriteSetting(QStringLiteral("output_engine"), QString::fromStdString(Settings::values.sink_id),
QStringLiteral("auto"));
WriteSetting(QStringLiteral("enable_audio_stretching"),
Settings::values.enable_audio_stretching, true);
WriteSetting(QStringLiteral("output_device"),
QString::fromStdString(Settings::values.audio_device_id), QStringLiteral("auto"));
WriteSetting(QStringLiteral("volume"), Settings::values.volume, 1.0f);
if (global) {
WriteSetting(QStringLiteral("output_engine"),
QString::fromStdString(Settings::values.sink_id), QStringLiteral("auto"));
WriteSetting(QStringLiteral("output_device"),
QString::fromStdString(Settings::values.audio_device_id),
QStringLiteral("auto"));
}
WriteSettingGlobal(QStringLiteral("enable_audio_stretching"),
Settings::values.enable_audio_stretching, true);
WriteSettingGlobal(QStringLiteral("volume"), Settings::values.volume, 1.0f);
qt_config->endGroup();
}
@@ -946,6 +969,7 @@ void Config::SaveControlValues() {
WriteSetting(QStringLiteral("udp_input_port"), Settings::values.udp_input_port,
InputCommon::CemuhookUDP::DEFAULT_PORT);
WriteSetting(QStringLiteral("udp_pad_index"), Settings::values.udp_pad_index, 0);
WriteSetting(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false);
qt_config->endGroup();
}
@@ -953,7 +977,7 @@ void Config::SaveControlValues() {
void Config::SaveCoreValues() {
qt_config->beginGroup(QStringLiteral("Core"));
WriteSetting(QStringLiteral("use_multi_core"), Settings::values.use_multi_core, false);
WriteSettingGlobal(QStringLiteral("use_multi_core"), Settings::values.use_multi_core, false);
qt_config->endGroup();
}
@@ -1076,29 +1100,34 @@ void Config::SavePathValues() {
void Config::SaveRendererValues() {
qt_config->beginGroup(QStringLiteral("Renderer"));
WriteSetting(QStringLiteral("backend"), static_cast<int>(Settings::values.renderer_backend), 0);
WriteSettingGlobal(QStringLiteral("backend"),
static_cast<int>(Settings::values.renderer_backend.GetValue(global)),
Settings::values.renderer_backend.UsingGlobal(), 0);
WriteSetting(QStringLiteral("debug"), Settings::values.renderer_debug, false);
WriteSetting(QStringLiteral("vulkan_device"), Settings::values.vulkan_device, 0);
WriteSetting(QStringLiteral("aspect_ratio"), Settings::values.aspect_ratio, 0);
WriteSetting(QStringLiteral("max_anisotropy"), Settings::values.max_anisotropy, 0);
WriteSetting(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true);
WriteSetting(QStringLiteral("frame_limit"), Settings::values.frame_limit, 100);
WriteSetting(QStringLiteral("use_disk_shader_cache"), Settings::values.use_disk_shader_cache,
true);
WriteSetting(QStringLiteral("gpu_accuracy"), static_cast<int>(Settings::values.gpu_accuracy),
0);
WriteSetting(QStringLiteral("use_asynchronous_gpu_emulation"),
Settings::values.use_asynchronous_gpu_emulation, false);
WriteSetting(QStringLiteral("use_vsync"), Settings::values.use_vsync, true);
WriteSetting(QStringLiteral("use_assembly_shaders"), Settings::values.use_assembly_shaders,
false);
WriteSetting(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time, true);
WriteSetting(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode, false);
WriteSettingGlobal(QStringLiteral("vulkan_device"), Settings::values.vulkan_device, 0);
WriteSettingGlobal(QStringLiteral("aspect_ratio"), Settings::values.aspect_ratio, 0);
WriteSettingGlobal(QStringLiteral("max_anisotropy"), Settings::values.max_anisotropy, 0);
WriteSettingGlobal(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true);
WriteSettingGlobal(QStringLiteral("frame_limit"), Settings::values.frame_limit, 100);
WriteSettingGlobal(QStringLiteral("use_disk_shader_cache"),
Settings::values.use_disk_shader_cache, true);
WriteSettingGlobal(QStringLiteral("gpu_accuracy"),
static_cast<int>(Settings::values.gpu_accuracy.GetValue(global)),
Settings::values.gpu_accuracy.UsingGlobal(), 0);
WriteSettingGlobal(QStringLiteral("use_asynchronous_gpu_emulation"),
Settings::values.use_asynchronous_gpu_emulation, false);
WriteSettingGlobal(QStringLiteral("use_vsync"), Settings::values.use_vsync, true);
WriteSettingGlobal(QStringLiteral("use_assembly_shaders"),
Settings::values.use_assembly_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,
false);
// Cast to double because Qt's written float values are not human-readable
WriteSetting(QStringLiteral("bg_red"), static_cast<double>(Settings::values.bg_red), 0.0);
WriteSetting(QStringLiteral("bg_green"), static_cast<double>(Settings::values.bg_green), 0.0);
WriteSetting(QStringLiteral("bg_blue"), static_cast<double>(Settings::values.bg_blue), 0.0);
WriteSettingGlobal(QStringLiteral("bg_red"), Settings::values.bg_red, 0.0);
WriteSettingGlobal(QStringLiteral("bg_green"), Settings::values.bg_green, 0.0);
WriteSettingGlobal(QStringLiteral("bg_blue"), Settings::values.bg_blue, 0.0);
qt_config->endGroup();
}
@@ -1126,23 +1155,28 @@ void Config::SaveShortcutValues() {
void Config::SaveSystemValues() {
qt_config->beginGroup(QStringLiteral("System"));
WriteSetting(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false);
WriteSetting(QStringLiteral("current_user"), Settings::values.current_user, 0);
WriteSetting(QStringLiteral("language_index"), Settings::values.language_index, 1);
WriteSetting(QStringLiteral("region_index"), Settings::values.region_index, 1);
WriteSetting(QStringLiteral("time_zone_index"), Settings::values.time_zone_index, 0);
WriteSettingGlobal(QStringLiteral("language_index"), Settings::values.language_index, 1);
WriteSettingGlobal(QStringLiteral("region_index"), Settings::values.region_index, 1);
WriteSettingGlobal(QStringLiteral("time_zone_index"), Settings::values.time_zone_index, 0);
WriteSetting(QStringLiteral("rng_seed_enabled"), Settings::values.rng_seed.has_value(), false);
WriteSetting(QStringLiteral("rng_seed"), Settings::values.rng_seed.value_or(0), 0);
WriteSettingGlobal(QStringLiteral("rng_seed_enabled"),
Settings::values.rng_seed.GetValue(global).has_value(),
Settings::values.rng_seed.UsingGlobal(), false);
WriteSettingGlobal(QStringLiteral("rng_seed"),
Settings::values.rng_seed.GetValue(global).value_or(0),
Settings::values.rng_seed.UsingGlobal(), 0);
WriteSetting(QStringLiteral("custom_rtc_enabled"), Settings::values.custom_rtc.has_value(),
false);
WriteSetting(QStringLiteral("custom_rtc"),
QVariant::fromValue<long long>(
Settings::values.custom_rtc.value_or(std::chrono::seconds{}).count()),
0);
WriteSettingGlobal(QStringLiteral("custom_rtc_enabled"),
Settings::values.custom_rtc.GetValue(global).has_value(),
Settings::values.custom_rtc.UsingGlobal(), false);
WriteSettingGlobal(
QStringLiteral("custom_rtc"),
QVariant::fromValue<long long>(
Settings::values.custom_rtc.GetValue(global).value_or(std::chrono::seconds{}).count()),
Settings::values.custom_rtc.UsingGlobal(), 0);
WriteSetting(QStringLiteral("sound_index"), Settings::values.sound_index, 1);
WriteSettingGlobal(QStringLiteral("sound_index"), Settings::values.sound_index, 1);
qt_config->endGroup();
}
@@ -1234,6 +1268,34 @@ QVariant Config::ReadSetting(const QString& name, const QVariant& default_value)
return result;
}
template <typename Type>
void Config::ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name) {
const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool();
setting.SetGlobal(use_global);
if (global || !use_global) {
setting.SetValue(ReadSetting(name).value<Type>());
}
}
template <typename Type>
void Config::ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name,
const QVariant& default_value) {
const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool();
setting.SetGlobal(use_global);
if (global || !use_global) {
setting.SetValue(ReadSetting(name, default_value).value<Type>());
}
}
template <typename Type>
void Config::ReadSettingGlobal(Type& setting, const QString& name,
const QVariant& default_value) const {
const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool();
if (global || !use_global) {
setting = ReadSetting(name, default_value).value<Type>();
}
}
void Config::WriteSetting(const QString& name, const QVariant& value) {
qt_config->setValue(name, value);
}
@@ -1244,6 +1306,40 @@ void Config::WriteSetting(const QString& name, const QVariant& value,
qt_config->setValue(name, value);
}
template <typename Type>
void Config::WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting) {
if (!global) {
qt_config->setValue(name + QStringLiteral("/use_global"), setting.UsingGlobal());
}
if (global || !setting.UsingGlobal()) {
qt_config->setValue(name, setting.GetValue(global));
}
}
template <typename Type>
void Config::WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting,
const QVariant& default_value) {
if (!global) {
qt_config->setValue(name + QStringLiteral("/use_global"), setting.UsingGlobal());
}
if (global || !setting.UsingGlobal()) {
qt_config->setValue(name + QStringLiteral("/default"),
setting.GetValue(global) == default_value.value<Type>());
qt_config->setValue(name, setting.GetValue(global));
}
}
void Config::WriteSettingGlobal(const QString& name, const QVariant& value, bool use_global,
const QVariant& default_value) {
if (!global) {
qt_config->setValue(name + QStringLiteral("/use_global"), use_global);
}
if (global || !use_global) {
qt_config->setValue(name + QStringLiteral("/default"), value == default_value);
qt_config->setValue(name, value);
}
}
void Config::Reload() {
ReadValues();
// To apply default value changes

View File

@@ -7,6 +7,7 @@
#include <array>
#include <memory>
#include <string>
#include <QMetaType>
#include <QVariant>
#include "core/settings.h"
#include "yuzu/uisettings.h"
@@ -15,7 +16,7 @@ class QSettings;
class Config {
public:
Config();
explicit Config(const std::string& config_loc = "qt-config.ini", bool is_global = true);
~Config();
void Reload();
@@ -82,9 +83,33 @@ private:
QVariant ReadSetting(const QString& name) const;
QVariant ReadSetting(const QString& name, const QVariant& default_value) const;
// Templated ReadSettingGlobal functions will also look for the use_global setting and set
// both the value and the global state properly
template <typename Type>
void ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name);
template <typename Type>
void ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name,
const QVariant& default_value);
template <typename Type>
void ReadSettingGlobal(Type& setting, const QString& name, const QVariant& default_value) const;
// Templated WriteSettingGlobal functions will also write the global state if needed and will
// skip writing the actual setting if it defers to the global value
void WriteSetting(const QString& name, const QVariant& value);
void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value);
template <typename Type>
void WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting);
template <typename Type>
void WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting,
const QVariant& default_value);
void WriteSettingGlobal(const QString& name, const QVariant& value, bool use_global,
const QVariant& default_value);
std::unique_ptr<QSettings> qt_config;
std::string qt_config_loc;
bool global;
};
// These metatype declarations cannot be in core/settings.h because core is devoid of QT
Q_DECLARE_METATYPE(Settings::RendererBackend);
Q_DECLARE_METATYPE(Settings::GPUAccuracy);

View File

@@ -0,0 +1,76 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <QCheckBox>
#include <QComboBox>
#include "core/settings.h"
#include "yuzu/configuration/configuration_shared.h"
#include "yuzu/configuration/configure_per_game.h"
void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<bool>* setting,
const QCheckBox* checkbox) {
if (checkbox->checkState() == Qt::PartiallyChecked) {
setting->SetGlobal(true);
} else {
setting->SetGlobal(false);
setting->SetValue(checkbox->checkState() == Qt::Checked);
}
}
void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<int>* setting,
const QComboBox* combobox) {
if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
setting->SetGlobal(true);
} else {
setting->SetGlobal(false);
setting->SetValue(combobox->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET);
}
}
void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<Settings::RendererBackend>* setting,
const QComboBox* combobox) {
if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
setting->SetGlobal(true);
} else {
setting->SetGlobal(false);
setting->SetValue(static_cast<Settings::RendererBackend>(
combobox->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET));
}
}
void ConfigurationShared::SetPerGameSetting(QCheckBox* checkbox,
const Settings::Setting<bool>* setting) {
if (setting->UsingGlobal()) {
checkbox->setCheckState(Qt::PartiallyChecked);
} else {
checkbox->setCheckState(setting->GetValue() ? Qt::Checked : Qt::Unchecked);
}
}
void ConfigurationShared::SetPerGameSetting(QComboBox* combobox,
const Settings::Setting<int>* setting) {
combobox->setCurrentIndex(setting->UsingGlobal()
? ConfigurationShared::USE_GLOBAL_INDEX
: setting->GetValue() + ConfigurationShared::USE_GLOBAL_OFFSET);
}
void ConfigurationShared::SetPerGameSetting(
QComboBox* combobox, const Settings::Setting<Settings::RendererBackend>* setting) {
combobox->setCurrentIndex(setting->UsingGlobal() ? ConfigurationShared::USE_GLOBAL_INDEX
: static_cast<int>(setting->GetValue()) +
ConfigurationShared::USE_GLOBAL_OFFSET);
}
void ConfigurationShared::SetPerGameSetting(
QComboBox* combobox, const Settings::Setting<Settings::GPUAccuracy>* setting) {
combobox->setCurrentIndex(setting->UsingGlobal() ? ConfigurationShared::USE_GLOBAL_INDEX
: static_cast<int>(setting->GetValue()) +
ConfigurationShared::USE_GLOBAL_OFFSET);
}
void ConfigurationShared::InsertGlobalItem(QComboBox* combobox) {
const QString use_global_text = ConfigurePerGame::tr("Use global configuration");
combobox->insertItem(ConfigurationShared::USE_GLOBAL_INDEX, use_global_text);
combobox->insertSeparator(ConfigurationShared::USE_GLOBAL_SEPARATOR_INDEX);
}

View File

@@ -0,0 +1,36 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <QCheckBox>
#include <QComboBox>
#include <QString>
#include "core/settings.h"
namespace ConfigurationShared {
constexpr int USE_GLOBAL_INDEX = 0;
constexpr int USE_GLOBAL_SEPARATOR_INDEX = 1;
constexpr int USE_GLOBAL_OFFSET = 2;
// Global-aware apply and set functions
void ApplyPerGameSetting(Settings::Setting<bool>* setting, const QCheckBox* checkbox);
void ApplyPerGameSetting(Settings::Setting<int>* setting, const QComboBox* combobox);
void ApplyPerGameSetting(Settings::Setting<Settings::RendererBackend>* setting,
const QComboBox* combobox);
void ApplyPerGameSetting(Settings::Setting<Settings::GPUAccuracy>* setting,
const QComboBox* combobox);
void SetPerGameSetting(QCheckBox* checkbox, const Settings::Setting<bool>* setting);
void SetPerGameSetting(QComboBox* combobox, const Settings::Setting<int>* setting);
void SetPerGameSetting(QComboBox* combobox,
const Settings::Setting<Settings::RendererBackend>* setting);
void SetPerGameSetting(QComboBox* combobox,
const Settings::Setting<Settings::GPUAccuracy>* setting);
void InsertGlobalItem(QComboBox* combobox);
} // namespace ConfigurationShared

View File

@@ -11,6 +11,7 @@
#include "core/core.h"
#include "core/settings.h"
#include "ui_configure_audio.h"
#include "yuzu/configuration/configuration_shared.h"
#include "yuzu/configuration/configure_audio.h"
ConfigureAudio::ConfigureAudio(QWidget* parent)
@@ -24,6 +25,11 @@ ConfigureAudio::ConfigureAudio(QWidget* parent)
connect(ui->output_sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this,
&ConfigureAudio::UpdateAudioDevices);
ui->volume_label->setVisible(Settings::configuring_global);
ui->volume_combo_box->setVisible(!Settings::configuring_global);
SetupPerGameUI();
SetConfiguration();
const bool is_powered_on = Core::System::GetInstance().IsPoweredOn();
@@ -41,8 +47,22 @@ void ConfigureAudio::SetConfiguration() {
SetAudioDeviceFromDeviceID();
ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching);
ui->volume_slider->setValue(Settings::values.volume * ui->volume_slider->maximum());
ui->volume_slider->setValue(Settings::values.volume.GetValue() * ui->volume_slider->maximum());
if (Settings::configuring_global) {
ui->toggle_audio_stretching->setChecked(
Settings::values.enable_audio_stretching.GetValue());
} else {
ConfigurationShared::SetPerGameSetting(ui->toggle_audio_stretching,
&Settings::values.enable_audio_stretching);
if (Settings::values.volume.UsingGlobal()) {
ui->volume_combo_box->setCurrentIndex(0);
ui->volume_slider->setEnabled(false);
} else {
ui->volume_combo_box->setCurrentIndex(1);
ui->volume_slider->setEnabled(true);
}
}
SetVolumeIndicatorText(ui->volume_slider->sliderPosition());
}
@@ -80,15 +100,36 @@ void ConfigureAudio::SetVolumeIndicatorText(int percentage) {
}
void ConfigureAudio::ApplyConfiguration() {
Settings::values.sink_id =
ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex())
.toStdString();
Settings::values.enable_audio_stretching = ui->toggle_audio_stretching->isChecked();
Settings::values.audio_device_id =
ui->audio_device_combo_box->itemText(ui->audio_device_combo_box->currentIndex())
.toStdString();
Settings::values.volume =
static_cast<float>(ui->volume_slider->sliderPosition()) / ui->volume_slider->maximum();
if (Settings::configuring_global) {
Settings::values.sink_id =
ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex())
.toStdString();
Settings::values.audio_device_id =
ui->audio_device_combo_box->itemText(ui->audio_device_combo_box->currentIndex())
.toStdString();
// Guard if during game and set to game-specific value
if (Settings::values.enable_audio_stretching.UsingGlobal()) {
Settings::values.enable_audio_stretching.SetValue(
ui->toggle_audio_stretching->isChecked());
}
if (Settings::values.volume.UsingGlobal()) {
Settings::values.volume.SetValue(
static_cast<float>(ui->volume_slider->sliderPosition()) /
ui->volume_slider->maximum());
}
} else {
ConfigurationShared::ApplyPerGameSetting(&Settings::values.enable_audio_stretching,
ui->toggle_audio_stretching);
if (ui->volume_combo_box->currentIndex() == 0) {
Settings::values.volume.SetGlobal(true);
} else {
Settings::values.volume.SetGlobal(false);
Settings::values.volume.SetValue(
static_cast<float>(ui->volume_slider->sliderPosition()) /
ui->volume_slider->maximum());
}
}
}
void ConfigureAudio::changeEvent(QEvent* event) {
@@ -122,3 +163,22 @@ void ConfigureAudio::RetranslateUI() {
ui->retranslateUi(this);
SetVolumeIndicatorText(ui->volume_slider->sliderPosition());
}
void ConfigureAudio::SetupPerGameUI() {
if (Settings::configuring_global) {
ui->volume_slider->setEnabled(Settings::values.volume.UsingGlobal());
ui->toggle_audio_stretching->setEnabled(
Settings::values.enable_audio_stretching.UsingGlobal());
return;
}
ui->toggle_audio_stretching->setTristate(true);
connect(ui->volume_combo_box, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated),
this, [this](int index) { ui->volume_slider->setEnabled(index == 1); });
ui->output_sink_combo_box->setVisible(false);
ui->output_sink_label->setVisible(false);
ui->audio_device_combo_box->setVisible(false);
ui->audio_device_label->setVisible(false);
}

View File

@@ -34,5 +34,7 @@ private:
void SetAudioDeviceFromDeviceID();
void SetVolumeIndicatorText(int percentage);
void SetupPerGameUI();
std::unique_ptr<Ui::ConfigureAudio> ui;
};

View File

@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>188</width>
<height>246</height>
<width>367</width>
<height>368</height>
</rect>
</property>
<layout class="QVBoxLayout">
@@ -18,9 +18,9 @@
</property>
<layout class="QVBoxLayout">
<item>
<layout class="QHBoxLayout">
<layout class="QHBoxLayout" name="_3">
<item>
<widget class="QLabel" name="label_1">
<widget class="QLabel" name="output_sink_label">
<property name="text">
<string>Output Engine:</string>
</property>
@@ -31,20 +31,20 @@
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="toggle_audio_stretching">
<property name="toolTip">
<string>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</string>
</property>
<property name="text">
<string>Enable audio stretching</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout">
<widget class="QCheckBox" name="toggle_audio_stretching">
<property name="toolTip">
<string>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</string>
</property>
<property name="text">
<string>Enable audio stretching</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="_2">
<item>
<widget class="QLabel" name="label_2">
<widget class="QLabel" name="audio_device_label">
<property name="text">
<string>Audio Device:</string>
</property>
@@ -61,7 +61,21 @@
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_3">
<widget class="QComboBox" name="volume_combo_box">
<item>
<property name="text">
<string>Use global volume</string>
</property>
</item>
<item>
<property name="text">
<string>Set volume:</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="volume_label">
<property name="text">
<string>Volume:</string>
</property>
@@ -74,7 +88,7 @@
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<width>30</width>
<height>20</height>
</size>
</property>

View File

@@ -14,6 +14,8 @@
ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry)
: QDialog(parent), ui(new Ui::ConfigureDialog), registry(registry) {
Settings::configuring_global = true;
ui->setupUi(this);
ui->hotkeysTab->Populate(registry);
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);

View File

@@ -7,17 +7,21 @@
#include "core/core.h"
#include "core/settings.h"
#include "ui_configure_general.h"
#include "yuzu/configuration/configuration_shared.h"
#include "yuzu/configuration/configure_general.h"
#include "yuzu/uisettings.h"
ConfigureGeneral::ConfigureGeneral(QWidget* parent)
: QWidget(parent), ui(new Ui::ConfigureGeneral) {
ui->setupUi(this);
SetupPerGameUI();
SetConfiguration();
connect(ui->toggle_frame_limit, &QCheckBox::toggled, ui->frame_limit, &QSpinBox::setEnabled);
connect(ui->toggle_frame_limit, &QCheckBox::stateChanged, ui->frame_limit, [this]() {
ui->frame_limit->setEnabled(ui->toggle_frame_limit->checkState() == Qt::Checked);
});
}
ConfigureGeneral::~ConfigureGeneral() = default;
@@ -26,27 +30,56 @@ void ConfigureGeneral::SetConfiguration() {
const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
ui->use_multi_core->setEnabled(runtime_lock);
ui->use_multi_core->setChecked(Settings::values.use_multi_core);
ui->use_multi_core->setChecked(Settings::values.use_multi_core.GetValue());
ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing);
ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot);
ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background);
ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse);
ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit);
ui->frame_limit->setEnabled(ui->toggle_frame_limit->isChecked());
ui->frame_limit->setValue(Settings::values.frame_limit);
ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit.GetValue());
ui->frame_limit->setValue(Settings::values.frame_limit.GetValue());
if (!Settings::configuring_global) {
if (Settings::values.use_multi_core.UsingGlobal()) {
ui->use_multi_core->setCheckState(Qt::PartiallyChecked);
}
if (Settings::values.use_frame_limit.UsingGlobal()) {
ui->toggle_frame_limit->setCheckState(Qt::PartiallyChecked);
}
}
ui->frame_limit->setEnabled(ui->toggle_frame_limit->checkState() == Qt::Checked &&
ui->toggle_frame_limit->isEnabled());
}
void ConfigureGeneral::ApplyConfiguration() {
UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked();
UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked();
UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked();
if (Settings::configuring_global) {
UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked();
UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked();
UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked();
Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked();
Settings::values.frame_limit = ui->frame_limit->value();
Settings::values.use_multi_core = ui->use_multi_core->isChecked();
// Guard if during game and set to game-specific value
if (Settings::values.use_frame_limit.UsingGlobal()) {
Settings::values.use_frame_limit.SetValue(ui->toggle_frame_limit->checkState() ==
Qt::Checked);
Settings::values.frame_limit.SetValue(ui->frame_limit->value());
Settings::values.use_multi_core.SetValue(ui->use_multi_core->isChecked());
}
} else {
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_multi_core,
ui->use_multi_core);
bool global_frame_limit = ui->toggle_frame_limit->checkState() == Qt::PartiallyChecked;
Settings::values.use_frame_limit.SetGlobal(global_frame_limit);
Settings::values.frame_limit.SetGlobal(global_frame_limit);
if (!global_frame_limit) {
Settings::values.use_frame_limit.SetValue(ui->toggle_frame_limit->checkState() ==
Qt::Checked);
Settings::values.frame_limit.SetValue(ui->frame_limit->value());
}
}
}
void ConfigureGeneral::changeEvent(QEvent* event) {
@@ -60,3 +93,20 @@ void ConfigureGeneral::changeEvent(QEvent* event) {
void ConfigureGeneral::RetranslateUI() {
ui->retranslateUi(this);
}
void ConfigureGeneral::SetupPerGameUI() {
if (Settings::configuring_global) {
ui->toggle_frame_limit->setEnabled(Settings::values.use_frame_limit.UsingGlobal());
ui->frame_limit->setEnabled(Settings::values.frame_limit.UsingGlobal());
return;
}
ui->toggle_check_exit->setVisible(false);
ui->toggle_user_on_boot->setVisible(false);
ui->toggle_background_pause->setVisible(false);
ui->toggle_hide_mouse->setVisible(false);
ui->toggle_frame_limit->setTristate(true);
ui->use_multi_core->setTristate(true);
}

View File

@@ -28,5 +28,7 @@ private:
void SetConfiguration();
void SetupPerGameUI();
std::unique_ptr<Ui::ConfigureGeneral> ui;
};

View File

@@ -13,6 +13,7 @@
#include "core/core.h"
#include "core/settings.h"
#include "ui_configure_graphics.h"
#include "yuzu/configuration/configuration_shared.h"
#include "yuzu/configuration/configure_graphics.h"
#ifdef HAS_VULKAN
@@ -21,16 +22,18 @@
ConfigureGraphics::ConfigureGraphics(QWidget* parent)
: QWidget(parent), ui(new Ui::ConfigureGraphics) {
vulkan_device = Settings::values.vulkan_device;
vulkan_device = Settings::values.vulkan_device.GetValue();
RetrieveVulkanDevices();
ui->setupUi(this);
SetupPerGameUI();
SetConfiguration();
connect(ui->api, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
connect(ui->api, qOverload<int>(&QComboBox::currentIndexChanged), this,
[this] { UpdateDeviceComboBox(); });
connect(ui->device, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this,
connect(ui->device, qOverload<int>(&QComboBox::activated), this,
[this](int device) { UpdateDeviceSelection(device); });
connect(ui->bg_button, &QPushButton::clicked, this, [this] {
@@ -40,6 +43,9 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
}
UpdateBackgroundColorButton(new_bg_color);
});
ui->bg_label->setVisible(Settings::configuring_global);
ui->bg_combobox->setVisible(!Settings::configuring_global);
}
void ConfigureGraphics::UpdateDeviceSelection(int device) {
@@ -57,27 +63,95 @@ void ConfigureGraphics::SetConfiguration() {
const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
ui->api->setEnabled(runtime_lock);
ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend));
ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio);
ui->use_disk_shader_cache->setEnabled(runtime_lock);
ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache);
ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock);
ui->use_asynchronous_gpu_emulation->setChecked(Settings::values.use_asynchronous_gpu_emulation);
UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green,
Settings::values.bg_blue));
ui->use_disk_shader_cache->setEnabled(runtime_lock);
if (Settings::configuring_global) {
ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend.GetValue()));
ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue());
ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue());
ui->use_asynchronous_gpu_emulation->setChecked(
Settings::values.use_asynchronous_gpu_emulation.GetValue());
} else {
ConfigurationShared::SetPerGameSetting(ui->use_disk_shader_cache,
&Settings::values.use_disk_shader_cache);
ConfigurationShared::SetPerGameSetting(ui->use_asynchronous_gpu_emulation,
&Settings::values.use_asynchronous_gpu_emulation);
ConfigurationShared::SetPerGameSetting(ui->api, &Settings::values.renderer_backend);
ConfigurationShared::SetPerGameSetting(ui->aspect_ratio_combobox,
&Settings::values.aspect_ratio);
ui->bg_combobox->setCurrentIndex(Settings::values.bg_red.UsingGlobal() ? 0 : 1);
ui->bg_button->setEnabled(!Settings::values.bg_red.UsingGlobal());
}
UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red.GetValue(),
Settings::values.bg_green.GetValue(),
Settings::values.bg_blue.GetValue()));
UpdateDeviceComboBox();
}
void ConfigureGraphics::ApplyConfiguration() {
Settings::values.renderer_backend = GetCurrentGraphicsBackend();
Settings::values.vulkan_device = vulkan_device;
Settings::values.aspect_ratio = ui->aspect_ratio_combobox->currentIndex();
Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked();
Settings::values.use_asynchronous_gpu_emulation =
ui->use_asynchronous_gpu_emulation->isChecked();
Settings::values.bg_red = static_cast<float>(bg_color.redF());
Settings::values.bg_green = static_cast<float>(bg_color.greenF());
Settings::values.bg_blue = static_cast<float>(bg_color.blueF());
if (Settings::configuring_global) {
// Guard if during game and set to game-specific value
if (Settings::values.renderer_backend.UsingGlobal()) {
Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend());
}
if (Settings::values.vulkan_device.UsingGlobal()) {
Settings::values.vulkan_device.SetValue(vulkan_device);
}
if (Settings::values.aspect_ratio.UsingGlobal()) {
Settings::values.aspect_ratio.SetValue(ui->aspect_ratio_combobox->currentIndex());
}
if (Settings::values.use_disk_shader_cache.UsingGlobal()) {
Settings::values.use_disk_shader_cache.SetValue(ui->use_disk_shader_cache->isChecked());
}
if (Settings::values.use_asynchronous_gpu_emulation.UsingGlobal()) {
Settings::values.use_asynchronous_gpu_emulation.SetValue(
ui->use_asynchronous_gpu_emulation->isChecked());
}
if (Settings::values.bg_red.UsingGlobal()) {
Settings::values.bg_red.SetValue(static_cast<float>(bg_color.redF()));
Settings::values.bg_green.SetValue(static_cast<float>(bg_color.greenF()));
Settings::values.bg_blue.SetValue(static_cast<float>(bg_color.blueF()));
}
} else {
if (ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
Settings::values.renderer_backend.SetGlobal(true);
Settings::values.vulkan_device.SetGlobal(true);
} else {
Settings::values.renderer_backend.SetGlobal(false);
Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend());
if (GetCurrentGraphicsBackend() == Settings::RendererBackend::Vulkan) {
Settings::values.vulkan_device.SetGlobal(false);
Settings::values.vulkan_device.SetValue(vulkan_device);
} else {
Settings::values.vulkan_device.SetGlobal(true);
}
}
ConfigurationShared::ApplyPerGameSetting(&Settings::values.aspect_ratio,
ui->aspect_ratio_combobox);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_disk_shader_cache,
ui->use_disk_shader_cache);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_gpu_emulation,
ui->use_asynchronous_gpu_emulation);
if (ui->bg_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
Settings::values.bg_red.SetGlobal(true);
Settings::values.bg_green.SetGlobal(true);
Settings::values.bg_blue.SetGlobal(true);
} else {
Settings::values.bg_red.SetGlobal(false);
Settings::values.bg_green.SetGlobal(false);
Settings::values.bg_blue.SetGlobal(false);
Settings::values.bg_red.SetValue(static_cast<float>(bg_color.redF()));
Settings::values.bg_green.SetValue(static_cast<float>(bg_color.greenF()));
Settings::values.bg_blue.SetValue(static_cast<float>(bg_color.blueF()));
}
}
}
void ConfigureGraphics::changeEvent(QEvent* event) {
@@ -106,19 +180,27 @@ void ConfigureGraphics::UpdateDeviceComboBox() {
ui->device->clear();
bool enabled = false;
if (!Settings::configuring_global &&
ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
vulkan_device = Settings::values.vulkan_device.GetValue();
}
switch (GetCurrentGraphicsBackend()) {
case Settings::RendererBackend::OpenGL:
ui->device->addItem(tr("OpenGL Graphics Device"));
enabled = false;
break;
case Settings::RendererBackend::Vulkan:
for (const auto device : vulkan_devices) {
for (const auto& device : vulkan_devices) {
ui->device->addItem(device);
}
ui->device->setCurrentIndex(vulkan_device);
enabled = !vulkan_devices.empty();
break;
}
// If in per-game config and use global is selected, don't enable.
enabled &= !(!Settings::configuring_global &&
ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX);
ui->device->setEnabled(enabled && !Core::System::GetInstance().IsPoweredOn());
}
@@ -132,5 +214,37 @@ void ConfigureGraphics::RetrieveVulkanDevices() {
}
Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const {
return static_cast<Settings::RendererBackend>(ui->api->currentIndex());
if (Settings::configuring_global) {
return static_cast<Settings::RendererBackend>(ui->api->currentIndex());
}
if (ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
Settings::values.renderer_backend.SetGlobal(true);
return Settings::values.renderer_backend.GetValue();
}
Settings::values.renderer_backend.SetGlobal(false);
return static_cast<Settings::RendererBackend>(ui->api->currentIndex() -
ConfigurationShared::USE_GLOBAL_OFFSET);
}
void ConfigureGraphics::SetupPerGameUI() {
if (Settings::configuring_global) {
ui->api->setEnabled(Settings::values.renderer_backend.UsingGlobal());
ui->device->setEnabled(Settings::values.renderer_backend.UsingGlobal());
ui->aspect_ratio_combobox->setEnabled(Settings::values.aspect_ratio.UsingGlobal());
ui->use_asynchronous_gpu_emulation->setEnabled(
Settings::values.use_asynchronous_gpu_emulation.UsingGlobal());
ui->use_disk_shader_cache->setEnabled(Settings::values.use_disk_shader_cache.UsingGlobal());
ui->bg_button->setEnabled(Settings::values.bg_red.UsingGlobal());
return;
}
connect(ui->bg_combobox, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this,
[this](int index) { ui->bg_button->setEnabled(index == 1); });
ui->use_disk_shader_cache->setTristate(true);
ui->use_asynchronous_gpu_emulation->setTristate(true);
ConfigurationShared::InsertGlobalItem(ui->aspect_ratio_combobox);
ConfigurationShared::InsertGlobalItem(ui->api);
}

View File

@@ -35,6 +35,8 @@ private:
void RetrieveVulkanDevices();
void SetupPerGameUI();
Settings::RendererBackend GetCurrentGraphicsBackend() const;
std::unique_ptr<Ui::ConfigureGraphics> ui;

View File

@@ -121,6 +121,29 @@
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QComboBox" name="bg_combobox">
<property name="currentText">
<string>Use global background color</string>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<property name="maxVisibleItems">
<number>10</number>
</property>
<item>
<property name="text">
<string>Use global background color</string>
</property>
</item>
<item>
<property name="text">
<string>Set background color:</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="bg_label">
<property name="text">
@@ -128,6 +151,19 @@
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="bg_button">
<property name="maximumSize">

View File

@@ -5,6 +5,7 @@
#include "core/core.h"
#include "core/settings.h"
#include "ui_configure_graphics_advanced.h"
#include "yuzu/configuration/configuration_shared.h"
#include "yuzu/configuration/configure_graphics_advanced.h"
ConfigureGraphicsAdvanced::ConfigureGraphicsAdvanced(QWidget* parent)
@@ -12,6 +13,8 @@ ConfigureGraphicsAdvanced::ConfigureGraphicsAdvanced(QWidget* parent)
ui->setupUi(this);
SetupPerGameUI();
SetConfiguration();
}
@@ -19,26 +22,81 @@ ConfigureGraphicsAdvanced::~ConfigureGraphicsAdvanced() = default;
void ConfigureGraphicsAdvanced::SetConfiguration() {
const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
ui->gpu_accuracy->setCurrentIndex(static_cast<int>(Settings::values.gpu_accuracy));
ui->use_vsync->setEnabled(runtime_lock);
ui->use_vsync->setChecked(Settings::values.use_vsync);
ui->use_assembly_shaders->setEnabled(runtime_lock);
ui->use_assembly_shaders->setChecked(Settings::values.use_assembly_shaders);
ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time);
ui->force_30fps_mode->setEnabled(runtime_lock);
ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode);
ui->anisotropic_filtering_combobox->setEnabled(runtime_lock);
ui->anisotropic_filtering_combobox->setCurrentIndex(Settings::values.max_anisotropy);
if (Settings::configuring_global) {
ui->gpu_accuracy->setCurrentIndex(
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_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(
Settings::values.max_anisotropy.GetValue());
} else {
ConfigurationShared::SetPerGameSetting(ui->gpu_accuracy, &Settings::values.gpu_accuracy);
ConfigurationShared::SetPerGameSetting(ui->use_vsync, &Settings::values.use_vsync);
ConfigurationShared::SetPerGameSetting(ui->use_assembly_shaders,
&Settings::values.use_assembly_shaders);
ConfigurationShared::SetPerGameSetting(ui->use_fast_gpu_time,
&Settings::values.use_fast_gpu_time);
ConfigurationShared::SetPerGameSetting(ui->force_30fps_mode,
&Settings::values.force_30fps_mode);
ConfigurationShared::SetPerGameSetting(ui->anisotropic_filtering_combobox,
&Settings::values.max_anisotropy);
}
}
void ConfigureGraphicsAdvanced::ApplyConfiguration() {
auto gpu_accuracy = static_cast<Settings::GPUAccuracy>(ui->gpu_accuracy->currentIndex());
Settings::values.gpu_accuracy = gpu_accuracy;
Settings::values.use_vsync = ui->use_vsync->isChecked();
Settings::values.use_assembly_shaders = ui->use_assembly_shaders->isChecked();
Settings::values.use_fast_gpu_time = ui->use_fast_gpu_time->isChecked();
Settings::values.force_30fps_mode = ui->force_30fps_mode->isChecked();
Settings::values.max_anisotropy = ui->anisotropic_filtering_combobox->currentIndex();
// Subtract 2 if configuring per-game (separator and "use global configuration" take 2 slots)
const auto gpu_accuracy = static_cast<Settings::GPUAccuracy>(
ui->gpu_accuracy->currentIndex() -
((Settings::configuring_global) ? 0 : ConfigurationShared::USE_GLOBAL_OFFSET));
if (Settings::configuring_global) {
// Must guard in case of a during-game configuration when set to be game-specific.
if (Settings::values.gpu_accuracy.UsingGlobal()) {
Settings::values.gpu_accuracy.SetValue(gpu_accuracy);
}
if (Settings::values.use_vsync.UsingGlobal()) {
Settings::values.use_vsync.SetValue(ui->use_vsync->isChecked());
}
if (Settings::values.use_assembly_shaders.UsingGlobal()) {
Settings::values.use_assembly_shaders.SetValue(ui->use_assembly_shaders->isChecked());
}
if (Settings::values.use_fast_gpu_time.UsingGlobal()) {
Settings::values.use_fast_gpu_time.SetValue(ui->use_fast_gpu_time->isChecked());
}
if (Settings::values.force_30fps_mode.UsingGlobal()) {
Settings::values.force_30fps_mode.SetValue(ui->force_30fps_mode->isChecked());
}
if (Settings::values.max_anisotropy.UsingGlobal()) {
Settings::values.max_anisotropy.SetValue(
ui->anisotropic_filtering_combobox->currentIndex());
}
} else {
ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy,
ui->anisotropic_filtering_combobox);
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_fast_gpu_time,
ui->use_fast_gpu_time);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.force_30fps_mode,
ui->force_30fps_mode);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy,
ui->anisotropic_filtering_combobox);
if (ui->gpu_accuracy->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
Settings::values.gpu_accuracy.SetGlobal(true);
} else {
Settings::values.gpu_accuracy.SetGlobal(false);
Settings::values.gpu_accuracy.SetValue(gpu_accuracy);
}
}
}
void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) {
@@ -52,3 +110,25 @@ void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) {
void ConfigureGraphicsAdvanced::RetranslateUI() {
ui->retranslateUi(this);
}
void ConfigureGraphicsAdvanced::SetupPerGameUI() {
// Disable if not global (only happens during game)
if (Settings::configuring_global) {
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_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(
Settings::values.max_anisotropy.UsingGlobal());
return;
}
ConfigurationShared::InsertGlobalItem(ui->gpu_accuracy);
ui->use_vsync->setTristate(true);
ui->use_assembly_shaders->setTristate(true);
ui->use_fast_gpu_time->setTristate(true);
ui->force_30fps_mode->setTristate(true);
ConfigurationShared::InsertGlobalItem(ui->anisotropic_filtering_combobox);
}

View File

@@ -26,5 +26,7 @@ private:
void SetConfiguration();
void SetupPerGameUI();
std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui;
};

Some files were not shown because too many files have changed in this diff Show More