Compare commits

..

94 Commits

Author SHA1 Message Date
Fernando Sahmkow
f58ee3f15f ShaderDecompiler: Add a debug option to dump the game's shaders. 2022-01-04 02:39:00 +01:00
Fernando S
da8e0f6571 Merge pull request #7648 from bunnei/thread-pinning
core: hle: kernel: Implement thread pinning.
2022-01-03 02:01:26 +01:00
Fernando S
3fa9702952 Merge pull request #7624 from ameerj/intel-msaa-scale
vk_texture_cache: Use 3D scale helpers for MSAA texture scaling on Intel Windows drivers
2022-01-03 00:40:14 +01:00
Fernando S
ae7da0b12d Merge pull request #7629 from ameerj/nv-driver-fixes
shaders: Add fixes for NVIDIA drivers 495+
2022-01-03 00:39:59 +01:00
Fernando S
214b9fc9a7 Merge pull request #7659 from ameerj/overlap-overflow
texture_cache/util: Fix s32 overflow when resolving overlaps
2022-01-01 22:10:29 +01:00
ameerj
951c61aeaa texture_cache/util: Fix s32 overflow when resolving overlaps 2021-12-31 20:03:22 -05:00
Mai M
eb7d361657 Merge pull request #7654 from Morph1984/dynarmic
externals: Update dynarmic to 28714ee7
2021-12-31 02:49:16 -05:00
Morph
af89f7683d externals: Update dynarmic to 28714ee7
Reduces compilation times on MSVC.
2021-12-30 22:28:27 -05:00
bunnei
667a8ae163 Merge pull request #7647 from german77/toad
core/hid: Fix controller type validation
2021-12-30 16:54:35 -08:00
bunnei
3a89723d97 core: hle: kernel: Implement thread pinning.
- We largely had the mechanics in place for thread pinning, this change hooks these up.
- Validated with tests https://github.com/Atmosphere-NX/Atmosphere/blob/master/tests/TestSvc/source/test_thread_pinning.cpp.
2021-12-30 15:50:45 -08:00
german77
9ee5c4ec56 core/hid: Fix controller type validation 2021-12-29 22:51:53 -06:00
bunnei
5e58271903 Merge pull request #7635 from bunnei/set-heap-size
core: hle: kernel: Updated implementation of svcSetHeapSize.
2021-12-29 20:30:12 -08:00
ameerj
8c907c620d glsl: Add boolean reference workaround 2021-12-29 19:03:50 -05:00
ameerj
b84d429c2e glsl_context_get_set: Add alternative cbuf type for broken drivers
some drivers have a bug bitwise converting floating point cbuf values to uint variables. This adds a workaround for these drivers to make all cbufs uint and convert to floating point as needed.
2021-12-29 19:03:50 -05:00
ameerj
9f34be5a61 emit_glsl_integer: Use negation work around 2021-12-29 19:03:50 -05:00
ameerj
14ac0c2923 shader: Add integer attribute get optimization pass
Works around an nvidia driver bug, where casting the integer attributes to float and back to an integer always returned 0.
2021-12-29 19:03:49 -05:00
bunnei
279c7bcc1a Merge pull request #7618 from goldenx86/patch-4
Increase boost requirement to 1.78.0
2021-12-28 16:25:37 -08:00
Matías Locatti
c7235e67ef Empty spaces 2021-12-28 18:50:51 -03:00
Matías Locatti
840d5520d2 Changes to avoid warnings in SSE4.2 optimized SPIR-V 2021-12-28 17:35:55 -03:00
bunnei
091463a429 core: hle: kernel: Updated implementation of svcSetHeapSize.
- Updates our svcSetHeapSize with latest HOS, furthermore allowing heap size to properly be extended/shrunk.
- Validated with tests https://github.com/Atmosphere-NX/Atmosphere/blob/master/tests/TestSvc/source/test_set_heap_size.cpp.
2021-12-28 01:25:20 -08:00
bunnei
f67605e6aa Merge pull request #7622 from ameerj/vk-rescale-invalid-ptr
vk_texture_cache: Fix invalidated pointer access
2021-12-28 00:46:37 -08:00
bunnei
9a0648ff0a Merge pull request #7621 from bunnei/set-mem-perm
core: hle: kernel: Implement SetMemoryPermission.
2021-12-27 23:33:11 -08:00
bunnei
c9e4acc4e2 Merge pull request #7630 from ameerj/glasm-get-int
emit_glasm_context_get_set: Fix GetAttribute return value type.
2021-12-27 16:35:11 -08:00
bunnei
292dfac25e Merge pull request #7620 from bunnei/kernel-thread-x18
core: hle: kernel: KThread: X18 should be a cryptographically random number.
2021-12-25 00:42:54 -08:00
ameerj
37addf7a94 emit_glasm_context_get_set: Fix GetAttribute return value type.
GetAttribute expects an F32 result type at the IR level, this fixes the return value of attributes which were not returning an F32
2021-12-24 20:45:07 -05:00
ameerj
640fc1418b emit_glsl_floating_point: Fix FPNeg on newer Nvidia drivers 2021-12-24 20:03:54 -05:00
bunnei
8a48c4ed1c Merge pull request #7623 from ameerj/unused-func
blit_image: Remove unused function
2021-12-23 22:21:28 -08:00
ameerj
f9e0681d59 vk_texture_cache: Use 3D scale helpers for MSAA texture scaling on Intel Windows drivers
Fixes a crash when scaling MSAA textures in titles such as Sonic Colors Ultimate.
2021-12-23 22:35:19 -05:00
ameerj
cbc0f0a66e blit_image: Remove unused function 2021-12-23 21:06:32 -05:00
ameerj
481b210c0d vk_texture_cache: Fix invalidated pointer access
The vulkan ImageView held a reference to its source image for rescale status checking. This pointer is sometimes invalidated when the texture cache slot_images container is resized.
To avoid an invalid pointer dereference, the ImageView now holds a reference to the container itself.
2021-12-23 20:55:48 -05:00
bunnei
4e7a6639d2 core: hle: kernel: Implement SetMemoryPermission.
- Not seen in any games yet, but validated with kernel tests.
2021-12-23 01:10:36 -08:00
bunnei
a0c7d93b84 core: hle: kernel: KThread: X18 should be a cryptographically random number.
- This was added with firmware 11.0.0 (https://switchbrew.org/wiki/11.0.0).
    - X18 is OR'd by kernel with 1, to make sure it is odd.
2021-12-23 00:03:39 -08:00
bunnei
516325eba8 Merge pull request #7614 from liushuyu/fix-linux-inhibit
main: Fix wake lock (prevent sleep) in Flatpak
2021-12-22 16:53:54 -08:00
Matías Locatti
e0193e2be5 Increase boost requirement to 1.78.0
Liu's finding, this allows to build yuzu on VS 2022.
Ignore at will.
2021-12-22 16:10:21 -03:00
Fernando S
b85f5b1332 Merge pull request #7616 from bunnei/fix-get-idle-ticks
hle: kernel: svc: GetInfo: Fix error checking with IdleTickCount.
2021-12-22 17:39:17 +01:00
Fernando S
648c7b4ed6 Merge pull request #7375 from vonchenplus/convert_legacy
Convert all legacy attributes to generic attributes
2021-12-22 17:36:05 +01:00
liushuyu
14fc1bec17 main: reword inhibit reason 2021-12-22 02:33:01 -07:00
liushuyu
fa7abafa5f main: fix wake lock in Flatpak ...
... by using the XDP system
2021-12-22 02:27:09 -07:00
bunnei
36df305b13 Merge pull request #7599 from FernandoS27/primrestart-vulkan
Vulkan: Fix Primitive Restart and implement Logical Operations
2021-12-22 00:19:23 -08:00
bunnei
f1eff447bb Merge pull request #7602 from jbeich/freebsd-vaapi
build: enable VA-API on FreeBSD
2021-12-21 22:46:49 -08:00
bunnei
49e3c073a5 hle: kernel: svc: GetInfo: Fix error checking with IdleTickCount.
- Enforce tha the supplied handle is invalid, not valid.
- This gets Witcher 3 booting.
2021-12-21 22:41:23 -08:00
bunnei
6991d447d4 Merge pull request #7604 from ameerj/fullscreen-render-window
main: Make separate render window fullscreen toggle on the monitor it resides in
2021-12-21 18:05:11 -08:00
bunnei
b30a1d49ff Merge pull request #7608 from Tatsh/scm-ver-override
Allow overriding SCM version info
2021-12-21 16:07:27 -08:00
bunnei
5e24f7ed31 Merge pull request #7481 from german77/gyro-bias
service/hid: Improve console motion accuracy
2021-12-21 00:13:54 -08:00
bunnei
cf221ca92d Merge pull request #7609 from Tatsh/file-assoc
dist/XDG: add more file associations
2021-12-20 21:55:43 -08:00
Andrew Udvare
59c6f45e7a dist/XDG: add more file associations 2021-12-20 19:18:02 -05:00
Andrew Udvare
caf38725ae Allow overriding SCM version info
If the build is from a non-repository, these functions will return empty. This
patch allows using defines to CMake to set version info such as
-DGIT_BRANCH=master.
2021-12-20 19:13:07 -05:00
bunnei
ee6d40d414 Merge pull request #7597 from bunnei/remove-global-lock
core: hle: Remove global HLE lock.
2021-12-20 14:24:50 -08:00
bunnei
eb4ea7e5c7 Merge pull request #7603 from ameerj/here-we-go-again
kernel: Manually destroy the current process during shut down
2021-12-19 02:09:53 -08:00
ameerj
3074b2eb93 main: Refactor to reduce code duplication in ShowFullscreen() 2021-12-19 02:09:37 -05:00
ameerj
39bb6851e4 main: Make render window borderless fullscreen toggle on the monitor it resides in
Toggling borderless fullscreen on the separate render window made it fullscreen on the monitor which the main yuzu window resided in.

This change allows the render window to go fullscreen on the monitor it resides in, independent of the main window location.
2021-12-19 02:08:48 -05:00
ameerj
55650c5b75 kernel: Manually destroy the current process during shut down
Avoids a memory leak.
2021-12-19 01:38:25 -05:00
Jan Beich
e57b13ad94 video_core/codecs: re-enable VAAPI/VDPAU on BSDs after 72aa418b0b 2021-12-18 20:57:30 +00:00
Jan Beich
1a9576fdff cmake: enable VA-API on more Unix-like after 0be4e402e2 2021-12-18 20:57:30 +00:00
Morph
8e33cf1c2b Merge pull request #7593 from german77/brrr_test
core/hid: Cancel any vibration after the test
2021-12-18 15:53:15 -05:00
Morph
6fb212784e Merge pull request #7600 from bunnei/fix-kip-loading
core: loader: kip: Minimal changes to fix KIP loading.
2021-12-18 15:50:25 -05:00
bunnei
2030522d86 Merge pull request #7587 from liushuyu/fix-linux-decoding
[Patch v2] externals/ffmpeg: refactor ffmpeg searching and handling in cmake
2021-12-18 02:33:07 -08:00
bunnei
1490b49fa9 Merge pull request #7596 from Tatsh/externals-sdl-config-joycon-fix
externals/CMakeLists: fix detection/init of Switch controllers in SDL 2.0.18
2021-12-18 01:00:17 -08:00
bunnei
212b497d5c Merge pull request #7302 from VPeruS/check-deadlock
[input_common] Fixed thread hang
2021-12-17 23:43:19 -08:00
bunnei
7feac8ba46 core: loader: kip: Minimal changes to fix KIP loading.
- Allows us to boot KIP (kernal apps), useful for testing the kernel.
2021-12-17 23:08:51 -08:00
vonchenplus
4908a07c20 Address format clang 2021-12-18 14:27:07 +08:00
Fernando Sahmkow
6c00151d17 Vulkan: Fix the checks for primitive restart extension. 2021-12-18 07:17:08 +01:00
Fernando S
04b4f3b051 Merge pull request #7399 from ameerj/art-refactor
video_core: Refactoring post A.R.T. merge
2021-12-18 07:09:58 +01:00
vonchenplus
6ebc972c2b Remove spirv handle legacy related code 2021-12-18 14:08:50 +08:00
vonchenplus
94652e122d Remove glsl handle legacy related code 2021-12-18 14:03:40 +08:00
Feng Chen
e49184e606 Merge branch 'yuzu-emu:master' into convert_legacy 2021-12-18 13:57:14 +08:00
Fernando Sahmkow
14d2c77f91 Vulkan: implement Logical Operations. 2021-12-18 06:52:28 +01:00
Fernando Sahmkow
6430fc29a9 Vulkan: Implement VK_EXT_primitive_topology_list_restart 2021-12-18 05:47:48 +01:00
bunnei
77d06d5df0 Merge pull request #7570 from ameerj/favorites-expanded
game_list: Add persistent setting for the favorites row expanded state
2021-12-17 16:09:05 -08:00
bunnei
c73841500a core: hle: Remove global HLE lock.
- This was added early on as a hack to protect against some concurrency issues.
- It's not clear that this serves any purpose anymore, and if it does, individual components should be fixed rather than using a global recursive mutex.
2021-12-17 16:05:51 -08:00
vperus
11f4bf8a9a [input_common] Move variable declaration closer to usage
MSVC supplied with VS2022 generates "warning C4189: 'CALIBRATION_THRESHOLD':
local variable is initialized but not referenced" which is treated as an
error.

Circumvent it by moving constexpr variable directly into body of lambda function.
2021-12-17 20:51:47 +02:00
Andrew Udvare
9b3611eb8d externals/SDL: update SDL to version with Wayland build fix 2021-12-17 09:38:46 -05:00
Andrew Udvare
e610485cd2 externals/CMakeLists: fix detection/init of Switch controllers in SDL 2.0.18
Enable SDL_THREADS and SDL_ATOMIC
Also set SDL_WAYLAND=OFF due to build issue

Closes #7572
2021-12-16 21:49:39 -05:00
Narr the Reg
c82e6dc810 core/hid: Cancel any vibration after the test 2021-12-16 13:35:15 -06:00
bunnei
e242f16986 Merge pull request #7532 from goldenx86/patch-3
Update video core popup
2021-12-15 22:32:27 -08:00
Matías Locatti
333ccf23f8 Suggestions from CrusadingNinja 2021-12-16 02:57:45 -03:00
Matías Locatti
1cdddd17d2 Changed link 2021-12-16 02:40:30 -03:00
bunnei
7cf74abbf5 Merge pull request #7551 from vonchenplus/fix_blit_image_view_mismatching
Fix blit image/view not compatible
2021-12-15 21:39:53 -08:00
Narr the Reg
316f80af87 service/hid: Improve console motion accuracy 2021-12-12 23:26:04 -06:00
ameerj
b64d28492a game_list: Add persistent setting for the favorites row expanded state
Previously, the favorites row was always expanded on launch. This change introduces a persistent setting that allows the favorites row's expanded state to be remembered between launches.
2021-12-12 15:46:42 -05:00
Feng Chen
1598426493 Fix blit image/view not compatible 2021-12-10 12:41:09 +08:00
Matías Locatti
60bdedc7dd main: Update video core popup
Old version had formatting issues, and I want to provide an answer to the most common reason this pops up in the first place, outdated drivers.
2021-12-06 20:35:07 -05:00
ameerj
228a381aed vk_texture_cache: Add ABGR src format check for D24S8 conversions 2021-12-05 15:54:58 -05:00
ameerj
c22c4f5d59 renderer_opengl: Minor refactoring of filter selection 2021-12-05 15:42:45 -05:00
ameerj
218d790bd6 texture_cache: Fix image convert dimensions assertion 2021-12-05 15:42:45 -05:00
ameerj
b8f3e5157b blit_image: Refactor upscale factors usage
The image view itself can be queried to see if it is being rescaled or not, removing the need to pass the upscale/down shift factors from the texture cache.
2021-12-05 15:42:44 -05:00
ameerj
35d94dcb2b vk_texture_cache: Add a function to ImageView to check if src image is rescaled 2021-12-05 15:39:00 -05:00
ameerj
4a13f9eecd blit_image: Refactor ConvertPipeline functions 2021-12-05 15:39:00 -05:00
ameerj
ad99bbf5fe blit_image: Refactor ConvertPipelineEx functions
reduces much of the duplication between the color/depth variants
2021-12-05 15:38:59 -05:00
ameerj
b387a26f30 vk_blit_screen: Minor refactor of filter pipeline selection 2021-12-05 15:35:35 -05:00
ameerj
75c4aec8ab Revert "Merge pull request #7395 from Morph1984/resolve-comments"
This reverts commit d20f91da11, reversing
changes made to 5082712b4e.
2021-12-05 15:35:35 -05:00
vperus
660c6bec22 Revert of b01aa72
Caused worker_thread to be stuck in Stage1Completed state until job's destruction.
2021-11-29 16:37:11 +02:00
vperus
04fa990b0c [input_common] Add completion test for CalibrationConfigurationJob 2021-11-29 16:33:12 +02:00
Feng Chen
4dd85f86a8 Implement convert legacy to generic 2021-11-19 22:53:58 +08:00
105 changed files with 1392 additions and 898 deletions

View File

@@ -229,7 +229,7 @@ elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR YUZU_USE_BUNDLED_BOOST)
include_directories(SYSTEM "${Boost_INCLUDE_DIRS}")
else()
message(STATUS "Boost 1.73.0 or newer not found, falling back to Conan")
list(APPEND CONAN_REQUIRED_LIBS "boost/1.73.0")
list(APPEND CONAN_REQUIRED_LIBS "boost/1.78.0")
endif()
# Attempt to locate any packages that are required and report the missing ones in CONAN_REQUIRED_LIBS
@@ -249,7 +249,7 @@ if(ENABLE_QT)
# Check for system Qt on Linux, fallback to bundled Qt
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
if (NOT YUZU_USE_BUNDLED_QT)
find_package(Qt5 ${QT_VERSION} COMPONENTS Widgets)
find_package(Qt5 ${QT_VERSION} COMPONENTS Widgets DBus)
endif()
if (NOT Qt5_FOUND OR YUZU_USE_BUNDLED_QT)
# Check for dependencies, then enable bundled Qt download
@@ -508,7 +508,7 @@ set(FFmpeg_COMPONENTS
avutil
swscale)
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
if (UNIX AND NOT APPLE)
Include(FindPkgConfig REQUIRED)
pkg_check_modules(LIBVA libva)
endif()

View File

@@ -11,9 +11,15 @@ find_package(Git QUIET PATHS "${GIT_EXECUTABLE}")
# generate git/build information
include(GetGitRevisionDescription)
get_git_head_revision(GIT_REF_SPEC GIT_REV)
git_describe(GIT_DESC --always --long --dirty)
git_branch_name(GIT_BRANCH)
if(NOT GIT_REF_SPEC)
get_git_head_revision(GIT_REF_SPEC GIT_REV)
endif()
if(NOT GIT_DESC)
git_describe(GIT_DESC --always --long --dirty)
endif()
if (NOT GIT_BRANCH)
git_branch_name(GIT_BRANCH)
endif()
get_timestamp(BUILD_DATE)
# Generate cpp with Git revision from template

2
dist/yuzu.desktop vendored
View File

@@ -8,5 +8,5 @@ Icon=yuzu
TryExec=yuzu
Exec=yuzu %f
Categories=Game;Emulator;Qt;
MimeType=application/x-nx-nro;application/x-nx-nso;
MimeType=application/x-nx-nro;application/x-nx-nso;application/x-nx-nsp;application/x-nx-xci;
Keywords=Switch;Nintendo;

15
dist/yuzu.xml vendored
View File

@@ -15,4 +15,19 @@
<glob pattern="*.nso"/>
<magic><match value="NSO" type="string" offset="0"/></magic>
</mime-type>
<mime-type type="application/x-nx-nsp">
<comment>Nintendo Switch Package</comment>
<acronym>NSP</acronym>
<icon name="yuzu"/>
<glob pattern="*.nsp"/>
<magic><match value="PFS" type="string" offset="0"/></magic>
</mime-type>
<mime-type type="application/x-nx-xci">
<comment>Nintendo Switch Card Image</comment>
<acronym>XCI</acronym>
<icon name="yuzu"/>
<glob pattern="*.xci"/>
</mime-type>
</mime-info>

View File

@@ -52,11 +52,12 @@ endif()
# SDL2
if (YUZU_USE_EXTERNAL_SDL2)
if (NOT WIN32)
# Yuzu itself needs: Events Joystick Haptic Sensor Timers Audio
# Yuzu itself needs: Atomic Audio Events Joystick Haptic Sensor Threads Timers
# Since 2.0.18 Atomic+Threads required for HIDAPI/libusb (see https://github.com/libsdl-org/SDL/issues/5095)
# Yuzu-cmd also needs: Video (depends on Loadso/Dlopen)
set(SDL_UNUSED_SUBSYSTEMS
Atomic Render Power Threads
File CPUinfo Filesystem Locale)
CPUinfo File Filesystem
Locale Power Render)
foreach(_SUB ${SDL_UNUSED_SUBSYSTEMS})
string(TOUPPER ${_SUB} _OPT)
option(SDL_${_OPT} "" OFF)

2
externals/SDL vendored

View File

@@ -22,6 +22,11 @@ add_custom_command(OUTPUT scm_rev.cpp
-DTITLE_BAR_FORMAT_RUNNING=${TITLE_BAR_FORMAT_RUNNING}
-DBUILD_TAG=${BUILD_TAG}
-DBUILD_ID=${DISPLAY_VERSION}
-DGIT_REF_SPEC=${GIT_REF_SPEC}
-DGIT_REV=${GIT_REV}
-DGIT_DESC=${GIT_DESC}
-DGIT_BRANCH=${GIT_BRANCH}
-DBUILD_FULLNAME=${BUILD_FULLNAME}
-DGIT_EXECUTABLE=${GIT_EXECUTABLE}
-P ${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake
DEPENDS

View File

@@ -597,6 +597,7 @@ struct Values {
BasicSetting<std::string> program_args{std::string(), "program_args"};
BasicSetting<bool> dump_exefs{false, "dump_exefs"};
BasicSetting<bool> dump_nso{false, "dump_nso"};
BasicSetting<bool> dump_shaders{false, "dump_shaders"};
BasicSetting<bool> enable_fs_access_log{false, "enable_fs_access_log"};
BasicSetting<bool> reporting_services{false, "reporting_services"};
BasicSetting<bool> quest_flag{false, "quest_flag"};

View File

@@ -187,6 +187,8 @@ add_library(core STATIC
hle/kernel/k_event.h
hle/kernel/k_handle_table.cpp
hle/kernel/k_handle_table.h
hle/kernel/k_interrupt_manager.cpp
hle/kernel/k_interrupt_manager.h
hle/kernel/k_light_condition_variable.cpp
hle/kernel/k_light_condition_variable.h
hle/kernel/k_light_lock.cpp
@@ -265,8 +267,6 @@ add_library(core STATIC
hle/kernel/svc_wrap.h
hle/kernel/time_manager.cpp
hle/kernel/time_manager.h
hle/lock.cpp
hle/lock.h
hle/result.h
hle/service/acc/acc.cpp
hle/service/acc/acc.h

View File

@@ -45,26 +45,26 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb
// Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld
if (parameters.allow_pro_controller) {
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
controller->Connect();
controller->Connect(true);
} else if (parameters.allow_dual_joycons) {
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconDual);
controller->Connect();
controller->Connect(true);
} else if (parameters.allow_left_joycon && parameters.allow_right_joycon) {
// Assign left joycons to even player indices and right joycons to odd player indices.
// We do this since Captain Toad Treasure Tracker expects a left joycon for Player 1 and
// a right Joycon for Player 2 in 2 Player Assist mode.
if (index % 2 == 0) {
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconLeft);
controller->Connect();
controller->Connect(true);
} else {
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconRight);
controller->Connect();
controller->Connect(true);
}
} else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld &&
!Settings::values.use_docked_mode.GetValue()) {
// We should *never* reach here under any normal circumstances.
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
controller->Connect();
controller->Connect(true);
} else {
UNREACHABLE_MSG("Unable to add a new controller based on the given parameters!");
}

View File

@@ -161,7 +161,10 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
motion.rotation = emulated.GetGyroscope();
motion.orientation = emulated.GetOrientation();
motion.quaternion = emulated.GetQuaternion();
motion.gyro_bias = emulated.GetGyroBias();
motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
// Find what is this value
motion.verticalization_error = 0.0f;
TriggerOnChange(ConsoleTriggerType::Motion);
}

View File

@@ -50,6 +50,8 @@ struct ConsoleMotion {
Common::Vec3f rotation{};
std::array<Common::Vec3f, 3> orientation{};
Common::Quaternion<f32> quaternion{};
Common::Vec3f gyro_bias{};
f32 verticalization_error{};
bool is_at_rest{};
};

View File

@@ -843,23 +843,18 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v
}
bool EmulatedController::TestVibration(std::size_t device_index) {
if (device_index >= output_devices.size()) {
return false;
}
if (!output_devices[device_index]) {
return false;
}
// Send a slight vibration to test for rumble support
constexpr Common::Input::VibrationStatus status = {
static constexpr VibrationValue test_vibration = {
.low_amplitude = 0.001f,
.low_frequency = 160.0f,
.high_amplitude = 0.001f,
.high_frequency = 320.0f,
.type = Common::Input::VibrationAmplificationType::Linear,
};
return output_devices[device_index]->SetVibration(status) ==
Common::Input::VibrationError::None;
// Send a slight vibration to test for rumble support
SetVibration(device_index, test_vibration);
// Stop any vibration and return the result
return SetVibration(device_index, DEFAULT_VIBRATION_VALUE);
}
void EmulatedController::SetLedPattern() {
@@ -891,8 +886,9 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
}
}
bool EmulatedController::IsControllerSupported() const {
switch (npad_type) {
bool EmulatedController::IsControllerSupported(bool use_temporary_value) const {
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
switch (type) {
case NpadStyleIndex::ProController:
return supported_style_tag.fullkey;
case NpadStyleIndex::Handheld:
@@ -920,9 +916,10 @@ bool EmulatedController::IsControllerSupported() const {
}
}
void EmulatedController::Connect() {
if (!IsControllerSupported()) {
LOG_ERROR(Service_HID, "Controller type {} is not supported", npad_type);
void EmulatedController::Connect(bool use_temporary_value) {
if (!IsControllerSupported(use_temporary_value)) {
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
LOG_ERROR(Service_HID, "Controller type {} is not supported", type);
return;
}
{

View File

@@ -167,8 +167,11 @@ public:
*/
void SetSupportedNpadStyleTag(NpadStyleTag supported_styles);
/// Sets the connected status to true
void Connect();
/**
* Sets the connected status to true
* @param use_temporary_value If true tmp_npad_type will be used
*/
void Connect(bool use_temporary_value = false);
/// Sets the connected status to false
void Disconnect();
@@ -319,9 +322,10 @@ private:
/**
* Checks the current controller type against the supported_style_tag
* @param use_temporary_value If true tmp_npad_type will be used
* @return true if the controller is supported
*/
bool IsControllerSupported() const;
bool IsControllerSupported(bool use_temporary_value = false) const;
/**
* Updates the button status of the controller

View File

@@ -496,6 +496,13 @@ struct VibrationValue {
};
static_assert(sizeof(VibrationValue) == 0x10, "VibrationValue has incorrect size.");
constexpr VibrationValue DEFAULT_VIBRATION_VALUE{
.low_amplitude = 0.0f,
.low_frequency = 160.0f,
.high_amplitude = 0.0f,
.high_frequency = 320.0f,
};
// This is nn::hid::VibrationDeviceInfo
struct VibrationDeviceInfo {
VibrationDeviceType type{};

View File

@@ -23,11 +23,11 @@ void MotionInput::SetAcceleration(const Common::Vec3f& acceleration) {
}
void MotionInput::SetGyroscope(const Common::Vec3f& gyroscope) {
gyro = gyroscope - gyro_drift;
gyro = gyroscope - gyro_bias;
// Auto adjust drift to minimize drift
if (!IsMoving(0.1f)) {
gyro_drift = (gyro_drift * 0.9999f) + (gyroscope * 0.0001f);
gyro_bias = (gyro_bias * 0.9999f) + (gyroscope * 0.0001f);
}
if (gyro.Length2() < gyro_threshold) {
@@ -41,8 +41,8 @@ void MotionInput::SetQuaternion(const Common::Quaternion<f32>& quaternion) {
quat = quaternion;
}
void MotionInput::SetGyroDrift(const Common::Vec3f& drift) {
gyro_drift = drift;
void MotionInput::SetGyroBias(const Common::Vec3f& bias) {
gyro_bias = bias;
}
void MotionInput::SetGyroThreshold(f32 threshold) {
@@ -192,6 +192,10 @@ Common::Vec3f MotionInput::GetGyroscope() const {
return gyro;
}
Common::Vec3f MotionInput::GetGyroBias() const {
return gyro_bias;
}
Common::Quaternion<f32> MotionInput::GetQuaternion() const {
return quat;
}

View File

@@ -24,7 +24,7 @@ public:
void SetAcceleration(const Common::Vec3f& acceleration);
void SetGyroscope(const Common::Vec3f& gyroscope);
void SetQuaternion(const Common::Quaternion<f32>& quaternion);
void SetGyroDrift(const Common::Vec3f& drift);
void SetGyroBias(const Common::Vec3f& bias);
void SetGyroThreshold(f32 threshold);
void EnableReset(bool reset);
@@ -36,6 +36,7 @@ public:
[[nodiscard]] std::array<Common::Vec3f, 3> GetOrientation() const;
[[nodiscard]] Common::Vec3f GetAcceleration() const;
[[nodiscard]] Common::Vec3f GetGyroscope() const;
[[nodiscard]] Common::Vec3f GetGyroBias() const;
[[nodiscard]] Common::Vec3f GetRotations() const;
[[nodiscard]] Common::Quaternion<f32> GetQuaternion() const;
@@ -69,7 +70,7 @@ private:
Common::Vec3f gyro;
// Vector to be substracted from gyro measurements
Common::Vec3f gyro_drift;
Common::Vec3f gyro_bias;
// Minimum gyro amplitude to detect if the device is moving
f32 gyro_threshold = 0.0f;

View File

@@ -9,6 +9,7 @@
#include "core/hle/kernel/global_scheduler_context.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_core.h"
namespace Kernel {
@@ -42,6 +43,11 @@ void GlobalSchedulerContext::PreemptThreads() {
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
const u32 priority = preemption_priorities[core_id];
kernel.Scheduler(core_id).RotateScheduledQueue(core_id, priority);
// Signal an interrupt occurred. For core 3, this is a certainty, as preemption will result
// in the rotator thread being scheduled. For cores 0-2, this is to simulate or system
// interrupts that may have occurred.
kernel.PhysicalCore(core_id).Interrupt();
}
}

View File

@@ -0,0 +1,34 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/kernel/k_interrupt_manager.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
namespace Kernel::KInterruptManager {
void HandleInterrupt(KernelCore& kernel, s32 core_id) {
auto* process = kernel.CurrentProcess();
if (!process) {
return;
}
auto& scheduler = kernel.Scheduler(core_id);
auto& current_thread = *scheduler.GetCurrentThread();
// If the user disable count is set, we may need to pin the current thread.
if (current_thread.GetUserDisableCount() && !process->GetPinnedThread(core_id)) {
KScopedSchedulerLock sl{kernel};
// Pin the current thread.
process->PinCurrentThread(core_id);
// Set the interrupt flag for the thread.
scheduler.GetCurrentThread()->SetInterruptFlag();
}
}
} // namespace Kernel::KInterruptManager

View File

@@ -0,0 +1,17 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
namespace Kernel {
class KernelCore;
namespace KInterruptManager {
void HandleInterrupt(KernelCore& kernel, s32 core_id);
}
} // namespace Kernel

View File

@@ -120,7 +120,7 @@ static_assert(static_cast<u32>(KMemoryState::CodeOut) == 0x00402015);
enum class KMemoryPermission : u8 {
None = 0,
Mask = static_cast<u8>(~None),
All = static_cast<u8>(~None),
Read = 1 << 0,
Write = 1 << 1,

View File

@@ -264,9 +264,9 @@ ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_
ASSERT(heap_last < stack_start || stack_last < heap_start);
ASSERT(heap_last < kmap_start || kmap_last < heap_start);
current_heap_addr = heap_region_start;
heap_capacity = 0;
physical_memory_usage = 0;
current_heap_end = heap_region_start;
max_heap_size = 0;
mapped_physical_memory_size = 0;
memory_pool = pool;
page_table_impl.Resize(address_space_width, PageBits);
@@ -306,7 +306,7 @@ ResultCode KPageTable::MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std:
KMemoryState state{};
KMemoryPermission perm{};
CASCADE_CODE(CheckMemoryState(&state, &perm, nullptr, src_addr, size, KMemoryState::All,
KMemoryState::Normal, KMemoryPermission::Mask,
KMemoryState::Normal, KMemoryPermission::All,
KMemoryPermission::ReadAndWrite, KMemoryAttribute::Mask,
KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
@@ -465,7 +465,7 @@ ResultCode KPageTable::MapPhysicalMemory(VAddr addr, std::size_t size) {
MapPhysicalMemory(page_linked_list, addr, end_addr);
physical_memory_usage += remaining_size;
mapped_physical_memory_size += remaining_size;
const std::size_t num_pages{size / PageSize};
block_manager->Update(addr, num_pages, KMemoryState::Free, KMemoryPermission::None,
@@ -507,7 +507,7 @@ ResultCode KPageTable::UnmapPhysicalMemory(VAddr addr, std::size_t size) {
auto process{system.Kernel().CurrentProcess()};
process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, mapped_size);
physical_memory_usage -= mapped_size;
mapped_physical_memory_size -= mapped_size;
return ResultSuccess;
}
@@ -554,7 +554,7 @@ ResultCode KPageTable::Map(VAddr dst_addr, VAddr src_addr, std::size_t size) {
KMemoryState src_state{};
CASCADE_CODE(CheckMemoryState(
&src_state, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias,
KMemoryState::FlagCanAlias, KMemoryPermission::Mask, KMemoryPermission::ReadAndWrite,
KMemoryState::FlagCanAlias, KMemoryPermission::All, KMemoryPermission::ReadAndWrite,
KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
if (IsRegionMapped(dst_addr, size)) {
@@ -593,7 +593,7 @@ ResultCode KPageTable::Unmap(VAddr dst_addr, VAddr src_addr, std::size_t size) {
KMemoryState src_state{};
CASCADE_CODE(CheckMemoryState(
&src_state, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias,
KMemoryState::FlagCanAlias, KMemoryPermission::Mask, KMemoryPermission::None,
KMemoryState::FlagCanAlias, KMemoryPermission::All, KMemoryPermission::None,
KMemoryAttribute::Mask, KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped));
KMemoryPermission dst_perm{};
@@ -784,7 +784,7 @@ ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemo
CASCADE_CODE(CheckMemoryState(
&state, nullptr, &attribute, addr, size,
KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted,
KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, KMemoryPermission::Mask,
KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, KMemoryPermission::All,
KMemoryPermission::ReadAndWrite, KMemoryAttribute::Mask, KMemoryAttribute::None,
KMemoryAttribute::IpcAndDeviceMapped));
@@ -806,6 +806,33 @@ ResultCode KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) {
KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped));
block_manager->Update(addr, size / PageSize, state, KMemoryPermission::ReadAndWrite);
return ResultSuccess;
}
ResultCode KPageTable::SetMemoryPermission(VAddr addr, std::size_t size,
Svc::MemoryPermission svc_perm) {
const size_t num_pages = size / PageSize;
// Lock the table.
std::lock_guard lock{page_table_lock};
// Verify we can change the memory permission.
KMemoryState old_state;
KMemoryPermission old_perm;
R_TRY(this->CheckMemoryState(
std::addressof(old_state), std::addressof(old_perm), nullptr, addr, size,
KMemoryState::FlagCanReprotect, KMemoryState::FlagCanReprotect, KMemoryPermission::None,
KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None));
// Determine new perm.
const KMemoryPermission new_perm = ConvertToKMemoryPermission(svc_perm);
R_SUCCEED_IF(old_perm == new_perm);
// Perform mapping operation.
R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions));
// Update the blocks.
block_manager->Update(addr, num_pages, old_state, new_perm, KMemoryAttribute::None);
return ResultSuccess;
}
@@ -832,61 +859,125 @@ ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, KMemoryA
return ResultSuccess;
}
ResultCode KPageTable::SetHeapCapacity(std::size_t new_heap_capacity) {
ResultCode KPageTable::SetMaxHeapSize(std::size_t size) {
// Lock the table.
std::lock_guard lock{page_table_lock};
heap_capacity = new_heap_capacity;
// Only process page tables are allowed to set heap size.
ASSERT(!this->IsKernel());
max_heap_size = size;
return ResultSuccess;
}
ResultVal<VAddr> KPageTable::SetHeapSize(std::size_t size) {
ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) {
// Try to perform a reduction in heap, instead of an extension.
VAddr cur_address{};
std::size_t allocation_size{};
{
// Lock the table.
std::lock_guard lk(page_table_lock);
if (size > heap_region_end - heap_region_start) {
return ResultOutOfMemory;
// Validate that setting heap size is possible at all.
R_UNLESS(!is_kernel, ResultOutOfMemory);
R_UNLESS(size <= static_cast<std::size_t>(heap_region_end - heap_region_start),
ResultOutOfMemory);
R_UNLESS(size <= max_heap_size, ResultOutOfMemory);
if (size < GetHeapSize()) {
// The size being requested is less than the current size, so we need to free the end of
// the heap.
// Validate memory state.
std::size_t num_allocator_blocks;
R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks),
heap_region_start + size, GetHeapSize() - size,
KMemoryState::All, KMemoryState::Normal,
KMemoryPermission::All, KMemoryPermission::ReadAndWrite,
KMemoryAttribute::All, KMemoryAttribute::None));
// Unmap the end of the heap.
const auto num_pages = (GetHeapSize() - size) / PageSize;
R_TRY(Operate(heap_region_start + size, num_pages, KMemoryPermission::None,
OperationType::Unmap));
// Release the memory from the resource limit.
system.Kernel().CurrentProcess()->GetResourceLimit()->Release(
LimitableResource::PhysicalMemory, num_pages * PageSize);
// Apply the memory block update.
block_manager->Update(heap_region_start + size, num_pages, KMemoryState::Free,
KMemoryPermission::None, KMemoryAttribute::None);
// Update the current heap end.
current_heap_end = heap_region_start + size;
// Set the output.
*out = heap_region_start;
return ResultSuccess;
} else if (size == GetHeapSize()) {
// The size requested is exactly the current size.
*out = heap_region_start;
return ResultSuccess;
} else {
// We have to allocate memory. Determine how much to allocate and where while the table
// is locked.
cur_address = current_heap_end;
allocation_size = size - GetHeapSize();
}
}
const u64 previous_heap_size{GetHeapSize()};
// Reserve memory for the heap extension.
KScopedResourceReservation memory_reservation(
system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory,
allocation_size);
R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
UNIMPLEMENTED_IF_MSG(previous_heap_size > size, "Heap shrink is unimplemented");
// Allocate pages for the heap extension.
KPageLinkedList page_linked_list;
R_TRY(system.Kernel().MemoryManager().Allocate(page_linked_list, allocation_size / PageSize,
memory_pool));
// Increase the heap size
// Map the pages.
{
std::lock_guard lock{page_table_lock};
// Lock the table.
std::lock_guard lk(page_table_lock);
const u64 delta{size - previous_heap_size};
// Ensure that the heap hasn't changed since we began executing.
ASSERT(cur_address == current_heap_end);
// Reserve memory for the heap extension.
KScopedResourceReservation memory_reservation(
system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory,
delta);
// Check the memory state.
std::size_t num_allocator_blocks{};
R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), current_heap_end,
allocation_size, KMemoryState::All, KMemoryState::Free,
KMemoryPermission::None, KMemoryPermission::None,
KMemoryAttribute::None, KMemoryAttribute::None));
if (!memory_reservation.Succeeded()) {
LOG_ERROR(Kernel, "Could not reserve heap extension of size {:X} bytes", delta);
return ResultLimitReached;
// Map the pages.
const auto num_pages = allocation_size / PageSize;
R_TRY(Operate(current_heap_end, num_pages, page_linked_list, OperationType::MapGroup));
// Clear all the newly allocated pages.
for (std::size_t cur_page = 0; cur_page < num_pages; ++cur_page) {
std::memset(system.Memory().GetPointer(current_heap_end + (cur_page * PageSize)), 0,
PageSize);
}
KPageLinkedList page_linked_list;
const std::size_t num_pages{delta / PageSize};
CASCADE_CODE(
system.Kernel().MemoryManager().Allocate(page_linked_list, num_pages, memory_pool));
if (IsRegionMapped(current_heap_addr, delta)) {
return ResultInvalidCurrentMemory;
}
CASCADE_CODE(
Operate(current_heap_addr, num_pages, page_linked_list, OperationType::MapGroup));
// Succeeded in allocation, commit the resource reservation
// We succeeded, so commit our memory reservation.
memory_reservation.Commit();
block_manager->Update(current_heap_addr, num_pages, KMemoryState::Normal,
KMemoryPermission::ReadAndWrite);
// Apply the memory block update.
block_manager->Update(current_heap_end, num_pages, KMemoryState::Normal,
KMemoryPermission::ReadAndWrite, KMemoryAttribute::None);
current_heap_addr = heap_region_start + size;
// Update the current heap end.
current_heap_end = heap_region_start + size;
// Set the output.
*out = heap_region_start;
return ResultSuccess;
}
return heap_region_start;
}
ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align,
@@ -978,7 +1069,7 @@ ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
if (const ResultCode result{CheckMemoryState(
nullptr, &old_perm, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
KMemoryState::FlagCanCodeMemory, KMemoryPermission::Mask,
KMemoryState::FlagCanCodeMemory, KMemoryPermission::All,
KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)};
result.IsError()) {
return result;
@@ -1031,9 +1122,8 @@ ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
bool KPageTable::IsRegionMapped(VAddr address, u64 size) {
return CheckMemoryState(address, size, KMemoryState::All, KMemoryState::Free,
KMemoryPermission::Mask, KMemoryPermission::None,
KMemoryAttribute::Mask, KMemoryAttribute::None,
KMemoryAttribute::IpcAndDeviceMapped)
KMemoryPermission::All, KMemoryPermission::None, KMemoryAttribute::Mask,
KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)
.IsError();
}

View File

@@ -47,10 +47,11 @@ public:
KMemoryInfo QueryInfo(VAddr addr);
ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm);
ResultCode ResetTransferMemory(VAddr addr, std::size_t size);
ResultCode SetMemoryPermission(VAddr addr, std::size_t size, Svc::MemoryPermission perm);
ResultCode SetMemoryAttribute(VAddr addr, std::size_t size, KMemoryAttribute mask,
KMemoryAttribute value);
ResultCode SetHeapCapacity(std::size_t new_heap_capacity);
ResultVal<VAddr> SetHeapSize(std::size_t size);
ResultCode SetMaxHeapSize(std::size_t size);
ResultCode SetHeapSize(VAddr* out, std::size_t size);
ResultVal<VAddr> AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align,
bool is_map_only, VAddr region_start,
std::size_t region_num_pages, KMemoryState state,
@@ -182,14 +183,15 @@ public:
constexpr VAddr GetAliasCodeRegionSize() const {
return alias_code_region_end - alias_code_region_start;
}
size_t GetNormalMemorySize() {
std::lock_guard lk(page_table_lock);
return GetHeapSize() + mapped_physical_memory_size;
}
constexpr std::size_t GetAddressSpaceWidth() const {
return address_space_width;
}
constexpr std::size_t GetHeapSize() {
return current_heap_addr - heap_region_start;
}
constexpr std::size_t GetTotalHeapSize() {
return GetHeapSize() + physical_memory_usage;
constexpr std::size_t GetHeapSize() const {
return current_heap_end - heap_region_start;
}
constexpr bool IsInsideAddressSpace(VAddr address, std::size_t size) const {
return address_space_start <= address && address + size - 1 <= address_space_end - 1;
@@ -269,10 +271,8 @@ private:
VAddr code_region_end{};
VAddr alias_code_region_start{};
VAddr alias_code_region_end{};
VAddr current_heap_addr{};
std::size_t heap_capacity{};
std::size_t physical_memory_usage{};
std::size_t mapped_physical_memory_size{};
std::size_t max_heap_size{};
std::size_t max_physical_memory_size{};
std::size_t address_space_width{};

View File

@@ -28,7 +28,6 @@
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/lock.h"
#include "core/memory.h"
namespace Kernel {
@@ -173,7 +172,7 @@ void KProcess::DecrementThreadCount() {
u64 KProcess::GetTotalPhysicalMemoryAvailable() const {
const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemory) +
page_table->GetTotalHeapSize() + GetSystemResourceSize() + image_size +
page_table->GetNormalMemorySize() + GetSystemResourceSize() + image_size +
main_thread_stack_size};
if (const auto pool_size = kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application);
capacity != pool_size) {
@@ -190,7 +189,7 @@ u64 KProcess::GetTotalPhysicalMemoryAvailableWithoutSystemResource() const {
}
u64 KProcess::GetTotalPhysicalMemoryUsed() const {
return image_size + main_thread_stack_size + page_table->GetTotalHeapSize() +
return image_size + main_thread_stack_size + page_table->GetNormalMemorySize() +
GetSystemResourceSize();
}
@@ -221,30 +220,28 @@ bool KProcess::ReleaseUserException(KThread* thread) {
}
}
void KProcess::PinCurrentThread() {
void KProcess::PinCurrentThread(s32 core_id) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// Get the current thread.
const s32 core_id = GetCurrentCoreId(kernel);
KThread* cur_thread = GetCurrentThreadPointer(kernel);
KThread* cur_thread = kernel.Scheduler(static_cast<std::size_t>(core_id)).GetCurrentThread();
// If the thread isn't terminated, pin it.
if (!cur_thread->IsTerminationRequested()) {
// Pin it.
PinThread(core_id, cur_thread);
cur_thread->Pin();
cur_thread->Pin(core_id);
// An update is needed.
KScheduler::SetSchedulerUpdateNeeded(kernel);
}
}
void KProcess::UnpinCurrentThread() {
void KProcess::UnpinCurrentThread(s32 core_id) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// Get the current thread.
const s32 core_id = GetCurrentCoreId(kernel);
KThread* cur_thread = GetCurrentThreadPointer(kernel);
KThread* cur_thread = kernel.Scheduler(static_cast<std::size_t>(core_id)).GetCurrentThread();
// Unpin it.
cur_thread->Unpin();
@@ -411,8 +408,8 @@ void KProcess::Run(s32 main_thread_priority, u64 stack_size) {
resource_limit->Reserve(LimitableResource::Threads, 1);
resource_limit->Reserve(LimitableResource::PhysicalMemory, main_thread_stack_size);
const std::size_t heap_capacity{memory_usage_capacity - main_thread_stack_size - image_size};
ASSERT(!page_table->SetHeapCapacity(heap_capacity).IsError());
const std::size_t heap_capacity{memory_usage_capacity - (main_thread_stack_size + image_size)};
ASSERT(!page_table->SetMaxHeapSize(heap_capacity).IsError());
ChangeStatus(ProcessStatus::Running);
@@ -543,7 +540,6 @@ void KProcess::FreeTLSRegion(VAddr tls_address) {
}
void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) {
std::lock_guard lock{HLE::g_hle_lock};
const auto ReprotectSegment = [&](const CodeSet::Segment& segment,
KMemoryPermission permission) {
page_table->SetProcessMemoryPermission(segment.addr + base_addr, segment.size, permission);

View File

@@ -345,8 +345,8 @@ public:
bool IsSignaled() const override;
void PinCurrentThread();
void UnpinCurrentThread();
void PinCurrentThread(s32 core_id);
void UnpinCurrentThread(s32 core_id);
void UnpinThread(KThread* thread);
KLightLock& GetStateLock() {

View File

@@ -15,6 +15,7 @@
#include "core/core.h"
#include "core/core_timing.h"
#include "core/cpu_manager.h"
#include "core/hle/kernel/k_interrupt_manager.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
@@ -53,6 +54,13 @@ void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedul
}
cores_pending_reschedule &= ~(1ULL << core);
}
for (std::size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; ++core_id) {
if (kernel.PhysicalCore(core_id).IsInterrupted()) {
KInterruptManager::HandleInterrupt(kernel, static_cast<s32>(core_id));
}
}
if (must_context_switch) {
auto core_scheduler = kernel.CurrentScheduler();
kernel.ExitSVCProfile();

View File

@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <algorithm>
#include <atomic>
#include <cinttypes>
#include <optional>
#include <vector>
@@ -26,12 +27,14 @@
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_system_control.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/time_manager.h"
#include "core/hle/result.h"
#include "core/memory.h"
#ifdef ARCHITECTURE_x86_64
#include "core/arm/dynarmic/arm_dynarmic_32.h"
@@ -50,6 +53,7 @@ static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context,
VAddr entry_point, u64 arg) {
context = {};
context.cpu_registers[0] = arg;
context.cpu_registers[18] = Kernel::KSystemControl::GenerateRandomU64() | 1;
context.pc = entry_point;
context.sp = stack_top;
// TODO(merry): Perform a hardware test to determine the below value.
@@ -61,6 +65,13 @@ namespace Kernel {
namespace {
struct ThreadLocalRegion {
static constexpr std::size_t MessageBufferSize = 0x100;
std::array<u32, MessageBufferSize / sizeof(u32)> message_buffer;
std::atomic_uint16_t disable_count;
std::atomic_uint16_t interrupt_flag;
};
class ThreadQueueImplForKThreadSleep final : public KThreadQueueWithoutEndWait {
public:
explicit ThreadQueueImplForKThreadSleep(KernelCore& kernel_)
@@ -344,7 +355,7 @@ void KThread::StartTermination() {
if (parent != nullptr) {
parent->ReleaseUserException(this);
if (parent->GetPinnedThread(GetCurrentCoreId(kernel)) == this) {
parent->UnpinCurrentThread();
parent->UnpinCurrentThread(core_id);
}
}
@@ -370,7 +381,7 @@ void KThread::StartTermination() {
this->Close();
}
void KThread::Pin() {
void KThread::Pin(s32 current_core) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// Set ourselves as pinned.
@@ -387,7 +398,6 @@ void KThread::Pin() {
// Bind ourselves to this core.
const s32 active_core = GetActiveCore();
const s32 current_core = GetCurrentCoreId(kernel);
SetActiveCore(current_core);
physical_ideal_core_id = current_core;
@@ -480,6 +490,36 @@ void KThread::Unpin() {
}
}
u16 KThread::GetUserDisableCount() const {
if (!IsUserThread()) {
// We only emulate TLS for user threads
return {};
}
auto& memory = kernel.System().Memory();
return memory.Read16(tls_address + offsetof(ThreadLocalRegion, disable_count));
}
void KThread::SetInterruptFlag() {
if (!IsUserThread()) {
// We only emulate TLS for user threads
return;
}
auto& memory = kernel.System().Memory();
memory.Write16(tls_address + offsetof(ThreadLocalRegion, interrupt_flag), 1);
}
void KThread::ClearInterruptFlag() {
if (!IsUserThread()) {
// We only emulate TLS for user threads
return;
}
auto& memory = kernel.System().Memory();
memory.Write16(tls_address + offsetof(ThreadLocalRegion, interrupt_flag), 0);
}
ResultCode KThread::GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask) {
KScopedSchedulerLock sl{kernel};

View File

@@ -307,6 +307,10 @@ public:
return parent != nullptr;
}
u16 GetUserDisableCount() const;
void SetInterruptFlag();
void ClearInterruptFlag();
[[nodiscard]] KThread* GetLockOwner() const {
return lock_owner;
}
@@ -490,7 +494,7 @@ public:
this->GetStackParameters().disable_count--;
}
void Pin();
void Pin(s32 current_core);
void Unpin();

View File

@@ -182,7 +182,10 @@ struct KernelCore::Impl {
// Shutdown all processes.
if (current_process) {
current_process->Finalize();
current_process->Close();
// current_process->Close();
// TODO: The current process should be destroyed based on accurate ref counting after
// calling Close(). Adding a manual Destroy() call instead to avoid a memory leak.
current_process->Destroy();
current_process = nullptr;
}

View File

@@ -41,7 +41,6 @@
#include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/svc_types.h"
#include "core/hle/kernel/svc_wrap.h"
#include "core/hle/lock.h"
#include "core/hle/result.h"
#include "core/memory.h"
#include "core/reporter.h"
@@ -136,25 +135,15 @@ enum class ResourceLimitValueType {
} // Anonymous namespace
/// Set the process heap to a given Size. It can both extend and shrink the heap.
static ResultCode SetHeapSize(Core::System& system, VAddr* heap_addr, u64 heap_size) {
std::lock_guard lock{HLE::g_hle_lock};
LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", heap_size);
static ResultCode SetHeapSize(Core::System& system, VAddr* out_address, u64 size) {
LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", size);
// Size must be a multiple of 0x200000 (2MB) and be equal to or less than 8GB.
if ((heap_size % 0x200000) != 0) {
LOG_ERROR(Kernel_SVC, "The heap size is not a multiple of 2MB, heap_size=0x{:016X}",
heap_size);
return ResultInvalidSize;
}
// Validate size.
R_UNLESS(Common::IsAligned(size, HeapSizeAlignment), ResultInvalidSize);
R_UNLESS(size < MainMemorySizeMax, ResultInvalidSize);
if (heap_size >= 0x200000000) {
LOG_ERROR(Kernel_SVC, "The heap size is not less than 8GB, heap_size=0x{:016X}", heap_size);
return ResultInvalidSize;
}
auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
CASCADE_RESULT(*heap_addr, page_table.SetHeapSize(heap_size));
// Set the heap size.
R_TRY(system.Kernel().CurrentProcess()->PageTable().SetHeapSize(out_address, size));
return ResultSuccess;
}
@@ -166,9 +155,38 @@ static ResultCode SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_s
return result;
}
constexpr bool IsValidSetMemoryPermission(MemoryPermission perm) {
switch (perm) {
case MemoryPermission::None:
case MemoryPermission::Read:
case MemoryPermission::ReadWrite:
return true;
default:
return false;
}
}
static ResultCode SetMemoryPermission(Core::System& system, VAddr address, u64 size,
MemoryPermission perm) {
// Validate address / size.
R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
R_UNLESS(size > 0, ResultInvalidSize);
R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
// Validate the permission.
R_UNLESS(IsValidSetMemoryPermission(perm), ResultInvalidNewMemoryPermission);
// Validate that the region is in range for the current process.
auto& page_table = system.Kernel().CurrentProcess()->PageTable();
R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
// Set the memory attribute.
return page_table.SetMemoryPermission(address, size, perm);
}
static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask,
u32 attribute) {
std::lock_guard lock{HLE::g_hle_lock};
LOG_DEBUG(Kernel_SVC,
"called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address,
size, mask, attribute);
@@ -212,7 +230,6 @@ static ResultCode SetMemoryAttribute32(Core::System& system, u32 address, u32 si
/// Maps a memory range into a different range.
static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
std::lock_guard lock{HLE::g_hle_lock};
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
src_addr, size);
@@ -232,7 +249,6 @@ static ResultCode MapMemory32(Core::System& system, u32 dst_addr, u32 src_addr,
/// Unmaps a region that was previously mapped with svcMapMemory
static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
std::lock_guard lock{HLE::g_hle_lock};
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
src_addr, size);
@@ -642,7 +658,6 @@ static void OutputDebugString(Core::System& system, VAddr address, u64 len) {
/// Gets system/memory information for the current process
static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle handle,
u64 info_sub_id) {
std::lock_guard lock{HLE::g_hle_lock};
LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id,
info_sub_id, handle);
@@ -886,22 +901,17 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle
return ResultSuccess;
}
case GetInfoType::IdleTickCount: {
if (handle == 0) {
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}",
static_cast<Handle>(handle));
return ResultInvalidHandle;
}
// Verify the input handle is invalid.
R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
if (info_sub_id != 0xFFFFFFFFFFFFFFFF &&
info_sub_id != system.Kernel().CurrentPhysicalCoreIndex()) {
LOG_ERROR(Kernel_SVC, "Core is not the current core, got {}", info_sub_id);
return ResultInvalidCombination;
}
// Verify the requested core is valid.
const bool core_valid =
(info_sub_id == static_cast<u64>(-1ULL)) ||
(info_sub_id == static_cast<u64>(system.Kernel().CurrentPhysicalCoreIndex()));
R_UNLESS(core_valid, ResultInvalidCombination);
const auto& scheduler = *system.Kernel().CurrentScheduler();
const auto* const idle_thread = scheduler.GetIdleThread();
*result = idle_thread->GetCpuTime();
// Get the idle tick count.
*result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime();
return ResultSuccess;
}
default:
@@ -924,7 +934,6 @@ static ResultCode GetInfo32(Core::System& system, u32* result_low, u32* result_h
/// Maps memory at a desired address
static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
std::lock_guard lock{HLE::g_hle_lock};
LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
if (!Common::Is4KBAligned(addr)) {
@@ -978,7 +987,6 @@ static ResultCode MapPhysicalMemory32(Core::System& system, u32 addr, u32 size)
/// Unmaps memory previously mapped via MapPhysicalMemory
static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
std::lock_guard lock{HLE::g_hle_lock};
LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
if (!Common::Is4KBAligned(addr)) {
@@ -1520,7 +1528,6 @@ static ResultCode ControlCodeMemory(Core::System& system, Handle code_memory_han
static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address,
VAddr page_info_address, Handle process_handle,
VAddr address) {
std::lock_guard lock{HLE::g_hle_lock};
LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address);
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
@@ -2020,6 +2027,25 @@ static ResultCode SignalToAddress(Core::System& system, VAddr address, Svc::Sign
count);
}
static void SynchronizePreemptionState(Core::System& system) {
auto& kernel = system.Kernel();
// Lock the scheduler.
KScopedSchedulerLock sl{kernel};
// If the current thread is pinned, unpin it.
KProcess* cur_process = system.Kernel().CurrentProcess();
const auto core_id = GetCurrentCoreId(kernel);
if (cur_process->GetPinnedThread(core_id) == GetCurrentThreadPointer(kernel)) {
// Clear the current thread's interrupt flag.
GetCurrentThread(kernel).ClearInterruptFlag();
// Unpin the current thread.
cur_process->UnpinCurrentThread(core_id);
}
}
static ResultCode SignalToAddress32(Core::System& system, u32 address, Svc::SignalType signal_type,
s32 value, s32 count) {
return SignalToAddress(system, address, signal_type, value, count);
@@ -2738,7 +2764,7 @@ static const FunctionDef SVC_Table_32[] = {
static const FunctionDef SVC_Table_64[] = {
{0x00, nullptr, "Unknown"},
{0x01, SvcWrap64<SetHeapSize>, "SetHeapSize"},
{0x02, nullptr, "SetMemoryPermission"},
{0x02, SvcWrap64<SetMemoryPermission>, "SetMemoryPermission"},
{0x03, SvcWrap64<SetMemoryAttribute>, "SetMemoryAttribute"},
{0x04, SvcWrap64<MapMemory>, "MapMemory"},
{0x05, SvcWrap64<UnmapMemory>, "UnmapMemory"},
@@ -2790,7 +2816,7 @@ static const FunctionDef SVC_Table_64[] = {
{0x33, SvcWrap64<GetThreadContext>, "GetThreadContext"},
{0x34, SvcWrap64<WaitForAddress>, "WaitForAddress"},
{0x35, SvcWrap64<SignalToAddress>, "SignalToAddress"},
{0x36, nullptr, "SynchronizePreemptionState"},
{0x36, SvcWrap64<SynchronizePreemptionState>, "SynchronizePreemptionState"},
{0x37, nullptr, "Unknown"},
{0x38, nullptr, "Unknown"},
{0x39, nullptr, "Unknown"},

View File

@@ -5,6 +5,7 @@
#pragma once
#include "common/common_types.h"
#include "common/literals.h"
namespace Kernel {
using Handle = u32;
@@ -12,9 +13,13 @@ using Handle = u32;
namespace Kernel::Svc {
using namespace Common::Literals;
constexpr s32 ArgumentHandleCountMax = 0x40;
constexpr u32 HandleWaitMask{1u << 30};
constexpr inline std::size_t HeapSizeAlignment = 2_MiB;
constexpr inline Handle InvalidHandle = Handle(0);
enum PseudoHandle : Handle {

View File

@@ -249,6 +249,14 @@ void SvcWrap64(Core::System& system) {
func(system, Param(system, 0), Param(system, 1), static_cast<u32>(Param(system, 2))).raw);
}
// Used by SetMemoryPermission
template <ResultCode func(Core::System&, u64, u64, Svc::MemoryPermission)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
static_cast<Svc::MemoryPermission>(Param(system, 2)))
.raw);
}
// Used by MapSharedMemory
template <ResultCode func(Core::System&, Handle, u64, u64, Svc::MemoryPermission)>
void SvcWrap64(Core::System& system) {

View File

@@ -1,9 +0,0 @@
// Copyright 2017 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <core/hle/lock.h>
namespace HLE {
std::recursive_mutex g_hle_lock;
}

View File

@@ -1,18 +0,0 @@
// Copyright 2017 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <mutex>
namespace HLE {
/*
* Synchronizes access to the internal HLE kernel structures, it is acquired when a guest
* application thread performs a syscall. It should be acquired by any host threads that read or
* modify the HLE kernel state. Note: Any operation that directly or indirectly reads from or writes
* to the emulated memory is not protected by this mutex, and should be avoided in any threads other
* than the CPU thread.
*/
extern std::recursive_mutex g_hle_lock;
} // namespace HLE

View File

@@ -6,7 +6,6 @@
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/lock.h"
#include "core/hle/service/bcat/backend/backend.h"
namespace Service::BCAT {
@@ -29,10 +28,6 @@ DeliveryCacheProgressImpl& ProgressServiceBackend::GetImpl() {
return impl;
}
void ProgressServiceBackend::SetNeedHLELock(bool need) {
need_hle_lock = need;
}
void ProgressServiceBackend::SetTotalSize(u64 size) {
impl.total_bytes = size;
SignalUpdate();
@@ -88,12 +83,7 @@ void ProgressServiceBackend::FinishDownload(ResultCode result) {
}
void ProgressServiceBackend::SignalUpdate() {
if (need_hle_lock) {
std::lock_guard lock(HLE::g_hle_lock);
update_event->GetWritableEvent().Signal();
} else {
update_event->GetWritableEvent().Signal();
}
update_event->GetWritableEvent().Signal();
}
Backend::Backend(DirectoryGetter getter) : dir_getter(std::move(getter)) {}

View File

@@ -71,10 +71,6 @@ class ProgressServiceBackend {
public:
~ProgressServiceBackend();
// Clients should call this with true if any of the functions are going to be called from a
// non-HLE thread and this class need to lock the hle mutex. (default is false)
void SetNeedHLELock(bool need);
// Sets the number of bytes total in the entire download.
void SetTotalSize(u64 size);
@@ -109,7 +105,6 @@ private:
DeliveryCacheProgressImpl impl{};
Kernel::KEvent* update_event;
bool need_hle_lock = false;
};
// A class representing an abstract backend for BCAT functionality.

View File

@@ -33,15 +33,14 @@ void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_ti
const auto& last_entry = seven_sixaxis_lifo.ReadCurrentEntry().state;
next_seven_sixaxis_state.sampling_number = last_entry.sampling_number + 1;
// Try to read sixaxis sensor states
const auto motion_status = console->GetMotion();
last_global_timestamp = core_timing.GetGlobalTimeNs().count();
console_six_axis.is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest;
// This value increments every time the switch goes to sleep
next_seven_sixaxis_state.unknown = 1;
next_seven_sixaxis_state.timestamp = last_global_timestamp - last_saved_timestamp;
next_seven_sixaxis_state.accel = motion_status.accel;
// Zero gyro values as they just mess up with the camera
// Note: Probably a correct sensivity setting must be set
next_seven_sixaxis_state.gyro = {};
next_seven_sixaxis_state.gyro = motion_status.gyro;
next_seven_sixaxis_state.quaternion = {
{
motion_status.quaternion.xyz.y,
@@ -52,9 +51,9 @@ void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_ti
};
console_six_axis.sampling_number++;
// TODO(German77): Find the purpose of those values
console_six_axis.verticalization_error = 0.0f;
console_six_axis.gyro_bias = {0.0f, 0.0f, 0.0f};
console_six_axis.is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest;
console_six_axis.verticalization_error = motion_status.verticalization_error;
console_six_axis.gyro_bias = motion_status.gyro_bias;
// Update console six axis shared memory
std::memcpy(data + SHARED_MEMORY_OFFSET, &console_six_axis, sizeof(console_six_axis));
@@ -69,7 +68,6 @@ void Controller_ConsoleSixAxis::SetTransferMemoryPointer(u8* t_mem) {
}
void Controller_ConsoleSixAxis::ResetTimestamp() {
seven_sixaxis_lifo.buffer_count = 0;
seven_sixaxis_lifo.buffer_tail = 0;
last_saved_timestamp = last_global_timestamp;
}
} // namespace Service::HID

View File

@@ -39,8 +39,9 @@ public:
private:
struct SevenSixAxisState {
INSERT_PADDING_WORDS(4); // unused
s64 sampling_number{};
INSERT_PADDING_WORDS(2); // unused
u64 timestamp{};
u64 sampling_number{};
u64 unknown{};
Common::Vec3f accel{};
Common::Vec3f gyro{};
@@ -52,9 +53,10 @@ private:
struct ConsoleSharedMemory {
u64 sampling_number{};
bool is_seven_six_axis_sensor_at_rest{};
INSERT_PADDING_BYTES(4); // padding
INSERT_PADDING_BYTES(3); // padding
f32 verticalization_error{};
Common::Vec3f gyro_bias{};
INSERT_PADDING_BYTES(4); // padding
};
static_assert(sizeof(ConsoleSharedMemory) == 0x20, "ConsoleSharedMemory is an invalid size");
@@ -64,6 +66,8 @@ private:
Core::HID::EmulatedConsole* console;
u8* transfer_memory = nullptr;
bool is_transfer_memory_set = false;
u64 last_saved_timestamp{};
u64 last_global_timestamp{};
ConsoleSharedMemory console_six_axis{};
SevenSixAxisState next_seven_sixaxis_state{};
};

View File

@@ -66,9 +66,9 @@ Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_,
auto& controller = controller_data[i];
controller.device = hid_core.GetEmulatedControllerByIndex(i);
controller.vibration[Core::HID::EmulatedDeviceIndex::LeftIndex].latest_vibration_value =
DEFAULT_VIBRATION_VALUE;
Core::HID::DEFAULT_VIBRATION_VALUE;
controller.vibration[Core::HID::EmulatedDeviceIndex::RightIndex].latest_vibration_value =
DEFAULT_VIBRATION_VALUE;
Core::HID::DEFAULT_VIBRATION_VALUE;
Core::HID::ControllerUpdateCallback engine_callback{
.on_change = [this,
i](Core::HID::ControllerTriggerType type) { ControllerUpdate(type, i); },
@@ -781,7 +781,8 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
Core::HID::VibrationValue vibration{0.0f, 160.0f, 0.0f, 320.0f};
controller.device->SetVibration(device_index, vibration);
// Then reset the vibration value to its default value.
controller.vibration[device_index].latest_vibration_value = DEFAULT_VIBRATION_VALUE;
controller.vibration[device_index].latest_vibration_value =
Core::HID::DEFAULT_VIBRATION_VALUE;
}
return false;

View File

@@ -90,13 +90,6 @@ public:
Default = 3,
};
static constexpr Core::HID::VibrationValue DEFAULT_VIBRATION_VALUE{
.low_amplitude = 0.0f,
.low_frequency = 160.0f,
.high_amplitude = 0.0f,
.high_frequency = 320.0f,
};
void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set);
Core::HID::NpadStyleTag GetSupportedStyleSet() const;

View File

@@ -1404,7 +1404,7 @@ void Hid::SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
.high_frequency = 0.0f,
};
default:
return Controller_NPad::DEFAULT_VIBRATION_VALUE;
return Core::HID::DEFAULT_VIBRATION_VALUE;
}
}();

View File

@@ -9,7 +9,6 @@
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/lock.h"
#include "core/hle/service/nfp/nfp.h"
#include "core/hle/service/nfp/nfp_user.h"
@@ -337,7 +336,6 @@ void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
}
bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) {
std::lock_guard lock{HLE::g_hle_lock};
if (buffer.size() < sizeof(AmiiboFile)) {
return false;
}

View File

@@ -71,7 +71,6 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::KProcess& process,
kip->GetTitleID(), 0xFFFFFFFFFFFFFFFF, 0x1FE00000,
kip->GetKernelCapabilities());
const VAddr base_address = process.PageTable().GetCodeRegionStart();
Kernel::CodeSet codeset;
Kernel::PhysicalMemory program_image;
@@ -91,7 +90,14 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::KProcess& process,
program_image.resize(PageAlignSize(kip->GetBSSOffset()) + kip->GetBSSSize());
codeset.DataSegment().size += kip->GetBSSSize();
// Setup the process code layout
if (process.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), program_image.size())
.IsError()) {
return {ResultStatus::ErrorNotInitialized, {}};
}
codeset.memory = std::move(program_image);
const VAddr base_address = process.PageTable().GetCodeRegionStart();
process.LoadModule(std::move(codeset), base_address);
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", kip->GetName(), base_address);

View File

@@ -536,42 +536,46 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
std::function<void(u16, u16, u16, u16)> data_callback) {
std::thread([=, this] {
u16 min_x{UINT16_MAX};
u16 min_y{UINT16_MAX};
u16 max_x{};
u16 max_y{};
Status current_status{Status::Initialized};
SocketCallback callback{
[](Response::Version) {}, [](Response::PortInfo) {},
[&](Response::PadData data) {
static constexpr u16 CALIBRATION_THRESHOLD = 100;
static constexpr u16 MAX_VALUE = UINT16_MAX;
SocketCallback callback{[](Response::Version) {}, [](Response::PortInfo) {},
[&](Response::PadData data) {
constexpr u16 CALIBRATION_THRESHOLD = 100;
if (current_status == Status::Initialized) {
// Receiving data means the communication is ready now
current_status = Status::Ready;
status_callback(current_status);
}
const auto& touchpad_0 = data.touch[0];
if (touchpad_0.is_active == 0) {
return;
}
LOG_DEBUG(Input, "Current touch: {} {}", touchpad_0.x, touchpad_0.y);
const u16 min_x = std::min(MAX_VALUE, static_cast<u16>(touchpad_0.x));
const u16 min_y = std::min(MAX_VALUE, static_cast<u16>(touchpad_0.y));
if (current_status == Status::Ready) {
// First touch - min data (min_x/min_y)
current_status = Status::Stage1Completed;
status_callback(current_status);
}
if (touchpad_0.x - min_x > CALIBRATION_THRESHOLD &&
touchpad_0.y - min_y > CALIBRATION_THRESHOLD) {
// Set the current position as max value and finishes configuration
const u16 max_x = touchpad_0.x;
const u16 max_y = touchpad_0.y;
current_status = Status::Completed;
data_callback(min_x, min_y, max_x, max_y);
status_callback(current_status);
if (current_status == Status::Initialized) {
// Receiving data means the communication is ready now
current_status = Status::Ready;
status_callback(current_status);
}
if (data.touch[0].is_active == 0) {
return;
}
LOG_DEBUG(Input, "Current touch: {} {}", data.touch[0].x,
data.touch[0].y);
min_x = std::min(min_x, static_cast<u16>(data.touch[0].x));
min_y = std::min(min_y, static_cast<u16>(data.touch[0].y));
if (current_status == Status::Ready) {
// First touch - min data (min_x/min_y)
current_status = Status::Stage1Completed;
status_callback(current_status);
}
if (data.touch[0].x - min_x > CALIBRATION_THRESHOLD &&
data.touch[0].y - min_y > CALIBRATION_THRESHOLD) {
// Set the current position as max value and finishes
// configuration
max_x = data.touch[0].x;
max_y = data.touch[0].y;
current_status = Status::Completed;
data_callback(min_x, min_y, max_x, max_y);
status_callback(current_status);
complete_event.Set();
}
}};
complete_event.Set();
}
}};
Socket socket{host, port, std::move(callback)};
std::thread worker_thread{SocketLoop, &socket};
complete_event.Wait();

View File

@@ -54,6 +54,18 @@ struct Message {
template <typename T>
constexpr Type GetMessageType();
template <typename T>
Message<T> CreateMessage(const u32 magic, const T data, const u32 sender_id) {
boost::crc_32_type crc;
Header header{
magic, PROTOCOL_VERSION, sizeof(T) + sizeof(Type), 0, sender_id, GetMessageType<T>(),
};
Message<T> message{header, data};
crc.process_bytes(&message, sizeof(Message<T>));
message.header.crc = crc.checksum();
return message;
}
namespace Request {
enum RegisterFlags : u8 {
@@ -101,14 +113,7 @@ static_assert(std::is_trivially_copyable_v<PadData>,
*/
template <typename T>
Message<T> Create(const T data, const u32 client_id = 0) {
boost::crc_32_type crc;
Header header{
CLIENT_MAGIC, PROTOCOL_VERSION, sizeof(T) + sizeof(Type), 0, client_id, GetMessageType<T>(),
};
Message<T> message{header, data};
crc.process_bytes(&message, sizeof(Message<T>));
message.header.crc = crc.checksum();
return message;
return CreateMessage(CLIENT_MAGIC, data, client_id);
}
} // namespace Request

View File

@@ -86,7 +86,7 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, Scal
}
switch (attr) {
case IR::Attribute::PrimitiveId:
ctx.Add("MOV.S {}.x,primitive.id;", inst);
ctx.Add("MOV.F {}.x,primitive.id;", inst);
break;
case IR::Attribute::PositionX:
case IR::Attribute::PositionY:
@@ -112,17 +112,33 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, Scal
case IR::Attribute::TessellationEvaluationPointV:
ctx.Add("MOV.F {}.x,vertex.tesscoord.{};", inst, swizzle);
break;
case IR::Attribute::InstanceId:
ctx.Add("MOV.F {}.x,{}.instance;", inst, ctx.attrib_name);
break;
case IR::Attribute::VertexId:
ctx.Add("MOV.F {}.x,{}.id;", inst, ctx.attrib_name);
break;
case IR::Attribute::FrontFace:
ctx.Add("CMP.F {}.x,{}.facing.x,0,-1;", inst, ctx.attrib_name);
break;
default:
throw NotImplementedException("Get attribute {}", attr);
}
}
void EmitGetAttributeU32(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, ScalarU32) {
switch (attr) {
case IR::Attribute::PrimitiveId:
ctx.Add("MOV.S {}.x,primitive.id;", inst);
break;
case IR::Attribute::InstanceId:
ctx.Add("MOV.S {}.x,{}.instance;", inst, ctx.attrib_name);
break;
case IR::Attribute::VertexId:
ctx.Add("MOV.S {}.x,{}.id;", inst, ctx.attrib_name);
break;
case IR::Attribute::FrontFace:
ctx.Add("CMP.S {}.x,{}.facing.x,0,-1;", inst, ctx.attrib_name);
break;
default:
throw NotImplementedException("Get attribute {}", attr);
throw NotImplementedException("Get U32 attribute {}", attr);
}
}

View File

@@ -50,6 +50,7 @@ void EmitGetCbufU32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
void EmitGetCbufF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset);
void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset);
void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, ScalarU32 vertex);
void EmitGetAttributeU32(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, ScalarU32 vertex);
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, ScalarF32 value, ScalarU32 vertex);
void EmitGetAttributeIndexed(EmitContext& ctx, IR::Inst& inst, ScalarS32 offset, ScalarU32 vertex);
void EmitSetAttributeIndexed(EmitContext& ctx, ScalarU32 offset, ScalarF32 value, ScalarU32 vertex);

View File

@@ -176,7 +176,7 @@ void EmitCode(EmitContext& ctx, const IR::Program& program) {
}
std::string GlslVersionSpecifier(const EmitContext& ctx) {
if (ctx.uses_y_direction || ctx.info.stores.Legacy() || ctx.info.loads.Legacy()) {
if (ctx.uses_y_direction) {
return " compatibility";
}
return "";

View File

@@ -7,6 +7,7 @@
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
#include "shader_recompiler/frontend/ir/value.h"
#include "shader_recompiler/profile.h"
namespace Shader::Backend::GLSL {
namespace {
@@ -30,8 +31,9 @@ void EmitConditionRef(EmitContext& ctx, IR::Inst& inst, const IR::Value& value)
inst.DestructiveAddUsage(1);
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U1)};
const auto input{ctx.var_alloc.Consume(value)};
const auto suffix{ctx.profile.has_gl_bool_ref_bug ? "?true:false" : ""};
if (ret != input) {
ctx.Add("{}={};", ret, input);
ctx.Add("{}={}{};", ret, input, suffix);
}
}

View File

@@ -98,47 +98,50 @@ void GetCbuf16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, const
GetCbuf(ctx, ret, binding, offset, 16, cast, bit_offset);
}
}
u32 TexCoordIndex(IR::Attribute attr) {
return (static_cast<u32>(attr) - static_cast<u32>(IR::Attribute::FixedFncTexture0S)) / 4;
}
} // Anonymous namespace
void EmitGetCbufU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
const IR::Value& offset) {
GetCbuf8(ctx, inst, binding, offset, "ftou");
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
GetCbuf8(ctx, inst, binding, offset, cast);
}
void EmitGetCbufS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
const IR::Value& offset) {
GetCbuf8(ctx, inst, binding, offset, "ftoi");
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "int" : "ftoi"};
GetCbuf8(ctx, inst, binding, offset, cast);
}
void EmitGetCbufU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
const IR::Value& offset) {
GetCbuf16(ctx, inst, binding, offset, "ftou");
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
GetCbuf16(ctx, inst, binding, offset, cast);
}
void EmitGetCbufS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
const IR::Value& offset) {
GetCbuf16(ctx, inst, binding, offset, "ftoi");
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "int" : "ftoi"};
GetCbuf16(ctx, inst, binding, offset, cast);
}
void EmitGetCbufU32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
const IR::Value& offset) {
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)};
GetCbuf(ctx, ret, binding, offset, 32, "ftou");
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
GetCbuf(ctx, ret, binding, offset, 32, cast);
}
void EmitGetCbufF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
const IR::Value& offset) {
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::F32)};
GetCbuf(ctx, ret, binding, offset, 32);
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "utof" : ""};
GetCbuf(ctx, ret, binding, offset, 32, cast);
}
void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
const IR::Value& offset) {
const auto cbuf{fmt::format("{}_cbuf{}", ctx.stage_name, binding.U32())};
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
if (offset.IsImmediate()) {
static constexpr u32 cbuf_size{0x10000};
const u32 u32_offset{offset.U32()};
@@ -149,26 +152,26 @@ void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding
return;
}
if (u32_offset % 2 == 0) {
ctx.AddU32x2("{}=ftou({}[{}].{}{});", inst, cbuf, u32_offset / 16,
ctx.AddU32x2("{}={}({}[{}].{}{});", inst, cast, cbuf, u32_offset / 16,
OffsetSwizzle(u32_offset), OffsetSwizzle(u32_offset + 4));
} else {
ctx.AddU32x2("{}=uvec2(ftou({}[{}].{}),ftou({}[{}].{}));", inst, cbuf, u32_offset / 16,
OffsetSwizzle(u32_offset), cbuf, (u32_offset + 4) / 16,
OffsetSwizzle(u32_offset + 4));
ctx.AddU32x2("{}=uvec2({}({}[{}].{}),{}({}[{}].{}));", inst, cast, cbuf,
u32_offset / 16, OffsetSwizzle(u32_offset), cast, cbuf,
(u32_offset + 4) / 16, OffsetSwizzle(u32_offset + 4));
}
return;
}
const auto offset_var{ctx.var_alloc.Consume(offset)};
if (!ctx.profile.has_gl_component_indexing_bug) {
ctx.AddU32x2("{}=uvec2(ftou({}[{}>>4][({}>>2)%4]),ftou({}[({}+4)>>4][(({}+4)>>2)%4]));",
inst, cbuf, offset_var, offset_var, cbuf, offset_var, offset_var);
ctx.AddU32x2("{}=uvec2({}({}[{}>>4][({}>>2)%4]),{}({}[({}+4)>>4][(({}+4)>>2)%4]));", inst,
cast, cbuf, offset_var, offset_var, cast, cbuf, offset_var, offset_var);
return;
}
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x2)};
const auto cbuf_offset{fmt::format("{}>>2", offset_var)};
for (u32 swizzle = 0; swizzle < 4; ++swizzle) {
ctx.Add("if(({}&3)=={}){}=uvec2(ftou({}[{}>>4].{}),ftou({}[({}+4)>>4].{}));", cbuf_offset,
swizzle, ret, cbuf, offset_var, "xyzw"[swizzle], cbuf, offset_var,
ctx.Add("if(({}&3)=={}){}=uvec2({}({}[{}>>4].{}),{}({}[({}+4)>>4].{}));", cbuf_offset,
swizzle, ret, cast, cbuf, offset_var, "xyzw"[swizzle], cast, cbuf, offset_var,
"xyzw"[(swizzle + 1) % 4]);
}
}
@@ -190,18 +193,6 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
ctx.AddF32("{}=in_attr{}{}.{};", inst, index, InputVertexIndex(ctx, vertex), swizzle);
return;
}
// GLSL only exposes 8 legacy texcoords
if (attr >= IR::Attribute::FixedFncTexture8S && attr <= IR::Attribute::FixedFncTexture9Q) {
LOG_WARNING(Shader_GLSL, "GLSL does not allow access to gl_TexCoord[{}]",
TexCoordIndex(attr));
ctx.AddF32("{}=0.f;", inst);
return;
}
if (attr >= IR::Attribute::FixedFncTexture0S && attr <= IR::Attribute::FixedFncTexture7Q) {
const u32 index{TexCoordIndex(attr)};
ctx.AddF32("{}=gl_TexCoord[{}].{};", inst, index, swizzle);
return;
}
switch (attr) {
case IR::Attribute::PrimitiveId:
ctx.AddF32("{}=itof(gl_PrimitiveID);", inst);
@@ -215,16 +206,6 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
ctx.AddF32("{}={}{}.{};", inst, input_decorator, ctx.position_name, swizzle);
break;
}
case IR::Attribute::ColorFrontDiffuseR:
case IR::Attribute::ColorFrontDiffuseG:
case IR::Attribute::ColorFrontDiffuseB:
case IR::Attribute::ColorFrontDiffuseA:
if (ctx.stage == Stage::Fragment) {
ctx.AddF32("{}=gl_Color.{};", inst, swizzle);
} else {
ctx.AddF32("{}=gl_FrontColor.{};", inst, swizzle);
}
break;
case IR::Attribute::PointSpriteS:
case IR::Attribute::PointSpriteT:
ctx.AddF32("{}=gl_PointCoord.{};", inst, swizzle);
@@ -247,6 +228,22 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
}
}
void EmitGetAttributeU32(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, std::string_view) {
switch (attr) {
case IR::Attribute::PrimitiveId:
ctx.AddU32("{}=uint(gl_PrimitiveID);", inst);
break;
case IR::Attribute::InstanceId:
ctx.AddU32("{}=uint(gl_InstanceID);", inst);
break;
case IR::Attribute::VertexId:
ctx.AddU32("{}=uint(gl_VertexID);", inst);
break;
default:
throw NotImplementedException("Get U32 attribute {}", attr);
}
}
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, std::string_view value,
[[maybe_unused]] std::string_view vertex) {
if (IR::IsGeneric(attr)) {
@@ -264,17 +261,6 @@ void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, std::string_view val
}
const u32 element{static_cast<u32>(attr) % 4};
const char swizzle{"xyzw"[element]};
// GLSL only exposes 8 legacy texcoords
if (attr >= IR::Attribute::FixedFncTexture8S && attr <= IR::Attribute::FixedFncTexture9Q) {
LOG_WARNING(Shader_GLSL, "GLSL does not allow access to gl_TexCoord[{}]",
TexCoordIndex(attr));
return;
}
if (attr >= IR::Attribute::FixedFncTexture0S && attr <= IR::Attribute::FixedFncTexture7Q) {
const u32 index{TexCoordIndex(attr)};
ctx.Add("gl_TexCoord[{}].{}={};", index, swizzle, value);
return;
}
switch (attr) {
case IR::Attribute::Layer:
if (ctx.stage != Stage::Geometry &&
@@ -312,33 +298,6 @@ void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, std::string_view val
case IR::Attribute::PositionW:
ctx.Add("gl_Position.{}={};", swizzle, value);
break;
case IR::Attribute::ColorFrontDiffuseR:
case IR::Attribute::ColorFrontDiffuseG:
case IR::Attribute::ColorFrontDiffuseB:
case IR::Attribute::ColorFrontDiffuseA:
ctx.Add("gl_FrontColor.{}={};", swizzle, value);
break;
case IR::Attribute::ColorFrontSpecularR:
case IR::Attribute::ColorFrontSpecularG:
case IR::Attribute::ColorFrontSpecularB:
case IR::Attribute::ColorFrontSpecularA:
ctx.Add("gl_FrontSecondaryColor.{}={};", swizzle, value);
break;
case IR::Attribute::ColorBackDiffuseR:
case IR::Attribute::ColorBackDiffuseG:
case IR::Attribute::ColorBackDiffuseB:
case IR::Attribute::ColorBackDiffuseA:
ctx.Add("gl_BackColor.{}={};", swizzle, value);
break;
case IR::Attribute::ColorBackSpecularR:
case IR::Attribute::ColorBackSpecularG:
case IR::Attribute::ColorBackSpecularB:
case IR::Attribute::ColorBackSpecularA:
ctx.Add("gl_BackSecondaryColor.{}={};", swizzle, value);
break;
case IR::Attribute::FogCoordinate:
ctx.Add("gl_FogFragCoord={};", value);
break;
case IR::Attribute::ClipDistance0:
case IR::Attribute::ClipDistance1:
case IR::Attribute::ClipDistance2:

View File

@@ -125,11 +125,11 @@ void EmitFPNeg16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& i
}
void EmitFPNeg32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
ctx.AddF32("{}=-({});", inst, value);
ctx.AddF32("{}=0.f-({});", inst, value);
}
void EmitFPNeg64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
ctx.AddF64("{}=-({});", inst, value);
ctx.AddF64("{}=double(0.)-({});", inst, value);
}
void EmitFPSin(EmitContext& ctx, IR::Inst& inst, std::string_view value) {

View File

@@ -60,6 +60,8 @@ void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding
const IR::Value& offset);
void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
std::string_view vertex);
void EmitGetAttributeU32(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
std::string_view vertex);
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, std::string_view value,
std::string_view vertex);
void EmitGetAttributeIndexed(EmitContext& ctx, IR::Inst& inst, std::string_view offset,

View File

@@ -87,11 +87,11 @@ void EmitUDiv32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::strin
}
void EmitINeg32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
ctx.AddU32("{}=uint(-({}));", inst, value);
ctx.AddU32("{}=uint(int(0)-int({}));", inst, value);
}
void EmitINeg64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
ctx.AddU64("{}=-({});", inst, value);
ctx.AddU64("{}=uint64_t(int64_t(0)-int64_t({}));", inst, value);
}
void EmitIAbs32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {

View File

@@ -90,7 +90,9 @@ void EmitPhiMove(EmitContext& ctx, const IR::Value& phi_value, const IR::Value&
if (phi_reg == val_reg) {
return;
}
ctx.Add("{}={};", phi_reg, val_reg);
const bool needs_workaround{ctx.profile.has_gl_bool_ref_bug && phi_type == IR::Type::U1};
const auto suffix{needs_workaround ? "?true:false" : ""};
ctx.Add("{}={}{};", phi_reg, val_reg, suffix);
}
void EmitPrologue(EmitContext& ctx) {

View File

@@ -211,27 +211,6 @@ std::string_view OutputPrimitive(OutputTopology topology) {
throw InvalidArgument("Invalid output topology {}", topology);
}
void SetupLegacyOutPerVertex(EmitContext& ctx, std::string& header) {
if (!ctx.info.stores.Legacy()) {
return;
}
if (ctx.info.stores.FixedFunctionTexture()) {
header += "vec4 gl_TexCoord[8];";
}
if (ctx.info.stores.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) {
header += "vec4 gl_FrontColor;";
}
if (ctx.info.stores.AnyComponent(IR::Attribute::ColorFrontSpecularR)) {
header += "vec4 gl_FrontSecondaryColor;";
}
if (ctx.info.stores.AnyComponent(IR::Attribute::ColorBackDiffuseR)) {
header += "vec4 gl_BackColor;";
}
if (ctx.info.stores.AnyComponent(IR::Attribute::ColorBackSpecularR)) {
header += "vec4 gl_BackSecondaryColor;";
}
}
void SetupOutPerVertex(EmitContext& ctx, std::string& header) {
if (!StoresPerVertexAttributes(ctx.stage)) {
return;
@@ -250,7 +229,6 @@ void SetupOutPerVertex(EmitContext& ctx, std::string& header) {
ctx.profile.support_viewport_index_layer_non_geometry && ctx.stage != Stage::Geometry) {
header += "int gl_ViewportIndex;";
}
SetupLegacyOutPerVertex(ctx, header);
header += "};";
if (ctx.info.stores[IR::Attribute::ViewportIndex] && ctx.stage == Stage::Geometry) {
header += "out int gl_ViewportIndex;";
@@ -282,21 +260,6 @@ void SetupInPerVertex(EmitContext& ctx, std::string& header) {
}
header += "}gl_in[gl_MaxPatchVertices];";
}
void SetupLegacyInPerFragment(EmitContext& ctx, std::string& header) {
if (!ctx.info.loads.Legacy()) {
return;
}
header += "in gl_PerFragment{";
if (ctx.info.loads.FixedFunctionTexture()) {
header += "vec4 gl_TexCoord[8];";
}
if (ctx.info.loads.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) {
header += "vec4 gl_Color;";
}
header += "};";
}
} // Anonymous namespace
EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_,
@@ -361,7 +324,6 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
}
SetupOutPerVertex(*this, header);
SetupInPerVertex(*this, header);
SetupLegacyInPerFragment(*this, header);
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
if (!info.loads.Generic(index) || !runtime_info.previous_stage_stores.Generic(index)) {
@@ -466,9 +428,10 @@ void EmitContext::DefineConstantBuffers(Bindings& bindings) {
return;
}
for (const auto& desc : info.constant_buffer_descriptors) {
header += fmt::format(
"layout(std140,binding={}) uniform {}_cbuf_{}{{vec4 {}_cbuf{}[{}];}};",
bindings.uniform_buffer, stage_name, desc.index, stage_name, desc.index, 4 * 1024);
const auto cbuf_type{profile.has_gl_cbuf_ftou_bug ? "uvec4" : "vec4"};
header += fmt::format("layout(std140,binding={}) uniform {}_cbuf_{}{{{} {}_cbuf{}[{}];}};",
bindings.uniform_buffer, stage_name, desc.index, cbuf_type,
stage_name, desc.index, 4 * 1024);
bindings.uniform_buffer += desc.count;
}
}

View File

@@ -30,11 +30,20 @@ struct FuncTraits<ReturnType_ (*)(Args...)> {
using ArgType = std::tuple_element_t<I, std::tuple<Args...>>;
};
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4702) // Ignore unreachable code warning
#endif
template <auto func, typename... Args>
void SetDefinition(EmitContext& ctx, IR::Inst* inst, Args... args) {
inst->SetDefinition<Id>(func(ctx, std::forward<Args>(args)...));
}
#ifdef _MSC_VER
#pragma warning(pop)
#endif
template <typename ArgType>
ArgType Arg(EmitContext& ctx, const IR::Value& arg) {
if constexpr (std::is_same_v<ArgType, Id>) {

View File

@@ -44,14 +44,6 @@ Id AttrPointer(EmitContext& ctx, Id pointer_type, Id vertex, Id base, Args&&...
}
}
bool IsLegacyAttribute(IR::Attribute attribute) {
return (attribute >= IR::Attribute::ColorFrontDiffuseR &&
attribute <= IR::Attribute::ColorBackSpecularA) ||
attribute == IR::Attribute::FogCoordinate ||
(attribute >= IR::Attribute::FixedFncTexture0S &&
attribute <= IR::Attribute::FixedFncTexture9Q);
}
template <typename... Args>
Id OutputAccessChain(EmitContext& ctx, Id result_type, Id base, Args&&... args) {
if (ctx.stage == Stage::TessellationControl) {
@@ -83,17 +75,6 @@ std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
return OutputAccessChain(ctx, ctx.output_f32, info.id, index_id);
}
}
if (IsLegacyAttribute(attr)) {
if (attr == IR::Attribute::FogCoordinate) {
return OutputAccessChain(ctx, ctx.output_f32, ctx.OutputLegacyAttribute(attr),
ctx.Const(0u));
} else {
const u32 element{static_cast<u32>(attr) % 4};
const Id element_id{ctx.Const(element)};
return OutputAccessChain(ctx, ctx.output_f32, ctx.OutputLegacyAttribute(attr),
element_id);
}
}
switch (attr) {
case IR::Attribute::PointSize:
return ctx.output_point_size;
@@ -327,18 +308,6 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
const Id value{ctx.OpLoad(type->id, pointer)};
return type->needs_cast ? ctx.OpBitcast(ctx.F32[1], value) : value;
}
if (IsLegacyAttribute(attr)) {
if (attr == IR::Attribute::FogCoordinate) {
const Id attr_ptr{AttrPointer(ctx, ctx.input_f32, vertex,
ctx.InputLegacyAttribute(attr), ctx.Const(0u))};
return ctx.OpLoad(ctx.F32[1], attr_ptr);
} else {
const Id element_id{ctx.Const(element)};
const Id attr_ptr{AttrPointer(ctx, ctx.input_f32, vertex,
ctx.InputLegacyAttribute(attr), element_id)};
return ctx.OpLoad(ctx.F32[1], attr_ptr);
}
}
switch (attr) {
case IR::Attribute::PrimitiveId:
return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.primitive_id));
@@ -386,6 +355,31 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
}
}
Id EmitGetAttributeU32(EmitContext& ctx, IR::Attribute attr, Id) {
switch (attr) {
case IR::Attribute::PrimitiveId:
return ctx.OpLoad(ctx.U32[1], ctx.primitive_id);
case IR::Attribute::InstanceId:
if (ctx.profile.support_vertex_instance_id) {
return ctx.OpLoad(ctx.U32[1], ctx.instance_id);
} else {
const Id index{ctx.OpLoad(ctx.U32[1], ctx.instance_index)};
const Id base{ctx.OpLoad(ctx.U32[1], ctx.base_instance)};
return ctx.OpISub(ctx.U32[1], index, base);
}
case IR::Attribute::VertexId:
if (ctx.profile.support_vertex_instance_id) {
return ctx.OpLoad(ctx.U32[1], ctx.vertex_id);
} else {
const Id index{ctx.OpLoad(ctx.U32[1], ctx.vertex_index)};
const Id base{ctx.OpLoad(ctx.U32[1], ctx.base_vertex)};
return ctx.OpISub(ctx.U32[1], index, base);
}
default:
throw NotImplementedException("Read U32 attribute {}", attr);
}
}
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, [[maybe_unused]] Id vertex) {
const std::optional<OutAttr> output{OutputAttrPointer(ctx, attr)};
if (!output) {

View File

@@ -53,6 +53,7 @@ Id EmitGetCbufU32(EmitContext& ctx, const IR::Value& binding, const IR::Value& o
Id EmitGetCbufF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
Id EmitGetCbufU32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex);
Id EmitGetAttributeU32(EmitContext& ctx, IR::Attribute attr, Id vertex);
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, Id vertex);
Id EmitGetAttributeIndexed(EmitContext& ctx, Id offset, Id vertex);
void EmitSetAttributeIndexed(EmitContext& ctx, Id offset, Id value, Id vertex);

View File

@@ -18,8 +18,6 @@
namespace Shader::Backend::SPIRV {
namespace {
constexpr size_t NUM_FIXEDFNCTEXTURE = 10;
enum class Operation {
Increment,
Decrement,
@@ -432,34 +430,6 @@ Id DescType(EmitContext& ctx, Id sampled_type, Id pointer_type, u32 count) {
return pointer_type;
}
}
size_t FindAndSetNextUnusedLocation(std::bitset<IR::NUM_GENERICS>& used_locations,
size_t& start_offset) {
for (size_t location = start_offset; location < used_locations.size(); ++location) {
if (!used_locations.test(location)) {
start_offset = location;
used_locations.set(location);
return location;
}
}
throw RuntimeError("Unable to get an unused location for legacy attribute");
}
Id DefineLegacyInput(EmitContext& ctx, std::bitset<IR::NUM_GENERICS>& used_locations,
size_t& start_offset) {
const Id id{DefineInput(ctx, ctx.F32[4], true)};
const size_t location = FindAndSetNextUnusedLocation(used_locations, start_offset);
ctx.Decorate(id, spv::Decoration::Location, location);
return id;
}
Id DefineLegacyOutput(EmitContext& ctx, std::bitset<IR::NUM_GENERICS>& used_locations,
size_t& start_offset, std::optional<u32> invocations) {
const Id id{DefineOutput(ctx, ctx.F32[4], invocations)};
const size_t location = FindAndSetNextUnusedLocation(used_locations, start_offset);
ctx.Decorate(id, spv::Decoration::Location, location);
return id;
}
} // Anonymous namespace
void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name) {
@@ -543,64 +513,6 @@ Id EmitContext::BitOffset16(const IR::Value& offset) {
return OpBitwiseAnd(U32[1], OpShiftLeftLogical(U32[1], Def(offset), Const(3u)), Const(16u));
}
Id EmitContext::InputLegacyAttribute(IR::Attribute attribute) {
if (attribute >= IR::Attribute::ColorFrontDiffuseR &&
attribute <= IR::Attribute::ColorFrontDiffuseA) {
return input_front_color;
}
if (attribute >= IR::Attribute::ColorFrontSpecularR &&
attribute <= IR::Attribute::ColorFrontSpecularA) {
return input_front_secondary_color;
}
if (attribute >= IR::Attribute::ColorBackDiffuseR &&
attribute <= IR::Attribute::ColorBackDiffuseA) {
return input_back_color;
}
if (attribute >= IR::Attribute::ColorBackSpecularR &&
attribute <= IR::Attribute::ColorBackSpecularA) {
return input_back_secondary_color;
}
if (attribute == IR::Attribute::FogCoordinate) {
return input_fog_frag_coord;
}
if (attribute >= IR::Attribute::FixedFncTexture0S &&
attribute <= IR::Attribute::FixedFncTexture9Q) {
u32 index =
(static_cast<u32>(attribute) - static_cast<u32>(IR::Attribute::FixedFncTexture0S)) / 4;
return input_fixed_fnc_textures[index];
}
throw InvalidArgument("Attribute is not legacy attribute {}", attribute);
}
Id EmitContext::OutputLegacyAttribute(IR::Attribute attribute) {
if (attribute >= IR::Attribute::ColorFrontDiffuseR &&
attribute <= IR::Attribute::ColorFrontDiffuseA) {
return output_front_color;
}
if (attribute >= IR::Attribute::ColorFrontSpecularR &&
attribute <= IR::Attribute::ColorFrontSpecularA) {
return output_front_secondary_color;
}
if (attribute >= IR::Attribute::ColorBackDiffuseR &&
attribute <= IR::Attribute::ColorBackDiffuseA) {
return output_back_color;
}
if (attribute >= IR::Attribute::ColorBackSpecularR &&
attribute <= IR::Attribute::ColorBackSpecularA) {
return output_back_secondary_color;
}
if (attribute == IR::Attribute::FogCoordinate) {
return output_fog_frag_coord;
}
if (attribute >= IR::Attribute::FixedFncTexture0S &&
attribute <= IR::Attribute::FixedFncTexture9Q) {
u32 index =
(static_cast<u32>(attribute) - static_cast<u32>(IR::Attribute::FixedFncTexture0S)) / 4;
return output_fixed_fnc_textures[index];
}
throw InvalidArgument("Attribute is not legacy attribute {}", attribute);
}
void EmitContext::DefineCommonTypes(const Info& info) {
void_id = TypeVoid();
@@ -1389,7 +1301,6 @@ void EmitContext::DefineInputs(const IR::Program& program) {
loads[IR::Attribute::TessellationEvaluationPointV]) {
tess_coord = DefineInput(*this, F32[3], false, spv::BuiltIn::TessCoord);
}
std::bitset<IR::NUM_GENERICS> used_locations{};
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
const AttributeType input_type{runtime_info.generic_input_types[index]};
if (!runtime_info.previous_stage_stores.Generic(index)) {
@@ -1401,7 +1312,6 @@ void EmitContext::DefineInputs(const IR::Program& program) {
if (input_type == AttributeType::Disabled) {
continue;
}
used_locations.set(index);
const Id type{GetAttributeType(*this, input_type)};
const Id id{DefineInput(*this, type, true)};
Decorate(id, spv::Decoration::Location, static_cast<u32>(index));
@@ -1427,30 +1337,6 @@ void EmitContext::DefineInputs(const IR::Program& program) {
break;
}
}
size_t previous_unused_location = 0;
if (loads.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) {
input_front_color = DefineLegacyInput(*this, used_locations, previous_unused_location);
}
if (loads.AnyComponent(IR::Attribute::ColorFrontSpecularR)) {
input_front_secondary_color =
DefineLegacyInput(*this, used_locations, previous_unused_location);
}
if (loads.AnyComponent(IR::Attribute::ColorBackDiffuseR)) {
input_back_color = DefineLegacyInput(*this, used_locations, previous_unused_location);
}
if (loads.AnyComponent(IR::Attribute::ColorBackSpecularR)) {
input_back_secondary_color =
DefineLegacyInput(*this, used_locations, previous_unused_location);
}
if (loads.AnyComponent(IR::Attribute::FogCoordinate)) {
input_fog_frag_coord = DefineLegacyInput(*this, used_locations, previous_unused_location);
}
for (size_t index = 0; index < NUM_FIXEDFNCTEXTURE; ++index) {
if (loads.AnyComponent(IR::Attribute::FixedFncTexture0S + index * 4)) {
input_fixed_fnc_textures[index] =
DefineLegacyInput(*this, used_locations, previous_unused_location);
}
}
if (stage == Stage::TessellationEval) {
for (size_t index = 0; index < info.uses_patches.size(); ++index) {
if (!info.uses_patches[index]) {
@@ -1501,38 +1387,9 @@ void EmitContext::DefineOutputs(const IR::Program& program) {
viewport_mask = DefineOutput(*this, TypeArray(U32[1], Const(1u)), std::nullopt,
spv::BuiltIn::ViewportMaskNV);
}
std::bitset<IR::NUM_GENERICS> used_locations{};
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
if (info.stores.Generic(index)) {
DefineGenericOutput(*this, index, invocations);
used_locations.set(index);
}
}
size_t previous_unused_location = 0;
if (info.stores.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) {
output_front_color =
DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations);
}
if (info.stores.AnyComponent(IR::Attribute::ColorFrontSpecularR)) {
output_front_secondary_color =
DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations);
}
if (info.stores.AnyComponent(IR::Attribute::ColorBackDiffuseR)) {
output_back_color =
DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations);
}
if (info.stores.AnyComponent(IR::Attribute::ColorBackSpecularR)) {
output_back_secondary_color =
DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations);
}
if (info.stores.AnyComponent(IR::Attribute::FogCoordinate)) {
output_fog_frag_coord =
DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations);
}
for (size_t index = 0; index < NUM_FIXEDFNCTEXTURE; ++index) {
if (info.stores.AnyComponent(IR::Attribute::FixedFncTexture0S + index * 4)) {
output_fixed_fnc_textures[index] =
DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations);
}
}
switch (stage) {

View File

@@ -113,9 +113,6 @@ public:
[[nodiscard]] Id BitOffset8(const IR::Value& offset);
[[nodiscard]] Id BitOffset16(const IR::Value& offset);
Id InputLegacyAttribute(IR::Attribute attribute);
Id OutputLegacyAttribute(IR::Attribute attribute);
Id Const(u32 value) {
return Constant(U32[1], value);
}
@@ -281,22 +278,10 @@ public:
Id write_global_func_u32x4{};
Id input_position{};
Id input_front_color{};
Id input_front_secondary_color{};
Id input_back_color{};
Id input_back_secondary_color{};
Id input_fog_frag_coord{};
std::array<Id, 10> input_fixed_fnc_textures{};
std::array<Id, 32> input_generics{};
Id output_point_size{};
Id output_position{};
Id output_front_color{};
Id output_front_secondary_color{};
Id output_back_color{};
Id output_back_secondary_color{};
Id output_fog_frag_coord{};
std::array<Id, 10> output_fixed_fnc_textures{};
std::array<std::array<GenericElementInfo, 4>, 32> output_generics{};
Id output_tess_level_outer{};

View File

@@ -31,6 +31,8 @@ public:
[[nodiscard]] virtual std::array<u32, 3> WorkgroupSize() const = 0;
virtual void Dump(u64 hash) = 0;
[[nodiscard]] const ProgramHeader& SPH() const noexcept {
return sph;
}

View File

@@ -224,6 +224,8 @@ enum class Attribute : u64 {
constexpr size_t NUM_GENERICS = 32;
constexpr size_t NUM_FIXEDFNCTEXTURE = 10;
[[nodiscard]] bool IsGeneric(Attribute attribute) noexcept;
[[nodiscard]] u32 GenericAttributeIndex(Attribute attribute);

View File

@@ -40,6 +40,7 @@ OPCODE(GetCbufU32, U32, U32,
OPCODE(GetCbufF32, F32, U32, U32, )
OPCODE(GetCbufU32x2, U32x2, U32, U32, )
OPCODE(GetAttribute, F32, Attribute, U32, )
OPCODE(GetAttributeU32, U32, Attribute, U32, )
OPCODE(SetAttribute, Void, Attribute, F32, U32, )
OPCODE(GetAttributeIndexed, F32, U32, U32, )
OPCODE(SetAttributeIndexed, Void, U32, F32, U32, )

View File

@@ -5,6 +5,7 @@
#include <algorithm>
#include <memory>
#include <vector>
#include <queue>
#include "common/settings.h"
#include "shader_recompiler/exception.h"
@@ -127,6 +128,42 @@ void AddNVNStorageBuffers(IR::Program& program) {
});
}
}
bool IsLegacyAttribute(IR::Attribute attribute) {
return (attribute >= IR::Attribute::ColorFrontDiffuseR &&
attribute <= IR::Attribute::ColorBackSpecularA) ||
attribute == IR::Attribute::FogCoordinate ||
(attribute >= IR::Attribute::FixedFncTexture0S &&
attribute <= IR::Attribute::FixedFncTexture9Q);
}
std::map<IR::Attribute, IR::Attribute> GenerateLegacyToGenericMappings(
const VaryingState& state, std::queue<IR::Attribute> ununsed_generics) {
std::map<IR::Attribute, IR::Attribute> mapping;
for (size_t index = 0; index < 4; ++index) {
auto attr = IR::Attribute::ColorFrontDiffuseR + index * 4;
if (state.AnyComponent(attr)) {
for (size_t i = 0; i < 4; ++i) {
mapping.insert({attr + i, ununsed_generics.front() + i});
}
ununsed_generics.pop();
}
}
if (state[IR::Attribute::FogCoordinate]) {
mapping.insert({IR::Attribute::FogCoordinate, ununsed_generics.front()});
ununsed_generics.pop();
}
for (size_t index = 0; index < IR::NUM_FIXEDFNCTEXTURE; ++index) {
auto attr = IR::Attribute::FixedFncTexture0S + index * 4;
if (state.AnyComponent(attr)) {
for (size_t i = 0; i < 4; ++i) {
mapping.insert({attr + i, ununsed_generics.front() + i});
}
ununsed_generics.pop();
}
}
return mapping;
}
} // Anonymous namespace
IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
@@ -226,4 +263,62 @@ IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b
return result;
}
void ConvertLegacyToGeneric(IR::Program& program, const Shader::RuntimeInfo& runtime_info) {
auto& stores = program.info.stores;
if (stores.Legacy()) {
std::queue<IR::Attribute> ununsed_output_generics{};
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
if (!stores.Generic(index)) {
ununsed_output_generics.push(IR::Attribute::Generic0X + index * 4);
}
}
auto mappings = GenerateLegacyToGenericMappings(stores, ununsed_output_generics);
for (IR::Block* const block : program.post_order_blocks) {
for (IR::Inst& inst : block->Instructions()) {
switch (inst.GetOpcode()) {
case IR::Opcode::SetAttribute: {
const auto attr = inst.Arg(0).Attribute();
if (IsLegacyAttribute(attr)) {
stores.Set(mappings[attr], true);
inst.SetArg(0, Shader::IR::Value(mappings[attr]));
}
break;
}
default:
break;
}
}
}
}
auto& loads = program.info.loads;
if (loads.Legacy()) {
std::queue<IR::Attribute> ununsed_input_generics{};
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
const AttributeType input_type{runtime_info.generic_input_types[index]};
if (!runtime_info.previous_stage_stores.Generic(index) || !loads.Generic(index) ||
input_type == AttributeType::Disabled) {
ununsed_input_generics.push(IR::Attribute::Generic0X + index * 4);
}
}
auto mappings = GenerateLegacyToGenericMappings(loads, ununsed_input_generics);
for (IR::Block* const block : program.post_order_blocks) {
for (IR::Inst& inst : block->Instructions()) {
switch (inst.GetOpcode()) {
case IR::Opcode::GetAttribute: {
const auto attr = inst.Arg(0).Attribute();
if (IsLegacyAttribute(attr)) {
loads.Set(mappings[attr], true);
inst.SetArg(0, Shader::IR::Value(mappings[attr]));
}
break;
}
default:
break;
}
}
}
}
}
} // namespace Shader::Maxwell

View File

@@ -10,6 +10,7 @@
#include "shader_recompiler/frontend/maxwell/control_flow.h"
#include "shader_recompiler/host_translate_info.h"
#include "shader_recompiler/object_pool.h"
#include "shader_recompiler/runtime_info.h"
namespace Shader::Maxwell {
@@ -20,4 +21,7 @@ namespace Shader::Maxwell {
[[nodiscard]] IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b,
Environment& env_vertex_b);
[[nodiscard]] void ConvertLegacyToGeneric(IR::Program& program,
const Shader::RuntimeInfo& runtime_info);
} // namespace Shader::Maxwell

View File

@@ -389,6 +389,7 @@ void VisitUsages(Info& info, IR::Inst& inst) {
info.uses_demote_to_helper_invocation = true;
break;
case IR::Opcode::GetAttribute:
case IR::Opcode::GetAttributeU32:
info.loads.mask[static_cast<size_t>(inst.Arg(0).Attribute())] = true;
break;
case IR::Opcode::SetAttribute:

View File

@@ -505,6 +505,29 @@ void FoldBitCast(IR::Inst& inst, IR::Opcode reverse) {
return;
}
}
if constexpr (op == IR::Opcode::BitCastU32F32) {
// Workaround for new NVIDIA driver bug, where:
// uint attr = ftou(itof(gl_InstanceID));
// always returned 0.
// We can instead manually optimize this and work around the driver bug:
// uint attr = uint(gl_InstanceID);
if (arg_inst->GetOpcode() == IR::Opcode::GetAttribute) {
const IR::Attribute attr{arg_inst->Arg(0).Attribute()};
switch (attr) {
case IR::Attribute::PrimitiveId:
case IR::Attribute::InstanceId:
case IR::Attribute::VertexId:
break;
default:
return;
}
// Replace the bitcasts with an integer attribute get
inst.ReplaceOpcode(IR::Opcode::GetAttributeU32);
inst.SetArg(0, arg_inst->Arg(0));
inst.SetArg(1, arg_inst->Arg(1));
return;
}
}
}
void FoldInverseFunc(IR::Inst& inst, IR::Opcode reverse) {

View File

@@ -65,6 +65,10 @@ struct Profile {
bool has_gl_component_indexing_bug{};
/// The precise type qualifier is broken in the fragment stage of some drivers
bool has_gl_precise_bug{};
/// Some drivers do not properly support floatBitsToUint when used on cbufs
bool has_gl_cbuf_ftou_bug{};
/// Some drivers poorly optimize boolean variable references
bool has_gl_bool_ref_bug{};
/// Ignores SPIR-V ordered vs unordered using GLSL semantics
bool ignore_nan_fp_comparisons{};

View File

@@ -53,7 +53,8 @@ struct VaryingState {
return AnyComponent(IR::Attribute::ColorFrontDiffuseR) ||
AnyComponent(IR::Attribute::ColorFrontSpecularR) ||
AnyComponent(IR::Attribute::ColorBackDiffuseR) ||
AnyComponent(IR::Attribute::ColorBackSpecularR) || FixedFunctionTexture();
AnyComponent(IR::Attribute::ColorBackSpecularR) || FixedFunctionTexture() ||
mask[static_cast<size_t>(IR::Attribute::FogCoordinate)];
}
[[nodiscard]] bool FixedFunctionTexture() const noexcept {

View File

@@ -10,11 +10,12 @@ add_executable(tests
core/network/network.cpp
tests.cpp
video_core/buffer_base.cpp
input_common/calibration_configuration_job.cpp
)
create_target_directory_groups(tests)
target_link_libraries(tests PRIVATE common core)
target_link_libraries(tests PRIVATE common core input_common)
target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} catch-single-include Threads::Threads)
add_test(NAME tests COMMAND tests)

View File

@@ -0,0 +1,136 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <string>
#include <thread>
#include <boost/asio.hpp>
#include <boost/crc.hpp>
#include <catch2/catch.hpp>
#include "input_common/drivers/udp_client.h"
#include "input_common/helpers/udp_protocol.h"
class FakeCemuhookServer {
public:
FakeCemuhookServer()
: socket(io_service, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 0)) {}
~FakeCemuhookServer() {
is_running = false;
boost::system::error_code error_code;
socket.shutdown(boost::asio::socket_base::shutdown_both, error_code);
socket.close();
if (handler.joinable()) {
handler.join();
}
}
u16 GetPort() {
return socket.local_endpoint().port();
}
std::string GetHost() {
return socket.local_endpoint().address().to_string();
}
void Run(const std::vector<InputCommon::CemuhookUDP::Response::TouchPad> touch_movement_path) {
constexpr size_t HeaderSize = sizeof(InputCommon::CemuhookUDP::Header);
constexpr size_t PadDataSize =
sizeof(InputCommon::CemuhookUDP::Message<InputCommon::CemuhookUDP::Response::PadData>);
REQUIRE(touch_movement_path.size() > 0);
is_running = true;
handler = std::thread([touch_movement_path, this]() {
auto current_touch_position = touch_movement_path.begin();
while (is_running) {
boost::asio::ip::udp::endpoint sender_endpoint;
boost::system::error_code error_code;
auto received_size = socket.receive_from(boost::asio::buffer(receive_buffer),
sender_endpoint, 0, error_code);
if (received_size < HeaderSize) {
continue;
}
InputCommon::CemuhookUDP::Header header{};
std::memcpy(&header, receive_buffer.data(), HeaderSize);
switch (header.type) {
case InputCommon::CemuhookUDP::Type::PadData: {
InputCommon::CemuhookUDP::Response::PadData pad_data{};
pad_data.touch[0] = *current_touch_position;
const auto pad_message = InputCommon::CemuhookUDP::CreateMessage(
InputCommon::CemuhookUDP::SERVER_MAGIC, pad_data, 0);
std::memcpy(send_buffer.data(), &pad_message, PadDataSize);
socket.send_to(boost::asio::buffer(send_buffer, PadDataSize), sender_endpoint,
0, error_code);
bool can_advance =
std::next(current_touch_position) != touch_movement_path.end();
if (can_advance) {
std::advance(current_touch_position, 1);
}
break;
}
case InputCommon::CemuhookUDP::Type::PortInfo:
case InputCommon::CemuhookUDP::Type::Version:
default:
break;
}
}
});
}
private:
boost::asio::io_service io_service;
boost::asio::ip::udp::socket socket;
std::array<u8, InputCommon::CemuhookUDP::MAX_PACKET_SIZE> send_buffer;
std::array<u8, InputCommon::CemuhookUDP::MAX_PACKET_SIZE> receive_buffer;
bool is_running = false;
std::thread handler;
};
TEST_CASE("CalibrationConfigurationJob completed", "[input_common]") {
Common::Event complete_event;
FakeCemuhookServer server;
server.Run({{
.is_active = 1,
.x = 0,
.y = 0,
},
{
.is_active = 1,
.x = 200,
.y = 200,
}});
InputCommon::CemuhookUDP::CalibrationConfigurationJob::Status status{};
u16 min_x{};
u16 min_y{};
u16 max_x{};
u16 max_y{};
InputCommon::CemuhookUDP::CalibrationConfigurationJob job(
server.GetHost(), server.GetPort(),
[&status,
&complete_event](InputCommon::CemuhookUDP::CalibrationConfigurationJob::Status status_) {
status = status_;
if (status ==
InputCommon::CemuhookUDP::CalibrationConfigurationJob::Status::Completed) {
complete_event.Set();
}
},
[&](u16 min_x_, u16 min_y_, u16 max_x_, u16 max_y_) {
min_x = min_x_;
min_y = min_y_;
max_x = max_x_;
max_y = max_y_;
});
complete_event.WaitUntil(std::chrono::system_clock::now() + std::chrono::seconds(10));
REQUIRE(status == InputCommon::CemuhookUDP::CalibrationConfigurationJob::Status::Completed);
REQUIRE(min_x == 0);
REQUIRE(min_y == 0);
REQUIRE(max_x == 200);
REQUIRE(max_y == 200);
}

View File

@@ -32,7 +32,7 @@ constexpr std::array PREFERRED_GPU_DECODERS = {
#ifdef _WIN32
AV_HWDEVICE_TYPE_D3D11VA,
AV_HWDEVICE_TYPE_DXVA2,
#elif defined(__linux__)
#elif defined(__unix__)
AV_HWDEVICE_TYPE_VAAPI,
AV_HWDEVICE_TYPE_VDPAU,
#endif

View File

@@ -182,17 +182,13 @@ Device::Device() {
shader_backend = Settings::ShaderBackend::GLSL;
}
if (shader_backend == Settings::ShaderBackend::GLSL && is_nvidia &&
!Settings::values.renderer_debug) {
if (shader_backend == Settings::ShaderBackend::GLSL && is_nvidia) {
const std::string_view driver_version = version.substr(13);
const int version_major =
std::atoi(driver_version.substr(0, driver_version.find(".")).data());
if (version_major >= 495) {
LOG_WARNING(Render_OpenGL, "NVIDIA drivers 495 and later causes significant problems "
"with yuzu. Forcing GLASM as a mitigation.");
shader_backend = Settings::ShaderBackend::GLASM;
use_assembly_shaders = true;
has_cbuf_ftou_bug = true;
has_bool_ref_bug = true;
}
}

View File

@@ -152,6 +152,14 @@ public:
return need_fastmath_off;
}
bool HasCbufFtouBug() const {
return has_cbuf_ftou_bug;
}
bool HasBoolRefBug() const {
return has_bool_ref_bug;
}
Settings::ShaderBackend GetShaderBackend() const {
return shader_backend;
}
@@ -200,6 +208,8 @@ private:
bool has_sparse_texture_2{};
bool warp_size_potentially_larger_than_guest{};
bool need_fastmath_off{};
bool has_cbuf_ftou_bug{};
bool has_bool_ref_bug{};
std::string vendor_name;
};

View File

@@ -42,6 +42,7 @@ namespace {
using Shader::Backend::GLASM::EmitGLASM;
using Shader::Backend::GLSL::EmitGLSL;
using Shader::Backend::SPIRV::EmitSPIRV;
using Shader::Maxwell::ConvertLegacyToGeneric;
using Shader::Maxwell::MergeDualVertexPrograms;
using Shader::Maxwell::TranslateProgram;
using VideoCommon::ComputeEnvironment;
@@ -213,6 +214,8 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo
.has_broken_fp16_float_controls = false,
.has_gl_component_indexing_bug = device.HasComponentIndexingBug(),
.has_gl_precise_bug = device.HasPreciseBug(),
.has_gl_cbuf_ftou_bug = device.HasCbufFtouBug(),
.has_gl_bool_ref_bug = device.HasBoolRefBug(),
.ignore_nan_fp_comparisons = true,
.gl_max_compute_smem_size = device.GetMaxComputeSharedMemorySize(),
},
@@ -422,6 +425,11 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
const u32 cfg_offset{static_cast<u32>(env.StartAddress() + sizeof(Shader::ProgramHeader))};
Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0);
if (Settings::values.dump_shaders) {
env.Dump(key.unique_hashes[index]);
}
if (!uses_vertex_a || index != 1) {
// Normal path
programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info);
@@ -462,12 +470,14 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
MakeRuntimeInfo(key, program, previous_program, glasm_use_storage_buffers, use_glasm)};
switch (device.GetShaderBackend()) {
case Settings::ShaderBackend::GLSL:
ConvertLegacyToGeneric(program, runtime_info);
sources[stage_index] = EmitGLSL(profile, runtime_info, program, binding);
break;
case Settings::ShaderBackend::GLASM:
sources[stage_index] = EmitGLASM(profile, runtime_info, program, binding);
break;
case Settings::ShaderBackend::SPIRV:
ConvertLegacyToGeneric(program, runtime_info);
sources_spirv[stage_index] = EmitSPIRV(profile, runtime_info, program, binding);
break;
}
@@ -506,8 +516,12 @@ std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
LOG_INFO(Render_OpenGL, "0x{:016x}", key.Hash());
Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
if (Settings::values.dump_shaders) {
env.Dump(key.Hash());
}
auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
const u32 num_storage_buffers{Shader::NumDescriptors(program.info.storage_buffers_descriptors)};
Shader::RuntimeInfo info;
info.glasm_use_storage_buffers = num_storage_buffers <= device.GetMaxGLASMStorageBufferBlocks();

View File

@@ -1047,7 +1047,7 @@ bool Image::ScaleDown(bool ignore) {
}
ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewInfo& info,
ImageId image_id_, Image& image)
ImageId image_id_, Image& image, const SlotVector<Image>&)
: VideoCommon::ImageViewBase{info, image.info, image_id_}, views{runtime.null_image_views} {
const Device& device = runtime.device;
if (True(image.flags & ImageFlagBits::Converted)) {

View File

@@ -36,6 +36,7 @@ using VideoCommon::ImageViewType;
using VideoCommon::NUM_RT;
using VideoCommon::Region2D;
using VideoCommon::RenderTargets;
using VideoCommon::SlotVector;
struct ImageBufferMap {
~ImageBufferMap();
@@ -92,7 +93,7 @@ public:
void ReinterpretImage(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies);
void ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view, bool rescaled) {
void ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view) {
UNIMPLEMENTED();
}
@@ -234,7 +235,8 @@ class ImageView : public VideoCommon::ImageViewBase {
friend Image;
public:
explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageViewInfo&, ImageId, Image&);
explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageViewInfo&, ImageId, Image&,
const SlotVector<Image>&);
explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo&,
const VideoCommon::ImageViewInfo&, GPUVAddr);
explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo& info,

View File

@@ -437,39 +437,29 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
glBindTextureUnit(0, fxaa_texture.handle);
}
// Set projection matrix
const std::array ortho_matrix =
MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height));
GLuint fragment_handle;
const auto filter = Settings::values.scaling_filter.GetValue();
switch (filter) {
case Settings::ScalingFilter::NearestNeighbor:
fragment_handle = present_bilinear_fragment.handle;
break;
case Settings::ScalingFilter::Bilinear:
fragment_handle = present_bilinear_fragment.handle;
break;
case Settings::ScalingFilter::Bicubic:
fragment_handle = present_bicubic_fragment.handle;
break;
case Settings::ScalingFilter::Gaussian:
fragment_handle = present_gaussian_fragment.handle;
break;
case Settings::ScalingFilter::ScaleForce:
fragment_handle = present_scaleforce_fragment.handle;
break;
case Settings::ScalingFilter::Fsr:
LOG_WARNING(
Render_OpenGL,
"FidelityFX FSR Super Sampling is not supported in OpenGL, changing to ScaleForce");
fragment_handle = present_scaleforce_fragment.handle;
break;
default:
fragment_handle = present_bilinear_fragment.handle;
break;
}
const auto fragment_handle = [this]() {
switch (Settings::values.scaling_filter.GetValue()) {
case Settings::ScalingFilter::NearestNeighbor:
case Settings::ScalingFilter::Bilinear:
return present_bilinear_fragment.handle;
case Settings::ScalingFilter::Bicubic:
return present_bicubic_fragment.handle;
case Settings::ScalingFilter::Gaussian:
return present_gaussian_fragment.handle;
case Settings::ScalingFilter::ScaleForce:
return present_scaleforce_fragment.handle;
case Settings::ScalingFilter::Fsr:
LOG_WARNING(
Render_OpenGL,
"FidelityFX Super Resolution is not supported in OpenGL, changing to ScaleForce");
return present_scaleforce_fragment.handle;
default:
return present_bilinear_fragment.handle;
}
}();
program_manager.BindPresentPrograms(present_vertex.handle, fragment_handle);
glProgramUniformMatrix3x2fv(present_vertex.handle, ModelViewMatrixLocation, 1, GL_FALSE,
ortho_matrix.data());

View File

@@ -4,6 +4,7 @@
#include <algorithm>
#include "common/settings.h"
#include "video_core/host_shaders/convert_abgr8_to_d24s8_frag_spv.h"
#include "video_core/host_shaders/convert_d24s8_to_abgr8_frag_spv.h"
#include "video_core/host_shaders/convert_depth_to_float_frag_spv.h"
@@ -335,6 +336,17 @@ void BindBlitState(vk::CommandBuffer cmdbuf, VkPipelineLayout layout, const Regi
cmdbuf.SetScissor(0, scissor);
cmdbuf.PushConstants(layout, VK_SHADER_STAGE_VERTEX_BIT, push_constants);
}
VkExtent2D GetConversionExtent(const ImageView& src_image_view) {
const auto& resolution = Settings::values.resolution_info;
const bool is_rescaled = src_image_view.IsRescaled();
u32 width = src_image_view.size.width;
u32 height = src_image_view.size.height;
return VkExtent2D{
.width = is_rescaled ? resolution.ScaleUp(width) : width,
.height = is_rescaled ? resolution.ScaleUp(height) : height,
};
}
} // Anonymous namespace
BlitImageHelper::BlitImageHelper(const Device& device_, VKScheduler& scheduler_,
@@ -425,108 +437,52 @@ void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer,
}
void BlitImageHelper::ConvertD32ToR32(const Framebuffer* dst_framebuffer,
const ImageView& src_image_view, u32 up_scale,
u32 down_shift) {
const ImageView& src_image_view) {
ConvertDepthToColorPipeline(convert_d32_to_r32_pipeline, dst_framebuffer->RenderPass());
Convert(*convert_d32_to_r32_pipeline, dst_framebuffer, src_image_view, up_scale, down_shift);
Convert(*convert_d32_to_r32_pipeline, dst_framebuffer, src_image_view);
}
void BlitImageHelper::ConvertR32ToD32(const Framebuffer* dst_framebuffer,
const ImageView& src_image_view, u32 up_scale,
u32 down_shift) {
const ImageView& src_image_view) {
ConvertColorToDepthPipeline(convert_r32_to_d32_pipeline, dst_framebuffer->RenderPass());
Convert(*convert_r32_to_d32_pipeline, dst_framebuffer, src_image_view, up_scale, down_shift);
Convert(*convert_r32_to_d32_pipeline, dst_framebuffer, src_image_view);
}
void BlitImageHelper::ConvertD16ToR16(const Framebuffer* dst_framebuffer,
const ImageView& src_image_view, u32 up_scale,
u32 down_shift) {
const ImageView& src_image_view) {
ConvertDepthToColorPipeline(convert_d16_to_r16_pipeline, dst_framebuffer->RenderPass());
Convert(*convert_d16_to_r16_pipeline, dst_framebuffer, src_image_view, up_scale, down_shift);
Convert(*convert_d16_to_r16_pipeline, dst_framebuffer, src_image_view);
}
void BlitImageHelper::ConvertR16ToD16(const Framebuffer* dst_framebuffer,
const ImageView& src_image_view, u32 up_scale,
u32 down_shift) {
const ImageView& src_image_view) {
ConvertColorToDepthPipeline(convert_r16_to_d16_pipeline, dst_framebuffer->RenderPass());
Convert(*convert_r16_to_d16_pipeline, dst_framebuffer, src_image_view, up_scale, down_shift);
Convert(*convert_r16_to_d16_pipeline, dst_framebuffer, src_image_view);
}
void BlitImageHelper::ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer,
ImageView& src_image_view, u32 up_scale, u32 down_shift) {
const ImageView& src_image_view) {
ConvertPipelineDepthTargetEx(convert_abgr8_to_d24s8_pipeline, dst_framebuffer->RenderPass(),
convert_abgr8_to_d24s8_frag, true);
ConvertColor(*convert_abgr8_to_d24s8_pipeline, dst_framebuffer, src_image_view, up_scale,
down_shift);
convert_abgr8_to_d24s8_frag);
Convert(*convert_abgr8_to_d24s8_pipeline, dst_framebuffer, src_image_view);
}
void BlitImageHelper::ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer,
ImageView& src_image_view, u32 up_scale, u32 down_shift) {
ImageView& src_image_view) {
ConvertPipelineColorTargetEx(convert_d24s8_to_abgr8_pipeline, dst_framebuffer->RenderPass(),
convert_d24s8_to_abgr8_frag, false);
ConvertDepthStencil(*convert_d24s8_to_abgr8_pipeline, dst_framebuffer, src_image_view, up_scale,
down_shift);
convert_d24s8_to_abgr8_frag);
ConvertDepthStencil(*convert_d24s8_to_abgr8_pipeline, dst_framebuffer, src_image_view);
}
void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
const ImageView& src_image_view, u32 up_scale, u32 down_shift) {
const ImageView& src_image_view) {
const VkPipelineLayout layout = *one_texture_pipeline_layout;
const VkImageView src_view = src_image_view.Handle(Shader::TextureType::Color2D);
const VkSampler sampler = *nearest_sampler;
const VkExtent2D extent{
.width = std::max((src_image_view.size.width * up_scale) >> down_shift, 1U),
.height = std::max((src_image_view.size.height * up_scale) >> down_shift, 1U),
};
scheduler.RequestRenderpass(dst_framebuffer);
scheduler.Record([pipeline, layout, sampler, src_view, extent, up_scale, down_shift,
this](vk::CommandBuffer cmdbuf) {
const VkOffset2D offset{
.x = 0,
.y = 0,
};
const VkViewport viewport{
.x = 0.0f,
.y = 0.0f,
.width = static_cast<float>(extent.width),
.height = static_cast<float>(extent.height),
.minDepth = 0.0f,
.maxDepth = 0.0f,
};
const VkRect2D scissor{
.offset = offset,
.extent = extent,
};
const PushConstants push_constants{
.tex_scale = {viewport.width, viewport.height},
.tex_offset = {0.0f, 0.0f},
};
const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit();
UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view);
const VkExtent2D extent = GetConversionExtent(src_image_view);
// TODO: Barriers
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
nullptr);
cmdbuf.SetViewport(0, viewport);
cmdbuf.SetScissor(0, scissor);
cmdbuf.PushConstants(layout, VK_SHADER_STAGE_VERTEX_BIT, push_constants);
cmdbuf.Draw(3, 1, 0, 0);
});
scheduler.InvalidateState();
}
void BlitImageHelper::ConvertColor(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
ImageView& src_image_view, u32 up_scale, u32 down_shift) {
const VkPipelineLayout layout = *one_texture_pipeline_layout;
const VkImageView src_view = src_image_view.ColorView();
const VkSampler sampler = *nearest_sampler;
const VkExtent2D extent{
.width = std::max((src_image_view.size.width * up_scale) >> down_shift, 1U),
.height = std::max((src_image_view.size.height * up_scale) >> down_shift, 1U),
};
scheduler.RequestRenderpass(dst_framebuffer);
scheduler.Record([pipeline, layout, sampler, src_view, extent, up_scale, down_shift,
this](vk::CommandBuffer cmdbuf) {
scheduler.Record([pipeline, layout, sampler, src_view, extent, this](vk::CommandBuffer cmdbuf) {
const VkOffset2D offset{
.x = 0,
.y = 0,
@@ -563,18 +519,16 @@ void BlitImageHelper::ConvertColor(VkPipeline pipeline, const Framebuffer* dst_f
}
void BlitImageHelper::ConvertDepthStencil(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
ImageView& src_image_view, u32 up_scale, u32 down_shift) {
ImageView& src_image_view) {
const VkPipelineLayout layout = *two_textures_pipeline_layout;
const VkImageView src_depth_view = src_image_view.DepthView();
const VkImageView src_stencil_view = src_image_view.StencilView();
const VkSampler sampler = *nearest_sampler;
const VkExtent2D extent{
.width = std::max((src_image_view.size.width * up_scale) >> down_shift, 1U),
.height = std::max((src_image_view.size.height * up_scale) >> down_shift, 1U),
};
const VkExtent2D extent = GetConversionExtent(src_image_view);
scheduler.RequestRenderpass(dst_framebuffer);
scheduler.Record([pipeline, layout, sampler, src_depth_view, src_stencil_view, extent, up_scale,
down_shift, this](vk::CommandBuffer cmdbuf) {
scheduler.Record([pipeline, layout, sampler, src_depth_view, src_stencil_view, extent,
this](vk::CommandBuffer cmdbuf) {
const VkOffset2D offset{
.x = 0,
.y = 0,
@@ -695,11 +649,14 @@ VkPipeline BlitImageHelper::FindOrEmplaceDepthStencilPipeline(const BlitImagePip
return *blit_depth_stencil_pipelines.back();
}
void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass) {
void BlitImageHelper::ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass,
bool is_target_depth) {
if (pipeline) {
return;
}
const std::array stages = MakeStages(*full_screen_vert, *convert_depth_to_float_frag);
VkShaderModule frag_shader =
is_target_depth ? *convert_float_to_depth_frag : *convert_depth_to_float_frag;
const std::array stages = MakeStages(*full_screen_vert, frag_shader);
pipeline = device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr,
@@ -712,8 +669,9 @@ void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRend
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.pDepthStencilState = nullptr,
.pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO,
.pDepthStencilState = is_target_depth ? &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO : nullptr,
.pColorBlendState = is_target_depth ? &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO
: &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO,
.pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.layout = *one_texture_pipeline_layout,
.renderPass = renderpass,
@@ -723,37 +681,17 @@ void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRend
});
}
void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass) {
ConvertPipeline(pipeline, renderpass, false);
}
void BlitImageHelper::ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass) {
if (pipeline) {
return;
}
const std::array stages = MakeStages(*full_screen_vert, *convert_float_to_depth_frag);
pipeline = device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.stageCount = static_cast<u32>(stages.size()),
.pStages = stages.data(),
.pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.pInputAssemblyState = &PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.pTessellationState = nullptr,
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.pDepthStencilState = &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
.pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO,
.pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.layout = *one_texture_pipeline_layout,
.renderPass = renderpass,
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
});
ConvertPipeline(pipeline, renderpass, true);
}
void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
vk::ShaderModule& module, bool is_target_depth,
bool single_texture) {
vk::ShaderModule& module, bool single_texture,
bool is_target_depth) {
if (pipeline) {
return;
}
@@ -782,13 +720,13 @@ void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass ren
}
void BlitImageHelper::ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
vk::ShaderModule& module, bool single_texture) {
ConvertPipelineEx(pipeline, renderpass, module, false, single_texture);
vk::ShaderModule& module) {
ConvertPipelineEx(pipeline, renderpass, module, false, false);
}
void BlitImageHelper::ConvertPipelineDepthTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
vk::ShaderModule& module, bool single_texture) {
ConvertPipelineEx(pipeline, renderpass, module, true, single_texture);
vk::ShaderModule& module) {
ConvertPipelineEx(pipeline, renderpass, module, true, true);
}
} // namespace Vulkan

View File

@@ -44,50 +44,43 @@ public:
const Region2D& src_region, Tegra::Engines::Fermi2D::Filter filter,
Tegra::Engines::Fermi2D::Operation operation);
void ConvertD32ToR32(const Framebuffer* dst_framebuffer, const ImageView& src_image_view,
u32 up_scale, u32 down_shift);
void ConvertD32ToR32(const Framebuffer* dst_framebuffer, const ImageView& src_image_view);
void ConvertR32ToD32(const Framebuffer* dst_framebuffer, const ImageView& src_image_view,
u32 up_scale, u32 down_shift);
void ConvertR32ToD32(const Framebuffer* dst_framebuffer, const ImageView& src_image_view);
void ConvertD16ToR16(const Framebuffer* dst_framebuffer, const ImageView& src_image_view,
u32 up_scale, u32 down_shift);
void ConvertD16ToR16(const Framebuffer* dst_framebuffer, const ImageView& src_image_view);
void ConvertR16ToD16(const Framebuffer* dst_framebuffer, const ImageView& src_image_view,
u32 up_scale, u32 down_shift);
void ConvertR16ToD16(const Framebuffer* dst_framebuffer, const ImageView& src_image_view);
void ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer, ImageView& src_image_view,
u32 up_scale, u32 down_shift);
void ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer, const ImageView& src_image_view);
void ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view,
u32 up_scale, u32 down_shift);
void ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view);
private:
void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
const ImageView& src_image_view, u32 up_scale, u32 down_shift);
void ConvertColor(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
ImageView& src_image_view, u32 up_scale, u32 down_shift);
const ImageView& src_image_view);
void ConvertDepthStencil(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
ImageView& src_image_view, u32 up_scale, u32 down_shift);
ImageView& src_image_view);
[[nodiscard]] VkPipeline FindOrEmplaceColorPipeline(const BlitImagePipelineKey& key);
[[nodiscard]] VkPipeline FindOrEmplaceDepthStencilPipeline(const BlitImagePipelineKey& key);
void ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass, bool is_target_depth);
void ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass);
void ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass);
void ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
vk::ShaderModule& module, bool is_target_depth, bool single_texture);
vk::ShaderModule& module, bool single_texture, bool is_target_depth);
void ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
vk::ShaderModule& module, bool single_texture);
vk::ShaderModule& module);
void ConvertPipelineDepthTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
vk::ShaderModule& module, bool single_texture);
vk::ShaderModule& module);
const Device& device;
VKScheduler& scheduler;

View File

@@ -391,28 +391,23 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
.offset = {0, 0},
.extent = size,
};
const auto filter = Settings::values.scaling_filter.GetValue();
cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
switch (filter) {
case Settings::ScalingFilter::NearestNeighbor:
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *bilinear_pipeline);
break;
case Settings::ScalingFilter::Bilinear:
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *bilinear_pipeline);
break;
case Settings::ScalingFilter::Bicubic:
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *bicubic_pipeline);
break;
case Settings::ScalingFilter::Gaussian:
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *gaussian_pipeline);
break;
case Settings::ScalingFilter::ScaleForce:
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *scaleforce_pipeline);
break;
default:
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *bilinear_pipeline);
break;
}
auto graphics_pipeline = [this]() {
switch (Settings::values.scaling_filter.GetValue()) {
case Settings::ScalingFilter::NearestNeighbor:
case Settings::ScalingFilter::Bilinear:
return *bilinear_pipeline;
case Settings::ScalingFilter::Bicubic:
return *bicubic_pipeline;
case Settings::ScalingFilter::Gaussian:
return *gaussian_pipeline;
case Settings::ScalingFilter::ScaleForce:
return *scaleforce_pipeline;
default:
return *bilinear_pipeline;
}
}();
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline);
cmdbuf.SetViewport(0, viewport);
cmdbuf.SetScissor(0, scissor);

View File

@@ -605,7 +605,11 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
.flags = 0,
.topology = input_assembly_topology,
.primitiveRestartEnable = key.state.primitive_restart_enable != 0 &&
SupportsPrimitiveRestart(input_assembly_topology),
((input_assembly_topology != VK_PRIMITIVE_TOPOLOGY_PATCH_LIST &&
device.IsTopologyListPrimitiveRestartSupported()) ||
SupportsPrimitiveRestart(input_assembly_topology) ||
(input_assembly_topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST &&
device.IsPatchListPrimitiveRestartSupported())),
};
const VkPipelineTessellationStateCreateInfo tessellation_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO,
@@ -613,7 +617,6 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
.flags = 0,
.patchControlPoints = key.state.patch_control_points_minus_one.Value() + 1,
};
std::array<VkViewportSwizzleNV, Maxwell::NumViewports> swizzles;
std::ranges::transform(key.state.viewport_swizzles, swizzles.begin(), UnpackViewportSwizzle);
const VkPipelineViewportSwizzleStateCreateInfoNV swizzle_ci{
@@ -748,8 +751,8 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.logicOpEnable = VK_FALSE,
.logicOp = VK_LOGIC_OP_COPY,
.logicOpEnable = key.state.logic_op_enable != 0,
.logicOp = static_cast<VkLogicOp>(key.state.logic_op.Value()),
.attachmentCount = static_cast<u32>(cb_attachments.size()),
.pAttachments = cb_attachments.data(),
.blendConstants = {},

View File

@@ -48,6 +48,7 @@ MICROPROFILE_DECLARE(Vulkan_PipelineCache);
namespace {
using Shader::Backend::SPIRV::EmitSPIRV;
using Shader::Maxwell::ConvertLegacyToGeneric;
using Shader::Maxwell::MergeDualVertexPrograms;
using Shader::Maxwell::TranslateProgram;
using VideoCommon::ComputeEnvironment;
@@ -516,6 +517,9 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
const u32 cfg_offset{static_cast<u32>(env.StartAddress() + sizeof(Shader::ProgramHeader))};
Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0);
if (Settings::values.dump_shaders) {
env.Dump(key.unique_hashes[index]);
}
if (!uses_vertex_a || index != 1) {
// Normal path
programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info);
@@ -543,6 +547,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
infos[stage_index] = &program.info;
const auto runtime_info{MakeRuntimeInfo(programs, key, program, previous_stage)};
ConvertLegacyToGeneric(program, runtime_info);
const std::vector<u32> code{EmitSPIRV(profile, runtime_info, program, binding)};
device.SaveShader(code);
modules[stage_index] = BuildShader(device, code);
@@ -611,6 +616,12 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
LOG_INFO(Render_Vulkan, "0x{:016x}", key.Hash());
Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
// Dump it before error.
if (Settings::values.dump_shaders) {
env.Dump(key.Hash());
}
auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
const std::vector<u32> code{EmitSPIRV(profile, program)};
device.SaveShader(code);

View File

@@ -1057,37 +1057,37 @@ void TextureCacheRuntime::BlitImage(Framebuffer* dst_framebuffer, ImageView& dst
});
}
void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view,
bool rescaled) {
const u32 up_scale = rescaled ? resolution.up_scale : 1;
const u32 down_shift = rescaled ? resolution.down_shift : 0;
void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view) {
switch (dst_view.format) {
case PixelFormat::R16_UNORM:
if (src_view.format == PixelFormat::D16_UNORM) {
return blit_image_helper.ConvertD16ToR16(dst, src_view, up_scale, down_shift);
return blit_image_helper.ConvertD16ToR16(dst, src_view);
}
break;
case PixelFormat::A8B8G8R8_UNORM:
if (src_view.format == PixelFormat::S8_UINT_D24_UNORM) {
return blit_image_helper.ConvertD24S8ToABGR8(dst, src_view, up_scale, down_shift);
return blit_image_helper.ConvertD24S8ToABGR8(dst, src_view);
}
break;
case PixelFormat::R32_FLOAT:
if (src_view.format == PixelFormat::D32_FLOAT) {
return blit_image_helper.ConvertD32ToR32(dst, src_view, up_scale, down_shift);
return blit_image_helper.ConvertD32ToR32(dst, src_view);
}
break;
case PixelFormat::D16_UNORM:
if (src_view.format == PixelFormat::R16_UNORM) {
return blit_image_helper.ConvertR16ToD16(dst, src_view, up_scale, down_shift);
return blit_image_helper.ConvertR16ToD16(dst, src_view);
}
break;
case PixelFormat::S8_UINT_D24_UNORM:
return blit_image_helper.ConvertABGR8ToD24S8(dst, src_view, up_scale, down_shift);
if (src_view.format == PixelFormat::A8B8G8R8_UNORM ||
src_view.format == PixelFormat::B8G8R8A8_UNORM) {
return blit_image_helper.ConvertABGR8ToD24S8(dst, src_view);
}
break;
case PixelFormat::D32_FLOAT:
if (src_view.format == PixelFormat::R32_FLOAT) {
return blit_image_helper.ConvertR32ToD32(dst, src_view, up_scale, down_shift);
return blit_image_helper.ConvertR32ToD32(dst, src_view);
}
break;
default:
@@ -1329,6 +1329,10 @@ void Image::DownloadMemory(const StagingBufferRef& map, std::span<const BufferIm
}
}
bool Image::IsRescaled() const noexcept {
return True(flags & ImageFlagBits::Rescaled);
}
bool Image::ScaleUp(bool ignore) {
if (True(flags & ImageFlagBits::Rescaled)) {
return false;
@@ -1340,7 +1344,6 @@ bool Image::ScaleUp(bool ignore) {
return false;
}
has_scaled = true;
const auto& device = runtime->device;
if (!scaled_image) {
const bool is_2d = info.type == ImageType::e2D;
const u32 scaled_width = resolution.ScaleUp(info.size.width);
@@ -1348,7 +1351,7 @@ bool Image::ScaleUp(bool ignore) {
auto scaled_info = info;
scaled_info.size.width = scaled_width;
scaled_info.size.height = scaled_height;
scaled_image = MakeImage(device, scaled_info);
scaled_image = MakeImage(runtime->device, scaled_info);
auto& allocator = runtime->memory_allocator;
scaled_commit = MemoryCommit(allocator.Commit(scaled_image, MemoryUsage::DeviceLocal));
ignore = false;
@@ -1357,18 +1360,13 @@ bool Image::ScaleUp(bool ignore) {
if (ignore) {
return true;
}
if (aspect_mask == 0) {
aspect_mask = ImageAspectMask(info.format);
}
static constexpr auto OPTIMAL_FORMAT = FormatType::Optimal;
const PixelFormat format = StorageFormat(info.format);
const auto vk_format = MaxwellToVK::SurfaceFormat(device, OPTIMAL_FORMAT, false, format).format;
const auto blit_usage = VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT;
if (device.IsFormatSupported(vk_format, blit_usage, OPTIMAL_FORMAT)) {
BlitScale(*scheduler, *original_image, *scaled_image, info, aspect_mask, resolution);
} else {
if (NeedsScaleHelper()) {
return BlitScaleHelper(true);
} else {
BlitScale(*scheduler, *original_image, *scaled_image, info, aspect_mask, resolution);
}
return true;
}
@@ -1390,15 +1388,10 @@ bool Image::ScaleDown(bool ignore) {
if (aspect_mask == 0) {
aspect_mask = ImageAspectMask(info.format);
}
static constexpr auto OPTIMAL_FORMAT = FormatType::Optimal;
const PixelFormat format = StorageFormat(info.format);
const auto& device = runtime->device;
const auto vk_format = MaxwellToVK::SurfaceFormat(device, OPTIMAL_FORMAT, false, format).format;
const auto blit_usage = VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT;
if (device.IsFormatSupported(vk_format, blit_usage, OPTIMAL_FORMAT)) {
BlitScale(*scheduler, *scaled_image, *original_image, info, aspect_mask, resolution, false);
} else {
if (NeedsScaleHelper()) {
return BlitScaleHelper(false);
} else {
BlitScale(*scheduler, *scaled_image, *original_image, info, aspect_mask, resolution, false);
}
return true;
}
@@ -1466,10 +1459,24 @@ bool Image::BlitScaleHelper(bool scale_up) {
return true;
}
bool Image::NeedsScaleHelper() const {
const auto& device = runtime->device;
const bool needs_msaa_helper = info.num_samples > 1 && device.CantBlitMSAA();
if (needs_msaa_helper) {
return true;
}
static constexpr auto OPTIMAL_FORMAT = FormatType::Optimal;
const PixelFormat format = StorageFormat(info.format);
const auto vk_format = MaxwellToVK::SurfaceFormat(device, OPTIMAL_FORMAT, false, format).format;
const auto blit_usage = VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT;
const bool needs_blit_helper = !device.IsFormatSupported(vk_format, blit_usage, OPTIMAL_FORMAT);
return needs_blit_helper;
}
ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewInfo& info,
ImageId image_id_, Image& image)
: VideoCommon::ImageViewBase{info, image.info, image_id_}, device{&runtime.device},
image_handle{image.Handle()}, samples{ConvertSampleCount(image.info.num_samples)} {
image_handle{image.Handle()}, samples(ConvertSampleCount(image.info.num_samples)) {
using Shader::TextureType;
const VkImageAspectFlags aspect_mask = ImageViewAspectMask(info);
@@ -1552,6 +1559,12 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
}
}
ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewInfo& info,
ImageId image_id_, Image& image, const SlotVector<Image>& slot_imgs)
: ImageView{runtime, info, image_id_, image} {
slot_images = &slot_imgs;
}
ImageView::ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo& info,
const VideoCommon::ImageViewInfo& view_info, GPUVAddr gpu_addr_)
: VideoCommon::ImageViewBase{info, view_info}, gpu_addr{gpu_addr_},
@@ -1607,6 +1620,15 @@ VkImageView ImageView::StorageView(Shader::TextureType texture_type,
return *view;
}
bool ImageView::IsRescaled() const noexcept {
if (!slot_images) {
return false;
}
const auto& slots = *slot_images;
const auto& src_image = slots[image_id];
return src_image.IsRescaled();
}
vk::ImageView ImageView::MakeView(VkFormat vk_format, VkImageAspectFlags aspect_mask) {
return device->GetLogical().CreateImageView({
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,

View File

@@ -23,6 +23,7 @@ using VideoCommon::ImageId;
using VideoCommon::NUM_RT;
using VideoCommon::Region2D;
using VideoCommon::RenderTargets;
using VideoCommon::SlotVector;
using VideoCore::Surface::PixelFormat;
class ASTCDecoderPass;
@@ -65,7 +66,7 @@ public:
void ReinterpretImage(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies);
void ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view, bool rescaled);
void ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view);
bool CanAccelerateImageUpload(Image&) const noexcept {
return false;
@@ -139,6 +140,8 @@ public:
return std::exchange(initialized, true);
}
bool IsRescaled() const noexcept;
bool ScaleUp(bool ignore = false);
bool ScaleDown(bool ignore = false);
@@ -146,6 +149,8 @@ public:
private:
bool BlitScaleHelper(bool scale_up);
bool NeedsScaleHelper() const;
VKScheduler* scheduler{};
TextureCacheRuntime* runtime{};
@@ -168,6 +173,8 @@ private:
class ImageView : public VideoCommon::ImageViewBase {
public:
explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageViewInfo&, ImageId, Image&);
explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageViewInfo&, ImageId, Image&,
const SlotVector<Image>&);
explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo&,
const VideoCommon::ImageViewInfo&, GPUVAddr);
explicit ImageView(TextureCacheRuntime&, const VideoCommon::NullImageViewParams&);
@@ -189,6 +196,8 @@ public:
[[nodiscard]] VkImageView StorageView(Shader::TextureType texture_type,
Shader::ImageFormat image_format);
[[nodiscard]] bool IsRescaled() const noexcept;
[[nodiscard]] VkImageView Handle(Shader::TextureType texture_type) const noexcept {
return *image_views[static_cast<size_t>(texture_type)];
}
@@ -222,6 +231,8 @@ private:
[[nodiscard]] vk::ImageView MakeView(VkFormat vk_format, VkImageAspectFlags aspect_mask);
const Device* device = nullptr;
const SlotVector<Image>* slot_images = nullptr;
std::array<vk::ImageView, Shader::NUM_TEXTURE_TYPES> image_views;
std::unique_ptr<StorageViews> storage_views;
vk::ImageView depth_view;

View File

@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <algorithm>
#include <bit>
#include <filesystem>
#include <fstream>
#include <memory>
@@ -14,6 +15,7 @@
#include "common/common_types.h"
#include "common/div_ceil.h"
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/logging/log.h"
#include "shader_recompiler/environment.h"
#include "video_core/engines/kepler_compute.h"
@@ -57,6 +59,47 @@ static Shader::TextureType ConvertType(const Tegra::Texture::TICEntry& entry) {
}
}
static std::string_view StageToPrefix(Shader::Stage stage) {
switch (stage) {
case Shader::Stage::VertexB:
return "VB";
case Shader::Stage::TessellationControl:
return "TC";
case Shader::Stage::TessellationEval:
return "TE";
case Shader::Stage::Geometry:
return "GS";
case Shader::Stage::Fragment:
return "FS";
case Shader::Stage::Compute:
return "CS";
case Shader::Stage::VertexA:
return "VA";
default:
return "UK";
}
}
static void DumpImpl(u64 hash, const u64* code, u32 read_highest, u32 read_lowest,
u32 initial_offset, Shader::Stage stage) {
const auto shader_dir{Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir)};
const auto base_dir{shader_dir / "shaders"};
if (!Common::FS::CreateDir(shader_dir) || !Common::FS::CreateDir(base_dir)) {
LOG_ERROR(Common_Filesystem, "Failed to create shader dump directories");
return;
}
const auto prefix = StageToPrefix(stage);
const auto name{base_dir / fmt::format("{}{:016x}.ash", prefix, hash)};
const size_t real_size = read_highest - read_lowest + initial_offset;
const size_t padding_needed = ((32 - (real_size % 32)) % 32);
std::fstream shader_file(name, std::ios::out | std::ios::binary);
const size_t jump_index = initial_offset / sizeof(u64);
shader_file.write(reinterpret_cast<const char*>(code + jump_index), real_size);
for (size_t i = 0; i < padding_needed; i++) {
shader_file.put(0);
}
}
GenericEnvironment::GenericEnvironment(Tegra::MemoryManager& gpu_memory_, GPUVAddr program_base_,
u32 start_address_)
: gpu_memory{&gpu_memory_}, program_base{program_base_} {
@@ -128,6 +171,10 @@ u64 GenericEnvironment::CalculateHash() const {
return Common::CityHash64(data.get(), size);
}
void GenericEnvironment::Dump(u64 hash) {
DumpImpl(hash, code.data(), read_highest, read_lowest, initial_offset, stage);
}
void GenericEnvironment::Serialize(std::ofstream& file) const {
const u64 code_size{static_cast<u64>(CachedSize())};
const u64 num_texture_types{static_cast<u64>(texture_types.size())};
@@ -207,6 +254,7 @@ GraphicsEnvironment::GraphicsEnvironment(Tegra::Engines::Maxwell3D& maxwell3d_,
u32 start_address_)
: GenericEnvironment{gpu_memory_, program_base_, start_address_}, maxwell3d{&maxwell3d_} {
gpu_memory->ReadBlock(program_base + start_address, &sph, sizeof(sph));
initial_offset = sizeof(sph);
gp_passthrough_mask = maxwell3d->regs.gp_passthrough_mask;
switch (program) {
case Maxwell::ShaderProgram::VertexA:
@@ -323,14 +371,20 @@ void FileEnvironment::Deserialize(std::ifstream& file) {
if (stage == Shader::Stage::Compute) {
file.read(reinterpret_cast<char*>(&workgroup_size), sizeof(workgroup_size))
.read(reinterpret_cast<char*>(&shared_memory_size), sizeof(shared_memory_size));
initial_offset = 0;
} else {
file.read(reinterpret_cast<char*>(&sph), sizeof(sph));
initial_offset = sizeof(sph);
if (stage == Shader::Stage::Geometry) {
file.read(reinterpret_cast<char*>(&gp_passthrough_mask), sizeof(gp_passthrough_mask));
}
}
}
void FileEnvironment::Dump(u64 [[maybe_unused]] hash) {
DumpImpl(hash, code.get(), read_highest, read_lowest, initial_offset, stage);
}
u64 FileEnvironment::ReadInstruction(u32 address) {
if (address < read_lowest || address > read_highest) {
throw Shader::LogicError("Out of bounds address {}", address);

View File

@@ -57,6 +57,8 @@ public:
[[nodiscard]] u64 CalculateHash() const;
void Dump(u64 hash) override;
void Serialize(std::ofstream& file) const;
protected:
@@ -82,6 +84,7 @@ protected:
u32 cached_lowest = std::numeric_limits<u32>::max();
u32 cached_highest = 0;
u32 initial_offset = 0;
bool has_unbound_instructions = false;
};
@@ -149,6 +152,8 @@ public:
[[nodiscard]] std::array<u32, 3> WorkgroupSize() const override;
void Dump(u64 hash) override;
private:
std::unique_ptr<u64[]> code;
std::unordered_map<u32, Shader::TextureType> texture_types;
@@ -159,6 +164,7 @@ private:
u32 texture_bound{};
u32 read_lowest{};
u32 read_highest{};
u32 initial_offset{};
};
void SerializePipeline(std::span<const char> key, std::span<const GenericEnvironment* const> envs,

View File

@@ -1137,8 +1137,13 @@ typename TextureCache<P>::BlitImages TextureCache<P>::GetBlitImages(
} while (has_deleted_images);
const ImageBase& src_image = slot_images[src_id];
const ImageBase& dst_image = slot_images[dst_id];
const bool native_bgr = runtime.HasNativeBgr();
if (GetFormatType(dst_info.format) != GetFormatType(dst_image.info.format) ||
GetFormatType(src_info.format) != GetFormatType(src_image.info.format)) {
GetFormatType(src_info.format) != GetFormatType(src_image.info.format) ||
!VideoCore::Surface::IsViewCompatible(dst_info.format, dst_image.info.format, false,
native_bgr) ||
!VideoCore::Surface::IsViewCompatible(src_info.format, src_image.info.format, false,
native_bgr)) {
// Make sure the images match the expected format.
do {
has_deleted_images = false;
@@ -1392,7 +1397,8 @@ ImageViewId TextureCache<P>::FindOrEmplaceImageView(ImageId image_id, const Imag
if (const ImageViewId image_view_id = image.FindView(info); image_view_id) {
return image_view_id;
}
const ImageViewId image_view_id = slot_image_views.insert(runtime, info, image_id, image);
const ImageViewId image_view_id =
slot_image_views.insert(runtime, info, image_id, image, slot_images);
image.InsertView(info, image_view_id);
return image_view_id;
}
@@ -1850,9 +1856,20 @@ void TextureCache<P>::CopyImage(ImageId dst_id, ImageId src_id, std::vector<Imag
.height = std::min(dst_view.size.height, src_view.size.height),
.depth = std::min(dst_view.size.depth, src_view.size.depth),
};
UNIMPLEMENTED_IF(copy.extent != expected_size);
const Extent3D scaled_extent = [is_rescaled, expected_size]() {
if (!is_rescaled) {
return expected_size;
}
const auto& resolution = Settings::values.resolution_info;
return Extent3D{
.width = resolution.ScaleUp(expected_size.width),
.height = resolution.ScaleUp(expected_size.height),
.depth = expected_size.depth,
};
}();
UNIMPLEMENTED_IF(copy.extent != scaled_extent);
runtime.ConvertImage(dst_framebuffer, dst_view, src_view, is_rescaled);
runtime.ConvertImage(dst_framebuffer, dst_view, src_view);
}
}

View File

@@ -364,14 +364,14 @@ template <u32 GOB_EXTENT>
[[nodiscard]] std::optional<SubresourceExtent> ResolveOverlapRightAddress2D(
const ImageInfo& new_info, GPUVAddr gpu_addr, const ImageBase& overlap, bool strict_size) {
const u32 layer_stride = new_info.layer_stride;
const s32 new_size = layer_stride * new_info.resources.layers;
const s32 diff = static_cast<s32>(overlap.gpu_addr - gpu_addr);
const u64 layer_stride = new_info.layer_stride;
const u64 new_size = layer_stride * new_info.resources.layers;
const u64 diff = overlap.gpu_addr - gpu_addr;
if (diff > new_size) {
return std::nullopt;
}
const s32 base_layer = diff / layer_stride;
const s32 mip_offset = diff % layer_stride;
const s32 base_layer = static_cast<s32>(diff / layer_stride);
const s32 mip_offset = static_cast<s32>(diff % layer_stride);
const std::array offsets = CalculateMipLevelOffsets(new_info);
const auto end = offsets.begin() + new_info.resources.levels;
const auto it = std::find(offsets.begin(), end, static_cast<u32>(mip_offset));

View File

@@ -271,7 +271,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
.tessellationShader = true,
.sampleRateShading = true,
.dualSrcBlend = true,
.logicOp = false,
.logicOp = true,
.multiDrawIndirect = false,
.drawIndirectFirstInstance = false,
.depthClamp = true,
@@ -433,6 +433,19 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
LOG_INFO(Render_Vulkan, "Device doesn't support uint8 indexes");
}
VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT primitive_topology_list_restart;
if (is_topology_list_restart_supported || is_patch_list_restart_supported) {
primitive_topology_list_restart = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT,
.pNext = nullptr,
.primitiveTopologyListRestart = is_topology_list_restart_supported,
.primitiveTopologyPatchListRestart = is_patch_list_restart_supported,
};
SetNext(next, primitive_topology_list_restart);
} else {
LOG_INFO(Render_Vulkan, "Device doesn't support list topology primitive restart");
}
VkPhysicalDeviceTransformFeedbackFeaturesEXT transform_feedback;
if (ext_transform_feedback) {
transform_feedback = {
@@ -625,15 +638,20 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
}
}
if (ext_vertex_input_dynamic_state && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) {
const bool is_intel_windows = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS;
if (ext_vertex_input_dynamic_state && is_intel_windows) {
LOG_WARNING(Render_Vulkan, "Blacklisting Intel for VK_EXT_vertex_input_dynamic_state");
ext_vertex_input_dynamic_state = false;
}
if (is_float16_supported && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) {
if (is_float16_supported && is_intel_windows) {
// Intel's compiler crashes when using fp16 on Astral Chain, disable it for the time being.
LOG_WARNING(Render_Vulkan, "Blacklisting Intel proprietary from float16 math");
is_float16_supported = false;
}
if (is_intel_windows) {
LOG_WARNING(Render_Vulkan, "Intel proprietary drivers do not support MSAA image blits");
cant_blit_msaa = true;
}
supports_d24_depth =
IsFormatSupported(VK_FORMAT_D24_UNORM_S8_UINT,
@@ -891,6 +909,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
bool has_ext_provoking_vertex{};
bool has_ext_vertex_input_dynamic_state{};
bool has_ext_line_rasterization{};
bool has_ext_primitive_topology_list_restart{};
for (const std::string& extension : supported_extensions) {
const auto test = [&](std::optional<std::reference_wrapper<bool>> status, const char* name,
bool push) {
@@ -915,6 +934,8 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
test(has_khr_shader_float16_int8, VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, false);
test(ext_depth_range_unrestricted, VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME, true);
test(ext_index_type_uint8, VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME, true);
test(has_ext_primitive_topology_list_restart,
VK_EXT_PRIMITIVE_TOPOLOGY_LIST_RESTART_EXTENSION_NAME, true);
test(ext_sampler_filter_minmax, VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME, true);
test(ext_shader_viewport_index_layer, VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME,
true);
@@ -1113,6 +1134,19 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
khr_pipeline_executable_properties = true;
}
}
if (has_ext_primitive_topology_list_restart) {
VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT primitive_topology_list_restart{};
primitive_topology_list_restart.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT;
primitive_topology_list_restart.pNext = nullptr;
features.pNext = &primitive_topology_list_restart;
physical.GetFeatures2KHR(features);
is_topology_list_restart_supported =
primitive_topology_list_restart.primitiveTopologyListRestart;
is_patch_list_restart_supported =
primitive_topology_list_restart.primitiveTopologyPatchListRestart;
}
if (has_khr_image_format_list && has_khr_swapchain_mutable_format) {
extensions.push_back(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME);
extensions.push_back(VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME);

View File

@@ -238,6 +238,16 @@ public:
return khr_workgroup_memory_explicit_layout;
}
/// Returns true if the device supports VK_EXT_primitive_topology_list_restart.
bool IsTopologyListPrimitiveRestartSupported() const {
return is_topology_list_restart_supported;
}
/// Returns true if the device supports VK_EXT_primitive_topology_list_restart.
bool IsPatchListPrimitiveRestartSupported() const {
return is_patch_list_restart_supported;
}
/// Returns true if the device supports VK_EXT_index_type_uint8.
bool IsExtIndexTypeUint8Supported() const {
return ext_index_type_uint8;
@@ -340,6 +350,10 @@ public:
return supports_d24_depth;
}
bool CantBlitMSAA() const {
return cant_blit_msaa;
}
private:
/// Checks if the physical device is suitable.
void CheckSuitability(bool requires_swapchain) const;
@@ -401,6 +415,9 @@ private:
bool is_shader_int16_supported{}; ///< Support for int16.
bool is_shader_storage_image_multisample{}; ///< Support for image operations on MSAA images.
bool is_blit_depth_stencil_supported{}; ///< Support for blitting from and to depth stencil.
bool is_topology_list_restart_supported{}; ///< Support for primitive restart with list
///< topologies.
bool is_patch_list_restart_supported{}; ///< Support for primitive restart with list patch.
bool nv_viewport_swizzle{}; ///< Support for VK_NV_viewport_swizzle.
bool nv_viewport_array2{}; ///< Support for VK_NV_viewport_array2.
bool nv_geometry_shader_passthrough{}; ///< Support for VK_NV_geometry_shader_passthrough.
@@ -430,6 +447,7 @@ private:
bool has_renderdoc{}; ///< Has RenderDoc attached
bool has_nsight_graphics{}; ///< Has Nsight Graphics attached
bool supports_d24_depth{}; ///< Supports D24 depth buffers.
bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting.
// Telemetry parameters
std::string vendor_name; ///< Device's driver name.

View File

@@ -251,6 +251,9 @@ target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include)
if (NOT WIN32)
target_include_directories(yuzu PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
endif()
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
target_link_libraries(yuzu PRIVATE Qt5::DBus)
endif()
target_compile_definitions(yuzu PRIVATE
# Use QStringBuilder for string concatenation to reduce

View File

@@ -12,7 +12,6 @@
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "core/hid/hid_types.h"
#include "core/hle/lock.h"
#include "core/hle/service/hid/controllers/npad.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/sm/sm.h"
@@ -34,7 +33,7 @@ void UpdateController(Core::HID::EmulatedController* controller,
}
controller->SetNpadStyleIndex(controller_type);
if (connected) {
controller->Connect();
controller->Connect(true);
}
}
@@ -664,7 +663,5 @@ void QtControllerSelector::ReconfigureControllers(
}
void QtControllerSelector::MainWindowReconfigureFinished() {
// Acquire the HLE mutex
std::lock_guard lock(HLE::g_hle_lock);
callback();
}

View File

@@ -3,7 +3,6 @@
// Refer to the license.txt file included.
#include <QDateTime>
#include "core/hle/lock.h"
#include "yuzu/applets/qt_error.h"
#include "yuzu/main.h"
@@ -57,7 +56,5 @@ void QtErrorDisplay::ShowCustomErrorText(ResultCode error, std::string dialog_te
}
void QtErrorDisplay::MainWindowFinishedError() {
// Acquire the HLE mutex
std::lock_guard lock{HLE::g_hle_lock};
callback();
}

View File

@@ -14,7 +14,6 @@
#include "common/fs/path_util.h"
#include "common/string_util.h"
#include "core/constants.h"
#include "core/hle/lock.h"
#include "yuzu/applets/qt_profile_select.h"
#include "yuzu/main.h"
#include "yuzu/util/controller_navigation.h"
@@ -170,7 +169,5 @@ void QtProfileSelector::SelectProfile(
}
void QtProfileSelector::MainWindowFinishedSelection(std::optional<Common::UUID> uuid) {
// Acquire the HLE mutex
std::lock_guard lock{HLE::g_hle_lock};
callback(uuid);
}

View File

@@ -776,6 +776,7 @@ void Config::ReadUIGamelistValues() {
ReadBasicSetting(UISettings::values.row_1_text_id);
ReadBasicSetting(UISettings::values.row_2_text_id);
ReadBasicSetting(UISettings::values.cache_game_list);
ReadBasicSetting(UISettings::values.favorites_expanded);
const int favorites_size = qt_config->beginReadArray(QStringLiteral("favorites"));
for (int i = 0; i < favorites_size; i++) {
qt_config->setArrayIndex(i);
@@ -1300,6 +1301,7 @@ void Config::SaveUIGamelistValues() {
WriteBasicSetting(UISettings::values.row_1_text_id);
WriteBasicSetting(UISettings::values.row_2_text_id);
WriteBasicSetting(UISettings::values.cache_game_list);
WriteBasicSetting(UISettings::values.favorites_expanded);
qt_config->beginWriteArray(QStringLiteral("favorites"));
for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) {
qt_config->setArrayIndex(i);

View File

@@ -51,6 +51,8 @@ void ConfigureDebug::SetConfiguration() {
ui->enable_cpu_debugging->setChecked(Settings::values.cpu_debug_mode.GetValue());
ui->enable_nsight_aftermath->setEnabled(runtime_lock);
ui->enable_nsight_aftermath->setChecked(Settings::values.enable_nsight_aftermath.GetValue());
ui->dump_shaders->setEnabled(runtime_lock);
ui->dump_shaders->setChecked(Settings::values.dump_shaders.GetValue());
ui->disable_macro_jit->setEnabled(runtime_lock);
ui->disable_macro_jit->setChecked(Settings::values.disable_macro_jit.GetValue());
ui->disable_loop_safety_checks->setEnabled(runtime_lock);
@@ -73,6 +75,7 @@ void ConfigureDebug::ApplyConfiguration() {
Settings::values.renderer_shader_feedback = ui->enable_shader_feedback->isChecked();
Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked();
Settings::values.enable_nsight_aftermath = ui->enable_nsight_aftermath->isChecked();
Settings::values.dump_shaders = ui->dump_shaders->isChecked();
Settings::values.disable_shader_loop_safety_checks =
ui->disable_loop_safety_checks->isChecked();
Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked();

View File

@@ -105,6 +105,19 @@
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="dump_shaders">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>When checked, it will dump all the original assembler shaders from the disk shader cache or game as found</string>
</property>
<property name="text">
<string>Dump Game Shaders</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="disable_macro_jit">
<property name="enabled">

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