summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.ci/scripts/common/post-upload.sh4
-rw-r--r--.ci/scripts/common/pre-upload.sh3
-rwxr-xr-x.ci/scripts/linux/docker.sh39
-rw-r--r--.ci/scripts/linux/upload.sh21
-rw-r--r--CMakeLists.txt10
-rw-r--r--README.md1
m---------externals/dynarmic0
-rw-r--r--src/CMakeLists.txt9
-rw-r--r--src/audio_core/sink_context.h6
-rw-r--r--src/common/CMakeLists.txt6
-rw-r--r--src/common/alignment.h29
-rw-r--r--src/common/bit_util.h76
-rw-r--r--src/common/color.h271
-rw-r--r--src/common/common_funcs.h16
-rw-r--r--src/common/div_ceil.h10
-rw-r--r--src/common/intrusive_red_black_tree.h602
-rw-r--r--src/common/logging/backend.cpp16
-rw-r--r--src/common/page_table.h2
-rw-r--r--src/common/parent_of_member.h191
-rw-r--r--src/common/swap.h4
-rw-r--r--src/common/timer.cpp159
-rw-r--r--src/common/timer.h41
-rw-r--r--src/common/tree.h674
-rw-r--r--src/core/CMakeLists.txt20
-rw-r--r--src/core/arm/arm_interface.h7
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp12
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp3
-rw-r--r--src/core/core_timing.cpp1
-rw-r--r--src/core/crypto/key_manager.cpp11
-rw-r--r--src/core/file_sys/content_archive.cpp24
-rw-r--r--src/core/file_sys/nca_patch.cpp2
-rw-r--r--src/core/file_sys/registered_cache.cpp3
-rw-r--r--src/core/file_sys/registered_cache.h8
-rw-r--r--src/core/hle/ipc.h4
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp317
-rw-r--r--src/core/hle/kernel/address_arbiter.h91
-rw-r--r--src/core/hle/kernel/client_port.cpp3
-rw-r--r--src/core/hle/kernel/client_session.cpp11
-rw-r--r--src/core/hle/kernel/client_session.h8
-rw-r--r--src/core/hle/kernel/errors.h3
-rw-r--r--src/core/hle/kernel/k_address_arbiter.cpp367
-rw-r--r--src/core/hle/kernel/k_address_arbiter.h70
-rw-r--r--src/core/hle/kernel/k_condition_variable.cpp349
-rw-r--r--src/core/hle/kernel/k_condition_variable.h59
-rw-r--r--src/core/hle/kernel/k_priority_queue.h4
-rw-r--r--src/core/hle/kernel/k_scheduler.cpp45
-rw-r--r--src/core/hle/kernel/k_scheduler.h5
-rw-r--r--src/core/hle/kernel/k_scheduler_lock.h2
-rw-r--r--src/core/hle/kernel/k_synchronization_object.cpp172
-rw-r--r--src/core/hle/kernel/k_synchronization_object.h58
-rw-r--r--src/core/hle/kernel/kernel.cpp19
-rw-r--r--src/core/hle/kernel/kernel.h7
-rw-r--r--src/core/hle/kernel/memory/address_space_info.cpp2
-rw-r--r--src/core/hle/kernel/memory/memory_block.h14
-rw-r--r--src/core/hle/kernel/memory/memory_layout.h19
-rw-r--r--src/core/hle/kernel/memory/page_heap.h4
-rw-r--r--src/core/hle/kernel/memory/page_table.cpp12
-rw-r--r--src/core/hle/kernel/mutex.cpp170
-rw-r--r--src/core/hle/kernel/mutex.h42
-rw-r--r--src/core/hle/kernel/object.h5
-rw-r--r--src/core/hle/kernel/process.cpp67
-rw-r--r--src/core/hle/kernel/process.h64
-rw-r--r--src/core/hle/kernel/process_capability.cpp4
-rw-r--r--src/core/hle/kernel/readable_event.cpp18
-rw-r--r--src/core/hle/kernel/readable_event.h12
-rw-r--r--src/core/hle/kernel/server_port.cpp14
-rw-r--r--src/core/hle/kernel/server_port.h7
-rw-r--r--src/core/hle/kernel/server_session.cpp23
-rw-r--r--src/core/hle/kernel/server_session.h12
-rw-r--r--src/core/hle/kernel/session.cpp11
-rw-r--r--src/core/hle/kernel/session.h8
-rw-r--r--src/core/hle/kernel/svc.cpp397
-rw-r--r--src/core/hle/kernel/svc_common.h14
-rw-r--r--src/core/hle/kernel/svc_results.h20
-rw-r--r--src/core/hle/kernel/svc_types.h16
-rw-r--r--src/core/hle/kernel/svc_wrap.h47
-rw-r--r--src/core/hle/kernel/synchronization.cpp116
-rw-r--r--src/core/hle/kernel/synchronization.h44
-rw-r--r--src/core/hle/kernel/synchronization_object.cpp49
-rw-r--r--src/core/hle/kernel/synchronization_object.h77
-rw-r--r--src/core/hle/kernel/thread.cpp328
-rw-r--r--src/core/hle/kernel/thread.h497
-rw-r--r--src/core/hle/kernel/time_manager.cpp9
-rw-r--r--src/core/hle/service/am/am.cpp6
-rw-r--r--src/core/hle/service/am/am.h2
-rw-r--r--src/core/hle/service/am/applets/error.cpp10
-rw-r--r--src/core/hle/service/apm/interface.cpp10
-rw-r--r--src/core/hle/service/apm/interface.h1
-rw-r--r--src/core/hle/service/nfp/nfp.cpp6
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.h3
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp31
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h14
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.cpp5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.h4
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.cpp6
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp36
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.h6
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp4
-rw-r--r--src/core/hle/service/sm/sm.cpp3
-rw-r--r--src/core/hle/service/sockets/sockets_translate.cpp1
-rw-r--r--src/core/loader/loader.cpp4
-rw-r--r--src/core/loader/loader.h1
-rw-r--r--src/core/settings.h3
-rw-r--r--src/input_common/gcadapter/gc_adapter.h6
-rw-r--r--src/input_common/motion_input.cpp2
-rw-r--r--src/input_common/mouse/mouse_input.h2
-rw-r--r--src/input_common/udp/udp.cpp8
-rw-r--r--src/tests/CMakeLists.txt1
-rw-r--r--src/tests/common/bit_utils.cpp23
-rw-r--r--src/tests/common/fibers.cpp4
-rw-r--r--src/tests/common/ring_buffer.cpp30
-rw-r--r--src/video_core/CMakeLists.txt26
-rw-r--r--src/video_core/cdma_pusher.cpp19
-rw-r--r--src/video_core/cdma_pusher.h12
-rw-r--r--src/video_core/command_classes/codecs/h264.cpp4
-rw-r--r--src/video_core/command_classes/host1x.cpp19
-rw-r--r--src/video_core/command_classes/host1x.h49
-rw-r--r--src/video_core/command_classes/vic.cpp2
-rw-r--r--src/video_core/compatible_formats.cpp9
-rw-r--r--src/video_core/compatible_formats.h2
-rw-r--r--src/video_core/engines/fermi_2d.h30
-rw-r--r--src/video_core/engines/kepler_compute.h16
-rw-r--r--src/video_core/engines/kepler_memory.h4
-rw-r--r--src/video_core/engines/maxwell_3d.h150
-rw-r--r--src/video_core/engines/shader_header.h38
-rw-r--r--src/video_core/gpu.h8
-rw-r--r--src/video_core/renderer_opengl/gl_device.cpp4
-rw-r--r--src/video_core/renderer_opengl/gl_device.h5
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp195
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h6
-rw-r--r--src/video_core/renderer_vulkan/blit_image.cpp10
-rw-r--r--src/video_core/renderer_vulkan/blit_image.h13
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.cpp14
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.h11
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp12
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.h12
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp320
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.h19
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp28
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.h18
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp48
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.h30
-rw-r--r--src/video_core/renderer_vulkan/vk_command_pool.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_command_pool.h8
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pass.cpp44
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pass.h27
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.h8
-rw-r--r--src/video_core/renderer_vulkan/vk_descriptor_pool.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_descriptor_pool.h8
-rw-r--r--src/video_core/renderer_vulkan/vk_fence_manager.cpp58
-rw-r--r--src/video_core/renderer_vulkan/vk_fence_manager.h19
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp19
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.h8
-rw-r--r--src/video_core/renderer_vulkan/vk_master_semaphore.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_master_semaphore.h6
-rw-r--r--src/video_core/renderer_vulkan/vk_memory_manager.cpp230
-rw-r--r--src/video_core/renderer_vulkan/vk_memory_manager.h132
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.h8
-rw-r--r--src/video_core/renderer_vulkan/vk_query_cache.cpp24
-rw-r--r--src/video_core/renderer_vulkan/vk_query_cache.h16
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp33
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h16
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.h8
-rw-r--r--src/video_core/renderer_vulkan/vk_shader_decompiler.cpp19
-rw-r--r--src/video_core/renderer_vulkan/vk_shader_decompiler.h6
-rw-r--r--src/video_core/renderer_vulkan/vk_shader_util.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_shader_util.h6
-rw-r--r--src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp130
-rw-r--r--src/video_core/renderer_vulkan/vk_staging_buffer_pool.h68
-rw-r--r--src/video_core/renderer_vulkan/vk_stream_buffer.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_stream_buffer.h8
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.h8
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp34
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h35
-rw-r--r--src/video_core/renderer_vulkan/vk_update_descriptor.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_update_descriptor.h8
-rw-r--r--src/video_core/shader/async_shaders.cpp2
-rw-r--r--src/video_core/shader/async_shaders.h6
-rw-r--r--src/video_core/texture_cache/accelerated_swizzle.cpp4
-rw-r--r--src/video_core/texture_cache/image_base.cpp4
-rw-r--r--src/video_core/texture_cache/image_view_base.cpp2
-rw-r--r--src/video_core/texture_cache/texture_cache.h27
-rw-r--r--src/video_core/texture_cache/util.cpp23
-rw-r--r--src/video_core/texture_cache/util.h8
-rw-r--r--src/video_core/textures/astc.cpp41
-rw-r--r--src/video_core/textures/decoders.cpp8
-rw-r--r--src/video_core/vulkan_common/nsight_aftermath_tracker.cpp (renamed from src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp)30
-rw-r--r--src/video_core/vulkan_common/nsight_aftermath_tracker.h (renamed from src/video_core/renderer_vulkan/nsight_aftermath_tracker.h)5
-rw-r--r--src/video_core/vulkan_common/vulkan_debug_callback.cpp45
-rw-r--r--src/video_core/vulkan_common/vulkan_debug_callback.h11
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp (renamed from src/video_core/renderer_vulkan/vk_device.cpp)192
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h (renamed from src/video_core/renderer_vulkan/vk_device.h)62
-rw-r--r--src/video_core/vulkan_common/vulkan_instance.cpp151
-rw-r--r--src/video_core/vulkan_common/vulkan_instance.h32
-rw-r--r--src/video_core/vulkan_common/vulkan_library.cpp36
-rw-r--r--src/video_core/vulkan_common/vulkan_library.h13
-rw-r--r--src/video_core/vulkan_common/vulkan_memory_allocator.cpp268
-rw-r--r--src/video_core/vulkan_common/vulkan_memory_allocator.h118
-rw-r--r--src/video_core/vulkan_common/vulkan_surface.cpp81
-rw-r--r--src/video_core/vulkan_common/vulkan_surface.h18
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.cpp (renamed from src/video_core/renderer_vulkan/wrapper.cpp)58
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.h (renamed from src/video_core/renderer_vulkan/wrapper.h)307
-rw-r--r--src/yuzu/applets/controller.cpp2
-rw-r--r--src/yuzu/applets/error.cpp6
-rw-r--r--src/yuzu/bootmanager.cpp15
-rw-r--r--src/yuzu/bootmanager.h2
-rw-r--r--src/yuzu/compatdb.cpp2
-rw-r--r--src/yuzu/configuration/config.cpp8
-rw-r--r--src/yuzu/configuration/configure_cpu.cpp3
-rw-r--r--src/yuzu/configuration/configure_cpu.ui12
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp22
-rw-r--r--src/yuzu/configuration/configure_input.cpp13
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp32
-rw-r--r--src/yuzu/configuration/configure_input_player.h12
-rw-r--r--src/yuzu/configuration/configure_motion_touch.cpp24
-rw-r--r--src/yuzu/debugger/wait_tree.cpp128
-rw-r--r--src/yuzu/debugger/wait_tree.h17
-rw-r--r--src/yuzu/game_list.cpp2
-rw-r--r--src/yuzu/main.cpp95
-rw-r--r--src/yuzu/main.h3
-rw-r--r--src/yuzu/util/url_request_interceptor.cpp2
-rw-r--r--src/yuzu_cmd/config.cpp2
-rw-r--r--src/yuzu_cmd/default_ini.h2
-rw-r--r--src/yuzu_cmd/yuzu.cpp4
-rw-r--r--src/yuzu_tester/CMakeLists.txt32
-rw-r--r--src/yuzu_tester/config.cpp194
-rw-r--r--src/yuzu_tester/config.h24
-rw-r--r--src/yuzu_tester/default_ini.h182
-rw-r--r--src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp146
-rw-r--r--src/yuzu_tester/emu_window/emu_window_sdl2_hide.h37
-rw-r--r--src/yuzu_tester/resource.h16
-rw-r--r--src/yuzu_tester/service/yuzutest.cpp115
-rw-r--r--src/yuzu_tester/service/yuzutest.h25
-rw-r--r--src/yuzu_tester/yuzu.cpp268
-rw-r--r--src/yuzu_tester/yuzu.rc17
240 files changed, 5791 insertions, 5646 deletions
diff --git a/.ci/scripts/common/post-upload.sh b/.ci/scripts/common/post-upload.sh
index e46ee0abb..99e79fcb6 100644
--- a/.ci/scripts/common/post-upload.sh
+++ b/.ci/scripts/common/post-upload.sh
@@ -15,5 +15,5 @@ mv "${REV_NAME}-source.tar.xz" $RELEASE_NAME
157z a "$REV_NAME.7z" $RELEASE_NAME 157z a "$REV_NAME.7z" $RELEASE_NAME
16 16
17# move the compiled archive into the artifacts directory to be uploaded by travis releases 17# move the compiled archive into the artifacts directory to be uploaded by travis releases
18mv "$ARCHIVE_NAME" artifacts/ 18mv "$ARCHIVE_NAME" "${ARTIFACTS_DIR}/"
19mv "$REV_NAME.7z" artifacts/ 19mv "$REV_NAME.7z" "${ARTIFACTS_DIR}/"
diff --git a/.ci/scripts/common/pre-upload.sh b/.ci/scripts/common/pre-upload.sh
index 3c2fc79a2..a49e3fff3 100644
--- a/.ci/scripts/common/pre-upload.sh
+++ b/.ci/scripts/common/pre-upload.sh
@@ -2,5 +2,6 @@
2 2
3GITDATE="`git show -s --date=short --format='%ad' | sed 's/-//g'`" 3GITDATE="`git show -s --date=short --format='%ad' | sed 's/-//g'`"
4GITREV="`git show -s --format='%h'`" 4GITREV="`git show -s --format='%h'`"
5ARTIFACTS_DIR="artifacts"
5 6
6mkdir -p artifacts 7mkdir -p "${ARTIFACTS_DIR}/"
diff --git a/.ci/scripts/linux/docker.sh b/.ci/scripts/linux/docker.sh
index e0c018cfd..39b1f77d7 100755
--- a/.ci/scripts/linux/docker.sh
+++ b/.ci/scripts/linux/docker.sh
@@ -1,14 +1,49 @@
1#!/bin/bash -ex 1#!/bin/bash -ex
2 2
3# Exit on error, rather than continuing with the rest of the script.
4set -e
5
3cd /yuzu 6cd /yuzu
4 7
5ccache -s 8ccache -s
6 9
7mkdir build || true && cd build 10mkdir build || true && cd build
8cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON 11cmake .. -DDISPLAY_VERSION=$1 -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DCMAKE_INSTALL_PREFIX="/usr"
9 12
10ninja 13make -j$(nproc)
11 14
12ccache -s 15ccache -s
13 16
14ctest -VV -C Release 17ctest -VV -C Release
18
19make install DESTDIR=AppDir
20rm -vf AppDir/usr/bin/yuzu-cmd AppDir/usr/bin/yuzu-tester
21
22# Download tools needed to build an AppImage
23wget -nc https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
24wget -nc https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage
25wget -nc https://github.com/darealshinji/AppImageKit-checkrt/releases/download/continuous/AppRun-patched-x86_64
26wget -nc https://github.com/darealshinji/AppImageKit-checkrt/releases/download/continuous/exec-x86_64.so
27# Set executable bit
28chmod 755 \
29 AppRun-patched-x86_64 \
30 exec-x86_64.so \
31 linuxdeploy-x86_64.AppImage \
32 linuxdeploy-plugin-qt-x86_64.AppImage
33
34# Workaround for https://github.com/AppImage/AppImageKit/issues/828
35export APPIMAGE_EXTRACT_AND_RUN=1
36
37mkdir -p AppDir/usr/optional
38mkdir -p AppDir/usr/optional/libstdc++
39mkdir -p AppDir/usr/optional/libgcc_s
40
41# Deploy yuzu's needed dependencies
42./linuxdeploy-x86_64.AppImage --appdir AppDir --plugin qt
43
44# Workaround for building yuzu with GCC 10 but also trying to distribute it to Ubuntu 18.04 et al.
45# See https://github.com/darealshinji/AppImageKit-checkrt
46cp exec-x86_64.so AppDir/usr/optional/exec.so
47cp AppRun-patched-x86_64 AppDir/AppRun
48cp --dereference /usr/lib/x86_64-linux-gnu/libstdc++.so.6 AppDir/usr/optional/libstdc++/libstdc++.so.6
49cp --dereference /lib/x86_64-linux-gnu/libgcc_s.so.1 AppDir/usr/optional/libgcc_s/libgcc_s.so.1
diff --git a/.ci/scripts/linux/upload.sh b/.ci/scripts/linux/upload.sh
index fe4e6b2ac..b2ea07388 100644
--- a/.ci/scripts/linux/upload.sh
+++ b/.ci/scripts/linux/upload.sh
@@ -2,6 +2,7 @@
2 2
3. .ci/scripts/common/pre-upload.sh 3. .ci/scripts/common/pre-upload.sh
4 4
5APPIMAGE_NAME="yuzu-${GITDATE}-${GITREV}.AppImage"
5REV_NAME="yuzu-linux-${GITDATE}-${GITREV}" 6REV_NAME="yuzu-linux-${GITDATE}-${GITREV}"
6ARCHIVE_NAME="${REV_NAME}.tar.xz" 7ARCHIVE_NAME="${REV_NAME}.tar.xz"
7COMPRESSION_FLAGS="-cJvf" 8COMPRESSION_FLAGS="-cJvf"
@@ -17,4 +18,24 @@ mkdir "$DIR_NAME"
17cp build/bin/yuzu-cmd "$DIR_NAME" 18cp build/bin/yuzu-cmd "$DIR_NAME"
18cp build/bin/yuzu "$DIR_NAME" 19cp build/bin/yuzu "$DIR_NAME"
19 20
21# Build an AppImage
22cd build
23
24wget -nc https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
25chmod 755 appimagetool-x86_64.AppImage
26
27if [ "${RELEASE_NAME}" = "mainline" ]; then
28 # Generate update information if releasing to mainline
29 ./appimagetool-x86_64.AppImage -u "gh-releases-zsync|yuzu-emu|yuzu-${RELEASE_NAME}|latest|yuzu-*.AppImage.zsync" AppDir "${APPIMAGE_NAME}"
30else
31 ./appimagetool-x86_64.AppImage AppDir "${APPIMAGE_NAME}"
32fi
33cd ..
34
35# Copy the AppImage and update info to the artifacts directory and avoid compressing it
36cp "build/${APPIMAGE_NAME}" "${ARTIFACTS_DIR}/"
37if [ -f "build/${APPIMAGE_NAME}.zsync" ]; then
38 cp "build/${APPIMAGE_NAME}.zsync" "${ARTIFACTS_DIR}/"
39fi
40
20. .ci/scripts/common/post-upload.sh 41. .ci/scripts/common/post-upload.sh
diff --git a/CMakeLists.txt b/CMakeLists.txt
index eda555494..aaf3a90cf 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -26,6 +26,10 @@ option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
26 26
27option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF) 27option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF)
28 28
29if (NOT ENABLE_WEB_SERVICE)
30 set(YUZU_ENABLE_BOXCAT OFF)
31endif()
32
29# Default to a Release build 33# Default to a Release build
30get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) 34get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
31if (NOT IS_MULTI_CONFIG AND NOT CMAKE_BUILD_TYPE) 35if (NOT IS_MULTI_CONFIG AND NOT CMAKE_BUILD_TYPE)
@@ -165,7 +169,7 @@ macro(yuzu_find_packages)
165 "lz4 1.8 lz4/1.9.2" 169 "lz4 1.8 lz4/1.9.2"
166 "nlohmann_json 3.8 nlohmann_json/3.8.0" 170 "nlohmann_json 3.8 nlohmann_json/3.8.0"
167 "ZLIB 1.2 zlib/1.2.11" 171 "ZLIB 1.2 zlib/1.2.11"
168 "zstd 1.4 zstd/1.4.5" 172 "zstd 1.4 zstd/1.4.8"
169 ) 173 )
170 174
171 foreach(PACKAGE ${REQUIRED_LIBS}) 175 foreach(PACKAGE ${REQUIRED_LIBS})
@@ -239,7 +243,7 @@ if(ENABLE_QT)
239 if (YUZU_USE_QT_WEB_ENGINE) 243 if (YUZU_USE_QT_WEB_ENGINE)
240 find_package(Qt5 COMPONENTS WebEngineCore WebEngineWidgets) 244 find_package(Qt5 COMPONENTS WebEngineCore WebEngineWidgets)
241 endif() 245 endif()
242 246
243 if (ENABLE_QT_TRANSLATION) 247 if (ENABLE_QT_TRANSLATION)
244 find_package(Qt5 REQUIRED COMPONENTS LinguistTools ${QT_PREFIX_HINT}) 248 find_package(Qt5 REQUIRED COMPONENTS LinguistTools ${QT_PREFIX_HINT})
245 endif() 249 endif()
@@ -322,7 +326,7 @@ if (CONAN_REQUIRED_LIBS)
322 list(APPEND Boost_LIBRARIES Boost::context) 326 list(APPEND Boost_LIBRARIES Boost::context)
323 endif() 327 endif()
324 endif() 328 endif()
325 329
326 # Due to issues with variable scopes in functions, we need to also find_package(qt5) outside of the function 330 # Due to issues with variable scopes in functions, we need to also find_package(qt5) outside of the function
327 if(ENABLE_QT) 331 if(ENABLE_QT)
328 list(APPEND CMAKE_MODULE_PATH "${CONAN_QT_ROOT_RELEASE}") 332 list(APPEND CMAKE_MODULE_PATH "${CONAN_QT_ROOT_RELEASE}")
diff --git a/README.md b/README.md
index 981c8ef24..fbf62eb7c 100644
--- a/README.md
+++ b/README.md
@@ -30,7 +30,6 @@ If you want to contribute to the user interface translation, please check out th
30 30
31* __Windows__: [Windows Build](https://github.com/yuzu-emu/yuzu/wiki/Building-For-Windows) 31* __Windows__: [Windows Build](https://github.com/yuzu-emu/yuzu/wiki/Building-For-Windows)
32* __Linux__: [Linux Build](https://github.com/yuzu-emu/yuzu/wiki/Building-For-Linux) 32* __Linux__: [Linux Build](https://github.com/yuzu-emu/yuzu/wiki/Building-For-Linux)
33* __macOS__: [macOS Build](https://github.com/yuzu-emu/yuzu/wiki/Building-for-macOS)
34 33
35 34
36### Support 35### Support
diff --git a/externals/dynarmic b/externals/dynarmic
Subproject 4a9a0d07f7376b439430e686721e8176c7b56ce Subproject 3806284cbefc4115436dcdc687776a45ec31309
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 8777df751..478246b6f 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -45,10 +45,15 @@ if (MSVC)
45 45
46 # Warnings 46 # Warnings
47 /W3 47 /W3
48 /we4062 # enumerator 'identifier' in a switch of enum 'enumeration' is not handled
49 /we4101 # 'identifier': unreferenced local variable
50 /we4265 # 'class': class has virtual functions, but destructor is not virtual
51 /we4388 # signed/unsigned mismatch
48 /we4547 # 'operator' : operator before comma has no effect; expected operator with side-effect 52 /we4547 # 'operator' : operator before comma has no effect; expected operator with side-effect
49 /we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'? 53 /we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'?
50 /we4555 # Expression has no effect; expected expression with side-effect 54 /we4555 # Expression has no effect; expected expression with side-effect
51 /we4834 # Discarding return value of function with 'nodiscard' attribute 55 /we4834 # Discarding return value of function with 'nodiscard' attribute
56 /we5038 # data member 'member1' will be initialized after data member 'member2'
52 ) 57 )
53 58
54 # /GS- - No stack buffer overflow checks 59 # /GS- - No stack buffer overflow checks
@@ -62,8 +67,11 @@ else()
62 -Werror=implicit-fallthrough 67 -Werror=implicit-fallthrough
63 -Werror=missing-declarations 68 -Werror=missing-declarations
64 -Werror=reorder 69 -Werror=reorder
70 -Werror=switch
65 -Werror=uninitialized 71 -Werror=uninitialized
72 -Werror=unused-function
66 -Werror=unused-result 73 -Werror=unused-result
74 -Werror=unused-variable
67 -Wextra 75 -Wextra
68 -Wmissing-declarations 76 -Wmissing-declarations
69 -Wno-attributes 77 -Wno-attributes
@@ -122,7 +130,6 @@ add_subdirectory(tests)
122 130
123if (ENABLE_SDL2) 131if (ENABLE_SDL2)
124 add_subdirectory(yuzu_cmd) 132 add_subdirectory(yuzu_cmd)
125 add_subdirectory(yuzu_tester)
126endif() 133endif()
127 134
128if (ENABLE_QT) 135if (ENABLE_QT)
diff --git a/src/audio_core/sink_context.h b/src/audio_core/sink_context.h
index 05541becb..66ee4e8a0 100644
--- a/src/audio_core/sink_context.h
+++ b/src/audio_core/sink_context.h
@@ -40,17 +40,17 @@ public:
40 SinkSampleFormat sample_format; 40 SinkSampleFormat sample_format;
41 std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> input; 41 std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> input;
42 bool in_use; 42 bool in_use;
43 INSERT_UNION_PADDING_BYTES(5); 43 INSERT_PADDING_BYTES_NOINIT(5);
44 }; 44 };
45 static_assert(sizeof(CircularBufferIn) == 0x28, 45 static_assert(sizeof(CircularBufferIn) == 0x28,
46 "SinkInfo::CircularBufferIn is in invalid size"); 46 "SinkInfo::CircularBufferIn is in invalid size");
47 47
48 struct DeviceIn { 48 struct DeviceIn {
49 std::array<u8, 255> device_name; 49 std::array<u8, 255> device_name;
50 INSERT_UNION_PADDING_BYTES(1); 50 INSERT_PADDING_BYTES_NOINIT(1);
51 s32_le input_count; 51 s32_le input_count;
52 std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> input; 52 std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> input;
53 INSERT_UNION_PADDING_BYTES(1); 53 INSERT_PADDING_BYTES_NOINIT(1);
54 bool down_matrix_enabled; 54 bool down_matrix_enabled;
55 DownmixCoefficients down_matrix_coef; 55 DownmixCoefficients down_matrix_coef;
56 }; 56 };
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index abe62543e..f77575a00 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -107,7 +107,6 @@ add_library(common STATIC
107 bit_util.h 107 bit_util.h
108 cityhash.cpp 108 cityhash.cpp
109 cityhash.h 109 cityhash.h
110 color.h
111 common_funcs.h 110 common_funcs.h
112 common_paths.h 111 common_paths.h
113 common_types.h 112 common_types.h
@@ -122,6 +121,7 @@ add_library(common STATIC
122 hash.h 121 hash.h
123 hex_util.cpp 122 hex_util.cpp
124 hex_util.h 123 hex_util.h
124 intrusive_red_black_tree.h
125 logging/backend.cpp 125 logging/backend.cpp
126 logging/backend.h 126 logging/backend.h
127 logging/filter.cpp 127 logging/filter.cpp
@@ -142,6 +142,7 @@ add_library(common STATIC
142 page_table.h 142 page_table.h
143 param_package.cpp 143 param_package.cpp
144 param_package.h 144 param_package.h
145 parent_of_member.h
145 quaternion.h 146 quaternion.h
146 ring_buffer.h 147 ring_buffer.h
147 scm_rev.cpp 148 scm_rev.cpp
@@ -164,8 +165,7 @@ add_library(common STATIC
164 threadsafe_queue.h 165 threadsafe_queue.h
165 time_zone.cpp 166 time_zone.cpp
166 time_zone.h 167 time_zone.h
167 timer.cpp 168 tree.h
168 timer.h
169 uint128.cpp 169 uint128.cpp
170 uint128.h 170 uint128.h
171 uuid.cpp 171 uuid.cpp
diff --git a/src/common/alignment.h b/src/common/alignment.h
index 5040043de..fb81f10d8 100644
--- a/src/common/alignment.h
+++ b/src/common/alignment.h
@@ -9,50 +9,45 @@
9namespace Common { 9namespace Common {
10 10
11template <typename T> 11template <typename T>
12[[nodiscard]] constexpr T AlignUp(T value, std::size_t size) { 12requires std::is_unsigned_v<T>[[nodiscard]] constexpr T AlignUp(T value, size_t size) {
13 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
14 auto mod{static_cast<T>(value % size)}; 13 auto mod{static_cast<T>(value % size)};
15 value -= mod; 14 value -= mod;
16 return static_cast<T>(mod == T{0} ? value : value + size); 15 return static_cast<T>(mod == T{0} ? value : value + size);
17} 16}
18 17
19template <typename T> 18template <typename T>
20[[nodiscard]] constexpr T AlignDown(T value, std::size_t size) { 19requires std::is_unsigned_v<T>[[nodiscard]] constexpr T AlignUpLog2(T value, size_t align_log2) {
21 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); 20 return static_cast<T>((value + ((1ULL << align_log2) - 1)) >> align_log2 << align_log2);
22 return static_cast<T>(value - value % size);
23} 21}
24 22
25template <typename T> 23template <typename T>
26[[nodiscard]] constexpr T AlignBits(T value, std::size_t align) { 24requires std::is_unsigned_v<T>[[nodiscard]] constexpr T AlignDown(T value, size_t size) {
27 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); 25 return static_cast<T>(value - value % size);
28 return static_cast<T>((value + ((1ULL << align) - 1)) >> align << align);
29} 26}
30 27
31template <typename T> 28template <typename T>
32[[nodiscard]] constexpr bool Is4KBAligned(T value) { 29requires std::is_unsigned_v<T>[[nodiscard]] constexpr bool Is4KBAligned(T value) {
33 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
34 return (value & 0xFFF) == 0; 30 return (value & 0xFFF) == 0;
35} 31}
36 32
37template <typename T> 33template <typename T>
38[[nodiscard]] constexpr bool IsWordAligned(T value) { 34requires std::is_unsigned_v<T>[[nodiscard]] constexpr bool IsWordAligned(T value) {
39 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
40 return (value & 0b11) == 0; 35 return (value & 0b11) == 0;
41} 36}
42 37
43template <typename T> 38template <typename T>
44[[nodiscard]] constexpr bool IsAligned(T value, std::size_t alignment) { 39requires std::is_integral_v<T>[[nodiscard]] constexpr bool IsAligned(T value, size_t alignment) {
45 using U = typename std::make_unsigned<T>::type; 40 using U = typename std::make_unsigned_t<T>;
46 const U mask = static_cast<U>(alignment - 1); 41 const U mask = static_cast<U>(alignment - 1);
47 return (value & mask) == 0; 42 return (value & mask) == 0;
48} 43}
49 44
50template <typename T, std::size_t Align = 16> 45template <typename T, size_t Align = 16>
51class AlignmentAllocator { 46class AlignmentAllocator {
52public: 47public:
53 using value_type = T; 48 using value_type = T;
54 using size_type = std::size_t; 49 using size_type = size_t;
55 using difference_type = std::ptrdiff_t; 50 using difference_type = ptrdiff_t;
56 51
57 using propagate_on_container_copy_assignment = std::true_type; 52 using propagate_on_container_copy_assignment = std::true_type;
58 using propagate_on_container_move_assignment = std::true_type; 53 using propagate_on_container_move_assignment = std::true_type;
diff --git a/src/common/bit_util.h b/src/common/bit_util.h
index 29f59a9a3..685e7fc9b 100644
--- a/src/common/bit_util.h
+++ b/src/common/bit_util.h
@@ -22,82 +22,6 @@ template <typename T>
22} 22}
23 23
24#ifdef _MSC_VER 24#ifdef _MSC_VER
25[[nodiscard]] inline u32 CountLeadingZeroes32(u32 value) {
26 unsigned long leading_zero = 0;
27
28 if (_BitScanReverse(&leading_zero, value) != 0) {
29 return 31 - leading_zero;
30 }
31
32 return 32;
33}
34
35[[nodiscard]] inline u32 CountLeadingZeroes64(u64 value) {
36 unsigned long leading_zero = 0;
37
38 if (_BitScanReverse64(&leading_zero, value) != 0) {
39 return 63 - leading_zero;
40 }
41
42 return 64;
43}
44#else
45[[nodiscard]] inline u32 CountLeadingZeroes32(u32 value) {
46 if (value == 0) {
47 return 32;
48 }
49
50 return static_cast<u32>(__builtin_clz(value));
51}
52
53[[nodiscard]] inline u32 CountLeadingZeroes64(u64 value) {
54 if (value == 0) {
55 return 64;
56 }
57
58 return static_cast<u32>(__builtin_clzll(value));
59}
60#endif
61
62#ifdef _MSC_VER
63[[nodiscard]] inline u32 CountTrailingZeroes32(u32 value) {
64 unsigned long trailing_zero = 0;
65
66 if (_BitScanForward(&trailing_zero, value) != 0) {
67 return trailing_zero;
68 }
69
70 return 32;
71}
72
73[[nodiscard]] inline u32 CountTrailingZeroes64(u64 value) {
74 unsigned long trailing_zero = 0;
75
76 if (_BitScanForward64(&trailing_zero, value) != 0) {
77 return trailing_zero;
78 }
79
80 return 64;
81}
82#else
83[[nodiscard]] inline u32 CountTrailingZeroes32(u32 value) {
84 if (value == 0) {
85 return 32;
86 }
87
88 return static_cast<u32>(__builtin_ctz(value));
89}
90
91[[nodiscard]] inline u32 CountTrailingZeroes64(u64 value) {
92 if (value == 0) {
93 return 64;
94 }
95
96 return static_cast<u32>(__builtin_ctzll(value));
97}
98#endif
99
100#ifdef _MSC_VER
101 25
102[[nodiscard]] inline u32 MostSignificantBit32(const u32 value) { 26[[nodiscard]] inline u32 MostSignificantBit32(const u32 value) {
103 unsigned long result; 27 unsigned long result;
diff --git a/src/common/color.h b/src/common/color.h
deleted file mode 100644
index bbcac858e..000000000
--- a/src/common/color.h
+++ /dev/null
@@ -1,271 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <cstring>
8
9#include "common/common_types.h"
10#include "common/swap.h"
11#include "common/vector_math.h"
12
13namespace Common::Color {
14
15/// Convert a 1-bit color component to 8 bit
16[[nodiscard]] constexpr u8 Convert1To8(u8 value) {
17 return value * 255;
18}
19
20/// Convert a 4-bit color component to 8 bit
21[[nodiscard]] constexpr u8 Convert4To8(u8 value) {
22 return (value << 4) | value;
23}
24
25/// Convert a 5-bit color component to 8 bit
26[[nodiscard]] constexpr u8 Convert5To8(u8 value) {
27 return (value << 3) | (value >> 2);
28}
29
30/// Convert a 6-bit color component to 8 bit
31[[nodiscard]] constexpr u8 Convert6To8(u8 value) {
32 return (value << 2) | (value >> 4);
33}
34
35/// Convert a 8-bit color component to 1 bit
36[[nodiscard]] constexpr u8 Convert8To1(u8 value) {
37 return value >> 7;
38}
39
40/// Convert a 8-bit color component to 4 bit
41[[nodiscard]] constexpr u8 Convert8To4(u8 value) {
42 return value >> 4;
43}
44
45/// Convert a 8-bit color component to 5 bit
46[[nodiscard]] constexpr u8 Convert8To5(u8 value) {
47 return value >> 3;
48}
49
50/// Convert a 8-bit color component to 6 bit
51[[nodiscard]] constexpr u8 Convert8To6(u8 value) {
52 return value >> 2;
53}
54
55/**
56 * Decode a color stored in RGBA8 format
57 * @param bytes Pointer to encoded source color
58 * @return Result color decoded as Common::Vec4<u8>
59 */
60[[nodiscard]] inline Common::Vec4<u8> DecodeRGBA8(const u8* bytes) {
61 return {bytes[3], bytes[2], bytes[1], bytes[0]};
62}
63
64/**
65 * Decode a color stored in RGB8 format
66 * @param bytes Pointer to encoded source color
67 * @return Result color decoded as Common::Vec4<u8>
68 */
69[[nodiscard]] inline Common::Vec4<u8> DecodeRGB8(const u8* bytes) {
70 return {bytes[2], bytes[1], bytes[0], 255};
71}
72
73/**
74 * Decode a color stored in RG8 (aka HILO8) format
75 * @param bytes Pointer to encoded source color
76 * @return Result color decoded as Common::Vec4<u8>
77 */
78[[nodiscard]] inline Common::Vec4<u8> DecodeRG8(const u8* bytes) {
79 return {bytes[1], bytes[0], 0, 255};
80}
81
82/**
83 * Decode a color stored in RGB565 format
84 * @param bytes Pointer to encoded source color
85 * @return Result color decoded as Common::Vec4<u8>
86 */
87[[nodiscard]] inline Common::Vec4<u8> DecodeRGB565(const u8* bytes) {
88 u16_le pixel;
89 std::memcpy(&pixel, bytes, sizeof(pixel));
90 return {Convert5To8((pixel >> 11) & 0x1F), Convert6To8((pixel >> 5) & 0x3F),
91 Convert5To8(pixel & 0x1F), 255};
92}
93
94/**
95 * Decode a color stored in RGB5A1 format
96 * @param bytes Pointer to encoded source color
97 * @return Result color decoded as Common::Vec4<u8>
98 */
99[[nodiscard]] inline Common::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
100 u16_le pixel;
101 std::memcpy(&pixel, bytes, sizeof(pixel));
102 return {Convert5To8((pixel >> 11) & 0x1F), Convert5To8((pixel >> 6) & 0x1F),
103 Convert5To8((pixel >> 1) & 0x1F), Convert1To8(pixel & 0x1)};
104}
105
106/**
107 * Decode a color stored in RGBA4 format
108 * @param bytes Pointer to encoded source color
109 * @return Result color decoded as Common::Vec4<u8>
110 */
111[[nodiscard]] inline Common::Vec4<u8> DecodeRGBA4(const u8* bytes) {
112 u16_le pixel;
113 std::memcpy(&pixel, bytes, sizeof(pixel));
114 return {Convert4To8((pixel >> 12) & 0xF), Convert4To8((pixel >> 8) & 0xF),
115 Convert4To8((pixel >> 4) & 0xF), Convert4To8(pixel & 0xF)};
116}
117
118/**
119 * Decode a depth value stored in D16 format
120 * @param bytes Pointer to encoded source value
121 * @return Depth value as an u32
122 */
123[[nodiscard]] inline u32 DecodeD16(const u8* bytes) {
124 u16_le data;
125 std::memcpy(&data, bytes, sizeof(data));
126 return data;
127}
128
129/**
130 * Decode a depth value stored in D24 format
131 * @param bytes Pointer to encoded source value
132 * @return Depth value as an u32
133 */
134[[nodiscard]] inline u32 DecodeD24(const u8* bytes) {
135 return (bytes[2] << 16) | (bytes[1] << 8) | bytes[0];
136}
137
138/**
139 * Decode a depth value and a stencil value stored in D24S8 format
140 * @param bytes Pointer to encoded source values
141 * @return Resulting values stored as a Common::Vec2
142 */
143[[nodiscard]] inline Common::Vec2<u32> DecodeD24S8(const u8* bytes) {
144 return {static_cast<u32>((bytes[2] << 16) | (bytes[1] << 8) | bytes[0]), bytes[3]};
145}
146
147/**
148 * Encode a color as RGBA8 format
149 * @param color Source color to encode
150 * @param bytes Destination pointer to store encoded color
151 */
152inline void EncodeRGBA8(const Common::Vec4<u8>& color, u8* bytes) {
153 bytes[3] = color.r();
154 bytes[2] = color.g();
155 bytes[1] = color.b();
156 bytes[0] = color.a();
157}
158
159/**
160 * Encode a color as RGB8 format
161 * @param color Source color to encode
162 * @param bytes Destination pointer to store encoded color
163 */
164inline void EncodeRGB8(const Common::Vec4<u8>& color, u8* bytes) {
165 bytes[2] = color.r();
166 bytes[1] = color.g();
167 bytes[0] = color.b();
168}
169
170/**
171 * Encode a color as RG8 (aka HILO8) format
172 * @param color Source color to encode
173 * @param bytes Destination pointer to store encoded color
174 */
175inline void EncodeRG8(const Common::Vec4<u8>& color, u8* bytes) {
176 bytes[1] = color.r();
177 bytes[0] = color.g();
178}
179/**
180 * Encode a color as RGB565 format
181 * @param color Source color to encode
182 * @param bytes Destination pointer to store encoded color
183 */
184inline void EncodeRGB565(const Common::Vec4<u8>& color, u8* bytes) {
185 const u16_le data =
186 (Convert8To5(color.r()) << 11) | (Convert8To6(color.g()) << 5) | Convert8To5(color.b());
187
188 std::memcpy(bytes, &data, sizeof(data));
189}
190
191/**
192 * Encode a color as RGB5A1 format
193 * @param color Source color to encode
194 * @param bytes Destination pointer to store encoded color
195 */
196inline void EncodeRGB5A1(const Common::Vec4<u8>& color, u8* bytes) {
197 const u16_le data = (Convert8To5(color.r()) << 11) | (Convert8To5(color.g()) << 6) |
198 (Convert8To5(color.b()) << 1) | Convert8To1(color.a());
199
200 std::memcpy(bytes, &data, sizeof(data));
201}
202
203/**
204 * Encode a color as RGBA4 format
205 * @param color Source color to encode
206 * @param bytes Destination pointer to store encoded color
207 */
208inline void EncodeRGBA4(const Common::Vec4<u8>& color, u8* bytes) {
209 const u16 data = (Convert8To4(color.r()) << 12) | (Convert8To4(color.g()) << 8) |
210 (Convert8To4(color.b()) << 4) | Convert8To4(color.a());
211
212 std::memcpy(bytes, &data, sizeof(data));
213}
214
215/**
216 * Encode a 16 bit depth value as D16 format
217 * @param value 16 bit source depth value to encode
218 * @param bytes Pointer where to store the encoded value
219 */
220inline void EncodeD16(u32 value, u8* bytes) {
221 const u16_le data = static_cast<u16>(value);
222 std::memcpy(bytes, &data, sizeof(data));
223}
224
225/**
226 * Encode a 24 bit depth value as D24 format
227 * @param value 24 bit source depth value to encode
228 * @param bytes Pointer where to store the encoded value
229 */
230inline void EncodeD24(u32 value, u8* bytes) {
231 bytes[0] = value & 0xFF;
232 bytes[1] = (value >> 8) & 0xFF;
233 bytes[2] = (value >> 16) & 0xFF;
234}
235
236/**
237 * Encode a 24 bit depth and 8 bit stencil values as D24S8 format
238 * @param depth 24 bit source depth value to encode
239 * @param stencil 8 bit source stencil value to encode
240 * @param bytes Pointer where to store the encoded value
241 */
242inline void EncodeD24S8(u32 depth, u8 stencil, u8* bytes) {
243 bytes[0] = depth & 0xFF;
244 bytes[1] = (depth >> 8) & 0xFF;
245 bytes[2] = (depth >> 16) & 0xFF;
246 bytes[3] = stencil;
247}
248
249/**
250 * Encode a 24 bit depth value as D24X8 format (32 bits per pixel with 8 bits unused)
251 * @param depth 24 bit source depth value to encode
252 * @param bytes Pointer where to store the encoded value
253 * @note unused bits will not be modified
254 */
255inline void EncodeD24X8(u32 depth, u8* bytes) {
256 bytes[0] = depth & 0xFF;
257 bytes[1] = (depth >> 8) & 0xFF;
258 bytes[2] = (depth >> 16) & 0xFF;
259}
260
261/**
262 * Encode an 8 bit stencil value as X24S8 format (32 bits per pixel with 24 bits unused)
263 * @param stencil 8 bit source stencil value to encode
264 * @param bytes Pointer where to store the encoded value
265 * @note unused bits will not be modified
266 */
267inline void EncodeX24S8(u8 stencil, u8* bytes) {
268 bytes[3] = stencil;
269}
270
271} // namespace Common::Color
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h
index 367b6bf6e..75f3027fb 100644
--- a/src/common/common_funcs.h
+++ b/src/common/common_funcs.h
@@ -24,10 +24,10 @@
24#define INSERT_PADDING_WORDS(num_words) \ 24#define INSERT_PADDING_WORDS(num_words) \
25 std::array<u32, num_words> CONCAT2(pad, __LINE__) {} 25 std::array<u32, num_words> CONCAT2(pad, __LINE__) {}
26 26
27/// These are similar to the INSERT_PADDING_* macros, but are needed for padding unions. This is 27/// These are similar to the INSERT_PADDING_* macros but do not zero-initialize the contents.
28/// because unions can only be initialized by one member. 28/// This keeps the structure trivial to construct.
29#define INSERT_UNION_PADDING_BYTES(num_bytes) std::array<u8, num_bytes> CONCAT2(pad, __LINE__) 29#define INSERT_PADDING_BYTES_NOINIT(num_bytes) std::array<u8, num_bytes> CONCAT2(pad, __LINE__)
30#define INSERT_UNION_PADDING_WORDS(num_words) std::array<u32, num_words> CONCAT2(pad, __LINE__) 30#define INSERT_PADDING_WORDS_NOINIT(num_words) std::array<u32, num_words> CONCAT2(pad, __LINE__)
31 31
32#ifndef _MSC_VER 32#ifndef _MSC_VER
33 33
@@ -93,6 +93,14 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
93 return static_cast<T>(key) == 0; \ 93 return static_cast<T>(key) == 0; \
94 } 94 }
95 95
96/// Evaluates a boolean expression, and returns a result unless that expression is true.
97#define R_UNLESS(expr, res) \
98 { \
99 if (!(expr)) { \
100 return res; \
101 } \
102 }
103
96namespace Common { 104namespace Common {
97 105
98[[nodiscard]] constexpr u32 MakeMagic(char a, char b, char c, char d) { 106[[nodiscard]] constexpr u32 MakeMagic(char a, char b, char c, char d) {
diff --git a/src/common/div_ceil.h b/src/common/div_ceil.h
index 6b2c48f91..95e1489a9 100644
--- a/src/common/div_ceil.h
+++ b/src/common/div_ceil.h
@@ -11,16 +11,16 @@ namespace Common {
11 11
12/// Ceiled integer division. 12/// Ceiled integer division.
13template <typename N, typename D> 13template <typename N, typename D>
14requires std::is_integral_v<N>&& std::is_unsigned_v<D>[[nodiscard]] constexpr auto DivCeil( 14requires std::is_integral_v<N>&& std::is_unsigned_v<D>[[nodiscard]] constexpr N DivCeil(N number,
15 N number, D divisor) { 15 D divisor) {
16 return (static_cast<D>(number) + divisor - 1) / divisor; 16 return static_cast<N>((static_cast<D>(number) + divisor - 1) / divisor);
17} 17}
18 18
19/// Ceiled integer division with logarithmic divisor in base 2 19/// Ceiled integer division with logarithmic divisor in base 2
20template <typename N, typename D> 20template <typename N, typename D>
21requires std::is_integral_v<N>&& std::is_unsigned_v<D>[[nodiscard]] constexpr auto DivCeilLog2( 21requires std::is_integral_v<N>&& std::is_unsigned_v<D>[[nodiscard]] constexpr N DivCeilLog2(
22 N value, D alignment_log2) { 22 N value, D alignment_log2) {
23 return (static_cast<D>(value) + (D(1) << alignment_log2) - 1) >> alignment_log2; 23 return static_cast<N>((static_cast<D>(value) + (D(1) << alignment_log2) - 1) >> alignment_log2);
24} 24}
25 25
26} // namespace Common 26} // namespace Common
diff --git a/src/common/intrusive_red_black_tree.h b/src/common/intrusive_red_black_tree.h
new file mode 100644
index 000000000..c0bbcd457
--- /dev/null
+++ b/src/common/intrusive_red_black_tree.h
@@ -0,0 +1,602 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/parent_of_member.h"
8#include "common/tree.h"
9
10namespace Common {
11
12namespace impl {
13
14class IntrusiveRedBlackTreeImpl;
15
16}
17
18struct IntrusiveRedBlackTreeNode {
19public:
20 using EntryType = RBEntry<IntrusiveRedBlackTreeNode>;
21
22 constexpr IntrusiveRedBlackTreeNode() = default;
23
24 void SetEntry(const EntryType& new_entry) {
25 entry = new_entry;
26 }
27
28 [[nodiscard]] EntryType& GetEntry() {
29 return entry;
30 }
31
32 [[nodiscard]] const EntryType& GetEntry() const {
33 return entry;
34 }
35
36private:
37 EntryType entry{};
38
39 friend class impl::IntrusiveRedBlackTreeImpl;
40
41 template <class, class, class>
42 friend class IntrusiveRedBlackTree;
43};
44
45template <class T, class Traits, class Comparator>
46class IntrusiveRedBlackTree;
47
48namespace impl {
49
50class IntrusiveRedBlackTreeImpl {
51private:
52 template <class, class, class>
53 friend class ::Common::IntrusiveRedBlackTree;
54
55 using RootType = RBHead<IntrusiveRedBlackTreeNode>;
56 RootType root;
57
58public:
59 template <bool Const>
60 class Iterator;
61
62 using value_type = IntrusiveRedBlackTreeNode;
63 using size_type = size_t;
64 using difference_type = ptrdiff_t;
65 using pointer = value_type*;
66 using const_pointer = const value_type*;
67 using reference = value_type&;
68 using const_reference = const value_type&;
69 using iterator = Iterator<false>;
70 using const_iterator = Iterator<true>;
71
72 template <bool Const>
73 class Iterator {
74 public:
75 using iterator_category = std::bidirectional_iterator_tag;
76 using value_type = typename IntrusiveRedBlackTreeImpl::value_type;
77 using difference_type = typename IntrusiveRedBlackTreeImpl::difference_type;
78 using pointer = std::conditional_t<Const, IntrusiveRedBlackTreeImpl::const_pointer,
79 IntrusiveRedBlackTreeImpl::pointer>;
80 using reference = std::conditional_t<Const, IntrusiveRedBlackTreeImpl::const_reference,
81 IntrusiveRedBlackTreeImpl::reference>;
82
83 private:
84 pointer node;
85
86 public:
87 explicit Iterator(pointer n) : node(n) {}
88
89 bool operator==(const Iterator& rhs) const {
90 return this->node == rhs.node;
91 }
92
93 bool operator!=(const Iterator& rhs) const {
94 return !(*this == rhs);
95 }
96
97 pointer operator->() const {
98 return this->node;
99 }
100
101 reference operator*() const {
102 return *this->node;
103 }
104
105 Iterator& operator++() {
106 this->node = GetNext(this->node);
107 return *this;
108 }
109
110 Iterator& operator--() {
111 this->node = GetPrev(this->node);
112 return *this;
113 }
114
115 Iterator operator++(int) {
116 const Iterator it{*this};
117 ++(*this);
118 return it;
119 }
120
121 Iterator operator--(int) {
122 const Iterator it{*this};
123 --(*this);
124 return it;
125 }
126
127 operator Iterator<true>() const {
128 return Iterator<true>(this->node);
129 }
130 };
131
132private:
133 // Define accessors using RB_* functions.
134 bool EmptyImpl() const {
135 return root.IsEmpty();
136 }
137
138 IntrusiveRedBlackTreeNode* GetMinImpl() const {
139 return RB_MIN(const_cast<RootType*>(&root));
140 }
141
142 IntrusiveRedBlackTreeNode* GetMaxImpl() const {
143 return RB_MAX(const_cast<RootType*>(&root));
144 }
145
146 IntrusiveRedBlackTreeNode* RemoveImpl(IntrusiveRedBlackTreeNode* node) {
147 return RB_REMOVE(&root, node);
148 }
149
150public:
151 static IntrusiveRedBlackTreeNode* GetNext(IntrusiveRedBlackTreeNode* node) {
152 return RB_NEXT(node);
153 }
154
155 static IntrusiveRedBlackTreeNode* GetPrev(IntrusiveRedBlackTreeNode* node) {
156 return RB_PREV(node);
157 }
158
159 static const IntrusiveRedBlackTreeNode* GetNext(const IntrusiveRedBlackTreeNode* node) {
160 return static_cast<const IntrusiveRedBlackTreeNode*>(
161 GetNext(const_cast<IntrusiveRedBlackTreeNode*>(node)));
162 }
163
164 static const IntrusiveRedBlackTreeNode* GetPrev(const IntrusiveRedBlackTreeNode* node) {
165 return static_cast<const IntrusiveRedBlackTreeNode*>(
166 GetPrev(const_cast<IntrusiveRedBlackTreeNode*>(node)));
167 }
168
169public:
170 constexpr IntrusiveRedBlackTreeImpl() {}
171
172 // Iterator accessors.
173 iterator begin() {
174 return iterator(this->GetMinImpl());
175 }
176
177 const_iterator begin() const {
178 return const_iterator(this->GetMinImpl());
179 }
180
181 iterator end() {
182 return iterator(static_cast<IntrusiveRedBlackTreeNode*>(nullptr));
183 }
184
185 const_iterator end() const {
186 return const_iterator(static_cast<const IntrusiveRedBlackTreeNode*>(nullptr));
187 }
188
189 const_iterator cbegin() const {
190 return this->begin();
191 }
192
193 const_iterator cend() const {
194 return this->end();
195 }
196
197 iterator iterator_to(reference ref) {
198 return iterator(&ref);
199 }
200
201 const_iterator iterator_to(const_reference ref) const {
202 return const_iterator(&ref);
203 }
204
205 // Content management.
206 bool empty() const {
207 return this->EmptyImpl();
208 }
209
210 reference back() {
211 return *this->GetMaxImpl();
212 }
213
214 const_reference back() const {
215 return *this->GetMaxImpl();
216 }
217
218 reference front() {
219 return *this->GetMinImpl();
220 }
221
222 const_reference front() const {
223 return *this->GetMinImpl();
224 }
225
226 iterator erase(iterator it) {
227 auto cur = std::addressof(*it);
228 auto next = GetNext(cur);
229 this->RemoveImpl(cur);
230 return iterator(next);
231 }
232};
233
234} // namespace impl
235
236template <typename T>
237concept HasLightCompareType = requires {
238 { std::is_same<typename T::LightCompareType, void>::value }
239 ->std::convertible_to<bool>;
240};
241
242namespace impl {
243
244template <typename T, typename Default>
245consteval auto* GetLightCompareType() {
246 if constexpr (HasLightCompareType<T>) {
247 return static_cast<typename T::LightCompareType*>(nullptr);
248 } else {
249 return static_cast<Default*>(nullptr);
250 }
251}
252
253} // namespace impl
254
255template <typename T, typename Default>
256using LightCompareType = std::remove_pointer_t<decltype(impl::GetLightCompareType<T, Default>())>;
257
258template <class T, class Traits, class Comparator>
259class IntrusiveRedBlackTree {
260
261public:
262 using ImplType = impl::IntrusiveRedBlackTreeImpl;
263
264private:
265 ImplType impl{};
266
267public:
268 template <bool Const>
269 class Iterator;
270
271 using value_type = T;
272 using size_type = size_t;
273 using difference_type = ptrdiff_t;
274 using pointer = T*;
275 using const_pointer = const T*;
276 using reference = T&;
277 using const_reference = const T&;
278 using iterator = Iterator<false>;
279 using const_iterator = Iterator<true>;
280
281 using light_value_type = LightCompareType<Comparator, value_type>;
282 using const_light_pointer = const light_value_type*;
283 using const_light_reference = const light_value_type&;
284
285 template <bool Const>
286 class Iterator {
287 public:
288 friend class IntrusiveRedBlackTree<T, Traits, Comparator>;
289
290 using ImplIterator =
291 std::conditional_t<Const, ImplType::const_iterator, ImplType::iterator>;
292
293 using iterator_category = std::bidirectional_iterator_tag;
294 using value_type = typename IntrusiveRedBlackTree::value_type;
295 using difference_type = typename IntrusiveRedBlackTree::difference_type;
296 using pointer = std::conditional_t<Const, IntrusiveRedBlackTree::const_pointer,
297 IntrusiveRedBlackTree::pointer>;
298 using reference = std::conditional_t<Const, IntrusiveRedBlackTree::const_reference,
299 IntrusiveRedBlackTree::reference>;
300
301 private:
302 ImplIterator iterator;
303
304 private:
305 explicit Iterator(ImplIterator it) : iterator(it) {}
306
307 explicit Iterator(typename std::conditional<Const, ImplType::const_iterator,
308 ImplType::iterator>::type::pointer ptr)
309 : iterator(ptr) {}
310
311 ImplIterator GetImplIterator() const {
312 return this->iterator;
313 }
314
315 public:
316 bool operator==(const Iterator& rhs) const {
317 return this->iterator == rhs.iterator;
318 }
319
320 bool operator!=(const Iterator& rhs) const {
321 return !(*this == rhs);
322 }
323
324 pointer operator->() const {
325 return Traits::GetParent(std::addressof(*this->iterator));
326 }
327
328 reference operator*() const {
329 return *Traits::GetParent(std::addressof(*this->iterator));
330 }
331
332 Iterator& operator++() {
333 ++this->iterator;
334 return *this;
335 }
336
337 Iterator& operator--() {
338 --this->iterator;
339 return *this;
340 }
341
342 Iterator operator++(int) {
343 const Iterator it{*this};
344 ++this->iterator;
345 return it;
346 }
347
348 Iterator operator--(int) {
349 const Iterator it{*this};
350 --this->iterator;
351 return it;
352 }
353
354 operator Iterator<true>() const {
355 return Iterator<true>(this->iterator);
356 }
357 };
358
359private:
360 static int CompareImpl(const IntrusiveRedBlackTreeNode* lhs,
361 const IntrusiveRedBlackTreeNode* rhs) {
362 return Comparator::Compare(*Traits::GetParent(lhs), *Traits::GetParent(rhs));
363 }
364
365 static int LightCompareImpl(const void* elm, const IntrusiveRedBlackTreeNode* rhs) {
366 return Comparator::Compare(*static_cast<const_light_pointer>(elm), *Traits::GetParent(rhs));
367 }
368
369 // Define accessors using RB_* functions.
370 IntrusiveRedBlackTreeNode* InsertImpl(IntrusiveRedBlackTreeNode* node) {
371 return RB_INSERT(&impl.root, node, CompareImpl);
372 }
373
374 IntrusiveRedBlackTreeNode* FindImpl(const IntrusiveRedBlackTreeNode* node) const {
375 return RB_FIND(const_cast<ImplType::RootType*>(&impl.root),
376 const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl);
377 }
378
379 IntrusiveRedBlackTreeNode* NFindImpl(const IntrusiveRedBlackTreeNode* node) const {
380 return RB_NFIND(const_cast<ImplType::RootType*>(&impl.root),
381 const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl);
382 }
383
384 IntrusiveRedBlackTreeNode* FindLightImpl(const_light_pointer lelm) const {
385 return RB_FIND_LIGHT(const_cast<ImplType::RootType*>(&impl.root),
386 static_cast<const void*>(lelm), LightCompareImpl);
387 }
388
389 IntrusiveRedBlackTreeNode* NFindLightImpl(const_light_pointer lelm) const {
390 return RB_NFIND_LIGHT(const_cast<ImplType::RootType*>(&impl.root),
391 static_cast<const void*>(lelm), LightCompareImpl);
392 }
393
394public:
395 constexpr IntrusiveRedBlackTree() = default;
396
397 // Iterator accessors.
398 iterator begin() {
399 return iterator(this->impl.begin());
400 }
401
402 const_iterator begin() const {
403 return const_iterator(this->impl.begin());
404 }
405
406 iterator end() {
407 return iterator(this->impl.end());
408 }
409
410 const_iterator end() const {
411 return const_iterator(this->impl.end());
412 }
413
414 const_iterator cbegin() const {
415 return this->begin();
416 }
417
418 const_iterator cend() const {
419 return this->end();
420 }
421
422 iterator iterator_to(reference ref) {
423 return iterator(this->impl.iterator_to(*Traits::GetNode(std::addressof(ref))));
424 }
425
426 const_iterator iterator_to(const_reference ref) const {
427 return const_iterator(this->impl.iterator_to(*Traits::GetNode(std::addressof(ref))));
428 }
429
430 // Content management.
431 bool empty() const {
432 return this->impl.empty();
433 }
434
435 reference back() {
436 return *Traits::GetParent(std::addressof(this->impl.back()));
437 }
438
439 const_reference back() const {
440 return *Traits::GetParent(std::addressof(this->impl.back()));
441 }
442
443 reference front() {
444 return *Traits::GetParent(std::addressof(this->impl.front()));
445 }
446
447 const_reference front() const {
448 return *Traits::GetParent(std::addressof(this->impl.front()));
449 }
450
451 iterator erase(iterator it) {
452 return iterator(this->impl.erase(it.GetImplIterator()));
453 }
454
455 iterator insert(reference ref) {
456 ImplType::pointer node = Traits::GetNode(std::addressof(ref));
457 this->InsertImpl(node);
458 return iterator(node);
459 }
460
461 iterator find(const_reference ref) const {
462 return iterator(this->FindImpl(Traits::GetNode(std::addressof(ref))));
463 }
464
465 iterator nfind(const_reference ref) const {
466 return iterator(this->NFindImpl(Traits::GetNode(std::addressof(ref))));
467 }
468
469 iterator find_light(const_light_reference ref) const {
470 return iterator(this->FindLightImpl(std::addressof(ref)));
471 }
472
473 iterator nfind_light(const_light_reference ref) const {
474 return iterator(this->NFindLightImpl(std::addressof(ref)));
475 }
476};
477
478template <auto T, class Derived = impl::GetParentType<T>>
479class IntrusiveRedBlackTreeMemberTraits;
480
481template <class Parent, IntrusiveRedBlackTreeNode Parent::*Member, class Derived>
482class IntrusiveRedBlackTreeMemberTraits<Member, Derived> {
483public:
484 template <class Comparator>
485 using TreeType = IntrusiveRedBlackTree<Derived, IntrusiveRedBlackTreeMemberTraits, Comparator>;
486 using TreeTypeImpl = impl::IntrusiveRedBlackTreeImpl;
487
488private:
489 template <class, class, class>
490 friend class IntrusiveRedBlackTree;
491
492 friend class impl::IntrusiveRedBlackTreeImpl;
493
494 static constexpr IntrusiveRedBlackTreeNode* GetNode(Derived* parent) {
495 return std::addressof(parent->*Member);
496 }
497
498 static constexpr IntrusiveRedBlackTreeNode const* GetNode(Derived const* parent) {
499 return std::addressof(parent->*Member);
500 }
501
502 static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) {
503 return GetParentPointer<Member, Derived>(node);
504 }
505
506 static constexpr Derived const* GetParent(const IntrusiveRedBlackTreeNode* node) {
507 return GetParentPointer<Member, Derived>(node);
508 }
509
510private:
511 static constexpr TypedStorage<Derived> DerivedStorage = {};
512 static_assert(GetParent(GetNode(GetPointer(DerivedStorage))) == GetPointer(DerivedStorage));
513};
514
515template <auto T, class Derived = impl::GetParentType<T>>
516class IntrusiveRedBlackTreeMemberTraitsDeferredAssert;
517
518template <class Parent, IntrusiveRedBlackTreeNode Parent::*Member, class Derived>
519class IntrusiveRedBlackTreeMemberTraitsDeferredAssert<Member, Derived> {
520public:
521 template <class Comparator>
522 using TreeType =
523 IntrusiveRedBlackTree<Derived, IntrusiveRedBlackTreeMemberTraitsDeferredAssert, Comparator>;
524 using TreeTypeImpl = impl::IntrusiveRedBlackTreeImpl;
525
526 static constexpr bool IsValid() {
527 TypedStorage<Derived> DerivedStorage = {};
528 return GetParent(GetNode(GetPointer(DerivedStorage))) == GetPointer(DerivedStorage);
529 }
530
531private:
532 template <class, class, class>
533 friend class IntrusiveRedBlackTree;
534
535 friend class impl::IntrusiveRedBlackTreeImpl;
536
537 static constexpr IntrusiveRedBlackTreeNode* GetNode(Derived* parent) {
538 return std::addressof(parent->*Member);
539 }
540
541 static constexpr IntrusiveRedBlackTreeNode const* GetNode(Derived const* parent) {
542 return std::addressof(parent->*Member);
543 }
544
545 static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) {
546 return GetParentPointer<Member, Derived>(node);
547 }
548
549 static constexpr Derived const* GetParent(const IntrusiveRedBlackTreeNode* node) {
550 return GetParentPointer<Member, Derived>(node);
551 }
552};
553
554template <class Derived>
555class IntrusiveRedBlackTreeBaseNode : public IntrusiveRedBlackTreeNode {
556public:
557 constexpr Derived* GetPrev() {
558 return static_cast<Derived*>(impl::IntrusiveRedBlackTreeImpl::GetPrev(this));
559 }
560 constexpr const Derived* GetPrev() const {
561 return static_cast<const Derived*>(impl::IntrusiveRedBlackTreeImpl::GetPrev(this));
562 }
563
564 constexpr Derived* GetNext() {
565 return static_cast<Derived*>(impl::IntrusiveRedBlackTreeImpl::GetNext(this));
566 }
567 constexpr const Derived* GetNext() const {
568 return static_cast<const Derived*>(impl::IntrusiveRedBlackTreeImpl::GetNext(this));
569 }
570};
571
572template <class Derived>
573class IntrusiveRedBlackTreeBaseTraits {
574public:
575 template <class Comparator>
576 using TreeType = IntrusiveRedBlackTree<Derived, IntrusiveRedBlackTreeBaseTraits, Comparator>;
577 using TreeTypeImpl = impl::IntrusiveRedBlackTreeImpl;
578
579private:
580 template <class, class, class>
581 friend class IntrusiveRedBlackTree;
582
583 friend class impl::IntrusiveRedBlackTreeImpl;
584
585 static constexpr IntrusiveRedBlackTreeNode* GetNode(Derived* parent) {
586 return static_cast<IntrusiveRedBlackTreeNode*>(parent);
587 }
588
589 static constexpr IntrusiveRedBlackTreeNode const* GetNode(Derived const* parent) {
590 return static_cast<const IntrusiveRedBlackTreeNode*>(parent);
591 }
592
593 static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) {
594 return static_cast<Derived*>(node);
595 }
596
597 static constexpr Derived const* GetParent(const IntrusiveRedBlackTreeNode* node) {
598 return static_cast<const Derived*>(node);
599 }
600};
601
602} // namespace Common
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 631f64d05..2d4d2e9e7 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -145,10 +145,18 @@ void ColorConsoleBackend::Write(const Entry& entry) {
145 PrintColoredMessage(entry); 145 PrintColoredMessage(entry);
146} 146}
147 147
148// _SH_DENYWR allows read only access to the file for other programs. 148FileBackend::FileBackend(const std::string& filename) : bytes_written(0) {
149// It is #defined to 0 on other platforms 149 if (Common::FS::Exists(filename + ".old.txt")) {
150FileBackend::FileBackend(const std::string& filename) 150 Common::FS::Delete(filename + ".old.txt");
151 : file(filename, "w", _SH_DENYWR), bytes_written(0) {} 151 }
152 if (Common::FS::Exists(filename)) {
153 Common::FS::Rename(filename, filename + ".old.txt");
154 }
155
156 // _SH_DENYWR allows read only access to the file for other programs.
157 // It is #defined to 0 on other platforms
158 file = Common::FS::IOFile(filename, "w", _SH_DENYWR);
159}
152 160
153void FileBackend::Write(const Entry& entry) { 161void FileBackend::Write(const Entry& entry) {
154 // prevent logs from going over the maximum size (in case its spamming and the user doesn't 162 // prevent logs from going over the maximum size (in case its spamming and the user doesn't
diff --git a/src/common/page_table.h b/src/common/page_table.h
index 0c14e6433..61c5552e0 100644
--- a/src/common/page_table.h
+++ b/src/common/page_table.h
@@ -90,7 +90,7 @@ struct PageTable {
90 PageTable& operator=(PageTable&&) noexcept = default; 90 PageTable& operator=(PageTable&&) noexcept = default;
91 91
92 /** 92 /**
93 * Resizes the page table to be able to accomodate enough pages within 93 * Resizes the page table to be able to accommodate enough pages within
94 * a given address space. 94 * a given address space.
95 * 95 *
96 * @param address_space_width_in_bits The address size width in bits. 96 * @param address_space_width_in_bits The address size width in bits.
diff --git a/src/common/parent_of_member.h b/src/common/parent_of_member.h
new file mode 100644
index 000000000..d9a14529d
--- /dev/null
+++ b/src/common/parent_of_member.h
@@ -0,0 +1,191 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <type_traits>
8
9#include "common/assert.h"
10#include "common/common_types.h"
11
12namespace Common {
13namespace detail {
14template <typename T, size_t Size, size_t Align>
15struct TypedStorageImpl {
16 std::aligned_storage_t<Size, Align> storage_;
17};
18} // namespace detail
19
20template <typename T>
21using TypedStorage = detail::TypedStorageImpl<T, sizeof(T), alignof(T)>;
22
23template <typename T>
24static constexpr T* GetPointer(TypedStorage<T>& ts) {
25 return static_cast<T*>(static_cast<void*>(std::addressof(ts.storage_)));
26}
27
28template <typename T>
29static constexpr const T* GetPointer(const TypedStorage<T>& ts) {
30 return static_cast<const T*>(static_cast<const void*>(std::addressof(ts.storage_)));
31}
32
33namespace impl {
34
35template <size_t MaxDepth>
36struct OffsetOfUnionHolder {
37 template <typename ParentType, typename MemberType, size_t Offset>
38 union UnionImpl {
39 using PaddingMember = char;
40 static constexpr size_t GetOffset() {
41 return Offset;
42 }
43
44#pragma pack(push, 1)
45 struct {
46 PaddingMember padding[Offset];
47 MemberType members[(sizeof(ParentType) / sizeof(MemberType)) + 1];
48 } data;
49#pragma pack(pop)
50 UnionImpl<ParentType, MemberType, Offset + 1> next_union;
51 };
52
53 template <typename ParentType, typename MemberType>
54 union UnionImpl<ParentType, MemberType, 0> {
55 static constexpr size_t GetOffset() {
56 return 0;
57 }
58
59 struct {
60 MemberType members[(sizeof(ParentType) / sizeof(MemberType)) + 1];
61 } data;
62 UnionImpl<ParentType, MemberType, 1> next_union;
63 };
64
65 template <typename ParentType, typename MemberType>
66 union UnionImpl<ParentType, MemberType, MaxDepth> {};
67};
68
69template <typename ParentType, typename MemberType>
70struct OffsetOfCalculator {
71 using UnionHolder =
72 typename OffsetOfUnionHolder<sizeof(MemberType)>::template UnionImpl<ParentType, MemberType,
73 0>;
74 union Union {
75 char c{};
76 UnionHolder first_union;
77 TypedStorage<ParentType> parent;
78
79 constexpr Union() : c() {}
80 };
81 static constexpr Union U = {};
82
83 static constexpr const MemberType* GetNextAddress(const MemberType* start,
84 const MemberType* target) {
85 while (start < target) {
86 start++;
87 }
88 return start;
89 }
90
91 static constexpr std::ptrdiff_t GetDifference(const MemberType* start,
92 const MemberType* target) {
93 return (target - start) * sizeof(MemberType);
94 }
95
96 template <typename CurUnion>
97 static constexpr std::ptrdiff_t OffsetOfImpl(MemberType ParentType::*member,
98 CurUnion& cur_union) {
99 constexpr size_t Offset = CurUnion::GetOffset();
100 const auto target = std::addressof(GetPointer(U.parent)->*member);
101 const auto start = std::addressof(cur_union.data.members[0]);
102 const auto next = GetNextAddress(start, target);
103
104 if (next != target) {
105 if constexpr (Offset < sizeof(MemberType) - 1) {
106 return OffsetOfImpl(member, cur_union.next_union);
107 } else {
108 UNREACHABLE();
109 }
110 }
111
112 return (next - start) * sizeof(MemberType) + Offset;
113 }
114
115 static constexpr std::ptrdiff_t OffsetOf(MemberType ParentType::*member) {
116 return OffsetOfImpl(member, U.first_union);
117 }
118};
119
120template <typename T>
121struct GetMemberPointerTraits;
122
123template <typename P, typename M>
124struct GetMemberPointerTraits<M P::*> {
125 using Parent = P;
126 using Member = M;
127};
128
129template <auto MemberPtr>
130using GetParentType = typename GetMemberPointerTraits<decltype(MemberPtr)>::Parent;
131
132template <auto MemberPtr>
133using GetMemberType = typename GetMemberPointerTraits<decltype(MemberPtr)>::Member;
134
135template <auto MemberPtr, typename RealParentType = GetParentType<MemberPtr>>
136static inline std::ptrdiff_t OffsetOf = [] {
137 using DeducedParentType = GetParentType<MemberPtr>;
138 using MemberType = GetMemberType<MemberPtr>;
139 static_assert(std::is_base_of<DeducedParentType, RealParentType>::value ||
140 std::is_same<RealParentType, DeducedParentType>::value);
141
142 return OffsetOfCalculator<RealParentType, MemberType>::OffsetOf(MemberPtr);
143}();
144
145} // namespace impl
146
147template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
148constexpr RealParentType& GetParentReference(impl::GetMemberType<MemberPtr>* member) {
149 std::ptrdiff_t Offset = impl::OffsetOf<MemberPtr, RealParentType>;
150 return *static_cast<RealParentType*>(
151 static_cast<void*>(static_cast<uint8_t*>(static_cast<void*>(member)) - Offset));
152}
153
154template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
155constexpr RealParentType const& GetParentReference(impl::GetMemberType<MemberPtr> const* member) {
156 std::ptrdiff_t Offset = impl::OffsetOf<MemberPtr, RealParentType>;
157 return *static_cast<const RealParentType*>(static_cast<const void*>(
158 static_cast<const uint8_t*>(static_cast<const void*>(member)) - Offset));
159}
160
161template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
162constexpr RealParentType* GetParentPointer(impl::GetMemberType<MemberPtr>* member) {
163 return std::addressof(GetParentReference<MemberPtr, RealParentType>(member));
164}
165
166template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
167constexpr RealParentType const* GetParentPointer(impl::GetMemberType<MemberPtr> const* member) {
168 return std::addressof(GetParentReference<MemberPtr, RealParentType>(member));
169}
170
171template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
172constexpr RealParentType& GetParentReference(impl::GetMemberType<MemberPtr>& member) {
173 return GetParentReference<MemberPtr, RealParentType>(std::addressof(member));
174}
175
176template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
177constexpr RealParentType const& GetParentReference(impl::GetMemberType<MemberPtr> const& member) {
178 return GetParentReference<MemberPtr, RealParentType>(std::addressof(member));
179}
180
181template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
182constexpr RealParentType* GetParentPointer(impl::GetMemberType<MemberPtr>& member) {
183 return std::addressof(GetParentReference<MemberPtr, RealParentType>(member));
184}
185
186template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
187constexpr RealParentType const* GetParentPointer(impl::GetMemberType<MemberPtr> const& member) {
188 return std::addressof(GetParentReference<MemberPtr, RealParentType>(member));
189}
190
191} // namespace Common
diff --git a/src/common/swap.h b/src/common/swap.h
index 7665942a2..a80e191dc 100644
--- a/src/common/swap.h
+++ b/src/common/swap.h
@@ -394,7 +394,7 @@ public:
394 template <typename S, typename T2, typename F2> 394 template <typename S, typename T2, typename F2>
395 friend S operator%(const S& p, const swapped_t v); 395 friend S operator%(const S& p, const swapped_t v);
396 396
397 // Arithmetics + assignements 397 // Arithmetics + assignments
398 template <typename S, typename T2, typename F2> 398 template <typename S, typename T2, typename F2>
399 friend S operator+=(const S& p, const swapped_t v); 399 friend S operator+=(const S& p, const swapped_t v);
400 400
@@ -451,7 +451,7 @@ S operator%(const S& i, const swap_struct_t<T, F> v) {
451 return i % v.swap(); 451 return i % v.swap();
452} 452}
453 453
454// Arithmetics + assignements 454// Arithmetics + assignments
455template <typename S, typename T, typename F> 455template <typename S, typename T, typename F>
456S& operator+=(S& i, const swap_struct_t<T, F> v) { 456S& operator+=(S& i, const swap_struct_t<T, F> v) {
457 i += v.swap(); 457 i += v.swap();
diff --git a/src/common/timer.cpp b/src/common/timer.cpp
deleted file mode 100644
index d17dc2a50..000000000
--- a/src/common/timer.cpp
+++ /dev/null
@@ -1,159 +0,0 @@
1// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <ctime>
6#include <fmt/format.h>
7#include "common/common_types.h"
8#include "common/string_util.h"
9#include "common/timer.h"
10
11namespace Common {
12
13std::chrono::milliseconds Timer::GetTimeMs() {
14 return std::chrono::duration_cast<std::chrono::milliseconds>(
15 std::chrono::system_clock::now().time_since_epoch());
16}
17
18// --------------------------------------------
19// Initiate, Start, Stop, and Update the time
20// --------------------------------------------
21
22// Set initial values for the class
23Timer::Timer() : m_LastTime(0), m_StartTime(0), m_Running(false) {
24 Update();
25}
26
27// Write the starting time
28void Timer::Start() {
29 m_StartTime = GetTimeMs();
30 m_Running = true;
31}
32
33// Stop the timer
34void Timer::Stop() {
35 // Write the final time
36 m_LastTime = GetTimeMs();
37 m_Running = false;
38}
39
40// Update the last time variable
41void Timer::Update() {
42 m_LastTime = GetTimeMs();
43 // TODO(ector) - QPF
44}
45
46// -------------------------------------
47// Get time difference and elapsed time
48// -------------------------------------
49
50// Get the number of milliseconds since the last Update()
51std::chrono::milliseconds Timer::GetTimeDifference() {
52 return GetTimeMs() - m_LastTime;
53}
54
55// Add the time difference since the last Update() to the starting time.
56// This is used to compensate for a paused game.
57void Timer::AddTimeDifference() {
58 m_StartTime += GetTimeDifference();
59}
60
61// Get the time elapsed since the Start()
62std::chrono::milliseconds Timer::GetTimeElapsed() {
63 // If we have not started yet, return 1 (because then I don't
64 // have to change the FPS calculation in CoreRerecording.cpp .
65 if (m_StartTime.count() == 0)
66 return std::chrono::milliseconds(1);
67
68 // Return the final timer time if the timer is stopped
69 if (!m_Running)
70 return (m_LastTime - m_StartTime);
71
72 return (GetTimeMs() - m_StartTime);
73}
74
75// Get the formatted time elapsed since the Start()
76std::string Timer::GetTimeElapsedFormatted() const {
77 // If we have not started yet, return zero
78 if (m_StartTime.count() == 0)
79 return "00:00:00:000";
80
81 // The number of milliseconds since the start.
82 // Use a different value if the timer is stopped.
83 std::chrono::milliseconds Milliseconds;
84 if (m_Running)
85 Milliseconds = GetTimeMs() - m_StartTime;
86 else
87 Milliseconds = m_LastTime - m_StartTime;
88 // Seconds
89 std::chrono::seconds Seconds = std::chrono::duration_cast<std::chrono::seconds>(Milliseconds);
90 // Minutes
91 std::chrono::minutes Minutes = std::chrono::duration_cast<std::chrono::minutes>(Milliseconds);
92 // Hours
93 std::chrono::hours Hours = std::chrono::duration_cast<std::chrono::hours>(Milliseconds);
94
95 std::string TmpStr = fmt::format("{:02}:{:02}:{:02}:{:03}", Hours.count(), Minutes.count() % 60,
96 Seconds.count() % 60, Milliseconds.count() % 1000);
97 return TmpStr;
98}
99
100// Get the number of seconds since January 1 1970
101std::chrono::seconds Timer::GetTimeSinceJan1970() {
102 return std::chrono::duration_cast<std::chrono::seconds>(GetTimeMs());
103}
104
105std::chrono::seconds Timer::GetLocalTimeSinceJan1970() {
106 time_t sysTime, tzDiff, tzDST;
107 struct tm* gmTime;
108
109 time(&sysTime);
110
111 // Account for DST where needed
112 gmTime = localtime(&sysTime);
113 if (gmTime->tm_isdst == 1)
114 tzDST = 3600;
115 else
116 tzDST = 0;
117
118 // Lazy way to get local time in sec
119 gmTime = gmtime(&sysTime);
120 tzDiff = sysTime - mktime(gmTime);
121
122 return std::chrono::seconds(sysTime + tzDiff + tzDST);
123}
124
125// Return the current time formatted as Minutes:Seconds:Milliseconds
126// in the form 00:00:000.
127std::string Timer::GetTimeFormatted() {
128 time_t sysTime;
129 struct tm* gmTime;
130 char tmp[13];
131
132 time(&sysTime);
133 gmTime = localtime(&sysTime);
134
135 strftime(tmp, 6, "%M:%S", gmTime);
136
137 u64 milliseconds = static_cast<u64>(GetTimeMs().count()) % 1000;
138 return fmt::format("{}:{:03}", tmp, milliseconds);
139}
140
141// Returns a timestamp with decimals for precise time comparisons
142// ----------------
143double Timer::GetDoubleTime() {
144 // Get continuous timestamp
145 auto tmp_seconds = static_cast<u64>(GetTimeSinceJan1970().count());
146 const auto ms = static_cast<double>(static_cast<u64>(GetTimeMs().count()) % 1000);
147
148 // Remove a few years. We only really want enough seconds to make
149 // sure that we are detecting actual actions, perhaps 60 seconds is
150 // enough really, but I leave a year of seconds anyway, in case the
151 // user's clock is incorrect or something like that.
152 tmp_seconds = tmp_seconds - (38 * 365 * 24 * 60 * 60);
153
154 // Make a smaller integer that fits in the double
155 const auto seconds = static_cast<u32>(tmp_seconds);
156 return seconds + ms;
157}
158
159} // Namespace Common
diff --git a/src/common/timer.h b/src/common/timer.h
deleted file mode 100644
index 8894a143d..000000000
--- a/src/common/timer.h
+++ /dev/null
@@ -1,41 +0,0 @@
1// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <chrono>
8#include <string>
9#include "common/common_types.h"
10
11namespace Common {
12class Timer {
13public:
14 Timer();
15
16 void Start();
17 void Stop();
18 void Update();
19
20 // The time difference is always returned in milliseconds, regardless of alternative internal
21 // representation
22 [[nodiscard]] std::chrono::milliseconds GetTimeDifference();
23 void AddTimeDifference();
24
25 [[nodiscard]] static std::chrono::seconds GetTimeSinceJan1970();
26 [[nodiscard]] static std::chrono::seconds GetLocalTimeSinceJan1970();
27 [[nodiscard]] static double GetDoubleTime();
28
29 [[nodiscard]] static std::string GetTimeFormatted();
30 [[nodiscard]] std::string GetTimeElapsedFormatted() const;
31 [[nodiscard]] std::chrono::milliseconds GetTimeElapsed();
32
33 [[nodiscard]] static std::chrono::milliseconds GetTimeMs();
34
35private:
36 std::chrono::milliseconds m_LastTime;
37 std::chrono::milliseconds m_StartTime;
38 bool m_Running;
39};
40
41} // Namespace Common
diff --git a/src/common/tree.h b/src/common/tree.h
new file mode 100644
index 000000000..3da49e422
--- /dev/null
+++ b/src/common/tree.h
@@ -0,0 +1,674 @@
1/* $NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $ */
2/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */
3/* $FreeBSD$ */
4
5/*-
6 * Copyright 2002 Niels Provos <provos@citi.umich.edu>
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#pragma once
31
32/*
33 * This file defines data structures for red-black trees.
34 *
35 * A red-black tree is a binary search tree with the node color as an
36 * extra attribute. It fulfills a set of conditions:
37 * - every search path from the root to a leaf consists of the
38 * same number of black nodes,
39 * - each red node (except for the root) has a black parent,
40 * - each leaf node is black.
41 *
42 * Every operation on a red-black tree is bounded as O(lg n).
43 * The maximum height of a red-black tree is 2lg (n+1).
44 */
45
46namespace Common {
47template <typename T>
48class RBHead {
49public:
50 [[nodiscard]] T* Root() {
51 return rbh_root;
52 }
53
54 [[nodiscard]] const T* Root() const {
55 return rbh_root;
56 }
57
58 void SetRoot(T* root) {
59 rbh_root = root;
60 }
61
62 [[nodiscard]] bool IsEmpty() const {
63 return Root() == nullptr;
64 }
65
66private:
67 T* rbh_root = nullptr;
68};
69
70enum class EntryColor {
71 Black,
72 Red,
73};
74
75template <typename T>
76class RBEntry {
77public:
78 [[nodiscard]] T* Left() {
79 return rbe_left;
80 }
81
82 [[nodiscard]] const T* Left() const {
83 return rbe_left;
84 }
85
86 void SetLeft(T* left) {
87 rbe_left = left;
88 }
89
90 [[nodiscard]] T* Right() {
91 return rbe_right;
92 }
93
94 [[nodiscard]] const T* Right() const {
95 return rbe_right;
96 }
97
98 void SetRight(T* right) {
99 rbe_right = right;
100 }
101
102 [[nodiscard]] T* Parent() {
103 return rbe_parent;
104 }
105
106 [[nodiscard]] const T* Parent() const {
107 return rbe_parent;
108 }
109
110 void SetParent(T* parent) {
111 rbe_parent = parent;
112 }
113
114 [[nodiscard]] bool IsBlack() const {
115 return rbe_color == EntryColor::Black;
116 }
117
118 [[nodiscard]] bool IsRed() const {
119 return rbe_color == EntryColor::Red;
120 }
121
122 [[nodiscard]] EntryColor Color() const {
123 return rbe_color;
124 }
125
126 void SetColor(EntryColor color) {
127 rbe_color = color;
128 }
129
130private:
131 T* rbe_left = nullptr;
132 T* rbe_right = nullptr;
133 T* rbe_parent = nullptr;
134 EntryColor rbe_color{};
135};
136
137template <typename Node>
138[[nodiscard]] RBEntry<Node>& RB_ENTRY(Node* node) {
139 return node->GetEntry();
140}
141
142template <typename Node>
143[[nodiscard]] const RBEntry<Node>& RB_ENTRY(const Node* node) {
144 return node->GetEntry();
145}
146
147template <typename Node>
148[[nodiscard]] Node* RB_PARENT(Node* node) {
149 return RB_ENTRY(node).Parent();
150}
151
152template <typename Node>
153[[nodiscard]] const Node* RB_PARENT(const Node* node) {
154 return RB_ENTRY(node).Parent();
155}
156
157template <typename Node>
158void RB_SET_PARENT(Node* node, Node* parent) {
159 return RB_ENTRY(node).SetParent(parent);
160}
161
162template <typename Node>
163[[nodiscard]] Node* RB_LEFT(Node* node) {
164 return RB_ENTRY(node).Left();
165}
166
167template <typename Node>
168[[nodiscard]] const Node* RB_LEFT(const Node* node) {
169 return RB_ENTRY(node).Left();
170}
171
172template <typename Node>
173void RB_SET_LEFT(Node* node, Node* left) {
174 return RB_ENTRY(node).SetLeft(left);
175}
176
177template <typename Node>
178[[nodiscard]] Node* RB_RIGHT(Node* node) {
179 return RB_ENTRY(node).Right();
180}
181
182template <typename Node>
183[[nodiscard]] const Node* RB_RIGHT(const Node* node) {
184 return RB_ENTRY(node).Right();
185}
186
187template <typename Node>
188void RB_SET_RIGHT(Node* node, Node* right) {
189 return RB_ENTRY(node).SetRight(right);
190}
191
192template <typename Node>
193[[nodiscard]] bool RB_IS_BLACK(const Node* node) {
194 return RB_ENTRY(node).IsBlack();
195}
196
197template <typename Node>
198[[nodiscard]] bool RB_IS_RED(const Node* node) {
199 return RB_ENTRY(node).IsRed();
200}
201
202template <typename Node>
203[[nodiscard]] EntryColor RB_COLOR(const Node* node) {
204 return RB_ENTRY(node).Color();
205}
206
207template <typename Node>
208void RB_SET_COLOR(Node* node, EntryColor color) {
209 return RB_ENTRY(node).SetColor(color);
210}
211
212template <typename Node>
213void RB_SET(Node* node, Node* parent) {
214 auto& entry = RB_ENTRY(node);
215 entry.SetParent(parent);
216 entry.SetLeft(nullptr);
217 entry.SetRight(nullptr);
218 entry.SetColor(EntryColor::Red);
219}
220
221template <typename Node>
222void RB_SET_BLACKRED(Node* black, Node* red) {
223 RB_SET_COLOR(black, EntryColor::Black);
224 RB_SET_COLOR(red, EntryColor::Red);
225}
226
227template <typename Node>
228void RB_ROTATE_LEFT(RBHead<Node>* head, Node* elm, Node*& tmp) {
229 tmp = RB_RIGHT(elm);
230 RB_SET_RIGHT(elm, RB_LEFT(tmp));
231 if (RB_RIGHT(elm) != nullptr) {
232 RB_SET_PARENT(RB_LEFT(tmp), elm);
233 }
234
235 RB_SET_PARENT(tmp, RB_PARENT(elm));
236 if (RB_PARENT(tmp) != nullptr) {
237 if (elm == RB_LEFT(RB_PARENT(elm))) {
238 RB_SET_LEFT(RB_PARENT(elm), tmp);
239 } else {
240 RB_SET_RIGHT(RB_PARENT(elm), tmp);
241 }
242 } else {
243 head->SetRoot(tmp);
244 }
245
246 RB_SET_LEFT(tmp, elm);
247 RB_SET_PARENT(elm, tmp);
248}
249
250template <typename Node>
251void RB_ROTATE_RIGHT(RBHead<Node>* head, Node* elm, Node*& tmp) {
252 tmp = RB_LEFT(elm);
253 RB_SET_LEFT(elm, RB_RIGHT(tmp));
254 if (RB_LEFT(elm) != nullptr) {
255 RB_SET_PARENT(RB_RIGHT(tmp), elm);
256 }
257
258 RB_SET_PARENT(tmp, RB_PARENT(elm));
259 if (RB_PARENT(tmp) != nullptr) {
260 if (elm == RB_LEFT(RB_PARENT(elm))) {
261 RB_SET_LEFT(RB_PARENT(elm), tmp);
262 } else {
263 RB_SET_RIGHT(RB_PARENT(elm), tmp);
264 }
265 } else {
266 head->SetRoot(tmp);
267 }
268
269 RB_SET_RIGHT(tmp, elm);
270 RB_SET_PARENT(elm, tmp);
271}
272
273template <typename Node>
274void RB_INSERT_COLOR(RBHead<Node>* head, Node* elm) {
275 Node* parent = nullptr;
276 Node* tmp = nullptr;
277
278 while ((parent = RB_PARENT(elm)) != nullptr && RB_IS_RED(parent)) {
279 Node* gparent = RB_PARENT(parent);
280 if (parent == RB_LEFT(gparent)) {
281 tmp = RB_RIGHT(gparent);
282 if (tmp && RB_IS_RED(tmp)) {
283 RB_SET_COLOR(tmp, EntryColor::Black);
284 RB_SET_BLACKRED(parent, gparent);
285 elm = gparent;
286 continue;
287 }
288
289 if (RB_RIGHT(parent) == elm) {
290 RB_ROTATE_LEFT(head, parent, tmp);
291 tmp = parent;
292 parent = elm;
293 elm = tmp;
294 }
295
296 RB_SET_BLACKRED(parent, gparent);
297 RB_ROTATE_RIGHT(head, gparent, tmp);
298 } else {
299 tmp = RB_LEFT(gparent);
300 if (tmp && RB_IS_RED(tmp)) {
301 RB_SET_COLOR(tmp, EntryColor::Black);
302 RB_SET_BLACKRED(parent, gparent);
303 elm = gparent;
304 continue;
305 }
306
307 if (RB_LEFT(parent) == elm) {
308 RB_ROTATE_RIGHT(head, parent, tmp);
309 tmp = parent;
310 parent = elm;
311 elm = tmp;
312 }
313
314 RB_SET_BLACKRED(parent, gparent);
315 RB_ROTATE_LEFT(head, gparent, tmp);
316 }
317 }
318
319 RB_SET_COLOR(head->Root(), EntryColor::Black);
320}
321
322template <typename Node>
323void RB_REMOVE_COLOR(RBHead<Node>* head, Node* parent, Node* elm) {
324 Node* tmp;
325 while ((elm == nullptr || RB_IS_BLACK(elm)) && elm != head->Root()) {
326 if (RB_LEFT(parent) == elm) {
327 tmp = RB_RIGHT(parent);
328 if (RB_IS_RED(tmp)) {
329 RB_SET_BLACKRED(tmp, parent);
330 RB_ROTATE_LEFT(head, parent, tmp);
331 tmp = RB_RIGHT(parent);
332 }
333
334 if ((RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) &&
335 (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp)))) {
336 RB_SET_COLOR(tmp, EntryColor::Red);
337 elm = parent;
338 parent = RB_PARENT(elm);
339 } else {
340 if (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp))) {
341 Node* oleft;
342 if ((oleft = RB_LEFT(tmp)) != nullptr) {
343 RB_SET_COLOR(oleft, EntryColor::Black);
344 }
345
346 RB_SET_COLOR(tmp, EntryColor::Red);
347 RB_ROTATE_RIGHT(head, tmp, oleft);
348 tmp = RB_RIGHT(parent);
349 }
350
351 RB_SET_COLOR(tmp, RB_COLOR(parent));
352 RB_SET_COLOR(parent, EntryColor::Black);
353 if (RB_RIGHT(tmp)) {
354 RB_SET_COLOR(RB_RIGHT(tmp), EntryColor::Black);
355 }
356
357 RB_ROTATE_LEFT(head, parent, tmp);
358 elm = head->Root();
359 break;
360 }
361 } else {
362 tmp = RB_LEFT(parent);
363 if (RB_IS_RED(tmp)) {
364 RB_SET_BLACKRED(tmp, parent);
365 RB_ROTATE_RIGHT(head, parent, tmp);
366 tmp = RB_LEFT(parent);
367 }
368
369 if ((RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) &&
370 (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp)))) {
371 RB_SET_COLOR(tmp, EntryColor::Red);
372 elm = parent;
373 parent = RB_PARENT(elm);
374 } else {
375 if (RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) {
376 Node* oright;
377 if ((oright = RB_RIGHT(tmp)) != nullptr) {
378 RB_SET_COLOR(oright, EntryColor::Black);
379 }
380
381 RB_SET_COLOR(tmp, EntryColor::Red);
382 RB_ROTATE_LEFT(head, tmp, oright);
383 tmp = RB_LEFT(parent);
384 }
385
386 RB_SET_COLOR(tmp, RB_COLOR(parent));
387 RB_SET_COLOR(parent, EntryColor::Black);
388
389 if (RB_LEFT(tmp)) {
390 RB_SET_COLOR(RB_LEFT(tmp), EntryColor::Black);
391 }
392
393 RB_ROTATE_RIGHT(head, parent, tmp);
394 elm = head->Root();
395 break;
396 }
397 }
398 }
399
400 if (elm) {
401 RB_SET_COLOR(elm, EntryColor::Black);
402 }
403}
404
405template <typename Node>
406Node* RB_REMOVE(RBHead<Node>* head, Node* elm) {
407 Node* child = nullptr;
408 Node* parent = nullptr;
409 Node* old = elm;
410 EntryColor color{};
411
412 const auto finalize = [&] {
413 if (color == EntryColor::Black) {
414 RB_REMOVE_COLOR(head, parent, child);
415 }
416
417 return old;
418 };
419
420 if (RB_LEFT(elm) == nullptr) {
421 child = RB_RIGHT(elm);
422 } else if (RB_RIGHT(elm) == nullptr) {
423 child = RB_LEFT(elm);
424 } else {
425 Node* left;
426 elm = RB_RIGHT(elm);
427 while ((left = RB_LEFT(elm)) != nullptr) {
428 elm = left;
429 }
430
431 child = RB_RIGHT(elm);
432 parent = RB_PARENT(elm);
433 color = RB_COLOR(elm);
434
435 if (child) {
436 RB_SET_PARENT(child, parent);
437 }
438 if (parent) {
439 if (RB_LEFT(parent) == elm) {
440 RB_SET_LEFT(parent, child);
441 } else {
442 RB_SET_RIGHT(parent, child);
443 }
444 } else {
445 head->SetRoot(child);
446 }
447
448 if (RB_PARENT(elm) == old) {
449 parent = elm;
450 }
451
452 elm->SetEntry(old->GetEntry());
453
454 if (RB_PARENT(old)) {
455 if (RB_LEFT(RB_PARENT(old)) == old) {
456 RB_SET_LEFT(RB_PARENT(old), elm);
457 } else {
458 RB_SET_RIGHT(RB_PARENT(old), elm);
459 }
460 } else {
461 head->SetRoot(elm);
462 }
463 RB_SET_PARENT(RB_LEFT(old), elm);
464 if (RB_RIGHT(old)) {
465 RB_SET_PARENT(RB_RIGHT(old), elm);
466 }
467 if (parent) {
468 left = parent;
469 }
470
471 return finalize();
472 }
473
474 parent = RB_PARENT(elm);
475 color = RB_COLOR(elm);
476
477 if (child) {
478 RB_SET_PARENT(child, parent);
479 }
480 if (parent) {
481 if (RB_LEFT(parent) == elm) {
482 RB_SET_LEFT(parent, child);
483 } else {
484 RB_SET_RIGHT(parent, child);
485 }
486 } else {
487 head->SetRoot(child);
488 }
489
490 return finalize();
491}
492
493// Inserts a node into the RB tree
494template <typename Node, typename CompareFunction>
495Node* RB_INSERT(RBHead<Node>* head, Node* elm, CompareFunction cmp) {
496 Node* parent = nullptr;
497 Node* tmp = head->Root();
498 int comp = 0;
499
500 while (tmp) {
501 parent = tmp;
502 comp = cmp(elm, parent);
503 if (comp < 0) {
504 tmp = RB_LEFT(tmp);
505 } else if (comp > 0) {
506 tmp = RB_RIGHT(tmp);
507 } else {
508 return tmp;
509 }
510 }
511
512 RB_SET(elm, parent);
513
514 if (parent != nullptr) {
515 if (comp < 0) {
516 RB_SET_LEFT(parent, elm);
517 } else {
518 RB_SET_RIGHT(parent, elm);
519 }
520 } else {
521 head->SetRoot(elm);
522 }
523
524 RB_INSERT_COLOR(head, elm);
525 return nullptr;
526}
527
528// Finds the node with the same key as elm
529template <typename Node, typename CompareFunction>
530Node* RB_FIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) {
531 Node* tmp = head->Root();
532
533 while (tmp) {
534 const int comp = cmp(elm, tmp);
535 if (comp < 0) {
536 tmp = RB_LEFT(tmp);
537 } else if (comp > 0) {
538 tmp = RB_RIGHT(tmp);
539 } else {
540 return tmp;
541 }
542 }
543
544 return nullptr;
545}
546
547// Finds the first node greater than or equal to the search key
548template <typename Node, typename CompareFunction>
549Node* RB_NFIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) {
550 Node* tmp = head->Root();
551 Node* res = nullptr;
552
553 while (tmp) {
554 const int comp = cmp(elm, tmp);
555 if (comp < 0) {
556 res = tmp;
557 tmp = RB_LEFT(tmp);
558 } else if (comp > 0) {
559 tmp = RB_RIGHT(tmp);
560 } else {
561 return tmp;
562 }
563 }
564
565 return res;
566}
567
568// Finds the node with the same key as lelm
569template <typename Node, typename CompareFunction>
570Node* RB_FIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp) {
571 Node* tmp = head->Root();
572
573 while (tmp) {
574 const int comp = lcmp(lelm, tmp);
575 if (comp < 0) {
576 tmp = RB_LEFT(tmp);
577 } else if (comp > 0) {
578 tmp = RB_RIGHT(tmp);
579 } else {
580 return tmp;
581 }
582 }
583
584 return nullptr;
585}
586
587// Finds the first node greater than or equal to the search key
588template <typename Node, typename CompareFunction>
589Node* RB_NFIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp) {
590 Node* tmp = head->Root();
591 Node* res = nullptr;
592
593 while (tmp) {
594 const int comp = lcmp(lelm, tmp);
595 if (comp < 0) {
596 res = tmp;
597 tmp = RB_LEFT(tmp);
598 } else if (comp > 0) {
599 tmp = RB_RIGHT(tmp);
600 } else {
601 return tmp;
602 }
603 }
604
605 return res;
606}
607
608template <typename Node>
609Node* RB_NEXT(Node* elm) {
610 if (RB_RIGHT(elm)) {
611 elm = RB_RIGHT(elm);
612 while (RB_LEFT(elm)) {
613 elm = RB_LEFT(elm);
614 }
615 } else {
616 if (RB_PARENT(elm) && (elm == RB_LEFT(RB_PARENT(elm)))) {
617 elm = RB_PARENT(elm);
618 } else {
619 while (RB_PARENT(elm) && (elm == RB_RIGHT(RB_PARENT(elm)))) {
620 elm = RB_PARENT(elm);
621 }
622 elm = RB_PARENT(elm);
623 }
624 }
625 return elm;
626}
627
628template <typename Node>
629Node* RB_PREV(Node* elm) {
630 if (RB_LEFT(elm)) {
631 elm = RB_LEFT(elm);
632 while (RB_RIGHT(elm)) {
633 elm = RB_RIGHT(elm);
634 }
635 } else {
636 if (RB_PARENT(elm) && (elm == RB_RIGHT(RB_PARENT(elm)))) {
637 elm = RB_PARENT(elm);
638 } else {
639 while (RB_PARENT(elm) && (elm == RB_LEFT(RB_PARENT(elm)))) {
640 elm = RB_PARENT(elm);
641 }
642 elm = RB_PARENT(elm);
643 }
644 }
645 return elm;
646}
647
648template <typename Node>
649Node* RB_MINMAX(RBHead<Node>* head, bool is_min) {
650 Node* tmp = head->Root();
651 Node* parent = nullptr;
652
653 while (tmp) {
654 parent = tmp;
655 if (is_min) {
656 tmp = RB_LEFT(tmp);
657 } else {
658 tmp = RB_RIGHT(tmp);
659 }
660 }
661
662 return parent;
663}
664
665template <typename Node>
666Node* RB_MIN(RBHead<Node>* head) {
667 return RB_MINMAX(head, true);
668}
669
670template <typename Node>
671Node* RB_MAX(RBHead<Node>* head) {
672 return RB_MINMAX(head, false);
673}
674} // namespace Common
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 01f3e9419..99310dc50 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -142,8 +142,6 @@ add_library(core STATIC
142 hardware_interrupt_manager.h 142 hardware_interrupt_manager.h
143 hle/ipc.h 143 hle/ipc.h
144 hle/ipc_helpers.h 144 hle/ipc_helpers.h
145 hle/kernel/address_arbiter.cpp
146 hle/kernel/address_arbiter.h
147 hle/kernel/client_port.cpp 145 hle/kernel/client_port.cpp
148 hle/kernel/client_port.h 146 hle/kernel/client_port.h
149 hle/kernel/client_session.cpp 147 hle/kernel/client_session.cpp
@@ -157,13 +155,19 @@ add_library(core STATIC
157 hle/kernel/handle_table.h 155 hle/kernel/handle_table.h
158 hle/kernel/hle_ipc.cpp 156 hle/kernel/hle_ipc.cpp
159 hle/kernel/hle_ipc.h 157 hle/kernel/hle_ipc.h
158 hle/kernel/k_address_arbiter.cpp
159 hle/kernel/k_address_arbiter.h
160 hle/kernel/k_affinity_mask.h 160 hle/kernel/k_affinity_mask.h
161 hle/kernel/k_condition_variable.cpp
162 hle/kernel/k_condition_variable.h
161 hle/kernel/k_priority_queue.h 163 hle/kernel/k_priority_queue.h
162 hle/kernel/k_scheduler.cpp 164 hle/kernel/k_scheduler.cpp
163 hle/kernel/k_scheduler.h 165 hle/kernel/k_scheduler.h
164 hle/kernel/k_scheduler_lock.h 166 hle/kernel/k_scheduler_lock.h
165 hle/kernel/k_scoped_lock.h 167 hle/kernel/k_scoped_lock.h
166 hle/kernel/k_scoped_scheduler_lock_and_sleep.h 168 hle/kernel/k_scoped_scheduler_lock_and_sleep.h
169 hle/kernel/k_synchronization_object.cpp
170 hle/kernel/k_synchronization_object.h
167 hle/kernel/kernel.cpp 171 hle/kernel/kernel.cpp
168 hle/kernel/kernel.h 172 hle/kernel/kernel.h
169 hle/kernel/memory/address_space_info.cpp 173 hle/kernel/memory/address_space_info.cpp
@@ -183,8 +187,6 @@ add_library(core STATIC
183 hle/kernel/memory/slab_heap.h 187 hle/kernel/memory/slab_heap.h
184 hle/kernel/memory/system_control.cpp 188 hle/kernel/memory/system_control.cpp
185 hle/kernel/memory/system_control.h 189 hle/kernel/memory/system_control.h
186 hle/kernel/mutex.cpp
187 hle/kernel/mutex.h
188 hle/kernel/object.cpp 190 hle/kernel/object.cpp
189 hle/kernel/object.h 191 hle/kernel/object.h
190 hle/kernel/physical_core.cpp 192 hle/kernel/physical_core.cpp
@@ -210,12 +212,10 @@ add_library(core STATIC
210 hle/kernel/shared_memory.h 212 hle/kernel/shared_memory.h
211 hle/kernel/svc.cpp 213 hle/kernel/svc.cpp
212 hle/kernel/svc.h 214 hle/kernel/svc.h
215 hle/kernel/svc_common.h
216 hle/kernel/svc_results.h
213 hle/kernel/svc_types.h 217 hle/kernel/svc_types.h
214 hle/kernel/svc_wrap.h 218 hle/kernel/svc_wrap.h
215 hle/kernel/synchronization_object.cpp
216 hle/kernel/synchronization_object.h
217 hle/kernel/synchronization.cpp
218 hle/kernel/synchronization.h
219 hle/kernel/thread.cpp 219 hle/kernel/thread.cpp
220 hle/kernel/thread.h 220 hle/kernel/thread.h
221 hle/kernel/time_manager.cpp 221 hle/kernel/time_manager.cpp
@@ -635,15 +635,15 @@ if (MSVC)
635 /we4267 635 /we4267
636 # 'context' : truncation from 'type1' to 'type2' 636 # 'context' : truncation from 'type1' to 'type2'
637 /we4305 637 /we4305
638 # 'function' : not all control paths return a value
639 /we4715
638 ) 640 )
639else() 641else()
640 target_compile_options(core PRIVATE 642 target_compile_options(core PRIVATE
641 -Werror=conversion 643 -Werror=conversion
642 -Werror=ignored-qualifiers 644 -Werror=ignored-qualifiers
643 -Werror=implicit-fallthrough 645 -Werror=implicit-fallthrough
644 -Werror=reorder
645 -Werror=sign-compare 646 -Werror=sign-compare
646 -Werror=unused-variable
647 647
648 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter> 648 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
649 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable> 649 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 70098c526..9a0151736 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -26,9 +26,10 @@ using CPUInterrupts = std::array<CPUInterruptHandler, Core::Hardware::NUM_CPU_CO
26/// Generic ARMv8 CPU interface 26/// Generic ARMv8 CPU interface
27class ARM_Interface : NonCopyable { 27class ARM_Interface : NonCopyable {
28public: 28public:
29 explicit ARM_Interface(System& system_, CPUInterrupts& interrupt_handlers, bool uses_wall_clock) 29 explicit ARM_Interface(System& system_, CPUInterrupts& interrupt_handlers_,
30 : system{system_}, interrupt_handlers{interrupt_handlers}, uses_wall_clock{ 30 bool uses_wall_clock_)
31 uses_wall_clock} {} 31 : system{system_}, interrupt_handlers{interrupt_handlers_}, uses_wall_clock{
32 uses_wall_clock_} {}
32 virtual ~ARM_Interface() = default; 33 virtual ~ARM_Interface() = default;
33 34
34 struct ThreadContext32 { 35 struct ThreadContext32 {
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index 8aaf11eee..6c4c8e9e4 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -71,15 +71,8 @@ public:
71 } 71 }
72 72
73 void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override { 73 void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
74 switch (exception) {
75 case Dynarmic::A32::Exception::UndefinedInstruction:
76 case Dynarmic::A32::Exception::UnpredictableInstruction:
77 break;
78 case Dynarmic::A32::Exception::Breakpoint:
79 break;
80 }
81 LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", 74 LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
82 static_cast<std::size_t>(exception), pc, MemoryReadCode(pc)); 75 exception, pc, MemoryReadCode(pc));
83 UNIMPLEMENTED(); 76 UNIMPLEMENTED();
84 } 77 }
85 78
@@ -181,6 +174,9 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable&
181 if (Settings::values.cpuopt_unsafe_reduce_fp_error) { 174 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
182 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; 175 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
183 } 176 }
177 if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
178 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
179 }
184 } 180 }
185 181
186 return std::make_unique<Dynarmic::A32::Jit>(config); 182 return std::make_unique<Dynarmic::A32::Jit>(config);
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index d2e1dc724..4c5ebca22 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -212,6 +212,9 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable&
212 if (Settings::values.cpuopt_unsafe_reduce_fp_error) { 212 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
213 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; 213 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
214 } 214 }
215 if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
216 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
217 }
215 } 218 }
216 219
217 return std::make_shared<Dynarmic::A64::Jit>(config); 220 return std::make_shared<Dynarmic::A64::Jit>(config);
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index e6c8461a5..874b5673a 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -49,6 +49,7 @@ void CoreTiming::ThreadEntry(CoreTiming& instance) {
49 Common::SetCurrentThreadPriority(Common::ThreadPriority::VeryHigh); 49 Common::SetCurrentThreadPriority(Common::ThreadPriority::VeryHigh);
50 instance.on_thread_init(); 50 instance.on_thread_init();
51 instance.ThreadLoop(); 51 instance.ThreadLoop();
52 MicroProfileOnThreadExit();
52} 53}
53 54
54void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) { 55void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index da15f764a..cebe2ce37 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -143,6 +143,7 @@ u64 GetSignatureTypeDataSize(SignatureType type) {
143 return 0x3C; 143 return 0x3C;
144 } 144 }
145 UNREACHABLE(); 145 UNREACHABLE();
146 return 0;
146} 147}
147 148
148u64 GetSignatureTypePaddingSize(SignatureType type) { 149u64 GetSignatureTypePaddingSize(SignatureType type) {
@@ -157,6 +158,7 @@ u64 GetSignatureTypePaddingSize(SignatureType type) {
157 return 0x40; 158 return 0x40;
158 } 159 }
159 UNREACHABLE(); 160 UNREACHABLE();
161 return 0;
160} 162}
161 163
162SignatureType Ticket::GetSignatureType() const { 164SignatureType Ticket::GetSignatureType() const {
@@ -169,8 +171,7 @@ SignatureType Ticket::GetSignatureType() const {
169 if (const auto* ticket = std::get_if<ECDSATicket>(&data)) { 171 if (const auto* ticket = std::get_if<ECDSATicket>(&data)) {
170 return ticket->sig_type; 172 return ticket->sig_type;
171 } 173 }
172 174 throw std::bad_variant_access{};
173 UNREACHABLE();
174} 175}
175 176
176TicketData& Ticket::GetData() { 177TicketData& Ticket::GetData() {
@@ -183,8 +184,7 @@ TicketData& Ticket::GetData() {
183 if (auto* ticket = std::get_if<ECDSATicket>(&data)) { 184 if (auto* ticket = std::get_if<ECDSATicket>(&data)) {
184 return ticket->data; 185 return ticket->data;
185 } 186 }
186 187 throw std::bad_variant_access{};
187 UNREACHABLE();
188} 188}
189 189
190const TicketData& Ticket::GetData() const { 190const TicketData& Ticket::GetData() const {
@@ -197,8 +197,7 @@ const TicketData& Ticket::GetData() const {
197 if (const auto* ticket = std::get_if<ECDSATicket>(&data)) { 197 if (const auto* ticket = std::get_if<ECDSATicket>(&data)) {
198 return ticket->data; 198 return ticket->data;
199 } 199 }
200 200 throw std::bad_variant_access{};
201 UNREACHABLE();
202} 201}
203 202
204u64 Ticket::GetSize() const { 203u64 Ticket::GetSize() const {
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index a6c0337fa..d12218fc2 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -43,17 +43,17 @@ static_assert(sizeof(IVFCLevel) == 0x18, "IVFCLevel has incorrect size.");
43struct IVFCHeader { 43struct IVFCHeader {
44 u32_le magic; 44 u32_le magic;
45 u32_le magic_number; 45 u32_le magic_number;
46 INSERT_UNION_PADDING_BYTES(8); 46 INSERT_PADDING_BYTES_NOINIT(8);
47 std::array<IVFCLevel, 6> levels; 47 std::array<IVFCLevel, 6> levels;
48 INSERT_UNION_PADDING_BYTES(64); 48 INSERT_PADDING_BYTES_NOINIT(64);
49}; 49};
50static_assert(sizeof(IVFCHeader) == 0xE0, "IVFCHeader has incorrect size."); 50static_assert(sizeof(IVFCHeader) == 0xE0, "IVFCHeader has incorrect size.");
51 51
52struct NCASectionHeaderBlock { 52struct NCASectionHeaderBlock {
53 INSERT_UNION_PADDING_BYTES(3); 53 INSERT_PADDING_BYTES_NOINIT(3);
54 NCASectionFilesystemType filesystem_type; 54 NCASectionFilesystemType filesystem_type;
55 NCASectionCryptoType crypto_type; 55 NCASectionCryptoType crypto_type;
56 INSERT_UNION_PADDING_BYTES(3); 56 INSERT_PADDING_BYTES_NOINIT(3);
57}; 57};
58static_assert(sizeof(NCASectionHeaderBlock) == 0x8, "NCASectionHeaderBlock has incorrect size."); 58static_assert(sizeof(NCASectionHeaderBlock) == 0x8, "NCASectionHeaderBlock has incorrect size.");
59 59
@@ -61,7 +61,7 @@ struct NCASectionRaw {
61 NCASectionHeaderBlock header; 61 NCASectionHeaderBlock header;
62 std::array<u8, 0x138> block_data; 62 std::array<u8, 0x138> block_data;
63 std::array<u8, 0x8> section_ctr; 63 std::array<u8, 0x8> section_ctr;
64 INSERT_UNION_PADDING_BYTES(0xB8); 64 INSERT_PADDING_BYTES_NOINIT(0xB8);
65}; 65};
66static_assert(sizeof(NCASectionRaw) == 0x200, "NCASectionRaw has incorrect size."); 66static_assert(sizeof(NCASectionRaw) == 0x200, "NCASectionRaw has incorrect size.");
67 67
@@ -69,19 +69,19 @@ struct PFS0Superblock {
69 NCASectionHeaderBlock header_block; 69 NCASectionHeaderBlock header_block;
70 std::array<u8, 0x20> hash; 70 std::array<u8, 0x20> hash;
71 u32_le size; 71 u32_le size;
72 INSERT_UNION_PADDING_BYTES(4); 72 INSERT_PADDING_BYTES_NOINIT(4);
73 u64_le hash_table_offset; 73 u64_le hash_table_offset;
74 u64_le hash_table_size; 74 u64_le hash_table_size;
75 u64_le pfs0_header_offset; 75 u64_le pfs0_header_offset;
76 u64_le pfs0_size; 76 u64_le pfs0_size;
77 INSERT_UNION_PADDING_BYTES(0x1B0); 77 INSERT_PADDING_BYTES_NOINIT(0x1B0);
78}; 78};
79static_assert(sizeof(PFS0Superblock) == 0x200, "PFS0Superblock has incorrect size."); 79static_assert(sizeof(PFS0Superblock) == 0x200, "PFS0Superblock has incorrect size.");
80 80
81struct RomFSSuperblock { 81struct RomFSSuperblock {
82 NCASectionHeaderBlock header_block; 82 NCASectionHeaderBlock header_block;
83 IVFCHeader ivfc; 83 IVFCHeader ivfc;
84 INSERT_UNION_PADDING_BYTES(0x118); 84 INSERT_PADDING_BYTES_NOINIT(0x118);
85}; 85};
86static_assert(sizeof(RomFSSuperblock) == 0x200, "RomFSSuperblock has incorrect size."); 86static_assert(sizeof(RomFSSuperblock) == 0x200, "RomFSSuperblock has incorrect size.");
87 87
@@ -89,19 +89,19 @@ struct BKTRHeader {
89 u64_le offset; 89 u64_le offset;
90 u64_le size; 90 u64_le size;
91 u32_le magic; 91 u32_le magic;
92 INSERT_UNION_PADDING_BYTES(0x4); 92 INSERT_PADDING_BYTES_NOINIT(0x4);
93 u32_le number_entries; 93 u32_le number_entries;
94 INSERT_UNION_PADDING_BYTES(0x4); 94 INSERT_PADDING_BYTES_NOINIT(0x4);
95}; 95};
96static_assert(sizeof(BKTRHeader) == 0x20, "BKTRHeader has incorrect size."); 96static_assert(sizeof(BKTRHeader) == 0x20, "BKTRHeader has incorrect size.");
97 97
98struct BKTRSuperblock { 98struct BKTRSuperblock {
99 NCASectionHeaderBlock header_block; 99 NCASectionHeaderBlock header_block;
100 IVFCHeader ivfc; 100 IVFCHeader ivfc;
101 INSERT_UNION_PADDING_BYTES(0x18); 101 INSERT_PADDING_BYTES_NOINIT(0x18);
102 BKTRHeader relocation; 102 BKTRHeader relocation;
103 BKTRHeader subsection; 103 BKTRHeader subsection;
104 INSERT_UNION_PADDING_BYTES(0xC0); 104 INSERT_PADDING_BYTES_NOINIT(0xC0);
105}; 105};
106static_assert(sizeof(BKTRSuperblock) == 0x200, "BKTRSuperblock has incorrect size."); 106static_assert(sizeof(BKTRSuperblock) == 0x200, "BKTRSuperblock has incorrect size.");
107 107
diff --git a/src/core/file_sys/nca_patch.cpp b/src/core/file_sys/nca_patch.cpp
index adcf0732f..a65ec6798 100644
--- a/src/core/file_sys/nca_patch.cpp
+++ b/src/core/file_sys/nca_patch.cpp
@@ -51,8 +51,8 @@ std::pair<std::size_t, std::size_t> SearchBucketEntry(u64 offset, const BlockTyp
51 low = mid + 1; 51 low = mid + 1;
52 } 52 }
53 } 53 }
54
55 UNREACHABLE_MSG("Offset could not be found in BKTR block."); 54 UNREACHABLE_MSG("Offset could not be found in BKTR block.");
55 return {0, 0};
56} 56}
57} // Anonymous namespace 57} // Anonymous namespace
58 58
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index da01002d5..431302f55 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -105,7 +105,8 @@ ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
105 // TODO(DarkLordZach): Peek at NCA contents to differentiate Manual and Legal. 105 // TODO(DarkLordZach): Peek at NCA contents to differentiate Manual and Legal.
106 return ContentRecordType::HtmlDocument; 106 return ContentRecordType::HtmlDocument;
107 default: 107 default:
108 UNREACHABLE_MSG("Invalid NCAContentType={:02X}", static_cast<u8>(type)); 108 UNREACHABLE_MSG("Invalid NCAContentType={:02X}", type);
109 return ContentRecordType{};
109 } 110 }
110} 111}
111 112
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index 5b414b0f0..b08a1687a 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -67,18 +67,18 @@ public:
67 virtual void Refresh() = 0; 67 virtual void Refresh() = 0;
68 68
69 virtual bool HasEntry(u64 title_id, ContentRecordType type) const = 0; 69 virtual bool HasEntry(u64 title_id, ContentRecordType type) const = 0;
70 virtual bool HasEntry(ContentProviderEntry entry) const; 70 bool HasEntry(ContentProviderEntry entry) const;
71 71
72 virtual std::optional<u32> GetEntryVersion(u64 title_id) const = 0; 72 virtual std::optional<u32> GetEntryVersion(u64 title_id) const = 0;
73 73
74 virtual VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const = 0; 74 virtual VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const = 0;
75 virtual VirtualFile GetEntryUnparsed(ContentProviderEntry entry) const; 75 VirtualFile GetEntryUnparsed(ContentProviderEntry entry) const;
76 76
77 virtual VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const = 0; 77 virtual VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const = 0;
78 virtual VirtualFile GetEntryRaw(ContentProviderEntry entry) const; 78 VirtualFile GetEntryRaw(ContentProviderEntry entry) const;
79 79
80 virtual std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const = 0; 80 virtual std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const = 0;
81 virtual std::unique_ptr<NCA> GetEntry(ContentProviderEntry entry) const; 81 std::unique_ptr<NCA> GetEntry(ContentProviderEntry entry) const;
82 82
83 virtual std::vector<ContentProviderEntry> ListEntries() const; 83 virtual std::vector<ContentProviderEntry> ListEntries() const;
84 84
diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h
index 7ce313190..79bcf5762 100644
--- a/src/core/hle/ipc.h
+++ b/src/core/hle/ipc.h
@@ -160,7 +160,7 @@ struct DomainMessageHeader {
160 // Used when responding to an IPC request, Server -> Client. 160 // Used when responding to an IPC request, Server -> Client.
161 struct { 161 struct {
162 u32_le num_objects; 162 u32_le num_objects;
163 INSERT_UNION_PADDING_WORDS(3); 163 INSERT_PADDING_WORDS_NOINIT(3);
164 }; 164 };
165 165
166 // Used when performing an IPC request, Client -> Server. 166 // Used when performing an IPC request, Client -> Server.
@@ -171,7 +171,7 @@ struct DomainMessageHeader {
171 BitField<16, 16, u32> size; 171 BitField<16, 16, u32> size;
172 }; 172 };
173 u32_le object_id; 173 u32_le object_id;
174 INSERT_UNION_PADDING_WORDS(2); 174 INSERT_PADDING_WORDS_NOINIT(2);
175 }; 175 };
176 176
177 std::array<u32, 4> raw{}; 177 std::array<u32, 4> raw{};
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
deleted file mode 100644
index 20ffa7d47..000000000
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ /dev/null
@@ -1,317 +0,0 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <vector>
7
8#include "common/assert.h"
9#include "common/common_types.h"
10#include "core/arm/exclusive_monitor.h"
11#include "core/core.h"
12#include "core/hle/kernel/address_arbiter.h"
13#include "core/hle/kernel/errors.h"
14#include "core/hle/kernel/handle_table.h"
15#include "core/hle/kernel/k_scheduler.h"
16#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
17#include "core/hle/kernel/kernel.h"
18#include "core/hle/kernel/thread.h"
19#include "core/hle/kernel/time_manager.h"
20#include "core/hle/result.h"
21#include "core/memory.h"
22
23namespace Kernel {
24
25// Wake up num_to_wake (or all) threads in a vector.
26void AddressArbiter::WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads,
27 s32 num_to_wake) {
28 // Only process up to 'target' threads, unless 'target' is <= 0, in which case process
29 // them all.
30 std::size_t last = waiting_threads.size();
31 if (num_to_wake > 0) {
32 last = std::min(last, static_cast<std::size_t>(num_to_wake));
33 }
34
35 // Signal the waiting threads.
36 for (std::size_t i = 0; i < last; i++) {
37 waiting_threads[i]->SetSynchronizationResults(nullptr, RESULT_SUCCESS);
38 RemoveThread(waiting_threads[i]);
39 waiting_threads[i]->WaitForArbitration(false);
40 waiting_threads[i]->ResumeFromWait();
41 }
42}
43
44AddressArbiter::AddressArbiter(Core::System& system) : system{system} {}
45AddressArbiter::~AddressArbiter() = default;
46
47ResultCode AddressArbiter::SignalToAddress(VAddr address, SignalType type, s32 value,
48 s32 num_to_wake) {
49 switch (type) {
50 case SignalType::Signal:
51 return SignalToAddressOnly(address, num_to_wake);
52 case SignalType::IncrementAndSignalIfEqual:
53 return IncrementAndSignalToAddressIfEqual(address, value, num_to_wake);
54 case SignalType::ModifyByWaitingCountAndSignalIfEqual:
55 return ModifyByWaitingCountAndSignalToAddressIfEqual(address, value, num_to_wake);
56 default:
57 return ERR_INVALID_ENUM_VALUE;
58 }
59}
60
61ResultCode AddressArbiter::SignalToAddressOnly(VAddr address, s32 num_to_wake) {
62 KScopedSchedulerLock lock(system.Kernel());
63 const std::vector<std::shared_ptr<Thread>> waiting_threads =
64 GetThreadsWaitingOnAddress(address);
65 WakeThreads(waiting_threads, num_to_wake);
66 return RESULT_SUCCESS;
67}
68
69ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 value,
70 s32 num_to_wake) {
71 KScopedSchedulerLock lock(system.Kernel());
72 auto& memory = system.Memory();
73
74 // Ensure that we can write to the address.
75 if (!memory.IsValidVirtualAddress(address)) {
76 return ERR_INVALID_ADDRESS_STATE;
77 }
78
79 const std::size_t current_core = system.CurrentCoreIndex();
80 auto& monitor = system.Monitor();
81 u32 current_value;
82 do {
83 current_value = monitor.ExclusiveRead32(current_core, address);
84
85 if (current_value != static_cast<u32>(value)) {
86 return ERR_INVALID_STATE;
87 }
88 current_value++;
89 } while (!monitor.ExclusiveWrite32(current_core, address, current_value));
90
91 return SignalToAddressOnly(address, num_to_wake);
92}
93
94ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value,
95 s32 num_to_wake) {
96 KScopedSchedulerLock lock(system.Kernel());
97 auto& memory = system.Memory();
98
99 // Ensure that we can write to the address.
100 if (!memory.IsValidVirtualAddress(address)) {
101 return ERR_INVALID_ADDRESS_STATE;
102 }
103
104 // Get threads waiting on the address.
105 const std::vector<std::shared_ptr<Thread>> waiting_threads =
106 GetThreadsWaitingOnAddress(address);
107
108 const std::size_t current_core = system.CurrentCoreIndex();
109 auto& monitor = system.Monitor();
110 s32 updated_value;
111 do {
112 updated_value = monitor.ExclusiveRead32(current_core, address);
113
114 if (updated_value != value) {
115 return ERR_INVALID_STATE;
116 }
117 // Determine the modified value depending on the waiting count.
118 if (num_to_wake <= 0) {
119 if (waiting_threads.empty()) {
120 updated_value = value + 1;
121 } else {
122 updated_value = value - 1;
123 }
124 } else {
125 if (waiting_threads.empty()) {
126 updated_value = value + 1;
127 } else if (waiting_threads.size() <= static_cast<u32>(num_to_wake)) {
128 updated_value = value - 1;
129 } else {
130 updated_value = value;
131 }
132 }
133 } while (!monitor.ExclusiveWrite32(current_core, address, updated_value));
134
135 WakeThreads(waiting_threads, num_to_wake);
136 return RESULT_SUCCESS;
137}
138
139ResultCode AddressArbiter::WaitForAddress(VAddr address, ArbitrationType type, s32 value,
140 s64 timeout_ns) {
141 switch (type) {
142 case ArbitrationType::WaitIfLessThan:
143 return WaitForAddressIfLessThan(address, value, timeout_ns, false);
144 case ArbitrationType::DecrementAndWaitIfLessThan:
145 return WaitForAddressIfLessThan(address, value, timeout_ns, true);
146 case ArbitrationType::WaitIfEqual:
147 return WaitForAddressIfEqual(address, value, timeout_ns);
148 default:
149 return ERR_INVALID_ENUM_VALUE;
150 }
151}
152
153ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout,
154 bool should_decrement) {
155 auto& memory = system.Memory();
156 auto& kernel = system.Kernel();
157 Thread* current_thread = kernel.CurrentScheduler()->GetCurrentThread();
158
159 Handle event_handle = InvalidHandle;
160 {
161 KScopedSchedulerLockAndSleep lock(kernel, event_handle, current_thread, timeout);
162
163 if (current_thread->IsPendingTermination()) {
164 lock.CancelSleep();
165 return ERR_THREAD_TERMINATING;
166 }
167
168 // Ensure that we can read the address.
169 if (!memory.IsValidVirtualAddress(address)) {
170 lock.CancelSleep();
171 return ERR_INVALID_ADDRESS_STATE;
172 }
173
174 s32 current_value = static_cast<s32>(memory.Read32(address));
175 if (current_value >= value) {
176 lock.CancelSleep();
177 return ERR_INVALID_STATE;
178 }
179
180 current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT);
181
182 s32 decrement_value;
183
184 const std::size_t current_core = system.CurrentCoreIndex();
185 auto& monitor = system.Monitor();
186 do {
187 current_value = static_cast<s32>(monitor.ExclusiveRead32(current_core, address));
188 if (should_decrement) {
189 decrement_value = current_value - 1;
190 } else {
191 decrement_value = current_value;
192 }
193 } while (
194 !monitor.ExclusiveWrite32(current_core, address, static_cast<u32>(decrement_value)));
195
196 // Short-circuit without rescheduling, if timeout is zero.
197 if (timeout == 0) {
198 lock.CancelSleep();
199 return RESULT_TIMEOUT;
200 }
201
202 current_thread->SetArbiterWaitAddress(address);
203 InsertThread(SharedFrom(current_thread));
204 current_thread->SetStatus(ThreadStatus::WaitArb);
205 current_thread->WaitForArbitration(true);
206 }
207
208 if (event_handle != InvalidHandle) {
209 auto& time_manager = kernel.TimeManager();
210 time_manager.UnscheduleTimeEvent(event_handle);
211 }
212
213 {
214 KScopedSchedulerLock lock(kernel);
215 if (current_thread->IsWaitingForArbitration()) {
216 RemoveThread(SharedFrom(current_thread));
217 current_thread->WaitForArbitration(false);
218 }
219 }
220
221 return current_thread->GetSignalingResult();
222}
223
224ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) {
225 auto& memory = system.Memory();
226 auto& kernel = system.Kernel();
227 Thread* current_thread = kernel.CurrentScheduler()->GetCurrentThread();
228
229 Handle event_handle = InvalidHandle;
230 {
231 KScopedSchedulerLockAndSleep lock(kernel, event_handle, current_thread, timeout);
232
233 if (current_thread->IsPendingTermination()) {
234 lock.CancelSleep();
235 return ERR_THREAD_TERMINATING;
236 }
237
238 // Ensure that we can read the address.
239 if (!memory.IsValidVirtualAddress(address)) {
240 lock.CancelSleep();
241 return ERR_INVALID_ADDRESS_STATE;
242 }
243
244 s32 current_value = static_cast<s32>(memory.Read32(address));
245 if (current_value != value) {
246 lock.CancelSleep();
247 return ERR_INVALID_STATE;
248 }
249
250 // Short-circuit without rescheduling, if timeout is zero.
251 if (timeout == 0) {
252 lock.CancelSleep();
253 return RESULT_TIMEOUT;
254 }
255
256 current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT);
257 current_thread->SetArbiterWaitAddress(address);
258 InsertThread(SharedFrom(current_thread));
259 current_thread->SetStatus(ThreadStatus::WaitArb);
260 current_thread->WaitForArbitration(true);
261 }
262
263 if (event_handle != InvalidHandle) {
264 auto& time_manager = kernel.TimeManager();
265 time_manager.UnscheduleTimeEvent(event_handle);
266 }
267
268 {
269 KScopedSchedulerLock lock(kernel);
270 if (current_thread->IsWaitingForArbitration()) {
271 RemoveThread(SharedFrom(current_thread));
272 current_thread->WaitForArbitration(false);
273 }
274 }
275
276 return current_thread->GetSignalingResult();
277}
278
279void AddressArbiter::InsertThread(std::shared_ptr<Thread> thread) {
280 const VAddr arb_addr = thread->GetArbiterWaitAddress();
281 std::list<std::shared_ptr<Thread>>& thread_list = arb_threads[arb_addr];
282
283 const auto iter =
284 std::find_if(thread_list.cbegin(), thread_list.cend(), [&thread](const auto& entry) {
285 return entry->GetPriority() >= thread->GetPriority();
286 });
287
288 if (iter == thread_list.cend()) {
289 thread_list.push_back(std::move(thread));
290 } else {
291 thread_list.insert(iter, std::move(thread));
292 }
293}
294
295void AddressArbiter::RemoveThread(std::shared_ptr<Thread> thread) {
296 const VAddr arb_addr = thread->GetArbiterWaitAddress();
297 std::list<std::shared_ptr<Thread>>& thread_list = arb_threads[arb_addr];
298
299 const auto iter = std::find_if(thread_list.cbegin(), thread_list.cend(),
300 [&thread](const auto& entry) { return thread == entry; });
301
302 if (iter != thread_list.cend()) {
303 thread_list.erase(iter);
304 }
305}
306
307std::vector<std::shared_ptr<Thread>> AddressArbiter::GetThreadsWaitingOnAddress(
308 VAddr address) const {
309 const auto iter = arb_threads.find(address);
310 if (iter == arb_threads.cend()) {
311 return {};
312 }
313
314 const std::list<std::shared_ptr<Thread>>& thread_list = iter->second;
315 return {thread_list.cbegin(), thread_list.cend()};
316}
317} // namespace Kernel
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h
deleted file mode 100644
index b91edc67d..000000000
--- a/src/core/hle/kernel/address_arbiter.h
+++ /dev/null
@@ -1,91 +0,0 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <list>
8#include <memory>
9#include <unordered_map>
10#include <vector>
11
12#include "common/common_types.h"
13
14union ResultCode;
15
16namespace Core {
17class System;
18}
19
20namespace Kernel {
21
22class Thread;
23
24class AddressArbiter {
25public:
26 enum class ArbitrationType {
27 WaitIfLessThan = 0,
28 DecrementAndWaitIfLessThan = 1,
29 WaitIfEqual = 2,
30 };
31
32 enum class SignalType {
33 Signal = 0,
34 IncrementAndSignalIfEqual = 1,
35 ModifyByWaitingCountAndSignalIfEqual = 2,
36 };
37
38 explicit AddressArbiter(Core::System& system);
39 ~AddressArbiter();
40
41 AddressArbiter(const AddressArbiter&) = delete;
42 AddressArbiter& operator=(const AddressArbiter&) = delete;
43
44 AddressArbiter(AddressArbiter&&) = default;
45 AddressArbiter& operator=(AddressArbiter&&) = delete;
46
47 /// Signals an address being waited on with a particular signaling type.
48 ResultCode SignalToAddress(VAddr address, SignalType type, s32 value, s32 num_to_wake);
49
50 /// Waits on an address with a particular arbitration type.
51 ResultCode WaitForAddress(VAddr address, ArbitrationType type, s32 value, s64 timeout_ns);
52
53private:
54 /// Signals an address being waited on.
55 ResultCode SignalToAddressOnly(VAddr address, s32 num_to_wake);
56
57 /// Signals an address being waited on and increments its value if equal to the value argument.
58 ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake);
59
60 /// Signals an address being waited on and modifies its value based on waiting thread count if
61 /// equal to the value argument.
62 ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value,
63 s32 num_to_wake);
64
65 /// Waits on an address if the value passed is less than the argument value,
66 /// optionally decrementing.
67 ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout,
68 bool should_decrement);
69
70 /// Waits on an address if the value passed is equal to the argument value.
71 ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout);
72
73 /// Wake up num_to_wake (or all) threads in a vector.
74 void WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads, s32 num_to_wake);
75
76 /// Insert a thread into the address arbiter container
77 void InsertThread(std::shared_ptr<Thread> thread);
78
79 /// Removes a thread from the address arbiter container
80 void RemoveThread(std::shared_ptr<Thread> thread);
81
82 // Gets the threads waiting on an address.
83 std::vector<std::shared_ptr<Thread>> GetThreadsWaitingOnAddress(VAddr address) const;
84
85 /// List of threads waiting for a address arbiter
86 std::unordered_map<VAddr, std::list<std::shared_ptr<Thread>>> arb_threads;
87
88 Core::System& system;
89};
90
91} // namespace Kernel
diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp
index 8aff2227a..f8f005f15 100644
--- a/src/core/hle/kernel/client_port.cpp
+++ b/src/core/hle/kernel/client_port.cpp
@@ -33,9 +33,6 @@ ResultVal<std::shared_ptr<ClientSession>> ClientPort::Connect() {
33 server_port->AppendPendingSession(std::move(server)); 33 server_port->AppendPendingSession(std::move(server));
34 } 34 }
35 35
36 // Wake the threads waiting on the ServerPort
37 server_port->Signal();
38
39 return MakeResult(std::move(client)); 36 return MakeResult(std::move(client));
40} 37}
41 38
diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp
index be9eba519..e8e52900d 100644
--- a/src/core/hle/kernel/client_session.cpp
+++ b/src/core/hle/kernel/client_session.cpp
@@ -12,7 +12,7 @@
12 12
13namespace Kernel { 13namespace Kernel {
14 14
15ClientSession::ClientSession(KernelCore& kernel) : SynchronizationObject{kernel} {} 15ClientSession::ClientSession(KernelCore& kernel) : KSynchronizationObject{kernel} {}
16 16
17ClientSession::~ClientSession() { 17ClientSession::~ClientSession() {
18 // This destructor will be called automatically when the last ClientSession handle is closed by 18 // This destructor will be called automatically when the last ClientSession handle is closed by
@@ -22,15 +22,6 @@ ClientSession::~ClientSession() {
22 } 22 }
23} 23}
24 24
25bool ClientSession::ShouldWait(const Thread* thread) const {
26 UNIMPLEMENTED();
27 return {};
28}
29
30void ClientSession::Acquire(Thread* thread) {
31 UNIMPLEMENTED();
32}
33
34bool ClientSession::IsSignaled() const { 25bool ClientSession::IsSignaled() const {
35 UNIMPLEMENTED(); 26 UNIMPLEMENTED();
36 return true; 27 return true;
diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h
index e5e0690c2..d5c9ebee8 100644
--- a/src/core/hle/kernel/client_session.h
+++ b/src/core/hle/kernel/client_session.h
@@ -7,7 +7,7 @@
7#include <memory> 7#include <memory>
8#include <string> 8#include <string>
9 9
10#include "core/hle/kernel/synchronization_object.h" 10#include "core/hle/kernel/k_synchronization_object.h"
11#include "core/hle/result.h" 11#include "core/hle/result.h"
12 12
13union ResultCode; 13union ResultCode;
@@ -26,7 +26,7 @@ class KernelCore;
26class Session; 26class Session;
27class Thread; 27class Thread;
28 28
29class ClientSession final : public SynchronizationObject { 29class ClientSession final : public KSynchronizationObject {
30public: 30public:
31 explicit ClientSession(KernelCore& kernel); 31 explicit ClientSession(KernelCore& kernel);
32 ~ClientSession() override; 32 ~ClientSession() override;
@@ -49,10 +49,6 @@ public:
49 ResultCode SendSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory, 49 ResultCode SendSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory,
50 Core::Timing::CoreTiming& core_timing); 50 Core::Timing::CoreTiming& core_timing);
51 51
52 bool ShouldWait(const Thread* thread) const override;
53
54 void Acquire(Thread* thread) override;
55
56 bool IsSignaled() const override; 52 bool IsSignaled() const override;
57 53
58private: 54private:
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h
index d4e5d88cf..7d32a39f0 100644
--- a/src/core/hle/kernel/errors.h
+++ b/src/core/hle/kernel/errors.h
@@ -13,12 +13,14 @@ namespace Kernel {
13constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7}; 13constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7};
14constexpr ResultCode ERR_INVALID_CAPABILITY_DESCRIPTOR{ErrorModule::Kernel, 14}; 14constexpr ResultCode ERR_INVALID_CAPABILITY_DESCRIPTOR{ErrorModule::Kernel, 14};
15constexpr ResultCode ERR_THREAD_TERMINATING{ErrorModule::Kernel, 59}; 15constexpr ResultCode ERR_THREAD_TERMINATING{ErrorModule::Kernel, 59};
16constexpr ResultCode ERR_TERMINATION_REQUESTED{ErrorModule::Kernel, 59};
16constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101}; 17constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101};
17constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102}; 18constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102};
18constexpr ResultCode ERR_OUT_OF_RESOURCES{ErrorModule::Kernel, 103}; 19constexpr ResultCode ERR_OUT_OF_RESOURCES{ErrorModule::Kernel, 103};
19constexpr ResultCode ERR_OUT_OF_MEMORY{ErrorModule::Kernel, 104}; 20constexpr ResultCode ERR_OUT_OF_MEMORY{ErrorModule::Kernel, 104};
20constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105}; 21constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105};
21constexpr ResultCode ERR_INVALID_ADDRESS_STATE{ErrorModule::Kernel, 106}; 22constexpr ResultCode ERR_INVALID_ADDRESS_STATE{ErrorModule::Kernel, 106};
23constexpr ResultCode ERR_INVALID_CURRENT_MEMORY{ErrorModule::Kernel, 106};
22constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS{ErrorModule::Kernel, 108}; 24constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS{ErrorModule::Kernel, 108};
23constexpr ResultCode ERR_INVALID_MEMORY_RANGE{ErrorModule::Kernel, 110}; 25constexpr ResultCode ERR_INVALID_MEMORY_RANGE{ErrorModule::Kernel, 110};
24constexpr ResultCode ERR_INVALID_PROCESSOR_ID{ErrorModule::Kernel, 113}; 26constexpr ResultCode ERR_INVALID_PROCESSOR_ID{ErrorModule::Kernel, 113};
@@ -28,6 +30,7 @@ constexpr ResultCode ERR_INVALID_POINTER{ErrorModule::Kernel, 115};
28constexpr ResultCode ERR_INVALID_COMBINATION{ErrorModule::Kernel, 116}; 30constexpr ResultCode ERR_INVALID_COMBINATION{ErrorModule::Kernel, 116};
29constexpr ResultCode RESULT_TIMEOUT{ErrorModule::Kernel, 117}; 31constexpr ResultCode RESULT_TIMEOUT{ErrorModule::Kernel, 117};
30constexpr ResultCode ERR_SYNCHRONIZATION_CANCELED{ErrorModule::Kernel, 118}; 32constexpr ResultCode ERR_SYNCHRONIZATION_CANCELED{ErrorModule::Kernel, 118};
33constexpr ResultCode ERR_CANCELLED{ErrorModule::Kernel, 118};
31constexpr ResultCode ERR_OUT_OF_RANGE{ErrorModule::Kernel, 119}; 34constexpr ResultCode ERR_OUT_OF_RANGE{ErrorModule::Kernel, 119};
32constexpr ResultCode ERR_INVALID_ENUM_VALUE{ErrorModule::Kernel, 120}; 35constexpr ResultCode ERR_INVALID_ENUM_VALUE{ErrorModule::Kernel, 120};
33constexpr ResultCode ERR_NOT_FOUND{ErrorModule::Kernel, 121}; 36constexpr ResultCode ERR_NOT_FOUND{ErrorModule::Kernel, 121};
diff --git a/src/core/hle/kernel/k_address_arbiter.cpp b/src/core/hle/kernel/k_address_arbiter.cpp
new file mode 100644
index 000000000..d9e702f13
--- /dev/null
+++ b/src/core/hle/kernel/k_address_arbiter.cpp
@@ -0,0 +1,367 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/arm/exclusive_monitor.h"
6#include "core/core.h"
7#include "core/hle/kernel/k_address_arbiter.h"
8#include "core/hle/kernel/k_scheduler.h"
9#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
10#include "core/hle/kernel/kernel.h"
11#include "core/hle/kernel/svc_results.h"
12#include "core/hle/kernel/thread.h"
13#include "core/hle/kernel/time_manager.h"
14#include "core/memory.h"
15
16namespace Kernel {
17
18KAddressArbiter::KAddressArbiter(Core::System& system_)
19 : system{system_}, kernel{system.Kernel()} {}
20KAddressArbiter::~KAddressArbiter() = default;
21
22namespace {
23
24bool ReadFromUser(Core::System& system, s32* out, VAddr address) {
25 *out = system.Memory().Read32(address);
26 return true;
27}
28
29bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 value) {
30 auto& monitor = system.Monitor();
31 const auto current_core = system.CurrentCoreIndex();
32
33 // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
34 // TODO(bunnei): We should call CanAccessAtomic(..) here.
35
36 // Load the value from the address.
37 const s32 current_value = static_cast<s32>(monitor.ExclusiveRead32(current_core, address));
38
39 // Compare it to the desired one.
40 if (current_value < value) {
41 // If less than, we want to try to decrement.
42 const s32 decrement_value = current_value - 1;
43
44 // Decrement and try to store.
45 if (!monitor.ExclusiveWrite32(current_core, address, static_cast<u32>(decrement_value))) {
46 // If we failed to store, try again.
47 DecrementIfLessThan(system, out, address, value);
48 }
49 } else {
50 // Otherwise, clear our exclusive hold and finish
51 monitor.ClearExclusive();
52 }
53
54 // We're done.
55 *out = current_value;
56 return true;
57}
58
59bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32 new_value) {
60 auto& monitor = system.Monitor();
61 const auto current_core = system.CurrentCoreIndex();
62
63 // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
64 // TODO(bunnei): We should call CanAccessAtomic(..) here.
65
66 // Load the value from the address.
67 const s32 current_value = static_cast<s32>(monitor.ExclusiveRead32(current_core, address));
68
69 // Compare it to the desired one.
70 if (current_value == value) {
71 // If equal, we want to try to write the new value.
72
73 // Try to store.
74 if (!monitor.ExclusiveWrite32(current_core, address, static_cast<u32>(new_value))) {
75 // If we failed to store, try again.
76 UpdateIfEqual(system, out, address, value, new_value);
77 }
78 } else {
79 // Otherwise, clear our exclusive hold and finish.
80 monitor.ClearExclusive();
81 }
82
83 // We're done.
84 *out = current_value;
85 return true;
86}
87
88} // namespace
89
90ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) {
91 // Perform signaling.
92 s32 num_waiters{};
93 {
94 KScopedSchedulerLock sl(kernel);
95
96 auto it = thread_tree.nfind_light({addr, -1});
97 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
98 (it->GetAddressArbiterKey() == addr)) {
99 Thread* target_thread = std::addressof(*it);
100 target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
101
102 ASSERT(target_thread->IsWaitingForAddressArbiter());
103 target_thread->Wakeup();
104
105 it = thread_tree.erase(it);
106 target_thread->ClearAddressArbiter();
107 ++num_waiters;
108 }
109 }
110 return RESULT_SUCCESS;
111}
112
113ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 count) {
114 // Perform signaling.
115 s32 num_waiters{};
116 {
117 KScopedSchedulerLock sl(kernel);
118
119 // Check the userspace value.
120 s32 user_value{};
121 R_UNLESS(UpdateIfEqual(system, std::addressof(user_value), addr, value, value + 1),
122 Svc::ResultInvalidCurrentMemory);
123 R_UNLESS(user_value == value, Svc::ResultInvalidState);
124
125 auto it = thread_tree.nfind_light({addr, -1});
126 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
127 (it->GetAddressArbiterKey() == addr)) {
128 Thread* target_thread = std::addressof(*it);
129 target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
130
131 ASSERT(target_thread->IsWaitingForAddressArbiter());
132 target_thread->Wakeup();
133
134 it = thread_tree.erase(it);
135 target_thread->ClearAddressArbiter();
136 ++num_waiters;
137 }
138 }
139 return RESULT_SUCCESS;
140}
141
142ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 value, s32 count) {
143 // Perform signaling.
144 s32 num_waiters{};
145 {
146 KScopedSchedulerLock sl(kernel);
147
148 auto it = thread_tree.nfind_light({addr, -1});
149 // Determine the updated value.
150 s32 new_value{};
151 if (/*GetTargetFirmware() >= TargetFirmware_7_0_0*/ true) {
152 if (count <= 0) {
153 if ((it != thread_tree.end()) && (it->GetAddressArbiterKey() == addr)) {
154 new_value = value - 2;
155 } else {
156 new_value = value + 1;
157 }
158 } else {
159 if ((it != thread_tree.end()) && (it->GetAddressArbiterKey() == addr)) {
160 auto tmp_it = it;
161 s32 tmp_num_waiters{};
162 while ((++tmp_it != thread_tree.end()) &&
163 (tmp_it->GetAddressArbiterKey() == addr)) {
164 if ((tmp_num_waiters++) >= count) {
165 break;
166 }
167 }
168
169 if (tmp_num_waiters < count) {
170 new_value = value - 1;
171 } else {
172 new_value = value;
173 }
174 } else {
175 new_value = value + 1;
176 }
177 }
178 } else {
179 if (count <= 0) {
180 if ((it != thread_tree.end()) && (it->GetAddressArbiterKey() == addr)) {
181 new_value = value - 1;
182 } else {
183 new_value = value + 1;
184 }
185 } else {
186 auto tmp_it = it;
187 s32 tmp_num_waiters{};
188 while ((tmp_it != thread_tree.end()) && (tmp_it->GetAddressArbiterKey() == addr) &&
189 (tmp_num_waiters < count + 1)) {
190 ++tmp_num_waiters;
191 ++tmp_it;
192 }
193
194 if (tmp_num_waiters == 0) {
195 new_value = value + 1;
196 } else if (tmp_num_waiters <= count) {
197 new_value = value - 1;
198 } else {
199 new_value = value;
200 }
201 }
202 }
203
204 // Check the userspace value.
205 s32 user_value{};
206 bool succeeded{};
207 if (value != new_value) {
208 succeeded = UpdateIfEqual(system, std::addressof(user_value), addr, value, new_value);
209 } else {
210 succeeded = ReadFromUser(system, std::addressof(user_value), addr);
211 }
212
213 R_UNLESS(succeeded, Svc::ResultInvalidCurrentMemory);
214 R_UNLESS(user_value == value, Svc::ResultInvalidState);
215
216 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
217 (it->GetAddressArbiterKey() == addr)) {
218 Thread* target_thread = std::addressof(*it);
219 target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
220
221 ASSERT(target_thread->IsWaitingForAddressArbiter());
222 target_thread->Wakeup();
223
224 it = thread_tree.erase(it);
225 target_thread->ClearAddressArbiter();
226 ++num_waiters;
227 }
228 }
229 return RESULT_SUCCESS;
230}
231
232ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) {
233 // Prepare to wait.
234 Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
235 Handle timer = InvalidHandle;
236
237 {
238 KScopedSchedulerLockAndSleep slp(kernel, timer, cur_thread, timeout);
239
240 // Check that the thread isn't terminating.
241 if (cur_thread->IsTerminationRequested()) {
242 slp.CancelSleep();
243 return Svc::ResultTerminationRequested;
244 }
245
246 // Set the synced object.
247 cur_thread->SetSyncedObject(nullptr, Svc::ResultTimedOut);
248
249 // Read the value from userspace.
250 s32 user_value{};
251 bool succeeded{};
252 if (decrement) {
253 succeeded = DecrementIfLessThan(system, std::addressof(user_value), addr, value);
254 } else {
255 succeeded = ReadFromUser(system, std::addressof(user_value), addr);
256 }
257
258 if (!succeeded) {
259 slp.CancelSleep();
260 return Svc::ResultInvalidCurrentMemory;
261 }
262
263 // Check that the value is less than the specified one.
264 if (user_value >= value) {
265 slp.CancelSleep();
266 return Svc::ResultInvalidState;
267 }
268
269 // Check that the timeout is non-zero.
270 if (timeout == 0) {
271 slp.CancelSleep();
272 return Svc::ResultTimedOut;
273 }
274
275 // Set the arbiter.
276 cur_thread->SetAddressArbiter(std::addressof(thread_tree), addr);
277 thread_tree.insert(*cur_thread);
278 cur_thread->SetState(ThreadState::Waiting);
279 cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
280 }
281
282 // Cancel the timer wait.
283 if (timer != InvalidHandle) {
284 auto& time_manager = kernel.TimeManager();
285 time_manager.UnscheduleTimeEvent(timer);
286 }
287
288 // Remove from the address arbiter.
289 {
290 KScopedSchedulerLock sl(kernel);
291
292 if (cur_thread->IsWaitingForAddressArbiter()) {
293 thread_tree.erase(thread_tree.iterator_to(*cur_thread));
294 cur_thread->ClearAddressArbiter();
295 }
296 }
297
298 // Get the result.
299 KSynchronizationObject* dummy{};
300 return cur_thread->GetWaitResult(std::addressof(dummy));
301}
302
303ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
304 // Prepare to wait.
305 Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
306 Handle timer = InvalidHandle;
307
308 {
309 KScopedSchedulerLockAndSleep slp(kernel, timer, cur_thread, timeout);
310
311 // Check that the thread isn't terminating.
312 if (cur_thread->IsTerminationRequested()) {
313 slp.CancelSleep();
314 return Svc::ResultTerminationRequested;
315 }
316
317 // Set the synced object.
318 cur_thread->SetSyncedObject(nullptr, Svc::ResultTimedOut);
319
320 // Read the value from userspace.
321 s32 user_value{};
322 if (!ReadFromUser(system, std::addressof(user_value), addr)) {
323 slp.CancelSleep();
324 return Svc::ResultInvalidCurrentMemory;
325 }
326
327 // Check that the value is equal.
328 if (value != user_value) {
329 slp.CancelSleep();
330 return Svc::ResultInvalidState;
331 }
332
333 // Check that the timeout is non-zero.
334 if (timeout == 0) {
335 slp.CancelSleep();
336 return Svc::ResultTimedOut;
337 }
338
339 // Set the arbiter.
340 cur_thread->SetAddressArbiter(std::addressof(thread_tree), addr);
341 thread_tree.insert(*cur_thread);
342 cur_thread->SetState(ThreadState::Waiting);
343 cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
344 }
345
346 // Cancel the timer wait.
347 if (timer != InvalidHandle) {
348 auto& time_manager = kernel.TimeManager();
349 time_manager.UnscheduleTimeEvent(timer);
350 }
351
352 // Remove from the address arbiter.
353 {
354 KScopedSchedulerLock sl(kernel);
355
356 if (cur_thread->IsWaitingForAddressArbiter()) {
357 thread_tree.erase(thread_tree.iterator_to(*cur_thread));
358 cur_thread->ClearAddressArbiter();
359 }
360 }
361
362 // Get the result.
363 KSynchronizationObject* dummy{};
364 return cur_thread->GetWaitResult(std::addressof(dummy));
365}
366
367} // namespace Kernel
diff --git a/src/core/hle/kernel/k_address_arbiter.h b/src/core/hle/kernel/k_address_arbiter.h
new file mode 100644
index 000000000..8d379b524
--- /dev/null
+++ b/src/core/hle/kernel/k_address_arbiter.h
@@ -0,0 +1,70 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/assert.h"
8#include "common/common_types.h"
9#include "core/hle/kernel/k_condition_variable.h"
10#include "core/hle/kernel/svc_types.h"
11
12union ResultCode;
13
14namespace Core {
15class System;
16}
17
18namespace Kernel {
19
20class KernelCore;
21
22class KAddressArbiter {
23public:
24 using ThreadTree = KConditionVariable::ThreadTree;
25
26 explicit KAddressArbiter(Core::System& system_);
27 ~KAddressArbiter();
28
29 [[nodiscard]] ResultCode SignalToAddress(VAddr addr, Svc::SignalType type, s32 value,
30 s32 count) {
31 switch (type) {
32 case Svc::SignalType::Signal:
33 return Signal(addr, count);
34 case Svc::SignalType::SignalAndIncrementIfEqual:
35 return SignalAndIncrementIfEqual(addr, value, count);
36 case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual:
37 return SignalAndModifyByWaitingCountIfEqual(addr, value, count);
38 }
39 UNREACHABLE();
40 return RESULT_UNKNOWN;
41 }
42
43 [[nodiscard]] ResultCode WaitForAddress(VAddr addr, Svc::ArbitrationType type, s32 value,
44 s64 timeout) {
45 switch (type) {
46 case Svc::ArbitrationType::WaitIfLessThan:
47 return WaitIfLessThan(addr, value, false, timeout);
48 case Svc::ArbitrationType::DecrementAndWaitIfLessThan:
49 return WaitIfLessThan(addr, value, true, timeout);
50 case Svc::ArbitrationType::WaitIfEqual:
51 return WaitIfEqual(addr, value, timeout);
52 }
53 UNREACHABLE();
54 return RESULT_UNKNOWN;
55 }
56
57private:
58 [[nodiscard]] ResultCode Signal(VAddr addr, s32 count);
59 [[nodiscard]] ResultCode SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 count);
60 [[nodiscard]] ResultCode SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 value, s32 count);
61 [[nodiscard]] ResultCode WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout);
62 [[nodiscard]] ResultCode WaitIfEqual(VAddr addr, s32 value, s64 timeout);
63
64 ThreadTree thread_tree;
65
66 Core::System& system;
67 KernelCore& kernel;
68};
69
70} // namespace Kernel
diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp
new file mode 100644
index 000000000..49a068310
--- /dev/null
+++ b/src/core/hle/kernel/k_condition_variable.cpp
@@ -0,0 +1,349 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <vector>
6
7#include "core/arm/exclusive_monitor.h"
8#include "core/core.h"
9#include "core/hle/kernel/k_condition_variable.h"
10#include "core/hle/kernel/k_scheduler.h"
11#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
12#include "core/hle/kernel/k_synchronization_object.h"
13#include "core/hle/kernel/kernel.h"
14#include "core/hle/kernel/process.h"
15#include "core/hle/kernel/svc_common.h"
16#include "core/hle/kernel/svc_results.h"
17#include "core/hle/kernel/thread.h"
18#include "core/memory.h"
19
20namespace Kernel {
21
22namespace {
23
24bool ReadFromUser(Core::System& system, u32* out, VAddr address) {
25 *out = system.Memory().Read32(address);
26 return true;
27}
28
29bool WriteToUser(Core::System& system, VAddr address, const u32* p) {
30 system.Memory().Write32(address, *p);
31 return true;
32}
33
34bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero,
35 u32 new_orr_mask) {
36 auto& monitor = system.Monitor();
37 const auto current_core = system.CurrentCoreIndex();
38
39 // Load the value from the address.
40 const auto expected = monitor.ExclusiveRead32(current_core, address);
41
42 // Orr in the new mask.
43 u32 value = expected | new_orr_mask;
44
45 // If the value is zero, use the if_zero value, otherwise use the newly orr'd value.
46 if (!expected) {
47 value = if_zero;
48 }
49
50 // Try to store.
51 if (!monitor.ExclusiveWrite32(current_core, address, value)) {
52 // If we failed to store, try again.
53 return UpdateLockAtomic(system, out, address, if_zero, new_orr_mask);
54 }
55
56 // We're done.
57 *out = expected;
58 return true;
59}
60
61} // namespace
62
63KConditionVariable::KConditionVariable(Core::System& system_)
64 : system{system_}, kernel{system.Kernel()} {}
65
66KConditionVariable::~KConditionVariable() = default;
67
68ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
69 Thread* owner_thread = kernel.CurrentScheduler()->GetCurrentThread();
70
71 // Signal the address.
72 {
73 KScopedSchedulerLock sl(kernel);
74
75 // Remove waiter thread.
76 s32 num_waiters{};
77 Thread* next_owner_thread =
78 owner_thread->RemoveWaiterByKey(std::addressof(num_waiters), addr);
79
80 // Determine the next tag.
81 u32 next_value{};
82 if (next_owner_thread) {
83 next_value = next_owner_thread->GetAddressKeyValue();
84 if (num_waiters > 1) {
85 next_value |= Svc::HandleWaitMask;
86 }
87
88 next_owner_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
89 next_owner_thread->Wakeup();
90 }
91
92 // Write the value to userspace.
93 if (!WriteToUser(system, addr, std::addressof(next_value))) {
94 if (next_owner_thread) {
95 next_owner_thread->SetSyncedObject(nullptr, Svc::ResultInvalidCurrentMemory);
96 }
97
98 return Svc::ResultInvalidCurrentMemory;
99 }
100 }
101
102 return RESULT_SUCCESS;
103}
104
105ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) {
106 Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
107
108 // Wait for the address.
109 {
110 std::shared_ptr<Thread> owner_thread;
111 ASSERT(!owner_thread);
112 {
113 KScopedSchedulerLock sl(kernel);
114 cur_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
115
116 // Check if the thread should terminate.
117 R_UNLESS(!cur_thread->IsTerminationRequested(), Svc::ResultTerminationRequested);
118
119 {
120 // Read the tag from userspace.
121 u32 test_tag{};
122 R_UNLESS(ReadFromUser(system, std::addressof(test_tag), addr),
123 Svc::ResultInvalidCurrentMemory);
124
125 // If the tag isn't the handle (with wait mask), we're done.
126 R_UNLESS(test_tag == (handle | Svc::HandleWaitMask), RESULT_SUCCESS);
127
128 // Get the lock owner thread.
129 owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<Thread>(handle);
130 R_UNLESS(owner_thread, Svc::ResultInvalidHandle);
131
132 // Update the lock.
133 cur_thread->SetAddressKey(addr, value);
134 owner_thread->AddWaiter(cur_thread);
135 cur_thread->SetState(ThreadState::Waiting);
136 cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
137 cur_thread->SetMutexWaitAddressForDebugging(addr);
138 }
139 }
140 ASSERT(owner_thread);
141 }
142
143 // Remove the thread as a waiter from the lock owner.
144 {
145 KScopedSchedulerLock sl(kernel);
146 Thread* owner_thread = cur_thread->GetLockOwner();
147 if (owner_thread != nullptr) {
148 owner_thread->RemoveWaiter(cur_thread);
149 }
150 }
151
152 // Get the wait result.
153 KSynchronizationObject* dummy{};
154 return cur_thread->GetWaitResult(std::addressof(dummy));
155}
156
157Thread* KConditionVariable::SignalImpl(Thread* thread) {
158 // Check pre-conditions.
159 ASSERT(kernel.GlobalSchedulerContext().IsLocked());
160
161 // Update the tag.
162 VAddr address = thread->GetAddressKey();
163 u32 own_tag = thread->GetAddressKeyValue();
164
165 u32 prev_tag{};
166 bool can_access{};
167 {
168 // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
169 // TODO(bunnei): We should call CanAccessAtomic(..) here.
170 can_access = true;
171 if (can_access) {
172 UpdateLockAtomic(system, std::addressof(prev_tag), address, own_tag,
173 Svc::HandleWaitMask);
174 }
175 }
176
177 Thread* thread_to_close = nullptr;
178 if (can_access) {
179 if (prev_tag == InvalidHandle) {
180 // If nobody held the lock previously, we're all good.
181 thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
182 thread->Wakeup();
183 } else {
184 // Get the previous owner.
185 auto owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<Thread>(
186 prev_tag & ~Svc::HandleWaitMask);
187
188 if (owner_thread) {
189 // Add the thread as a waiter on the owner.
190 owner_thread->AddWaiter(thread);
191 thread_to_close = owner_thread.get();
192 } else {
193 // The lock was tagged with a thread that doesn't exist.
194 thread->SetSyncedObject(nullptr, Svc::ResultInvalidState);
195 thread->Wakeup();
196 }
197 }
198 } else {
199 // If the address wasn't accessible, note so.
200 thread->SetSyncedObject(nullptr, Svc::ResultInvalidCurrentMemory);
201 thread->Wakeup();
202 }
203
204 return thread_to_close;
205}
206
207void KConditionVariable::Signal(u64 cv_key, s32 count) {
208 // Prepare for signaling.
209 constexpr int MaxThreads = 16;
210
211 // TODO(bunnei): This should just be Thread once we implement KAutoObject instead of using
212 // std::shared_ptr.
213 std::vector<std::shared_ptr<Thread>> thread_list;
214 std::array<Thread*, MaxThreads> thread_array;
215 s32 num_to_close{};
216
217 // Perform signaling.
218 s32 num_waiters{};
219 {
220 KScopedSchedulerLock sl(kernel);
221
222 auto it = thread_tree.nfind_light({cv_key, -1});
223 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
224 (it->GetConditionVariableKey() == cv_key)) {
225 Thread* target_thread = std::addressof(*it);
226
227 if (Thread* thread = SignalImpl(target_thread); thread != nullptr) {
228 if (num_to_close < MaxThreads) {
229 thread_array[num_to_close++] = thread;
230 } else {
231 thread_list.push_back(SharedFrom(thread));
232 }
233 }
234
235 it = thread_tree.erase(it);
236 target_thread->ClearConditionVariable();
237 ++num_waiters;
238 }
239
240 // If we have no waiters, clear the has waiter flag.
241 if (it == thread_tree.end() || it->GetConditionVariableKey() != cv_key) {
242 const u32 has_waiter_flag{};
243 WriteToUser(system, cv_key, std::addressof(has_waiter_flag));
244 }
245 }
246
247 // Close threads in the array.
248 for (auto i = 0; i < num_to_close; ++i) {
249 thread_array[i]->Close();
250 }
251
252 // Close threads in the list.
253 for (auto it = thread_list.begin(); it != thread_list.end(); it = thread_list.erase(it)) {
254 (*it)->Close();
255 }
256}
257
258ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) {
259 // Prepare to wait.
260 Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
261 Handle timer = InvalidHandle;
262
263 {
264 KScopedSchedulerLockAndSleep slp(kernel, timer, cur_thread, timeout);
265
266 // Set the synced object.
267 cur_thread->SetSyncedObject(nullptr, Svc::ResultTimedOut);
268
269 // Check that the thread isn't terminating.
270 if (cur_thread->IsTerminationRequested()) {
271 slp.CancelSleep();
272 return Svc::ResultTerminationRequested;
273 }
274
275 // Update the value and process for the next owner.
276 {
277 // Remove waiter thread.
278 s32 num_waiters{};
279 Thread* next_owner_thread =
280 cur_thread->RemoveWaiterByKey(std::addressof(num_waiters), addr);
281
282 // Update for the next owner thread.
283 u32 next_value{};
284 if (next_owner_thread != nullptr) {
285 // Get the next tag value.
286 next_value = next_owner_thread->GetAddressKeyValue();
287 if (num_waiters > 1) {
288 next_value |= Svc::HandleWaitMask;
289 }
290
291 // Wake up the next owner.
292 next_owner_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
293 next_owner_thread->Wakeup();
294 }
295
296 // Write to the cv key.
297 {
298 const u32 has_waiter_flag = 1;
299 WriteToUser(system, key, std::addressof(has_waiter_flag));
300 // TODO(bunnei): We should call DataMemoryBarrier(..) here.
301 }
302
303 // Write the value to userspace.
304 if (!WriteToUser(system, addr, std::addressof(next_value))) {
305 slp.CancelSleep();
306 return Svc::ResultInvalidCurrentMemory;
307 }
308 }
309
310 // Update condition variable tracking.
311 {
312 cur_thread->SetConditionVariable(std::addressof(thread_tree), addr, key, value);
313 thread_tree.insert(*cur_thread);
314 }
315
316 // If the timeout is non-zero, set the thread as waiting.
317 if (timeout != 0) {
318 cur_thread->SetState(ThreadState::Waiting);
319 cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
320 cur_thread->SetMutexWaitAddressForDebugging(addr);
321 }
322 }
323
324 // Cancel the timer wait.
325 if (timer != InvalidHandle) {
326 auto& time_manager = kernel.TimeManager();
327 time_manager.UnscheduleTimeEvent(timer);
328 }
329
330 // Remove from the condition variable.
331 {
332 KScopedSchedulerLock sl(kernel);
333
334 if (Thread* owner = cur_thread->GetLockOwner(); owner != nullptr) {
335 owner->RemoveWaiter(cur_thread);
336 }
337
338 if (cur_thread->IsWaitingForConditionVariable()) {
339 thread_tree.erase(thread_tree.iterator_to(*cur_thread));
340 cur_thread->ClearConditionVariable();
341 }
342 }
343
344 // Get the result.
345 KSynchronizationObject* dummy{};
346 return cur_thread->GetWaitResult(std::addressof(dummy));
347}
348
349} // namespace Kernel
diff --git a/src/core/hle/kernel/k_condition_variable.h b/src/core/hle/kernel/k_condition_variable.h
new file mode 100644
index 000000000..98ed5b323
--- /dev/null
+++ b/src/core/hle/kernel/k_condition_variable.h
@@ -0,0 +1,59 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/assert.h"
8#include "common/common_types.h"
9
10#include "core/hle/kernel/k_scheduler.h"
11#include "core/hle/kernel/kernel.h"
12#include "core/hle/kernel/thread.h"
13#include "core/hle/result.h"
14
15namespace Core {
16class System;
17}
18
19namespace Kernel {
20
21class KConditionVariable {
22public:
23 using ThreadTree = typename Thread::ConditionVariableThreadTreeType;
24
25 explicit KConditionVariable(Core::System& system_);
26 ~KConditionVariable();
27
28 // Arbitration
29 [[nodiscard]] ResultCode SignalToAddress(VAddr addr);
30 [[nodiscard]] ResultCode WaitForAddress(Handle handle, VAddr addr, u32 value);
31
32 // Condition variable
33 void Signal(u64 cv_key, s32 count);
34 [[nodiscard]] ResultCode Wait(VAddr addr, u64 key, u32 value, s64 timeout);
35
36private:
37 [[nodiscard]] Thread* SignalImpl(Thread* thread);
38
39 ThreadTree thread_tree;
40
41 Core::System& system;
42 KernelCore& kernel;
43};
44
45inline void BeforeUpdatePriority(const KernelCore& kernel, KConditionVariable::ThreadTree* tree,
46 Thread* thread) {
47 ASSERT(kernel.GlobalSchedulerContext().IsLocked());
48
49 tree->erase(tree->iterator_to(*thread));
50}
51
52inline void AfterUpdatePriority(const KernelCore& kernel, KConditionVariable::ThreadTree* tree,
53 Thread* thread) {
54 ASSERT(kernel.GlobalSchedulerContext().IsLocked());
55
56 tree->insert(*thread);
57}
58
59} // namespace Kernel
diff --git a/src/core/hle/kernel/k_priority_queue.h b/src/core/hle/kernel/k_priority_queue.h
index 99fb8fe93..0dc929040 100644
--- a/src/core/hle/kernel/k_priority_queue.h
+++ b/src/core/hle/kernel/k_priority_queue.h
@@ -8,11 +8,11 @@
8#pragma once 8#pragma once
9 9
10#include <array> 10#include <array>
11#include <bit>
11#include <concepts> 12#include <concepts>
12 13
13#include "common/assert.h" 14#include "common/assert.h"
14#include "common/bit_set.h" 15#include "common/bit_set.h"
15#include "common/bit_util.h"
16#include "common/common_types.h" 16#include "common/common_types.h"
17#include "common/concepts.h" 17#include "common/concepts.h"
18 18
@@ -268,7 +268,7 @@ private:
268 } 268 }
269 269
270 constexpr s32 GetNextCore(u64& affinity) { 270 constexpr s32 GetNextCore(u64& affinity) {
271 const s32 core = Common::CountTrailingZeroes64(affinity); 271 const s32 core = std::countr_zero(affinity);
272 ClearAffinityBit(affinity, core); 272 ClearAffinityBit(affinity, core);
273 return core; 273 return core;
274 } 274 }
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp
index c5fd82a6b..12b5619fb 100644
--- a/src/core/hle/kernel/k_scheduler.cpp
+++ b/src/core/hle/kernel/k_scheduler.cpp
@@ -5,6 +5,8 @@
5// This file references various implementation details from Atmosphere, an open-source firmware for 5// This file references various implementation details from Atmosphere, an open-source firmware for
6// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. 6// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
7 7
8#include <bit>
9
8#include "common/assert.h" 10#include "common/assert.h"
9#include "common/bit_util.h" 11#include "common/bit_util.h"
10#include "common/fiber.h" 12#include "common/fiber.h"
@@ -31,12 +33,12 @@ static void IncrementScheduledCount(Kernel::Thread* thread) {
31 33
32void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule, 34void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule,
33 Core::EmuThreadHandle global_thread) { 35 Core::EmuThreadHandle global_thread) {
34 u32 current_core = global_thread.host_handle; 36 const u32 current_core = global_thread.host_handle;
35 bool must_context_switch = global_thread.guest_handle != InvalidHandle && 37 bool must_context_switch = global_thread.guest_handle != InvalidHandle &&
36 (current_core < Core::Hardware::NUM_CPU_CORES); 38 (current_core < Core::Hardware::NUM_CPU_CORES);
37 39
38 while (cores_pending_reschedule != 0) { 40 while (cores_pending_reschedule != 0) {
39 u32 core = Common::CountTrailingZeroes64(cores_pending_reschedule); 41 const auto core = static_cast<u32>(std::countr_zero(cores_pending_reschedule));
40 ASSERT(core < Core::Hardware::NUM_CPU_CORES); 42 ASSERT(core < Core::Hardware::NUM_CPU_CORES);
41 if (!must_context_switch || core != current_core) { 43 if (!must_context_switch || core != current_core) {
42 auto& phys_core = kernel.PhysicalCore(core); 44 auto& phys_core = kernel.PhysicalCore(core);
@@ -109,7 +111,7 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
109 111
110 // Idle cores are bad. We're going to try to migrate threads to each idle core in turn. 112 // Idle cores are bad. We're going to try to migrate threads to each idle core in turn.
111 while (idle_cores != 0) { 113 while (idle_cores != 0) {
112 u32 core_id = Common::CountTrailingZeroes64(idle_cores); 114 const auto core_id = static_cast<u32>(std::countr_zero(idle_cores));
113 if (Thread* suggested = priority_queue.GetSuggestedFront(core_id); suggested != nullptr) { 115 if (Thread* suggested = priority_queue.GetSuggestedFront(core_id); suggested != nullptr) {
114 s32 migration_candidates[Core::Hardware::NUM_CPU_CORES]; 116 s32 migration_candidates[Core::Hardware::NUM_CPU_CORES];
115 size_t num_candidates = 0; 117 size_t num_candidates = 0;
@@ -180,22 +182,22 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
180 return cores_needing_scheduling; 182 return cores_needing_scheduling;
181} 183}
182 184
183void KScheduler::OnThreadStateChanged(KernelCore& kernel, Thread* thread, u32 old_state) { 185void KScheduler::OnThreadStateChanged(KernelCore& kernel, Thread* thread, ThreadState old_state) {
184 ASSERT(kernel.GlobalSchedulerContext().IsLocked()); 186 ASSERT(kernel.GlobalSchedulerContext().IsLocked());
185 187
186 // Check if the state has changed, because if it hasn't there's nothing to do. 188 // Check if the state has changed, because if it hasn't there's nothing to do.
187 const auto cur_state = thread->scheduling_state; 189 const auto cur_state = thread->GetRawState();
188 if (cur_state == old_state) { 190 if (cur_state == old_state) {
189 return; 191 return;
190 } 192 }
191 193
192 // Update the priority queues. 194 // Update the priority queues.
193 if (old_state == static_cast<u32>(ThreadSchedStatus::Runnable)) { 195 if (old_state == ThreadState::Runnable) {
194 // If we were previously runnable, then we're not runnable now, and we should remove. 196 // If we were previously runnable, then we're not runnable now, and we should remove.
195 GetPriorityQueue(kernel).Remove(thread); 197 GetPriorityQueue(kernel).Remove(thread);
196 IncrementScheduledCount(thread); 198 IncrementScheduledCount(thread);
197 SetSchedulerUpdateNeeded(kernel); 199 SetSchedulerUpdateNeeded(kernel);
198 } else if (cur_state == static_cast<u32>(ThreadSchedStatus::Runnable)) { 200 } else if (cur_state == ThreadState::Runnable) {
199 // If we're now runnable, then we weren't previously, and we should add. 201 // If we're now runnable, then we weren't previously, and we should add.
200 GetPriorityQueue(kernel).PushBack(thread); 202 GetPriorityQueue(kernel).PushBack(thread);
201 IncrementScheduledCount(thread); 203 IncrementScheduledCount(thread);
@@ -203,13 +205,11 @@ void KScheduler::OnThreadStateChanged(KernelCore& kernel, Thread* thread, u32 ol
203 } 205 }
204} 206}
205 207
206void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, Thread* thread, Thread* current_thread, 208void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, Thread* thread, s32 old_priority) {
207 u32 old_priority) {
208
209 ASSERT(kernel.GlobalSchedulerContext().IsLocked()); 209 ASSERT(kernel.GlobalSchedulerContext().IsLocked());
210 210
211 // If the thread is runnable, we want to change its priority in the queue. 211 // If the thread is runnable, we want to change its priority in the queue.
212 if (thread->scheduling_state == static_cast<u32>(ThreadSchedStatus::Runnable)) { 212 if (thread->GetRawState() == ThreadState::Runnable) {
213 GetPriorityQueue(kernel).ChangePriority( 213 GetPriorityQueue(kernel).ChangePriority(
214 old_priority, thread == kernel.CurrentScheduler()->GetCurrentThread(), thread); 214 old_priority, thread == kernel.CurrentScheduler()->GetCurrentThread(), thread);
215 IncrementScheduledCount(thread); 215 IncrementScheduledCount(thread);
@@ -222,7 +222,7 @@ void KScheduler::OnThreadAffinityMaskChanged(KernelCore& kernel, Thread* thread,
222 ASSERT(kernel.GlobalSchedulerContext().IsLocked()); 222 ASSERT(kernel.GlobalSchedulerContext().IsLocked());
223 223
224 // If the thread is runnable, we want to change its affinity in the queue. 224 // If the thread is runnable, we want to change its affinity in the queue.
225 if (thread->scheduling_state == static_cast<u32>(ThreadSchedStatus::Runnable)) { 225 if (thread->GetRawState() == ThreadState::Runnable) {
226 GetPriorityQueue(kernel).ChangeAffinityMask(old_core, old_affinity, thread); 226 GetPriorityQueue(kernel).ChangeAffinityMask(old_core, old_affinity, thread);
227 IncrementScheduledCount(thread); 227 IncrementScheduledCount(thread);
228 SetSchedulerUpdateNeeded(kernel); 228 SetSchedulerUpdateNeeded(kernel);
@@ -292,7 +292,7 @@ void KScheduler::RotateScheduledQueue(s32 core_id, s32 priority) {
292 292
293 // If the best thread we can choose has a priority the same or worse than ours, try to 293 // If the best thread we can choose has a priority the same or worse than ours, try to
294 // migrate a higher priority thread. 294 // migrate a higher priority thread.
295 if (best_thread != nullptr && best_thread->GetPriority() >= static_cast<u32>(priority)) { 295 if (best_thread != nullptr && best_thread->GetPriority() >= priority) {
296 Thread* suggested = priority_queue.GetSuggestedFront(core_id); 296 Thread* suggested = priority_queue.GetSuggestedFront(core_id);
297 while (suggested != nullptr) { 297 while (suggested != nullptr) {
298 // If the suggestion's priority is the same as ours, don't bother. 298 // If the suggestion's priority is the same as ours, don't bother.
@@ -395,8 +395,8 @@ void KScheduler::YieldWithoutCoreMigration() {
395 { 395 {
396 KScopedSchedulerLock lock(kernel); 396 KScopedSchedulerLock lock(kernel);
397 397
398 const auto cur_state = cur_thread.scheduling_state; 398 const auto cur_state = cur_thread.GetRawState();
399 if (cur_state == static_cast<u32>(ThreadSchedStatus::Runnable)) { 399 if (cur_state == ThreadState::Runnable) {
400 // Put the current thread at the back of the queue. 400 // Put the current thread at the back of the queue.
401 Thread* next_thread = priority_queue.MoveToScheduledBack(std::addressof(cur_thread)); 401 Thread* next_thread = priority_queue.MoveToScheduledBack(std::addressof(cur_thread));
402 IncrementScheduledCount(std::addressof(cur_thread)); 402 IncrementScheduledCount(std::addressof(cur_thread));
@@ -436,8 +436,8 @@ void KScheduler::YieldWithCoreMigration() {
436 { 436 {
437 KScopedSchedulerLock lock(kernel); 437 KScopedSchedulerLock lock(kernel);
438 438
439 const auto cur_state = cur_thread.scheduling_state; 439 const auto cur_state = cur_thread.GetRawState();
440 if (cur_state == static_cast<u32>(ThreadSchedStatus::Runnable)) { 440 if (cur_state == ThreadState::Runnable) {
441 // Get the current active core. 441 // Get the current active core.
442 const s32 core_id = cur_thread.GetActiveCore(); 442 const s32 core_id = cur_thread.GetActiveCore();
443 443
@@ -526,8 +526,8 @@ void KScheduler::YieldToAnyThread() {
526 { 526 {
527 KScopedSchedulerLock lock(kernel); 527 KScopedSchedulerLock lock(kernel);
528 528
529 const auto cur_state = cur_thread.scheduling_state; 529 const auto cur_state = cur_thread.GetRawState();
530 if (cur_state == static_cast<u32>(ThreadSchedStatus::Runnable)) { 530 if (cur_state == ThreadState::Runnable) {
531 // Get the current active core. 531 // Get the current active core.
532 const s32 core_id = cur_thread.GetActiveCore(); 532 const s32 core_id = cur_thread.GetActiveCore();
533 533
@@ -645,8 +645,7 @@ void KScheduler::Unload(Thread* thread) {
645 645
646void KScheduler::Reload(Thread* thread) { 646void KScheduler::Reload(Thread* thread) {
647 if (thread) { 647 if (thread) {
648 ASSERT_MSG(thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable, 648 ASSERT_MSG(thread->GetState() == ThreadState::Runnable, "Thread must be runnable.");
649 "Thread must be runnable.");
650 649
651 // Cancel any outstanding wakeup events for this thread 650 // Cancel any outstanding wakeup events for this thread
652 thread->SetIsRunning(true); 651 thread->SetIsRunning(true);
@@ -725,7 +724,7 @@ void KScheduler::SwitchToCurrent() {
725 do { 724 do {
726 if (current_thread != nullptr && !current_thread->IsHLEThread()) { 725 if (current_thread != nullptr && !current_thread->IsHLEThread()) {
727 current_thread->context_guard.lock(); 726 current_thread->context_guard.lock();
728 if (!current_thread->IsRunnable()) { 727 if (current_thread->GetRawState() != ThreadState::Runnable) {
729 current_thread->context_guard.unlock(); 728 current_thread->context_guard.unlock();
730 break; 729 break;
731 } 730 }
@@ -772,7 +771,7 @@ void KScheduler::Initialize() {
772 771
773 { 772 {
774 KScopedSchedulerLock lock{system.Kernel()}; 773 KScopedSchedulerLock lock{system.Kernel()};
775 idle_thread->SetStatus(ThreadStatus::Ready); 774 idle_thread->SetState(ThreadState::Runnable);
776 } 775 }
777} 776}
778 777
diff --git a/src/core/hle/kernel/k_scheduler.h b/src/core/hle/kernel/k_scheduler.h
index e84abc84c..783665123 100644
--- a/src/core/hle/kernel/k_scheduler.h
+++ b/src/core/hle/kernel/k_scheduler.h
@@ -100,11 +100,10 @@ public:
100 void YieldToAnyThread(); 100 void YieldToAnyThread();
101 101
102 /// Notify the scheduler a thread's status has changed. 102 /// Notify the scheduler a thread's status has changed.
103 static void OnThreadStateChanged(KernelCore& kernel, Thread* thread, u32 old_state); 103 static void OnThreadStateChanged(KernelCore& kernel, Thread* thread, ThreadState old_state);
104 104
105 /// Notify the scheduler a thread's priority has changed. 105 /// Notify the scheduler a thread's priority has changed.
106 static void OnThreadPriorityChanged(KernelCore& kernel, Thread* thread, Thread* current_thread, 106 static void OnThreadPriorityChanged(KernelCore& kernel, Thread* thread, s32 old_priority);
107 u32 old_priority);
108 107
109 /// Notify the scheduler a thread's core and/or affinity mask has changed. 108 /// Notify the scheduler a thread's core and/or affinity mask has changed.
110 static void OnThreadAffinityMaskChanged(KernelCore& kernel, Thread* thread, 109 static void OnThreadAffinityMaskChanged(KernelCore& kernel, Thread* thread,
diff --git a/src/core/hle/kernel/k_scheduler_lock.h b/src/core/hle/kernel/k_scheduler_lock.h
index 2f1c1f691..9b40bd22c 100644
--- a/src/core/hle/kernel/k_scheduler_lock.h
+++ b/src/core/hle/kernel/k_scheduler_lock.h
@@ -19,7 +19,7 @@ class KernelCore;
19template <typename SchedulerType> 19template <typename SchedulerType>
20class KAbstractSchedulerLock { 20class KAbstractSchedulerLock {
21public: 21public:
22 explicit KAbstractSchedulerLock(KernelCore& kernel) : kernel{kernel} {} 22 explicit KAbstractSchedulerLock(KernelCore& kernel_) : kernel{kernel_} {}
23 23
24 bool IsLockedByCurrentThread() const { 24 bool IsLockedByCurrentThread() const {
25 return this->owner_thread == kernel.GetCurrentEmuThreadID(); 25 return this->owner_thread == kernel.GetCurrentEmuThreadID();
diff --git a/src/core/hle/kernel/k_synchronization_object.cpp b/src/core/hle/kernel/k_synchronization_object.cpp
new file mode 100644
index 000000000..1c508cb55
--- /dev/null
+++ b/src/core/hle/kernel/k_synchronization_object.cpp
@@ -0,0 +1,172 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "common/common_types.h"
7#include "core/hle/kernel/k_scheduler.h"
8#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
9#include "core/hle/kernel/k_synchronization_object.h"
10#include "core/hle/kernel/kernel.h"
11#include "core/hle/kernel/svc_results.h"
12#include "core/hle/kernel/thread.h"
13
14namespace Kernel {
15
16ResultCode KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index,
17 KSynchronizationObject** objects, const s32 num_objects,
18 s64 timeout) {
19 // Allocate space on stack for thread nodes.
20 std::vector<ThreadListNode> thread_nodes(num_objects);
21
22 // Prepare for wait.
23 Thread* thread = kernel.CurrentScheduler()->GetCurrentThread();
24 Handle timer = InvalidHandle;
25
26 {
27 // Setup the scheduling lock and sleep.
28 KScopedSchedulerLockAndSleep slp(kernel, timer, thread, timeout);
29
30 // Check if any of the objects are already signaled.
31 for (auto i = 0; i < num_objects; ++i) {
32 ASSERT(objects[i] != nullptr);
33
34 if (objects[i]->IsSignaled()) {
35 *out_index = i;
36 slp.CancelSleep();
37 return RESULT_SUCCESS;
38 }
39 }
40
41 // Check if the timeout is zero.
42 if (timeout == 0) {
43 slp.CancelSleep();
44 return Svc::ResultTimedOut;
45 }
46
47 // Check if the thread should terminate.
48 if (thread->IsTerminationRequested()) {
49 slp.CancelSleep();
50 return Svc::ResultTerminationRequested;
51 }
52
53 // Check if waiting was canceled.
54 if (thread->IsWaitCancelled()) {
55 slp.CancelSleep();
56 thread->ClearWaitCancelled();
57 return Svc::ResultCancelled;
58 }
59
60 // Add the waiters.
61 for (auto i = 0; i < num_objects; ++i) {
62 thread_nodes[i].thread = thread;
63 thread_nodes[i].next = nullptr;
64
65 if (objects[i]->thread_list_tail == nullptr) {
66 objects[i]->thread_list_head = std::addressof(thread_nodes[i]);
67 } else {
68 objects[i]->thread_list_tail->next = std::addressof(thread_nodes[i]);
69 }
70
71 objects[i]->thread_list_tail = std::addressof(thread_nodes[i]);
72 }
73
74 // For debugging only
75 thread->SetWaitObjectsForDebugging({objects, static_cast<std::size_t>(num_objects)});
76
77 // Mark the thread as waiting.
78 thread->SetCancellable();
79 thread->SetSyncedObject(nullptr, Svc::ResultTimedOut);
80 thread->SetState(ThreadState::Waiting);
81 thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Synchronization);
82 }
83
84 // The lock/sleep is done, so we should be able to get our result.
85
86 // Thread is no longer cancellable.
87 thread->ClearCancellable();
88
89 // For debugging only
90 thread->SetWaitObjectsForDebugging({});
91
92 // Cancel the timer as needed.
93 if (timer != InvalidHandle) {
94 auto& time_manager = kernel.TimeManager();
95 time_manager.UnscheduleTimeEvent(timer);
96 }
97
98 // Get the wait result.
99 ResultCode wait_result{RESULT_SUCCESS};
100 s32 sync_index = -1;
101 {
102 KScopedSchedulerLock lock(kernel);
103 KSynchronizationObject* synced_obj;
104 wait_result = thread->GetWaitResult(std::addressof(synced_obj));
105
106 for (auto i = 0; i < num_objects; ++i) {
107 // Unlink the object from the list.
108 ThreadListNode* prev_ptr =
109 reinterpret_cast<ThreadListNode*>(std::addressof(objects[i]->thread_list_head));
110 ThreadListNode* prev_val = nullptr;
111 ThreadListNode *prev, *tail_prev;
112
113 do {
114 prev = prev_ptr;
115 prev_ptr = prev_ptr->next;
116 tail_prev = prev_val;
117 prev_val = prev_ptr;
118 } while (prev_ptr != std::addressof(thread_nodes[i]));
119
120 if (objects[i]->thread_list_tail == std::addressof(thread_nodes[i])) {
121 objects[i]->thread_list_tail = tail_prev;
122 }
123
124 prev->next = thread_nodes[i].next;
125
126 if (objects[i] == synced_obj) {
127 sync_index = i;
128 }
129 }
130 }
131
132 // Set output.
133 *out_index = sync_index;
134 return wait_result;
135}
136
137KSynchronizationObject::KSynchronizationObject(KernelCore& kernel) : Object{kernel} {}
138
139KSynchronizationObject ::~KSynchronizationObject() = default;
140
141void KSynchronizationObject::NotifyAvailable(ResultCode result) {
142 KScopedSchedulerLock lock(kernel);
143
144 // If we're not signaled, we've nothing to notify.
145 if (!this->IsSignaled()) {
146 return;
147 }
148
149 // Iterate over each thread.
150 for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) {
151 Thread* thread = cur_node->thread;
152 if (thread->GetState() == ThreadState::Waiting) {
153 thread->SetSyncedObject(this, result);
154 thread->SetState(ThreadState::Runnable);
155 }
156 }
157}
158
159std::vector<Thread*> KSynchronizationObject::GetWaitingThreadsForDebugging() const {
160 std::vector<Thread*> threads;
161
162 // If debugging, dump the list of waiters.
163 {
164 KScopedSchedulerLock lock(kernel);
165 for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) {
166 threads.emplace_back(cur_node->thread);
167 }
168 }
169
170 return threads;
171}
172} // namespace Kernel
diff --git a/src/core/hle/kernel/k_synchronization_object.h b/src/core/hle/kernel/k_synchronization_object.h
new file mode 100644
index 000000000..14d80ebf1
--- /dev/null
+++ b/src/core/hle/kernel/k_synchronization_object.h
@@ -0,0 +1,58 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <vector>
8
9#include "core/hle/kernel/object.h"
10#include "core/hle/result.h"
11
12namespace Kernel {
13
14class KernelCore;
15class Synchronization;
16class Thread;
17
18/// Class that represents a Kernel object that a thread can be waiting on
19class KSynchronizationObject : public Object {
20public:
21 struct ThreadListNode {
22 ThreadListNode* next{};
23 Thread* thread{};
24 };
25
26 [[nodiscard]] static ResultCode Wait(KernelCore& kernel, s32* out_index,
27 KSynchronizationObject** objects, const s32 num_objects,
28 s64 timeout);
29
30 [[nodiscard]] virtual bool IsSignaled() const = 0;
31
32 [[nodiscard]] std::vector<Thread*> GetWaitingThreadsForDebugging() const;
33
34protected:
35 explicit KSynchronizationObject(KernelCore& kernel);
36 virtual ~KSynchronizationObject();
37
38 void NotifyAvailable(ResultCode result);
39 void NotifyAvailable() {
40 return this->NotifyAvailable(RESULT_SUCCESS);
41 }
42
43private:
44 ThreadListNode* thread_list_head{};
45 ThreadListNode* thread_list_tail{};
46};
47
48// Specialization of DynamicObjectCast for KSynchronizationObjects
49template <>
50inline std::shared_ptr<KSynchronizationObject> DynamicObjectCast<KSynchronizationObject>(
51 std::shared_ptr<Object> object) {
52 if (object != nullptr && object->IsWaitable()) {
53 return std::static_pointer_cast<KSynchronizationObject>(object);
54 }
55 return nullptr;
56}
57
58} // namespace Kernel
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index e8ece8164..c0ff287a6 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -38,7 +38,6 @@
38#include "core/hle/kernel/resource_limit.h" 38#include "core/hle/kernel/resource_limit.h"
39#include "core/hle/kernel/service_thread.h" 39#include "core/hle/kernel/service_thread.h"
40#include "core/hle/kernel/shared_memory.h" 40#include "core/hle/kernel/shared_memory.h"
41#include "core/hle/kernel/synchronization.h"
42#include "core/hle/kernel/thread.h" 41#include "core/hle/kernel/thread.h"
43#include "core/hle/kernel/time_manager.h" 42#include "core/hle/kernel/time_manager.h"
44#include "core/hle/lock.h" 43#include "core/hle/lock.h"
@@ -51,8 +50,7 @@ namespace Kernel {
51 50
52struct KernelCore::Impl { 51struct KernelCore::Impl {
53 explicit Impl(Core::System& system, KernelCore& kernel) 52 explicit Impl(Core::System& system, KernelCore& kernel)
54 : synchronization{system}, time_manager{system}, global_handle_table{kernel}, system{ 53 : time_manager{system}, global_handle_table{kernel}, system{system} {}
55 system} {}
56 54
57 void SetMulticore(bool is_multicore) { 55 void SetMulticore(bool is_multicore) {
58 this->is_multicore = is_multicore; 56 this->is_multicore = is_multicore;
@@ -307,7 +305,6 @@ struct KernelCore::Impl {
307 std::vector<std::shared_ptr<Process>> process_list; 305 std::vector<std::shared_ptr<Process>> process_list;
308 Process* current_process = nullptr; 306 Process* current_process = nullptr;
309 std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context; 307 std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context;
310 Kernel::Synchronization synchronization;
311 Kernel::TimeManager time_manager; 308 Kernel::TimeManager time_manager;
312 309
313 std::shared_ptr<ResourceLimit> system_resource_limit; 310 std::shared_ptr<ResourceLimit> system_resource_limit;
@@ -461,14 +458,6 @@ const std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& Kern
461 return impl->interrupts; 458 return impl->interrupts;
462} 459}
463 460
464Kernel::Synchronization& KernelCore::Synchronization() {
465 return impl->synchronization;
466}
467
468const Kernel::Synchronization& KernelCore::Synchronization() const {
469 return impl->synchronization;
470}
471
472Kernel::TimeManager& KernelCore::TimeManager() { 461Kernel::TimeManager& KernelCore::TimeManager() {
473 return impl->time_manager; 462 return impl->time_manager;
474} 463}
@@ -613,9 +602,11 @@ void KernelCore::Suspend(bool in_suspention) {
613 const bool should_suspend = exception_exited || in_suspention; 602 const bool should_suspend = exception_exited || in_suspention;
614 { 603 {
615 KScopedSchedulerLock lock(*this); 604 KScopedSchedulerLock lock(*this);
616 ThreadStatus status = should_suspend ? ThreadStatus::Ready : ThreadStatus::WaitSleep; 605 const auto state = should_suspend ? ThreadState::Runnable : ThreadState::Waiting;
617 for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { 606 for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
618 impl->suspend_threads[i]->SetStatus(status); 607 impl->suspend_threads[i]->SetState(state);
608 impl->suspend_threads[i]->SetWaitReasonForDebugging(
609 ThreadWaitReasonForDebugging::Suspended);
619 } 610 }
620 } 611 }
621} 612}
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index e3169f5a7..933d9a7d6 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -33,7 +33,6 @@ template <typename T>
33class SlabHeap; 33class SlabHeap;
34} // namespace Memory 34} // namespace Memory
35 35
36class AddressArbiter;
37class ClientPort; 36class ClientPort;
38class GlobalSchedulerContext; 37class GlobalSchedulerContext;
39class HandleTable; 38class HandleTable;
@@ -129,12 +128,6 @@ public:
129 /// Gets the an instance of the current physical CPU core. 128 /// Gets the an instance of the current physical CPU core.
130 const Kernel::PhysicalCore& CurrentPhysicalCore() const; 129 const Kernel::PhysicalCore& CurrentPhysicalCore() const;
131 130
132 /// Gets the an instance of the Synchronization Interface.
133 Kernel::Synchronization& Synchronization();
134
135 /// Gets the an instance of the Synchronization Interface.
136 const Kernel::Synchronization& Synchronization() const;
137
138 /// Gets the an instance of the TimeManager Interface. 131 /// Gets the an instance of the TimeManager Interface.
139 Kernel::TimeManager& TimeManager(); 132 Kernel::TimeManager& TimeManager();
140 133
diff --git a/src/core/hle/kernel/memory/address_space_info.cpp b/src/core/hle/kernel/memory/address_space_info.cpp
index e4288cab4..6cf43ba24 100644
--- a/src/core/hle/kernel/memory/address_space_info.cpp
+++ b/src/core/hle/kernel/memory/address_space_info.cpp
@@ -96,6 +96,7 @@ u64 AddressSpaceInfo::GetAddressSpaceStart(std::size_t width, Type type) {
96 return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].address; 96 return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].address;
97 } 97 }
98 UNREACHABLE(); 98 UNREACHABLE();
99 return 0;
99} 100}
100 101
101std::size_t AddressSpaceInfo::GetAddressSpaceSize(std::size_t width, Type type) { 102std::size_t AddressSpaceInfo::GetAddressSpaceSize(std::size_t width, Type type) {
@@ -112,6 +113,7 @@ std::size_t AddressSpaceInfo::GetAddressSpaceSize(std::size_t width, Type type)
112 return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].size; 113 return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].size;
113 } 114 }
114 UNREACHABLE(); 115 UNREACHABLE();
116 return 0;
115} 117}
116 118
117} // namespace Kernel::Memory 119} // namespace Kernel::Memory
diff --git a/src/core/hle/kernel/memory/memory_block.h b/src/core/hle/kernel/memory/memory_block.h
index 37fe19916..83acece1e 100644
--- a/src/core/hle/kernel/memory/memory_block.h
+++ b/src/core/hle/kernel/memory/memory_block.h
@@ -73,12 +73,12 @@ enum class MemoryState : u32 {
73 ThreadLocal = 73 ThreadLocal =
74 static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagReferenceCounted, 74 static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagReferenceCounted,
75 75
76 Transfered = static_cast<u32>(Svc::MemoryState::Transfered) | FlagsMisc | 76 Transferred = static_cast<u32>(Svc::MemoryState::Transferred) | FlagsMisc |
77 FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc | 77 FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc |
78 FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, 78 FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
79 79
80 SharedTransfered = static_cast<u32>(Svc::MemoryState::SharedTransfered) | FlagsMisc | 80 SharedTransferred = static_cast<u32>(Svc::MemoryState::SharedTransferred) | FlagsMisc |
81 FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, 81 FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
82 82
83 SharedCode = static_cast<u32>(Svc::MemoryState::SharedCode) | FlagMapped | 83 SharedCode = static_cast<u32>(Svc::MemoryState::SharedCode) | FlagMapped |
84 FlagReferenceCounted | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, 84 FlagReferenceCounted | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
@@ -111,8 +111,8 @@ static_assert(static_cast<u32>(MemoryState::AliasCodeData) == 0x03FFBD09);
111static_assert(static_cast<u32>(MemoryState::Ipc) == 0x005C3C0A); 111static_assert(static_cast<u32>(MemoryState::Ipc) == 0x005C3C0A);
112static_assert(static_cast<u32>(MemoryState::Stack) == 0x005C3C0B); 112static_assert(static_cast<u32>(MemoryState::Stack) == 0x005C3C0B);
113static_assert(static_cast<u32>(MemoryState::ThreadLocal) == 0x0040200C); 113static_assert(static_cast<u32>(MemoryState::ThreadLocal) == 0x0040200C);
114static_assert(static_cast<u32>(MemoryState::Transfered) == 0x015C3C0D); 114static_assert(static_cast<u32>(MemoryState::Transferred) == 0x015C3C0D);
115static_assert(static_cast<u32>(MemoryState::SharedTransfered) == 0x005C380E); 115static_assert(static_cast<u32>(MemoryState::SharedTransferred) == 0x005C380E);
116static_assert(static_cast<u32>(MemoryState::SharedCode) == 0x0040380F); 116static_assert(static_cast<u32>(MemoryState::SharedCode) == 0x0040380F);
117static_assert(static_cast<u32>(MemoryState::Inaccessible) == 0x00000010); 117static_assert(static_cast<u32>(MemoryState::Inaccessible) == 0x00000010);
118static_assert(static_cast<u32>(MemoryState::NonSecureIpc) == 0x005C3811); 118static_assert(static_cast<u32>(MemoryState::NonSecureIpc) == 0x005C3811);
diff --git a/src/core/hle/kernel/memory/memory_layout.h b/src/core/hle/kernel/memory/memory_layout.h
index 9b3d6267a..c7c0b2f49 100644
--- a/src/core/hle/kernel/memory/memory_layout.h
+++ b/src/core/hle/kernel/memory/memory_layout.h
@@ -5,9 +5,28 @@
5#pragma once 5#pragma once
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "core/device_memory.h"
8 9
9namespace Kernel::Memory { 10namespace Kernel::Memory {
10 11
12constexpr std::size_t KernelAslrAlignment = 2 * 1024 * 1024;
13constexpr std::size_t KernelVirtualAddressSpaceWidth = 1ULL << 39;
14constexpr std::size_t KernelPhysicalAddressSpaceWidth = 1ULL << 48;
15constexpr std::size_t KernelVirtualAddressSpaceBase = 0ULL - KernelVirtualAddressSpaceWidth;
16constexpr std::size_t KernelVirtualAddressSpaceEnd =
17 KernelVirtualAddressSpaceBase + (KernelVirtualAddressSpaceWidth - KernelAslrAlignment);
18constexpr std::size_t KernelVirtualAddressSpaceLast = KernelVirtualAddressSpaceEnd - 1;
19constexpr std::size_t KernelVirtualAddressSpaceSize =
20 KernelVirtualAddressSpaceEnd - KernelVirtualAddressSpaceBase;
21
22constexpr bool IsKernelAddressKey(VAddr key) {
23 return KernelVirtualAddressSpaceBase <= key && key <= KernelVirtualAddressSpaceLast;
24}
25
26constexpr bool IsKernelAddress(VAddr address) {
27 return KernelVirtualAddressSpaceBase <= address && address < KernelVirtualAddressSpaceEnd;
28}
29
11class MemoryRegion final { 30class MemoryRegion final {
12 friend class MemoryLayout; 31 friend class MemoryLayout;
13 32
diff --git a/src/core/hle/kernel/memory/page_heap.h b/src/core/hle/kernel/memory/page_heap.h
index 22b0de860..131093284 100644
--- a/src/core/hle/kernel/memory/page_heap.h
+++ b/src/core/hle/kernel/memory/page_heap.h
@@ -8,11 +8,11 @@
8#pragma once 8#pragma once
9 9
10#include <array> 10#include <array>
11#include <bit>
11#include <vector> 12#include <vector>
12 13
13#include "common/alignment.h" 14#include "common/alignment.h"
14#include "common/assert.h" 15#include "common/assert.h"
15#include "common/bit_util.h"
16#include "common/common_funcs.h" 16#include "common/common_funcs.h"
17#include "common/common_types.h" 17#include "common/common_types.h"
18#include "core/hle/kernel/memory/memory_types.h" 18#include "core/hle/kernel/memory/memory_types.h"
@@ -105,7 +105,7 @@ private:
105 ASSERT(depth == 0); 105 ASSERT(depth == 0);
106 return -1; 106 return -1;
107 } 107 }
108 offset = offset * 64 + Common::CountTrailingZeroes64(v); 108 offset = offset * 64 + static_cast<u32>(std::countr_zero(v));
109 ++depth; 109 ++depth;
110 } while (depth < static_cast<s32>(used_depths)); 110 } while (depth < static_cast<s32>(used_depths));
111 111
diff --git a/src/core/hle/kernel/memory/page_table.cpp b/src/core/hle/kernel/memory/page_table.cpp
index f3e8bc333..080886554 100644
--- a/src/core/hle/kernel/memory/page_table.cpp
+++ b/src/core/hle/kernel/memory/page_table.cpp
@@ -1007,8 +1007,8 @@ constexpr VAddr PageTable::GetRegionAddress(MemoryState state) const {
1007 case MemoryState::Shared: 1007 case MemoryState::Shared:
1008 case MemoryState::AliasCode: 1008 case MemoryState::AliasCode:
1009 case MemoryState::AliasCodeData: 1009 case MemoryState::AliasCodeData:
1010 case MemoryState::Transfered: 1010 case MemoryState::Transferred:
1011 case MemoryState::SharedTransfered: 1011 case MemoryState::SharedTransferred:
1012 case MemoryState::SharedCode: 1012 case MemoryState::SharedCode:
1013 case MemoryState::GeneratedCode: 1013 case MemoryState::GeneratedCode:
1014 case MemoryState::CodeOut: 1014 case MemoryState::CodeOut:
@@ -1042,8 +1042,8 @@ constexpr std::size_t PageTable::GetRegionSize(MemoryState state) const {
1042 case MemoryState::Shared: 1042 case MemoryState::Shared:
1043 case MemoryState::AliasCode: 1043 case MemoryState::AliasCode:
1044 case MemoryState::AliasCodeData: 1044 case MemoryState::AliasCodeData:
1045 case MemoryState::Transfered: 1045 case MemoryState::Transferred:
1046 case MemoryState::SharedTransfered: 1046 case MemoryState::SharedTransferred:
1047 case MemoryState::SharedCode: 1047 case MemoryState::SharedCode:
1048 case MemoryState::GeneratedCode: 1048 case MemoryState::GeneratedCode:
1049 case MemoryState::CodeOut: 1049 case MemoryState::CodeOut:
@@ -1080,8 +1080,8 @@ constexpr bool PageTable::CanContain(VAddr addr, std::size_t size, MemoryState s
1080 case MemoryState::AliasCodeData: 1080 case MemoryState::AliasCodeData:
1081 case MemoryState::Stack: 1081 case MemoryState::Stack:
1082 case MemoryState::ThreadLocal: 1082 case MemoryState::ThreadLocal:
1083 case MemoryState::Transfered: 1083 case MemoryState::Transferred:
1084 case MemoryState::SharedTransfered: 1084 case MemoryState::SharedTransferred:
1085 case MemoryState::SharedCode: 1085 case MemoryState::SharedCode:
1086 case MemoryState::GeneratedCode: 1086 case MemoryState::GeneratedCode:
1087 case MemoryState::CodeOut: 1087 case MemoryState::CodeOut:
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
deleted file mode 100644
index 4f8075e0e..000000000
--- a/src/core/hle/kernel/mutex.cpp
+++ /dev/null
@@ -1,170 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <memory>
6#include <utility>
7#include <vector>
8
9#include "common/assert.h"
10#include "common/logging/log.h"
11#include "core/core.h"
12#include "core/hle/kernel/errors.h"
13#include "core/hle/kernel/handle_table.h"
14#include "core/hle/kernel/k_scheduler.h"
15#include "core/hle/kernel/kernel.h"
16#include "core/hle/kernel/mutex.h"
17#include "core/hle/kernel/object.h"
18#include "core/hle/kernel/process.h"
19#include "core/hle/kernel/thread.h"
20#include "core/hle/result.h"
21#include "core/memory.h"
22
23namespace Kernel {
24
25/// Returns the number of threads that are waiting for a mutex, and the highest priority one among
26/// those.
27static std::pair<std::shared_ptr<Thread>, u32> GetHighestPriorityMutexWaitingThread(
28 const std::shared_ptr<Thread>& current_thread, VAddr mutex_addr) {
29
30 std::shared_ptr<Thread> highest_priority_thread;
31 u32 num_waiters = 0;
32
33 for (const auto& thread : current_thread->GetMutexWaitingThreads()) {
34 if (thread->GetMutexWaitAddress() != mutex_addr)
35 continue;
36
37 ++num_waiters;
38 if (highest_priority_thread == nullptr ||
39 thread->GetPriority() < highest_priority_thread->GetPriority()) {
40 highest_priority_thread = thread;
41 }
42 }
43
44 return {highest_priority_thread, num_waiters};
45}
46
47/// Update the mutex owner field of all threads waiting on the mutex to point to the new owner.
48static void TransferMutexOwnership(VAddr mutex_addr, std::shared_ptr<Thread> current_thread,
49 std::shared_ptr<Thread> new_owner) {
50 current_thread->RemoveMutexWaiter(new_owner);
51 const auto threads = current_thread->GetMutexWaitingThreads();
52 for (const auto& thread : threads) {
53 if (thread->GetMutexWaitAddress() != mutex_addr)
54 continue;
55
56 ASSERT(thread->GetLockOwner() == current_thread.get());
57 current_thread->RemoveMutexWaiter(thread);
58 if (new_owner != thread)
59 new_owner->AddMutexWaiter(thread);
60 }
61}
62
63Mutex::Mutex(Core::System& system) : system{system} {}
64Mutex::~Mutex() = default;
65
66ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
67 Handle requesting_thread_handle) {
68 // The mutex address must be 4-byte aligned
69 if ((address % sizeof(u32)) != 0) {
70 LOG_ERROR(Kernel, "Address is not 4-byte aligned! address={:016X}", address);
71 return ERR_INVALID_ADDRESS;
72 }
73
74 auto& kernel = system.Kernel();
75 std::shared_ptr<Thread> current_thread =
76 SharedFrom(kernel.CurrentScheduler()->GetCurrentThread());
77 {
78 KScopedSchedulerLock lock(kernel);
79 // The mutex address must be 4-byte aligned
80 if ((address % sizeof(u32)) != 0) {
81 return ERR_INVALID_ADDRESS;
82 }
83
84 const auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
85 std::shared_ptr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle);
86 std::shared_ptr<Thread> requesting_thread =
87 handle_table.Get<Thread>(requesting_thread_handle);
88
89 // TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of
90 // another thread.
91 ASSERT(requesting_thread == current_thread);
92
93 current_thread->SetSynchronizationResults(nullptr, RESULT_SUCCESS);
94
95 const u32 addr_value = system.Memory().Read32(address);
96
97 // If the mutex isn't being held, just return success.
98 if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) {
99 return RESULT_SUCCESS;
100 }
101
102 if (holding_thread == nullptr) {
103 return ERR_INVALID_HANDLE;
104 }
105
106 // Wait until the mutex is released
107 current_thread->SetMutexWaitAddress(address);
108 current_thread->SetWaitHandle(requesting_thread_handle);
109
110 current_thread->SetStatus(ThreadStatus::WaitMutex);
111
112 // Update the lock holder thread's priority to prevent priority inversion.
113 holding_thread->AddMutexWaiter(current_thread);
114 }
115
116 {
117 KScopedSchedulerLock lock(kernel);
118 auto* owner = current_thread->GetLockOwner();
119 if (owner != nullptr) {
120 owner->RemoveMutexWaiter(current_thread);
121 }
122 }
123 return current_thread->GetSignalingResult();
124}
125
126std::pair<ResultCode, std::shared_ptr<Thread>> Mutex::Unlock(std::shared_ptr<Thread> owner,
127 VAddr address) {
128 // The mutex address must be 4-byte aligned
129 if ((address % sizeof(u32)) != 0) {
130 LOG_ERROR(Kernel, "Address is not 4-byte aligned! address={:016X}", address);
131 return {ERR_INVALID_ADDRESS, nullptr};
132 }
133
134 auto [new_owner, num_waiters] = GetHighestPriorityMutexWaitingThread(owner, address);
135 if (new_owner == nullptr) {
136 system.Memory().Write32(address, 0);
137 return {RESULT_SUCCESS, nullptr};
138 }
139 // Transfer the ownership of the mutex from the previous owner to the new one.
140 TransferMutexOwnership(address, owner, new_owner);
141 u32 mutex_value = new_owner->GetWaitHandle();
142 if (num_waiters >= 2) {
143 // Notify the guest that there are still some threads waiting for the mutex
144 mutex_value |= Mutex::MutexHasWaitersFlag;
145 }
146 new_owner->SetSynchronizationResults(nullptr, RESULT_SUCCESS);
147 new_owner->SetLockOwner(nullptr);
148 new_owner->ResumeFromWait();
149
150 system.Memory().Write32(address, mutex_value);
151 return {RESULT_SUCCESS, new_owner};
152}
153
154ResultCode Mutex::Release(VAddr address) {
155 auto& kernel = system.Kernel();
156 KScopedSchedulerLock lock(kernel);
157
158 std::shared_ptr<Thread> current_thread =
159 SharedFrom(kernel.CurrentScheduler()->GetCurrentThread());
160
161 auto [result, new_owner] = Unlock(current_thread, address);
162
163 if (result != RESULT_SUCCESS && new_owner != nullptr) {
164 new_owner->SetSynchronizationResults(nullptr, result);
165 }
166
167 return result;
168}
169
170} // namespace Kernel
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h
deleted file mode 100644
index 3b81dc3df..000000000
--- a/src/core/hle/kernel/mutex.h
+++ /dev/null
@@ -1,42 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8
9union ResultCode;
10
11namespace Core {
12class System;
13}
14
15namespace Kernel {
16
17class Mutex final {
18public:
19 explicit Mutex(Core::System& system);
20 ~Mutex();
21
22 /// Flag that indicates that a mutex still has threads waiting for it.
23 static constexpr u32 MutexHasWaitersFlag = 0x40000000;
24 /// Mask of the bits in a mutex address value that contain the mutex owner.
25 static constexpr u32 MutexOwnerMask = 0xBFFFFFFF;
26
27 /// Attempts to acquire a mutex at the specified address.
28 ResultCode TryAcquire(VAddr address, Handle holding_thread_handle,
29 Handle requesting_thread_handle);
30
31 /// Unlocks a mutex for owner at address
32 std::pair<ResultCode, std::shared_ptr<Thread>> Unlock(std::shared_ptr<Thread> owner,
33 VAddr address);
34
35 /// Releases the mutex at the specified address.
36 ResultCode Release(VAddr address);
37
38private:
39 Core::System& system;
40};
41
42} // namespace Kernel
diff --git a/src/core/hle/kernel/object.h b/src/core/hle/kernel/object.h
index e3391e2af..27124ef67 100644
--- a/src/core/hle/kernel/object.h
+++ b/src/core/hle/kernel/object.h
@@ -50,6 +50,11 @@ public:
50 } 50 }
51 virtual HandleType GetHandleType() const = 0; 51 virtual HandleType GetHandleType() const = 0;
52 52
53 void Close() {
54 // TODO(bunnei): This is a placeholder to decrement the reference count, which we will use
55 // when we implement KAutoObject instead of using shared_ptr.
56 }
57
53 /** 58 /**
54 * Check if a thread can wait on the object 59 * Check if a thread can wait on the object
55 * @return True if a thread can wait on the object, otherwise false 60 * @return True if a thread can wait on the object, otherwise false
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index b905b486a..37b77fa6e 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -55,7 +55,7 @@ void SetupMainThread(Core::System& system, Process& owner_process, u32 priority,
55 // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires 55 // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires
56 { 56 {
57 KScopedSchedulerLock lock{kernel}; 57 KScopedSchedulerLock lock{kernel};
58 thread->SetStatus(ThreadStatus::Ready); 58 thread->SetState(ThreadState::Runnable);
59 } 59 }
60} 60}
61} // Anonymous namespace 61} // Anonymous namespace
@@ -162,48 +162,6 @@ u64 Process::GetTotalPhysicalMemoryUsedWithoutSystemResource() const {
162 return GetTotalPhysicalMemoryUsed() - GetSystemResourceUsage(); 162 return GetTotalPhysicalMemoryUsed() - GetSystemResourceUsage();
163} 163}
164 164
165void Process::InsertConditionVariableThread(std::shared_ptr<Thread> thread) {
166 VAddr cond_var_addr = thread->GetCondVarWaitAddress();
167 std::list<std::shared_ptr<Thread>>& thread_list = cond_var_threads[cond_var_addr];
168 auto it = thread_list.begin();
169 while (it != thread_list.end()) {
170 const std::shared_ptr<Thread> current_thread = *it;
171 if (current_thread->GetPriority() > thread->GetPriority()) {
172 thread_list.insert(it, thread);
173 return;
174 }
175 ++it;
176 }
177 thread_list.push_back(thread);
178}
179
180void Process::RemoveConditionVariableThread(std::shared_ptr<Thread> thread) {
181 VAddr cond_var_addr = thread->GetCondVarWaitAddress();
182 std::list<std::shared_ptr<Thread>>& thread_list = cond_var_threads[cond_var_addr];
183 auto it = thread_list.begin();
184 while (it != thread_list.end()) {
185 const std::shared_ptr<Thread> current_thread = *it;
186 if (current_thread.get() == thread.get()) {
187 thread_list.erase(it);
188 return;
189 }
190 ++it;
191 }
192}
193
194std::vector<std::shared_ptr<Thread>> Process::GetConditionVariableThreads(
195 const VAddr cond_var_addr) {
196 std::vector<std::shared_ptr<Thread>> result{};
197 std::list<std::shared_ptr<Thread>>& thread_list = cond_var_threads[cond_var_addr];
198 auto it = thread_list.begin();
199 while (it != thread_list.end()) {
200 std::shared_ptr<Thread> current_thread = *it;
201 result.push_back(current_thread);
202 ++it;
203 }
204 return result;
205}
206
207void Process::RegisterThread(const Thread* thread) { 165void Process::RegisterThread(const Thread* thread) {
208 thread_list.push_back(thread); 166 thread_list.push_back(thread);
209} 167}
@@ -318,7 +276,7 @@ void Process::PrepareForTermination() {
318 continue; 276 continue;
319 277
320 // TODO(Subv): When are the other running/ready threads terminated? 278 // TODO(Subv): When are the other running/ready threads terminated?
321 ASSERT_MSG(thread->GetStatus() == ThreadStatus::WaitSynch, 279 ASSERT_MSG(thread->GetState() == ThreadState::Waiting,
322 "Exiting processes with non-waiting threads is currently unimplemented"); 280 "Exiting processes with non-waiting threads is currently unimplemented");
323 281
324 thread->Stop(); 282 thread->Stop();
@@ -406,21 +364,18 @@ void Process::LoadModule(CodeSet code_set, VAddr base_addr) {
406 ReprotectSegment(code_set.DataSegment(), Memory::MemoryPermission::ReadAndWrite); 364 ReprotectSegment(code_set.DataSegment(), Memory::MemoryPermission::ReadAndWrite);
407} 365}
408 366
367bool Process::IsSignaled() const {
368 ASSERT(kernel.GlobalSchedulerContext().IsLocked());
369 return is_signaled;
370}
371
409Process::Process(Core::System& system) 372Process::Process(Core::System& system)
410 : SynchronizationObject{system.Kernel()}, page_table{std::make_unique<Memory::PageTable>( 373 : KSynchronizationObject{system.Kernel()},
411 system)}, 374 page_table{std::make_unique<Memory::PageTable>(system)}, handle_table{system.Kernel()},
412 handle_table{system.Kernel()}, address_arbiter{system}, mutex{system}, system{system} {} 375 address_arbiter{system}, condition_var{system}, system{system} {}
413 376
414Process::~Process() = default; 377Process::~Process() = default;
415 378
416void Process::Acquire(Thread* thread) {
417 ASSERT_MSG(!ShouldWait(thread), "Object unavailable!");
418}
419
420bool Process::ShouldWait(const Thread* thread) const {
421 return !is_signaled;
422}
423
424void Process::ChangeStatus(ProcessStatus new_status) { 379void Process::ChangeStatus(ProcessStatus new_status) {
425 if (status == new_status) { 380 if (status == new_status) {
426 return; 381 return;
@@ -428,7 +383,7 @@ void Process::ChangeStatus(ProcessStatus new_status) {
428 383
429 status = new_status; 384 status = new_status;
430 is_signaled = true; 385 is_signaled = true;
431 Signal(); 386 NotifyAvailable();
432} 387}
433 388
434ResultCode Process::AllocateMainThreadStack(std::size_t stack_size) { 389ResultCode Process::AllocateMainThreadStack(std::size_t stack_size) {
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index e412e58aa..564e1f27d 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -11,11 +11,11 @@
11#include <unordered_map> 11#include <unordered_map>
12#include <vector> 12#include <vector>
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "core/hle/kernel/address_arbiter.h"
15#include "core/hle/kernel/handle_table.h" 14#include "core/hle/kernel/handle_table.h"
16#include "core/hle/kernel/mutex.h" 15#include "core/hle/kernel/k_address_arbiter.h"
16#include "core/hle/kernel/k_condition_variable.h"
17#include "core/hle/kernel/k_synchronization_object.h"
17#include "core/hle/kernel/process_capability.h" 18#include "core/hle/kernel/process_capability.h"
18#include "core/hle/kernel/synchronization_object.h"
19#include "core/hle/result.h" 19#include "core/hle/result.h"
20 20
21namespace Core { 21namespace Core {
@@ -63,7 +63,7 @@ enum class ProcessStatus {
63 DebugBreak, 63 DebugBreak,
64}; 64};
65 65
66class Process final : public SynchronizationObject { 66class Process final : public KSynchronizationObject {
67public: 67public:
68 explicit Process(Core::System& system); 68 explicit Process(Core::System& system);
69 ~Process() override; 69 ~Process() override;
@@ -123,24 +123,30 @@ public:
123 return handle_table; 123 return handle_table;
124 } 124 }
125 125
126 /// Gets a reference to the process' address arbiter. 126 ResultCode SignalToAddress(VAddr address) {
127 AddressArbiter& GetAddressArbiter() { 127 return condition_var.SignalToAddress(address);
128 return address_arbiter;
129 } 128 }
130 129
131 /// Gets a const reference to the process' address arbiter. 130 ResultCode WaitForAddress(Handle handle, VAddr address, u32 tag) {
132 const AddressArbiter& GetAddressArbiter() const { 131 return condition_var.WaitForAddress(handle, address, tag);
133 return address_arbiter;
134 } 132 }
135 133
136 /// Gets a reference to the process' mutex lock. 134 void SignalConditionVariable(u64 cv_key, int32_t count) {
137 Mutex& GetMutex() { 135 return condition_var.Signal(cv_key, count);
138 return mutex;
139 } 136 }
140 137
141 /// Gets a const reference to the process' mutex lock 138 ResultCode WaitConditionVariable(VAddr address, u64 cv_key, u32 tag, s64 ns) {
142 const Mutex& GetMutex() const { 139 return condition_var.Wait(address, cv_key, tag, ns);
143 return mutex; 140 }
141
142 ResultCode SignalAddressArbiter(VAddr address, Svc::SignalType signal_type, s32 value,
143 s32 count) {
144 return address_arbiter.SignalToAddress(address, signal_type, value, count);
145 }
146
147 ResultCode WaitAddressArbiter(VAddr address, Svc::ArbitrationType arb_type, s32 value,
148 s64 timeout) {
149 return address_arbiter.WaitForAddress(address, arb_type, value, timeout);
144 } 150 }
145 151
146 /// Gets the address to the process' dedicated TLS region. 152 /// Gets the address to the process' dedicated TLS region.
@@ -250,15 +256,6 @@ public:
250 return thread_list; 256 return thread_list;
251 } 257 }
252 258
253 /// Insert a thread into the condition variable wait container
254 void InsertConditionVariableThread(std::shared_ptr<Thread> thread);
255
256 /// Remove a thread from the condition variable wait container
257 void RemoveConditionVariableThread(std::shared_ptr<Thread> thread);
258
259 /// Obtain all condition variable threads waiting for some address
260 std::vector<std::shared_ptr<Thread>> GetConditionVariableThreads(VAddr cond_var_addr);
261
262 /// Registers a thread as being created under this process, 259 /// Registers a thread as being created under this process,
263 /// adding it to this process' thread list. 260 /// adding it to this process' thread list.
264 void RegisterThread(const Thread* thread); 261 void RegisterThread(const Thread* thread);
@@ -304,6 +301,8 @@ public:
304 301
305 void LoadModule(CodeSet code_set, VAddr base_addr); 302 void LoadModule(CodeSet code_set, VAddr base_addr);
306 303
304 bool IsSignaled() const override;
305
307 /////////////////////////////////////////////////////////////////////////////////////////////// 306 ///////////////////////////////////////////////////////////////////////////////////////////////
308 // Thread-local storage management 307 // Thread-local storage management
309 308
@@ -314,12 +313,6 @@ public:
314 void FreeTLSRegion(VAddr tls_address); 313 void FreeTLSRegion(VAddr tls_address);
315 314
316private: 315private:
317 /// Checks if the specified thread should wait until this process is available.
318 bool ShouldWait(const Thread* thread) const override;
319
320 /// Acquires/locks this process for the specified thread if it's available.
321 void Acquire(Thread* thread) override;
322
323 /// Changes the process status. If the status is different 316 /// Changes the process status. If the status is different
324 /// from the current process status, then this will trigger 317 /// from the current process status, then this will trigger
325 /// a process signal. 318 /// a process signal.
@@ -373,12 +366,12 @@ private:
373 HandleTable handle_table; 366 HandleTable handle_table;
374 367
375 /// Per-process address arbiter. 368 /// Per-process address arbiter.
376 AddressArbiter address_arbiter; 369 KAddressArbiter address_arbiter;
377 370
378 /// The per-process mutex lock instance used for handling various 371 /// The per-process mutex lock instance used for handling various
379 /// forms of services, such as lock arbitration, and condition 372 /// forms of services, such as lock arbitration, and condition
380 /// variable related facilities. 373 /// variable related facilities.
381 Mutex mutex; 374 KConditionVariable condition_var;
382 375
383 /// Address indicating the location of the process' dedicated TLS region. 376 /// Address indicating the location of the process' dedicated TLS region.
384 VAddr tls_region_address = 0; 377 VAddr tls_region_address = 0;
@@ -389,9 +382,6 @@ private:
389 /// List of threads that are running with this process as their owner. 382 /// List of threads that are running with this process as their owner.
390 std::list<const Thread*> thread_list; 383 std::list<const Thread*> thread_list;
391 384
392 /// List of threads waiting for a condition variable
393 std::unordered_map<VAddr, std::list<std::shared_ptr<Thread>>> cond_var_threads;
394
395 /// Address of the top of the main thread's stack 385 /// Address of the top of the main thread's stack
396 VAddr main_thread_stack_top{}; 386 VAddr main_thread_stack_top{};
397 387
@@ -410,6 +400,8 @@ private:
410 /// Schedule count of this process 400 /// Schedule count of this process
411 s64 schedule_count{}; 401 s64 schedule_count{};
412 402
403 bool is_signaled{};
404
413 /// System context 405 /// System context
414 Core::System& system; 406 Core::System& system;
415}; 407};
diff --git a/src/core/hle/kernel/process_capability.cpp b/src/core/hle/kernel/process_capability.cpp
index 0f128c586..0566311b6 100644
--- a/src/core/hle/kernel/process_capability.cpp
+++ b/src/core/hle/kernel/process_capability.cpp
@@ -2,6 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <bit>
6
5#include "common/bit_util.h" 7#include "common/bit_util.h"
6#include "common/logging/log.h" 8#include "common/logging/log.h"
7#include "core/hle/kernel/errors.h" 9#include "core/hle/kernel/errors.h"
@@ -60,7 +62,7 @@ constexpr CapabilityType GetCapabilityType(u32 value) {
60 62
61u32 GetFlagBitOffset(CapabilityType type) { 63u32 GetFlagBitOffset(CapabilityType type) {
62 const auto value = static_cast<u32>(type); 64 const auto value = static_cast<u32>(type);
63 return static_cast<u32>(Common::BitSize<u32>() - Common::CountLeadingZeroes32(value)); 65 return static_cast<u32>(Common::BitSize<u32>() - static_cast<u32>(std::countl_zero(value)));
64} 66}
65 67
66} // Anonymous namespace 68} // Anonymous namespace
diff --git a/src/core/hle/kernel/readable_event.cpp b/src/core/hle/kernel/readable_event.cpp
index cea262ce0..99ed0857e 100644
--- a/src/core/hle/kernel/readable_event.cpp
+++ b/src/core/hle/kernel/readable_event.cpp
@@ -14,24 +14,22 @@
14 14
15namespace Kernel { 15namespace Kernel {
16 16
17ReadableEvent::ReadableEvent(KernelCore& kernel) : SynchronizationObject{kernel} {} 17ReadableEvent::ReadableEvent(KernelCore& kernel) : KSynchronizationObject{kernel} {}
18ReadableEvent::~ReadableEvent() = default; 18ReadableEvent::~ReadableEvent() = default;
19 19
20bool ReadableEvent::ShouldWait(const Thread* thread) const {
21 return !is_signaled;
22}
23
24void ReadableEvent::Acquire(Thread* thread) {
25 ASSERT_MSG(IsSignaled(), "object unavailable!");
26}
27
28void ReadableEvent::Signal() { 20void ReadableEvent::Signal() {
29 if (is_signaled) { 21 if (is_signaled) {
30 return; 22 return;
31 } 23 }
32 24
33 is_signaled = true; 25 is_signaled = true;
34 SynchronizationObject::Signal(); 26 NotifyAvailable();
27}
28
29bool ReadableEvent::IsSignaled() const {
30 ASSERT(kernel.GlobalSchedulerContext().IsLocked());
31
32 return is_signaled;
35} 33}
36 34
37void ReadableEvent::Clear() { 35void ReadableEvent::Clear() {
diff --git a/src/core/hle/kernel/readable_event.h b/src/core/hle/kernel/readable_event.h
index 3264dd066..34e477274 100644
--- a/src/core/hle/kernel/readable_event.h
+++ b/src/core/hle/kernel/readable_event.h
@@ -4,8 +4,8 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "core/hle/kernel/k_synchronization_object.h"
7#include "core/hle/kernel/object.h" 8#include "core/hle/kernel/object.h"
8#include "core/hle/kernel/synchronization_object.h"
9 9
10union ResultCode; 10union ResultCode;
11 11
@@ -14,7 +14,7 @@ namespace Kernel {
14class KernelCore; 14class KernelCore;
15class WritableEvent; 15class WritableEvent;
16 16
17class ReadableEvent final : public SynchronizationObject { 17class ReadableEvent final : public KSynchronizationObject {
18 friend class WritableEvent; 18 friend class WritableEvent;
19 19
20public: 20public:
@@ -32,9 +32,6 @@ public:
32 return HANDLE_TYPE; 32 return HANDLE_TYPE;
33 } 33 }
34 34
35 bool ShouldWait(const Thread* thread) const override;
36 void Acquire(Thread* thread) override;
37
38 /// Unconditionally clears the readable event's state. 35 /// Unconditionally clears the readable event's state.
39 void Clear(); 36 void Clear();
40 37
@@ -46,11 +43,14 @@ public:
46 /// then ERR_INVALID_STATE will be returned. 43 /// then ERR_INVALID_STATE will be returned.
47 ResultCode Reset(); 44 ResultCode Reset();
48 45
49 void Signal() override; 46 void Signal();
47
48 bool IsSignaled() const override;
50 49
51private: 50private:
52 explicit ReadableEvent(KernelCore& kernel); 51 explicit ReadableEvent(KernelCore& kernel);
53 52
53 bool is_signaled{};
54 std::string name; ///< Name of event (optional) 54 std::string name; ///< Name of event (optional)
55}; 55};
56 56
diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp
index a549ae9d7..82857f93b 100644
--- a/src/core/hle/kernel/server_port.cpp
+++ b/src/core/hle/kernel/server_port.cpp
@@ -13,7 +13,7 @@
13 13
14namespace Kernel { 14namespace Kernel {
15 15
16ServerPort::ServerPort(KernelCore& kernel) : SynchronizationObject{kernel} {} 16ServerPort::ServerPort(KernelCore& kernel) : KSynchronizationObject{kernel} {}
17ServerPort::~ServerPort() = default; 17ServerPort::~ServerPort() = default;
18 18
19ResultVal<std::shared_ptr<ServerSession>> ServerPort::Accept() { 19ResultVal<std::shared_ptr<ServerSession>> ServerPort::Accept() {
@@ -28,15 +28,9 @@ ResultVal<std::shared_ptr<ServerSession>> ServerPort::Accept() {
28 28
29void ServerPort::AppendPendingSession(std::shared_ptr<ServerSession> pending_session) { 29void ServerPort::AppendPendingSession(std::shared_ptr<ServerSession> pending_session) {
30 pending_sessions.push_back(std::move(pending_session)); 30 pending_sessions.push_back(std::move(pending_session));
31} 31 if (pending_sessions.size() == 1) {
32 32 NotifyAvailable();
33bool ServerPort::ShouldWait(const Thread* thread) const { 33 }
34 // If there are no pending sessions, we wait until a new one is added.
35 return pending_sessions.empty();
36}
37
38void ServerPort::Acquire(Thread* thread) {
39 ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
40} 34}
41 35
42bool ServerPort::IsSignaled() const { 36bool ServerPort::IsSignaled() const {
diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h
index 41b191b86..6470df993 100644
--- a/src/core/hle/kernel/server_port.h
+++ b/src/core/hle/kernel/server_port.h
@@ -9,8 +9,8 @@
9#include <utility> 9#include <utility>
10#include <vector> 10#include <vector>
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "core/hle/kernel/k_synchronization_object.h"
12#include "core/hle/kernel/object.h" 13#include "core/hle/kernel/object.h"
13#include "core/hle/kernel/synchronization_object.h"
14#include "core/hle/result.h" 14#include "core/hle/result.h"
15 15
16namespace Kernel { 16namespace Kernel {
@@ -20,7 +20,7 @@ class KernelCore;
20class ServerSession; 20class ServerSession;
21class SessionRequestHandler; 21class SessionRequestHandler;
22 22
23class ServerPort final : public SynchronizationObject { 23class ServerPort final : public KSynchronizationObject {
24public: 24public:
25 explicit ServerPort(KernelCore& kernel); 25 explicit ServerPort(KernelCore& kernel);
26 ~ServerPort() override; 26 ~ServerPort() override;
@@ -79,9 +79,6 @@ public:
79 /// waiting to be accepted by this port. 79 /// waiting to be accepted by this port.
80 void AppendPendingSession(std::shared_ptr<ServerSession> pending_session); 80 void AppendPendingSession(std::shared_ptr<ServerSession> pending_session);
81 81
82 bool ShouldWait(const Thread* thread) const override;
83 void Acquire(Thread* thread) override;
84
85 bool IsSignaled() const override; 82 bool IsSignaled() const override;
86 83
87private: 84private:
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index b40fe3916..4f2bb7822 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -24,7 +24,7 @@
24 24
25namespace Kernel { 25namespace Kernel {
26 26
27ServerSession::ServerSession(KernelCore& kernel) : SynchronizationObject{kernel} {} 27ServerSession::ServerSession(KernelCore& kernel) : KSynchronizationObject{kernel} {}
28 28
29ServerSession::~ServerSession() { 29ServerSession::~ServerSession() {
30 kernel.ReleaseServiceThread(service_thread); 30 kernel.ReleaseServiceThread(service_thread);
@@ -42,16 +42,6 @@ ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelCore& kern
42 return MakeResult(std::move(session)); 42 return MakeResult(std::move(session));
43} 43}
44 44
45bool ServerSession::ShouldWait(const Thread* thread) const {
46 // Closed sessions should never wait, an error will be returned from svcReplyAndReceive.
47 if (!parent->Client()) {
48 return false;
49 }
50
51 // Wait if we have no pending requests, or if we're currently handling a request.
52 return pending_requesting_threads.empty() || currently_handling != nullptr;
53}
54
55bool ServerSession::IsSignaled() const { 45bool ServerSession::IsSignaled() const {
56 // Closed sessions should never wait, an error will be returned from svcReplyAndReceive. 46 // Closed sessions should never wait, an error will be returned from svcReplyAndReceive.
57 if (!parent->Client()) { 47 if (!parent->Client()) {
@@ -62,15 +52,6 @@ bool ServerSession::IsSignaled() const {
62 return !pending_requesting_threads.empty() && currently_handling == nullptr; 52 return !pending_requesting_threads.empty() && currently_handling == nullptr;
63} 53}
64 54
65void ServerSession::Acquire(Thread* thread) {
66 ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
67 // We are now handling a request, pop it from the stack.
68 // TODO(Subv): What happens if the client endpoint is closed before any requests are made?
69 ASSERT(!pending_requesting_threads.empty());
70 currently_handling = pending_requesting_threads.back();
71 pending_requesting_threads.pop_back();
72}
73
74void ServerSession::ClientDisconnected() { 55void ServerSession::ClientDisconnected() {
75 // We keep a shared pointer to the hle handler to keep it alive throughout 56 // We keep a shared pointer to the hle handler to keep it alive throughout
76 // the call to ClientDisconnected, as ClientDisconnected invalidates the 57 // the call to ClientDisconnected, as ClientDisconnected invalidates the
@@ -172,7 +153,7 @@ ResultCode ServerSession::CompleteSyncRequest(HLERequestContext& context) {
172 { 153 {
173 KScopedSchedulerLock lock(kernel); 154 KScopedSchedulerLock lock(kernel);
174 if (!context.IsThreadWaiting()) { 155 if (!context.IsThreadWaiting()) {
175 context.GetThread().ResumeFromWait(); 156 context.GetThread().Wakeup();
176 context.GetThread().SetSynchronizationResults(nullptr, result); 157 context.GetThread().SetSynchronizationResults(nullptr, result);
177 } 158 }
178 } 159 }
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h
index e8d1d99ea..9155cf7f5 100644
--- a/src/core/hle/kernel/server_session.h
+++ b/src/core/hle/kernel/server_session.h
@@ -10,8 +10,8 @@
10#include <vector> 10#include <vector>
11 11
12#include "common/threadsafe_queue.h" 12#include "common/threadsafe_queue.h"
13#include "core/hle/kernel/k_synchronization_object.h"
13#include "core/hle/kernel/service_thread.h" 14#include "core/hle/kernel/service_thread.h"
14#include "core/hle/kernel/synchronization_object.h"
15#include "core/hle/result.h" 15#include "core/hle/result.h"
16 16
17namespace Core::Memory { 17namespace Core::Memory {
@@ -43,7 +43,7 @@ class Thread;
43 * After the server replies to the request, the response is marshalled back to the caller's 43 * After the server replies to the request, the response is marshalled back to the caller's
44 * TLS buffer and control is transferred back to it. 44 * TLS buffer and control is transferred back to it.
45 */ 45 */
46class ServerSession final : public SynchronizationObject { 46class ServerSession final : public KSynchronizationObject {
47 friend class ServiceThread; 47 friend class ServiceThread;
48 48
49public: 49public:
@@ -77,8 +77,6 @@ public:
77 return parent.get(); 77 return parent.get();
78 } 78 }
79 79
80 bool IsSignaled() const override;
81
82 /** 80 /**
83 * Sets the HLE handler for the session. This handler will be called to service IPC requests 81 * Sets the HLE handler for the session. This handler will be called to service IPC requests
84 * instead of the regular IPC machinery. (The regular IPC machinery is currently not 82 * instead of the regular IPC machinery. (The regular IPC machinery is currently not
@@ -100,10 +98,6 @@ public:
100 ResultCode HandleSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory, 98 ResultCode HandleSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory,
101 Core::Timing::CoreTiming& core_timing); 99 Core::Timing::CoreTiming& core_timing);
102 100
103 bool ShouldWait(const Thread* thread) const override;
104
105 void Acquire(Thread* thread) override;
106
107 /// Called when a client disconnection occurs. 101 /// Called when a client disconnection occurs.
108 void ClientDisconnected(); 102 void ClientDisconnected();
109 103
@@ -130,6 +124,8 @@ public:
130 convert_to_domain = true; 124 convert_to_domain = true;
131 } 125 }
132 126
127 bool IsSignaled() const override;
128
133private: 129private:
134 /// Queues a sync request from the emulated application. 130 /// Queues a sync request from the emulated application.
135 ResultCode QueueSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory); 131 ResultCode QueueSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory);
diff --git a/src/core/hle/kernel/session.cpp b/src/core/hle/kernel/session.cpp
index e4dd53e24..75304b961 100644
--- a/src/core/hle/kernel/session.cpp
+++ b/src/core/hle/kernel/session.cpp
@@ -9,7 +9,7 @@
9 9
10namespace Kernel { 10namespace Kernel {
11 11
12Session::Session(KernelCore& kernel) : SynchronizationObject{kernel} {} 12Session::Session(KernelCore& kernel) : KSynchronizationObject{kernel} {}
13Session::~Session() = default; 13Session::~Session() = default;
14 14
15Session::SessionPair Session::Create(KernelCore& kernel, std::string name) { 15Session::SessionPair Session::Create(KernelCore& kernel, std::string name) {
@@ -24,18 +24,9 @@ Session::SessionPair Session::Create(KernelCore& kernel, std::string name) {
24 return std::make_pair(std::move(client_session), std::move(server_session)); 24 return std::make_pair(std::move(client_session), std::move(server_session));
25} 25}
26 26
27bool Session::ShouldWait(const Thread* thread) const {
28 UNIMPLEMENTED();
29 return {};
30}
31
32bool Session::IsSignaled() const { 27bool Session::IsSignaled() const {
33 UNIMPLEMENTED(); 28 UNIMPLEMENTED();
34 return true; 29 return true;
35} 30}
36 31
37void Session::Acquire(Thread* thread) {
38 UNIMPLEMENTED();
39}
40
41} // namespace Kernel 32} // namespace Kernel
diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h
index 7cd9c0d77..f6dd2c1d2 100644
--- a/src/core/hle/kernel/session.h
+++ b/src/core/hle/kernel/session.h
@@ -8,7 +8,7 @@
8#include <string> 8#include <string>
9#include <utility> 9#include <utility>
10 10
11#include "core/hle/kernel/synchronization_object.h" 11#include "core/hle/kernel/k_synchronization_object.h"
12 12
13namespace Kernel { 13namespace Kernel {
14 14
@@ -19,7 +19,7 @@ class ServerSession;
19 * Parent structure to link the client and server endpoints of a session with their associated 19 * Parent structure to link the client and server endpoints of a session with their associated
20 * client port. 20 * client port.
21 */ 21 */
22class Session final : public SynchronizationObject { 22class Session final : public KSynchronizationObject {
23public: 23public:
24 explicit Session(KernelCore& kernel); 24 explicit Session(KernelCore& kernel);
25 ~Session() override; 25 ~Session() override;
@@ -37,12 +37,8 @@ public:
37 return HANDLE_TYPE; 37 return HANDLE_TYPE;
38 } 38 }
39 39
40 bool ShouldWait(const Thread* thread) const override;
41
42 bool IsSignaled() const override; 40 bool IsSignaled() const override;
43 41
44 void Acquire(Thread* thread) override;
45
46 std::shared_ptr<ClientSession> Client() { 42 std::shared_ptr<ClientSession> Client() {
47 if (auto result{client.lock()}) { 43 if (auto result{client.lock()}) {
48 return result; 44 return result;
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index de3ed25da..cc8b661af 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -10,6 +10,7 @@
10 10
11#include "common/alignment.h" 11#include "common/alignment.h"
12#include "common/assert.h" 12#include "common/assert.h"
13#include "common/common_funcs.h"
13#include "common/fiber.h" 14#include "common/fiber.h"
14#include "common/logging/log.h" 15#include "common/logging/log.h"
15#include "common/microprofile.h" 16#include "common/microprofile.h"
@@ -19,26 +20,28 @@
19#include "core/core_timing.h" 20#include "core/core_timing.h"
20#include "core/core_timing_util.h" 21#include "core/core_timing_util.h"
21#include "core/cpu_manager.h" 22#include "core/cpu_manager.h"
22#include "core/hle/kernel/address_arbiter.h"
23#include "core/hle/kernel/client_port.h" 23#include "core/hle/kernel/client_port.h"
24#include "core/hle/kernel/client_session.h" 24#include "core/hle/kernel/client_session.h"
25#include "core/hle/kernel/errors.h" 25#include "core/hle/kernel/errors.h"
26#include "core/hle/kernel/handle_table.h" 26#include "core/hle/kernel/handle_table.h"
27#include "core/hle/kernel/k_address_arbiter.h"
28#include "core/hle/kernel/k_condition_variable.h"
27#include "core/hle/kernel/k_scheduler.h" 29#include "core/hle/kernel/k_scheduler.h"
28#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" 30#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
31#include "core/hle/kernel/k_synchronization_object.h"
29#include "core/hle/kernel/kernel.h" 32#include "core/hle/kernel/kernel.h"
30#include "core/hle/kernel/memory/memory_block.h" 33#include "core/hle/kernel/memory/memory_block.h"
34#include "core/hle/kernel/memory/memory_layout.h"
31#include "core/hle/kernel/memory/page_table.h" 35#include "core/hle/kernel/memory/page_table.h"
32#include "core/hle/kernel/mutex.h"
33#include "core/hle/kernel/physical_core.h" 36#include "core/hle/kernel/physical_core.h"
34#include "core/hle/kernel/process.h" 37#include "core/hle/kernel/process.h"
35#include "core/hle/kernel/readable_event.h" 38#include "core/hle/kernel/readable_event.h"
36#include "core/hle/kernel/resource_limit.h" 39#include "core/hle/kernel/resource_limit.h"
37#include "core/hle/kernel/shared_memory.h" 40#include "core/hle/kernel/shared_memory.h"
38#include "core/hle/kernel/svc.h" 41#include "core/hle/kernel/svc.h"
42#include "core/hle/kernel/svc_results.h"
39#include "core/hle/kernel/svc_types.h" 43#include "core/hle/kernel/svc_types.h"
40#include "core/hle/kernel/svc_wrap.h" 44#include "core/hle/kernel/svc_wrap.h"
41#include "core/hle/kernel/synchronization.h"
42#include "core/hle/kernel/thread.h" 45#include "core/hle/kernel/thread.h"
43#include "core/hle/kernel/time_manager.h" 46#include "core/hle/kernel/time_manager.h"
44#include "core/hle/kernel/transfer_memory.h" 47#include "core/hle/kernel/transfer_memory.h"
@@ -343,27 +346,11 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
343 auto thread = kernel.CurrentScheduler()->GetCurrentThread(); 346 auto thread = kernel.CurrentScheduler()->GetCurrentThread();
344 { 347 {
345 KScopedSchedulerLock lock(kernel); 348 KScopedSchedulerLock lock(kernel);
346 thread->InvalidateHLECallback(); 349 thread->SetState(ThreadState::Waiting);
347 thread->SetStatus(ThreadStatus::WaitIPC); 350 thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
348 session->SendSyncRequest(SharedFrom(thread), system.Memory(), system.CoreTiming()); 351 session->SendSyncRequest(SharedFrom(thread), system.Memory(), system.CoreTiming());
349 } 352 }
350 353
351 if (thread->HasHLECallback()) {
352 Handle event_handle = thread->GetHLETimeEvent();
353 if (event_handle != InvalidHandle) {
354 auto& time_manager = kernel.TimeManager();
355 time_manager.UnscheduleTimeEvent(event_handle);
356 }
357
358 {
359 KScopedSchedulerLock lock(kernel);
360 auto* sync_object = thread->GetHLESyncObject();
361 sync_object->RemoveWaitingThread(SharedFrom(thread));
362 }
363
364 thread->InvokeHLECallback(SharedFrom(thread));
365 }
366
367 return thread->GetSignalingResult(); 354 return thread->GetSignalingResult();
368} 355}
369 356
@@ -436,7 +423,7 @@ static ResultCode GetProcessId32(Core::System& system, u32* process_id_low, u32*
436} 423}
437 424
438/// Wait for the given handles to synchronize, timeout after the specified nanoseconds 425/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
439static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr handles_address, 426static ResultCode WaitSynchronization(Core::System& system, s32* index, VAddr handles_address,
440 u64 handle_count, s64 nano_seconds) { 427 u64 handle_count, s64 nano_seconds) {
441 LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, handle_count={}, nano_seconds={}", 428 LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, handle_count={}, nano_seconds={}",
442 handles_address, handle_count, nano_seconds); 429 handles_address, handle_count, nano_seconds);
@@ -458,28 +445,26 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr
458 } 445 }
459 446
460 auto& kernel = system.Kernel(); 447 auto& kernel = system.Kernel();
461 Thread::ThreadSynchronizationObjects objects(handle_count); 448 std::vector<KSynchronizationObject*> objects(handle_count);
462 const auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); 449 const auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
463 450
464 for (u64 i = 0; i < handle_count; ++i) { 451 for (u64 i = 0; i < handle_count; ++i) {
465 const Handle handle = memory.Read32(handles_address + i * sizeof(Handle)); 452 const Handle handle = memory.Read32(handles_address + i * sizeof(Handle));
466 const auto object = handle_table.Get<SynchronizationObject>(handle); 453 const auto object = handle_table.Get<KSynchronizationObject>(handle);
467 454
468 if (object == nullptr) { 455 if (object == nullptr) {
469 LOG_ERROR(Kernel_SVC, "Object is a nullptr"); 456 LOG_ERROR(Kernel_SVC, "Object is a nullptr");
470 return ERR_INVALID_HANDLE; 457 return ERR_INVALID_HANDLE;
471 } 458 }
472 459
473 objects[i] = object; 460 objects[i] = object.get();
474 } 461 }
475 auto& synchronization = kernel.Synchronization(); 462 return KSynchronizationObject::Wait(kernel, index, objects.data(),
476 const auto [result, handle_result] = synchronization.WaitFor(objects, nano_seconds); 463 static_cast<s32>(objects.size()), nano_seconds);
477 *index = handle_result;
478 return result;
479} 464}
480 465
481static ResultCode WaitSynchronization32(Core::System& system, u32 timeout_low, u32 handles_address, 466static ResultCode WaitSynchronization32(Core::System& system, u32 timeout_low, u32 handles_address,
482 s32 handle_count, u32 timeout_high, Handle* index) { 467 s32 handle_count, u32 timeout_high, s32* index) {
483 const s64 nano_seconds{(static_cast<s64>(timeout_high) << 32) | static_cast<s64>(timeout_low)}; 468 const s64 nano_seconds{(static_cast<s64>(timeout_high) << 32) | static_cast<s64>(timeout_low)};
484 return WaitSynchronization(system, index, handles_address, handle_count, nano_seconds); 469 return WaitSynchronization(system, index, handles_address, handle_count, nano_seconds);
485} 470}
@@ -504,56 +489,37 @@ static ResultCode CancelSynchronization32(Core::System& system, Handle thread_ha
504 return CancelSynchronization(system, thread_handle); 489 return CancelSynchronization(system, thread_handle);
505} 490}
506 491
507/// Attempts to locks a mutex, creating it if it does not already exist 492/// Attempts to locks a mutex
508static ResultCode ArbitrateLock(Core::System& system, Handle holding_thread_handle, 493static ResultCode ArbitrateLock(Core::System& system, Handle thread_handle, VAddr address,
509 VAddr mutex_addr, Handle requesting_thread_handle) { 494 u32 tag) {
510 LOG_TRACE(Kernel_SVC, 495 LOG_TRACE(Kernel_SVC, "called thread_handle=0x{:08X}, address=0x{:X}, tag=0x{:08X}",
511 "called holding_thread_handle=0x{:08X}, mutex_addr=0x{:X}, " 496 thread_handle, address, tag);
512 "requesting_current_thread_handle=0x{:08X}",
513 holding_thread_handle, mutex_addr, requesting_thread_handle);
514
515 if (Core::Memory::IsKernelVirtualAddress(mutex_addr)) {
516 LOG_ERROR(Kernel_SVC, "Mutex Address is a kernel virtual address, mutex_addr={:016X}",
517 mutex_addr);
518 return ERR_INVALID_ADDRESS_STATE;
519 }
520 497
521 if (!Common::IsWordAligned(mutex_addr)) { 498 // Validate the input address.
522 LOG_ERROR(Kernel_SVC, "Mutex Address is not word aligned, mutex_addr={:016X}", mutex_addr); 499 R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory);
523 return ERR_INVALID_ADDRESS; 500 R_UNLESS(Common::IsAligned(address, sizeof(u32)), Svc::ResultInvalidAddress);
524 }
525 501
526 auto* const current_process = system.Kernel().CurrentProcess(); 502 return system.Kernel().CurrentProcess()->WaitForAddress(thread_handle, address, tag);
527 return current_process->GetMutex().TryAcquire(mutex_addr, holding_thread_handle,
528 requesting_thread_handle);
529} 503}
530 504
531static ResultCode ArbitrateLock32(Core::System& system, Handle holding_thread_handle, 505static ResultCode ArbitrateLock32(Core::System& system, Handle thread_handle, u32 address,
532 u32 mutex_addr, Handle requesting_thread_handle) { 506 u32 tag) {
533 return ArbitrateLock(system, holding_thread_handle, mutex_addr, requesting_thread_handle); 507 return ArbitrateLock(system, thread_handle, address, tag);
534} 508}
535 509
536/// Unlock a mutex 510/// Unlock a mutex
537static ResultCode ArbitrateUnlock(Core::System& system, VAddr mutex_addr) { 511static ResultCode ArbitrateUnlock(Core::System& system, VAddr address) {
538 LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr); 512 LOG_TRACE(Kernel_SVC, "called address=0x{:X}", address);
539
540 if (Core::Memory::IsKernelVirtualAddress(mutex_addr)) {
541 LOG_ERROR(Kernel_SVC, "Mutex Address is a kernel virtual address, mutex_addr={:016X}",
542 mutex_addr);
543 return ERR_INVALID_ADDRESS_STATE;
544 }
545 513
546 if (!Common::IsWordAligned(mutex_addr)) { 514 // Validate the input address.
547 LOG_ERROR(Kernel_SVC, "Mutex Address is not word aligned, mutex_addr={:016X}", mutex_addr); 515 R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory);
548 return ERR_INVALID_ADDRESS; 516 R_UNLESS(Common::IsAligned(address, sizeof(u32)), Svc::ResultInvalidAddress);
549 }
550 517
551 auto* const current_process = system.Kernel().CurrentProcess(); 518 return system.Kernel().CurrentProcess()->SignalToAddress(address);
552 return current_process->GetMutex().Release(mutex_addr);
553} 519}
554 520
555static ResultCode ArbitrateUnlock32(Core::System& system, u32 mutex_addr) { 521static ResultCode ArbitrateUnlock32(Core::System& system, u32 address) {
556 return ArbitrateUnlock(system, mutex_addr); 522 return ArbitrateUnlock(system, address);
557} 523}
558 524
559enum class BreakType : u32 { 525enum class BreakType : u32 {
@@ -1180,7 +1146,7 @@ static ResultCode SetThreadPriority(Core::System& system, Handle handle, u32 pri
1180 return ERR_INVALID_HANDLE; 1146 return ERR_INVALID_HANDLE;
1181 } 1147 }
1182 1148
1183 thread->SetPriority(priority); 1149 thread->SetBasePriority(priority);
1184 1150
1185 return RESULT_SUCCESS; 1151 return RESULT_SUCCESS;
1186} 1152}
@@ -1559,7 +1525,7 @@ static ResultCode StartThread(Core::System& system, Handle thread_handle) {
1559 return ERR_INVALID_HANDLE; 1525 return ERR_INVALID_HANDLE;
1560 } 1526 }
1561 1527
1562 ASSERT(thread->GetStatus() == ThreadStatus::Dormant); 1528 ASSERT(thread->GetState() == ThreadState::Initialized);
1563 1529
1564 return thread->Start(); 1530 return thread->Start();
1565} 1531}
@@ -1620,224 +1586,135 @@ static void SleepThread32(Core::System& system, u32 nanoseconds_low, u32 nanosec
1620} 1586}
1621 1587
1622/// Wait process wide key atomic 1588/// Wait process wide key atomic
1623static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_addr, 1589static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr address, VAddr cv_key,
1624 VAddr condition_variable_addr, Handle thread_handle, 1590 u32 tag, s64 timeout_ns) {
1625 s64 nano_seconds) { 1591 LOG_TRACE(Kernel_SVC, "called address={:X}, cv_key={:X}, tag=0x{:08X}, timeout_ns={}", address,
1626 LOG_TRACE( 1592 cv_key, tag, timeout_ns);
1627 Kernel_SVC, 1593
1628 "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}", 1594 // Validate input.
1629 mutex_addr, condition_variable_addr, thread_handle, nano_seconds); 1595 R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory);
1630 1596 R_UNLESS(Common::IsAligned(address, sizeof(int32_t)), Svc::ResultInvalidAddress);
1631 if (Core::Memory::IsKernelVirtualAddress(mutex_addr)) { 1597
1632 LOG_ERROR( 1598 // Convert timeout from nanoseconds to ticks.
1633 Kernel_SVC, 1599 s64 timeout{};
1634 "Given mutex address must not be within the kernel address space. address=0x{:016X}", 1600 if (timeout_ns > 0) {
1635 mutex_addr); 1601 const s64 offset_tick(timeout_ns);
1636 return ERR_INVALID_ADDRESS_STATE; 1602 if (offset_tick > 0) {
1637 } 1603 timeout = offset_tick + 2;
1638 1604 if (timeout <= 0) {
1639 if (!Common::IsWordAligned(mutex_addr)) { 1605 timeout = std::numeric_limits<s64>::max();
1640 LOG_ERROR(Kernel_SVC, "Given mutex address must be word-aligned. address=0x{:016X}", 1606 }
1641 mutex_addr); 1607 } else {
1642 return ERR_INVALID_ADDRESS; 1608 timeout = std::numeric_limits<s64>::max();
1643 }
1644
1645 ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4));
1646 auto& kernel = system.Kernel();
1647 Handle event_handle;
1648 Thread* current_thread = kernel.CurrentScheduler()->GetCurrentThread();
1649 auto* const current_process = kernel.CurrentProcess();
1650 {
1651 KScopedSchedulerLockAndSleep lock(kernel, event_handle, current_thread, nano_seconds);
1652 const auto& handle_table = current_process->GetHandleTable();
1653 std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle);
1654 ASSERT(thread);
1655
1656 current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT);
1657
1658 if (thread->IsPendingTermination()) {
1659 lock.CancelSleep();
1660 return ERR_THREAD_TERMINATING;
1661 }
1662
1663 const auto release_result = current_process->GetMutex().Release(mutex_addr);
1664 if (release_result.IsError()) {
1665 lock.CancelSleep();
1666 return release_result;
1667 }
1668
1669 if (nano_seconds == 0) {
1670 lock.CancelSleep();
1671 return RESULT_TIMEOUT;
1672 }
1673
1674 current_thread->SetCondVarWaitAddress(condition_variable_addr);
1675 current_thread->SetMutexWaitAddress(mutex_addr);
1676 current_thread->SetWaitHandle(thread_handle);
1677 current_thread->SetStatus(ThreadStatus::WaitCondVar);
1678 current_process->InsertConditionVariableThread(SharedFrom(current_thread));
1679 }
1680
1681 if (event_handle != InvalidHandle) {
1682 auto& time_manager = kernel.TimeManager();
1683 time_manager.UnscheduleTimeEvent(event_handle);
1684 }
1685
1686 {
1687 KScopedSchedulerLock lock(kernel);
1688
1689 auto* owner = current_thread->GetLockOwner();
1690 if (owner != nullptr) {
1691 owner->RemoveMutexWaiter(SharedFrom(current_thread));
1692 } 1609 }
1693 1610 } else {
1694 current_process->RemoveConditionVariableThread(SharedFrom(current_thread)); 1611 timeout = timeout_ns;
1695 } 1612 }
1696 // Note: Deliberately don't attempt to inherit the lock owner's priority.
1697 1613
1698 return current_thread->GetSignalingResult(); 1614 // Wait on the condition variable.
1615 return system.Kernel().CurrentProcess()->WaitConditionVariable(
1616 address, Common::AlignDown(cv_key, sizeof(u32)), tag, timeout);
1699} 1617}
1700 1618
1701static ResultCode WaitProcessWideKeyAtomic32(Core::System& system, u32 mutex_addr, 1619static ResultCode WaitProcessWideKeyAtomic32(Core::System& system, u32 address, u32 cv_key, u32 tag,
1702 u32 condition_variable_addr, Handle thread_handle, 1620 u32 timeout_ns_low, u32 timeout_ns_high) {
1703 u32 nanoseconds_low, u32 nanoseconds_high) { 1621 const auto timeout_ns = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32));
1704 const auto nanoseconds = static_cast<s64>(nanoseconds_low | (u64{nanoseconds_high} << 32)); 1622 return WaitProcessWideKeyAtomic(system, address, cv_key, tag, timeout_ns);
1705 return WaitProcessWideKeyAtomic(system, mutex_addr, condition_variable_addr, thread_handle,
1706 nanoseconds);
1707} 1623}
1708 1624
1709/// Signal process wide key 1625/// Signal process wide key
1710static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_addr, s32 target) { 1626static void SignalProcessWideKey(Core::System& system, VAddr cv_key, s32 count) {
1711 LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}", 1627 LOG_TRACE(Kernel_SVC, "called, cv_key=0x{:X}, count=0x{:08X}", cv_key, count);
1712 condition_variable_addr, target);
1713 1628
1714 ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4)); 1629 // Signal the condition variable.
1630 return system.Kernel().CurrentProcess()->SignalConditionVariable(
1631 Common::AlignDown(cv_key, sizeof(u32)), count);
1632}
1715 1633
1716 // Retrieve a list of all threads that are waiting for this condition variable. 1634static void SignalProcessWideKey32(Core::System& system, u32 cv_key, s32 count) {
1717 auto& kernel = system.Kernel(); 1635 SignalProcessWideKey(system, cv_key, count);
1718 KScopedSchedulerLock lock(kernel); 1636}
1719 auto* const current_process = kernel.CurrentProcess();
1720 std::vector<std::shared_ptr<Thread>> waiting_threads =
1721 current_process->GetConditionVariableThreads(condition_variable_addr);
1722
1723 // Only process up to 'target' threads, unless 'target' is less equal 0, in which case process
1724 // them all.
1725 std::size_t last = waiting_threads.size();
1726 if (target > 0) {
1727 last = std::min(waiting_threads.size(), static_cast<std::size_t>(target));
1728 }
1729 for (std::size_t index = 0; index < last; ++index) {
1730 auto& thread = waiting_threads[index];
1731
1732 ASSERT(thread->GetCondVarWaitAddress() == condition_variable_addr);
1733
1734 // liberate Cond Var Thread.
1735 current_process->RemoveConditionVariableThread(thread);
1736
1737 const std::size_t current_core = system.CurrentCoreIndex();
1738 auto& monitor = system.Monitor();
1739
1740 // Atomically read the value of the mutex.
1741 u32 mutex_val = 0;
1742 u32 update_val = 0;
1743 const VAddr mutex_address = thread->GetMutexWaitAddress();
1744 do {
1745 // If the mutex is not yet acquired, acquire it.
1746 mutex_val = monitor.ExclusiveRead32(current_core, mutex_address);
1747
1748 if (mutex_val != 0) {
1749 update_val = mutex_val | Mutex::MutexHasWaitersFlag;
1750 } else {
1751 update_val = thread->GetWaitHandle();
1752 }
1753 } while (!monitor.ExclusiveWrite32(current_core, mutex_address, update_val));
1754 monitor.ClearExclusive();
1755 if (mutex_val == 0) {
1756 // We were able to acquire the mutex, resume this thread.
1757 auto* const lock_owner = thread->GetLockOwner();
1758 if (lock_owner != nullptr) {
1759 lock_owner->RemoveMutexWaiter(thread);
1760 }
1761 1637
1762 thread->SetLockOwner(nullptr); 1638namespace {
1763 thread->SetSynchronizationResults(nullptr, RESULT_SUCCESS);
1764 thread->ResumeFromWait();
1765 } else {
1766 // The mutex is already owned by some other thread, make this thread wait on it.
1767 const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
1768 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1769 auto owner = handle_table.Get<Thread>(owner_handle);
1770 ASSERT(owner);
1771 if (thread->GetStatus() == ThreadStatus::WaitCondVar) {
1772 thread->SetStatus(ThreadStatus::WaitMutex);
1773 }
1774 1639
1775 owner->AddMutexWaiter(thread); 1640constexpr bool IsValidSignalType(Svc::SignalType type) {
1776 } 1641 switch (type) {
1642 case Svc::SignalType::Signal:
1643 case Svc::SignalType::SignalAndIncrementIfEqual:
1644 case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual:
1645 return true;
1646 default:
1647 return false;
1777 } 1648 }
1778} 1649}
1779 1650
1780static void SignalProcessWideKey32(Core::System& system, u32 condition_variable_addr, s32 target) { 1651constexpr bool IsValidArbitrationType(Svc::ArbitrationType type) {
1781 SignalProcessWideKey(system, condition_variable_addr, target); 1652 switch (type) {
1653 case Svc::ArbitrationType::WaitIfLessThan:
1654 case Svc::ArbitrationType::DecrementAndWaitIfLessThan:
1655 case Svc::ArbitrationType::WaitIfEqual:
1656 return true;
1657 default:
1658 return false;
1659 }
1782} 1660}
1783 1661
1784// Wait for an address (via Address Arbiter) 1662} // namespace
1785static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type, s32 value,
1786 s64 timeout) {
1787 LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, timeout={}", address,
1788 type, value, timeout);
1789
1790 // If the passed address is a kernel virtual address, return invalid memory state.
1791 if (Core::Memory::IsKernelVirtualAddress(address)) {
1792 LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address);
1793 return ERR_INVALID_ADDRESS_STATE;
1794 }
1795 1663
1796 // If the address is not properly aligned to 4 bytes, return invalid address. 1664// Wait for an address (via Address Arbiter)
1797 if (!Common::IsWordAligned(address)) { 1665static ResultCode WaitForAddress(Core::System& system, VAddr address, Svc::ArbitrationType arb_type,
1798 LOG_ERROR(Kernel_SVC, "Address is not word aligned, address={:016X}", address); 1666 s32 value, s64 timeout_ns) {
1799 return ERR_INVALID_ADDRESS; 1667 LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, arb_type=0x{:X}, value=0x{:X}, timeout_ns={}",
1668 address, arb_type, value, timeout_ns);
1669
1670 // Validate input.
1671 R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory);
1672 R_UNLESS(Common::IsAligned(address, sizeof(int32_t)), Svc::ResultInvalidAddress);
1673 R_UNLESS(IsValidArbitrationType(arb_type), Svc::ResultInvalidEnumValue);
1674
1675 // Convert timeout from nanoseconds to ticks.
1676 s64 timeout{};
1677 if (timeout_ns > 0) {
1678 const s64 offset_tick(timeout_ns);
1679 if (offset_tick > 0) {
1680 timeout = offset_tick + 2;
1681 if (timeout <= 0) {
1682 timeout = std::numeric_limits<s64>::max();
1683 }
1684 } else {
1685 timeout = std::numeric_limits<s64>::max();
1686 }
1687 } else {
1688 timeout = timeout_ns;
1800 } 1689 }
1801 1690
1802 const auto arbitration_type = static_cast<AddressArbiter::ArbitrationType>(type); 1691 return system.Kernel().CurrentProcess()->WaitAddressArbiter(address, arb_type, value, timeout);
1803 auto& address_arbiter = system.Kernel().CurrentProcess()->GetAddressArbiter();
1804 const ResultCode result =
1805 address_arbiter.WaitForAddress(address, arbitration_type, value, timeout);
1806 return result;
1807} 1692}
1808 1693
1809static ResultCode WaitForAddress32(Core::System& system, u32 address, u32 type, s32 value, 1694static ResultCode WaitForAddress32(Core::System& system, u32 address, Svc::ArbitrationType arb_type,
1810 u32 timeout_low, u32 timeout_high) { 1695 s32 value, u32 timeout_ns_low, u32 timeout_ns_high) {
1811 const auto timeout = static_cast<s64>(timeout_low | (u64{timeout_high} << 32)); 1696 const auto timeout = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32));
1812 return WaitForAddress(system, address, type, value, timeout); 1697 return WaitForAddress(system, address, arb_type, value, timeout);
1813} 1698}
1814 1699
1815// Signals to an address (via Address Arbiter) 1700// Signals to an address (via Address Arbiter)
1816static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type, s32 value, 1701static ResultCode SignalToAddress(Core::System& system, VAddr address, Svc::SignalType signal_type,
1817 s32 num_to_wake) { 1702 s32 value, s32 count) {
1818 LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, num_to_wake=0x{:X}", 1703 LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, signal_type=0x{:X}, value=0x{:X}, count=0x{:X}",
1819 address, type, value, num_to_wake); 1704 address, signal_type, value, count);
1820
1821 // If the passed address is a kernel virtual address, return invalid memory state.
1822 if (Core::Memory::IsKernelVirtualAddress(address)) {
1823 LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address);
1824 return ERR_INVALID_ADDRESS_STATE;
1825 }
1826 1705
1827 // If the address is not properly aligned to 4 bytes, return invalid address. 1706 // Validate input.
1828 if (!Common::IsWordAligned(address)) { 1707 R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory);
1829 LOG_ERROR(Kernel_SVC, "Address is not word aligned, address={:016X}", address); 1708 R_UNLESS(Common::IsAligned(address, sizeof(s32)), Svc::ResultInvalidAddress);
1830 return ERR_INVALID_ADDRESS; 1709 R_UNLESS(IsValidSignalType(signal_type), Svc::ResultInvalidEnumValue);
1831 }
1832 1710
1833 const auto signal_type = static_cast<AddressArbiter::SignalType>(type); 1711 return system.Kernel().CurrentProcess()->SignalAddressArbiter(address, signal_type, value,
1834 auto& address_arbiter = system.Kernel().CurrentProcess()->GetAddressArbiter(); 1712 count);
1835 return address_arbiter.SignalToAddress(address, signal_type, value, num_to_wake);
1836} 1713}
1837 1714
1838static ResultCode SignalToAddress32(Core::System& system, u32 address, u32 type, s32 value, 1715static ResultCode SignalToAddress32(Core::System& system, u32 address, Svc::SignalType signal_type,
1839 s32 num_to_wake) { 1716 s32 value, s32 count) {
1840 return SignalToAddress(system, address, type, value, num_to_wake); 1717 return SignalToAddress(system, address, signal_type, value, count);
1841} 1718}
1842 1719
1843static void KernelDebug([[maybe_unused]] Core::System& system, 1720static void KernelDebug([[maybe_unused]] Core::System& system,
diff --git a/src/core/hle/kernel/svc_common.h b/src/core/hle/kernel/svc_common.h
new file mode 100644
index 000000000..4af049551
--- /dev/null
+++ b/src/core/hle/kernel/svc_common.h
@@ -0,0 +1,14 @@
1// Copyright 2020 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8
9namespace Kernel::Svc {
10
11constexpr s32 ArgumentHandleCountMax = 0x40;
12constexpr u32 HandleWaitMask{1u << 30};
13
14} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc_results.h b/src/core/hle/kernel/svc_results.h
new file mode 100644
index 000000000..78282f021
--- /dev/null
+++ b/src/core/hle/kernel/svc_results.h
@@ -0,0 +1,20 @@
1// Copyright 2020 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/result.h"
8
9namespace Kernel::Svc {
10
11constexpr ResultCode ResultTerminationRequested{ErrorModule::Kernel, 59};
12constexpr ResultCode ResultInvalidAddress{ErrorModule::Kernel, 102};
13constexpr ResultCode ResultInvalidCurrentMemory{ErrorModule::Kernel, 106};
14constexpr ResultCode ResultInvalidHandle{ErrorModule::Kernel, 114};
15constexpr ResultCode ResultTimedOut{ErrorModule::Kernel, 117};
16constexpr ResultCode ResultCancelled{ErrorModule::Kernel, 118};
17constexpr ResultCode ResultInvalidEnumValue{ErrorModule::Kernel, 120};
18constexpr ResultCode ResultInvalidState{ErrorModule::Kernel, 125};
19
20} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h
index 986724beb..d623f7a50 100644
--- a/src/core/hle/kernel/svc_types.h
+++ b/src/core/hle/kernel/svc_types.h
@@ -23,8 +23,8 @@ enum class MemoryState : u32 {
23 Ipc = 0x0A, 23 Ipc = 0x0A,
24 Stack = 0x0B, 24 Stack = 0x0B,
25 ThreadLocal = 0x0C, 25 ThreadLocal = 0x0C,
26 Transfered = 0x0D, 26 Transferred = 0x0D,
27 SharedTransfered = 0x0E, 27 SharedTransferred = 0x0E,
28 SharedCode = 0x0F, 28 SharedCode = 0x0F,
29 Inaccessible = 0x10, 29 Inaccessible = 0x10,
30 NonSecureIpc = 0x11, 30 NonSecureIpc = 0x11,
@@ -65,4 +65,16 @@ struct MemoryInfo {
65 u32 padding{}; 65 u32 padding{};
66}; 66};
67 67
68enum class SignalType : u32 {
69 Signal = 0,
70 SignalAndIncrementIfEqual = 1,
71 SignalAndModifyByWaitingCountIfEqual = 2,
72};
73
74enum class ArbitrationType : u32 {
75 WaitIfLessThan = 0,
76 DecrementAndWaitIfLessThan = 1,
77 WaitIfEqual = 2,
78};
79
68} // namespace Kernel::Svc 80} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index 0b6dd9df0..a32750ed7 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -7,6 +7,7 @@
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "core/arm/arm_interface.h" 8#include "core/arm/arm_interface.h"
9#include "core/core.h" 9#include "core/core.h"
10#include "core/hle/kernel/svc_types.h"
10#include "core/hle/result.h" 11#include "core/hle/result.h"
11 12
12namespace Kernel { 13namespace Kernel {
@@ -215,9 +216,10 @@ void SvcWrap64(Core::System& system) {
215 func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2)).raw); 216 func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2)).raw);
216} 217}
217 218
218template <ResultCode func(Core::System&, u32*, u64, u64, s64)> 219// Used by WaitSynchronization
220template <ResultCode func(Core::System&, s32*, u64, u64, s64)>
219void SvcWrap64(Core::System& system) { 221void SvcWrap64(Core::System& system) {
220 u32 param_1 = 0; 222 s32 param_1 = 0;
221 const u32 retval = func(system, &param_1, Param(system, 1), static_cast<u32>(Param(system, 2)), 223 const u32 retval = func(system, &param_1, Param(system, 1), static_cast<u32>(Param(system, 2)),
222 static_cast<s64>(Param(system, 3))) 224 static_cast<s64>(Param(system, 3)))
223 .raw; 225 .raw;
@@ -276,18 +278,22 @@ void SvcWrap64(Core::System& system) {
276 FuncReturn(system, retval); 278 FuncReturn(system, retval);
277} 279}
278 280
279template <ResultCode func(Core::System&, u64, u32, s32, s64)> 281// Used by WaitForAddress
282template <ResultCode func(Core::System&, u64, Svc::ArbitrationType, s32, s64)>
280void SvcWrap64(Core::System& system) { 283void SvcWrap64(Core::System& system) {
281 FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)), 284 FuncReturn(system,
282 static_cast<s32>(Param(system, 2)), static_cast<s64>(Param(system, 3))) 285 func(system, Param(system, 0), static_cast<Svc::ArbitrationType>(Param(system, 1)),
283 .raw); 286 static_cast<s32>(Param(system, 2)), static_cast<s64>(Param(system, 3)))
287 .raw);
284} 288}
285 289
286template <ResultCode func(Core::System&, u64, u32, s32, s32)> 290// Used by SignalToAddress
291template <ResultCode func(Core::System&, u64, Svc::SignalType, s32, s32)>
287void SvcWrap64(Core::System& system) { 292void SvcWrap64(Core::System& system) {
288 FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)), 293 FuncReturn(system,
289 static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3))) 294 func(system, Param(system, 0), static_cast<Svc::SignalType>(Param(system, 1)),
290 .raw); 295 static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3)))
296 .raw);
291} 297}
292 298
293//////////////////////////////////////////////////////////////////////////////////////////////////// 299////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -503,22 +509,23 @@ void SvcWrap32(Core::System& system) {
503} 509}
504 510
505// Used by WaitForAddress32 511// Used by WaitForAddress32
506template <ResultCode func(Core::System&, u32, u32, s32, u32, u32)> 512template <ResultCode func(Core::System&, u32, Svc::ArbitrationType, s32, u32, u32)>
507void SvcWrap32(Core::System& system) { 513void SvcWrap32(Core::System& system) {
508 const u32 retval = func(system, static_cast<u32>(Param(system, 0)), 514 const u32 retval = func(system, static_cast<u32>(Param(system, 0)),
509 static_cast<u32>(Param(system, 1)), static_cast<s32>(Param(system, 2)), 515 static_cast<Svc::ArbitrationType>(Param(system, 1)),
510 static_cast<u32>(Param(system, 3)), static_cast<u32>(Param(system, 4))) 516 static_cast<s32>(Param(system, 2)), static_cast<u32>(Param(system, 3)),
517 static_cast<u32>(Param(system, 4)))
511 .raw; 518 .raw;
512 FuncReturn(system, retval); 519 FuncReturn(system, retval);
513} 520}
514 521
515// Used by SignalToAddress32 522// Used by SignalToAddress32
516template <ResultCode func(Core::System&, u32, u32, s32, s32)> 523template <ResultCode func(Core::System&, u32, Svc::SignalType, s32, s32)>
517void SvcWrap32(Core::System& system) { 524void SvcWrap32(Core::System& system) {
518 const u32 retval = 525 const u32 retval = func(system, static_cast<u32>(Param(system, 0)),
519 func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1)), 526 static_cast<Svc::SignalType>(Param(system, 1)),
520 static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3))) 527 static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3)))
521 .raw; 528 .raw;
522 FuncReturn(system, retval); 529 FuncReturn(system, retval);
523} 530}
524 531
@@ -539,9 +546,9 @@ void SvcWrap32(Core::System& system) {
539} 546}
540 547
541// Used by WaitSynchronization32 548// Used by WaitSynchronization32
542template <ResultCode func(Core::System&, u32, u32, s32, u32, Handle*)> 549template <ResultCode func(Core::System&, u32, u32, s32, u32, s32*)>
543void SvcWrap32(Core::System& system) { 550void SvcWrap32(Core::System& system) {
544 u32 param_1 = 0; 551 s32 param_1 = 0;
545 const u32 retval = func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2), 552 const u32 retval = func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2),
546 Param32(system, 3), &param_1) 553 Param32(system, 3), &param_1)
547 .raw; 554 .raw;
diff --git a/src/core/hle/kernel/synchronization.cpp b/src/core/hle/kernel/synchronization.cpp
deleted file mode 100644
index d3f520ea2..000000000
--- a/src/core/hle/kernel/synchronization.cpp
+++ /dev/null
@@ -1,116 +0,0 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/core.h"
6#include "core/hle/kernel/errors.h"
7#include "core/hle/kernel/handle_table.h"
8#include "core/hle/kernel/k_scheduler.h"
9#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
10#include "core/hle/kernel/kernel.h"
11#include "core/hle/kernel/synchronization.h"
12#include "core/hle/kernel/synchronization_object.h"
13#include "core/hle/kernel/thread.h"
14#include "core/hle/kernel/time_manager.h"
15
16namespace Kernel {
17
18Synchronization::Synchronization(Core::System& system) : system{system} {}
19
20void Synchronization::SignalObject(SynchronizationObject& obj) const {
21 auto& kernel = system.Kernel();
22 KScopedSchedulerLock lock(kernel);
23 if (obj.IsSignaled()) {
24 for (auto thread : obj.GetWaitingThreads()) {
25 if (thread->GetSchedulingStatus() == ThreadSchedStatus::Paused) {
26 if (thread->GetStatus() != ThreadStatus::WaitHLEEvent) {
27 ASSERT(thread->GetStatus() == ThreadStatus::WaitSynch);
28 ASSERT(thread->IsWaitingSync());
29 }
30 thread->SetSynchronizationResults(&obj, RESULT_SUCCESS);
31 thread->ResumeFromWait();
32 }
33 }
34 obj.ClearWaitingThreads();
35 }
36}
37
38std::pair<ResultCode, Handle> Synchronization::WaitFor(
39 std::vector<std::shared_ptr<SynchronizationObject>>& sync_objects, s64 nano_seconds) {
40 auto& kernel = system.Kernel();
41 auto* const thread = kernel.CurrentScheduler()->GetCurrentThread();
42 Handle event_handle = InvalidHandle;
43 {
44 KScopedSchedulerLockAndSleep lock(kernel, event_handle, thread, nano_seconds);
45 const auto itr =
46 std::find_if(sync_objects.begin(), sync_objects.end(),
47 [thread](const std::shared_ptr<SynchronizationObject>& object) {
48 return object->IsSignaled();
49 });
50
51 if (itr != sync_objects.end()) {
52 // We found a ready object, acquire it and set the result value
53 SynchronizationObject* object = itr->get();
54 object->Acquire(thread);
55 const u32 index = static_cast<s32>(std::distance(sync_objects.begin(), itr));
56 lock.CancelSleep();
57 return {RESULT_SUCCESS, index};
58 }
59
60 if (nano_seconds == 0) {
61 lock.CancelSleep();
62 return {RESULT_TIMEOUT, InvalidHandle};
63 }
64
65 if (thread->IsPendingTermination()) {
66 lock.CancelSleep();
67 return {ERR_THREAD_TERMINATING, InvalidHandle};
68 }
69
70 if (thread->IsSyncCancelled()) {
71 thread->SetSyncCancelled(false);
72 lock.CancelSleep();
73 return {ERR_SYNCHRONIZATION_CANCELED, InvalidHandle};
74 }
75
76 for (auto& object : sync_objects) {
77 object->AddWaitingThread(SharedFrom(thread));
78 }
79
80 thread->SetSynchronizationObjects(&sync_objects);
81 thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT);
82 thread->SetStatus(ThreadStatus::WaitSynch);
83 thread->SetWaitingSync(true);
84 }
85 thread->SetWaitingSync(false);
86
87 if (event_handle != InvalidHandle) {
88 auto& time_manager = kernel.TimeManager();
89 time_manager.UnscheduleTimeEvent(event_handle);
90 }
91
92 {
93 KScopedSchedulerLock lock(kernel);
94 ResultCode signaling_result = thread->GetSignalingResult();
95 SynchronizationObject* signaling_object = thread->GetSignalingObject();
96 thread->SetSynchronizationObjects(nullptr);
97 auto shared_thread = SharedFrom(thread);
98 for (auto& obj : sync_objects) {
99 obj->RemoveWaitingThread(shared_thread);
100 }
101 if (signaling_object != nullptr) {
102 const auto itr = std::find_if(
103 sync_objects.begin(), sync_objects.end(),
104 [signaling_object](const std::shared_ptr<SynchronizationObject>& object) {
105 return object.get() == signaling_object;
106 });
107 ASSERT(itr != sync_objects.end());
108 signaling_object->Acquire(thread);
109 const u32 index = static_cast<s32>(std::distance(sync_objects.begin(), itr));
110 return {signaling_result, index};
111 }
112 return {signaling_result, -1};
113 }
114}
115
116} // namespace Kernel
diff --git a/src/core/hle/kernel/synchronization.h b/src/core/hle/kernel/synchronization.h
deleted file mode 100644
index 379f4b1d3..000000000
--- a/src/core/hle/kernel/synchronization.h
+++ /dev/null
@@ -1,44 +0,0 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <utility>
9#include <vector>
10
11#include "core/hle/kernel/object.h"
12#include "core/hle/result.h"
13
14namespace Core {
15class System;
16} // namespace Core
17
18namespace Kernel {
19
20class SynchronizationObject;
21
22/**
23 * The 'Synchronization' class is an interface for handling synchronization methods
24 * used by Synchronization objects and synchronization SVCs. This centralizes processing of
25 * such
26 */
27class Synchronization {
28public:
29 explicit Synchronization(Core::System& system);
30
31 /// Signals a synchronization object, waking up all its waiting threads
32 void SignalObject(SynchronizationObject& obj) const;
33
34 /// Tries to see if waiting for any of the sync_objects is necessary, if not
35 /// it returns Success and the handle index of the signaled sync object. In
36 /// case not, the current thread will be locked and wait for nano_seconds or
37 /// for a synchronization object to signal.
38 std::pair<ResultCode, Handle> WaitFor(
39 std::vector<std::shared_ptr<SynchronizationObject>>& sync_objects, s64 nano_seconds);
40
41private:
42 Core::System& system;
43};
44} // namespace Kernel
diff --git a/src/core/hle/kernel/synchronization_object.cpp b/src/core/hle/kernel/synchronization_object.cpp
deleted file mode 100644
index ba4d39157..000000000
--- a/src/core/hle/kernel/synchronization_object.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include "common/assert.h"
7#include "common/common_types.h"
8#include "common/logging/log.h"
9#include "core/core.h"
10#include "core/hle/kernel/kernel.h"
11#include "core/hle/kernel/object.h"
12#include "core/hle/kernel/process.h"
13#include "core/hle/kernel/synchronization.h"
14#include "core/hle/kernel/synchronization_object.h"
15#include "core/hle/kernel/thread.h"
16
17namespace Kernel {
18
19SynchronizationObject::SynchronizationObject(KernelCore& kernel) : Object{kernel} {}
20SynchronizationObject::~SynchronizationObject() = default;
21
22void SynchronizationObject::Signal() {
23 kernel.Synchronization().SignalObject(*this);
24}
25
26void SynchronizationObject::AddWaitingThread(std::shared_ptr<Thread> thread) {
27 auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread);
28 if (itr == waiting_threads.end())
29 waiting_threads.push_back(std::move(thread));
30}
31
32void SynchronizationObject::RemoveWaitingThread(std::shared_ptr<Thread> thread) {
33 auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread);
34 // If a thread passed multiple handles to the same object,
35 // the kernel might attempt to remove the thread from the object's
36 // waiting threads list multiple times.
37 if (itr != waiting_threads.end())
38 waiting_threads.erase(itr);
39}
40
41void SynchronizationObject::ClearWaitingThreads() {
42 waiting_threads.clear();
43}
44
45const std::vector<std::shared_ptr<Thread>>& SynchronizationObject::GetWaitingThreads() const {
46 return waiting_threads;
47}
48
49} // namespace Kernel
diff --git a/src/core/hle/kernel/synchronization_object.h b/src/core/hle/kernel/synchronization_object.h
deleted file mode 100644
index 7408ed51f..000000000
--- a/src/core/hle/kernel/synchronization_object.h
+++ /dev/null
@@ -1,77 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <atomic>
8#include <memory>
9#include <vector>
10
11#include "core/hle/kernel/object.h"
12
13namespace Kernel {
14
15class KernelCore;
16class Synchronization;
17class Thread;
18
19/// Class that represents a Kernel object that a thread can be waiting on
20class SynchronizationObject : public Object {
21public:
22 explicit SynchronizationObject(KernelCore& kernel);
23 ~SynchronizationObject() override;
24
25 /**
26 * Check if the specified thread should wait until the object is available
27 * @param thread The thread about which we're deciding.
28 * @return True if the current thread should wait due to this object being unavailable
29 */
30 virtual bool ShouldWait(const Thread* thread) const = 0;
31
32 /// Acquire/lock the object for the specified thread if it is available
33 virtual void Acquire(Thread* thread) = 0;
34
35 /// Signal this object
36 virtual void Signal();
37
38 virtual bool IsSignaled() const {
39 return is_signaled;
40 }
41
42 /**
43 * Add a thread to wait on this object
44 * @param thread Pointer to thread to add
45 */
46 void AddWaitingThread(std::shared_ptr<Thread> thread);
47
48 /**
49 * Removes a thread from waiting on this object (e.g. if it was resumed already)
50 * @param thread Pointer to thread to remove
51 */
52 void RemoveWaitingThread(std::shared_ptr<Thread> thread);
53
54 /// Get a const reference to the waiting threads list for debug use
55 const std::vector<std::shared_ptr<Thread>>& GetWaitingThreads() const;
56
57 void ClearWaitingThreads();
58
59protected:
60 std::atomic_bool is_signaled{}; // Tells if this sync object is signaled
61
62private:
63 /// Threads waiting for this object to become available
64 std::vector<std::shared_ptr<Thread>> waiting_threads;
65};
66
67// Specialization of DynamicObjectCast for SynchronizationObjects
68template <>
69inline std::shared_ptr<SynchronizationObject> DynamicObjectCast<SynchronizationObject>(
70 std::shared_ptr<Object> object) {
71 if (object != nullptr && object->IsWaitable()) {
72 return std::static_pointer_cast<SynchronizationObject>(object);
73 }
74 return nullptr;
75}
76
77} // namespace Kernel
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index a4f9e0d97..d97323255 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -17,9 +17,11 @@
17#include "core/hardware_properties.h" 17#include "core/hardware_properties.h"
18#include "core/hle/kernel/errors.h" 18#include "core/hle/kernel/errors.h"
19#include "core/hle/kernel/handle_table.h" 19#include "core/hle/kernel/handle_table.h"
20#include "core/hle/kernel/k_condition_variable.h"
20#include "core/hle/kernel/k_scheduler.h" 21#include "core/hle/kernel/k_scheduler.h"
21#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" 22#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
22#include "core/hle/kernel/kernel.h" 23#include "core/hle/kernel/kernel.h"
24#include "core/hle/kernel/memory/memory_layout.h"
23#include "core/hle/kernel/object.h" 25#include "core/hle/kernel/object.h"
24#include "core/hle/kernel/process.h" 26#include "core/hle/kernel/process.h"
25#include "core/hle/kernel/thread.h" 27#include "core/hle/kernel/thread.h"
@@ -34,26 +36,19 @@
34 36
35namespace Kernel { 37namespace Kernel {
36 38
37bool Thread::ShouldWait(const Thread* thread) const {
38 return status != ThreadStatus::Dead;
39}
40
41bool Thread::IsSignaled() const { 39bool Thread::IsSignaled() const {
42 return status == ThreadStatus::Dead; 40 return signaled;
43}
44
45void Thread::Acquire(Thread* thread) {
46 ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
47} 41}
48 42
49Thread::Thread(KernelCore& kernel) : SynchronizationObject{kernel} {} 43Thread::Thread(KernelCore& kernel) : KSynchronizationObject{kernel} {}
50Thread::~Thread() = default; 44Thread::~Thread() = default;
51 45
52void Thread::Stop() { 46void Thread::Stop() {
53 { 47 {
54 KScopedSchedulerLock lock(kernel); 48 KScopedSchedulerLock lock(kernel);
55 SetStatus(ThreadStatus::Dead); 49 SetState(ThreadState::Terminated);
56 Signal(); 50 signaled = true;
51 NotifyAvailable();
57 kernel.GlobalHandleTable().Close(global_handle); 52 kernel.GlobalHandleTable().Close(global_handle);
58 53
59 if (owner_process) { 54 if (owner_process) {
@@ -67,59 +62,27 @@ void Thread::Stop() {
67 global_handle = 0; 62 global_handle = 0;
68} 63}
69 64
70void Thread::ResumeFromWait() { 65void Thread::Wakeup() {
71 KScopedSchedulerLock lock(kernel); 66 KScopedSchedulerLock lock(kernel);
72 switch (status) { 67 SetState(ThreadState::Runnable);
73 case ThreadStatus::Paused:
74 case ThreadStatus::WaitSynch:
75 case ThreadStatus::WaitHLEEvent:
76 case ThreadStatus::WaitSleep:
77 case ThreadStatus::WaitIPC:
78 case ThreadStatus::WaitMutex:
79 case ThreadStatus::WaitCondVar:
80 case ThreadStatus::WaitArb:
81 case ThreadStatus::Dormant:
82 break;
83
84 case ThreadStatus::Ready:
85 // The thread's wakeup callback must have already been cleared when the thread was first
86 // awoken.
87 ASSERT(hle_callback == nullptr);
88 // If the thread is waiting on multiple wait objects, it might be awoken more than once
89 // before actually resuming. We can ignore subsequent wakeups if the thread status has
90 // already been set to ThreadStatus::Ready.
91 return;
92 case ThreadStatus::Dead:
93 // This should never happen, as threads must complete before being stopped.
94 DEBUG_ASSERT_MSG(false, "Thread with object id {} cannot be resumed because it's DEAD.",
95 GetObjectId());
96 return;
97 }
98
99 SetStatus(ThreadStatus::Ready);
100}
101
102void Thread::OnWakeUp() {
103 KScopedSchedulerLock lock(kernel);
104 SetStatus(ThreadStatus::Ready);
105} 68}
106 69
107ResultCode Thread::Start() { 70ResultCode Thread::Start() {
108 KScopedSchedulerLock lock(kernel); 71 KScopedSchedulerLock lock(kernel);
109 SetStatus(ThreadStatus::Ready); 72 SetState(ThreadState::Runnable);
110 return RESULT_SUCCESS; 73 return RESULT_SUCCESS;
111} 74}
112 75
113void Thread::CancelWait() { 76void Thread::CancelWait() {
114 KScopedSchedulerLock lock(kernel); 77 KScopedSchedulerLock lock(kernel);
115 if (GetSchedulingStatus() != ThreadSchedStatus::Paused || !is_waiting_on_sync) { 78 if (GetState() != ThreadState::Waiting || !is_cancellable) {
116 is_sync_cancelled = true; 79 is_sync_cancelled = true;
117 return; 80 return;
118 } 81 }
119 // TODO(Blinkhawk): Implement cancel of server session 82 // TODO(Blinkhawk): Implement cancel of server session
120 is_sync_cancelled = false; 83 is_sync_cancelled = false;
121 SetSynchronizationResults(nullptr, ERR_SYNCHRONIZATION_CANCELED); 84 SetSynchronizationResults(nullptr, ERR_SYNCHRONIZATION_CANCELED);
122 SetStatus(ThreadStatus::Ready); 85 SetState(ThreadState::Runnable);
123} 86}
124 87
125static void ResetThreadContext32(Core::ARM_Interface::ThreadContext32& context, u32 stack_top, 88static void ResetThreadContext32(Core::ARM_Interface::ThreadContext32& context, u32 stack_top,
@@ -183,25 +146,24 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadTy
183 std::shared_ptr<Thread> thread = std::make_shared<Thread>(kernel); 146 std::shared_ptr<Thread> thread = std::make_shared<Thread>(kernel);
184 147
185 thread->thread_id = kernel.CreateNewThreadID(); 148 thread->thread_id = kernel.CreateNewThreadID();
186 thread->status = ThreadStatus::Dormant; 149 thread->thread_state = ThreadState::Initialized;
187 thread->entry_point = entry_point; 150 thread->entry_point = entry_point;
188 thread->stack_top = stack_top; 151 thread->stack_top = stack_top;
189 thread->disable_count = 1; 152 thread->disable_count = 1;
190 thread->tpidr_el0 = 0; 153 thread->tpidr_el0 = 0;
191 thread->nominal_priority = thread->current_priority = priority; 154 thread->current_priority = priority;
155 thread->base_priority = priority;
156 thread->lock_owner = nullptr;
192 thread->schedule_count = -1; 157 thread->schedule_count = -1;
193 thread->last_scheduled_tick = 0; 158 thread->last_scheduled_tick = 0;
194 thread->processor_id = processor_id; 159 thread->processor_id = processor_id;
195 thread->ideal_core = processor_id; 160 thread->ideal_core = processor_id;
196 thread->affinity_mask.SetAffinity(processor_id, true); 161 thread->affinity_mask.SetAffinity(processor_id, true);
197 thread->wait_objects = nullptr;
198 thread->mutex_wait_address = 0;
199 thread->condvar_wait_address = 0;
200 thread->wait_handle = 0;
201 thread->name = std::move(name); 162 thread->name = std::move(name);
202 thread->global_handle = kernel.GlobalHandleTable().Create(thread).Unwrap(); 163 thread->global_handle = kernel.GlobalHandleTable().Create(thread).Unwrap();
203 thread->owner_process = owner_process; 164 thread->owner_process = owner_process;
204 thread->type = type_flags; 165 thread->type = type_flags;
166 thread->signaled = false;
205 if ((type_flags & THREADTYPE_IDLE) == 0) { 167 if ((type_flags & THREADTYPE_IDLE) == 0) {
206 auto& scheduler = kernel.GlobalSchedulerContext(); 168 auto& scheduler = kernel.GlobalSchedulerContext();
207 scheduler.AddThread(thread); 169 scheduler.AddThread(thread);
@@ -226,153 +188,185 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadTy
226 return MakeResult<std::shared_ptr<Thread>>(std::move(thread)); 188 return MakeResult<std::shared_ptr<Thread>>(std::move(thread));
227} 189}
228 190
229void Thread::SetPriority(u32 priority) { 191void Thread::SetBasePriority(u32 priority) {
230 KScopedSchedulerLock lock(kernel);
231 ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST, 192 ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST,
232 "Invalid priority value."); 193 "Invalid priority value.");
233 nominal_priority = priority; 194
234 UpdatePriority(); 195 KScopedSchedulerLock lock(kernel);
196
197 // Change our base priority.
198 base_priority = priority;
199
200 // Perform a priority restoration.
201 RestorePriority(kernel, this);
235} 202}
236 203
237void Thread::SetSynchronizationResults(SynchronizationObject* object, ResultCode result) { 204void Thread::SetSynchronizationResults(KSynchronizationObject* object, ResultCode result) {
238 signaling_object = object; 205 signaling_object = object;
239 signaling_result = result; 206 signaling_result = result;
240} 207}
241 208
242s32 Thread::GetSynchronizationObjectIndex(std::shared_ptr<SynchronizationObject> object) const {
243 ASSERT_MSG(!wait_objects->empty(), "Thread is not waiting for anything");
244 const auto match = std::find(wait_objects->rbegin(), wait_objects->rend(), object);
245 return static_cast<s32>(std::distance(match, wait_objects->rend()) - 1);
246}
247
248VAddr Thread::GetCommandBufferAddress() const { 209VAddr Thread::GetCommandBufferAddress() const {
249 // Offset from the start of TLS at which the IPC command buffer begins. 210 // Offset from the start of TLS at which the IPC command buffer begins.
250 constexpr u64 command_header_offset = 0x80; 211 constexpr u64 command_header_offset = 0x80;
251 return GetTLSAddress() + command_header_offset; 212 return GetTLSAddress() + command_header_offset;
252} 213}
253 214
254void Thread::SetStatus(ThreadStatus new_status) { 215void Thread::SetState(ThreadState state) {
255 if (new_status == status) { 216 KScopedSchedulerLock sl(kernel);
256 return;
257 }
258 217
259 switch (new_status) { 218 // Clear debugging state
260 case ThreadStatus::Ready: 219 SetMutexWaitAddressForDebugging({});
261 SetSchedulingStatus(ThreadSchedStatus::Runnable); 220 SetWaitReasonForDebugging({});
262 break;
263 case ThreadStatus::Dormant:
264 SetSchedulingStatus(ThreadSchedStatus::None);
265 break;
266 case ThreadStatus::Dead:
267 SetSchedulingStatus(ThreadSchedStatus::Exited);
268 break;
269 default:
270 SetSchedulingStatus(ThreadSchedStatus::Paused);
271 break;
272 }
273 221
274 status = new_status; 222 const ThreadState old_state = thread_state;
223 thread_state =
224 static_cast<ThreadState>((old_state & ~ThreadState::Mask) | (state & ThreadState::Mask));
225 if (thread_state != old_state) {
226 KScheduler::OnThreadStateChanged(kernel, this, old_state);
227 }
275} 228}
276 229
277void Thread::AddMutexWaiter(std::shared_ptr<Thread> thread) { 230void Thread::AddWaiterImpl(Thread* thread) {
278 if (thread->lock_owner.get() == this) { 231 ASSERT(kernel.GlobalSchedulerContext().IsLocked());
279 // If the thread is already waiting for this thread to release the mutex, ensure that the 232
280 // waiters list is consistent and return without doing anything. 233 // Find the right spot to insert the waiter.
281 const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread); 234 auto it = waiter_list.begin();
282 ASSERT(iter != wait_mutex_threads.end()); 235 while (it != waiter_list.end()) {
283 return; 236 if (it->GetPriority() > thread->GetPriority()) {
237 break;
238 }
239 it++;
284 } 240 }
285 241
286 // A thread can't wait on two different mutexes at the same time. 242 // Keep track of how many kernel waiters we have.
287 ASSERT(thread->lock_owner == nullptr); 243 if (Memory::IsKernelAddressKey(thread->GetAddressKey())) {
244 ASSERT((num_kernel_waiters++) >= 0);
245 }
288 246
289 // Ensure that the thread is not already in the list of mutex waiters 247 // Insert the waiter.
290 const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread); 248 waiter_list.insert(it, *thread);
291 ASSERT(iter == wait_mutex_threads.end()); 249 thread->SetLockOwner(this);
250}
292 251
293 // Keep the list in an ordered fashion 252void Thread::RemoveWaiterImpl(Thread* thread) {
294 const auto insertion_point = std::find_if( 253 ASSERT(kernel.GlobalSchedulerContext().IsLocked());
295 wait_mutex_threads.begin(), wait_mutex_threads.end(),
296 [&thread](const auto& entry) { return entry->GetPriority() > thread->GetPriority(); });
297 wait_mutex_threads.insert(insertion_point, thread);
298 thread->lock_owner = SharedFrom(this);
299 254
300 UpdatePriority(); 255 // Keep track of how many kernel waiters we have.
301} 256 if (Memory::IsKernelAddressKey(thread->GetAddressKey())) {
257 ASSERT((num_kernel_waiters--) > 0);
258 }
302 259
303void Thread::RemoveMutexWaiter(std::shared_ptr<Thread> thread) { 260 // Remove the waiter.
304 ASSERT(thread->lock_owner.get() == this); 261 waiter_list.erase(waiter_list.iterator_to(*thread));
262 thread->SetLockOwner(nullptr);
263}
305 264
306 // Ensure that the thread is in the list of mutex waiters 265void Thread::RestorePriority(KernelCore& kernel, Thread* thread) {
307 const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread); 266 ASSERT(kernel.GlobalSchedulerContext().IsLocked());
308 ASSERT(iter != wait_mutex_threads.end());
309 267
310 wait_mutex_threads.erase(iter); 268 while (true) {
269 // We want to inherit priority where possible.
270 s32 new_priority = thread->GetBasePriority();
271 if (thread->HasWaiters()) {
272 new_priority = std::min(new_priority, thread->waiter_list.front().GetPriority());
273 }
311 274
312 thread->lock_owner = nullptr; 275 // If the priority we would inherit is not different from ours, don't do anything.
313 UpdatePriority(); 276 if (new_priority == thread->GetPriority()) {
314} 277 return;
278 }
315 279
316void Thread::UpdatePriority() { 280 // Ensure we don't violate condition variable red black tree invariants.
317 // If any of the threads waiting on the mutex have a higher priority 281 if (auto* cv_tree = thread->GetConditionVariableTree(); cv_tree != nullptr) {
318 // (taking into account priority inheritance), then this thread inherits 282 BeforeUpdatePriority(kernel, cv_tree, thread);
319 // that thread's priority.
320 u32 new_priority = nominal_priority;
321 if (!wait_mutex_threads.empty()) {
322 if (wait_mutex_threads.front()->current_priority < new_priority) {
323 new_priority = wait_mutex_threads.front()->current_priority;
324 } 283 }
325 }
326 284
327 if (new_priority == current_priority) { 285 // Change the priority.
328 return; 286 const s32 old_priority = thread->GetPriority();
329 } 287 thread->SetPriority(new_priority);
330 288
331 if (GetStatus() == ThreadStatus::WaitCondVar) { 289 // Restore the condition variable, if relevant.
332 owner_process->RemoveConditionVariableThread(SharedFrom(this)); 290 if (auto* cv_tree = thread->GetConditionVariableTree(); cv_tree != nullptr) {
333 } 291 AfterUpdatePriority(kernel, cv_tree, thread);
292 }
334 293
335 SetCurrentPriority(new_priority); 294 // Update the scheduler.
295 KScheduler::OnThreadPriorityChanged(kernel, thread, old_priority);
336 296
337 if (GetStatus() == ThreadStatus::WaitCondVar) { 297 // Keep the lock owner up to date.
338 owner_process->InsertConditionVariableThread(SharedFrom(this)); 298 Thread* lock_owner = thread->GetLockOwner();
339 } 299 if (lock_owner == nullptr) {
300 return;
301 }
340 302
341 if (!lock_owner) { 303 // Update the thread in the lock owner's sorted list, and continue inheriting.
342 return; 304 lock_owner->RemoveWaiterImpl(thread);
305 lock_owner->AddWaiterImpl(thread);
306 thread = lock_owner;
343 } 307 }
308}
344 309
345 // Ensure that the thread is within the correct location in the waiting list. 310void Thread::AddWaiter(Thread* thread) {
346 auto old_owner = lock_owner; 311 AddWaiterImpl(thread);
347 lock_owner->RemoveMutexWaiter(SharedFrom(this)); 312 RestorePriority(kernel, this);
348 old_owner->AddMutexWaiter(SharedFrom(this));
349
350 // Recursively update the priority of the thread that depends on the priority of this one.
351 lock_owner->UpdatePriority();
352} 313}
353 314
354bool Thread::AllSynchronizationObjectsReady() const { 315void Thread::RemoveWaiter(Thread* thread) {
355 return std::none_of(wait_objects->begin(), wait_objects->end(), 316 RemoveWaiterImpl(thread);
356 [this](const std::shared_ptr<SynchronizationObject>& object) { 317 RestorePriority(kernel, this);
357 return object->ShouldWait(this);
358 });
359} 318}
360 319
361bool Thread::InvokeHLECallback(std::shared_ptr<Thread> thread) { 320Thread* Thread::RemoveWaiterByKey(s32* out_num_waiters, VAddr key) {
362 ASSERT(hle_callback); 321 ASSERT(kernel.GlobalSchedulerContext().IsLocked());
363 return hle_callback(std::move(thread)); 322
323 s32 num_waiters{};
324 Thread* next_lock_owner{};
325 auto it = waiter_list.begin();
326 while (it != waiter_list.end()) {
327 if (it->GetAddressKey() == key) {
328 Thread* thread = std::addressof(*it);
329
330 // Keep track of how many kernel waiters we have.
331 if (Memory::IsKernelAddressKey(thread->GetAddressKey())) {
332 ASSERT((num_kernel_waiters--) > 0);
333 }
334 it = waiter_list.erase(it);
335
336 // Update the next lock owner.
337 if (next_lock_owner == nullptr) {
338 next_lock_owner = thread;
339 next_lock_owner->SetLockOwner(nullptr);
340 } else {
341 next_lock_owner->AddWaiterImpl(thread);
342 }
343 num_waiters++;
344 } else {
345 it++;
346 }
347 }
348
349 // Do priority updates, if we have a next owner.
350 if (next_lock_owner) {
351 RestorePriority(kernel, this);
352 RestorePriority(kernel, next_lock_owner);
353 }
354
355 // Return output.
356 *out_num_waiters = num_waiters;
357 return next_lock_owner;
364} 358}
365 359
366ResultCode Thread::SetActivity(ThreadActivity value) { 360ResultCode Thread::SetActivity(ThreadActivity value) {
367 KScopedSchedulerLock lock(kernel); 361 KScopedSchedulerLock lock(kernel);
368 362
369 auto sched_status = GetSchedulingStatus(); 363 auto sched_status = GetState();
370 364
371 if (sched_status != ThreadSchedStatus::Runnable && sched_status != ThreadSchedStatus::Paused) { 365 if (sched_status != ThreadState::Runnable && sched_status != ThreadState::Waiting) {
372 return ERR_INVALID_STATE; 366 return ERR_INVALID_STATE;
373 } 367 }
374 368
375 if (IsPendingTermination()) { 369 if (IsTerminationRequested()) {
376 return RESULT_SUCCESS; 370 return RESULT_SUCCESS;
377 } 371 }
378 372
@@ -394,7 +388,8 @@ ResultCode Thread::Sleep(s64 nanoseconds) {
394 Handle event_handle{}; 388 Handle event_handle{};
395 { 389 {
396 KScopedSchedulerLockAndSleep lock(kernel, event_handle, this, nanoseconds); 390 KScopedSchedulerLockAndSleep lock(kernel, event_handle, this, nanoseconds);
397 SetStatus(ThreadStatus::WaitSleep); 391 SetState(ThreadState::Waiting);
392 SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Sleep);
398 } 393 }
399 394
400 if (event_handle != InvalidHandle) { 395 if (event_handle != InvalidHandle) {
@@ -405,34 +400,21 @@ ResultCode Thread::Sleep(s64 nanoseconds) {
405} 400}
406 401
407void Thread::AddSchedulingFlag(ThreadSchedFlags flag) { 402void Thread::AddSchedulingFlag(ThreadSchedFlags flag) {
408 const u32 old_state = scheduling_state; 403 const auto old_state = GetRawState();
409 pausing_state |= static_cast<u32>(flag); 404 pausing_state |= static_cast<u32>(flag);
410 const u32 base_scheduling = static_cast<u32>(GetSchedulingStatus()); 405 const auto base_scheduling = GetState();
411 scheduling_state = base_scheduling | pausing_state; 406 thread_state = base_scheduling | static_cast<ThreadState>(pausing_state);
412 KScheduler::OnThreadStateChanged(kernel, this, old_state); 407 KScheduler::OnThreadStateChanged(kernel, this, old_state);
413} 408}
414 409
415void Thread::RemoveSchedulingFlag(ThreadSchedFlags flag) { 410void Thread::RemoveSchedulingFlag(ThreadSchedFlags flag) {
416 const u32 old_state = scheduling_state; 411 const auto old_state = GetRawState();
417 pausing_state &= ~static_cast<u32>(flag); 412 pausing_state &= ~static_cast<u32>(flag);
418 const u32 base_scheduling = static_cast<u32>(GetSchedulingStatus()); 413 const auto base_scheduling = GetState();
419 scheduling_state = base_scheduling | pausing_state; 414 thread_state = base_scheduling | static_cast<ThreadState>(pausing_state);
420 KScheduler::OnThreadStateChanged(kernel, this, old_state); 415 KScheduler::OnThreadStateChanged(kernel, this, old_state);
421} 416}
422 417
423void Thread::SetSchedulingStatus(ThreadSchedStatus new_status) {
424 const u32 old_state = scheduling_state;
425 scheduling_state = (scheduling_state & static_cast<u32>(ThreadSchedMasks::HighMask)) |
426 static_cast<u32>(new_status);
427 KScheduler::OnThreadStateChanged(kernel, this, old_state);
428}
429
430void Thread::SetCurrentPriority(u32 new_priority) {
431 const u32 old_priority = std::exchange(current_priority, new_priority);
432 KScheduler::OnThreadPriorityChanged(kernel, this, kernel.CurrentScheduler()->GetCurrentThread(),
433 old_priority);
434}
435
436ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) { 418ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) {
437 KScopedSchedulerLock lock(kernel); 419 KScopedSchedulerLock lock(kernel);
438 const auto HighestSetCore = [](u64 mask, u32 max_cores) { 420 const auto HighestSetCore = [](u64 mask, u32 max_cores) {
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 11ef29888..6b66c9a0e 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -6,16 +6,21 @@
6 6
7#include <array> 7#include <array>
8#include <functional> 8#include <functional>
9#include <span>
9#include <string> 10#include <string>
10#include <utility> 11#include <utility>
11#include <vector> 12#include <vector>
12 13
14#include <boost/intrusive/list.hpp>
15
13#include "common/common_types.h" 16#include "common/common_types.h"
17#include "common/intrusive_red_black_tree.h"
14#include "common/spin_lock.h" 18#include "common/spin_lock.h"
15#include "core/arm/arm_interface.h" 19#include "core/arm/arm_interface.h"
16#include "core/hle/kernel/k_affinity_mask.h" 20#include "core/hle/kernel/k_affinity_mask.h"
21#include "core/hle/kernel/k_synchronization_object.h"
17#include "core/hle/kernel/object.h" 22#include "core/hle/kernel/object.h"
18#include "core/hle/kernel/synchronization_object.h" 23#include "core/hle/kernel/svc_common.h"
19#include "core/hle/result.h" 24#include "core/hle/result.h"
20 25
21namespace Common { 26namespace Common {
@@ -73,19 +78,24 @@ enum ThreadProcessorId : s32 {
73 (1 << THREADPROCESSORID_2) | (1 << THREADPROCESSORID_3) 78 (1 << THREADPROCESSORID_2) | (1 << THREADPROCESSORID_3)
74}; 79};
75 80
76enum class ThreadStatus { 81enum class ThreadState : u16 {
77 Ready, ///< Ready to run 82 Initialized = 0,
78 Paused, ///< Paused by SetThreadActivity or debug 83 Waiting = 1,
79 WaitHLEEvent, ///< Waiting for hle event to finish 84 Runnable = 2,
80 WaitSleep, ///< Waiting due to a SleepThread SVC 85 Terminated = 3,
81 WaitIPC, ///< Waiting for the reply from an IPC request 86
82 WaitSynch, ///< Waiting due to WaitSynchronization 87 SuspendShift = 4,
83 WaitMutex, ///< Waiting due to an ArbitrateLock svc 88 Mask = (1 << SuspendShift) - 1,
84 WaitCondVar, ///< Waiting due to an WaitProcessWideKey svc 89
85 WaitArb, ///< Waiting due to a SignalToAddress/WaitForAddress svc 90 ProcessSuspended = (1 << (0 + SuspendShift)),
86 Dormant, ///< Created but not yet made ready 91 ThreadSuspended = (1 << (1 + SuspendShift)),
87 Dead ///< Run to completion, or forcefully terminated 92 DebugSuspended = (1 << (2 + SuspendShift)),
93 BacktraceSuspended = (1 << (3 + SuspendShift)),
94 InitSuspended = (1 << (4 + SuspendShift)),
95
96 SuspendFlagMask = ((1 << 5) - 1) << SuspendShift,
88}; 97};
98DECLARE_ENUM_FLAG_OPERATORS(ThreadState);
89 99
90enum class ThreadWakeupReason { 100enum class ThreadWakeupReason {
91 Signal, // The thread was woken up by WakeupAllWaitingThreads due to an object signal. 101 Signal, // The thread was woken up by WakeupAllWaitingThreads due to an object signal.
@@ -97,13 +107,6 @@ enum class ThreadActivity : u32 {
97 Paused = 1, 107 Paused = 1,
98}; 108};
99 109
100enum class ThreadSchedStatus : u32 {
101 None = 0,
102 Paused = 1,
103 Runnable = 2,
104 Exited = 3,
105};
106
107enum class ThreadSchedFlags : u32 { 110enum class ThreadSchedFlags : u32 {
108 ProcessPauseFlag = 1 << 4, 111 ProcessPauseFlag = 1 << 4,
109 ThreadPauseFlag = 1 << 5, 112 ThreadPauseFlag = 1 << 5,
@@ -111,13 +114,20 @@ enum class ThreadSchedFlags : u32 {
111 KernelInitPauseFlag = 1 << 8, 114 KernelInitPauseFlag = 1 << 8,
112}; 115};
113 116
114enum class ThreadSchedMasks : u32 { 117enum class ThreadWaitReasonForDebugging : u32 {
115 LowMask = 0x000f, 118 None, ///< Thread is not waiting
116 HighMask = 0xfff0, 119 Sleep, ///< Thread is waiting due to a SleepThread SVC
117 ForcePauseMask = 0x0070, 120 IPC, ///< Thread is waiting for the reply from an IPC request
121 Synchronization, ///< Thread is waiting due to a WaitSynchronization SVC
122 ConditionVar, ///< Thread is waiting due to a WaitProcessWideKey SVC
123 Arbitration, ///< Thread is waiting due to a SignalToAddress/WaitForAddress SVC
124 Suspended, ///< Thread is waiting due to process suspension
118}; 125};
119 126
120class Thread final : public SynchronizationObject { 127class Thread final : public KSynchronizationObject, public boost::intrusive::list_base_hook<> {
128 friend class KScheduler;
129 friend class Process;
130
121public: 131public:
122 explicit Thread(KernelCore& kernel); 132 explicit Thread(KernelCore& kernel);
123 ~Thread() override; 133 ~Thread() override;
@@ -127,10 +137,6 @@ public:
127 using ThreadContext32 = Core::ARM_Interface::ThreadContext32; 137 using ThreadContext32 = Core::ARM_Interface::ThreadContext32;
128 using ThreadContext64 = Core::ARM_Interface::ThreadContext64; 138 using ThreadContext64 = Core::ARM_Interface::ThreadContext64;
129 139
130 using ThreadSynchronizationObjects = std::vector<std::shared_ptr<SynchronizationObject>>;
131
132 using HLECallback = std::function<bool(std::shared_ptr<Thread> thread)>;
133
134 /** 140 /**
135 * Creates and returns a new thread. The new thread is immediately scheduled 141 * Creates and returns a new thread. The new thread is immediately scheduled
136 * @param system The instance of the whole system 142 * @param system The instance of the whole system
@@ -186,59 +192,54 @@ public:
186 return HANDLE_TYPE; 192 return HANDLE_TYPE;
187 } 193 }
188 194
189 bool ShouldWait(const Thread* thread) const override;
190 void Acquire(Thread* thread) override;
191 bool IsSignaled() const override;
192
193 /** 195 /**
194 * Gets the thread's current priority 196 * Gets the thread's current priority
195 * @return The current thread's priority 197 * @return The current thread's priority
196 */ 198 */
197 u32 GetPriority() const { 199 [[nodiscard]] s32 GetPriority() const {
198 return current_priority; 200 return current_priority;
199 } 201 }
200 202
201 /** 203 /**
204 * Sets the thread's current priority.
205 * @param priority The new priority.
206 */
207 void SetPriority(s32 priority) {
208 current_priority = priority;
209 }
210
211 /**
202 * Gets the thread's nominal priority. 212 * Gets the thread's nominal priority.
203 * @return The current thread's nominal priority. 213 * @return The current thread's nominal priority.
204 */ 214 */
205 u32 GetNominalPriority() const { 215 [[nodiscard]] s32 GetBasePriority() const {
206 return nominal_priority; 216 return base_priority;
207 } 217 }
208 218
209 /** 219 /**
210 * Sets the thread's current priority 220 * Sets the thread's nominal priority.
211 * @param priority The new priority 221 * @param priority The new priority.
212 */ 222 */
213 void SetPriority(u32 priority); 223 void SetBasePriority(u32 priority);
214
215 /// Adds a thread to the list of threads that are waiting for a lock held by this thread.
216 void AddMutexWaiter(std::shared_ptr<Thread> thread);
217
218 /// Removes a thread from the list of threads that are waiting for a lock held by this thread.
219 void RemoveMutexWaiter(std::shared_ptr<Thread> thread);
220
221 /// Recalculates the current priority taking into account priority inheritance.
222 void UpdatePriority();
223 224
224 /// Changes the core that the thread is running or scheduled to run on. 225 /// Changes the core that the thread is running or scheduled to run on.
225 ResultCode SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask); 226 [[nodiscard]] ResultCode SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask);
226 227
227 /** 228 /**
228 * Gets the thread's thread ID 229 * Gets the thread's thread ID
229 * @return The thread's ID 230 * @return The thread's ID
230 */ 231 */
231 u64 GetThreadID() const { 232 [[nodiscard]] u64 GetThreadID() const {
232 return thread_id; 233 return thread_id;
233 } 234 }
234 235
235 /// Resumes a thread from waiting 236 /// Resumes a thread from waiting
236 void ResumeFromWait(); 237 void Wakeup();
237
238 void OnWakeUp();
239 238
240 ResultCode Start(); 239 ResultCode Start();
241 240
241 virtual bool IsSignaled() const override;
242
242 /// Cancels a waiting operation that this thread may or may not be within. 243 /// Cancels a waiting operation that this thread may or may not be within.
243 /// 244 ///
244 /// When the thread is within a waiting state, this will set the thread's 245 /// When the thread is within a waiting state, this will set the thread's
@@ -247,29 +248,20 @@ public:
247 /// 248 ///
248 void CancelWait(); 249 void CancelWait();
249 250
250 void SetSynchronizationResults(SynchronizationObject* object, ResultCode result); 251 void SetSynchronizationResults(KSynchronizationObject* object, ResultCode result);
251 252
252 SynchronizationObject* GetSignalingObject() const { 253 void SetSyncedObject(KSynchronizationObject* object, ResultCode result) {
253 return signaling_object; 254 SetSynchronizationResults(object, result);
254 } 255 }
255 256
256 ResultCode GetSignalingResult() const { 257 ResultCode GetWaitResult(KSynchronizationObject** out) const {
258 *out = signaling_object;
257 return signaling_result; 259 return signaling_result;
258 } 260 }
259 261
260 /** 262 ResultCode GetSignalingResult() const {
261 * Retrieves the index that this particular object occupies in the list of objects 263 return signaling_result;
262 * that the thread passed to WaitSynchronization, starting the search from the last element. 264 }
263 *
264 * It is used to set the output index of WaitSynchronization when the thread is awakened.
265 *
266 * When a thread wakes up due to an object signal, the kernel will use the index of the last
267 * matching object in the wait objects list in case of having multiple instances of the same
268 * object in the list.
269 *
270 * @param object Object to query the index of.
271 */
272 s32 GetSynchronizationObjectIndex(std::shared_ptr<SynchronizationObject> object) const;
273 265
274 /** 266 /**
275 * Stops a thread, invalidating it from further use 267 * Stops a thread, invalidating it from further use
@@ -341,18 +333,22 @@ public:
341 333
342 std::shared_ptr<Common::Fiber>& GetHostContext(); 334 std::shared_ptr<Common::Fiber>& GetHostContext();
343 335
344 ThreadStatus GetStatus() const { 336 ThreadState GetState() const {
345 return status; 337 return thread_state & ThreadState::Mask;
338 }
339
340 ThreadState GetRawState() const {
341 return thread_state;
346 } 342 }
347 343
348 void SetStatus(ThreadStatus new_status); 344 void SetState(ThreadState state);
349 345
350 s64 GetLastScheduledTick() const { 346 s64 GetLastScheduledTick() const {
351 return this->last_scheduled_tick; 347 return last_scheduled_tick;
352 } 348 }
353 349
354 void SetLastScheduledTick(s64 tick) { 350 void SetLastScheduledTick(s64 tick) {
355 this->last_scheduled_tick = tick; 351 last_scheduled_tick = tick;
356 } 352 }
357 353
358 u64 GetTotalCPUTimeTicks() const { 354 u64 GetTotalCPUTimeTicks() const {
@@ -387,98 +383,18 @@ public:
387 return owner_process; 383 return owner_process;
388 } 384 }
389 385
390 const ThreadSynchronizationObjects& GetSynchronizationObjects() const {
391 return *wait_objects;
392 }
393
394 void SetSynchronizationObjects(ThreadSynchronizationObjects* objects) {
395 wait_objects = objects;
396 }
397
398 void ClearSynchronizationObjects() {
399 for (const auto& waiting_object : *wait_objects) {
400 waiting_object->RemoveWaitingThread(SharedFrom(this));
401 }
402 wait_objects->clear();
403 }
404
405 /// Determines whether all the objects this thread is waiting on are ready.
406 bool AllSynchronizationObjectsReady() const;
407
408 const MutexWaitingThreads& GetMutexWaitingThreads() const { 386 const MutexWaitingThreads& GetMutexWaitingThreads() const {
409 return wait_mutex_threads; 387 return wait_mutex_threads;
410 } 388 }
411 389
412 Thread* GetLockOwner() const { 390 Thread* GetLockOwner() const {
413 return lock_owner.get(); 391 return lock_owner;
414 }
415
416 void SetLockOwner(std::shared_ptr<Thread> owner) {
417 lock_owner = std::move(owner);
418 }
419
420 VAddr GetCondVarWaitAddress() const {
421 return condvar_wait_address;
422 }
423
424 void SetCondVarWaitAddress(VAddr address) {
425 condvar_wait_address = address;
426 }
427
428 VAddr GetMutexWaitAddress() const {
429 return mutex_wait_address;
430 }
431
432 void SetMutexWaitAddress(VAddr address) {
433 mutex_wait_address = address;
434 }
435
436 Handle GetWaitHandle() const {
437 return wait_handle;
438 }
439
440 void SetWaitHandle(Handle handle) {
441 wait_handle = handle;
442 }
443
444 VAddr GetArbiterWaitAddress() const {
445 return arb_wait_address;
446 }
447
448 void SetArbiterWaitAddress(VAddr address) {
449 arb_wait_address = address;
450 }
451
452 bool HasHLECallback() const {
453 return hle_callback != nullptr;
454 }
455
456 void SetHLECallback(HLECallback callback) {
457 hle_callback = std::move(callback);
458 }
459
460 void SetHLETimeEvent(Handle time_event) {
461 hle_time_event = time_event;
462 }
463
464 void SetHLESyncObject(SynchronizationObject* object) {
465 hle_object = object;
466 }
467
468 Handle GetHLETimeEvent() const {
469 return hle_time_event;
470 }
471
472 SynchronizationObject* GetHLESyncObject() const {
473 return hle_object;
474 } 392 }
475 393
476 void InvalidateHLECallback() { 394 void SetLockOwner(Thread* owner) {
477 SetHLECallback(nullptr); 395 lock_owner = owner;
478 } 396 }
479 397
480 bool InvokeHLECallback(std::shared_ptr<Thread> thread);
481
482 u32 GetIdealCore() const { 398 u32 GetIdealCore() const {
483 return ideal_core; 399 return ideal_core;
484 } 400 }
@@ -493,20 +409,11 @@ public:
493 ResultCode Sleep(s64 nanoseconds); 409 ResultCode Sleep(s64 nanoseconds);
494 410
495 s64 GetYieldScheduleCount() const { 411 s64 GetYieldScheduleCount() const {
496 return this->schedule_count; 412 return schedule_count;
497 } 413 }
498 414
499 void SetYieldScheduleCount(s64 count) { 415 void SetYieldScheduleCount(s64 count) {
500 this->schedule_count = count; 416 schedule_count = count;
501 }
502
503 ThreadSchedStatus GetSchedulingStatus() const {
504 return static_cast<ThreadSchedStatus>(scheduling_state &
505 static_cast<u32>(ThreadSchedMasks::LowMask));
506 }
507
508 bool IsRunnable() const {
509 return scheduling_state == static_cast<u32>(ThreadSchedStatus::Runnable);
510 } 417 }
511 418
512 bool IsRunning() const { 419 bool IsRunning() const {
@@ -517,36 +424,32 @@ public:
517 is_running = value; 424 is_running = value;
518 } 425 }
519 426
520 bool IsSyncCancelled() const { 427 bool IsWaitCancelled() const {
521 return is_sync_cancelled; 428 return is_sync_cancelled;
522 } 429 }
523 430
524 void SetSyncCancelled(bool value) { 431 void ClearWaitCancelled() {
525 is_sync_cancelled = value; 432 is_sync_cancelled = false;
526 } 433 }
527 434
528 Handle GetGlobalHandle() const { 435 Handle GetGlobalHandle() const {
529 return global_handle; 436 return global_handle;
530 } 437 }
531 438
532 bool IsWaitingForArbitration() const { 439 bool IsCancellable() const {
533 return waiting_for_arbitration; 440 return is_cancellable;
534 } 441 }
535 442
536 void WaitForArbitration(bool set) { 443 void SetCancellable() {
537 waiting_for_arbitration = set; 444 is_cancellable = true;
538 } 445 }
539 446
540 bool IsWaitingSync() const { 447 void ClearCancellable() {
541 return is_waiting_on_sync; 448 is_cancellable = false;
542 } 449 }
543 450
544 void SetWaitingSync(bool is_waiting) { 451 bool IsTerminationRequested() const {
545 is_waiting_on_sync = is_waiting; 452 return will_be_terminated || GetRawState() == ThreadState::Terminated;
546 }
547
548 bool IsPendingTermination() const {
549 return will_be_terminated || GetSchedulingStatus() == ThreadSchedStatus::Exited;
550 } 453 }
551 454
552 bool IsPaused() const { 455 bool IsPaused() const {
@@ -578,21 +481,21 @@ public:
578 constexpr QueueEntry() = default; 481 constexpr QueueEntry() = default;
579 482
580 constexpr void Initialize() { 483 constexpr void Initialize() {
581 this->prev = nullptr; 484 prev = nullptr;
582 this->next = nullptr; 485 next = nullptr;
583 } 486 }
584 487
585 constexpr Thread* GetPrev() const { 488 constexpr Thread* GetPrev() const {
586 return this->prev; 489 return prev;
587 } 490 }
588 constexpr Thread* GetNext() const { 491 constexpr Thread* GetNext() const {
589 return this->next; 492 return next;
590 } 493 }
591 constexpr void SetPrev(Thread* thread) { 494 constexpr void SetPrev(Thread* thread) {
592 this->prev = thread; 495 prev = thread;
593 } 496 }
594 constexpr void SetNext(Thread* thread) { 497 constexpr void SetNext(Thread* thread) {
595 this->next = thread; 498 next = thread;
596 } 499 }
597 500
598 private: 501 private:
@@ -601,11 +504,11 @@ public:
601 }; 504 };
602 505
603 QueueEntry& GetPriorityQueueEntry(s32 core) { 506 QueueEntry& GetPriorityQueueEntry(s32 core) {
604 return this->per_core_priority_queue_entry[core]; 507 return per_core_priority_queue_entry[core];
605 } 508 }
606 509
607 const QueueEntry& GetPriorityQueueEntry(s32 core) const { 510 const QueueEntry& GetPriorityQueueEntry(s32 core) const {
608 return this->per_core_priority_queue_entry[core]; 511 return per_core_priority_queue_entry[core];
609 } 512 }
610 513
611 s32 GetDisableDispatchCount() const { 514 s32 GetDisableDispatchCount() const {
@@ -622,24 +525,170 @@ public:
622 disable_count--; 525 disable_count--;
623 } 526 }
624 527
528 void SetWaitReasonForDebugging(ThreadWaitReasonForDebugging reason) {
529 wait_reason_for_debugging = reason;
530 }
531
532 [[nodiscard]] ThreadWaitReasonForDebugging GetWaitReasonForDebugging() const {
533 return wait_reason_for_debugging;
534 }
535
536 void SetWaitObjectsForDebugging(const std::span<KSynchronizationObject*>& objects) {
537 wait_objects_for_debugging.clear();
538 wait_objects_for_debugging.reserve(objects.size());
539 for (const auto& object : objects) {
540 wait_objects_for_debugging.emplace_back(object);
541 }
542 }
543
544 [[nodiscard]] const std::vector<KSynchronizationObject*>& GetWaitObjectsForDebugging() const {
545 return wait_objects_for_debugging;
546 }
547
548 void SetMutexWaitAddressForDebugging(VAddr address) {
549 mutex_wait_address_for_debugging = address;
550 }
551
552 [[nodiscard]] VAddr GetMutexWaitAddressForDebugging() const {
553 return mutex_wait_address_for_debugging;
554 }
555
556 void AddWaiter(Thread* thread);
557
558 void RemoveWaiter(Thread* thread);
559
560 [[nodiscard]] Thread* RemoveWaiterByKey(s32* out_num_waiters, VAddr key);
561
562 [[nodiscard]] VAddr GetAddressKey() const {
563 return address_key;
564 }
565
566 [[nodiscard]] u32 GetAddressKeyValue() const {
567 return address_key_value;
568 }
569
570 void SetAddressKey(VAddr key) {
571 address_key = key;
572 }
573
574 void SetAddressKey(VAddr key, u32 val) {
575 address_key = key;
576 address_key_value = val;
577 }
578
625private: 579private:
626 friend class GlobalSchedulerContext; 580 static constexpr size_t PriorityInheritanceCountMax = 10;
627 friend class KScheduler; 581 union SyncObjectBuffer {
628 friend class Process; 582 std::array<KSynchronizationObject*, Svc::ArgumentHandleCountMax> sync_objects{};
583 std::array<Handle,
584 Svc::ArgumentHandleCountMax*(sizeof(KSynchronizationObject*) / sizeof(Handle))>
585 handles;
586 constexpr SyncObjectBuffer() {}
587 };
588 static_assert(sizeof(SyncObjectBuffer::sync_objects) == sizeof(SyncObjectBuffer::handles));
589
590 struct ConditionVariableComparator {
591 struct LightCompareType {
592 u64 cv_key{};
593 s32 priority{};
594
595 [[nodiscard]] constexpr u64 GetConditionVariableKey() const {
596 return cv_key;
597 }
598
599 [[nodiscard]] constexpr s32 GetPriority() const {
600 return priority;
601 }
602 };
603
604 template <typename T>
605 requires(
606 std::same_as<T, Thread> ||
607 std::same_as<T, LightCompareType>) static constexpr int Compare(const T& lhs,
608 const Thread& rhs) {
609 const uintptr_t l_key = lhs.GetConditionVariableKey();
610 const uintptr_t r_key = rhs.GetConditionVariableKey();
611
612 if (l_key < r_key) {
613 // Sort first by key
614 return -1;
615 } else if (l_key == r_key && lhs.GetPriority() < rhs.GetPriority()) {
616 // And then by priority.
617 return -1;
618 } else {
619 return 1;
620 }
621 }
622 };
623
624 Common::IntrusiveRedBlackTreeNode condvar_arbiter_tree_node{};
625
626 using ConditionVariableThreadTreeTraits =
627 Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&Thread::condvar_arbiter_tree_node>;
628 using ConditionVariableThreadTree =
629 ConditionVariableThreadTreeTraits::TreeType<ConditionVariableComparator>;
630
631public:
632 using ConditionVariableThreadTreeType = ConditionVariableThreadTree;
633
634 [[nodiscard]] uintptr_t GetConditionVariableKey() const {
635 return condvar_key;
636 }
637
638 [[nodiscard]] uintptr_t GetAddressArbiterKey() const {
639 return condvar_key;
640 }
629 641
630 void SetSchedulingStatus(ThreadSchedStatus new_status); 642 void SetConditionVariable(ConditionVariableThreadTree* tree, VAddr address, uintptr_t cv_key,
643 u32 value) {
644 condvar_tree = tree;
645 condvar_key = cv_key;
646 address_key = address;
647 address_key_value = value;
648 }
649
650 void ClearConditionVariable() {
651 condvar_tree = nullptr;
652 }
653
654 [[nodiscard]] bool IsWaitingForConditionVariable() const {
655 return condvar_tree != nullptr;
656 }
657
658 void SetAddressArbiter(ConditionVariableThreadTree* tree, uintptr_t address) {
659 condvar_tree = tree;
660 condvar_key = address;
661 }
662
663 void ClearAddressArbiter() {
664 condvar_tree = nullptr;
665 }
666
667 [[nodiscard]] bool IsWaitingForAddressArbiter() const {
668 return condvar_tree != nullptr;
669 }
670
671 [[nodiscard]] ConditionVariableThreadTree* GetConditionVariableTree() const {
672 return condvar_tree;
673 }
674
675 [[nodiscard]] bool HasWaiters() const {
676 return !waiter_list.empty();
677 }
678
679private:
631 void AddSchedulingFlag(ThreadSchedFlags flag); 680 void AddSchedulingFlag(ThreadSchedFlags flag);
632 void RemoveSchedulingFlag(ThreadSchedFlags flag); 681 void RemoveSchedulingFlag(ThreadSchedFlags flag);
633 682 void AddWaiterImpl(Thread* thread);
634 void SetCurrentPriority(u32 new_priority); 683 void RemoveWaiterImpl(Thread* thread);
684 static void RestorePriority(KernelCore& kernel, Thread* thread);
635 685
636 Common::SpinLock context_guard{}; 686 Common::SpinLock context_guard{};
637 ThreadContext32 context_32{}; 687 ThreadContext32 context_32{};
638 ThreadContext64 context_64{}; 688 ThreadContext64 context_64{};
639 std::shared_ptr<Common::Fiber> host_context{}; 689 std::shared_ptr<Common::Fiber> host_context{};
640 690
641 ThreadStatus status = ThreadStatus::Dormant; 691 ThreadState thread_state = ThreadState::Initialized;
642 u32 scheduling_state = 0;
643 692
644 u64 thread_id = 0; 693 u64 thread_id = 0;
645 694
@@ -652,11 +701,11 @@ private:
652 /// Nominal thread priority, as set by the emulated application. 701 /// Nominal thread priority, as set by the emulated application.
653 /// The nominal priority is the thread priority without priority 702 /// The nominal priority is the thread priority without priority
654 /// inheritance taken into account. 703 /// inheritance taken into account.
655 u32 nominal_priority = 0; 704 s32 base_priority{};
656 705
657 /// Current thread priority. This may change over the course of the 706 /// Current thread priority. This may change over the course of the
658 /// thread's lifetime in order to facilitate priority inheritance. 707 /// thread's lifetime in order to facilitate priority inheritance.
659 u32 current_priority = 0; 708 s32 current_priority{};
660 709
661 u64 total_cpu_time_ticks = 0; ///< Total CPU running ticks. 710 u64 total_cpu_time_ticks = 0; ///< Total CPU running ticks.
662 s64 schedule_count{}; 711 s64 schedule_count{};
@@ -671,37 +720,27 @@ private:
671 Process* owner_process; 720 Process* owner_process;
672 721
673 /// Objects that the thread is waiting on, in the same order as they were 722 /// Objects that the thread is waiting on, in the same order as they were
674 /// passed to WaitSynchronization. 723 /// passed to WaitSynchronization. This is used for debugging only.
675 ThreadSynchronizationObjects* wait_objects; 724 std::vector<KSynchronizationObject*> wait_objects_for_debugging;
676 725
677 SynchronizationObject* signaling_object; 726 /// The current mutex wait address. This is used for debugging only.
727 VAddr mutex_wait_address_for_debugging{};
728
729 /// The reason the thread is waiting. This is used for debugging only.
730 ThreadWaitReasonForDebugging wait_reason_for_debugging{};
731
732 KSynchronizationObject* signaling_object;
678 ResultCode signaling_result{RESULT_SUCCESS}; 733 ResultCode signaling_result{RESULT_SUCCESS};
679 734
680 /// List of threads that are waiting for a mutex that is held by this thread. 735 /// List of threads that are waiting for a mutex that is held by this thread.
681 MutexWaitingThreads wait_mutex_threads; 736 MutexWaitingThreads wait_mutex_threads;
682 737
683 /// Thread that owns the lock that this thread is waiting for. 738 /// Thread that owns the lock that this thread is waiting for.
684 std::shared_ptr<Thread> lock_owner; 739 Thread* lock_owner{};
685
686 /// If waiting on a ConditionVariable, this is the ConditionVariable address
687 VAddr condvar_wait_address = 0;
688 /// If waiting on a Mutex, this is the mutex address
689 VAddr mutex_wait_address = 0;
690 /// The handle used to wait for the mutex.
691 Handle wait_handle = 0;
692
693 /// If waiting for an AddressArbiter, this is the address being waited on.
694 VAddr arb_wait_address{0};
695 bool waiting_for_arbitration{};
696 740
697 /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. 741 /// Handle used as userdata to reference this object when inserting into the CoreTiming queue.
698 Handle global_handle = 0; 742 Handle global_handle = 0;
699 743
700 /// Callback for HLE Events
701 HLECallback hle_callback;
702 Handle hle_time_event;
703 SynchronizationObject* hle_object;
704
705 KScheduler* scheduler = nullptr; 744 KScheduler* scheduler = nullptr;
706 745
707 std::array<QueueEntry, Core::Hardware::NUM_CPU_CORES> per_core_priority_queue_entry{}; 746 std::array<QueueEntry, Core::Hardware::NUM_CPU_CORES> per_core_priority_queue_entry{};
@@ -714,7 +753,7 @@ private:
714 753
715 u32 pausing_state = 0; 754 u32 pausing_state = 0;
716 bool is_running = false; 755 bool is_running = false;
717 bool is_waiting_on_sync = false; 756 bool is_cancellable = false;
718 bool is_sync_cancelled = false; 757 bool is_sync_cancelled = false;
719 758
720 bool is_continuous_on_svc = false; 759 bool is_continuous_on_svc = false;
@@ -725,6 +764,18 @@ private:
725 764
726 bool was_running = false; 765 bool was_running = false;
727 766
767 bool signaled{};
768
769 ConditionVariableThreadTree* condvar_tree{};
770 uintptr_t condvar_key{};
771 VAddr address_key{};
772 u32 address_key_value{};
773 s32 num_kernel_waiters{};
774
775 using WaiterList = boost::intrusive::list<Thread>;
776 WaiterList waiter_list{};
777 WaiterList pinned_waiter_list{};
778
728 std::string name; 779 std::string name;
729}; 780};
730 781
diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp
index 79628e2b4..832edd629 100644
--- a/src/core/hle/kernel/time_manager.cpp
+++ b/src/core/hle/kernel/time_manager.cpp
@@ -18,12 +18,10 @@ TimeManager::TimeManager(Core::System& system_) : system{system_} {
18 time_manager_event_type = Core::Timing::CreateEvent( 18 time_manager_event_type = Core::Timing::CreateEvent(
19 "Kernel::TimeManagerCallback", 19 "Kernel::TimeManagerCallback",
20 [this](std::uintptr_t thread_handle, std::chrono::nanoseconds) { 20 [this](std::uintptr_t thread_handle, std::chrono::nanoseconds) {
21 const KScopedSchedulerLock lock(system.Kernel());
22 const auto proper_handle = static_cast<Handle>(thread_handle);
23
24 std::shared_ptr<Thread> thread; 21 std::shared_ptr<Thread> thread;
25 { 22 {
26 std::lock_guard lock{mutex}; 23 std::lock_guard lock{mutex};
24 const auto proper_handle = static_cast<Handle>(thread_handle);
27 if (cancelled_events[proper_handle]) { 25 if (cancelled_events[proper_handle]) {
28 return; 26 return;
29 } 27 }
@@ -32,7 +30,7 @@ TimeManager::TimeManager(Core::System& system_) : system{system_} {
32 30
33 if (thread) { 31 if (thread) {
34 // Thread can be null if process has exited 32 // Thread can be null if process has exited
35 thread->OnWakeUp(); 33 thread->Wakeup();
36 } 34 }
37 }); 35 });
38} 36}
@@ -42,8 +40,7 @@ void TimeManager::ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64
42 event_handle = timetask->GetGlobalHandle(); 40 event_handle = timetask->GetGlobalHandle();
43 if (nanoseconds > 0) { 41 if (nanoseconds > 0) {
44 ASSERT(timetask); 42 ASSERT(timetask);
45 ASSERT(timetask->GetStatus() != ThreadStatus::Ready); 43 ASSERT(timetask->GetState() != ThreadState::Runnable);
46 ASSERT(timetask->GetStatus() != ThreadStatus::WaitMutex);
47 system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{nanoseconds}, 44 system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{nanoseconds},
48 time_manager_event_type, event_handle); 45 time_manager_event_type, event_handle);
49 } else { 46 } else {
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index cb13210e5..c9808060a 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -560,14 +560,14 @@ void ISelfController::GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequest
560 560
561AppletMessageQueue::AppletMessageQueue(Kernel::KernelCore& kernel) { 561AppletMessageQueue::AppletMessageQueue(Kernel::KernelCore& kernel) {
562 on_new_message = 562 on_new_message =
563 Kernel::WritableEvent::CreateEventPair(kernel, "AMMessageQueue:OnMessageRecieved"); 563 Kernel::WritableEvent::CreateEventPair(kernel, "AMMessageQueue:OnMessageReceived");
564 on_operation_mode_changed = 564 on_operation_mode_changed =
565 Kernel::WritableEvent::CreateEventPair(kernel, "AMMessageQueue:OperationModeChanged"); 565 Kernel::WritableEvent::CreateEventPair(kernel, "AMMessageQueue:OperationModeChanged");
566} 566}
567 567
568AppletMessageQueue::~AppletMessageQueue() = default; 568AppletMessageQueue::~AppletMessageQueue() = default;
569 569
570const std::shared_ptr<Kernel::ReadableEvent>& AppletMessageQueue::GetMesssageRecieveEvent() const { 570const std::shared_ptr<Kernel::ReadableEvent>& AppletMessageQueue::GetMessageReceiveEvent() const {
571 return on_new_message.readable; 571 return on_new_message.readable;
572} 572}
573 573
@@ -675,7 +675,7 @@ void ICommonStateGetter::GetEventHandle(Kernel::HLERequestContext& ctx) {
675 675
676 IPC::ResponseBuilder rb{ctx, 2, 1}; 676 IPC::ResponseBuilder rb{ctx, 2, 1};
677 rb.Push(RESULT_SUCCESS); 677 rb.Push(RESULT_SUCCESS);
678 rb.PushCopyObjects(msg_queue->GetMesssageRecieveEvent()); 678 rb.PushCopyObjects(msg_queue->GetMessageReceiveEvent());
679} 679}
680 680
681void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) { 681void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index b1da0d081..f51aca1af 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -55,7 +55,7 @@ public:
55 explicit AppletMessageQueue(Kernel::KernelCore& kernel); 55 explicit AppletMessageQueue(Kernel::KernelCore& kernel);
56 ~AppletMessageQueue(); 56 ~AppletMessageQueue();
57 57
58 const std::shared_ptr<Kernel::ReadableEvent>& GetMesssageRecieveEvent() const; 58 const std::shared_ptr<Kernel::ReadableEvent>& GetMessageReceiveEvent() const;
59 const std::shared_ptr<Kernel::ReadableEvent>& GetOperationModeChangedEvent() const; 59 const std::shared_ptr<Kernel::ReadableEvent>& GetOperationModeChangedEvent() const;
60 void PushMessage(AppletMessage msg); 60 void PushMessage(AppletMessage msg);
61 AppletMessage PopMessage(); 61 AppletMessage PopMessage();
diff --git a/src/core/hle/service/am/applets/error.cpp b/src/core/hle/service/am/applets/error.cpp
index d85505082..0c8b632e8 100644
--- a/src/core/hle/service/am/applets/error.cpp
+++ b/src/core/hle/service/am/applets/error.cpp
@@ -20,9 +20,9 @@ namespace Service::AM::Applets {
20struct ShowError { 20struct ShowError {
21 u8 mode; 21 u8 mode;
22 bool jump; 22 bool jump;
23 INSERT_UNION_PADDING_BYTES(4); 23 INSERT_PADDING_BYTES_NOINIT(4);
24 bool use_64bit_error_code; 24 bool use_64bit_error_code;
25 INSERT_UNION_PADDING_BYTES(1); 25 INSERT_PADDING_BYTES_NOINIT(1);
26 u64 error_code_64; 26 u64 error_code_64;
27 u32 error_code_32; 27 u32 error_code_32;
28}; 28};
@@ -32,7 +32,7 @@ static_assert(sizeof(ShowError) == 0x14, "ShowError has incorrect size.");
32struct ShowErrorRecord { 32struct ShowErrorRecord {
33 u8 mode; 33 u8 mode;
34 bool jump; 34 bool jump;
35 INSERT_UNION_PADDING_BYTES(6); 35 INSERT_PADDING_BYTES_NOINIT(6);
36 u64 error_code_64; 36 u64 error_code_64;
37 u64 posix_time; 37 u64 posix_time;
38}; 38};
@@ -41,7 +41,7 @@ static_assert(sizeof(ShowErrorRecord) == 0x18, "ShowErrorRecord has incorrect si
41struct SystemErrorArg { 41struct SystemErrorArg {
42 u8 mode; 42 u8 mode;
43 bool jump; 43 bool jump;
44 INSERT_UNION_PADDING_BYTES(6); 44 INSERT_PADDING_BYTES_NOINIT(6);
45 u64 error_code_64; 45 u64 error_code_64;
46 std::array<char, 8> language_code; 46 std::array<char, 8> language_code;
47 std::array<char, 0x800> main_text; 47 std::array<char, 0x800> main_text;
@@ -52,7 +52,7 @@ static_assert(sizeof(SystemErrorArg) == 0x1018, "SystemErrorArg has incorrect si
52struct ApplicationErrorArg { 52struct ApplicationErrorArg {
53 u8 mode; 53 u8 mode;
54 bool jump; 54 bool jump;
55 INSERT_UNION_PADDING_BYTES(6); 55 INSERT_PADDING_BYTES_NOINIT(6);
56 u32 error_code; 56 u32 error_code;
57 std::array<char, 8> language_code; 57 std::array<char, 8> language_code;
58 std::array<char, 0x800> main_text; 58 std::array<char, 0x800> main_text;
diff --git a/src/core/hle/service/apm/interface.cpp b/src/core/hle/service/apm/interface.cpp
index 298f6d520..0bff97a37 100644
--- a/src/core/hle/service/apm/interface.cpp
+++ b/src/core/hle/service/apm/interface.cpp
@@ -56,7 +56,7 @@ APM::APM(Core::System& system_, std::shared_ptr<Module> apm_, Controller& contro
56 static const FunctionInfo functions[] = { 56 static const FunctionInfo functions[] = {
57 {0, &APM::OpenSession, "OpenSession"}, 57 {0, &APM::OpenSession, "OpenSession"},
58 {1, &APM::GetPerformanceMode, "GetPerformanceMode"}, 58 {1, &APM::GetPerformanceMode, "GetPerformanceMode"},
59 {6, nullptr, "IsCpuOverclockEnabled"}, 59 {6, &APM::IsCpuOverclockEnabled, "IsCpuOverclockEnabled"},
60 }; 60 };
61 RegisterHandlers(functions); 61 RegisterHandlers(functions);
62} 62}
@@ -78,6 +78,14 @@ void APM::GetPerformanceMode(Kernel::HLERequestContext& ctx) {
78 rb.PushEnum(controller.GetCurrentPerformanceMode()); 78 rb.PushEnum(controller.GetCurrentPerformanceMode());
79} 79}
80 80
81void APM::IsCpuOverclockEnabled(Kernel::HLERequestContext& ctx) {
82 LOG_WARNING(Service_APM, "(STUBBED) called");
83
84 IPC::ResponseBuilder rb{ctx, 3};
85 rb.Push(RESULT_SUCCESS);
86 rb.Push(false);
87}
88
81APM_Sys::APM_Sys(Core::System& system_, Controller& controller_) 89APM_Sys::APM_Sys(Core::System& system_, Controller& controller_)
82 : ServiceFramework{system_, "apm:sys"}, controller{controller_} { 90 : ServiceFramework{system_, "apm:sys"}, controller{controller_} {
83 // clang-format off 91 // clang-format off
diff --git a/src/core/hle/service/apm/interface.h b/src/core/hle/service/apm/interface.h
index 7d57c4978..063ad5308 100644
--- a/src/core/hle/service/apm/interface.h
+++ b/src/core/hle/service/apm/interface.h
@@ -20,6 +20,7 @@ public:
20private: 20private:
21 void OpenSession(Kernel::HLERequestContext& ctx); 21 void OpenSession(Kernel::HLERequestContext& ctx);
22 void GetPerformanceMode(Kernel::HLERequestContext& ctx); 22 void GetPerformanceMode(Kernel::HLERequestContext& ctx);
23 void IsCpuOverclockEnabled(Kernel::HLERequestContext& ctx);
23 24
24 std::shared_ptr<Module> apm; 25 std::shared_ptr<Module> apm;
25 Controller& controller; 26 Controller& controller;
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index 5557da72e..641bcadea 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -190,12 +190,6 @@ private:
190 void GetDeviceState(Kernel::HLERequestContext& ctx) { 190 void GetDeviceState(Kernel::HLERequestContext& ctx) {
191 LOG_DEBUG(Service_NFP, "called"); 191 LOG_DEBUG(Service_NFP, "called");
192 192
193 auto nfc_event = nfp_interface.GetNFCEvent();
194 if (!nfc_event->ShouldWait(&ctx.GetThread()) && !has_attached_handle) {
195 device_state = DeviceState::TagFound;
196 nfc_event->Clear();
197 }
198
199 IPC::ResponseBuilder rb{ctx, 3}; 193 IPC::ResponseBuilder rb{ctx, 3};
200 rb.Push(RESULT_SUCCESS); 194 rb.Push(RESULT_SUCCESS);
201 rb.Push<u32>(static_cast<u32>(device_state)); 195 rb.Push<u32>(static_cast<u32>(device_state));
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index d8735491c..36970f828 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -11,8 +11,9 @@
11 11
12namespace Service::Nvidia::Devices { 12namespace Service::Nvidia::Devices {
13 13
14nvhost_nvdec::nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev) 14nvhost_nvdec::nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
15 : nvhost_nvdec_common(system, std::move(nvmap_dev)) {} 15 SyncpointManager& syncpoint_manager)
16 : nvhost_nvdec_common(system, std::move(nvmap_dev), syncpoint_manager) {}
16nvhost_nvdec::~nvhost_nvdec() = default; 17nvhost_nvdec::~nvhost_nvdec() = default;
17 18
18NvResult nvhost_nvdec::Ioctl1(Ioctl command, const std::vector<u8>& input, 19NvResult nvhost_nvdec::Ioctl1(Ioctl command, const std::vector<u8>& input,
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
index 79b8b6de1..77ef53cdd 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
@@ -11,7 +11,8 @@ namespace Service::Nvidia::Devices {
11 11
12class nvhost_nvdec final : public nvhost_nvdec_common { 12class nvhost_nvdec final : public nvhost_nvdec_common {
13public: 13public:
14 explicit nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); 14 explicit nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
15 SyncpointManager& syncpoint_manager);
15 ~nvhost_nvdec() override; 16 ~nvhost_nvdec() override;
16 17
17 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; 18 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
index b49cecb42..4898dc27a 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
@@ -11,6 +11,7 @@
11#include "core/core.h" 11#include "core/core.h"
12#include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h" 12#include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h"
13#include "core/hle/service/nvdrv/devices/nvmap.h" 13#include "core/hle/service/nvdrv/devices/nvmap.h"
14#include "core/hle/service/nvdrv/syncpoint_manager.h"
14#include "core/memory.h" 15#include "core/memory.h"
15#include "video_core/memory_manager.h" 16#include "video_core/memory_manager.h"
16#include "video_core/renderer_base.h" 17#include "video_core/renderer_base.h"
@@ -36,8 +37,9 @@ std::size_t WriteVectors(std::vector<u8>& dst, const std::vector<T>& src, std::s
36} 37}
37} // Anonymous namespace 38} // Anonymous namespace
38 39
39nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system, std::shared_ptr<nvmap> nvmap_dev) 40nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
40 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} 41 SyncpointManager& syncpoint_manager)
42 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)), syncpoint_manager(syncpoint_manager) {}
41nvhost_nvdec_common::~nvhost_nvdec_common() = default; 43nvhost_nvdec_common::~nvhost_nvdec_common() = default;
42 44
43NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) { 45NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) {
@@ -71,10 +73,15 @@ NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u
71 offset = SpliceVectors(input, wait_checks, params.syncpoint_count, offset); 73 offset = SpliceVectors(input, wait_checks, params.syncpoint_count, offset);
72 offset = SpliceVectors(input, fences, params.fence_count, offset); 74 offset = SpliceVectors(input, fences, params.fence_count, offset);
73 75
74 // TODO(ameerj): For async gpu, utilize fences for syncpoint 'max' increment
75
76 auto& gpu = system.GPU(); 76 auto& gpu = system.GPU();
77 77 if (gpu.UseNvdec()) {
78 for (std::size_t i = 0; i < syncpt_increments.size(); i++) {
79 const SyncptIncr& syncpt_incr = syncpt_increments[i];
80 fences[i].id = syncpt_incr.id;
81 fences[i].value =
82 syncpoint_manager.IncreaseSyncpoint(syncpt_incr.id, syncpt_incr.increments);
83 }
84 }
78 for (const auto& cmd_buffer : command_buffers) { 85 for (const auto& cmd_buffer : command_buffers) {
79 auto object = nvmap_dev->GetObject(cmd_buffer.memory_id); 86 auto object = nvmap_dev->GetObject(cmd_buffer.memory_id);
80 ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;); 87 ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;);
@@ -89,7 +96,13 @@ NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u
89 cmdlist.size() * sizeof(u32)); 96 cmdlist.size() * sizeof(u32));
90 gpu.PushCommandBuffer(cmdlist); 97 gpu.PushCommandBuffer(cmdlist);
91 } 98 }
99 if (gpu.UseNvdec()) {
92 100
101 fences[0].value = syncpoint_manager.IncreaseSyncpoint(fences[0].id, 1);
102
103 Tegra::ChCommandHeaderList cmdlist{{(4 << 28) | fences[0].id}};
104 gpu.PushCommandBuffer(cmdlist);
105 }
93 std::memcpy(output.data(), &params, sizeof(IoctlSubmit)); 106 std::memcpy(output.data(), &params, sizeof(IoctlSubmit));
94 // Some games expect command_buffers to be written back 107 // Some games expect command_buffers to be written back
95 offset = sizeof(IoctlSubmit); 108 offset = sizeof(IoctlSubmit);
@@ -98,6 +111,7 @@ NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u
98 offset = WriteVectors(output, reloc_shifts, offset); 111 offset = WriteVectors(output, reloc_shifts, offset);
99 offset = WriteVectors(output, syncpt_increments, offset); 112 offset = WriteVectors(output, syncpt_increments, offset);
100 offset = WriteVectors(output, wait_checks, offset); 113 offset = WriteVectors(output, wait_checks, offset);
114 offset = WriteVectors(output, fences, offset);
101 115
102 return NvResult::Success; 116 return NvResult::Success;
103} 117}
@@ -107,9 +121,10 @@ NvResult nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::ve
107 std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint)); 121 std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint));
108 LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param); 122 LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param);
109 123
110 // We found that implementing this causes deadlocks with async gpu, along with degraded 124 if (device_syncpoints[params.param] == 0 && system.GPU().UseNvdec()) {
111 // performance. TODO: RE the nvdec async implementation 125 device_syncpoints[params.param] = syncpoint_manager.AllocateSyncpoint();
112 params.value = 0; 126 }
127 params.value = device_syncpoints[params.param];
113 std::memcpy(output.data(), &params, sizeof(IoctlGetSyncpoint)); 128 std::memcpy(output.data(), &params, sizeof(IoctlGetSyncpoint));
114 129
115 return NvResult::Success; 130 return NvResult::Success;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
index d9f95ba58..4c9d4ba41 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
@@ -10,12 +10,16 @@
10#include "common/swap.h" 10#include "common/swap.h"
11#include "core/hle/service/nvdrv/devices/nvdevice.h" 11#include "core/hle/service/nvdrv/devices/nvdevice.h"
12 12
13namespace Service::Nvidia::Devices { 13namespace Service::Nvidia {
14class SyncpointManager;
15
16namespace Devices {
14class nvmap; 17class nvmap;
15 18
16class nvhost_nvdec_common : public nvdevice { 19class nvhost_nvdec_common : public nvdevice {
17public: 20public:
18 explicit nvhost_nvdec_common(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); 21 explicit nvhost_nvdec_common(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
22 SyncpointManager& syncpoint_manager);
19 ~nvhost_nvdec_common() override; 23 ~nvhost_nvdec_common() override;
20 24
21protected: 25protected:
@@ -157,8 +161,10 @@ protected:
157 s32_le nvmap_fd{}; 161 s32_le nvmap_fd{};
158 u32_le submit_timeout{}; 162 u32_le submit_timeout{};
159 std::shared_ptr<nvmap> nvmap_dev; 163 std::shared_ptr<nvmap> nvmap_dev;
160 164 SyncpointManager& syncpoint_manager;
165 std::array<u32, MaxSyncPoints> device_syncpoints{};
161 // This is expected to be ordered, therefore we must use a map, not unordered_map 166 // This is expected to be ordered, therefore we must use a map, not unordered_map
162 std::map<GPUVAddr, BufferMap> buffer_mappings; 167 std::map<GPUVAddr, BufferMap> buffer_mappings;
163}; 168};
164}; // namespace Service::Nvidia::Devices 169}; // namespace Devices
170} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
index 805fe86ae..72499654c 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
@@ -10,8 +10,9 @@
10#include "video_core/renderer_base.h" 10#include "video_core/renderer_base.h"
11 11
12namespace Service::Nvidia::Devices { 12namespace Service::Nvidia::Devices {
13nvhost_vic::nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev) 13nvhost_vic::nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
14 : nvhost_nvdec_common(system, std::move(nvmap_dev)) {} 14 SyncpointManager& syncpoint_manager)
15 : nvhost_nvdec_common(system, std::move(nvmap_dev), syncpoint_manager) {}
15 16
16nvhost_vic::~nvhost_vic() = default; 17nvhost_vic::~nvhost_vic() = default;
17 18
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
index b2e11f4d4..f401c61fa 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
@@ -7,11 +7,11 @@
7#include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h" 7#include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h"
8 8
9namespace Service::Nvidia::Devices { 9namespace Service::Nvidia::Devices {
10class nvmap;
11 10
12class nvhost_vic final : public nvhost_nvdec_common { 11class nvhost_vic final : public nvhost_nvdec_common {
13public: 12public:
14 explicit nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); 13 explicit nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
14 SyncpointManager& syncpoint_manager);
15 ~nvhost_vic(); 15 ~nvhost_vic();
16 16
17 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; 17 NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index e03195afe..620c18728 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -55,9 +55,11 @@ Module::Module(Core::System& system) : syncpoint_manager{system.GPU()} {
55 devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(system, nvmap_dev); 55 devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(system, nvmap_dev);
56 devices["/dev/nvhost-ctrl"] = 56 devices["/dev/nvhost-ctrl"] =
57 std::make_shared<Devices::nvhost_ctrl>(system, events_interface, syncpoint_manager); 57 std::make_shared<Devices::nvhost_ctrl>(system, events_interface, syncpoint_manager);
58 devices["/dev/nvhost-nvdec"] = std::make_shared<Devices::nvhost_nvdec>(system, nvmap_dev); 58 devices["/dev/nvhost-nvdec"] =
59 std::make_shared<Devices::nvhost_nvdec>(system, nvmap_dev, syncpoint_manager);
59 devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>(system); 60 devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>(system);
60 devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>(system, nvmap_dev); 61 devices["/dev/nvhost-vic"] =
62 std::make_shared<Devices::nvhost_vic>(system, nvmap_dev, syncpoint_manager);
61} 63}
62 64
63Module::~Module() = default; 65Module::~Module() = default;
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index c8c6a4d64..5578181a4 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -26,10 +26,10 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer)
26 LOG_WARNING(Service, "Adding graphics buffer {}", slot); 26 LOG_WARNING(Service, "Adding graphics buffer {}", slot);
27 27
28 { 28 {
29 std::unique_lock lock{queue_mutex}; 29 std::unique_lock lock{free_buffers_mutex};
30 free_buffers.push_back(slot); 30 free_buffers.push_back(slot);
31 } 31 }
32 condition.notify_one(); 32 free_buffers_condition.notify_one();
33 33
34 buffers[slot] = { 34 buffers[slot] = {
35 .slot = slot, 35 .slot = slot,
@@ -48,8 +48,8 @@ std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::Dequeue
48 u32 height) { 48 u32 height) {
49 // Wait for first request before trying to dequeue 49 // Wait for first request before trying to dequeue
50 { 50 {
51 std::unique_lock lock{queue_mutex}; 51 std::unique_lock lock{free_buffers_mutex};
52 condition.wait(lock, [this] { return !free_buffers.empty() || !is_connect; }); 52 free_buffers_condition.wait(lock, [this] { return !free_buffers.empty() || !is_connect; });
53 } 53 }
54 54
55 if (!is_connect) { 55 if (!is_connect) {
@@ -58,7 +58,7 @@ std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::Dequeue
58 return std::nullopt; 58 return std::nullopt;
59 } 59 }
60 60
61 std::unique_lock lock{queue_mutex}; 61 std::unique_lock lock{free_buffers_mutex};
62 62
63 auto f_itr = free_buffers.begin(); 63 auto f_itr = free_buffers.begin();
64 auto slot = buffers.size(); 64 auto slot = buffers.size();
@@ -100,6 +100,7 @@ void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
100 buffers[slot].crop_rect = crop_rect; 100 buffers[slot].crop_rect = crop_rect;
101 buffers[slot].swap_interval = swap_interval; 101 buffers[slot].swap_interval = swap_interval;
102 buffers[slot].multi_fence = multi_fence; 102 buffers[slot].multi_fence = multi_fence;
103 std::unique_lock lock{queue_sequence_mutex};
103 queue_sequence.push_back(slot); 104 queue_sequence.push_back(slot);
104} 105}
105 106
@@ -113,15 +114,16 @@ void BufferQueue::CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& mult
113 buffers[slot].swap_interval = 0; 114 buffers[slot].swap_interval = 0;
114 115
115 { 116 {
116 std::unique_lock lock{queue_mutex}; 117 std::unique_lock lock{free_buffers_mutex};
117 free_buffers.push_back(slot); 118 free_buffers.push_back(slot);
118 } 119 }
119 condition.notify_one(); 120 free_buffers_condition.notify_one();
120 121
121 buffer_wait_event.writable->Signal(); 122 buffer_wait_event.writable->Signal();
122} 123}
123 124
124std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() { 125std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() {
126 std::unique_lock lock{queue_sequence_mutex};
125 std::size_t buffer_slot = buffers.size(); 127 std::size_t buffer_slot = buffers.size();
126 // Iterate to find a queued buffer matching the requested slot. 128 // Iterate to find a queued buffer matching the requested slot.
127 while (buffer_slot == buffers.size() && !queue_sequence.empty()) { 129 while (buffer_slot == buffers.size() && !queue_sequence.empty()) {
@@ -147,27 +149,29 @@ void BufferQueue::ReleaseBuffer(u32 slot) {
147 149
148 buffers[slot].status = Buffer::Status::Free; 150 buffers[slot].status = Buffer::Status::Free;
149 { 151 {
150 std::unique_lock lock{queue_mutex}; 152 std::unique_lock lock{free_buffers_mutex};
151 free_buffers.push_back(slot); 153 free_buffers.push_back(slot);
152 } 154 }
153 condition.notify_one(); 155 free_buffers_condition.notify_one();
154 156
155 buffer_wait_event.writable->Signal(); 157 buffer_wait_event.writable->Signal();
156} 158}
157 159
158void BufferQueue::Connect() { 160void BufferQueue::Connect() {
161 std::unique_lock lock{queue_sequence_mutex};
159 queue_sequence.clear(); 162 queue_sequence.clear();
160 id = 1;
161 layer_id = 1;
162 is_connect = true; 163 is_connect = true;
163} 164}
164 165
165void BufferQueue::Disconnect() { 166void BufferQueue::Disconnect() {
166 buffers.fill({}); 167 buffers.fill({});
167 queue_sequence.clear(); 168 {
169 std::unique_lock lock{queue_sequence_mutex};
170 queue_sequence.clear();
171 }
168 buffer_wait_event.writable->Signal(); 172 buffer_wait_event.writable->Signal();
169 is_connect = false; 173 is_connect = false;
170 condition.notify_one(); 174 free_buffers_condition.notify_one();
171} 175}
172 176
173u32 BufferQueue::Query(QueryType type) { 177u32 BufferQueue::Query(QueryType type) {
@@ -176,9 +180,11 @@ u32 BufferQueue::Query(QueryType type) {
176 switch (type) { 180 switch (type) {
177 case QueryType::NativeWindowFormat: 181 case QueryType::NativeWindowFormat:
178 return static_cast<u32>(PixelFormat::RGBA8888); 182 return static_cast<u32>(PixelFormat::RGBA8888);
183 case QueryType::NativeWindowWidth:
184 case QueryType::NativeWindowHeight:
185 break;
179 } 186 }
180 187 UNIMPLEMENTED_MSG("Unimplemented query type={}", type);
181 UNIMPLEMENTED();
182 return 0; 188 return 0;
183} 189}
184 190
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
index a2f60d9eb..ad7469277 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ b/src/core/hle/service/nvflinger/buffer_queue.h
@@ -129,8 +129,10 @@ private:
129 std::list<u32> queue_sequence; 129 std::list<u32> queue_sequence;
130 Kernel::EventPair buffer_wait_event; 130 Kernel::EventPair buffer_wait_event;
131 131
132 std::mutex queue_mutex; 132 std::mutex free_buffers_mutex;
133 std::condition_variable condition; 133 std::condition_variable free_buffers_condition;
134
135 std::mutex queue_sequence_mutex;
134}; 136};
135 137
136} // namespace Service::NVFlinger 138} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 4b3581949..ceaa93d28 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -38,6 +38,10 @@ void NVFlinger::SplitVSync() {
38 system.RegisterHostThread(); 38 system.RegisterHostThread();
39 std::string name = "yuzu:VSyncThread"; 39 std::string name = "yuzu:VSyncThread";
40 MicroProfileOnThreadCreate(name.c_str()); 40 MicroProfileOnThreadCreate(name.c_str());
41
42 // Cleanup
43 SCOPE_EXIT({ MicroProfileOnThreadExit(); });
44
41 Common::SetCurrentThreadName(name.c_str()); 45 Common::SetCurrentThreadName(name.c_str());
42 Common::SetCurrentThreadPriority(Common::ThreadPriority::High); 46 Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
43 s64 delay = 0; 47 s64 delay = 0;
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index 4da69f503..2b91a89d1 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -139,9 +139,6 @@ void SM::GetService(Kernel::HLERequestContext& ctx) {
139 server_port->AppendPendingSession(server); 139 server_port->AppendPendingSession(server);
140 } 140 }
141 141
142 // Wake the threads waiting on the ServerPort
143 server_port->Signal();
144
145 LOG_DEBUG(Service_SM, "called service={} -> session={}", name, client->GetObjectId()); 142 LOG_DEBUG(Service_SM, "called service={} -> session={}", name, client->GetObjectId());
146 IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; 143 IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
147 rb.Push(RESULT_SUCCESS); 144 rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/sockets/sockets_translate.cpp b/src/core/hle/service/sockets/sockets_translate.cpp
index c822d21b8..ca61d72ca 100644
--- a/src/core/hle/service/sockets/sockets_translate.cpp
+++ b/src/core/hle/service/sockets/sockets_translate.cpp
@@ -64,6 +64,7 @@ Network::Type Translate(Type type) {
64 return Network::Type::DGRAM; 64 return Network::Type::DGRAM;
65 default: 65 default:
66 UNIMPLEMENTED_MSG("Unimplemented type={}", type); 66 UNIMPLEMENTED_MSG("Unimplemented type={}", type);
67 return Network::Type{};
67 } 68 }
68} 69}
69 70
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index d91c15561..e4f5fd40c 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -185,6 +185,10 @@ constexpr std::array<const char*, 66> RESULT_MESSAGES{
185 "The INI file contains more than the maximum allowable number of KIP files.", 185 "The INI file contains more than the maximum allowable number of KIP files.",
186}; 186};
187 187
188std::string GetResultStatusString(ResultStatus status) {
189 return RESULT_MESSAGES.at(static_cast<std::size_t>(status));
190}
191
188std::ostream& operator<<(std::ostream& os, ResultStatus status) { 192std::ostream& operator<<(std::ostream& os, ResultStatus status) {
189 os << RESULT_MESSAGES.at(static_cast<std::size_t>(status)); 193 os << RESULT_MESSAGES.at(static_cast<std::size_t>(status));
190 return os; 194 return os;
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 36e79e71d..b2e5b13de 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -135,6 +135,7 @@ enum class ResultStatus : u16 {
135 ErrorINITooManyKIPs, 135 ErrorINITooManyKIPs,
136}; 136};
137 137
138std::string GetResultStatusString(ResultStatus status);
138std::ostream& operator<<(std::ostream& os, ResultStatus status); 139std::ostream& operator<<(std::ostream& os, ResultStatus status);
139 140
140/// Interface for loading an application 141/// Interface for loading an application
diff --git a/src/core/settings.h b/src/core/settings.h
index 0cd3c0c84..a324530bd 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -131,6 +131,7 @@ struct Values {
131 131
132 bool cpuopt_unsafe_unfuse_fma; 132 bool cpuopt_unsafe_unfuse_fma;
133 bool cpuopt_unsafe_reduce_fp_error; 133 bool cpuopt_unsafe_reduce_fp_error;
134 bool cpuopt_unsafe_inaccurate_nan;
134 135
135 // Renderer 136 // Renderer
136 Setting<RendererBackend> renderer_backend; 137 Setting<RendererBackend> renderer_backend;
@@ -221,7 +222,7 @@ struct Values {
221 bool disable_macro_jit; 222 bool disable_macro_jit;
222 bool extended_logging; 223 bool extended_logging;
223 224
224 // Misceallaneous 225 // Miscellaneous
225 std::string log_filter; 226 std::string log_filter;
226 bool use_dev_keys; 227 bool use_dev_keys;
227 228
diff --git a/src/input_common/gcadapter/gc_adapter.h b/src/input_common/gcadapter/gc_adapter.h
index f1256c9da..7a6c545bd 100644
--- a/src/input_common/gcadapter/gc_adapter.h
+++ b/src/input_common/gcadapter/gc_adapter.h
@@ -120,17 +120,17 @@ private:
120 /// For use in initialization, querying devices to find the adapter 120 /// For use in initialization, querying devices to find the adapter
121 void Setup(); 121 void Setup();
122 122
123 /// Resets status of all GC controller devices to a disconected state 123 /// Resets status of all GC controller devices to a disconnected state
124 void ResetDevices(); 124 void ResetDevices();
125 125
126 /// Resets status of device connected to a disconected state 126 /// Resets status of device connected to a disconnected state
127 void ResetDevice(std::size_t port); 127 void ResetDevice(std::size_t port);
128 128
129 /// Returns true if we successfully gain access to GC Adapter 129 /// Returns true if we successfully gain access to GC Adapter
130 bool CheckDeviceAccess(); 130 bool CheckDeviceAccess();
131 131
132 /// Captures GC Adapter endpoint address 132 /// Captures GC Adapter endpoint address
133 /// Returns true if the endpoind was set correctly 133 /// Returns true if the endpoint was set correctly
134 bool GetGCEndpoint(libusb_device* device); 134 bool GetGCEndpoint(libusb_device* device);
135 135
136 /// For shutting down, clear all data, join all threads, release usb 136 /// For shutting down, clear all data, join all threads, release usb
diff --git a/src/input_common/motion_input.cpp b/src/input_common/motion_input.cpp
index f77ba535d..6a65f175e 100644
--- a/src/input_common/motion_input.cpp
+++ b/src/input_common/motion_input.cpp
@@ -129,7 +129,7 @@ void MotionInput::UpdateOrientation(u64 elapsed_time) {
129 rad_gyro += ki * integral_error; 129 rad_gyro += ki * integral_error;
130 rad_gyro += kd * derivative_error; 130 rad_gyro += kd * derivative_error;
131 } else { 131 } else {
132 // Give more weight to acelerometer values to compensate for the lack of gyro 132 // Give more weight to accelerometer values to compensate for the lack of gyro
133 rad_gyro += 35.0f * kp * real_error; 133 rad_gyro += 35.0f * kp * real_error;
134 rad_gyro += 10.0f * ki * integral_error; 134 rad_gyro += 10.0f * ki * integral_error;
135 rad_gyro += 10.0f * kd * derivative_error; 135 rad_gyro += 10.0f * kd * derivative_error;
diff --git a/src/input_common/mouse/mouse_input.h b/src/input_common/mouse/mouse_input.h
index 65e64bee7..58803c1bf 100644
--- a/src/input_common/mouse/mouse_input.h
+++ b/src/input_common/mouse/mouse_input.h
@@ -20,7 +20,7 @@ enum class MouseButton {
20 Left, 20 Left,
21 Wheel, 21 Wheel,
22 Right, 22 Right,
23 Foward, 23 Forward,
24 Backward, 24 Backward,
25 Undefined, 25 Undefined,
26}; 26};
diff --git a/src/input_common/udp/udp.cpp b/src/input_common/udp/udp.cpp
index 8686a059c..c5da27a38 100644
--- a/src/input_common/udp/udp.cpp
+++ b/src/input_common/udp/udp.cpp
@@ -28,14 +28,14 @@ private:
28 mutable std::mutex mutex; 28 mutable std::mutex mutex;
29}; 29};
30 30
31/// A motion device factory that creates motion devices from JC Adapter 31/// A motion device factory that creates motion devices from a UDP client
32UDPMotionFactory::UDPMotionFactory(std::shared_ptr<CemuhookUDP::Client> client_) 32UDPMotionFactory::UDPMotionFactory(std::shared_ptr<CemuhookUDP::Client> client_)
33 : client(std::move(client_)) {} 33 : client(std::move(client_)) {}
34 34
35/** 35/**
36 * Creates motion device 36 * Creates motion device
37 * @param params contains parameters for creating the device: 37 * @param params contains parameters for creating the device:
38 * - "port": the nth jcpad on the adapter 38 * - "port": the UDP port number
39 */ 39 */
40std::unique_ptr<Input::MotionDevice> UDPMotionFactory::Create(const Common::ParamPackage& params) { 40std::unique_ptr<Input::MotionDevice> UDPMotionFactory::Create(const Common::ParamPackage& params) {
41 auto ip = params.Get("ip", "127.0.0.1"); 41 auto ip = params.Get("ip", "127.0.0.1");
@@ -90,14 +90,14 @@ private:
90 mutable std::mutex mutex; 90 mutable std::mutex mutex;
91}; 91};
92 92
93/// A motion device factory that creates motion devices from JC Adapter 93/// A motion device factory that creates motion devices from a UDP client
94UDPTouchFactory::UDPTouchFactory(std::shared_ptr<CemuhookUDP::Client> client_) 94UDPTouchFactory::UDPTouchFactory(std::shared_ptr<CemuhookUDP::Client> client_)
95 : client(std::move(client_)) {} 95 : client(std::move(client_)) {}
96 96
97/** 97/**
98 * Creates motion device 98 * Creates motion device
99 * @param params contains parameters for creating the device: 99 * @param params contains parameters for creating the device:
100 * - "port": the nth jcpad on the adapter 100 * - "port": the UDP port number
101 */ 101 */
102std::unique_ptr<Input::TouchDevice> UDPTouchFactory::Create(const Common::ParamPackage& params) { 102std::unique_ptr<Input::TouchDevice> UDPTouchFactory::Create(const Common::ParamPackage& params) {
103 auto ip = params.Get("ip", "127.0.0.1"); 103 auto ip = params.Get("ip", "127.0.0.1");
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 8a606b448..33fa89583 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -1,6 +1,5 @@
1add_executable(tests 1add_executable(tests
2 common/bit_field.cpp 2 common/bit_field.cpp
3 common/bit_utils.cpp
4 common/fibers.cpp 3 common/fibers.cpp
5 common/param_package.cpp 4 common/param_package.cpp
6 common/ring_buffer.cpp 5 common/ring_buffer.cpp
diff --git a/src/tests/common/bit_utils.cpp b/src/tests/common/bit_utils.cpp
deleted file mode 100644
index 479b5995a..000000000
--- a/src/tests/common/bit_utils.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <catch2/catch.hpp>
6#include <math.h>
7#include "common/bit_util.h"
8
9namespace Common {
10
11TEST_CASE("BitUtils::CountTrailingZeroes", "[common]") {
12 REQUIRE(Common::CountTrailingZeroes32(0) == 32);
13 REQUIRE(Common::CountTrailingZeroes64(0) == 64);
14 REQUIRE(Common::CountTrailingZeroes32(9) == 0);
15 REQUIRE(Common::CountTrailingZeroes32(8) == 3);
16 REQUIRE(Common::CountTrailingZeroes32(0x801000) == 12);
17 REQUIRE(Common::CountTrailingZeroes64(9) == 0);
18 REQUIRE(Common::CountTrailingZeroes64(8) == 3);
19 REQUIRE(Common::CountTrailingZeroes64(0x801000) == 12);
20 REQUIRE(Common::CountTrailingZeroes64(0x801000000000UL) == 36);
21}
22
23} // namespace Common
diff --git a/src/tests/common/fibers.cpp b/src/tests/common/fibers.cpp
index 4757dd2b4..d94492fc6 100644
--- a/src/tests/common/fibers.cpp
+++ b/src/tests/common/fibers.cpp
@@ -207,7 +207,7 @@ static void ThreadStart2_2(u32 id, TestControl2& test_control) {
207} 207}
208 208
209/** This test checks for fiber thread exchange configuration and validates that fibers are 209/** This test checks for fiber thread exchange configuration and validates that fibers are
210 * that a fiber has been succesfully transfered from one thread to another and that the TLS 210 * that a fiber has been successfully transferred from one thread to another and that the TLS
211 * region of the thread is kept while changing fibers. 211 * region of the thread is kept while changing fibers.
212 */ 212 */
213TEST_CASE("Fibers::InterExchange", "[common]") { 213TEST_CASE("Fibers::InterExchange", "[common]") {
@@ -299,7 +299,7 @@ static void ThreadStart3(u32 id, TestControl3& test_control) {
299} 299}
300 300
301/** This test checks for one two threads racing for starting the same fiber. 301/** This test checks for one two threads racing for starting the same fiber.
302 * It checks execution occured in an ordered manner and by no time there were 302 * It checks execution occurred in an ordered manner and by no time there were
303 * two contexts at the same time. 303 * two contexts at the same time.
304 */ 304 */
305TEST_CASE("Fibers::StartRace", "[common]") { 305TEST_CASE("Fibers::StartRace", "[common]") {
diff --git a/src/tests/common/ring_buffer.cpp b/src/tests/common/ring_buffer.cpp
index c883c4d56..54def22da 100644
--- a/src/tests/common/ring_buffer.cpp
+++ b/src/tests/common/ring_buffer.cpp
@@ -20,60 +20,60 @@ TEST_CASE("RingBuffer: Basic Tests", "[common]") {
20 for (std::size_t i = 0; i < 4; i++) { 20 for (std::size_t i = 0; i < 4; i++) {
21 const char elem = static_cast<char>(i); 21 const char elem = static_cast<char>(i);
22 const std::size_t count = buf.Push(&elem, 1); 22 const std::size_t count = buf.Push(&elem, 1);
23 REQUIRE(count == 1); 23 REQUIRE(count == 1U);
24 } 24 }
25 25
26 REQUIRE(buf.Size() == 4); 26 REQUIRE(buf.Size() == 4U);
27 27
28 // Pushing values into a full ring buffer should fail. 28 // Pushing values into a full ring buffer should fail.
29 { 29 {
30 const char elem = static_cast<char>(42); 30 const char elem = static_cast<char>(42);
31 const std::size_t count = buf.Push(&elem, 1); 31 const std::size_t count = buf.Push(&elem, 1);
32 REQUIRE(count == 0); 32 REQUIRE(count == 0U);
33 } 33 }
34 34
35 REQUIRE(buf.Size() == 4); 35 REQUIRE(buf.Size() == 4U);
36 36
37 // Popping multiple values from a ring buffer with values should succeed. 37 // Popping multiple values from a ring buffer with values should succeed.
38 { 38 {
39 const std::vector<char> popped = buf.Pop(2); 39 const std::vector<char> popped = buf.Pop(2);
40 REQUIRE(popped.size() == 2); 40 REQUIRE(popped.size() == 2U);
41 REQUIRE(popped[0] == 0); 41 REQUIRE(popped[0] == 0);
42 REQUIRE(popped[1] == 1); 42 REQUIRE(popped[1] == 1);
43 } 43 }
44 44
45 REQUIRE(buf.Size() == 2); 45 REQUIRE(buf.Size() == 2U);
46 46
47 // Popping a single value from a ring buffer with values should succeed. 47 // Popping a single value from a ring buffer with values should succeed.
48 { 48 {
49 const std::vector<char> popped = buf.Pop(1); 49 const std::vector<char> popped = buf.Pop(1);
50 REQUIRE(popped.size() == 1); 50 REQUIRE(popped.size() == 1U);
51 REQUIRE(popped[0] == 2); 51 REQUIRE(popped[0] == 2);
52 } 52 }
53 53
54 REQUIRE(buf.Size() == 1); 54 REQUIRE(buf.Size() == 1U);
55 55
56 // Pushing more values than space available should partially suceed. 56 // Pushing more values than space available should partially suceed.
57 { 57 {
58 std::vector<char> to_push(6); 58 std::vector<char> to_push(6);
59 std::iota(to_push.begin(), to_push.end(), 88); 59 std::iota(to_push.begin(), to_push.end(), 88);
60 const std::size_t count = buf.Push(to_push); 60 const std::size_t count = buf.Push(to_push);
61 REQUIRE(count == 3); 61 REQUIRE(count == 3U);
62 } 62 }
63 63
64 REQUIRE(buf.Size() == 4); 64 REQUIRE(buf.Size() == 4U);
65 65
66 // Doing an unlimited pop should pop all values. 66 // Doing an unlimited pop should pop all values.
67 { 67 {
68 const std::vector<char> popped = buf.Pop(); 68 const std::vector<char> popped = buf.Pop();
69 REQUIRE(popped.size() == 4); 69 REQUIRE(popped.size() == 4U);
70 REQUIRE(popped[0] == 3); 70 REQUIRE(popped[0] == 3);
71 REQUIRE(popped[1] == 88); 71 REQUIRE(popped[1] == 88);
72 REQUIRE(popped[2] == 89); 72 REQUIRE(popped[2] == 89);
73 REQUIRE(popped[3] == 90); 73 REQUIRE(popped[3] == 90);
74 } 74 }
75 75
76 REQUIRE(buf.Size() == 0); 76 REQUIRE(buf.Size() == 0U);
77} 77}
78 78
79TEST_CASE("RingBuffer: Threaded Test", "[common]") { 79TEST_CASE("RingBuffer: Threaded Test", "[common]") {
@@ -93,7 +93,7 @@ TEST_CASE("RingBuffer: Threaded Test", "[common]") {
93 std::size_t i = 0; 93 std::size_t i = 0;
94 while (i < count) { 94 while (i < count) {
95 if (const std::size_t c = buf.Push(&value[0], 1); c > 0) { 95 if (const std::size_t c = buf.Push(&value[0], 1); c > 0) {
96 REQUIRE(c == 1); 96 REQUIRE(c == 1U);
97 i++; 97 i++;
98 next_value(value); 98 next_value(value);
99 } else { 99 } else {
@@ -108,7 +108,7 @@ TEST_CASE("RingBuffer: Threaded Test", "[common]") {
108 std::size_t i = 0; 108 std::size_t i = 0;
109 while (i < count) { 109 while (i < count) {
110 if (const std::vector<char> v = buf.Pop(1); v.size() > 0) { 110 if (const std::vector<char> v = buf.Pop(1); v.size() > 0) {
111 REQUIRE(v.size() == 2); 111 REQUIRE(v.size() == 2U);
112 REQUIRE(v[0] == value[0]); 112 REQUIRE(v[0] == value[0]);
113 REQUIRE(v[1] == value[1]); 113 REQUIRE(v[1] == value[1]);
114 i++; 114 i++;
@@ -123,7 +123,7 @@ TEST_CASE("RingBuffer: Threaded Test", "[common]") {
123 producer.join(); 123 producer.join();
124 consumer.join(); 124 consumer.join();
125 125
126 REQUIRE(buf.Size() == 0); 126 REQUIRE(buf.Size() == 0U);
127 printf("RingBuffer: Threaded Test: full: %zu, empty: %zu\n", full, empty); 127 printf("RingBuffer: Threaded Test: full: %zu, empty: %zu\n", full, empty);
128} 128}
129 129
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index e050f9aed..7a20d3a79 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -115,8 +115,6 @@ add_library(video_core STATIC
115 renderer_vulkan/fixed_pipeline_state.h 115 renderer_vulkan/fixed_pipeline_state.h
116 renderer_vulkan/maxwell_to_vk.cpp 116 renderer_vulkan/maxwell_to_vk.cpp
117 renderer_vulkan/maxwell_to_vk.h 117 renderer_vulkan/maxwell_to_vk.h
118 renderer_vulkan/nsight_aftermath_tracker.cpp
119 renderer_vulkan/nsight_aftermath_tracker.h
120 renderer_vulkan/renderer_vulkan.h 118 renderer_vulkan/renderer_vulkan.h
121 renderer_vulkan/renderer_vulkan.cpp 119 renderer_vulkan/renderer_vulkan.cpp
122 renderer_vulkan/vk_blit_screen.cpp 120 renderer_vulkan/vk_blit_screen.cpp
@@ -131,16 +129,12 @@ add_library(video_core STATIC
131 renderer_vulkan/vk_compute_pipeline.h 129 renderer_vulkan/vk_compute_pipeline.h
132 renderer_vulkan/vk_descriptor_pool.cpp 130 renderer_vulkan/vk_descriptor_pool.cpp
133 renderer_vulkan/vk_descriptor_pool.h 131 renderer_vulkan/vk_descriptor_pool.h
134 renderer_vulkan/vk_device.cpp
135 renderer_vulkan/vk_device.h
136 renderer_vulkan/vk_fence_manager.cpp 132 renderer_vulkan/vk_fence_manager.cpp
137 renderer_vulkan/vk_fence_manager.h 133 renderer_vulkan/vk_fence_manager.h
138 renderer_vulkan/vk_graphics_pipeline.cpp 134 renderer_vulkan/vk_graphics_pipeline.cpp
139 renderer_vulkan/vk_graphics_pipeline.h 135 renderer_vulkan/vk_graphics_pipeline.h
140 renderer_vulkan/vk_master_semaphore.cpp 136 renderer_vulkan/vk_master_semaphore.cpp
141 renderer_vulkan/vk_master_semaphore.h 137 renderer_vulkan/vk_master_semaphore.h
142 renderer_vulkan/vk_memory_manager.cpp
143 renderer_vulkan/vk_memory_manager.h
144 renderer_vulkan/vk_pipeline_cache.cpp 138 renderer_vulkan/vk_pipeline_cache.cpp
145 renderer_vulkan/vk_pipeline_cache.h 139 renderer_vulkan/vk_pipeline_cache.h
146 renderer_vulkan/vk_query_cache.cpp 140 renderer_vulkan/vk_query_cache.cpp
@@ -167,8 +161,6 @@ add_library(video_core STATIC
167 renderer_vulkan/vk_texture_cache.h 161 renderer_vulkan/vk_texture_cache.h
168 renderer_vulkan/vk_update_descriptor.cpp 162 renderer_vulkan/vk_update_descriptor.cpp
169 renderer_vulkan/vk_update_descriptor.h 163 renderer_vulkan/vk_update_descriptor.h
170 renderer_vulkan/wrapper.cpp
171 renderer_vulkan/wrapper.h
172 shader_cache.h 164 shader_cache.h
173 shader_notify.cpp 165 shader_notify.cpp
174 shader_notify.h 166 shader_notify.h
@@ -257,6 +249,22 @@ add_library(video_core STATIC
257 textures/texture.h 249 textures/texture.h
258 video_core.cpp 250 video_core.cpp
259 video_core.h 251 video_core.h
252 vulkan_common/vulkan_debug_callback.cpp
253 vulkan_common/vulkan_debug_callback.h
254 vulkan_common/vulkan_device.cpp
255 vulkan_common/vulkan_device.h
256 vulkan_common/vulkan_instance.cpp
257 vulkan_common/vulkan_instance.h
258 vulkan_common/vulkan_library.cpp
259 vulkan_common/vulkan_library.h
260 vulkan_common/vulkan_memory_allocator.cpp
261 vulkan_common/vulkan_memory_allocator.h
262 vulkan_common/vulkan_surface.cpp
263 vulkan_common/vulkan_surface.h
264 vulkan_common/vulkan_wrapper.cpp
265 vulkan_common/vulkan_wrapper.h
266 vulkan_common/nsight_aftermath_tracker.cpp
267 vulkan_common/nsight_aftermath_tracker.h
260) 268)
261 269
262create_target_directory_groups(video_core) 270create_target_directory_groups(video_core)
@@ -304,9 +312,7 @@ else()
304 -Werror=pessimizing-move 312 -Werror=pessimizing-move
305 -Werror=redundant-move 313 -Werror=redundant-move
306 -Werror=shadow 314 -Werror=shadow
307 -Werror=switch
308 -Werror=type-limits 315 -Werror=type-limits
309 -Werror=unused-variable
310 316
311 $<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess> 317 $<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess>
312 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter> 318 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
diff --git a/src/video_core/cdma_pusher.cpp b/src/video_core/cdma_pusher.cpp
index e3e7432f7..33b3c060b 100644
--- a/src/video_core/cdma_pusher.cpp
+++ b/src/video_core/cdma_pusher.cpp
@@ -18,10 +18,10 @@
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19// 19//
20 20
21#include <bit>
21#include "command_classes/host1x.h" 22#include "command_classes/host1x.h"
22#include "command_classes/nvdec.h" 23#include "command_classes/nvdec.h"
23#include "command_classes/vic.h" 24#include "command_classes/vic.h"
24#include "common/bit_util.h"
25#include "video_core/cdma_pusher.h" 25#include "video_core/cdma_pusher.h"
26#include "video_core/command_classes/nvdec_common.h" 26#include "video_core/command_classes/nvdec_common.h"
27#include "video_core/engines/maxwell_3d.h" 27#include "video_core/engines/maxwell_3d.h"
@@ -33,8 +33,7 @@ CDmaPusher::CDmaPusher(GPU& gpu_)
33 : gpu{gpu_}, nvdec_processor(std::make_shared<Nvdec>(gpu)), 33 : gpu{gpu_}, nvdec_processor(std::make_shared<Nvdec>(gpu)),
34 vic_processor(std::make_unique<Vic>(gpu, nvdec_processor)), 34 vic_processor(std::make_unique<Vic>(gpu, nvdec_processor)),
35 host1x_processor(std::make_unique<Host1x>(gpu)), 35 host1x_processor(std::make_unique<Host1x>(gpu)),
36 nvdec_sync(std::make_unique<SyncptIncrManager>(gpu)), 36 sync_manager(std::make_unique<SyncptIncrManager>(gpu)) {}
37 vic_sync(std::make_unique<SyncptIncrManager>(gpu)) {}
38 37
39CDmaPusher::~CDmaPusher() = default; 38CDmaPusher::~CDmaPusher() = default;
40 39
@@ -57,7 +56,7 @@ void CDmaPusher::Step() {
57 56
58 for (const u32 value : values) { 57 for (const u32 value : values) {
59 if (mask != 0) { 58 if (mask != 0) {
60 const u32 lbs = Common::CountTrailingZeroes32(mask); 59 const auto lbs = static_cast<u32>(std::countr_zero(mask));
61 mask &= ~(1U << lbs); 60 mask &= ~(1U << lbs);
62 ExecuteCommand(static_cast<u32>(offset + lbs), value); 61 ExecuteCommand(static_cast<u32>(offset + lbs), value);
63 continue; 62 continue;
@@ -110,10 +109,10 @@ void CDmaPusher::ExecuteCommand(u32 state_offset, u32 data) {
110 const auto syncpoint_id = static_cast<u32>(data & 0xFF); 109 const auto syncpoint_id = static_cast<u32>(data & 0xFF);
111 const auto cond = static_cast<u32>((data >> 8) & 0xFF); 110 const auto cond = static_cast<u32>((data >> 8) & 0xFF);
112 if (cond == 0) { 111 if (cond == 0) {
113 nvdec_sync->Increment(syncpoint_id); 112 sync_manager->Increment(syncpoint_id);
114 } else { 113 } else {
115 nvdec_sync->IncrementWhenDone(static_cast<u32>(current_class), syncpoint_id); 114 sync_manager->SignalDone(
116 nvdec_sync->SignalDone(syncpoint_id); 115 sync_manager->IncrementWhenDone(static_cast<u32>(current_class), syncpoint_id));
117 } 116 }
118 break; 117 break;
119 } 118 }
@@ -135,10 +134,10 @@ void CDmaPusher::ExecuteCommand(u32 state_offset, u32 data) {
135 const auto syncpoint_id = static_cast<u32>(data & 0xFF); 134 const auto syncpoint_id = static_cast<u32>(data & 0xFF);
136 const auto cond = static_cast<u32>((data >> 8) & 0xFF); 135 const auto cond = static_cast<u32>((data >> 8) & 0xFF);
137 if (cond == 0) { 136 if (cond == 0) {
138 vic_sync->Increment(syncpoint_id); 137 sync_manager->Increment(syncpoint_id);
139 } else { 138 } else {
140 vic_sync->IncrementWhenDone(static_cast<u32>(current_class), syncpoint_id); 139 sync_manager->SignalDone(
141 vic_sync->SignalDone(syncpoint_id); 140 sync_manager->IncrementWhenDone(static_cast<u32>(current_class), syncpoint_id));
142 } 141 }
143 break; 142 break;
144 } 143 }
diff --git a/src/video_core/cdma_pusher.h b/src/video_core/cdma_pusher.h
index 0db1cd646..e5f212c1a 100644
--- a/src/video_core/cdma_pusher.h
+++ b/src/video_core/cdma_pusher.h
@@ -116,19 +116,17 @@ private:
116 void ThiStateWrite(ThiRegisters& state, u32 state_offset, const std::vector<u32>& arguments); 116 void ThiStateWrite(ThiRegisters& state, u32 state_offset, const std::vector<u32>& arguments);
117 117
118 GPU& gpu; 118 GPU& gpu;
119 119 std::shared_ptr<Tegra::Nvdec> nvdec_processor;
120 std::shared_ptr<Nvdec> nvdec_processor; 120 std::unique_ptr<Tegra::Vic> vic_processor;
121 std::unique_ptr<Vic> vic_processor; 121 std::unique_ptr<Tegra::Host1x> host1x_processor;
122 std::unique_ptr<Host1x> host1x_processor; 122 std::unique_ptr<SyncptIncrManager> sync_manager;
123 std::unique_ptr<SyncptIncrManager> nvdec_sync;
124 std::unique_ptr<SyncptIncrManager> vic_sync;
125 ChClassId current_class{}; 123 ChClassId current_class{};
126 ThiRegisters vic_thi_state{}; 124 ThiRegisters vic_thi_state{};
127 ThiRegisters nvdec_thi_state{}; 125 ThiRegisters nvdec_thi_state{};
128 126
129 s32 count{}; 127 s32 count{};
130 s32 offset{}; 128 s32 offset{};
131 s32 mask{}; 129 u32 mask{};
132 bool incrementing{}; 130 bool incrementing{};
133 131
134 // Queue of command lists to be processed 132 // Queue of command lists to be processed
diff --git a/src/video_core/command_classes/codecs/h264.cpp b/src/video_core/command_classes/codecs/h264.cpp
index 65bbeac78..fea6aed98 100644
--- a/src/video_core/command_classes/codecs/h264.cpp
+++ b/src/video_core/command_classes/codecs/h264.cpp
@@ -19,7 +19,7 @@
19// 19//
20 20
21#include <array> 21#include <array>
22#include "common/bit_util.h" 22#include <bit>
23#include "video_core/command_classes/codecs/h264.h" 23#include "video_core/command_classes/codecs/h264.h"
24#include "video_core/gpu.h" 24#include "video_core/gpu.h"
25#include "video_core/memory_manager.h" 25#include "video_core/memory_manager.h"
@@ -266,7 +266,7 @@ void H264BitWriter::WriteExpGolombCodedInt(s32 value) {
266} 266}
267 267
268void H264BitWriter::WriteExpGolombCodedUInt(u32 value) { 268void H264BitWriter::WriteExpGolombCodedUInt(u32 value) {
269 const s32 size = 32 - Common::CountLeadingZeroes32(static_cast<s32>(value + 1)); 269 const s32 size = 32 - std::countl_zero(value + 1);
270 WriteBits(1, size); 270 WriteBits(1, size);
271 271
272 value -= (1U << (size - 1)) - 1; 272 value -= (1U << (size - 1)) - 1;
diff --git a/src/video_core/command_classes/host1x.cpp b/src/video_core/command_classes/host1x.cpp
index c4dd4881a..b12494528 100644
--- a/src/video_core/command_classes/host1x.cpp
+++ b/src/video_core/command_classes/host1x.cpp
@@ -10,22 +10,14 @@ Tegra::Host1x::Host1x(GPU& gpu_) : gpu(gpu_) {}
10 10
11Tegra::Host1x::~Host1x() = default; 11Tegra::Host1x::~Host1x() = default;
12 12
13void Tegra::Host1x::StateWrite(u32 offset, u32 arguments) { 13void Tegra::Host1x::ProcessMethod(Method method, u32 argument) {
14 u8* const state_offset = reinterpret_cast<u8*>(&state) + offset * sizeof(u32);
15 std::memcpy(state_offset, &arguments, sizeof(u32));
16}
17
18void Tegra::Host1x::ProcessMethod(Method method, const std::vector<u32>& arguments) {
19 StateWrite(static_cast<u32>(method), arguments[0]);
20 switch (method) { 14 switch (method) {
21 case Method::WaitSyncpt:
22 Execute(arguments[0]);
23 break;
24 case Method::LoadSyncptPayload32: 15 case Method::LoadSyncptPayload32:
25 syncpoint_value = arguments[0]; 16 syncpoint_value = argument;
26 break; 17 break;
18 case Method::WaitSyncpt:
27 case Method::WaitSyncpt32: 19 case Method::WaitSyncpt32:
28 Execute(arguments[0]); 20 Execute(argument);
29 break; 21 break;
30 default: 22 default:
31 UNIMPLEMENTED_MSG("Host1x method 0x{:X}", static_cast<u32>(method)); 23 UNIMPLEMENTED_MSG("Host1x method 0x{:X}", static_cast<u32>(method));
@@ -34,6 +26,5 @@ void Tegra::Host1x::ProcessMethod(Method method, const std::vector<u32>& argumen
34} 26}
35 27
36void Tegra::Host1x::Execute(u32 data) { 28void Tegra::Host1x::Execute(u32 data) {
37 // This method waits on a valid syncpoint. 29 gpu.WaitFence(data, syncpoint_value);
38 // TODO: Implement when proper Async is in place
39} 30}
diff --git a/src/video_core/command_classes/host1x.h b/src/video_core/command_classes/host1x.h
index 013eaa0c1..7e94799dd 100644
--- a/src/video_core/command_classes/host1x.h
+++ b/src/video_core/command_classes/host1x.h
@@ -14,64 +14,23 @@ class Nvdec;
14 14
15class Host1x { 15class Host1x {
16public: 16public:
17 struct Host1xClassRegisters {
18 u32 incr_syncpt{};
19 u32 incr_syncpt_ctrl{};
20 u32 incr_syncpt_error{};
21 INSERT_PADDING_WORDS(5);
22 u32 wait_syncpt{};
23 u32 wait_syncpt_base{};
24 u32 wait_syncpt_incr{};
25 u32 load_syncpt_base{};
26 u32 incr_syncpt_base{};
27 u32 clear{};
28 u32 wait{};
29 u32 wait_with_interrupt{};
30 u32 delay_use{};
31 u32 tick_count_high{};
32 u32 tick_count_low{};
33 u32 tick_ctrl{};
34 INSERT_PADDING_WORDS(23);
35 u32 ind_ctrl{};
36 u32 ind_off2{};
37 u32 ind_off{};
38 std::array<u32, 31> ind_data{};
39 INSERT_PADDING_WORDS(1);
40 u32 load_syncpoint_payload32{};
41 u32 stall_ctrl{};
42 u32 wait_syncpt32{};
43 u32 wait_syncpt_base32{};
44 u32 load_syncpt_base32{};
45 u32 incr_syncpt_base32{};
46 u32 stall_count_high{};
47 u32 stall_count_low{};
48 u32 xref_ctrl{};
49 u32 channel_xref_high{};
50 u32 channel_xref_low{};
51 };
52 static_assert(sizeof(Host1xClassRegisters) == 0x164, "Host1xClassRegisters is an invalid size");
53
54 enum class Method : u32 { 17 enum class Method : u32 {
55 WaitSyncpt = offsetof(Host1xClassRegisters, wait_syncpt) / 4, 18 WaitSyncpt = 0x8,
56 LoadSyncptPayload32 = offsetof(Host1xClassRegisters, load_syncpoint_payload32) / 4, 19 LoadSyncptPayload32 = 0x4e,
57 WaitSyncpt32 = offsetof(Host1xClassRegisters, wait_syncpt32) / 4, 20 WaitSyncpt32 = 0x50,
58 }; 21 };
59 22
60 explicit Host1x(GPU& gpu); 23 explicit Host1x(GPU& gpu);
61 ~Host1x(); 24 ~Host1x();
62 25
63 /// Writes the method into the state, Invoke Execute() if encountered 26 /// Writes the method into the state, Invoke Execute() if encountered
64 void ProcessMethod(Method method, const std::vector<u32>& arguments); 27 void ProcessMethod(Method method, u32 argument);
65 28
66private: 29private:
67 /// For Host1x, execute is waiting on a syncpoint previously written into the state 30 /// For Host1x, execute is waiting on a syncpoint previously written into the state
68 void Execute(u32 data); 31 void Execute(u32 data);
69 32
70 /// Write argument into the provided offset
71 void StateWrite(u32 offset, u32 arguments);
72
73 u32 syncpoint_value{}; 33 u32 syncpoint_value{};
74 Host1xClassRegisters state{};
75 GPU& gpu; 34 GPU& gpu;
76}; 35};
77 36
diff --git a/src/video_core/command_classes/vic.cpp b/src/video_core/command_classes/vic.cpp
index aa8c9f9de..55e632346 100644
--- a/src/video_core/command_classes/vic.cpp
+++ b/src/video_core/command_classes/vic.cpp
@@ -53,7 +53,7 @@ void Vic::ProcessMethod(Method method, const std::vector<u32>& arguments) {
53 53
54void Vic::Execute() { 54void Vic::Execute() {
55 if (output_surface_luma_address == 0) { 55 if (output_surface_luma_address == 0) {
56 LOG_ERROR(Service_NVDRV, "VIC Luma address not set. Recieved 0x{:X}", 56 LOG_ERROR(Service_NVDRV, "VIC Luma address not set. Received 0x{:X}",
57 vic_state.output_surface.luma_offset); 57 vic_state.output_surface.luma_offset);
58 return; 58 return;
59 } 59 }
diff --git a/src/video_core/compatible_formats.cpp b/src/video_core/compatible_formats.cpp
index 1619d8664..acf2668dc 100644
--- a/src/video_core/compatible_formats.cpp
+++ b/src/video_core/compatible_formats.cpp
@@ -10,9 +10,7 @@
10#include "video_core/surface.h" 10#include "video_core/surface.h"
11 11
12namespace VideoCore::Surface { 12namespace VideoCore::Surface {
13
14namespace { 13namespace {
15
16using Table = std::array<std::array<u64, 2>, MaxPixelFormat>; 14using Table = std::array<std::array<u64, 2>, MaxPixelFormat>;
17 15
18// Compatibility table taken from Table 3.X.2 in: 16// Compatibility table taken from Table 3.X.2 in:
@@ -233,10 +231,13 @@ constexpr Table MakeCopyTable() {
233 EnableRange(copy, COPY_CLASS_64_BITS); 231 EnableRange(copy, COPY_CLASS_64_BITS);
234 return copy; 232 return copy;
235} 233}
236
237} // Anonymous namespace 234} // Anonymous namespace
238 235
239bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b) { 236bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views) {
237 if (broken_views) {
238 // If format views are broken, only accept formats that are identical.
239 return format_a == format_b;
240 }
240 static constexpr Table TABLE = MakeViewTable(); 241 static constexpr Table TABLE = MakeViewTable();
241 return IsSupported(TABLE, format_a, format_b); 242 return IsSupported(TABLE, format_a, format_b);
242} 243}
diff --git a/src/video_core/compatible_formats.h b/src/video_core/compatible_formats.h
index b5eb03bea..9a0522988 100644
--- a/src/video_core/compatible_formats.h
+++ b/src/video_core/compatible_formats.h
@@ -8,7 +8,7 @@
8 8
9namespace VideoCore::Surface { 9namespace VideoCore::Surface {
10 10
11bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b); 11bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views);
12 12
13bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b); 13bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b);
14 14
diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h
index 81522988e..0de3280a2 100644
--- a/src/video_core/engines/fermi_2d.h
+++ b/src/video_core/engines/fermi_2d.h
@@ -171,30 +171,30 @@ public:
171 static constexpr std::size_t NUM_REGS = 0x258; 171 static constexpr std::size_t NUM_REGS = 0x258;
172 struct { 172 struct {
173 u32 object; 173 u32 object;
174 INSERT_UNION_PADDING_WORDS(0x3F); 174 INSERT_PADDING_WORDS_NOINIT(0x3F);
175 u32 no_operation; 175 u32 no_operation;
176 NotifyType notify; 176 NotifyType notify;
177 INSERT_UNION_PADDING_WORDS(0x2); 177 INSERT_PADDING_WORDS_NOINIT(0x2);
178 u32 wait_for_idle; 178 u32 wait_for_idle;
179 INSERT_UNION_PADDING_WORDS(0xB); 179 INSERT_PADDING_WORDS_NOINIT(0xB);
180 u32 pm_trigger; 180 u32 pm_trigger;
181 INSERT_UNION_PADDING_WORDS(0xF); 181 INSERT_PADDING_WORDS_NOINIT(0xF);
182 u32 context_dma_notify; 182 u32 context_dma_notify;
183 u32 dst_context_dma; 183 u32 dst_context_dma;
184 u32 src_context_dma; 184 u32 src_context_dma;
185 u32 semaphore_context_dma; 185 u32 semaphore_context_dma;
186 INSERT_UNION_PADDING_WORDS(0x1C); 186 INSERT_PADDING_WORDS_NOINIT(0x1C);
187 Surface dst; 187 Surface dst;
188 CpuIndexWrap pixels_from_cpu_index_wrap; 188 CpuIndexWrap pixels_from_cpu_index_wrap;
189 u32 kind2d_check_enable; 189 u32 kind2d_check_enable;
190 Surface src; 190 Surface src;
191 SectorPromotion pixels_from_memory_sector_promotion; 191 SectorPromotion pixels_from_memory_sector_promotion;
192 INSERT_UNION_PADDING_WORDS(0x1); 192 INSERT_PADDING_WORDS_NOINIT(0x1);
193 NumTpcs num_tpcs; 193 NumTpcs num_tpcs;
194 u32 render_enable_addr_upper; 194 u32 render_enable_addr_upper;
195 u32 render_enable_addr_lower; 195 u32 render_enable_addr_lower;
196 RenderEnableMode render_enable_mode; 196 RenderEnableMode render_enable_mode;
197 INSERT_UNION_PADDING_WORDS(0x4); 197 INSERT_PADDING_WORDS_NOINIT(0x4);
198 u32 clip_x0; 198 u32 clip_x0;
199 u32 clip_y0; 199 u32 clip_y0;
200 u32 clip_width; 200 u32 clip_width;
@@ -212,7 +212,7 @@ public:
212 BitField<8, 6, u32> y; 212 BitField<8, 6, u32> y;
213 } pattern_offset; 213 } pattern_offset;
214 BitField<0, 2, PatternSelect> pattern_select; 214 BitField<0, 2, PatternSelect> pattern_select;
215 INSERT_UNION_PADDING_WORDS(0xC); 215 INSERT_PADDING_WORDS_NOINIT(0xC);
216 struct { 216 struct {
217 BitField<0, 3, MonochromePatternColorFormat> color_format; 217 BitField<0, 3, MonochromePatternColorFormat> color_format;
218 BitField<0, 1, MonochromePatternFormat> format; 218 BitField<0, 1, MonochromePatternFormat> format;
@@ -227,15 +227,15 @@ public:
227 std::array<u32, 0x20> X1R5G5B5; 227 std::array<u32, 0x20> X1R5G5B5;
228 std::array<u32, 0x10> Y8; 228 std::array<u32, 0x10> Y8;
229 } color_pattern; 229 } color_pattern;
230 INSERT_UNION_PADDING_WORDS(0x10); 230 INSERT_PADDING_WORDS_NOINIT(0x10);
231 struct { 231 struct {
232 u32 prim_mode; 232 u32 prim_mode;
233 u32 prim_color_format; 233 u32 prim_color_format;
234 u32 prim_color; 234 u32 prim_color;
235 u32 line_tie_break_bits; 235 u32 line_tie_break_bits;
236 INSERT_UNION_PADDING_WORDS(0x14); 236 INSERT_PADDING_WORDS_NOINIT(0x14);
237 u32 prim_point_xy; 237 u32 prim_point_xy;
238 INSERT_UNION_PADDING_WORDS(0x7); 238 INSERT_PADDING_WORDS_NOINIT(0x7);
239 std::array<Point, 0x40> prim_point; 239 std::array<Point, 0x40> prim_point;
240 } render_solid; 240 } render_solid;
241 struct { 241 struct {
@@ -247,7 +247,7 @@ public:
247 u32 color0; 247 u32 color0;
248 u32 color1; 248 u32 color1;
249 u32 mono_opacity; 249 u32 mono_opacity;
250 INSERT_UNION_PADDING_WORDS(0x6); 250 INSERT_PADDING_WORDS_NOINIT(0x6);
251 u32 src_width; 251 u32 src_width;
252 u32 src_height; 252 u32 src_height;
253 u32 dx_du_frac; 253 u32 dx_du_frac;
@@ -260,9 +260,9 @@ public:
260 u32 dst_y0_int; 260 u32 dst_y0_int;
261 u32 data; 261 u32 data;
262 } pixels_from_cpu; 262 } pixels_from_cpu;
263 INSERT_UNION_PADDING_WORDS(0x3); 263 INSERT_PADDING_WORDS_NOINIT(0x3);
264 u32 big_endian_control; 264 u32 big_endian_control;
265 INSERT_UNION_PADDING_WORDS(0x3); 265 INSERT_PADDING_WORDS_NOINIT(0x3);
266 struct { 266 struct {
267 BitField<0, 3, u32> block_shape; 267 BitField<0, 3, u32> block_shape;
268 BitField<0, 5, u32> corral_size; 268 BitField<0, 5, u32> corral_size;
@@ -271,7 +271,7 @@ public:
271 BitField<0, 1, Origin> origin; 271 BitField<0, 1, Origin> origin;
272 BitField<4, 1, Filter> filter; 272 BitField<4, 1, Filter> filter;
273 } sample_mode; 273 } sample_mode;
274 INSERT_UNION_PADDING_WORDS(0x8); 274 INSERT_PADDING_WORDS_NOINIT(0x8);
275 s32 dst_x0; 275 s32 dst_x0;
276 s32 dst_y0; 276 s32 dst_y0;
277 s32 dst_width; 277 s32 dst_width;
diff --git a/src/video_core/engines/kepler_compute.h b/src/video_core/engines/kepler_compute.h
index 51a041202..9f0a7b76d 100644
--- a/src/video_core/engines/kepler_compute.h
+++ b/src/video_core/engines/kepler_compute.h
@@ -55,7 +55,7 @@ public:
55 55
56 union { 56 union {
57 struct { 57 struct {
58 INSERT_UNION_PADDING_WORDS(0x60); 58 INSERT_PADDING_WORDS_NOINIT(0x60);
59 59
60 Upload::Registers upload; 60 Upload::Registers upload;
61 61
@@ -67,7 +67,7 @@ public:
67 67
68 u32 data_upload; 68 u32 data_upload;
69 69
70 INSERT_UNION_PADDING_WORDS(0x3F); 70 INSERT_PADDING_WORDS_NOINIT(0x3F);
71 71
72 struct { 72 struct {
73 u32 address; 73 u32 address;
@@ -76,11 +76,11 @@ public:
76 } 76 }
77 } launch_desc_loc; 77 } launch_desc_loc;
78 78
79 INSERT_UNION_PADDING_WORDS(0x1); 79 INSERT_PADDING_WORDS_NOINIT(0x1);
80 80
81 u32 launch; 81 u32 launch;
82 82
83 INSERT_UNION_PADDING_WORDS(0x4A7); 83 INSERT_PADDING_WORDS_NOINIT(0x4A7);
84 84
85 struct { 85 struct {
86 u32 address_high; 86 u32 address_high;
@@ -92,7 +92,7 @@ public:
92 } 92 }
93 } tsc; 93 } tsc;
94 94
95 INSERT_UNION_PADDING_WORDS(0x3); 95 INSERT_PADDING_WORDS_NOINIT(0x3);
96 96
97 struct { 97 struct {
98 u32 address_high; 98 u32 address_high;
@@ -104,7 +104,7 @@ public:
104 } 104 }
105 } tic; 105 } tic;
106 106
107 INSERT_UNION_PADDING_WORDS(0x22); 107 INSERT_PADDING_WORDS_NOINIT(0x22);
108 108
109 struct { 109 struct {
110 u32 address_high; 110 u32 address_high;
@@ -115,11 +115,11 @@ public:
115 } 115 }
116 } code_loc; 116 } code_loc;
117 117
118 INSERT_UNION_PADDING_WORDS(0x3FE); 118 INSERT_PADDING_WORDS_NOINIT(0x3FE);
119 119
120 u32 tex_cb_index; 120 u32 tex_cb_index;
121 121
122 INSERT_UNION_PADDING_WORDS(0x374); 122 INSERT_PADDING_WORDS_NOINIT(0x374);
123 }; 123 };
124 std::array<u32, NUM_REGS> reg_array; 124 std::array<u32, NUM_REGS> reg_array;
125 }; 125 };
diff --git a/src/video_core/engines/kepler_memory.h b/src/video_core/engines/kepler_memory.h
index 62483589e..19808a5c6 100644
--- a/src/video_core/engines/kepler_memory.h
+++ b/src/video_core/engines/kepler_memory.h
@@ -50,7 +50,7 @@ public:
50 50
51 union { 51 union {
52 struct { 52 struct {
53 INSERT_UNION_PADDING_WORDS(0x60); 53 INSERT_PADDING_WORDS_NOINIT(0x60);
54 54
55 Upload::Registers upload; 55 Upload::Registers upload;
56 56
@@ -62,7 +62,7 @@ public:
62 62
63 u32 data; 63 u32 data;
64 64
65 INSERT_UNION_PADDING_WORDS(0x11); 65 INSERT_PADDING_WORDS_NOINIT(0x11);
66 }; 66 };
67 std::array<u32, NUM_REGS> reg_array; 67 std::array<u32, NUM_REGS> reg_array;
68 }; 68 };
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index bf9e07c9b..326b32228 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -536,7 +536,7 @@ public:
536 Equation equation_a; 536 Equation equation_a;
537 Factor factor_source_a; 537 Factor factor_source_a;
538 Factor factor_dest_a; 538 Factor factor_dest_a;
539 INSERT_UNION_PADDING_WORDS(1); 539 INSERT_PADDING_WORDS_NOINIT(1);
540 }; 540 };
541 541
542 enum class TessellationPrimitive : u32 { 542 enum class TessellationPrimitive : u32 {
@@ -608,7 +608,7 @@ public:
608 }; 608 };
609 u32 layer_stride; 609 u32 layer_stride;
610 u32 base_layer; 610 u32 base_layer;
611 INSERT_UNION_PADDING_WORDS(7); 611 INSERT_PADDING_WORDS_NOINIT(7);
612 612
613 GPUVAddr Address() const { 613 GPUVAddr Address() const {
614 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 614 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
@@ -640,7 +640,7 @@ public:
640 BitField<8, 3, ViewportSwizzle> z; 640 BitField<8, 3, ViewportSwizzle> z;
641 BitField<12, 3, ViewportSwizzle> w; 641 BitField<12, 3, ViewportSwizzle> w;
642 } swizzle; 642 } swizzle;
643 INSERT_UNION_PADDING_WORDS(1); 643 INSERT_PADDING_WORDS_NOINIT(1);
644 644
645 Common::Rectangle<f32> GetRect() const { 645 Common::Rectangle<f32> GetRect() const {
646 return { 646 return {
@@ -700,7 +700,7 @@ public:
700 u32 address_low; 700 u32 address_low;
701 s32 buffer_size; 701 s32 buffer_size;
702 s32 buffer_offset; 702 s32 buffer_offset;
703 INSERT_UNION_PADDING_WORDS(3); 703 INSERT_PADDING_WORDS_NOINIT(3);
704 704
705 GPUVAddr Address() const { 705 GPUVAddr Address() const {
706 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | 706 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
@@ -713,7 +713,7 @@ public:
713 u32 stream; 713 u32 stream;
714 u32 varying_count; 714 u32 varying_count;
715 u32 stride; 715 u32 stride;
716 INSERT_UNION_PADDING_WORDS(1); 716 INSERT_PADDING_WORDS_NOINIT(1);
717 }; 717 };
718 static_assert(sizeof(TransformFeedbackLayout) == 16); 718 static_assert(sizeof(TransformFeedbackLayout) == 16);
719 719
@@ -731,7 +731,7 @@ public:
731 731
732 union { 732 union {
733 struct { 733 struct {
734 INSERT_UNION_PADDING_WORDS(0x44); 734 INSERT_PADDING_WORDS_NOINIT(0x44);
735 735
736 u32 wait_for_idle; 736 u32 wait_for_idle;
737 737
@@ -744,7 +744,7 @@ public:
744 744
745 ShadowRamControl shadow_ram_control; 745 ShadowRamControl shadow_ram_control;
746 746
747 INSERT_UNION_PADDING_WORDS(0x16); 747 INSERT_PADDING_WORDS_NOINIT(0x16);
748 748
749 Upload::Registers upload; 749 Upload::Registers upload;
750 struct { 750 struct {
@@ -755,11 +755,11 @@ public:
755 755
756 u32 data_upload; 756 u32 data_upload;
757 757
758 INSERT_UNION_PADDING_WORDS(0x16); 758 INSERT_PADDING_WORDS_NOINIT(0x16);
759 759
760 u32 force_early_fragment_tests; 760 u32 force_early_fragment_tests;
761 761
762 INSERT_UNION_PADDING_WORDS(0x2D); 762 INSERT_PADDING_WORDS_NOINIT(0x2D);
763 763
764 struct { 764 struct {
765 union { 765 union {
@@ -769,7 +769,7 @@ public:
769 }; 769 };
770 } sync_info; 770 } sync_info;
771 771
772 INSERT_UNION_PADDING_WORDS(0x15); 772 INSERT_PADDING_WORDS_NOINIT(0x15);
773 773
774 union { 774 union {
775 BitField<0, 2, TessellationPrimitive> prim; 775 BitField<0, 2, TessellationPrimitive> prim;
@@ -781,21 +781,21 @@ public:
781 std::array<f32, 4> tess_level_outer; 781 std::array<f32, 4> tess_level_outer;
782 std::array<f32, 2> tess_level_inner; 782 std::array<f32, 2> tess_level_inner;
783 783
784 INSERT_UNION_PADDING_WORDS(0x10); 784 INSERT_PADDING_WORDS_NOINIT(0x10);
785 785
786 u32 rasterize_enable; 786 u32 rasterize_enable;
787 787
788 std::array<TransformFeedbackBinding, NumTransformFeedbackBuffers> tfb_bindings; 788 std::array<TransformFeedbackBinding, NumTransformFeedbackBuffers> tfb_bindings;
789 789
790 INSERT_UNION_PADDING_WORDS(0xC0); 790 INSERT_PADDING_WORDS_NOINIT(0xC0);
791 791
792 std::array<TransformFeedbackLayout, NumTransformFeedbackBuffers> tfb_layouts; 792 std::array<TransformFeedbackLayout, NumTransformFeedbackBuffers> tfb_layouts;
793 793
794 INSERT_UNION_PADDING_WORDS(0x1); 794 INSERT_PADDING_WORDS_NOINIT(0x1);
795 795
796 u32 tfb_enabled; 796 u32 tfb_enabled;
797 797
798 INSERT_UNION_PADDING_WORDS(0x2E); 798 INSERT_PADDING_WORDS_NOINIT(0x2E);
799 799
800 std::array<RenderTargetConfig, NumRenderTargets> rt; 800 std::array<RenderTargetConfig, NumRenderTargets> rt;
801 801
@@ -803,7 +803,7 @@ public:
803 803
804 std::array<ViewPort, NumViewports> viewports; 804 std::array<ViewPort, NumViewports> viewports;
805 805
806 INSERT_UNION_PADDING_WORDS(0x1D); 806 INSERT_PADDING_WORDS_NOINIT(0x1D);
807 807
808 struct { 808 struct {
809 u32 first; 809 u32 first;
@@ -815,16 +815,16 @@ public:
815 float clear_color[4]; 815 float clear_color[4];
816 float clear_depth; 816 float clear_depth;
817 817
818 INSERT_UNION_PADDING_WORDS(0x3); 818 INSERT_PADDING_WORDS_NOINIT(0x3);
819 819
820 s32 clear_stencil; 820 s32 clear_stencil;
821 821
822 INSERT_UNION_PADDING_WORDS(0x2); 822 INSERT_PADDING_WORDS_NOINIT(0x2);
823 823
824 PolygonMode polygon_mode_front; 824 PolygonMode polygon_mode_front;
825 PolygonMode polygon_mode_back; 825 PolygonMode polygon_mode_back;
826 826
827 INSERT_UNION_PADDING_WORDS(0x3); 827 INSERT_PADDING_WORDS_NOINIT(0x3);
828 828
829 u32 polygon_offset_point_enable; 829 u32 polygon_offset_point_enable;
830 u32 polygon_offset_line_enable; 830 u32 polygon_offset_line_enable;
@@ -832,47 +832,47 @@ public:
832 832
833 u32 patch_vertices; 833 u32 patch_vertices;
834 834
835 INSERT_UNION_PADDING_WORDS(0x4); 835 INSERT_PADDING_WORDS_NOINIT(0x4);
836 836
837 u32 fragment_barrier; 837 u32 fragment_barrier;
838 838
839 INSERT_UNION_PADDING_WORDS(0x7); 839 INSERT_PADDING_WORDS_NOINIT(0x7);
840 840
841 std::array<ScissorTest, NumViewports> scissor_test; 841 std::array<ScissorTest, NumViewports> scissor_test;
842 842
843 INSERT_UNION_PADDING_WORDS(0x15); 843 INSERT_PADDING_WORDS_NOINIT(0x15);
844 844
845 s32 stencil_back_func_ref; 845 s32 stencil_back_func_ref;
846 u32 stencil_back_mask; 846 u32 stencil_back_mask;
847 u32 stencil_back_func_mask; 847 u32 stencil_back_func_mask;
848 848
849 INSERT_UNION_PADDING_WORDS(0x5); 849 INSERT_PADDING_WORDS_NOINIT(0x5);
850 850
851 u32 invalidate_texture_data_cache; 851 u32 invalidate_texture_data_cache;
852 852
853 INSERT_UNION_PADDING_WORDS(0x1); 853 INSERT_PADDING_WORDS_NOINIT(0x1);
854 854
855 u32 tiled_cache_barrier; 855 u32 tiled_cache_barrier;
856 856
857 INSERT_UNION_PADDING_WORDS(0x4); 857 INSERT_PADDING_WORDS_NOINIT(0x4);
858 858
859 u32 color_mask_common; 859 u32 color_mask_common;
860 860
861 INSERT_UNION_PADDING_WORDS(0x2); 861 INSERT_PADDING_WORDS_NOINIT(0x2);
862 862
863 f32 depth_bounds[2]; 863 f32 depth_bounds[2];
864 864
865 INSERT_UNION_PADDING_WORDS(0x2); 865 INSERT_PADDING_WORDS_NOINIT(0x2);
866 866
867 u32 rt_separate_frag_data; 867 u32 rt_separate_frag_data;
868 868
869 INSERT_UNION_PADDING_WORDS(0x1); 869 INSERT_PADDING_WORDS_NOINIT(0x1);
870 870
871 u32 multisample_raster_enable; 871 u32 multisample_raster_enable;
872 u32 multisample_raster_samples; 872 u32 multisample_raster_samples;
873 std::array<u32, 4> multisample_sample_mask; 873 std::array<u32, 4> multisample_sample_mask;
874 874
875 INSERT_UNION_PADDING_WORDS(0x5); 875 INSERT_PADDING_WORDS_NOINIT(0x5);
876 876
877 struct { 877 struct {
878 u32 address_high; 878 u32 address_high;
@@ -898,7 +898,7 @@ public:
898 }; 898 };
899 } render_area; 899 } render_area;
900 900
901 INSERT_UNION_PADDING_WORDS(0x3F); 901 INSERT_PADDING_WORDS_NOINIT(0x3F);
902 902
903 union { 903 union {
904 BitField<0, 4, u32> stencil; 904 BitField<0, 4, u32> stencil;
@@ -907,24 +907,24 @@ public:
907 BitField<12, 4, u32> viewport; 907 BitField<12, 4, u32> viewport;
908 } clear_flags; 908 } clear_flags;
909 909
910 INSERT_UNION_PADDING_WORDS(0x10); 910 INSERT_PADDING_WORDS_NOINIT(0x10);
911 911
912 u32 fill_rectangle; 912 u32 fill_rectangle;
913 913
914 INSERT_UNION_PADDING_WORDS(0x8); 914 INSERT_PADDING_WORDS_NOINIT(0x8);
915 915
916 std::array<VertexAttribute, NumVertexAttributes> vertex_attrib_format; 916 std::array<VertexAttribute, NumVertexAttributes> vertex_attrib_format;
917 917
918 std::array<MsaaSampleLocation, 4> multisample_sample_locations; 918 std::array<MsaaSampleLocation, 4> multisample_sample_locations;
919 919
920 INSERT_UNION_PADDING_WORDS(0x2); 920 INSERT_PADDING_WORDS_NOINIT(0x2);
921 921
922 union { 922 union {
923 BitField<0, 1, u32> enable; 923 BitField<0, 1, u32> enable;
924 BitField<4, 3, u32> target; 924 BitField<4, 3, u32> target;
925 } multisample_coverage_to_color; 925 } multisample_coverage_to_color;
926 926
927 INSERT_UNION_PADDING_WORDS(0x8); 927 INSERT_PADDING_WORDS_NOINIT(0x8);
928 928
929 struct { 929 struct {
930 union { 930 union {
@@ -947,7 +947,7 @@ public:
947 } 947 }
948 } rt_control; 948 } rt_control;
949 949
950 INSERT_UNION_PADDING_WORDS(0x2); 950 INSERT_PADDING_WORDS_NOINIT(0x2);
951 951
952 u32 zeta_width; 952 u32 zeta_width;
953 u32 zeta_height; 953 u32 zeta_height;
@@ -958,11 +958,11 @@ public:
958 958
959 SamplerIndex sampler_index; 959 SamplerIndex sampler_index;
960 960
961 INSERT_UNION_PADDING_WORDS(0x25); 961 INSERT_PADDING_WORDS_NOINIT(0x25);
962 962
963 u32 depth_test_enable; 963 u32 depth_test_enable;
964 964
965 INSERT_UNION_PADDING_WORDS(0x5); 965 INSERT_PADDING_WORDS_NOINIT(0x5);
966 966
967 u32 independent_blend_enable; 967 u32 independent_blend_enable;
968 968
@@ -970,7 +970,7 @@ public:
970 970
971 u32 alpha_test_enabled; 971 u32 alpha_test_enabled;
972 972
973 INSERT_UNION_PADDING_WORDS(0x6); 973 INSERT_PADDING_WORDS_NOINIT(0x6);
974 974
975 u32 d3d_cull_mode; 975 u32 d3d_cull_mode;
976 976
@@ -985,7 +985,7 @@ public:
985 float a; 985 float a;
986 } blend_color; 986 } blend_color;
987 987
988 INSERT_UNION_PADDING_WORDS(0x4); 988 INSERT_PADDING_WORDS_NOINIT(0x4);
989 989
990 struct { 990 struct {
991 u32 separate_alpha; 991 u32 separate_alpha;
@@ -994,7 +994,7 @@ public:
994 Blend::Factor factor_dest_rgb; 994 Blend::Factor factor_dest_rgb;
995 Blend::Equation equation_a; 995 Blend::Equation equation_a;
996 Blend::Factor factor_source_a; 996 Blend::Factor factor_source_a;
997 INSERT_UNION_PADDING_WORDS(1); 997 INSERT_PADDING_WORDS_NOINIT(1);
998 Blend::Factor factor_dest_a; 998 Blend::Factor factor_dest_a;
999 999
1000 u32 enable_common; 1000 u32 enable_common;
@@ -1010,7 +1010,7 @@ public:
1010 u32 stencil_front_func_mask; 1010 u32 stencil_front_func_mask;
1011 u32 stencil_front_mask; 1011 u32 stencil_front_mask;
1012 1012
1013 INSERT_UNION_PADDING_WORDS(0x2); 1013 INSERT_PADDING_WORDS_NOINIT(0x2);
1014 1014
1015 u32 frag_color_clamp; 1015 u32 frag_color_clamp;
1016 1016
@@ -1022,17 +1022,17 @@ public:
1022 float line_width_smooth; 1022 float line_width_smooth;
1023 float line_width_aliased; 1023 float line_width_aliased;
1024 1024
1025 INSERT_UNION_PADDING_WORDS(0x1B); 1025 INSERT_PADDING_WORDS_NOINIT(0x1B);
1026 1026
1027 u32 invalidate_sampler_cache_no_wfi; 1027 u32 invalidate_sampler_cache_no_wfi;
1028 u32 invalidate_texture_header_cache_no_wfi; 1028 u32 invalidate_texture_header_cache_no_wfi;
1029 1029
1030 INSERT_UNION_PADDING_WORDS(0x2); 1030 INSERT_PADDING_WORDS_NOINIT(0x2);
1031 1031
1032 u32 vb_element_base; 1032 u32 vb_element_base;
1033 u32 vb_base_instance; 1033 u32 vb_base_instance;
1034 1034
1035 INSERT_UNION_PADDING_WORDS(0x35); 1035 INSERT_PADDING_WORDS_NOINIT(0x35);
1036 1036
1037 u32 clip_distance_enabled; 1037 u32 clip_distance_enabled;
1038 1038
@@ -1040,11 +1040,11 @@ public:
1040 1040
1041 float point_size; 1041 float point_size;
1042 1042
1043 INSERT_UNION_PADDING_WORDS(0x1); 1043 INSERT_PADDING_WORDS_NOINIT(0x1);
1044 1044
1045 u32 point_sprite_enable; 1045 u32 point_sprite_enable;
1046 1046
1047 INSERT_UNION_PADDING_WORDS(0x3); 1047 INSERT_PADDING_WORDS_NOINIT(0x3);
1048 1048
1049 CounterReset counter_reset; 1049 CounterReset counter_reset;
1050 1050
@@ -1057,7 +1057,7 @@ public:
1057 BitField<4, 1, u32> alpha_to_one; 1057 BitField<4, 1, u32> alpha_to_one;
1058 } multisample_control; 1058 } multisample_control;
1059 1059
1060 INSERT_UNION_PADDING_WORDS(0x4); 1060 INSERT_PADDING_WORDS_NOINIT(0x4);
1061 1061
1062 struct { 1062 struct {
1063 u32 address_high; 1063 u32 address_high;
@@ -1081,7 +1081,7 @@ public:
1081 } 1081 }
1082 } tsc; 1082 } tsc;
1083 1083
1084 INSERT_UNION_PADDING_WORDS(0x1); 1084 INSERT_PADDING_WORDS_NOINIT(0x1);
1085 1085
1086 float polygon_offset_factor; 1086 float polygon_offset_factor;
1087 1087
@@ -1098,7 +1098,7 @@ public:
1098 } 1098 }
1099 } tic; 1099 } tic;
1100 1100
1101 INSERT_UNION_PADDING_WORDS(0x5); 1101 INSERT_PADDING_WORDS_NOINIT(0x5);
1102 1102
1103 u32 stencil_two_side_enable; 1103 u32 stencil_two_side_enable;
1104 StencilOp stencil_back_op_fail; 1104 StencilOp stencil_back_op_fail;
@@ -1106,17 +1106,17 @@ public:
1106 StencilOp stencil_back_op_zpass; 1106 StencilOp stencil_back_op_zpass;
1107 ComparisonOp stencil_back_func_func; 1107 ComparisonOp stencil_back_func_func;
1108 1108
1109 INSERT_UNION_PADDING_WORDS(0x4); 1109 INSERT_PADDING_WORDS_NOINIT(0x4);
1110 1110
1111 u32 framebuffer_srgb; 1111 u32 framebuffer_srgb;
1112 1112
1113 float polygon_offset_units; 1113 float polygon_offset_units;
1114 1114
1115 INSERT_UNION_PADDING_WORDS(0x4); 1115 INSERT_PADDING_WORDS_NOINIT(0x4);
1116 1116
1117 Tegra::Texture::MsaaMode multisample_mode; 1117 Tegra::Texture::MsaaMode multisample_mode;
1118 1118
1119 INSERT_UNION_PADDING_WORDS(0xC); 1119 INSERT_PADDING_WORDS_NOINIT(0xC);
1120 1120
1121 union { 1121 union {
1122 BitField<2, 1, u32> coord_origin; 1122 BitField<2, 1, u32> coord_origin;
@@ -1132,7 +1132,7 @@ public:
1132 (static_cast<GPUVAddr>(code_address_high) << 32) | code_address_low); 1132 (static_cast<GPUVAddr>(code_address_high) << 32) | code_address_low);
1133 } 1133 }
1134 } code_address; 1134 } code_address;
1135 INSERT_UNION_PADDING_WORDS(1); 1135 INSERT_PADDING_WORDS_NOINIT(1);
1136 1136
1137 struct { 1137 struct {
1138 u32 vertex_end_gl; 1138 u32 vertex_end_gl;
@@ -1144,14 +1144,14 @@ public:
1144 }; 1144 };
1145 } draw; 1145 } draw;
1146 1146
1147 INSERT_UNION_PADDING_WORDS(0xA); 1147 INSERT_PADDING_WORDS_NOINIT(0xA);
1148 1148
1149 struct { 1149 struct {
1150 u32 enabled; 1150 u32 enabled;
1151 u32 index; 1151 u32 index;
1152 } primitive_restart; 1152 } primitive_restart;
1153 1153
1154 INSERT_UNION_PADDING_WORDS(0x5F); 1154 INSERT_PADDING_WORDS_NOINIT(0x5F);
1155 1155
1156 struct { 1156 struct {
1157 u32 start_addr_high; 1157 u32 start_addr_high;
@@ -1192,9 +1192,9 @@ public:
1192 } 1192 }
1193 } index_array; 1193 } index_array;
1194 1194
1195 INSERT_UNION_PADDING_WORDS(0x7); 1195 INSERT_PADDING_WORDS_NOINIT(0x7);
1196 1196
1197 INSERT_UNION_PADDING_WORDS(0x1F); 1197 INSERT_PADDING_WORDS_NOINIT(0x1F);
1198 1198
1199 float polygon_offset_clamp; 1199 float polygon_offset_clamp;
1200 1200
@@ -1208,14 +1208,14 @@ public:
1208 } 1208 }
1209 } instanced_arrays; 1209 } instanced_arrays;
1210 1210
1211 INSERT_UNION_PADDING_WORDS(0x4); 1211 INSERT_PADDING_WORDS_NOINIT(0x4);
1212 1212
1213 union { 1213 union {
1214 BitField<0, 1, u32> enable; 1214 BitField<0, 1, u32> enable;
1215 BitField<4, 8, u32> unk4; 1215 BitField<4, 8, u32> unk4;
1216 } vp_point_size; 1216 } vp_point_size;
1217 1217
1218 INSERT_UNION_PADDING_WORDS(1); 1218 INSERT_PADDING_WORDS_NOINIT(1);
1219 1219
1220 u32 cull_test_enabled; 1220 u32 cull_test_enabled;
1221 FrontFace front_face; 1221 FrontFace front_face;
@@ -1223,11 +1223,11 @@ public:
1223 1223
1224 u32 pixel_center_integer; 1224 u32 pixel_center_integer;
1225 1225
1226 INSERT_UNION_PADDING_WORDS(0x1); 1226 INSERT_PADDING_WORDS_NOINIT(0x1);
1227 1227
1228 u32 viewport_transform_enabled; 1228 u32 viewport_transform_enabled;
1229 1229
1230 INSERT_UNION_PADDING_WORDS(0x3); 1230 INSERT_PADDING_WORDS_NOINIT(0x3);
1231 1231
1232 union { 1232 union {
1233 BitField<0, 1, u32> depth_range_0_1; 1233 BitField<0, 1, u32> depth_range_0_1;
@@ -1236,18 +1236,18 @@ public:
1236 BitField<11, 1, u32> depth_clamp_disabled; 1236 BitField<11, 1, u32> depth_clamp_disabled;
1237 } view_volume_clip_control; 1237 } view_volume_clip_control;
1238 1238
1239 INSERT_UNION_PADDING_WORDS(0x1F); 1239 INSERT_PADDING_WORDS_NOINIT(0x1F);
1240 1240
1241 u32 depth_bounds_enable; 1241 u32 depth_bounds_enable;
1242 1242
1243 INSERT_UNION_PADDING_WORDS(1); 1243 INSERT_PADDING_WORDS_NOINIT(1);
1244 1244
1245 struct { 1245 struct {
1246 u32 enable; 1246 u32 enable;
1247 LogicOperation operation; 1247 LogicOperation operation;
1248 } logic_op; 1248 } logic_op;
1249 1249
1250 INSERT_UNION_PADDING_WORDS(0x1); 1250 INSERT_PADDING_WORDS_NOINIT(0x1);
1251 1251
1252 union { 1252 union {
1253 u32 raw; 1253 u32 raw;
@@ -1260,9 +1260,9 @@ public:
1260 BitField<6, 4, u32> RT; 1260 BitField<6, 4, u32> RT;
1261 BitField<10, 11, u32> layer; 1261 BitField<10, 11, u32> layer;
1262 } clear_buffers; 1262 } clear_buffers;
1263 INSERT_UNION_PADDING_WORDS(0xB); 1263 INSERT_PADDING_WORDS_NOINIT(0xB);
1264 std::array<ColorMask, NumRenderTargets> color_mask; 1264 std::array<ColorMask, NumRenderTargets> color_mask;
1265 INSERT_UNION_PADDING_WORDS(0x38); 1265 INSERT_PADDING_WORDS_NOINIT(0x38);
1266 1266
1267 struct { 1267 struct {
1268 u32 query_address_high; 1268 u32 query_address_high;
@@ -1284,7 +1284,7 @@ public:
1284 } 1284 }
1285 } query; 1285 } query;
1286 1286
1287 INSERT_UNION_PADDING_WORDS(0x3C); 1287 INSERT_PADDING_WORDS_NOINIT(0x3C);
1288 1288
1289 struct { 1289 struct {
1290 union { 1290 union {
@@ -1325,10 +1325,10 @@ public:
1325 BitField<4, 4, ShaderProgram> program; 1325 BitField<4, 4, ShaderProgram> program;
1326 }; 1326 };
1327 u32 offset; 1327 u32 offset;
1328 INSERT_UNION_PADDING_WORDS(14); 1328 INSERT_PADDING_WORDS_NOINIT(14);
1329 } shader_config[MaxShaderProgram]; 1329 } shader_config[MaxShaderProgram];
1330 1330
1331 INSERT_UNION_PADDING_WORDS(0x60); 1331 INSERT_PADDING_WORDS_NOINIT(0x60);
1332 1332
1333 u32 firmware[0x20]; 1333 u32 firmware[0x20];
1334 1334
@@ -1345,7 +1345,7 @@ public:
1345 } 1345 }
1346 } const_buffer; 1346 } const_buffer;
1347 1347
1348 INSERT_UNION_PADDING_WORDS(0x10); 1348 INSERT_PADDING_WORDS_NOINIT(0x10);
1349 1349
1350 struct { 1350 struct {
1351 union { 1351 union {
@@ -1353,18 +1353,18 @@ public:
1353 BitField<0, 1, u32> valid; 1353 BitField<0, 1, u32> valid;
1354 BitField<4, 5, u32> index; 1354 BitField<4, 5, u32> index;
1355 }; 1355 };
1356 INSERT_UNION_PADDING_WORDS(7); 1356 INSERT_PADDING_WORDS_NOINIT(7);
1357 } cb_bind[MaxShaderStage]; 1357 } cb_bind[MaxShaderStage];
1358 1358
1359 INSERT_UNION_PADDING_WORDS(0x56); 1359 INSERT_PADDING_WORDS_NOINIT(0x56);
1360 1360
1361 u32 tex_cb_index; 1361 u32 tex_cb_index;
1362 1362
1363 INSERT_UNION_PADDING_WORDS(0x7D); 1363 INSERT_PADDING_WORDS_NOINIT(0x7D);
1364 1364
1365 std::array<std::array<u8, 128>, NumTransformFeedbackBuffers> tfb_varying_locs; 1365 std::array<std::array<u8, 128>, NumTransformFeedbackBuffers> tfb_varying_locs;
1366 1366
1367 INSERT_UNION_PADDING_WORDS(0x298); 1367 INSERT_PADDING_WORDS_NOINIT(0x298);
1368 1368
1369 struct { 1369 struct {
1370 /// Compressed address of a buffer that holds information about bound SSBOs. 1370 /// Compressed address of a buffer that holds information about bound SSBOs.
@@ -1376,14 +1376,14 @@ public:
1376 } 1376 }
1377 } ssbo_info; 1377 } ssbo_info;
1378 1378
1379 INSERT_UNION_PADDING_WORDS(0x11); 1379 INSERT_PADDING_WORDS_NOINIT(0x11);
1380 1380
1381 struct { 1381 struct {
1382 u32 address[MaxShaderStage]; 1382 u32 address[MaxShaderStage];
1383 u32 size[MaxShaderStage]; 1383 u32 size[MaxShaderStage];
1384 } tex_info_buffers; 1384 } tex_info_buffers;
1385 1385
1386 INSERT_UNION_PADDING_WORDS(0xCC); 1386 INSERT_PADDING_WORDS_NOINIT(0xCC);
1387 }; 1387 };
1388 std::array<u32, NUM_REGS> reg_array; 1388 std::array<u32, NUM_REGS> reg_array;
1389 }; 1389 };
diff --git a/src/video_core/engines/shader_header.h b/src/video_core/engines/shader_header.h
index ceec05459..e0d7b89c5 100644
--- a/src/video_core/engines/shader_header.h
+++ b/src/video_core/engines/shader_header.h
@@ -68,10 +68,10 @@ struct Header {
68 68
69 union { 69 union {
70 struct { 70 struct {
71 INSERT_UNION_PADDING_BYTES(3); // ImapSystemValuesA 71 INSERT_PADDING_BYTES_NOINIT(3); // ImapSystemValuesA
72 INSERT_UNION_PADDING_BYTES(1); // ImapSystemValuesB 72 INSERT_PADDING_BYTES_NOINIT(1); // ImapSystemValuesB
73 INSERT_UNION_PADDING_BYTES(16); // ImapGenericVector[32] 73 INSERT_PADDING_BYTES_NOINIT(16); // ImapGenericVector[32]
74 INSERT_UNION_PADDING_BYTES(2); // ImapColor 74 INSERT_PADDING_BYTES_NOINIT(2); // ImapColor
75 union { 75 union {
76 BitField<0, 8, u16> clip_distances; 76 BitField<0, 8, u16> clip_distances;
77 BitField<8, 1, u16> point_sprite_s; 77 BitField<8, 1, u16> point_sprite_s;
@@ -82,20 +82,20 @@ struct Header {
82 BitField<14, 1, u16> instance_id; 82 BitField<14, 1, u16> instance_id;
83 BitField<15, 1, u16> vertex_id; 83 BitField<15, 1, u16> vertex_id;
84 }; 84 };
85 INSERT_UNION_PADDING_BYTES(5); // ImapFixedFncTexture[10] 85 INSERT_PADDING_BYTES_NOINIT(5); // ImapFixedFncTexture[10]
86 INSERT_UNION_PADDING_BYTES(1); // ImapReserved 86 INSERT_PADDING_BYTES_NOINIT(1); // ImapReserved
87 INSERT_UNION_PADDING_BYTES(3); // OmapSystemValuesA 87 INSERT_PADDING_BYTES_NOINIT(3); // OmapSystemValuesA
88 INSERT_UNION_PADDING_BYTES(1); // OmapSystemValuesB 88 INSERT_PADDING_BYTES_NOINIT(1); // OmapSystemValuesB
89 INSERT_UNION_PADDING_BYTES(16); // OmapGenericVector[32] 89 INSERT_PADDING_BYTES_NOINIT(16); // OmapGenericVector[32]
90 INSERT_UNION_PADDING_BYTES(2); // OmapColor 90 INSERT_PADDING_BYTES_NOINIT(2); // OmapColor
91 INSERT_UNION_PADDING_BYTES(2); // OmapSystemValuesC 91 INSERT_PADDING_BYTES_NOINIT(2); // OmapSystemValuesC
92 INSERT_UNION_PADDING_BYTES(5); // OmapFixedFncTexture[10] 92 INSERT_PADDING_BYTES_NOINIT(5); // OmapFixedFncTexture[10]
93 INSERT_UNION_PADDING_BYTES(1); // OmapReserved 93 INSERT_PADDING_BYTES_NOINIT(1); // OmapReserved
94 } vtg; 94 } vtg;
95 95
96 struct { 96 struct {
97 INSERT_UNION_PADDING_BYTES(3); // ImapSystemValuesA 97 INSERT_PADDING_BYTES_NOINIT(3); // ImapSystemValuesA
98 INSERT_UNION_PADDING_BYTES(1); // ImapSystemValuesB 98 INSERT_PADDING_BYTES_NOINIT(1); // ImapSystemValuesB
99 99
100 union { 100 union {
101 BitField<0, 2, PixelImap> x; 101 BitField<0, 2, PixelImap> x;
@@ -105,10 +105,10 @@ struct Header {
105 u8 raw; 105 u8 raw;
106 } imap_generic_vector[32]; 106 } imap_generic_vector[32];
107 107
108 INSERT_UNION_PADDING_BYTES(2); // ImapColor 108 INSERT_PADDING_BYTES_NOINIT(2); // ImapColor
109 INSERT_UNION_PADDING_BYTES(2); // ImapSystemValuesC 109 INSERT_PADDING_BYTES_NOINIT(2); // ImapSystemValuesC
110 INSERT_UNION_PADDING_BYTES(10); // ImapFixedFncTexture[10] 110 INSERT_PADDING_BYTES_NOINIT(10); // ImapFixedFncTexture[10]
111 INSERT_UNION_PADDING_BYTES(2); // ImapReserved 111 INSERT_PADDING_BYTES_NOINIT(2); // ImapReserved
112 112
113 struct { 113 struct {
114 u32 target; 114 u32 target;
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index d81e38680..b4ce6b154 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -270,7 +270,7 @@ public:
270 270
271 union { 271 union {
272 struct { 272 struct {
273 INSERT_UNION_PADDING_WORDS(0x4); 273 INSERT_PADDING_WORDS_NOINIT(0x4);
274 struct { 274 struct {
275 u32 address_high; 275 u32 address_high;
276 u32 address_low; 276 u32 address_low;
@@ -283,18 +283,18 @@ public:
283 283
284 u32 semaphore_sequence; 284 u32 semaphore_sequence;
285 u32 semaphore_trigger; 285 u32 semaphore_trigger;
286 INSERT_UNION_PADDING_WORDS(0xC); 286 INSERT_PADDING_WORDS_NOINIT(0xC);
287 287
288 // The pusher and the puller share the reference counter, the pusher only has read 288 // The pusher and the puller share the reference counter, the pusher only has read
289 // access 289 // access
290 u32 reference_count; 290 u32 reference_count;
291 INSERT_UNION_PADDING_WORDS(0x5); 291 INSERT_PADDING_WORDS_NOINIT(0x5);
292 292
293 u32 semaphore_acquire; 293 u32 semaphore_acquire;
294 u32 semaphore_release; 294 u32 semaphore_release;
295 u32 fence_value; 295 u32 fence_value;
296 FenceAction fence_action; 296 FenceAction fence_action;
297 INSERT_UNION_PADDING_WORDS(0xE2); 297 INSERT_PADDING_WORDS_NOINIT(0xE2);
298 298
299 // Puller state 299 // Puller state
300 u32 acquire_mode; 300 u32 acquire_mode;
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp
index b24179d59..81b71edfb 100644
--- a/src/video_core/renderer_opengl/gl_device.cpp
+++ b/src/video_core/renderer_opengl/gl_device.cpp
@@ -208,6 +208,7 @@ Device::Device()
208 208
209 const bool is_nvidia = vendor == "NVIDIA Corporation"; 209 const bool is_nvidia = vendor == "NVIDIA Corporation";
210 const bool is_amd = vendor == "ATI Technologies Inc."; 210 const bool is_amd = vendor == "ATI Technologies Inc.";
211 const bool is_intel = vendor == "Intel";
211 212
212 bool disable_fast_buffer_sub_data = false; 213 bool disable_fast_buffer_sub_data = false;
213 if (is_nvidia && version == "4.6.0 NVIDIA 443.24") { 214 if (is_nvidia && version == "4.6.0 NVIDIA 443.24") {
@@ -231,6 +232,7 @@ Device::Device()
231 has_variable_aoffi = TestVariableAoffi(); 232 has_variable_aoffi = TestVariableAoffi();
232 has_component_indexing_bug = is_amd; 233 has_component_indexing_bug = is_amd;
233 has_precise_bug = TestPreciseBug(); 234 has_precise_bug = TestPreciseBug();
235 has_broken_texture_view_formats = is_amd || is_intel;
234 has_nv_viewport_array2 = GLAD_GL_NV_viewport_array2; 236 has_nv_viewport_array2 = GLAD_GL_NV_viewport_array2;
235 has_vertex_buffer_unified_memory = GLAD_GL_NV_vertex_buffer_unified_memory; 237 has_vertex_buffer_unified_memory = GLAD_GL_NV_vertex_buffer_unified_memory;
236 has_debugging_tool_attached = IsDebugToolAttached(extensions); 238 has_debugging_tool_attached = IsDebugToolAttached(extensions);
@@ -248,6 +250,8 @@ Device::Device()
248 LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi); 250 LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi);
249 LOG_INFO(Render_OpenGL, "Renderer_ComponentIndexingBug: {}", has_component_indexing_bug); 251 LOG_INFO(Render_OpenGL, "Renderer_ComponentIndexingBug: {}", has_component_indexing_bug);
250 LOG_INFO(Render_OpenGL, "Renderer_PreciseBug: {}", has_precise_bug); 252 LOG_INFO(Render_OpenGL, "Renderer_PreciseBug: {}", has_precise_bug);
253 LOG_INFO(Render_OpenGL, "Renderer_BrokenTextureViewFormats: {}",
254 has_broken_texture_view_formats);
251 255
252 if (Settings::values.use_assembly_shaders.GetValue() && !use_assembly_shaders) { 256 if (Settings::values.use_assembly_shaders.GetValue() && !use_assembly_shaders) {
253 LOG_ERROR(Render_OpenGL, "Assembly shaders enabled but not supported"); 257 LOG_ERROR(Render_OpenGL, "Assembly shaders enabled but not supported");
diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h
index 13e66846c..3e79d1e37 100644
--- a/src/video_core/renderer_opengl/gl_device.h
+++ b/src/video_core/renderer_opengl/gl_device.h
@@ -96,6 +96,10 @@ public:
96 return has_precise_bug; 96 return has_precise_bug;
97 } 97 }
98 98
99 bool HasBrokenTextureViewFormats() const {
100 return has_broken_texture_view_formats;
101 }
102
99 bool HasFastBufferSubData() const { 103 bool HasFastBufferSubData() const {
100 return has_fast_buffer_sub_data; 104 return has_fast_buffer_sub_data;
101 } 105 }
@@ -137,6 +141,7 @@ private:
137 bool has_variable_aoffi{}; 141 bool has_variable_aoffi{};
138 bool has_component_indexing_bug{}; 142 bool has_component_indexing_bug{};
139 bool has_precise_bug{}; 143 bool has_precise_bug{};
144 bool has_broken_texture_view_formats{};
140 bool has_fast_buffer_sub_data{}; 145 bool has_fast_buffer_sub_data{};
141 bool has_nv_viewport_array2{}; 146 bool has_nv_viewport_array2{};
142 bool has_debugging_tool_attached{}; 147 bool has_debugging_tool_attached{};
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 4c690418c..546cb6d00 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -61,100 +61,99 @@ struct FormatTuple {
61 GLenum internal_format; 61 GLenum internal_format;
62 GLenum format = GL_NONE; 62 GLenum format = GL_NONE;
63 GLenum type = GL_NONE; 63 GLenum type = GL_NONE;
64 GLenum store_format = internal_format;
65}; 64};
66 65
67constexpr std::array<FormatTuple, MaxPixelFormat> FORMAT_TABLE = {{ 66constexpr std::array<FormatTuple, MaxPixelFormat> FORMAT_TABLE = {{
68 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV}, // A8B8G8R8_UNORM 67 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV}, // A8B8G8R8_UNORM
69 {GL_RGBA8_SNORM, GL_RGBA, GL_BYTE}, // A8B8G8R8_SNORM 68 {GL_RGBA8_SNORM, GL_RGBA, GL_BYTE}, // A8B8G8R8_SNORM
70 {GL_RGBA8I, GL_RGBA_INTEGER, GL_BYTE}, // A8B8G8R8_SINT 69 {GL_RGBA8I, GL_RGBA_INTEGER, GL_BYTE}, // A8B8G8R8_SINT
71 {GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE}, // A8B8G8R8_UINT 70 {GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE}, // A8B8G8R8_UINT
72 {GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, // R5G6B5_UNORM 71 {GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, // R5G6B5_UNORM
73 {GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV}, // B5G6R5_UNORM 72 {GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV}, // B5G6R5_UNORM
74 {GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, // A1R5G5B5_UNORM 73 {GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, // A1R5G5B5_UNORM
75 {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2B10G10R10_UNORM 74 {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2B10G10R10_UNORM
76 {GL_RGB10_A2UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2B10G10R10_UINT 75 {GL_RGB10_A2UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2B10G10R10_UINT
77 {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, // A1B5G5R5_UNORM 76 {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, // A1B5G5R5_UNORM
78 {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // R8_UNORM 77 {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // R8_UNORM
79 {GL_R8_SNORM, GL_RED, GL_BYTE}, // R8_SNORM 78 {GL_R8_SNORM, GL_RED, GL_BYTE}, // R8_SNORM
80 {GL_R8I, GL_RED_INTEGER, GL_BYTE}, // R8_SINT 79 {GL_R8I, GL_RED_INTEGER, GL_BYTE}, // R8_SINT
81 {GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE}, // R8_UINT 80 {GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE}, // R8_UINT
82 {GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT}, // R16G16B16A16_FLOAT 81 {GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT}, // R16G16B16A16_FLOAT
83 {GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT}, // R16G16B16A16_UNORM 82 {GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT}, // R16G16B16A16_UNORM
84 {GL_RGBA16_SNORM, GL_RGBA, GL_SHORT}, // R16G16B16A16_SNORM 83 {GL_RGBA16_SNORM, GL_RGBA, GL_SHORT}, // R16G16B16A16_SNORM
85 {GL_RGBA16I, GL_RGBA_INTEGER, GL_SHORT}, // R16G16B16A16_SINT 84 {GL_RGBA16I, GL_RGBA_INTEGER, GL_SHORT}, // R16G16B16A16_SINT
86 {GL_RGBA16UI, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT}, // R16G16B16A16_UINT 85 {GL_RGBA16UI, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT}, // R16G16B16A16_UINT
87 {GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV}, // B10G11R11_FLOAT 86 {GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV}, // B10G11R11_FLOAT
88 {GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT}, // R32G32B32A32_UINT 87 {GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT}, // R32G32B32A32_UINT
89 {GL_COMPRESSED_RGBA_S3TC_DXT1_EXT}, // BC1_RGBA_UNORM 88 {GL_COMPRESSED_RGBA_S3TC_DXT1_EXT}, // BC1_RGBA_UNORM
90 {GL_COMPRESSED_RGBA_S3TC_DXT3_EXT}, // BC2_UNORM 89 {GL_COMPRESSED_RGBA_S3TC_DXT3_EXT}, // BC2_UNORM
91 {GL_COMPRESSED_RGBA_S3TC_DXT5_EXT}, // BC3_UNORM 90 {GL_COMPRESSED_RGBA_S3TC_DXT5_EXT}, // BC3_UNORM
92 {GL_COMPRESSED_RED_RGTC1}, // BC4_UNORM 91 {GL_COMPRESSED_RED_RGTC1}, // BC4_UNORM
93 {GL_COMPRESSED_SIGNED_RED_RGTC1}, // BC4_SNORM 92 {GL_COMPRESSED_SIGNED_RED_RGTC1}, // BC4_SNORM
94 {GL_COMPRESSED_RG_RGTC2}, // BC5_UNORM 93 {GL_COMPRESSED_RG_RGTC2}, // BC5_UNORM
95 {GL_COMPRESSED_SIGNED_RG_RGTC2}, // BC5_SNORM 94 {GL_COMPRESSED_SIGNED_RG_RGTC2}, // BC5_SNORM
96 {GL_COMPRESSED_RGBA_BPTC_UNORM}, // BC7_UNORM 95 {GL_COMPRESSED_RGBA_BPTC_UNORM}, // BC7_UNORM
97 {GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT}, // BC6H_UFLOAT 96 {GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT}, // BC6H_UFLOAT
98 {GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT}, // BC6H_SFLOAT 97 {GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT}, // BC6H_SFLOAT
99 {GL_COMPRESSED_RGBA_ASTC_4x4_KHR}, // ASTC_2D_4X4_UNORM 98 {GL_COMPRESSED_RGBA_ASTC_4x4_KHR}, // ASTC_2D_4X4_UNORM
100 {GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE}, // B8G8R8A8_UNORM 99 {GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE}, // B8G8R8A8_UNORM
101 {GL_RGBA32F, GL_RGBA, GL_FLOAT}, // R32G32B32A32_FLOAT 100 {GL_RGBA32F, GL_RGBA, GL_FLOAT}, // R32G32B32A32_FLOAT
102 {GL_RGBA32I, GL_RGBA_INTEGER, GL_INT}, // R32G32B32A32_SINT 101 {GL_RGBA32I, GL_RGBA_INTEGER, GL_INT}, // R32G32B32A32_SINT
103 {GL_RG32F, GL_RG, GL_FLOAT}, // R32G32_FLOAT 102 {GL_RG32F, GL_RG, GL_FLOAT}, // R32G32_FLOAT
104 {GL_RG32I, GL_RG_INTEGER, GL_INT}, // R32G32_SINT 103 {GL_RG32I, GL_RG_INTEGER, GL_INT}, // R32G32_SINT
105 {GL_R32F, GL_RED, GL_FLOAT}, // R32_FLOAT 104 {GL_R32F, GL_RED, GL_FLOAT}, // R32_FLOAT
106 {GL_R16F, GL_RED, GL_HALF_FLOAT}, // R16_FLOAT 105 {GL_R16F, GL_RED, GL_HALF_FLOAT}, // R16_FLOAT
107 {GL_R16, GL_RED, GL_UNSIGNED_SHORT}, // R16_UNORM 106 {GL_R16, GL_RED, GL_UNSIGNED_SHORT}, // R16_UNORM
108 {GL_R16_SNORM, GL_RED, GL_SHORT}, // R16_SNORM 107 {GL_R16_SNORM, GL_RED, GL_SHORT}, // R16_SNORM
109 {GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT}, // R16_UINT 108 {GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT}, // R16_UINT
110 {GL_R16I, GL_RED_INTEGER, GL_SHORT}, // R16_SINT 109 {GL_R16I, GL_RED_INTEGER, GL_SHORT}, // R16_SINT
111 {GL_RG16, GL_RG, GL_UNSIGNED_SHORT}, // R16G16_UNORM 110 {GL_RG16, GL_RG, GL_UNSIGNED_SHORT}, // R16G16_UNORM
112 {GL_RG16F, GL_RG, GL_HALF_FLOAT}, // R16G16_FLOAT 111 {GL_RG16F, GL_RG, GL_HALF_FLOAT}, // R16G16_FLOAT
113 {GL_RG16UI, GL_RG_INTEGER, GL_UNSIGNED_SHORT}, // R16G16_UINT 112 {GL_RG16UI, GL_RG_INTEGER, GL_UNSIGNED_SHORT}, // R16G16_UINT
114 {GL_RG16I, GL_RG_INTEGER, GL_SHORT}, // R16G16_SINT 113 {GL_RG16I, GL_RG_INTEGER, GL_SHORT}, // R16G16_SINT
115 {GL_RG16_SNORM, GL_RG, GL_SHORT}, // R16G16_SNORM 114 {GL_RG16_SNORM, GL_RG, GL_SHORT}, // R16G16_SNORM
116 {GL_RGB32F, GL_RGB, GL_FLOAT}, // R32G32B32_FLOAT 115 {GL_RGB32F, GL_RGB, GL_FLOAT}, // R32G32B32_FLOAT
117 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, GL_RGBA8}, // A8B8G8R8_SRGB 116 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV}, // A8B8G8R8_SRGB
118 {GL_RG8, GL_RG, GL_UNSIGNED_BYTE}, // R8G8_UNORM 117 {GL_RG8, GL_RG, GL_UNSIGNED_BYTE}, // R8G8_UNORM
119 {GL_RG8_SNORM, GL_RG, GL_BYTE}, // R8G8_SNORM 118 {GL_RG8_SNORM, GL_RG, GL_BYTE}, // R8G8_SNORM
120 {GL_RG8I, GL_RG_INTEGER, GL_BYTE}, // R8G8_SINT 119 {GL_RG8I, GL_RG_INTEGER, GL_BYTE}, // R8G8_SINT
121 {GL_RG8UI, GL_RG_INTEGER, GL_UNSIGNED_BYTE}, // R8G8_UINT 120 {GL_RG8UI, GL_RG_INTEGER, GL_UNSIGNED_BYTE}, // R8G8_UINT
122 {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT}, // R32G32_UINT 121 {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT}, // R32G32_UINT
123 {GL_RGB16F, GL_RGBA, GL_HALF_FLOAT}, // R16G16B16X16_FLOAT 122 {GL_RGB16F, GL_RGBA, GL_HALF_FLOAT}, // R16G16B16X16_FLOAT
124 {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT}, // R32_UINT 123 {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT}, // R32_UINT
125 {GL_R32I, GL_RED_INTEGER, GL_INT}, // R32_SINT 124 {GL_R32I, GL_RED_INTEGER, GL_INT}, // R32_SINT
126 {GL_COMPRESSED_RGBA_ASTC_8x8_KHR}, // ASTC_2D_8X8_UNORM 125 {GL_COMPRESSED_RGBA_ASTC_8x8_KHR}, // ASTC_2D_8X8_UNORM
127 {GL_COMPRESSED_RGBA_ASTC_8x5_KHR}, // ASTC_2D_8X5_UNORM 126 {GL_COMPRESSED_RGBA_ASTC_8x5_KHR}, // ASTC_2D_8X5_UNORM
128 {GL_COMPRESSED_RGBA_ASTC_5x4_KHR}, // ASTC_2D_5X4_UNORM 127 {GL_COMPRESSED_RGBA_ASTC_5x4_KHR}, // ASTC_2D_5X4_UNORM
129 {GL_SRGB8_ALPHA8, GL_BGRA, GL_UNSIGNED_BYTE, GL_RGBA8}, // B8G8R8A8_UNORM 128 {GL_SRGB8_ALPHA8, GL_BGRA, GL_UNSIGNED_BYTE}, // B8G8R8A8_UNORM
130 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT}, // BC1_RGBA_SRGB 129 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT}, // BC1_RGBA_SRGB
131 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT}, // BC2_SRGB 130 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT}, // BC2_SRGB
132 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}, // BC3_SRGB 131 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}, // BC3_SRGB
133 {GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM}, // BC7_SRGB 132 {GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM}, // BC7_SRGB
134 {GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4_REV}, // A4B4G4R4_UNORM 133 {GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4_REV}, // A4B4G4R4_UNORM
135 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR}, // ASTC_2D_4X4_SRGB 134 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR}, // ASTC_2D_4X4_SRGB
136 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR}, // ASTC_2D_8X8_SRGB 135 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR}, // ASTC_2D_8X8_SRGB
137 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR}, // ASTC_2D_8X5_SRGB 136 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR}, // ASTC_2D_8X5_SRGB
138 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR}, // ASTC_2D_5X4_SRGB 137 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR}, // ASTC_2D_5X4_SRGB
139 {GL_COMPRESSED_RGBA_ASTC_5x5_KHR}, // ASTC_2D_5X5_UNORM 138 {GL_COMPRESSED_RGBA_ASTC_5x5_KHR}, // ASTC_2D_5X5_UNORM
140 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR}, // ASTC_2D_5X5_SRGB 139 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR}, // ASTC_2D_5X5_SRGB
141 {GL_COMPRESSED_RGBA_ASTC_10x8_KHR}, // ASTC_2D_10X8_UNORM 140 {GL_COMPRESSED_RGBA_ASTC_10x8_KHR}, // ASTC_2D_10X8_UNORM
142 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR}, // ASTC_2D_10X8_SRGB 141 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR}, // ASTC_2D_10X8_SRGB
143 {GL_COMPRESSED_RGBA_ASTC_6x6_KHR}, // ASTC_2D_6X6_UNORM 142 {GL_COMPRESSED_RGBA_ASTC_6x6_KHR}, // ASTC_2D_6X6_UNORM
144 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR}, // ASTC_2D_6X6_SRGB 143 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR}, // ASTC_2D_6X6_SRGB
145 {GL_COMPRESSED_RGBA_ASTC_10x10_KHR}, // ASTC_2D_10X10_UNORM 144 {GL_COMPRESSED_RGBA_ASTC_10x10_KHR}, // ASTC_2D_10X10_UNORM
146 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR}, // ASTC_2D_10X10_SRGB 145 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR}, // ASTC_2D_10X10_SRGB
147 {GL_COMPRESSED_RGBA_ASTC_12x12_KHR}, // ASTC_2D_12X12_UNORM 146 {GL_COMPRESSED_RGBA_ASTC_12x12_KHR}, // ASTC_2D_12X12_UNORM
148 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR}, // ASTC_2D_12X12_SRGB 147 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR}, // ASTC_2D_12X12_SRGB
149 {GL_COMPRESSED_RGBA_ASTC_8x6_KHR}, // ASTC_2D_8X6_UNORM 148 {GL_COMPRESSED_RGBA_ASTC_8x6_KHR}, // ASTC_2D_8X6_UNORM
150 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR}, // ASTC_2D_8X6_SRGB 149 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR}, // ASTC_2D_8X6_SRGB
151 {GL_COMPRESSED_RGBA_ASTC_6x5_KHR}, // ASTC_2D_6X5_UNORM 150 {GL_COMPRESSED_RGBA_ASTC_6x5_KHR}, // ASTC_2D_6X5_UNORM
152 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR}, // ASTC_2D_6X5_SRGB 151 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR}, // ASTC_2D_6X5_SRGB
153 {GL_RGB9_E5, GL_RGB, GL_UNSIGNED_INT_5_9_9_9_REV}, // E5B9G9R9_FLOAT 152 {GL_RGB9_E5, GL_RGB, GL_UNSIGNED_INT_5_9_9_9_REV}, // E5B9G9R9_FLOAT
154 {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT}, // D32_FLOAT 153 {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT}, // D32_FLOAT
155 {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, // D16_UNORM 154 {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, // D16_UNORM
156 {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // D24_UNORM_S8_UINT 155 {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // D24_UNORM_S8_UINT
157 {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // S8_UINT_D24_UNORM 156 {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // S8_UINT_D24_UNORM
158 {GL_DEPTH32F_STENCIL8, GL_DEPTH_STENCIL, 157 {GL_DEPTH32F_STENCIL8, GL_DEPTH_STENCIL,
159 GL_FLOAT_32_UNSIGNED_INT_24_8_REV}, // D32_FLOAT_S8_UINT 158 GL_FLOAT_32_UNSIGNED_INT_24_8_REV}, // D32_FLOAT_S8_UINT
160}}; 159}};
@@ -431,6 +430,8 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, ProgramManager&
431 format_properties[i].emplace(format, properties); 430 format_properties[i].emplace(format, properties);
432 } 431 }
433 } 432 }
433 has_broken_texture_view_formats = device.HasBrokenTextureViewFormats();
434
434 null_image_1d_array.Create(GL_TEXTURE_1D_ARRAY); 435 null_image_1d_array.Create(GL_TEXTURE_1D_ARRAY);
435 null_image_cube_array.Create(GL_TEXTURE_CUBE_MAP_ARRAY); 436 null_image_cube_array.Create(GL_TEXTURE_CUBE_MAP_ARRAY);
436 null_image_3d.Create(GL_TEXTURE_3D); 437 null_image_3d.Create(GL_TEXTURE_3D);
@@ -651,13 +652,11 @@ Image::Image(TextureCacheRuntime& runtime, const VideoCommon::ImageInfo& info_,
651 if (IsConverted(runtime.device, info.format, info.type)) { 652 if (IsConverted(runtime.device, info.format, info.type)) {
652 flags |= ImageFlagBits::Converted; 653 flags |= ImageFlagBits::Converted;
653 gl_internal_format = IsPixelFormatSRGB(info.format) ? GL_SRGB8_ALPHA8 : GL_RGBA8; 654 gl_internal_format = IsPixelFormatSRGB(info.format) ? GL_SRGB8_ALPHA8 : GL_RGBA8;
654 gl_store_format = GL_RGBA8;
655 gl_format = GL_RGBA; 655 gl_format = GL_RGBA;
656 gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; 656 gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
657 } else { 657 } else {
658 const auto& tuple = GetFormatTuple(info.format); 658 const auto& tuple = GetFormatTuple(info.format);
659 gl_internal_format = tuple.internal_format; 659 gl_internal_format = tuple.internal_format;
660 gl_store_format = tuple.store_format;
661 gl_format = tuple.format; 660 gl_format = tuple.format;
662 gl_type = tuple.type; 661 gl_type = tuple.type;
663 } 662 }
@@ -677,23 +676,23 @@ Image::Image(TextureCacheRuntime& runtime, const VideoCommon::ImageInfo& info_,
677 } 676 }
678 switch (target) { 677 switch (target) {
679 case GL_TEXTURE_1D_ARRAY: 678 case GL_TEXTURE_1D_ARRAY:
680 glTextureStorage2D(handle, num_levels, gl_store_format, width, num_layers); 679 glTextureStorage2D(handle, num_levels, gl_internal_format, width, num_layers);
681 break; 680 break;
682 case GL_TEXTURE_2D_ARRAY: 681 case GL_TEXTURE_2D_ARRAY:
683 glTextureStorage3D(handle, num_levels, gl_store_format, width, height, num_layers); 682 glTextureStorage3D(handle, num_levels, gl_internal_format, width, height, num_layers);
684 break; 683 break;
685 case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: { 684 case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: {
686 // TODO: Where should 'fixedsamplelocations' come from? 685 // TODO: Where should 'fixedsamplelocations' come from?
687 const auto [samples_x, samples_y] = SamplesLog2(info.num_samples); 686 const auto [samples_x, samples_y] = SamplesLog2(info.num_samples);
688 glTextureStorage3DMultisample(handle, num_samples, gl_store_format, width >> samples_x, 687 glTextureStorage3DMultisample(handle, num_samples, gl_internal_format, width >> samples_x,
689 height >> samples_y, num_layers, GL_FALSE); 688 height >> samples_y, num_layers, GL_FALSE);
690 break; 689 break;
691 } 690 }
692 case GL_TEXTURE_RECTANGLE: 691 case GL_TEXTURE_RECTANGLE:
693 glTextureStorage2D(handle, num_levels, gl_store_format, width, height); 692 glTextureStorage2D(handle, num_levels, gl_internal_format, width, height);
694 break; 693 break;
695 case GL_TEXTURE_3D: 694 case GL_TEXTURE_3D:
696 glTextureStorage3D(handle, num_levels, gl_store_format, width, height, depth); 695 glTextureStorage3D(handle, num_levels, gl_internal_format, width, height, depth);
697 break; 696 break;
698 case GL_TEXTURE_BUFFER: 697 case GL_TEXTURE_BUFFER:
699 buffer.Create(); 698 buffer.Create();
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index 04193e31e..15b7c3676 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -96,6 +96,10 @@ public:
96 96
97 FormatProperties FormatInfo(VideoCommon::ImageType type, GLenum internal_format) const; 97 FormatProperties FormatInfo(VideoCommon::ImageType type, GLenum internal_format) const;
98 98
99 bool HasBrokenTextureViewFormats() const noexcept {
100 return has_broken_texture_view_formats;
101 }
102
99private: 103private:
100 struct StagingBuffers { 104 struct StagingBuffers {
101 explicit StagingBuffers(GLenum storage_flags_, GLenum map_flags_); 105 explicit StagingBuffers(GLenum storage_flags_, GLenum map_flags_);
@@ -120,6 +124,7 @@ private:
120 UtilShaders util_shaders; 124 UtilShaders util_shaders;
121 125
122 std::array<std::unordered_map<GLenum, FormatProperties>, 3> format_properties; 126 std::array<std::unordered_map<GLenum, FormatProperties>, 3> format_properties;
127 bool has_broken_texture_view_formats = false;
123 128
124 StagingBuffers upload_buffers{GL_MAP_WRITE_BIT, GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT}; 129 StagingBuffers upload_buffers{GL_MAP_WRITE_BIT, GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT};
125 StagingBuffers download_buffers{GL_MAP_READ_BIT, GL_MAP_READ_BIT}; 130 StagingBuffers download_buffers{GL_MAP_READ_BIT, GL_MAP_READ_BIT};
@@ -165,7 +170,6 @@ private:
165 OGLTextureView store_view; 170 OGLTextureView store_view;
166 OGLBuffer buffer; 171 OGLBuffer buffer;
167 GLenum gl_internal_format = GL_NONE; 172 GLenum gl_internal_format = GL_NONE;
168 GLenum gl_store_format = GL_NONE;
169 GLenum gl_format = GL_NONE; 173 GLenum gl_format = GL_NONE;
170 GLenum gl_type = GL_NONE; 174 GLenum gl_type = GL_NONE;
171}; 175};
diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp
index 87c8e5693..1f6a169ae 100644
--- a/src/video_core/renderer_vulkan/blit_image.cpp
+++ b/src/video_core/renderer_vulkan/blit_image.cpp
@@ -11,14 +11,14 @@
11#include "video_core/host_shaders/vulkan_blit_depth_stencil_frag_spv.h" 11#include "video_core/host_shaders/vulkan_blit_depth_stencil_frag_spv.h"
12#include "video_core/renderer_vulkan/blit_image.h" 12#include "video_core/renderer_vulkan/blit_image.h"
13#include "video_core/renderer_vulkan/maxwell_to_vk.h" 13#include "video_core/renderer_vulkan/maxwell_to_vk.h"
14#include "video_core/renderer_vulkan/vk_device.h"
15#include "video_core/renderer_vulkan/vk_scheduler.h" 14#include "video_core/renderer_vulkan/vk_scheduler.h"
16#include "video_core/renderer_vulkan/vk_shader_util.h" 15#include "video_core/renderer_vulkan/vk_shader_util.h"
17#include "video_core/renderer_vulkan/vk_state_tracker.h" 16#include "video_core/renderer_vulkan/vk_state_tracker.h"
18#include "video_core/renderer_vulkan/vk_texture_cache.h" 17#include "video_core/renderer_vulkan/vk_texture_cache.h"
19#include "video_core/renderer_vulkan/vk_update_descriptor.h" 18#include "video_core/renderer_vulkan/vk_update_descriptor.h"
20#include "video_core/renderer_vulkan/wrapper.h"
21#include "video_core/surface.h" 19#include "video_core/surface.h"
20#include "video_core/vulkan_common/vulkan_device.h"
21#include "video_core/vulkan_common/vulkan_wrapper.h"
22 22
23namespace Vulkan { 23namespace Vulkan {
24 24
@@ -225,7 +225,7 @@ constexpr std::array<VkPipelineShaderStageCreateInfo, 2> MakeStages(
225 }; 225 };
226} 226}
227 227
228void UpdateOneTextureDescriptorSet(const VKDevice& device, VkDescriptorSet descriptor_set, 228void UpdateOneTextureDescriptorSet(const Device& device, VkDescriptorSet descriptor_set,
229 VkSampler sampler, VkImageView image_view) { 229 VkSampler sampler, VkImageView image_view) {
230 const VkDescriptorImageInfo image_info{ 230 const VkDescriptorImageInfo image_info{
231 .sampler = sampler, 231 .sampler = sampler,
@@ -247,7 +247,7 @@ void UpdateOneTextureDescriptorSet(const VKDevice& device, VkDescriptorSet descr
247 device.GetLogical().UpdateDescriptorSets(write_descriptor_set, nullptr); 247 device.GetLogical().UpdateDescriptorSets(write_descriptor_set, nullptr);
248} 248}
249 249
250void UpdateTwoTexturesDescriptorSet(const VKDevice& device, VkDescriptorSet descriptor_set, 250void UpdateTwoTexturesDescriptorSet(const Device& device, VkDescriptorSet descriptor_set,
251 VkSampler sampler, VkImageView image_view_0, 251 VkSampler sampler, VkImageView image_view_0,
252 VkImageView image_view_1) { 252 VkImageView image_view_1) {
253 const VkDescriptorImageInfo image_info_0{ 253 const VkDescriptorImageInfo image_info_0{
@@ -326,7 +326,7 @@ void BindBlitState(vk::CommandBuffer cmdbuf, VkPipelineLayout layout,
326 326
327} // Anonymous namespace 327} // Anonymous namespace
328 328
329BlitImageHelper::BlitImageHelper(const VKDevice& device_, VKScheduler& scheduler_, 329BlitImageHelper::BlitImageHelper(const Device& device_, VKScheduler& scheduler_,
330 StateTracker& state_tracker_, VKDescriptorPool& descriptor_pool) 330 StateTracker& state_tracker_, VKDescriptorPool& descriptor_pool)
331 : device{device_}, scheduler{scheduler_}, state_tracker{state_tracker_}, 331 : device{device_}, scheduler{scheduler_}, state_tracker{state_tracker_},
332 one_texture_set_layout(device.GetLogical().CreateDescriptorSetLayout( 332 one_texture_set_layout(device.GetLogical().CreateDescriptorSetLayout(
diff --git a/src/video_core/renderer_vulkan/blit_image.h b/src/video_core/renderer_vulkan/blit_image.h
index 2c2790bf9..43fd3d737 100644
--- a/src/video_core/renderer_vulkan/blit_image.h
+++ b/src/video_core/renderer_vulkan/blit_image.h
@@ -8,19 +8,18 @@
8 8
9#include "video_core/engines/fermi_2d.h" 9#include "video_core/engines/fermi_2d.h"
10#include "video_core/renderer_vulkan/vk_descriptor_pool.h" 10#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
11#include "video_core/renderer_vulkan/wrapper.h"
12#include "video_core/texture_cache/types.h" 11#include "video_core/texture_cache/types.h"
12#include "video_core/vulkan_common/vulkan_wrapper.h"
13 13
14namespace Vulkan { 14namespace Vulkan {
15 15
16using VideoCommon::Offset2D; 16using VideoCommon::Offset2D;
17 17
18class VKDevice; 18class Device;
19class VKScheduler;
20class StateTracker;
21
22class Framebuffer; 19class Framebuffer;
23class ImageView; 20class ImageView;
21class StateTracker;
22class VKScheduler;
24 23
25struct BlitImagePipelineKey { 24struct BlitImagePipelineKey {
26 constexpr auto operator<=>(const BlitImagePipelineKey&) const noexcept = default; 25 constexpr auto operator<=>(const BlitImagePipelineKey&) const noexcept = default;
@@ -31,7 +30,7 @@ struct BlitImagePipelineKey {
31 30
32class BlitImageHelper { 31class BlitImageHelper {
33public: 32public:
34 explicit BlitImageHelper(const VKDevice& device, VKScheduler& scheduler, 33 explicit BlitImageHelper(const Device& device, VKScheduler& scheduler,
35 StateTracker& state_tracker, VKDescriptorPool& descriptor_pool); 34 StateTracker& state_tracker, VKDescriptorPool& descriptor_pool);
36 ~BlitImageHelper(); 35 ~BlitImageHelper();
37 36
@@ -67,7 +66,7 @@ private:
67 66
68 void ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass); 67 void ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass);
69 68
70 const VKDevice& device; 69 const Device& device;
71 VKScheduler& scheduler; 70 VKScheduler& scheduler;
72 StateTracker& state_tracker; 71 StateTracker& state_tracker;
73 72
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
index 67dd10500..5be6dabd9 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
@@ -76,7 +76,7 @@ void FixedPipelineState::Fill(const Maxwell& regs, bool has_extended_dynamic_sta
76 regs.instanced_arrays.IsInstancingEnabled(index) ? regs.vertex_array[index].divisor : 0; 76 regs.instanced_arrays.IsInstancingEnabled(index) ? regs.vertex_array[index].divisor : 0;
77 } 77 }
78 78
79 for (std::size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) { 79 for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
80 const auto& input = regs.vertex_attrib_format[index]; 80 const auto& input = regs.vertex_attrib_format[index];
81 auto& attribute = attributes[index]; 81 auto& attribute = attributes[index];
82 attribute.raw = 0; 82 attribute.raw = 0;
@@ -85,6 +85,7 @@ void FixedPipelineState::Fill(const Maxwell& regs, bool has_extended_dynamic_sta
85 attribute.offset.Assign(input.offset); 85 attribute.offset.Assign(input.offset);
86 attribute.type.Assign(static_cast<u32>(input.type.Value())); 86 attribute.type.Assign(static_cast<u32>(input.type.Value()));
87 attribute.size.Assign(static_cast<u32>(input.size.Value())); 87 attribute.size.Assign(static_cast<u32>(input.size.Value()));
88 attribute.binding_index_enabled.Assign(regs.vertex_array[index].IsEnabled() ? 1 : 0);
88 } 89 }
89 90
90 for (std::size_t index = 0; index < std::size(attachments); ++index) { 91 for (std::size_t index = 0; index < std::size(attachments); ++index) {
@@ -172,14 +173,9 @@ void FixedPipelineState::DynamicState::Fill(const Maxwell& regs) {
172 depth_test_func.Assign(PackComparisonOp(regs.depth_test_func)); 173 depth_test_func.Assign(PackComparisonOp(regs.depth_test_func));
173 cull_face.Assign(PackCullFace(regs.cull_face)); 174 cull_face.Assign(PackCullFace(regs.cull_face));
174 cull_enable.Assign(regs.cull_test_enabled != 0 ? 1 : 0); 175 cull_enable.Assign(regs.cull_test_enabled != 0 ? 1 : 0);
175 176 std::ranges::transform(regs.vertex_array, vertex_strides.begin(), [](const auto& array) {
176 for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { 177 return static_cast<u16>(array.stride.Value());
177 const auto& input = regs.vertex_array[index]; 178 });
178 VertexBinding& binding = vertex_bindings[index];
179 binding.raw = 0;
180 binding.enabled.Assign(input.IsEnabled() ? 1 : 0);
181 binding.stride.Assign(static_cast<u16>(input.stride.Value()));
182 }
183} 179}
184 180
185std::size_t FixedPipelineState::Hash() const noexcept { 181std::size_t FixedPipelineState::Hash() const noexcept {
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
index 7e95e6fce..465a55fdb 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
@@ -96,6 +96,8 @@ struct FixedPipelineState {
96 BitField<6, 14, u32> offset; 96 BitField<6, 14, u32> offset;
97 BitField<20, 3, u32> type; 97 BitField<20, 3, u32> type;
98 BitField<23, 6, u32> size; 98 BitField<23, 6, u32> size;
99 // Not really an element of a vertex attribute, but it can be packed here
100 BitField<29, 1, u32> binding_index_enabled;
99 101
100 constexpr Maxwell::VertexAttribute::Type Type() const noexcept { 102 constexpr Maxwell::VertexAttribute::Type Type() const noexcept {
101 return static_cast<Maxwell::VertexAttribute::Type>(type.Value()); 103 return static_cast<Maxwell::VertexAttribute::Type>(type.Value());
@@ -130,12 +132,6 @@ struct FixedPipelineState {
130 } 132 }
131 }; 133 };
132 134
133 union VertexBinding {
134 u16 raw;
135 BitField<0, 12, u16> stride;
136 BitField<12, 1, u16> enabled;
137 };
138
139 struct DynamicState { 135 struct DynamicState {
140 union { 136 union {
141 u32 raw1; 137 u32 raw1;
@@ -153,7 +149,8 @@ struct FixedPipelineState {
153 BitField<0, 2, u32> cull_face; 149 BitField<0, 2, u32> cull_face;
154 BitField<2, 1, u32> cull_enable; 150 BitField<2, 1, u32> cull_enable;
155 }; 151 };
156 std::array<VertexBinding, Maxwell::NumVertexArrays> vertex_bindings; 152 // Vertex stride is a 12 bits value, we have 4 bits to spare per element
153 std::array<u16, Maxwell::NumVertexArrays> vertex_strides;
157 154
158 void Fill(const Maxwell& regs); 155 void Fill(const Maxwell& regs);
159 156
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index 4c988429f..ca7c2c579 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
@@ -9,9 +9,9 @@
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "video_core/engines/maxwell_3d.h" 10#include "video_core/engines/maxwell_3d.h"
11#include "video_core/renderer_vulkan/maxwell_to_vk.h" 11#include "video_core/renderer_vulkan/maxwell_to_vk.h"
12#include "video_core/renderer_vulkan/vk_device.h"
13#include "video_core/renderer_vulkan/wrapper.h"
14#include "video_core/surface.h" 12#include "video_core/surface.h"
13#include "video_core/vulkan_common/vulkan_device.h"
14#include "video_core/vulkan_common/vulkan_wrapper.h"
15 15
16namespace Vulkan::MaxwellToVK { 16namespace Vulkan::MaxwellToVK {
17 17
@@ -47,7 +47,7 @@ VkSamplerMipmapMode MipmapMode(Tegra::Texture::TextureMipmapFilter mipmap_filter
47 return {}; 47 return {};
48} 48}
49 49
50VkSamplerAddressMode WrapMode(const VKDevice& device, Tegra::Texture::WrapMode wrap_mode, 50VkSamplerAddressMode WrapMode(const Device& device, Tegra::Texture::WrapMode wrap_mode,
51 Tegra::Texture::TextureFilter filter) { 51 Tegra::Texture::TextureFilter filter) {
52 switch (wrap_mode) { 52 switch (wrap_mode) {
53 case Tegra::Texture::WrapMode::Wrap: 53 case Tegra::Texture::WrapMode::Wrap:
@@ -222,7 +222,7 @@ constexpr bool IsZetaFormat(PixelFormat pixel_format) {
222 222
223} // Anonymous namespace 223} // Anonymous namespace
224 224
225FormatInfo SurfaceFormat(const VKDevice& device, FormatType format_type, PixelFormat pixel_format) { 225FormatInfo SurfaceFormat(const Device& device, FormatType format_type, PixelFormat pixel_format) {
226 ASSERT(static_cast<std::size_t>(pixel_format) < std::size(tex_format_tuples)); 226 ASSERT(static_cast<std::size_t>(pixel_format) < std::size(tex_format_tuples));
227 227
228 auto tuple = tex_format_tuples[static_cast<std::size_t>(pixel_format)]; 228 auto tuple = tex_format_tuples[static_cast<std::size_t>(pixel_format)];
@@ -280,7 +280,7 @@ VkShaderStageFlagBits ShaderStage(Tegra::Engines::ShaderType stage) {
280 return {}; 280 return {};
281} 281}
282 282
283VkPrimitiveTopology PrimitiveTopology([[maybe_unused]] const VKDevice& device, 283VkPrimitiveTopology PrimitiveTopology([[maybe_unused]] const Device& device,
284 Maxwell::PrimitiveTopology topology) { 284 Maxwell::PrimitiveTopology topology) {
285 switch (topology) { 285 switch (topology) {
286 case Maxwell::PrimitiveTopology::Points: 286 case Maxwell::PrimitiveTopology::Points:
@@ -526,7 +526,7 @@ VkCompareOp ComparisonOp(Maxwell::ComparisonOp comparison) {
526 return {}; 526 return {};
527} 527}
528 528
529VkIndexType IndexFormat(const VKDevice& device, Maxwell::IndexFormat index_format) { 529VkIndexType IndexFormat(const Device& device, Maxwell::IndexFormat index_format) {
530 switch (index_format) { 530 switch (index_format) {
531 case Maxwell::IndexFormat::UnsignedByte: 531 case Maxwell::IndexFormat::UnsignedByte:
532 if (!device.IsExtIndexTypeUint8Supported()) { 532 if (!device.IsExtIndexTypeUint8Supported()) {
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.h b/src/video_core/renderer_vulkan/maxwell_to_vk.h
index 1a90f192e..537969840 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.h
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.h
@@ -6,10 +6,10 @@
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "video_core/engines/maxwell_3d.h" 8#include "video_core/engines/maxwell_3d.h"
9#include "video_core/renderer_vulkan/vk_device.h"
10#include "video_core/renderer_vulkan/wrapper.h"
11#include "video_core/surface.h" 9#include "video_core/surface.h"
12#include "video_core/textures/texture.h" 10#include "video_core/textures/texture.h"
11#include "video_core/vulkan_common/vulkan_device.h"
12#include "video_core/vulkan_common/vulkan_wrapper.h"
13 13
14namespace Vulkan::MaxwellToVK { 14namespace Vulkan::MaxwellToVK {
15 15
@@ -22,7 +22,7 @@ VkFilter Filter(Tegra::Texture::TextureFilter filter);
22 22
23VkSamplerMipmapMode MipmapMode(Tegra::Texture::TextureMipmapFilter mipmap_filter); 23VkSamplerMipmapMode MipmapMode(Tegra::Texture::TextureMipmapFilter mipmap_filter);
24 24
25VkSamplerAddressMode WrapMode(const VKDevice& device, Tegra::Texture::WrapMode wrap_mode, 25VkSamplerAddressMode WrapMode(const Device& device, Tegra::Texture::WrapMode wrap_mode,
26 Tegra::Texture::TextureFilter filter); 26 Tegra::Texture::TextureFilter filter);
27 27
28VkCompareOp DepthCompareFunction(Tegra::Texture::DepthCompareFunc depth_compare_func); 28VkCompareOp DepthCompareFunction(Tegra::Texture::DepthCompareFunc depth_compare_func);
@@ -35,17 +35,17 @@ struct FormatInfo {
35 bool storage; 35 bool storage;
36}; 36};
37 37
38FormatInfo SurfaceFormat(const VKDevice& device, FormatType format_type, PixelFormat pixel_format); 38FormatInfo SurfaceFormat(const Device& device, FormatType format_type, PixelFormat pixel_format);
39 39
40VkShaderStageFlagBits ShaderStage(Tegra::Engines::ShaderType stage); 40VkShaderStageFlagBits ShaderStage(Tegra::Engines::ShaderType stage);
41 41
42VkPrimitiveTopology PrimitiveTopology(const VKDevice& device, Maxwell::PrimitiveTopology topology); 42VkPrimitiveTopology PrimitiveTopology(const Device& device, Maxwell::PrimitiveTopology topology);
43 43
44VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttribute::Size size); 44VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttribute::Size size);
45 45
46VkCompareOp ComparisonOp(Maxwell::ComparisonOp comparison); 46VkCompareOp ComparisonOp(Maxwell::ComparisonOp comparison);
47 47
48VkIndexType IndexFormat(const VKDevice& device, Maxwell::IndexFormat index_format); 48VkIndexType IndexFormat(const Device& device, Maxwell::IndexFormat index_format);
49 49
50VkStencilOp StencilOp(Maxwell::StencilOp stencil_op); 50VkStencilOp StencilOp(Maxwell::StencilOp stencil_op);
51 51
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index 7f521cb9b..61796e33a 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -12,8 +12,6 @@
12 12
13#include <fmt/format.h> 13#include <fmt/format.h>
14 14
15#include "common/dynamic_library.h"
16#include "common/file_util.h"
17#include "common/logging/log.h" 15#include "common/logging/log.h"
18#include "common/telemetry.h" 16#include "common/telemetry.h"
19#include "core/core.h" 17#include "core/core.h"
@@ -24,182 +22,27 @@
24#include "video_core/gpu.h" 22#include "video_core/gpu.h"
25#include "video_core/renderer_vulkan/renderer_vulkan.h" 23#include "video_core/renderer_vulkan/renderer_vulkan.h"
26#include "video_core/renderer_vulkan/vk_blit_screen.h" 24#include "video_core/renderer_vulkan/vk_blit_screen.h"
27#include "video_core/renderer_vulkan/vk_device.h"
28#include "video_core/renderer_vulkan/vk_master_semaphore.h" 25#include "video_core/renderer_vulkan/vk_master_semaphore.h"
29#include "video_core/renderer_vulkan/vk_memory_manager.h"
30#include "video_core/renderer_vulkan/vk_rasterizer.h" 26#include "video_core/renderer_vulkan/vk_rasterizer.h"
31#include "video_core/renderer_vulkan/vk_scheduler.h" 27#include "video_core/renderer_vulkan/vk_scheduler.h"
32#include "video_core/renderer_vulkan/vk_state_tracker.h" 28#include "video_core/renderer_vulkan/vk_state_tracker.h"
33#include "video_core/renderer_vulkan/vk_swapchain.h" 29#include "video_core/renderer_vulkan/vk_swapchain.h"
34#include "video_core/renderer_vulkan/wrapper.h" 30#include "video_core/vulkan_common/vulkan_debug_callback.h"
35 31#include "video_core/vulkan_common/vulkan_device.h"
36// Include these late to avoid polluting previous headers 32#include "video_core/vulkan_common/vulkan_instance.h"
37#ifdef _WIN32 33#include "video_core/vulkan_common/vulkan_library.h"
38#include <windows.h> 34#include "video_core/vulkan_common/vulkan_memory_allocator.h"
39// ensure include order 35#include "video_core/vulkan_common/vulkan_surface.h"
40#include <vulkan/vulkan_win32.h> 36#include "video_core/vulkan_common/vulkan_wrapper.h"
41#endif
42
43#if !defined(_WIN32) && !defined(__APPLE__)
44#include <X11/Xlib.h>
45#include <vulkan/vulkan_wayland.h>
46#include <vulkan/vulkan_xlib.h>
47#endif
48 37
49namespace Vulkan { 38namespace Vulkan {
50
51namespace { 39namespace {
52
53using Core::Frontend::WindowSystemType;
54
55VkBool32 DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
56 VkDebugUtilsMessageTypeFlagsEXT type,
57 const VkDebugUtilsMessengerCallbackDataEXT* data,
58 [[maybe_unused]] void* user_data) {
59 const char* const message{data->pMessage};
60
61 if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
62 LOG_CRITICAL(Render_Vulkan, "{}", message);
63 } else if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
64 LOG_WARNING(Render_Vulkan, "{}", message);
65 } else if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) {
66 LOG_INFO(Render_Vulkan, "{}", message);
67 } else if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) {
68 LOG_DEBUG(Render_Vulkan, "{}", message);
69 }
70 return VK_FALSE;
71}
72
73Common::DynamicLibrary OpenVulkanLibrary() {
74 Common::DynamicLibrary library;
75#ifdef __APPLE__
76 // Check if a path to a specific Vulkan library has been specified.
77 char* libvulkan_env = getenv("LIBVULKAN_PATH");
78 if (!libvulkan_env || !library.Open(libvulkan_env)) {
79 // Use the libvulkan.dylib from the application bundle.
80 const std::string filename =
81 Common::FS::GetBundleDirectory() + "/Contents/Frameworks/libvulkan.dylib";
82 library.Open(filename.c_str());
83 }
84#else
85 std::string filename = Common::DynamicLibrary::GetVersionedFilename("vulkan", 1);
86 if (!library.Open(filename.c_str())) {
87 // Android devices may not have libvulkan.so.1, only libvulkan.so.
88 filename = Common::DynamicLibrary::GetVersionedFilename("vulkan");
89 (void)library.Open(filename.c_str());
90 }
91#endif
92 return library;
93}
94
95std::pair<vk::Instance, u32> CreateInstance(Common::DynamicLibrary& library,
96 vk::InstanceDispatch& dld, WindowSystemType window_type,
97 bool enable_debug_utils, bool enable_layers) {
98 if (!library.IsOpen()) {
99 LOG_ERROR(Render_Vulkan, "Vulkan library not available");
100 return {};
101 }
102 if (!library.GetSymbol("vkGetInstanceProcAddr", &dld.vkGetInstanceProcAddr)) {
103 LOG_ERROR(Render_Vulkan, "vkGetInstanceProcAddr not present in Vulkan");
104 return {};
105 }
106 if (!vk::Load(dld)) {
107 LOG_ERROR(Render_Vulkan, "Failed to load Vulkan function pointers");
108 return {};
109 }
110
111 std::vector<const char*> extensions;
112 extensions.reserve(6);
113 switch (window_type) {
114 case Core::Frontend::WindowSystemType::Headless:
115 break;
116#ifdef _WIN32
117 case Core::Frontend::WindowSystemType::Windows:
118 extensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
119 break;
120#endif
121#if !defined(_WIN32) && !defined(__APPLE__)
122 case Core::Frontend::WindowSystemType::X11:
123 extensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
124 break;
125 case Core::Frontend::WindowSystemType::Wayland:
126 extensions.push_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME);
127 break;
128#endif
129 default:
130 LOG_ERROR(Render_Vulkan, "Presentation not supported on this platform");
131 break;
132 }
133 if (window_type != Core::Frontend::WindowSystemType::Headless) {
134 extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
135 }
136 if (enable_debug_utils) {
137 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
138 }
139 extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
140
141 const std::optional properties = vk::EnumerateInstanceExtensionProperties(dld);
142 if (!properties) {
143 LOG_ERROR(Render_Vulkan, "Failed to query extension properties");
144 return {};
145 }
146
147 for (const char* extension : extensions) {
148 const auto it =
149 std::find_if(properties->begin(), properties->end(), [extension](const auto& prop) {
150 return !std::strcmp(extension, prop.extensionName);
151 });
152 if (it == properties->end()) {
153 LOG_ERROR(Render_Vulkan, "Required instance extension {} is not available", extension);
154 return {};
155 }
156 }
157
158 std::vector<const char*> layers;
159 layers.reserve(1);
160 if (enable_layers) {
161 layers.push_back("VK_LAYER_KHRONOS_validation");
162 }
163
164 const std::optional layer_properties = vk::EnumerateInstanceLayerProperties(dld);
165 if (!layer_properties) {
166 LOG_ERROR(Render_Vulkan, "Failed to query layer properties, disabling layers");
167 layers.clear();
168 }
169
170 for (auto layer_it = layers.begin(); layer_it != layers.end();) {
171 const char* const layer = *layer_it;
172 const auto it = std::find_if(
173 layer_properties->begin(), layer_properties->end(),
174 [layer](const VkLayerProperties& prop) { return !std::strcmp(layer, prop.layerName); });
175 if (it == layer_properties->end()) {
176 LOG_ERROR(Render_Vulkan, "Layer {} not available, removing it", layer);
177 layer_it = layers.erase(layer_it);
178 } else {
179 ++layer_it;
180 }
181 }
182
183 // Limit the maximum version of Vulkan to avoid using untested version.
184 const u32 version = std::min(vk::AvailableVersion(dld), static_cast<u32>(VK_API_VERSION_1_1));
185
186 vk::Instance instance = vk::Instance::Create(version, layers, extensions, dld);
187 if (!instance) {
188 LOG_ERROR(Render_Vulkan, "Failed to create Vulkan instance");
189 return {};
190 }
191 if (!vk::Load(*instance, dld)) {
192 LOG_ERROR(Render_Vulkan, "Failed to load Vulkan instance function pointers");
193 }
194 return std::make_pair(std::move(instance), version);
195}
196
197std::string GetReadableVersion(u32 version) { 40std::string GetReadableVersion(u32 version) {
198 return fmt::format("{}.{}.{}", VK_VERSION_MAJOR(version), VK_VERSION_MINOR(version), 41 return fmt::format("{}.{}.{}", VK_VERSION_MAJOR(version), VK_VERSION_MINOR(version),
199 VK_VERSION_PATCH(version)); 42 VK_VERSION_PATCH(version));
200} 43}
201 44
202std::string GetDriverVersion(const VKDevice& device) { 45std::string GetDriverVersion(const Device& device) {
203 // Extracted from 46 // Extracted from
204 // https://github.com/SaschaWillems/vulkan.gpuinfo.org/blob/5dddea46ea1120b0df14eef8f15ff8e318e35462/functions.php#L308-L314 47 // https://github.com/SaschaWillems/vulkan.gpuinfo.org/blob/5dddea46ea1120b0df14eef8f15ff8e318e35462/functions.php#L308-L314
205 const u32 version = device.GetDriverVersion(); 48 const u32 version = device.GetDriverVersion();
@@ -216,7 +59,6 @@ std::string GetDriverVersion(const VKDevice& device) {
216 const u32 minor = version & 0x3fff; 59 const u32 minor = version & 0x3fff;
217 return fmt::format("{}.{}", major, minor); 60 return fmt::format("{}.{}", major, minor);
218 } 61 }
219
220 return GetReadableVersion(version); 62 return GetReadableVersion(version);
221} 63}
222 64
@@ -255,7 +97,6 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
255 if (!framebuffer) { 97 if (!framebuffer) {
256 return; 98 return;
257 } 99 }
258
259 const auto& layout = render_window.GetFramebufferLayout(); 100 const auto& layout = render_window.GetFramebufferLayout();
260 if (layout.width > 0 && layout.height > 0 && render_window.IsShown()) { 101 if (layout.width > 0 && layout.height > 0 && render_window.IsShown()) {
261 const VAddr framebuffer_addr = framebuffer->address + framebuffer->offset; 102 const VAddr framebuffer_addr = framebuffer->address + framebuffer->offset;
@@ -284,17 +125,19 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
284 render_window.OnFrameDisplayed(); 125 render_window.OnFrameDisplayed();
285} 126}
286 127
287bool RendererVulkan::Init() { 128bool RendererVulkan::Init() try {
288 library = OpenVulkanLibrary(); 129 library = OpenLibrary();
289 std::tie(instance, instance_version) = CreateInstance( 130 instance = CreateInstance(library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type,
290 library, dld, render_window.GetWindowInfo().type, true, Settings::values.renderer_debug); 131 true, Settings::values.renderer_debug);
291 if (!instance || !CreateDebugCallback() || !CreateSurface() || !PickDevices()) { 132 if (Settings::values.renderer_debug) {
292 return false; 133 debug_callback = CreateDebugCallback(instance);
293 } 134 }
135 surface = CreateSurface(instance, render_window);
294 136
137 InitializeDevice();
295 Report(); 138 Report();
296 139
297 memory_manager = std::make_unique<VKMemoryManager>(*device); 140 memory_allocator = std::make_unique<MemoryAllocator>(*device);
298 141
299 state_tracker = std::make_unique<StateTracker>(gpu); 142 state_tracker = std::make_unique<StateTracker>(gpu);
300 143
@@ -306,13 +149,16 @@ bool RendererVulkan::Init() {
306 149
307 rasterizer = std::make_unique<RasterizerVulkan>(render_window, gpu, gpu.MemoryManager(), 150 rasterizer = std::make_unique<RasterizerVulkan>(render_window, gpu, gpu.MemoryManager(),
308 cpu_memory, screen_info, *device, 151 cpu_memory, screen_info, *device,
309 *memory_manager, *state_tracker, *scheduler); 152 *memory_allocator, *state_tracker, *scheduler);
310 153
311 blit_screen = 154 blit_screen =
312 std::make_unique<VKBlitScreen>(cpu_memory, render_window, *rasterizer, *device, 155 std::make_unique<VKBlitScreen>(cpu_memory, render_window, *rasterizer, *device,
313 *memory_manager, *swapchain, *scheduler, screen_info); 156 *memory_allocator, *swapchain, *scheduler, screen_info);
314
315 return true; 157 return true;
158
159} catch (const vk::Exception& exception) {
160 LOG_ERROR(Render_Vulkan, "Vulkan initialization failed with error: {}", exception.what());
161 return false;
316} 162}
317 163
318void RendererVulkan::ShutDown() { 164void RendererVulkan::ShutDown() {
@@ -322,104 +168,23 @@ void RendererVulkan::ShutDown() {
322 if (const auto& dev = device->GetLogical()) { 168 if (const auto& dev = device->GetLogical()) {
323 dev.WaitIdle(); 169 dev.WaitIdle();
324 } 170 }
325
326 rasterizer.reset(); 171 rasterizer.reset();
327 blit_screen.reset(); 172 blit_screen.reset();
328 scheduler.reset(); 173 scheduler.reset();
329 swapchain.reset(); 174 swapchain.reset();
330 memory_manager.reset(); 175 memory_allocator.reset();
331 device.reset(); 176 device.reset();
332} 177}
333 178
334bool RendererVulkan::CreateDebugCallback() { 179void RendererVulkan::InitializeDevice() {
335 if (!Settings::values.renderer_debug) { 180 const std::vector<VkPhysicalDevice> devices = instance.EnumeratePhysicalDevices();
336 return true;
337 }
338 debug_callback = instance.TryCreateDebugCallback(DebugCallback);
339 if (!debug_callback) {
340 LOG_ERROR(Render_Vulkan, "Failed to create debug callback");
341 return false;
342 }
343 return true;
344}
345
346bool RendererVulkan::CreateSurface() {
347 [[maybe_unused]] const auto& window_info = render_window.GetWindowInfo();
348 VkSurfaceKHR unsafe_surface = nullptr;
349
350#ifdef _WIN32
351 if (window_info.type == Core::Frontend::WindowSystemType::Windows) {
352 const HWND hWnd = static_cast<HWND>(window_info.render_surface);
353 const VkWin32SurfaceCreateInfoKHR win32_ci{VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR,
354 nullptr, 0, nullptr, hWnd};
355 const auto vkCreateWin32SurfaceKHR = reinterpret_cast<PFN_vkCreateWin32SurfaceKHR>(
356 dld.vkGetInstanceProcAddr(*instance, "vkCreateWin32SurfaceKHR"));
357 if (!vkCreateWin32SurfaceKHR ||
358 vkCreateWin32SurfaceKHR(*instance, &win32_ci, nullptr, &unsafe_surface) != VK_SUCCESS) {
359 LOG_ERROR(Render_Vulkan, "Failed to initialize Win32 surface");
360 return false;
361 }
362 }
363#endif
364#if !defined(_WIN32) && !defined(__APPLE__)
365 if (window_info.type == Core::Frontend::WindowSystemType::X11) {
366 const VkXlibSurfaceCreateInfoKHR xlib_ci{
367 VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, nullptr, 0,
368 static_cast<Display*>(window_info.display_connection),
369 reinterpret_cast<Window>(window_info.render_surface)};
370 const auto vkCreateXlibSurfaceKHR = reinterpret_cast<PFN_vkCreateXlibSurfaceKHR>(
371 dld.vkGetInstanceProcAddr(*instance, "vkCreateXlibSurfaceKHR"));
372 if (!vkCreateXlibSurfaceKHR ||
373 vkCreateXlibSurfaceKHR(*instance, &xlib_ci, nullptr, &unsafe_surface) != VK_SUCCESS) {
374 LOG_ERROR(Render_Vulkan, "Failed to initialize Xlib surface");
375 return false;
376 }
377 }
378 if (window_info.type == Core::Frontend::WindowSystemType::Wayland) {
379 const VkWaylandSurfaceCreateInfoKHR wayland_ci{
380 VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR, nullptr, 0,
381 static_cast<wl_display*>(window_info.display_connection),
382 static_cast<wl_surface*>(window_info.render_surface)};
383 const auto vkCreateWaylandSurfaceKHR = reinterpret_cast<PFN_vkCreateWaylandSurfaceKHR>(
384 dld.vkGetInstanceProcAddr(*instance, "vkCreateWaylandSurfaceKHR"));
385 if (!vkCreateWaylandSurfaceKHR ||
386 vkCreateWaylandSurfaceKHR(*instance, &wayland_ci, nullptr, &unsafe_surface) !=
387 VK_SUCCESS) {
388 LOG_ERROR(Render_Vulkan, "Failed to initialize Wayland surface");
389 return false;
390 }
391 }
392#endif
393 if (!unsafe_surface) {
394 LOG_ERROR(Render_Vulkan, "Presentation not supported on this platform");
395 return false;
396 }
397
398 surface = vk::SurfaceKHR(unsafe_surface, *instance, dld);
399 return true;
400}
401
402bool RendererVulkan::PickDevices() {
403 const auto devices = instance.EnumeratePhysicalDevices();
404 if (!devices) {
405 LOG_ERROR(Render_Vulkan, "Failed to enumerate physical devices");
406 return false;
407 }
408
409 const s32 device_index = Settings::values.vulkan_device.GetValue(); 181 const s32 device_index = Settings::values.vulkan_device.GetValue();
410 if (device_index < 0 || device_index >= static_cast<s32>(devices->size())) { 182 if (device_index < 0 || device_index >= static_cast<s32>(devices.size())) {
411 LOG_ERROR(Render_Vulkan, "Invalid device index {}!", device_index); 183 LOG_ERROR(Render_Vulkan, "Invalid device index {}!", device_index);
412 return false; 184 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
413 }
414 const vk::PhysicalDevice physical_device((*devices)[static_cast<std::size_t>(device_index)],
415 dld);
416 if (!VKDevice::IsSuitable(physical_device, *surface)) {
417 return false;
418 } 185 }
419 186 const vk::PhysicalDevice physical_device(devices[static_cast<size_t>(device_index)], dld);
420 device = 187 device = std::make_unique<Device>(*instance, physical_device, *surface, dld);
421 std::make_unique<VKDevice>(*instance, instance_version, physical_device, *surface, dld);
422 return device->Create();
423} 188}
424 189
425void RendererVulkan::Report() const { 190void RendererVulkan::Report() const {
@@ -444,26 +209,21 @@ void RendererVulkan::Report() const {
444 telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions); 209 telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions);
445} 210}
446 211
447std::vector<std::string> RendererVulkan::EnumerateDevices() { 212std::vector<std::string> RendererVulkan::EnumerateDevices() try {
448 vk::InstanceDispatch dld; 213 vk::InstanceDispatch dld;
449 Common::DynamicLibrary library = OpenVulkanLibrary(); 214 const Common::DynamicLibrary library = OpenLibrary();
450 vk::Instance instance = 215 const vk::Instance instance = CreateInstance(library, dld, VK_API_VERSION_1_0);
451 CreateInstance(library, dld, WindowSystemType::Headless, false, false).first; 216 const std::vector<VkPhysicalDevice> physical_devices = instance.EnumeratePhysicalDevices();
452 if (!instance) {
453 return {};
454 }
455
456 const std::optional physical_devices = instance.EnumeratePhysicalDevices();
457 if (!physical_devices) {
458 return {};
459 }
460
461 std::vector<std::string> names; 217 std::vector<std::string> names;
462 names.reserve(physical_devices->size()); 218 names.reserve(physical_devices.size());
463 for (const auto& device : *physical_devices) { 219 for (const VkPhysicalDevice device : physical_devices) {
464 names.push_back(vk::PhysicalDevice(device, dld).GetProperties().deviceName); 220 names.push_back(vk::PhysicalDevice(device, dld).GetProperties().deviceName);
465 } 221 }
466 return names; 222 return names;
223
224} catch (const vk::Exception& exception) {
225 LOG_ERROR(Render_Vulkan, "Failed to enumerate devices with error: {}", exception.what());
226 return {};
467} 227}
468 228
469} // namespace Vulkan 229} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index 74642fba4..daf55b9b4 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -11,7 +11,7 @@
11#include "common/dynamic_library.h" 11#include "common/dynamic_library.h"
12 12
13#include "video_core/renderer_base.h" 13#include "video_core/renderer_base.h"
14#include "video_core/renderer_vulkan/wrapper.h" 14#include "video_core/vulkan_common/vulkan_wrapper.h"
15 15
16namespace Core { 16namespace Core {
17class TelemetrySession; 17class TelemetrySession;
@@ -27,10 +27,10 @@ class GPU;
27 27
28namespace Vulkan { 28namespace Vulkan {
29 29
30class Device;
30class StateTracker; 31class StateTracker;
32class MemoryAllocator;
31class VKBlitScreen; 33class VKBlitScreen;
32class VKDevice;
33class VKMemoryManager;
34class VKSwapchain; 34class VKSwapchain;
35class VKScheduler; 35class VKScheduler;
36 36
@@ -56,11 +56,7 @@ public:
56 static std::vector<std::string> EnumerateDevices(); 56 static std::vector<std::string> EnumerateDevices();
57 57
58private: 58private:
59 bool CreateDebugCallback(); 59 void InitializeDevice();
60
61 bool CreateSurface();
62
63 bool PickDevices();
64 60
65 void Report() const; 61 void Report() const;
66 62
@@ -72,15 +68,14 @@ private:
72 vk::InstanceDispatch dld; 68 vk::InstanceDispatch dld;
73 69
74 vk::Instance instance; 70 vk::Instance instance;
75 u32 instance_version{};
76 71
77 vk::SurfaceKHR surface; 72 vk::SurfaceKHR surface;
78 73
79 VKScreenInfo screen_info; 74 VKScreenInfo screen_info;
80 75
81 vk::DebugCallback debug_callback; 76 vk::DebugUtilsMessenger debug_callback;
82 std::unique_ptr<VKDevice> device; 77 std::unique_ptr<Device> device;
83 std::unique_ptr<VKMemoryManager> memory_manager; 78 std::unique_ptr<MemoryAllocator> memory_allocator;
84 std::unique_ptr<StateTracker> state_tracker; 79 std::unique_ptr<StateTracker> state_tracker;
85 std::unique_ptr<VKScheduler> scheduler; 80 std::unique_ptr<VKScheduler> scheduler;
86 std::unique_ptr<VKSwapchain> swapchain; 81 std::unique_ptr<VKSwapchain> swapchain;
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index d3a83f22f..3e3b895e0 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -21,15 +21,15 @@
21#include "video_core/rasterizer_interface.h" 21#include "video_core/rasterizer_interface.h"
22#include "video_core/renderer_vulkan/renderer_vulkan.h" 22#include "video_core/renderer_vulkan/renderer_vulkan.h"
23#include "video_core/renderer_vulkan/vk_blit_screen.h" 23#include "video_core/renderer_vulkan/vk_blit_screen.h"
24#include "video_core/renderer_vulkan/vk_device.h"
25#include "video_core/renderer_vulkan/vk_master_semaphore.h" 24#include "video_core/renderer_vulkan/vk_master_semaphore.h"
26#include "video_core/renderer_vulkan/vk_memory_manager.h"
27#include "video_core/renderer_vulkan/vk_scheduler.h" 25#include "video_core/renderer_vulkan/vk_scheduler.h"
28#include "video_core/renderer_vulkan/vk_shader_util.h" 26#include "video_core/renderer_vulkan/vk_shader_util.h"
29#include "video_core/renderer_vulkan/vk_swapchain.h" 27#include "video_core/renderer_vulkan/vk_swapchain.h"
30#include "video_core/renderer_vulkan/wrapper.h"
31#include "video_core/surface.h" 28#include "video_core/surface.h"
32#include "video_core/textures/decoders.h" 29#include "video_core/textures/decoders.h"
30#include "video_core/vulkan_common/vulkan_device.h"
31#include "video_core/vulkan_common/vulkan_memory_allocator.h"
32#include "video_core/vulkan_common/vulkan_wrapper.h"
33 33
34namespace Vulkan { 34namespace Vulkan {
35 35
@@ -114,11 +114,11 @@ struct VKBlitScreen::BufferData {
114 114
115VKBlitScreen::VKBlitScreen(Core::Memory::Memory& cpu_memory_, 115VKBlitScreen::VKBlitScreen(Core::Memory::Memory& cpu_memory_,
116 Core::Frontend::EmuWindow& render_window_, 116 Core::Frontend::EmuWindow& render_window_,
117 VideoCore::RasterizerInterface& rasterizer_, const VKDevice& device_, 117 VideoCore::RasterizerInterface& rasterizer_, const Device& device_,
118 VKMemoryManager& memory_manager_, VKSwapchain& swapchain_, 118 MemoryAllocator& memory_allocator_, VKSwapchain& swapchain_,
119 VKScheduler& scheduler_, const VKScreenInfo& screen_info_) 119 VKScheduler& scheduler_, const VKScreenInfo& screen_info_)
120 : cpu_memory{cpu_memory_}, render_window{render_window_}, rasterizer{rasterizer_}, 120 : cpu_memory{cpu_memory_}, render_window{render_window_}, rasterizer{rasterizer_},
121 device{device_}, memory_manager{memory_manager_}, swapchain{swapchain_}, 121 device{device_}, memory_allocator{memory_allocator_}, swapchain{swapchain_},
122 scheduler{scheduler_}, image_count{swapchain.GetImageCount()}, screen_info{screen_info_} { 122 scheduler{scheduler_}, image_count{swapchain.GetImageCount()}, screen_info{screen_info_} {
123 resource_ticks.resize(image_count); 123 resource_ticks.resize(image_count);
124 124
@@ -150,8 +150,8 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, bool
150 SetUniformData(data, framebuffer); 150 SetUniformData(data, framebuffer);
151 SetVertexData(data, framebuffer); 151 SetVertexData(data, framebuffer);
152 152
153 auto map = buffer_commit->Map(); 153 const std::span<u8> map = buffer_commit.Map();
154 std::memcpy(map.Address(), &data, sizeof(data)); 154 std::memcpy(map.data(), &data, sizeof(data));
155 155
156 if (!use_accelerated) { 156 if (!use_accelerated) {
157 const u64 image_offset = GetRawImageOffset(framebuffer, image_index); 157 const u64 image_offset = GetRawImageOffset(framebuffer, image_index);
@@ -165,8 +165,8 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, bool
165 constexpr u32 block_height_log2 = 4; 165 constexpr u32 block_height_log2 = 4;
166 const u32 bytes_per_pixel = GetBytesPerPixel(framebuffer); 166 const u32 bytes_per_pixel = GetBytesPerPixel(framebuffer);
167 Tegra::Texture::UnswizzleTexture( 167 Tegra::Texture::UnswizzleTexture(
168 std::span(map.Address() + image_offset, size_bytes), std::span(host_ptr, size_bytes), 168 map.subspan(image_offset, size_bytes), std::span(host_ptr, size_bytes), bytes_per_pixel,
169 bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0); 169 framebuffer.width, framebuffer.height, 1, block_height_log2, 0);
170 170
171 const VkBufferImageCopy copy{ 171 const VkBufferImageCopy copy{
172 .bufferOffset = image_offset, 172 .bufferOffset = image_offset,
@@ -224,8 +224,6 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, bool
224 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, write_barrier); 224 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, write_barrier);
225 }); 225 });
226 } 226 }
227 map.Release();
228
229 scheduler.Record([renderpass = *renderpass, framebuffer = *framebuffers[image_index], 227 scheduler.Record([renderpass = *renderpass, framebuffer = *framebuffers[image_index],
230 descriptor_set = descriptor_sets[image_index], buffer = *buffer, 228 descriptor_set = descriptor_sets[image_index], buffer = *buffer,
231 size = swapchain.GetSize(), pipeline = *pipeline, 229 size = swapchain.GetSize(), pipeline = *pipeline,
@@ -642,7 +640,7 @@ void VKBlitScreen::ReleaseRawImages() {
642 raw_images.clear(); 640 raw_images.clear();
643 raw_buffer_commits.clear(); 641 raw_buffer_commits.clear();
644 buffer.reset(); 642 buffer.reset();
645 buffer_commit.reset(); 643 buffer_commit = MemoryCommit{};
646} 644}
647 645
648void VKBlitScreen::CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer) { 646void VKBlitScreen::CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer) {
@@ -659,7 +657,7 @@ void VKBlitScreen::CreateStagingBuffer(const Tegra::FramebufferConfig& framebuff
659 }; 657 };
660 658
661 buffer = device.GetLogical().CreateBuffer(ci); 659 buffer = device.GetLogical().CreateBuffer(ci);
662 buffer_commit = memory_manager.Commit(buffer, true); 660 buffer_commit = memory_allocator.Commit(buffer, MemoryUsage::Upload);
663} 661}
664 662
665void VKBlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) { 663void VKBlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) {
@@ -690,7 +688,7 @@ void VKBlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer)
690 .pQueueFamilyIndices = nullptr, 688 .pQueueFamilyIndices = nullptr,
691 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, 689 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
692 }); 690 });
693 raw_buffer_commits[i] = memory_manager.Commit(raw_images[i], false); 691 raw_buffer_commits[i] = memory_allocator.Commit(raw_images[i], MemoryUsage::DeviceLocal);
694 raw_image_views[i] = device.GetLogical().CreateImageView(VkImageViewCreateInfo{ 692 raw_image_views[i] = device.GetLogical().CreateImageView(VkImageViewCreateInfo{
695 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, 693 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
696 .pNext = nullptr, 694 .pNext = nullptr,
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.h b/src/video_core/renderer_vulkan/vk_blit_screen.h
index 2ee374247..b52576957 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.h
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.h
@@ -6,8 +6,8 @@
6 6
7#include <memory> 7#include <memory>
8 8
9#include "video_core/renderer_vulkan/vk_memory_manager.h" 9#include "video_core/vulkan_common/vulkan_memory_allocator.h"
10#include "video_core/renderer_vulkan/wrapper.h" 10#include "video_core/vulkan_common/vulkan_wrapper.h"
11 11
12namespace Core { 12namespace Core {
13class System; 13class System;
@@ -33,8 +33,8 @@ namespace Vulkan {
33 33
34struct ScreenInfo; 34struct ScreenInfo;
35 35
36class Device;
36class RasterizerVulkan; 37class RasterizerVulkan;
37class VKDevice;
38class VKScheduler; 38class VKScheduler;
39class VKSwapchain; 39class VKSwapchain;
40 40
@@ -42,8 +42,8 @@ class VKBlitScreen final {
42public: 42public:
43 explicit VKBlitScreen(Core::Memory::Memory& cpu_memory, 43 explicit VKBlitScreen(Core::Memory::Memory& cpu_memory,
44 Core::Frontend::EmuWindow& render_window, 44 Core::Frontend::EmuWindow& render_window,
45 VideoCore::RasterizerInterface& rasterizer, const VKDevice& device, 45 VideoCore::RasterizerInterface& rasterizer, const Device& device,
46 VKMemoryManager& memory_manager, VKSwapchain& swapchain, 46 MemoryAllocator& memory_allocator, VKSwapchain& swapchain,
47 VKScheduler& scheduler, const VKScreenInfo& screen_info); 47 VKScheduler& scheduler, const VKScreenInfo& screen_info);
48 ~VKBlitScreen(); 48 ~VKBlitScreen();
49 49
@@ -85,8 +85,8 @@ private:
85 Core::Memory::Memory& cpu_memory; 85 Core::Memory::Memory& cpu_memory;
86 Core::Frontend::EmuWindow& render_window; 86 Core::Frontend::EmuWindow& render_window;
87 VideoCore::RasterizerInterface& rasterizer; 87 VideoCore::RasterizerInterface& rasterizer;
88 const VKDevice& device; 88 const Device& device;
89 VKMemoryManager& memory_manager; 89 MemoryAllocator& memory_allocator;
90 VKSwapchain& swapchain; 90 VKSwapchain& swapchain;
91 VKScheduler& scheduler; 91 VKScheduler& scheduler;
92 const std::size_t image_count; 92 const std::size_t image_count;
@@ -104,14 +104,14 @@ private:
104 vk::Sampler sampler; 104 vk::Sampler sampler;
105 105
106 vk::Buffer buffer; 106 vk::Buffer buffer;
107 VKMemoryCommit buffer_commit; 107 MemoryCommit buffer_commit;
108 108
109 std::vector<u64> resource_ticks; 109 std::vector<u64> resource_ticks;
110 110
111 std::vector<vk::Semaphore> semaphores; 111 std::vector<vk::Semaphore> semaphores;
112 std::vector<vk::Image> raw_images; 112 std::vector<vk::Image> raw_images;
113 std::vector<vk::ImageView> raw_image_views; 113 std::vector<vk::ImageView> raw_image_views;
114 std::vector<VKMemoryCommit> raw_buffer_commits; 114 std::vector<MemoryCommit> raw_buffer_commits;
115 u32 raw_width = 0; 115 u32 raw_width = 0;
116 u32 raw_height = 0; 116 u32 raw_height = 0;
117}; 117};
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 10d296c2f..d8ad40a0f 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -9,10 +9,10 @@
9#include "core/core.h" 9#include "core/core.h"
10#include "video_core/buffer_cache/buffer_cache.h" 10#include "video_core/buffer_cache/buffer_cache.h"
11#include "video_core/renderer_vulkan/vk_buffer_cache.h" 11#include "video_core/renderer_vulkan/vk_buffer_cache.h"
12#include "video_core/renderer_vulkan/vk_device.h"
13#include "video_core/renderer_vulkan/vk_scheduler.h" 12#include "video_core/renderer_vulkan/vk_scheduler.h"
14#include "video_core/renderer_vulkan/vk_stream_buffer.h" 13#include "video_core/renderer_vulkan/vk_stream_buffer.h"
15#include "video_core/renderer_vulkan/wrapper.h" 14#include "video_core/vulkan_common/vulkan_device.h"
15#include "video_core/vulkan_common/vulkan_wrapper.h"
16 16
17namespace Vulkan { 17namespace Vulkan {
18 18
@@ -34,17 +34,13 @@ constexpr VkAccessFlags UPLOAD_ACCESS_BARRIERS =
34constexpr VkAccessFlags TRANSFORM_FEEDBACK_WRITE_ACCESS = 34constexpr VkAccessFlags TRANSFORM_FEEDBACK_WRITE_ACCESS =
35 VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT | VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT; 35 VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT | VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT;
36 36
37std::unique_ptr<VKStreamBuffer> CreateStreamBuffer(const VKDevice& device, VKScheduler& scheduler) {
38 return std::make_unique<VKStreamBuffer>(device, scheduler);
39}
40
41} // Anonymous namespace 37} // Anonymous namespace
42 38
43Buffer::Buffer(const VKDevice& device_, VKMemoryManager& memory_manager, VKScheduler& scheduler_, 39Buffer::Buffer(const Device& device_, MemoryAllocator& memory_allocator, VKScheduler& scheduler_,
44 VKStagingBufferPool& staging_pool_, VAddr cpu_addr_, std::size_t size_) 40 StagingBufferPool& staging_pool_, VAddr cpu_addr_, std::size_t size_)
45 : BufferBlock{cpu_addr_, size_}, device{device_}, scheduler{scheduler_}, staging_pool{ 41 : BufferBlock{cpu_addr_, size_}, device{device_}, scheduler{scheduler_}, staging_pool{
46 staging_pool_} { 42 staging_pool_} {
47 const VkBufferCreateInfo ci{ 43 buffer = device.GetLogical().CreateBuffer(VkBufferCreateInfo{
48 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, 44 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
49 .pNext = nullptr, 45 .pNext = nullptr,
50 .flags = 0, 46 .flags = 0,
@@ -53,22 +49,20 @@ Buffer::Buffer(const VKDevice& device_, VKMemoryManager& memory_manager, VKSched
53 .sharingMode = VK_SHARING_MODE_EXCLUSIVE, 49 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
54 .queueFamilyIndexCount = 0, 50 .queueFamilyIndexCount = 0,
55 .pQueueFamilyIndices = nullptr, 51 .pQueueFamilyIndices = nullptr,
56 }; 52 });
57 53 commit = memory_allocator.Commit(buffer, MemoryUsage::DeviceLocal);
58 buffer.handle = device.GetLogical().CreateBuffer(ci);
59 buffer.commit = memory_manager.Commit(buffer.handle, false);
60} 54}
61 55
62Buffer::~Buffer() = default; 56Buffer::~Buffer() = default;
63 57
64void Buffer::Upload(std::size_t offset, std::size_t data_size, const u8* data) { 58void Buffer::Upload(std::size_t offset, std::size_t data_size, const u8* data) {
65 const auto& staging = staging_pool.GetUnusedBuffer(data_size, true); 59 const auto& staging = staging_pool.Request(data_size, MemoryUsage::Upload);
66 std::memcpy(staging.commit->Map(data_size), data, data_size); 60 std::memcpy(staging.mapped_span.data(), data, data_size);
67 61
68 scheduler.RequestOutsideRenderPassOperationContext(); 62 scheduler.RequestOutsideRenderPassOperationContext();
69 63
70 const VkBuffer handle = Handle(); 64 const VkBuffer handle = Handle();
71 scheduler.Record([staging = *staging.handle, handle, offset, data_size, 65 scheduler.Record([staging = staging.buffer, handle, offset, data_size,
72 &device = device](vk::CommandBuffer cmdbuf) { 66 &device = device](vk::CommandBuffer cmdbuf) {
73 const VkBufferMemoryBarrier read_barrier{ 67 const VkBufferMemoryBarrier read_barrier{
74 .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, 68 .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
@@ -104,12 +98,12 @@ void Buffer::Upload(std::size_t offset, std::size_t data_size, const u8* data) {
104} 98}
105 99
106void Buffer::Download(std::size_t offset, std::size_t data_size, u8* data) { 100void Buffer::Download(std::size_t offset, std::size_t data_size, u8* data) {
107 const auto& staging = staging_pool.GetUnusedBuffer(data_size, true); 101 auto staging = staging_pool.Request(data_size, MemoryUsage::Download);
108 scheduler.RequestOutsideRenderPassOperationContext(); 102 scheduler.RequestOutsideRenderPassOperationContext();
109 103
110 const VkBuffer handle = Handle(); 104 const VkBuffer handle = Handle();
111 scheduler.Record( 105 scheduler.Record(
112 [staging = *staging.handle, handle, offset, data_size](vk::CommandBuffer cmdbuf) { 106 [staging = staging.buffer, handle, offset, data_size](vk::CommandBuffer cmdbuf) {
113 const VkBufferMemoryBarrier barrier{ 107 const VkBufferMemoryBarrier barrier{
114 .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, 108 .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
115 .pNext = nullptr, 109 .pNext = nullptr,
@@ -130,7 +124,7 @@ void Buffer::Download(std::size_t offset, std::size_t data_size, u8* data) {
130 }); 124 });
131 scheduler.Finish(); 125 scheduler.Finish();
132 126
133 std::memcpy(data, staging.commit->Map(data_size), data_size); 127 std::memcpy(data, staging.mapped_span.data(), data_size);
134} 128}
135 129
136void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, 130void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset,
@@ -168,29 +162,29 @@ void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst
168 162
169VKBufferCache::VKBufferCache(VideoCore::RasterizerInterface& rasterizer_, 163VKBufferCache::VKBufferCache(VideoCore::RasterizerInterface& rasterizer_,
170 Tegra::MemoryManager& gpu_memory_, Core::Memory::Memory& cpu_memory_, 164 Tegra::MemoryManager& gpu_memory_, Core::Memory::Memory& cpu_memory_,
171 const VKDevice& device_, VKMemoryManager& memory_manager_, 165 const Device& device_, MemoryAllocator& memory_allocator_,
172 VKScheduler& scheduler_, VKStreamBuffer& stream_buffer_, 166 VKScheduler& scheduler_, VKStreamBuffer& stream_buffer_,
173 VKStagingBufferPool& staging_pool_) 167 StagingBufferPool& staging_pool_)
174 : VideoCommon::BufferCache<Buffer, VkBuffer, VKStreamBuffer>{rasterizer_, gpu_memory_, 168 : VideoCommon::BufferCache<Buffer, VkBuffer, VKStreamBuffer>{rasterizer_, gpu_memory_,
175 cpu_memory_, stream_buffer_}, 169 cpu_memory_, stream_buffer_},
176 device{device_}, memory_manager{memory_manager_}, scheduler{scheduler_}, staging_pool{ 170 device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_},
177 staging_pool_} {} 171 staging_pool{staging_pool_} {}
178 172
179VKBufferCache::~VKBufferCache() = default; 173VKBufferCache::~VKBufferCache() = default;
180 174
181std::shared_ptr<Buffer> VKBufferCache::CreateBlock(VAddr cpu_addr, std::size_t size) { 175std::shared_ptr<Buffer> VKBufferCache::CreateBlock(VAddr cpu_addr, std::size_t size) {
182 return std::make_shared<Buffer>(device, memory_manager, scheduler, staging_pool, cpu_addr, 176 return std::make_shared<Buffer>(device, memory_allocator, scheduler, staging_pool, cpu_addr,
183 size); 177 size);
184} 178}
185 179
186VKBufferCache::BufferInfo VKBufferCache::GetEmptyBuffer(std::size_t size) { 180VKBufferCache::BufferInfo VKBufferCache::GetEmptyBuffer(std::size_t size) {
187 size = std::max(size, std::size_t(4)); 181 size = std::max(size, std::size_t(4));
188 const auto& empty = staging_pool.GetUnusedBuffer(size, false); 182 const auto& empty = staging_pool.Request(size, MemoryUsage::DeviceLocal);
189 scheduler.RequestOutsideRenderPassOperationContext(); 183 scheduler.RequestOutsideRenderPassOperationContext();
190 scheduler.Record([size, buffer = *empty.handle](vk::CommandBuffer cmdbuf) { 184 scheduler.Record([size, buffer = empty.buffer](vk::CommandBuffer cmdbuf) {
191 cmdbuf.FillBuffer(buffer, 0, size, 0); 185 cmdbuf.FillBuffer(buffer, 0, size, 0);
192 }); 186 });
193 return {*empty.handle, 0, 0}; 187 return {empty.buffer, 0, 0};
194} 188}
195 189
196} // namespace Vulkan 190} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h
index daf498222..41d577510 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.h
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h
@@ -8,21 +8,20 @@
8 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "video_core/buffer_cache/buffer_cache.h" 10#include "video_core/buffer_cache/buffer_cache.h"
11#include "video_core/renderer_vulkan/vk_memory_manager.h"
12#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" 11#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
13#include "video_core/renderer_vulkan/vk_stream_buffer.h" 12#include "video_core/renderer_vulkan/vk_stream_buffer.h"
14#include "video_core/renderer_vulkan/wrapper.h" 13#include "video_core/vulkan_common/vulkan_memory_allocator.h"
14#include "video_core/vulkan_common/vulkan_wrapper.h"
15 15
16namespace Vulkan { 16namespace Vulkan {
17 17
18class VKDevice; 18class Device;
19class VKMemoryManager;
20class VKScheduler; 19class VKScheduler;
21 20
22class Buffer final : public VideoCommon::BufferBlock { 21class Buffer final : public VideoCommon::BufferBlock {
23public: 22public:
24 explicit Buffer(const VKDevice& device, VKMemoryManager& memory_manager, VKScheduler& scheduler, 23 explicit Buffer(const Device& device, MemoryAllocator& memory_allocator, VKScheduler& scheduler,
25 VKStagingBufferPool& staging_pool, VAddr cpu_addr_, std::size_t size_); 24 StagingBufferPool& staging_pool, VAddr cpu_addr_, std::size_t size_);
26 ~Buffer(); 25 ~Buffer();
27 26
28 void Upload(std::size_t offset, std::size_t data_size, const u8* data); 27 void Upload(std::size_t offset, std::size_t data_size, const u8* data);
@@ -33,7 +32,7 @@ public:
33 std::size_t copy_size); 32 std::size_t copy_size);
34 33
35 VkBuffer Handle() const { 34 VkBuffer Handle() const {
36 return *buffer.handle; 35 return *buffer;
37 } 36 }
38 37
39 u64 Address() const { 38 u64 Address() const {
@@ -41,20 +40,21 @@ public:
41 } 40 }
42 41
43private: 42private:
44 const VKDevice& device; 43 const Device& device;
45 VKScheduler& scheduler; 44 VKScheduler& scheduler;
46 VKStagingBufferPool& staging_pool; 45 StagingBufferPool& staging_pool;
47 46
48 VKBuffer buffer; 47 vk::Buffer buffer;
48 MemoryCommit commit;
49}; 49};
50 50
51class VKBufferCache final : public VideoCommon::BufferCache<Buffer, VkBuffer, VKStreamBuffer> { 51class VKBufferCache final : public VideoCommon::BufferCache<Buffer, VkBuffer, VKStreamBuffer> {
52public: 52public:
53 explicit VKBufferCache(VideoCore::RasterizerInterface& rasterizer, 53 explicit VKBufferCache(VideoCore::RasterizerInterface& rasterizer,
54 Tegra::MemoryManager& gpu_memory, Core::Memory::Memory& cpu_memory, 54 Tegra::MemoryManager& gpu_memory, Core::Memory::Memory& cpu_memory,
55 const VKDevice& device, VKMemoryManager& memory_manager, 55 const Device& device, MemoryAllocator& memory_allocator,
56 VKScheduler& scheduler, VKStreamBuffer& stream_buffer, 56 VKScheduler& scheduler, VKStreamBuffer& stream_buffer,
57 VKStagingBufferPool& staging_pool); 57 StagingBufferPool& staging_pool);
58 ~VKBufferCache(); 58 ~VKBufferCache();
59 59
60 BufferInfo GetEmptyBuffer(std::size_t size) override; 60 BufferInfo GetEmptyBuffer(std::size_t size) override;
@@ -63,10 +63,10 @@ protected:
63 std::shared_ptr<Buffer> CreateBlock(VAddr cpu_addr, std::size_t size) override; 63 std::shared_ptr<Buffer> CreateBlock(VAddr cpu_addr, std::size_t size) override;
64 64
65private: 65private:
66 const VKDevice& device; 66 const Device& device;
67 VKMemoryManager& memory_manager; 67 MemoryAllocator& memory_allocator;
68 VKScheduler& scheduler; 68 VKScheduler& scheduler;
69 VKStagingBufferPool& staging_pool; 69 StagingBufferPool& staging_pool;
70}; 70};
71 71
72} // namespace Vulkan 72} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_command_pool.cpp b/src/video_core/renderer_vulkan/vk_command_pool.cpp
index 8f7d6410e..a99df9323 100644
--- a/src/video_core/renderer_vulkan/vk_command_pool.cpp
+++ b/src/video_core/renderer_vulkan/vk_command_pool.cpp
@@ -5,8 +5,8 @@
5#include <cstddef> 5#include <cstddef>
6 6
7#include "video_core/renderer_vulkan/vk_command_pool.h" 7#include "video_core/renderer_vulkan/vk_command_pool.h"
8#include "video_core/renderer_vulkan/vk_device.h" 8#include "video_core/vulkan_common/vulkan_device.h"
9#include "video_core/renderer_vulkan/wrapper.h" 9#include "video_core/vulkan_common/vulkan_wrapper.h"
10 10
11namespace Vulkan { 11namespace Vulkan {
12 12
@@ -17,7 +17,7 @@ struct CommandPool::Pool {
17 vk::CommandBuffers cmdbufs; 17 vk::CommandBuffers cmdbufs;
18}; 18};
19 19
20CommandPool::CommandPool(MasterSemaphore& master_semaphore_, const VKDevice& device_) 20CommandPool::CommandPool(MasterSemaphore& master_semaphore_, const Device& device_)
21 : ResourcePool(master_semaphore_, COMMAND_BUFFER_POOL_SIZE), device{device_} {} 21 : ResourcePool(master_semaphore_, COMMAND_BUFFER_POOL_SIZE), device{device_} {}
22 22
23CommandPool::~CommandPool() = default; 23CommandPool::~CommandPool() = default;
diff --git a/src/video_core/renderer_vulkan/vk_command_pool.h b/src/video_core/renderer_vulkan/vk_command_pool.h
index 62a7ce3f1..61c26a22a 100644
--- a/src/video_core/renderer_vulkan/vk_command_pool.h
+++ b/src/video_core/renderer_vulkan/vk_command_pool.h
@@ -8,16 +8,16 @@
8#include <vector> 8#include <vector>
9 9
10#include "video_core/renderer_vulkan/vk_resource_pool.h" 10#include "video_core/renderer_vulkan/vk_resource_pool.h"
11#include "video_core/renderer_vulkan/wrapper.h" 11#include "video_core/vulkan_common/vulkan_wrapper.h"
12 12
13namespace Vulkan { 13namespace Vulkan {
14 14
15class Device;
15class MasterSemaphore; 16class MasterSemaphore;
16class VKDevice;
17 17
18class CommandPool final : public ResourcePool { 18class CommandPool final : public ResourcePool {
19public: 19public:
20 explicit CommandPool(MasterSemaphore& master_semaphore_, const VKDevice& device_); 20 explicit CommandPool(MasterSemaphore& master_semaphore_, const Device& device_);
21 ~CommandPool() override; 21 ~CommandPool() override;
22 22
23 void Allocate(size_t begin, size_t end) override; 23 void Allocate(size_t begin, size_t end) override;
@@ -27,7 +27,7 @@ public:
27private: 27private:
28 struct Pool; 28 struct Pool;
29 29
30 const VKDevice& device; 30 const Device& device;
31 std::vector<Pool> pools; 31 std::vector<Pool> pools;
32}; 32};
33 33
diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
index 2c030e910..5eb6a54be 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
@@ -15,11 +15,11 @@
15#include "video_core/host_shaders/vulkan_uint8_comp_spv.h" 15#include "video_core/host_shaders/vulkan_uint8_comp_spv.h"
16#include "video_core/renderer_vulkan/vk_compute_pass.h" 16#include "video_core/renderer_vulkan/vk_compute_pass.h"
17#include "video_core/renderer_vulkan/vk_descriptor_pool.h" 17#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
18#include "video_core/renderer_vulkan/vk_device.h"
19#include "video_core/renderer_vulkan/vk_scheduler.h" 18#include "video_core/renderer_vulkan/vk_scheduler.h"
20#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" 19#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
21#include "video_core/renderer_vulkan/vk_update_descriptor.h" 20#include "video_core/renderer_vulkan/vk_update_descriptor.h"
22#include "video_core/renderer_vulkan/wrapper.h" 21#include "video_core/vulkan_common/vulkan_device.h"
22#include "video_core/vulkan_common/vulkan_wrapper.h"
23 23
24namespace Vulkan { 24namespace Vulkan {
25 25
@@ -86,7 +86,7 @@ VkDescriptorUpdateTemplateEntryKHR BuildInputOutputDescriptorUpdateTemplate() {
86 86
87} // Anonymous namespace 87} // Anonymous namespace
88 88
89VKComputePass::VKComputePass(const VKDevice& device, VKDescriptorPool& descriptor_pool, 89VKComputePass::VKComputePass(const Device& device, VKDescriptorPool& descriptor_pool,
90 vk::Span<VkDescriptorSetLayoutBinding> bindings, 90 vk::Span<VkDescriptorSetLayoutBinding> bindings,
91 vk::Span<VkDescriptorUpdateTemplateEntryKHR> templates, 91 vk::Span<VkDescriptorUpdateTemplateEntryKHR> templates,
92 vk::Span<VkPushConstantRange> push_constants, 92 vk::Span<VkPushConstantRange> push_constants,
@@ -162,9 +162,9 @@ VkDescriptorSet VKComputePass::CommitDescriptorSet(
162 return set; 162 return set;
163} 163}
164 164
165QuadArrayPass::QuadArrayPass(const VKDevice& device_, VKScheduler& scheduler_, 165QuadArrayPass::QuadArrayPass(const Device& device_, VKScheduler& scheduler_,
166 VKDescriptorPool& descriptor_pool_, 166 VKDescriptorPool& descriptor_pool_,
167 VKStagingBufferPool& staging_buffer_pool_, 167 StagingBufferPool& staging_buffer_pool_,
168 VKUpdateDescriptorQueue& update_descriptor_queue_) 168 VKUpdateDescriptorQueue& update_descriptor_queue_)
169 : VKComputePass(device_, descriptor_pool_, BuildQuadArrayPassDescriptorSetLayoutBinding(), 169 : VKComputePass(device_, descriptor_pool_, BuildQuadArrayPassDescriptorSetLayoutBinding(),
170 BuildQuadArrayPassDescriptorUpdateTemplateEntry(), 170 BuildQuadArrayPassDescriptorUpdateTemplateEntry(),
@@ -177,18 +177,18 @@ QuadArrayPass::~QuadArrayPass() = default;
177std::pair<VkBuffer, VkDeviceSize> QuadArrayPass::Assemble(u32 num_vertices, u32 first) { 177std::pair<VkBuffer, VkDeviceSize> QuadArrayPass::Assemble(u32 num_vertices, u32 first) {
178 const u32 num_triangle_vertices = (num_vertices / 4) * 6; 178 const u32 num_triangle_vertices = (num_vertices / 4) * 6;
179 const std::size_t staging_size = num_triangle_vertices * sizeof(u32); 179 const std::size_t staging_size = num_triangle_vertices * sizeof(u32);
180 auto& buffer = staging_buffer_pool.GetUnusedBuffer(staging_size, false); 180 const auto staging_ref = staging_buffer_pool.Request(staging_size, MemoryUsage::DeviceLocal);
181 181
182 update_descriptor_queue.Acquire(); 182 update_descriptor_queue.Acquire();
183 update_descriptor_queue.AddBuffer(*buffer.handle, 0, staging_size); 183 update_descriptor_queue.AddBuffer(staging_ref.buffer, 0, staging_size);
184 const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue); 184 const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue);
185 185
186 scheduler.RequestOutsideRenderPassOperationContext(); 186 scheduler.RequestOutsideRenderPassOperationContext();
187 187
188 ASSERT(num_vertices % 4 == 0); 188 ASSERT(num_vertices % 4 == 0);
189 const u32 num_quads = num_vertices / 4; 189 const u32 num_quads = num_vertices / 4;
190 scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = *buffer.handle, num_quads, 190 scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = staging_ref.buffer,
191 first, set](vk::CommandBuffer cmdbuf) { 191 num_quads, first, set](vk::CommandBuffer cmdbuf) {
192 constexpr u32 dispatch_size = 1024; 192 constexpr u32 dispatch_size = 1024;
193 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, pipeline); 193 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, pipeline);
194 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, layout, 0, set, {}); 194 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, layout, 0, set, {});
@@ -208,11 +208,11 @@ std::pair<VkBuffer, VkDeviceSize> QuadArrayPass::Assemble(u32 num_vertices, u32
208 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 208 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
209 VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, {}, {barrier}, {}); 209 VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, {}, {barrier}, {});
210 }); 210 });
211 return {*buffer.handle, 0}; 211 return {staging_ref.buffer, 0};
212} 212}
213 213
214Uint8Pass::Uint8Pass(const VKDevice& device, VKScheduler& scheduler_, 214Uint8Pass::Uint8Pass(const Device& device, VKScheduler& scheduler_,
215 VKDescriptorPool& descriptor_pool, VKStagingBufferPool& staging_buffer_pool_, 215 VKDescriptorPool& descriptor_pool, StagingBufferPool& staging_buffer_pool_,
216 VKUpdateDescriptorQueue& update_descriptor_queue_) 216 VKUpdateDescriptorQueue& update_descriptor_queue_)
217 : VKComputePass(device, descriptor_pool, BuildInputOutputDescriptorSetBindings(), 217 : VKComputePass(device, descriptor_pool, BuildInputOutputDescriptorSetBindings(),
218 BuildInputOutputDescriptorUpdateTemplate(), {}, VULKAN_UINT8_COMP_SPV), 218 BuildInputOutputDescriptorUpdateTemplate(), {}, VULKAN_UINT8_COMP_SPV),
@@ -224,15 +224,15 @@ Uint8Pass::~Uint8Pass() = default;
224std::pair<VkBuffer, u64> Uint8Pass::Assemble(u32 num_vertices, VkBuffer src_buffer, 224std::pair<VkBuffer, u64> Uint8Pass::Assemble(u32 num_vertices, VkBuffer src_buffer,
225 u64 src_offset) { 225 u64 src_offset) {
226 const u32 staging_size = static_cast<u32>(num_vertices * sizeof(u16)); 226 const u32 staging_size = static_cast<u32>(num_vertices * sizeof(u16));
227 auto& buffer = staging_buffer_pool.GetUnusedBuffer(staging_size, false); 227 const auto staging_ref = staging_buffer_pool.Request(staging_size, MemoryUsage::DeviceLocal);
228 228
229 update_descriptor_queue.Acquire(); 229 update_descriptor_queue.Acquire();
230 update_descriptor_queue.AddBuffer(src_buffer, src_offset, num_vertices); 230 update_descriptor_queue.AddBuffer(src_buffer, src_offset, num_vertices);
231 update_descriptor_queue.AddBuffer(*buffer.handle, 0, staging_size); 231 update_descriptor_queue.AddBuffer(staging_ref.buffer, 0, staging_size);
232 const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue); 232 const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue);
233 233
234 scheduler.RequestOutsideRenderPassOperationContext(); 234 scheduler.RequestOutsideRenderPassOperationContext();
235 scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = *buffer.handle, set, 235 scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = staging_ref.buffer, set,
236 num_vertices](vk::CommandBuffer cmdbuf) { 236 num_vertices](vk::CommandBuffer cmdbuf) {
237 constexpr u32 dispatch_size = 1024; 237 constexpr u32 dispatch_size = 1024;
238 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, pipeline); 238 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, pipeline);
@@ -252,12 +252,12 @@ std::pair<VkBuffer, u64> Uint8Pass::Assemble(u32 num_vertices, VkBuffer src_buff
252 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 252 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
253 VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, {}, barrier, {}); 253 VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, {}, barrier, {});
254 }); 254 });
255 return {*buffer.handle, 0}; 255 return {staging_ref.buffer, 0};
256} 256}
257 257
258QuadIndexedPass::QuadIndexedPass(const VKDevice& device_, VKScheduler& scheduler_, 258QuadIndexedPass::QuadIndexedPass(const Device& device_, VKScheduler& scheduler_,
259 VKDescriptorPool& descriptor_pool_, 259 VKDescriptorPool& descriptor_pool_,
260 VKStagingBufferPool& staging_buffer_pool_, 260 StagingBufferPool& staging_buffer_pool_,
261 VKUpdateDescriptorQueue& update_descriptor_queue_) 261 VKUpdateDescriptorQueue& update_descriptor_queue_)
262 : VKComputePass(device_, descriptor_pool_, BuildInputOutputDescriptorSetBindings(), 262 : VKComputePass(device_, descriptor_pool_, BuildInputOutputDescriptorSetBindings(),
263 BuildInputOutputDescriptorUpdateTemplate(), 263 BuildInputOutputDescriptorUpdateTemplate(),
@@ -286,15 +286,15 @@ std::pair<VkBuffer, u64> QuadIndexedPass::Assemble(
286 const u32 num_tri_vertices = (num_vertices / 4) * 6; 286 const u32 num_tri_vertices = (num_vertices / 4) * 6;
287 287
288 const std::size_t staging_size = num_tri_vertices * sizeof(u32); 288 const std::size_t staging_size = num_tri_vertices * sizeof(u32);
289 auto& buffer = staging_buffer_pool.GetUnusedBuffer(staging_size, false); 289 const auto staging_ref = staging_buffer_pool.Request(staging_size, MemoryUsage::DeviceLocal);
290 290
291 update_descriptor_queue.Acquire(); 291 update_descriptor_queue.Acquire();
292 update_descriptor_queue.AddBuffer(src_buffer, src_offset, input_size); 292 update_descriptor_queue.AddBuffer(src_buffer, src_offset, input_size);
293 update_descriptor_queue.AddBuffer(*buffer.handle, 0, staging_size); 293 update_descriptor_queue.AddBuffer(staging_ref.buffer, 0, staging_size);
294 const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue); 294 const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue);
295 295
296 scheduler.RequestOutsideRenderPassOperationContext(); 296 scheduler.RequestOutsideRenderPassOperationContext();
297 scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = *buffer.handle, set, 297 scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = staging_ref.buffer, set,
298 num_tri_vertices, base_vertex, index_shift](vk::CommandBuffer cmdbuf) { 298 num_tri_vertices, base_vertex, index_shift](vk::CommandBuffer cmdbuf) {
299 static constexpr u32 dispatch_size = 1024; 299 static constexpr u32 dispatch_size = 1024;
300 const std::array push_constants = {base_vertex, index_shift}; 300 const std::array push_constants = {base_vertex, index_shift};
@@ -317,7 +317,7 @@ std::pair<VkBuffer, u64> QuadIndexedPass::Assemble(
317 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 317 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
318 VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, {}, barrier, {}); 318 VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, {}, barrier, {});
319 }); 319 });
320 return {*buffer.handle, 0}; 320 return {staging_ref.buffer, 0};
321} 321}
322 322
323} // namespace Vulkan 323} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.h b/src/video_core/renderer_vulkan/vk_compute_pass.h
index abdf61e2c..f5c6f5f17 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pass.h
+++ b/src/video_core/renderer_vulkan/vk_compute_pass.h
@@ -11,18 +11,18 @@
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "video_core/engines/maxwell_3d.h" 12#include "video_core/engines/maxwell_3d.h"
13#include "video_core/renderer_vulkan/vk_descriptor_pool.h" 13#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
14#include "video_core/renderer_vulkan/wrapper.h" 14#include "video_core/vulkan_common/vulkan_wrapper.h"
15 15
16namespace Vulkan { 16namespace Vulkan {
17 17
18class VKDevice; 18class Device;
19class StagingBufferPool;
19class VKScheduler; 20class VKScheduler;
20class VKStagingBufferPool;
21class VKUpdateDescriptorQueue; 21class VKUpdateDescriptorQueue;
22 22
23class VKComputePass { 23class VKComputePass {
24public: 24public:
25 explicit VKComputePass(const VKDevice& device, VKDescriptorPool& descriptor_pool, 25 explicit VKComputePass(const Device& device, VKDescriptorPool& descriptor_pool,
26 vk::Span<VkDescriptorSetLayoutBinding> bindings, 26 vk::Span<VkDescriptorSetLayoutBinding> bindings,
27 vk::Span<VkDescriptorUpdateTemplateEntryKHR> templates, 27 vk::Span<VkDescriptorUpdateTemplateEntryKHR> templates,
28 vk::Span<VkPushConstantRange> push_constants, std::span<const u32> code); 28 vk::Span<VkPushConstantRange> push_constants, std::span<const u32> code);
@@ -43,9 +43,9 @@ private:
43 43
44class QuadArrayPass final : public VKComputePass { 44class QuadArrayPass final : public VKComputePass {
45public: 45public:
46 explicit QuadArrayPass(const VKDevice& device_, VKScheduler& scheduler_, 46 explicit QuadArrayPass(const Device& device_, VKScheduler& scheduler_,
47 VKDescriptorPool& descriptor_pool_, 47 VKDescriptorPool& descriptor_pool_,
48 VKStagingBufferPool& staging_buffer_pool_, 48 StagingBufferPool& staging_buffer_pool_,
49 VKUpdateDescriptorQueue& update_descriptor_queue_); 49 VKUpdateDescriptorQueue& update_descriptor_queue_);
50 ~QuadArrayPass(); 50 ~QuadArrayPass();
51 51
@@ -53,15 +53,14 @@ public:
53 53
54private: 54private:
55 VKScheduler& scheduler; 55 VKScheduler& scheduler;
56 VKStagingBufferPool& staging_buffer_pool; 56 StagingBufferPool& staging_buffer_pool;
57 VKUpdateDescriptorQueue& update_descriptor_queue; 57 VKUpdateDescriptorQueue& update_descriptor_queue;
58}; 58};
59 59
60class Uint8Pass final : public VKComputePass { 60class Uint8Pass final : public VKComputePass {
61public: 61public:
62 explicit Uint8Pass(const VKDevice& device_, VKScheduler& scheduler_, 62 explicit Uint8Pass(const Device& device_, VKScheduler& scheduler_,
63 VKDescriptorPool& descriptor_pool_, 63 VKDescriptorPool& descriptor_pool_, StagingBufferPool& staging_buffer_pool_,
64 VKStagingBufferPool& staging_buffer_pool_,
65 VKUpdateDescriptorQueue& update_descriptor_queue_); 64 VKUpdateDescriptorQueue& update_descriptor_queue_);
66 ~Uint8Pass(); 65 ~Uint8Pass();
67 66
@@ -69,15 +68,15 @@ public:
69 68
70private: 69private:
71 VKScheduler& scheduler; 70 VKScheduler& scheduler;
72 VKStagingBufferPool& staging_buffer_pool; 71 StagingBufferPool& staging_buffer_pool;
73 VKUpdateDescriptorQueue& update_descriptor_queue; 72 VKUpdateDescriptorQueue& update_descriptor_queue;
74}; 73};
75 74
76class QuadIndexedPass final : public VKComputePass { 75class QuadIndexedPass final : public VKComputePass {
77public: 76public:
78 explicit QuadIndexedPass(const VKDevice& device_, VKScheduler& scheduler_, 77 explicit QuadIndexedPass(const Device& device_, VKScheduler& scheduler_,
79 VKDescriptorPool& descriptor_pool_, 78 VKDescriptorPool& descriptor_pool_,
80 VKStagingBufferPool& staging_buffer_pool_, 79 StagingBufferPool& staging_buffer_pool_,
81 VKUpdateDescriptorQueue& update_descriptor_queue_); 80 VKUpdateDescriptorQueue& update_descriptor_queue_);
82 ~QuadIndexedPass(); 81 ~QuadIndexedPass();
83 82
@@ -87,7 +86,7 @@ public:
87 86
88private: 87private:
89 VKScheduler& scheduler; 88 VKScheduler& scheduler;
90 VKStagingBufferPool& staging_buffer_pool; 89 StagingBufferPool& staging_buffer_pool;
91 VKUpdateDescriptorQueue& update_descriptor_queue; 90 VKUpdateDescriptorQueue& update_descriptor_queue;
92}; 91};
93 92
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
index 62f44d6da..3a48219b7 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
@@ -6,16 +6,16 @@
6 6
7#include "video_core/renderer_vulkan/vk_compute_pipeline.h" 7#include "video_core/renderer_vulkan/vk_compute_pipeline.h"
8#include "video_core/renderer_vulkan/vk_descriptor_pool.h" 8#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
9#include "video_core/renderer_vulkan/vk_device.h"
10#include "video_core/renderer_vulkan/vk_pipeline_cache.h" 9#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
11#include "video_core/renderer_vulkan/vk_scheduler.h" 10#include "video_core/renderer_vulkan/vk_scheduler.h"
12#include "video_core/renderer_vulkan/vk_shader_decompiler.h" 11#include "video_core/renderer_vulkan/vk_shader_decompiler.h"
13#include "video_core/renderer_vulkan/vk_update_descriptor.h" 12#include "video_core/renderer_vulkan/vk_update_descriptor.h"
14#include "video_core/renderer_vulkan/wrapper.h" 13#include "video_core/vulkan_common/vulkan_device.h"
14#include "video_core/vulkan_common/vulkan_wrapper.h"
15 15
16namespace Vulkan { 16namespace Vulkan {
17 17
18VKComputePipeline::VKComputePipeline(const VKDevice& device_, VKScheduler& scheduler_, 18VKComputePipeline::VKComputePipeline(const Device& device_, VKScheduler& scheduler_,
19 VKDescriptorPool& descriptor_pool_, 19 VKDescriptorPool& descriptor_pool_,
20 VKUpdateDescriptorQueue& update_descriptor_queue_, 20 VKUpdateDescriptorQueue& update_descriptor_queue_,
21 const SPIRVShader& shader_) 21 const SPIRVShader& shader_)
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.h b/src/video_core/renderer_vulkan/vk_compute_pipeline.h
index 49e2113a2..7e16575ac 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pipeline.h
+++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.h
@@ -7,17 +7,17 @@
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "video_core/renderer_vulkan/vk_descriptor_pool.h" 8#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
9#include "video_core/renderer_vulkan/vk_shader_decompiler.h" 9#include "video_core/renderer_vulkan/vk_shader_decompiler.h"
10#include "video_core/renderer_vulkan/wrapper.h" 10#include "video_core/vulkan_common/vulkan_wrapper.h"
11 11
12namespace Vulkan { 12namespace Vulkan {
13 13
14class VKDevice; 14class Device;
15class VKScheduler; 15class VKScheduler;
16class VKUpdateDescriptorQueue; 16class VKUpdateDescriptorQueue;
17 17
18class VKComputePipeline final { 18class VKComputePipeline final {
19public: 19public:
20 explicit VKComputePipeline(const VKDevice& device_, VKScheduler& scheduler_, 20 explicit VKComputePipeline(const Device& device_, VKScheduler& scheduler_,
21 VKDescriptorPool& descriptor_pool_, 21 VKDescriptorPool& descriptor_pool_,
22 VKUpdateDescriptorQueue& update_descriptor_queue_, 22 VKUpdateDescriptorQueue& update_descriptor_queue_,
23 const SPIRVShader& shader_); 23 const SPIRVShader& shader_);
@@ -48,7 +48,7 @@ private:
48 48
49 vk::Pipeline CreatePipeline() const; 49 vk::Pipeline CreatePipeline() const;
50 50
51 const VKDevice& device; 51 const Device& device;
52 VKScheduler& scheduler; 52 VKScheduler& scheduler;
53 ShaderEntries entries; 53 ShaderEntries entries;
54 54
diff --git a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp
index f38e089d5..ef9fb5910 100644
--- a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp
+++ b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp
@@ -6,10 +6,10 @@
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "video_core/renderer_vulkan/vk_descriptor_pool.h" 8#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
9#include "video_core/renderer_vulkan/vk_device.h"
10#include "video_core/renderer_vulkan/vk_resource_pool.h" 9#include "video_core/renderer_vulkan/vk_resource_pool.h"
11#include "video_core/renderer_vulkan/vk_scheduler.h" 10#include "video_core/renderer_vulkan/vk_scheduler.h"
12#include "video_core/renderer_vulkan/wrapper.h" 11#include "video_core/vulkan_common/vulkan_device.h"
12#include "video_core/vulkan_common/vulkan_wrapper.h"
13 13
14namespace Vulkan { 14namespace Vulkan {
15 15
@@ -32,7 +32,7 @@ void DescriptorAllocator::Allocate(std::size_t begin, std::size_t end) {
32 descriptors_allocations.push_back(descriptor_pool.AllocateDescriptors(layout, end - begin)); 32 descriptors_allocations.push_back(descriptor_pool.AllocateDescriptors(layout, end - begin));
33} 33}
34 34
35VKDescriptorPool::VKDescriptorPool(const VKDevice& device_, VKScheduler& scheduler) 35VKDescriptorPool::VKDescriptorPool(const Device& device_, VKScheduler& scheduler)
36 : device{device_}, master_semaphore{scheduler.GetMasterSemaphore()}, active_pool{ 36 : device{device_}, master_semaphore{scheduler.GetMasterSemaphore()}, active_pool{
37 AllocateNewPool()} {} 37 AllocateNewPool()} {}
38 38
diff --git a/src/video_core/renderer_vulkan/vk_descriptor_pool.h b/src/video_core/renderer_vulkan/vk_descriptor_pool.h
index 544f32a20..f892be7be 100644
--- a/src/video_core/renderer_vulkan/vk_descriptor_pool.h
+++ b/src/video_core/renderer_vulkan/vk_descriptor_pool.h
@@ -7,11 +7,11 @@
7#include <vector> 7#include <vector>
8 8
9#include "video_core/renderer_vulkan/vk_resource_pool.h" 9#include "video_core/renderer_vulkan/vk_resource_pool.h"
10#include "video_core/renderer_vulkan/wrapper.h" 10#include "video_core/vulkan_common/vulkan_wrapper.h"
11 11
12namespace Vulkan { 12namespace Vulkan {
13 13
14class VKDevice; 14class Device;
15class VKDescriptorPool; 15class VKDescriptorPool;
16class VKScheduler; 16class VKScheduler;
17 17
@@ -39,7 +39,7 @@ class VKDescriptorPool final {
39 friend DescriptorAllocator; 39 friend DescriptorAllocator;
40 40
41public: 41public:
42 explicit VKDescriptorPool(const VKDevice& device, VKScheduler& scheduler); 42 explicit VKDescriptorPool(const Device& device, VKScheduler& scheduler);
43 ~VKDescriptorPool(); 43 ~VKDescriptorPool();
44 44
45 VKDescriptorPool(const VKDescriptorPool&) = delete; 45 VKDescriptorPool(const VKDescriptorPool&) = delete;
@@ -50,7 +50,7 @@ private:
50 50
51 vk::DescriptorSets AllocateDescriptors(VkDescriptorSetLayout layout, std::size_t count); 51 vk::DescriptorSets AllocateDescriptors(VkDescriptorSetLayout layout, std::size_t count);
52 52
53 const VKDevice& device; 53 const Device& device;
54 MasterSemaphore& master_semaphore; 54 MasterSemaphore& master_semaphore;
55 55
56 std::vector<vk::DescriptorPool> pools; 56 std::vector<vk::DescriptorPool> pools;
diff --git a/src/video_core/renderer_vulkan/vk_fence_manager.cpp b/src/video_core/renderer_vulkan/vk_fence_manager.cpp
index 774a12a53..6cd00884d 100644
--- a/src/video_core/renderer_vulkan/vk_fence_manager.cpp
+++ b/src/video_core/renderer_vulkan/vk_fence_manager.cpp
@@ -3,24 +3,21 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <memory> 5#include <memory>
6#include <thread>
7 6
8#include "video_core/renderer_vulkan/vk_buffer_cache.h" 7#include "video_core/renderer_vulkan/vk_buffer_cache.h"
9#include "video_core/renderer_vulkan/vk_device.h"
10#include "video_core/renderer_vulkan/vk_fence_manager.h" 8#include "video_core/renderer_vulkan/vk_fence_manager.h"
11#include "video_core/renderer_vulkan/vk_scheduler.h" 9#include "video_core/renderer_vulkan/vk_scheduler.h"
12#include "video_core/renderer_vulkan/vk_texture_cache.h" 10#include "video_core/renderer_vulkan/vk_texture_cache.h"
13#include "video_core/renderer_vulkan/wrapper.h" 11#include "video_core/vulkan_common/vulkan_device.h"
12#include "video_core/vulkan_common/vulkan_wrapper.h"
14 13
15namespace Vulkan { 14namespace Vulkan {
16 15
17InnerFence::InnerFence(const VKDevice& device_, VKScheduler& scheduler_, u32 payload_, 16InnerFence::InnerFence(VKScheduler& scheduler_, u32 payload_, bool is_stubbed_)
18 bool is_stubbed_) 17 : FenceBase{payload_, is_stubbed_}, scheduler{scheduler_} {}
19 : FenceBase{payload_, is_stubbed_}, device{device_}, scheduler{scheduler_} {}
20 18
21InnerFence::InnerFence(const VKDevice& device_, VKScheduler& scheduler_, GPUVAddr address_, 19InnerFence::InnerFence(VKScheduler& scheduler_, GPUVAddr address_, u32 payload_, bool is_stubbed_)
22 u32 payload_, bool is_stubbed_) 20 : FenceBase{address_, payload_, is_stubbed_}, scheduler{scheduler_} {}
23 : FenceBase{address_, payload_, is_stubbed_}, device{device_}, scheduler{scheduler_} {}
24 21
25InnerFence::~InnerFence() = default; 22InnerFence::~InnerFence() = default;
26 23
@@ -28,63 +25,38 @@ void InnerFence::Queue() {
28 if (is_stubbed) { 25 if (is_stubbed) {
29 return; 26 return;
30 } 27 }
31 ASSERT(!event); 28 // Get the current tick so we can wait for it
32 29 wait_tick = scheduler.CurrentTick();
33 event = device.GetLogical().CreateEvent(); 30 scheduler.Flush();
34 ticks = scheduler.CurrentTick();
35
36 scheduler.RequestOutsideRenderPassOperationContext();
37 scheduler.Record([event = *event](vk::CommandBuffer cmdbuf) {
38 cmdbuf.SetEvent(event, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
39 });
40} 31}
41 32
42bool InnerFence::IsSignaled() const { 33bool InnerFence::IsSignaled() const {
43 if (is_stubbed) { 34 if (is_stubbed) {
44 return true; 35 return true;
45 } 36 }
46 ASSERT(event); 37 return scheduler.IsFree(wait_tick);
47 return IsEventSignalled();
48} 38}
49 39
50void InnerFence::Wait() { 40void InnerFence::Wait() {
51 if (is_stubbed) { 41 if (is_stubbed) {
52 return; 42 return;
53 } 43 }
54 ASSERT(event); 44 scheduler.Wait(wait_tick);
55
56 if (ticks >= scheduler.CurrentTick()) {
57 scheduler.Flush();
58 }
59 while (!IsEventSignalled()) {
60 std::this_thread::yield();
61 }
62}
63
64bool InnerFence::IsEventSignalled() const {
65 switch (const VkResult result = event.GetStatus()) {
66 case VK_EVENT_SET:
67 return true;
68 case VK_EVENT_RESET:
69 return false;
70 default:
71 throw vk::Exception(result);
72 }
73} 45}
74 46
75VKFenceManager::VKFenceManager(VideoCore::RasterizerInterface& rasterizer_, Tegra::GPU& gpu_, 47VKFenceManager::VKFenceManager(VideoCore::RasterizerInterface& rasterizer_, Tegra::GPU& gpu_,
76 Tegra::MemoryManager& memory_manager_, TextureCache& texture_cache_, 48 Tegra::MemoryManager& memory_manager_, TextureCache& texture_cache_,
77 VKBufferCache& buffer_cache_, VKQueryCache& query_cache_, 49 VKBufferCache& buffer_cache_, VKQueryCache& query_cache_,
78 const VKDevice& device_, VKScheduler& scheduler_) 50 VKScheduler& scheduler_)
79 : GenericFenceManager{rasterizer_, gpu_, texture_cache_, buffer_cache_, query_cache_}, 51 : GenericFenceManager{rasterizer_, gpu_, texture_cache_, buffer_cache_, query_cache_},
80 device{device_}, scheduler{scheduler_} {} 52 scheduler{scheduler_} {}
81 53
82Fence VKFenceManager::CreateFence(u32 value, bool is_stubbed) { 54Fence VKFenceManager::CreateFence(u32 value, bool is_stubbed) {
83 return std::make_shared<InnerFence>(device, scheduler, value, is_stubbed); 55 return std::make_shared<InnerFence>(scheduler, value, is_stubbed);
84} 56}
85 57
86Fence VKFenceManager::CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) { 58Fence VKFenceManager::CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) {
87 return std::make_shared<InnerFence>(device, scheduler, addr, value, is_stubbed); 59 return std::make_shared<InnerFence>(scheduler, addr, value, is_stubbed);
88} 60}
89 61
90void VKFenceManager::QueueFence(Fence& fence) { 62void VKFenceManager::QueueFence(Fence& fence) {
diff --git a/src/video_core/renderer_vulkan/vk_fence_manager.h b/src/video_core/renderer_vulkan/vk_fence_manager.h
index c2869e8e3..9c5e5aa8f 100644
--- a/src/video_core/renderer_vulkan/vk_fence_manager.h
+++ b/src/video_core/renderer_vulkan/vk_fence_manager.h
@@ -9,7 +9,7 @@
9#include "video_core/fence_manager.h" 9#include "video_core/fence_manager.h"
10#include "video_core/renderer_vulkan/vk_buffer_cache.h" 10#include "video_core/renderer_vulkan/vk_buffer_cache.h"
11#include "video_core/renderer_vulkan/vk_texture_cache.h" 11#include "video_core/renderer_vulkan/vk_texture_cache.h"
12#include "video_core/renderer_vulkan/wrapper.h" 12#include "video_core/vulkan_common/vulkan_wrapper.h"
13 13
14namespace Core { 14namespace Core {
15class System; 15class System;
@@ -21,17 +21,15 @@ class RasterizerInterface;
21 21
22namespace Vulkan { 22namespace Vulkan {
23 23
24class Device;
24class VKBufferCache; 25class VKBufferCache;
25class VKDevice;
26class VKQueryCache; 26class VKQueryCache;
27class VKScheduler; 27class VKScheduler;
28 28
29class InnerFence : public VideoCommon::FenceBase { 29class InnerFence : public VideoCommon::FenceBase {
30public: 30public:
31 explicit InnerFence(const VKDevice& device_, VKScheduler& scheduler_, u32 payload_, 31 explicit InnerFence(VKScheduler& scheduler_, u32 payload_, bool is_stubbed_);
32 bool is_stubbed_); 32 explicit InnerFence(VKScheduler& scheduler_, GPUVAddr address_, u32 payload_, bool is_stubbed_);
33 explicit InnerFence(const VKDevice& device_, VKScheduler& scheduler_, GPUVAddr address_,
34 u32 payload_, bool is_stubbed_);
35 ~InnerFence(); 33 ~InnerFence();
36 34
37 void Queue(); 35 void Queue();
@@ -41,12 +39,8 @@ public:
41 void Wait(); 39 void Wait();
42 40
43private: 41private:
44 bool IsEventSignalled() const;
45
46 const VKDevice& device;
47 VKScheduler& scheduler; 42 VKScheduler& scheduler;
48 vk::Event event; 43 u64 wait_tick = 0;
49 u64 ticks = 0;
50}; 44};
51using Fence = std::shared_ptr<InnerFence>; 45using Fence = std::shared_ptr<InnerFence>;
52 46
@@ -58,7 +52,7 @@ public:
58 explicit VKFenceManager(VideoCore::RasterizerInterface& rasterizer_, Tegra::GPU& gpu_, 52 explicit VKFenceManager(VideoCore::RasterizerInterface& rasterizer_, Tegra::GPU& gpu_,
59 Tegra::MemoryManager& memory_manager_, TextureCache& texture_cache_, 53 Tegra::MemoryManager& memory_manager_, TextureCache& texture_cache_,
60 VKBufferCache& buffer_cache_, VKQueryCache& query_cache_, 54 VKBufferCache& buffer_cache_, VKQueryCache& query_cache_,
61 const VKDevice& device_, VKScheduler& scheduler_); 55 VKScheduler& scheduler_);
62 56
63protected: 57protected:
64 Fence CreateFence(u32 value, bool is_stubbed) override; 58 Fence CreateFence(u32 value, bool is_stubbed) override;
@@ -68,7 +62,6 @@ protected:
68 void WaitFence(Fence& fence) override; 62 void WaitFence(Fence& fence) override;
69 63
70private: 64private:
71 const VKDevice& device;
72 VKScheduler& scheduler; 65 VKScheduler& scheduler;
73}; 66};
74 67
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 7979df3a8..a5214d0bc 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -12,12 +12,12 @@
12#include "video_core/renderer_vulkan/fixed_pipeline_state.h" 12#include "video_core/renderer_vulkan/fixed_pipeline_state.h"
13#include "video_core/renderer_vulkan/maxwell_to_vk.h" 13#include "video_core/renderer_vulkan/maxwell_to_vk.h"
14#include "video_core/renderer_vulkan/vk_descriptor_pool.h" 14#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
15#include "video_core/renderer_vulkan/vk_device.h"
16#include "video_core/renderer_vulkan/vk_graphics_pipeline.h" 15#include "video_core/renderer_vulkan/vk_graphics_pipeline.h"
17#include "video_core/renderer_vulkan/vk_pipeline_cache.h" 16#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
18#include "video_core/renderer_vulkan/vk_scheduler.h" 17#include "video_core/renderer_vulkan/vk_scheduler.h"
19#include "video_core/renderer_vulkan/vk_update_descriptor.h" 18#include "video_core/renderer_vulkan/vk_update_descriptor.h"
20#include "video_core/renderer_vulkan/wrapper.h" 19#include "video_core/vulkan_common/vulkan_device.h"
20#include "video_core/vulkan_common/vulkan_wrapper.h"
21 21
22namespace Vulkan { 22namespace Vulkan {
23 23
@@ -94,7 +94,7 @@ VkSampleCountFlagBits ConvertMsaaMode(Tegra::Texture::MsaaMode msaa_mode) {
94 94
95} // Anonymous namespace 95} // Anonymous namespace
96 96
97VKGraphicsPipeline::VKGraphicsPipeline(const VKDevice& device_, VKScheduler& scheduler_, 97VKGraphicsPipeline::VKGraphicsPipeline(const Device& device_, VKScheduler& scheduler_,
98 VKDescriptorPool& descriptor_pool_, 98 VKDescriptorPool& descriptor_pool_,
99 VKUpdateDescriptorQueue& update_descriptor_queue_, 99 VKUpdateDescriptorQueue& update_descriptor_queue_,
100 const GraphicsPipelineCacheKey& key, 100 const GraphicsPipelineCacheKey& key,
@@ -212,11 +212,7 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const SPIRVProgram& program,
212 // state is ignored 212 // state is ignored
213 dynamic.raw1 = 0; 213 dynamic.raw1 = 0;
214 dynamic.raw2 = 0; 214 dynamic.raw2 = 0;
215 for (FixedPipelineState::VertexBinding& binding : dynamic.vertex_bindings) { 215 dynamic.vertex_strides.fill(0);
216 // Enable all vertex bindings
217 binding.raw = 0;
218 binding.enabled.Assign(1);
219 }
220 } else { 216 } else {
221 dynamic = state.dynamic_state; 217 dynamic = state.dynamic_state;
222 } 218 }
@@ -224,19 +220,16 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const SPIRVProgram& program,
224 std::vector<VkVertexInputBindingDescription> vertex_bindings; 220 std::vector<VkVertexInputBindingDescription> vertex_bindings;
225 std::vector<VkVertexInputBindingDivisorDescriptionEXT> vertex_binding_divisors; 221 std::vector<VkVertexInputBindingDivisorDescriptionEXT> vertex_binding_divisors;
226 for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { 222 for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
227 const auto& binding = dynamic.vertex_bindings[index]; 223 if (state.attributes[index].binding_index_enabled == 0) {
228 if (!binding.enabled) {
229 continue; 224 continue;
230 } 225 }
231 const bool instanced = state.binding_divisors[index] != 0; 226 const bool instanced = state.binding_divisors[index] != 0;
232 const auto rate = instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; 227 const auto rate = instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX;
233
234 vertex_bindings.push_back({ 228 vertex_bindings.push_back({
235 .binding = static_cast<u32>(index), 229 .binding = static_cast<u32>(index),
236 .stride = binding.stride, 230 .stride = dynamic.vertex_strides[index],
237 .inputRate = rate, 231 .inputRate = rate,
238 }); 232 });
239
240 if (instanced) { 233 if (instanced) {
241 vertex_binding_divisors.push_back({ 234 vertex_binding_divisors.push_back({
242 .binding = static_cast<u32>(index), 235 .binding = static_cast<u32>(index),
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
index 214d06b4c..8b6a98fe0 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
@@ -13,7 +13,7 @@
13#include "video_core/renderer_vulkan/fixed_pipeline_state.h" 13#include "video_core/renderer_vulkan/fixed_pipeline_state.h"
14#include "video_core/renderer_vulkan/vk_descriptor_pool.h" 14#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
15#include "video_core/renderer_vulkan/vk_shader_decompiler.h" 15#include "video_core/renderer_vulkan/vk_shader_decompiler.h"
16#include "video_core/renderer_vulkan/wrapper.h" 16#include "video_core/vulkan_common/vulkan_wrapper.h"
17 17
18namespace Vulkan { 18namespace Vulkan {
19 19
@@ -40,8 +40,8 @@ static_assert(std::has_unique_object_representations_v<GraphicsPipelineCacheKey>
40static_assert(std::is_trivially_copyable_v<GraphicsPipelineCacheKey>); 40static_assert(std::is_trivially_copyable_v<GraphicsPipelineCacheKey>);
41static_assert(std::is_trivially_constructible_v<GraphicsPipelineCacheKey>); 41static_assert(std::is_trivially_constructible_v<GraphicsPipelineCacheKey>);
42 42
43class Device;
43class VKDescriptorPool; 44class VKDescriptorPool;
44class VKDevice;
45class VKScheduler; 45class VKScheduler;
46class VKUpdateDescriptorQueue; 46class VKUpdateDescriptorQueue;
47 47
@@ -49,7 +49,7 @@ using SPIRVProgram = std::array<std::optional<SPIRVShader>, Maxwell::MaxShaderSt
49 49
50class VKGraphicsPipeline final { 50class VKGraphicsPipeline final {
51public: 51public:
52 explicit VKGraphicsPipeline(const VKDevice& device_, VKScheduler& scheduler_, 52 explicit VKGraphicsPipeline(const Device& device_, VKScheduler& scheduler_,
53 VKDescriptorPool& descriptor_pool, 53 VKDescriptorPool& descriptor_pool,
54 VKUpdateDescriptorQueue& update_descriptor_queue_, 54 VKUpdateDescriptorQueue& update_descriptor_queue_,
55 const GraphicsPipelineCacheKey& key, 55 const GraphicsPipelineCacheKey& key,
@@ -85,7 +85,7 @@ private:
85 vk::Pipeline CreatePipeline(const SPIRVProgram& program, VkRenderPass renderpass, 85 vk::Pipeline CreatePipeline(const SPIRVProgram& program, VkRenderPass renderpass,
86 u32 num_color_buffers) const; 86 u32 num_color_buffers) const;
87 87
88 const VKDevice& device; 88 const Device& device;
89 VKScheduler& scheduler; 89 VKScheduler& scheduler;
90 const GraphicsPipelineCacheKey cache_key; 90 const GraphicsPipelineCacheKey cache_key;
91 const u64 hash; 91 const u64 hash;
diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
index ae26e558d..56ec5e380 100644
--- a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
+++ b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
@@ -6,15 +6,15 @@
6#include <chrono> 6#include <chrono>
7 7
8#include "core/settings.h" 8#include "core/settings.h"
9#include "video_core/renderer_vulkan/vk_device.h"
10#include "video_core/renderer_vulkan/vk_master_semaphore.h" 9#include "video_core/renderer_vulkan/vk_master_semaphore.h"
11#include "video_core/renderer_vulkan/wrapper.h" 10#include "video_core/vulkan_common/vulkan_device.h"
11#include "video_core/vulkan_common/vulkan_wrapper.h"
12 12
13namespace Vulkan { 13namespace Vulkan {
14 14
15using namespace std::chrono_literals; 15using namespace std::chrono_literals;
16 16
17MasterSemaphore::MasterSemaphore(const VKDevice& device) { 17MasterSemaphore::MasterSemaphore(const Device& device) {
18 static constexpr VkSemaphoreTypeCreateInfoKHR semaphore_type_ci{ 18 static constexpr VkSemaphoreTypeCreateInfoKHR semaphore_type_ci{
19 .sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO_KHR, 19 .sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO_KHR,
20 .pNext = nullptr, 20 .pNext = nullptr,
diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.h b/src/video_core/renderer_vulkan/vk_master_semaphore.h
index 0e93706d7..f336f1862 100644
--- a/src/video_core/renderer_vulkan/vk_master_semaphore.h
+++ b/src/video_core/renderer_vulkan/vk_master_semaphore.h
@@ -8,15 +8,15 @@
8#include <thread> 8#include <thread>
9 9
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "video_core/renderer_vulkan/wrapper.h" 11#include "video_core/vulkan_common/vulkan_wrapper.h"
12 12
13namespace Vulkan { 13namespace Vulkan {
14 14
15class VKDevice; 15class Device;
16 16
17class MasterSemaphore { 17class MasterSemaphore {
18public: 18public:
19 explicit MasterSemaphore(const VKDevice& device); 19 explicit MasterSemaphore(const Device& device);
20 ~MasterSemaphore(); 20 ~MasterSemaphore();
21 21
22 /// Returns the current logical tick. 22 /// Returns the current logical tick.
diff --git a/src/video_core/renderer_vulkan/vk_memory_manager.cpp b/src/video_core/renderer_vulkan/vk_memory_manager.cpp
deleted file mode 100644
index 56b24b70f..000000000
--- a/src/video_core/renderer_vulkan/vk_memory_manager.cpp
+++ /dev/null
@@ -1,230 +0,0 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <optional>
7#include <tuple>
8#include <vector>
9
10#include "common/alignment.h"
11#include "common/assert.h"
12#include "common/common_types.h"
13#include "common/logging/log.h"
14#include "video_core/renderer_vulkan/vk_device.h"
15#include "video_core/renderer_vulkan/vk_memory_manager.h"
16#include "video_core/renderer_vulkan/wrapper.h"
17
18namespace Vulkan {
19
20namespace {
21
22u64 GetAllocationChunkSize(u64 required_size) {
23 static constexpr u64 sizes[] = {16ULL << 20, 32ULL << 20, 64ULL << 20, 128ULL << 20};
24 auto it = std::lower_bound(std::begin(sizes), std::end(sizes), required_size);
25 return it != std::end(sizes) ? *it : Common::AlignUp(required_size, 256ULL << 20);
26}
27
28} // Anonymous namespace
29
30class VKMemoryAllocation final {
31public:
32 explicit VKMemoryAllocation(const VKDevice& device_, vk::DeviceMemory memory_,
33 VkMemoryPropertyFlags properties_, u64 allocation_size_, u32 type_)
34 : device{device_}, memory{std::move(memory_)}, properties{properties_},
35 allocation_size{allocation_size_}, shifted_type{ShiftType(type_)} {}
36
37 VKMemoryCommit Commit(VkDeviceSize commit_size, VkDeviceSize alignment) {
38 auto found = TryFindFreeSection(free_iterator, allocation_size,
39 static_cast<u64>(commit_size), static_cast<u64>(alignment));
40 if (!found) {
41 found = TryFindFreeSection(0, free_iterator, static_cast<u64>(commit_size),
42 static_cast<u64>(alignment));
43 if (!found) {
44 // Signal out of memory, it'll try to do more allocations.
45 return nullptr;
46 }
47 }
48 auto commit = std::make_unique<VKMemoryCommitImpl>(device, this, memory, *found,
49 *found + commit_size);
50 commits.push_back(commit.get());
51
52 // Last commit's address is highly probable to be free.
53 free_iterator = *found + commit_size;
54
55 return commit;
56 }
57
58 void Free(const VKMemoryCommitImpl* commit) {
59 ASSERT(commit);
60
61 const auto it = std::find(std::begin(commits), std::end(commits), commit);
62 if (it == commits.end()) {
63 UNREACHABLE_MSG("Freeing unallocated commit!");
64 return;
65 }
66 commits.erase(it);
67 }
68
69 /// Returns whether this allocation is compatible with the arguments.
70 bool IsCompatible(VkMemoryPropertyFlags wanted_properties, u32 type_mask) const {
71 return (wanted_properties & properties) && (type_mask & shifted_type) != 0;
72 }
73
74private:
75 static constexpr u32 ShiftType(u32 type) {
76 return 1U << type;
77 }
78
79 /// A memory allocator, it may return a free region between "start" and "end" with the solicited
80 /// requirements.
81 std::optional<u64> TryFindFreeSection(u64 start, u64 end, u64 size, u64 alignment) const {
82 u64 iterator = Common::AlignUp(start, alignment);
83 while (iterator + size <= end) {
84 const u64 try_left = iterator;
85 const u64 try_right = try_left + size;
86
87 bool overlap = false;
88 for (const auto& commit : commits) {
89 const auto [commit_left, commit_right] = commit->interval;
90 if (try_left < commit_right && commit_left < try_right) {
91 // There's an overlap, continue the search where the overlapping commit ends.
92 iterator = Common::AlignUp(commit_right, alignment);
93 overlap = true;
94 break;
95 }
96 }
97 if (!overlap) {
98 // A free address has been found.
99 return try_left;
100 }
101 }
102
103 // No free regions where found, return an empty optional.
104 return std::nullopt;
105 }
106
107 const VKDevice& device; ///< Vulkan device.
108 const vk::DeviceMemory memory; ///< Vulkan memory allocation handler.
109 const VkMemoryPropertyFlags properties; ///< Vulkan properties.
110 const u64 allocation_size; ///< Size of this allocation.
111 const u32 shifted_type; ///< Stored Vulkan type of this allocation, shifted.
112
113 /// Hints where the next free region is likely going to be.
114 u64 free_iterator{};
115
116 /// Stores all commits done from this allocation.
117 std::vector<const VKMemoryCommitImpl*> commits;
118};
119
120VKMemoryManager::VKMemoryManager(const VKDevice& device_)
121 : device{device_}, properties{device_.GetPhysical().GetMemoryProperties()} {}
122
123VKMemoryManager::~VKMemoryManager() = default;
124
125VKMemoryCommit VKMemoryManager::Commit(const VkMemoryRequirements& requirements,
126 bool host_visible) {
127 const u64 chunk_size = GetAllocationChunkSize(requirements.size);
128
129 // When a host visible commit is asked, search for host visible and coherent, otherwise search
130 // for a fast device local type.
131 const VkMemoryPropertyFlags wanted_properties =
132 host_visible ? VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
133 : VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
134
135 if (auto commit = TryAllocCommit(requirements, wanted_properties)) {
136 return commit;
137 }
138
139 // Commit has failed, allocate more memory.
140 if (!AllocMemory(wanted_properties, requirements.memoryTypeBits, chunk_size)) {
141 // TODO(Rodrigo): Handle these situations in some way like flushing to guest memory.
142 // Allocation has failed, panic.
143 UNREACHABLE_MSG("Ran out of VRAM!");
144 return {};
145 }
146
147 // Commit again, this time it won't fail since there's a fresh allocation above. If it does,
148 // there's a bug.
149 auto commit = TryAllocCommit(requirements, wanted_properties);
150 ASSERT(commit);
151 return commit;
152}
153
154VKMemoryCommit VKMemoryManager::Commit(const vk::Buffer& buffer, bool host_visible) {
155 auto commit = Commit(device.GetLogical().GetBufferMemoryRequirements(*buffer), host_visible);
156 buffer.BindMemory(commit->GetMemory(), commit->GetOffset());
157 return commit;
158}
159
160VKMemoryCommit VKMemoryManager::Commit(const vk::Image& image, bool host_visible) {
161 auto commit = Commit(device.GetLogical().GetImageMemoryRequirements(*image), host_visible);
162 image.BindMemory(commit->GetMemory(), commit->GetOffset());
163 return commit;
164}
165
166bool VKMemoryManager::AllocMemory(VkMemoryPropertyFlags wanted_properties, u32 type_mask,
167 u64 size) {
168 const u32 type = [&] {
169 for (u32 type_index = 0; type_index < properties.memoryTypeCount; ++type_index) {
170 const auto flags = properties.memoryTypes[type_index].propertyFlags;
171 if ((type_mask & (1U << type_index)) && (flags & wanted_properties)) {
172 // The type matches in type and in the wanted properties.
173 return type_index;
174 }
175 }
176 UNREACHABLE_MSG("Couldn't find a compatible memory type!");
177 return 0U;
178 }();
179
180 // Try to allocate found type.
181 vk::DeviceMemory memory = device.GetLogical().TryAllocateMemory({
182 .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
183 .pNext = nullptr,
184 .allocationSize = size,
185 .memoryTypeIndex = type,
186 });
187 if (!memory) {
188 LOG_CRITICAL(Render_Vulkan, "Device allocation failed!");
189 return false;
190 }
191
192 allocations.push_back(std::make_unique<VKMemoryAllocation>(device, std::move(memory),
193 wanted_properties, size, type));
194 return true;
195}
196
197VKMemoryCommit VKMemoryManager::TryAllocCommit(const VkMemoryRequirements& requirements,
198 VkMemoryPropertyFlags wanted_properties) {
199 for (auto& allocation : allocations) {
200 if (!allocation->IsCompatible(wanted_properties, requirements.memoryTypeBits)) {
201 continue;
202 }
203 if (auto commit = allocation->Commit(requirements.size, requirements.alignment)) {
204 return commit;
205 }
206 }
207 return {};
208}
209
210VKMemoryCommitImpl::VKMemoryCommitImpl(const VKDevice& device_, VKMemoryAllocation* allocation_,
211 const vk::DeviceMemory& memory_, u64 begin_, u64 end_)
212 : device{device_}, memory{memory_}, interval{begin_, end_}, allocation{allocation_} {}
213
214VKMemoryCommitImpl::~VKMemoryCommitImpl() {
215 allocation->Free(this);
216}
217
218MemoryMap VKMemoryCommitImpl::Map(u64 size, u64 offset_) const {
219 return MemoryMap(this, std::span<u8>(memory.Map(interval.first + offset_, size), size));
220}
221
222void VKMemoryCommitImpl::Unmap() const {
223 memory.Unmap();
224}
225
226MemoryMap VKMemoryCommitImpl::Map() const {
227 return Map(interval.second - interval.first);
228}
229
230} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_memory_manager.h b/src/video_core/renderer_vulkan/vk_memory_manager.h
deleted file mode 100644
index 318f8b43e..000000000
--- a/src/video_core/renderer_vulkan/vk_memory_manager.h
+++ /dev/null
@@ -1,132 +0,0 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <span>
9#include <utility>
10#include <vector>
11#include "common/common_types.h"
12#include "video_core/renderer_vulkan/wrapper.h"
13
14namespace Vulkan {
15
16class MemoryMap;
17class VKDevice;
18class VKMemoryAllocation;
19class VKMemoryCommitImpl;
20
21using VKMemoryCommit = std::unique_ptr<VKMemoryCommitImpl>;
22
23class VKMemoryManager final {
24public:
25 explicit VKMemoryManager(const VKDevice& device_);
26 VKMemoryManager(const VKMemoryManager&) = delete;
27 ~VKMemoryManager();
28
29 /**
30 * Commits a memory with the specified requeriments.
31 * @param requirements Requirements returned from a Vulkan call.
32 * @param host_visible Signals the allocator that it *must* use host visible and coherent
33 * memory. When passing false, it will try to allocate device local memory.
34 * @returns A memory commit.
35 */
36 VKMemoryCommit Commit(const VkMemoryRequirements& requirements, bool host_visible);
37
38 /// Commits memory required by the buffer and binds it.
39 VKMemoryCommit Commit(const vk::Buffer& buffer, bool host_visible);
40
41 /// Commits memory required by the image and binds it.
42 VKMemoryCommit Commit(const vk::Image& image, bool host_visible);
43
44private:
45 /// Allocates a chunk of memory.
46 bool AllocMemory(VkMemoryPropertyFlags wanted_properties, u32 type_mask, u64 size);
47
48 /// Tries to allocate a memory commit.
49 VKMemoryCommit TryAllocCommit(const VkMemoryRequirements& requirements,
50 VkMemoryPropertyFlags wanted_properties);
51
52 const VKDevice& device; ///< Device handler.
53 const VkPhysicalDeviceMemoryProperties properties; ///< Physical device properties.
54 std::vector<std::unique_ptr<VKMemoryAllocation>> allocations; ///< Current allocations.
55};
56
57class VKMemoryCommitImpl final {
58 friend VKMemoryAllocation;
59 friend MemoryMap;
60
61public:
62 explicit VKMemoryCommitImpl(const VKDevice& device_, VKMemoryAllocation* allocation_,
63 const vk::DeviceMemory& memory_, u64 begin_, u64 end_);
64 ~VKMemoryCommitImpl();
65
66 /// Maps a memory region and returns a pointer to it.
67 /// It's illegal to have more than one memory map at the same time.
68 MemoryMap Map(u64 size, u64 offset = 0) const;
69
70 /// Maps the whole commit and returns a pointer to it.
71 /// It's illegal to have more than one memory map at the same time.
72 MemoryMap Map() const;
73
74 /// Returns the Vulkan memory handler.
75 VkDeviceMemory GetMemory() const {
76 return *memory;
77 }
78
79 /// Returns the start position of the commit relative to the allocation.
80 VkDeviceSize GetOffset() const {
81 return static_cast<VkDeviceSize>(interval.first);
82 }
83
84private:
85 /// Unmaps memory.
86 void Unmap() const;
87
88 const VKDevice& device; ///< Vulkan device.
89 const vk::DeviceMemory& memory; ///< Vulkan device memory handler.
90 std::pair<u64, u64> interval{}; ///< Interval where the commit exists.
91 VKMemoryAllocation* allocation{}; ///< Pointer to the large memory allocation.
92};
93
94/// Holds ownership of a memory map.
95class MemoryMap final {
96public:
97 explicit MemoryMap(const VKMemoryCommitImpl* commit_, std::span<u8> span_)
98 : commit{commit_}, span{span_} {}
99
100 ~MemoryMap() {
101 if (commit) {
102 commit->Unmap();
103 }
104 }
105
106 /// Prematurely releases the memory map.
107 void Release() {
108 commit->Unmap();
109 commit = nullptr;
110 }
111
112 /// Returns a span to the memory map.
113 [[nodiscard]] std::span<u8> Span() const noexcept {
114 return span;
115 }
116
117 /// Returns the address of the memory map.
118 [[nodiscard]] u8* Address() const noexcept {
119 return span.data();
120 }
121
122 /// Returns the address of the memory map;
123 [[nodiscard]] operator u8*() const noexcept {
124 return span.data();
125 }
126
127private:
128 const VKMemoryCommitImpl* commit{}; ///< Mapped memory commit.
129 std::span<u8> span; ///< Address to the mapped memory.
130};
131
132} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 083796d05..02282e36f 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -19,17 +19,17 @@
19#include "video_core/renderer_vulkan/maxwell_to_vk.h" 19#include "video_core/renderer_vulkan/maxwell_to_vk.h"
20#include "video_core/renderer_vulkan/vk_compute_pipeline.h" 20#include "video_core/renderer_vulkan/vk_compute_pipeline.h"
21#include "video_core/renderer_vulkan/vk_descriptor_pool.h" 21#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
22#include "video_core/renderer_vulkan/vk_device.h"
23#include "video_core/renderer_vulkan/vk_graphics_pipeline.h" 22#include "video_core/renderer_vulkan/vk_graphics_pipeline.h"
24#include "video_core/renderer_vulkan/vk_pipeline_cache.h" 23#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
25#include "video_core/renderer_vulkan/vk_rasterizer.h" 24#include "video_core/renderer_vulkan/vk_rasterizer.h"
26#include "video_core/renderer_vulkan/vk_scheduler.h" 25#include "video_core/renderer_vulkan/vk_scheduler.h"
27#include "video_core/renderer_vulkan/vk_update_descriptor.h" 26#include "video_core/renderer_vulkan/vk_update_descriptor.h"
28#include "video_core/renderer_vulkan/wrapper.h"
29#include "video_core/shader/compiler_settings.h" 27#include "video_core/shader/compiler_settings.h"
30#include "video_core/shader/memory_util.h" 28#include "video_core/shader/memory_util.h"
31#include "video_core/shader_cache.h" 29#include "video_core/shader_cache.h"
32#include "video_core/shader_notify.h" 30#include "video_core/shader_notify.h"
31#include "video_core/vulkan_common/vulkan_device.h"
32#include "video_core/vulkan_common/vulkan_wrapper.h"
33 33
34namespace Vulkan { 34namespace Vulkan {
35 35
@@ -149,7 +149,7 @@ Shader::~Shader() = default;
149VKPipelineCache::VKPipelineCache(RasterizerVulkan& rasterizer_, Tegra::GPU& gpu_, 149VKPipelineCache::VKPipelineCache(RasterizerVulkan& rasterizer_, Tegra::GPU& gpu_,
150 Tegra::Engines::Maxwell3D& maxwell3d_, 150 Tegra::Engines::Maxwell3D& maxwell3d_,
151 Tegra::Engines::KeplerCompute& kepler_compute_, 151 Tegra::Engines::KeplerCompute& kepler_compute_,
152 Tegra::MemoryManager& gpu_memory_, const VKDevice& device_, 152 Tegra::MemoryManager& gpu_memory_, const Device& device_,
153 VKScheduler& scheduler_, VKDescriptorPool& descriptor_pool_, 153 VKScheduler& scheduler_, VKDescriptorPool& descriptor_pool_,
154 VKUpdateDescriptorQueue& update_descriptor_queue_) 154 VKUpdateDescriptorQueue& update_descriptor_queue_)
155 : VideoCommon::ShaderCache<Shader>{rasterizer_}, gpu{gpu_}, maxwell3d{maxwell3d_}, 155 : VideoCommon::ShaderCache<Shader>{rasterizer_}, gpu{gpu_}, maxwell3d{maxwell3d_},
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
index fbaa8257c..89d635a3d 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
@@ -20,12 +20,12 @@
20#include "video_core/renderer_vulkan/fixed_pipeline_state.h" 20#include "video_core/renderer_vulkan/fixed_pipeline_state.h"
21#include "video_core/renderer_vulkan/vk_graphics_pipeline.h" 21#include "video_core/renderer_vulkan/vk_graphics_pipeline.h"
22#include "video_core/renderer_vulkan/vk_shader_decompiler.h" 22#include "video_core/renderer_vulkan/vk_shader_decompiler.h"
23#include "video_core/renderer_vulkan/wrapper.h"
24#include "video_core/shader/async_shaders.h" 23#include "video_core/shader/async_shaders.h"
25#include "video_core/shader/memory_util.h" 24#include "video_core/shader/memory_util.h"
26#include "video_core/shader/registry.h" 25#include "video_core/shader/registry.h"
27#include "video_core/shader/shader_ir.h" 26#include "video_core/shader/shader_ir.h"
28#include "video_core/shader_cache.h" 27#include "video_core/shader_cache.h"
28#include "video_core/vulkan_common/vulkan_wrapper.h"
29 29
30namespace Core { 30namespace Core {
31class System; 31class System;
@@ -33,10 +33,10 @@ class System;
33 33
34namespace Vulkan { 34namespace Vulkan {
35 35
36class Device;
36class RasterizerVulkan; 37class RasterizerVulkan;
37class VKComputePipeline; 38class VKComputePipeline;
38class VKDescriptorPool; 39class VKDescriptorPool;
39class VKDevice;
40class VKScheduler; 40class VKScheduler;
41class VKUpdateDescriptorQueue; 41class VKUpdateDescriptorQueue;
42 42
@@ -121,7 +121,7 @@ public:
121 explicit VKPipelineCache(RasterizerVulkan& rasterizer, Tegra::GPU& gpu, 121 explicit VKPipelineCache(RasterizerVulkan& rasterizer, Tegra::GPU& gpu,
122 Tegra::Engines::Maxwell3D& maxwell3d, 122 Tegra::Engines::Maxwell3D& maxwell3d,
123 Tegra::Engines::KeplerCompute& kepler_compute, 123 Tegra::Engines::KeplerCompute& kepler_compute,
124 Tegra::MemoryManager& gpu_memory, const VKDevice& device, 124 Tegra::MemoryManager& gpu_memory, const Device& device,
125 VKScheduler& scheduler, VKDescriptorPool& descriptor_pool, 125 VKScheduler& scheduler, VKDescriptorPool& descriptor_pool,
126 VKUpdateDescriptorQueue& update_descriptor_queue); 126 VKUpdateDescriptorQueue& update_descriptor_queue);
127 ~VKPipelineCache() override; 127 ~VKPipelineCache() override;
@@ -148,7 +148,7 @@ private:
148 Tegra::Engines::KeplerCompute& kepler_compute; 148 Tegra::Engines::KeplerCompute& kepler_compute;
149 Tegra::MemoryManager& gpu_memory; 149 Tegra::MemoryManager& gpu_memory;
150 150
151 const VKDevice& device; 151 const Device& device;
152 VKScheduler& scheduler; 152 VKScheduler& scheduler;
153 VKDescriptorPool& descriptor_pool; 153 VKDescriptorPool& descriptor_pool;
154 VKUpdateDescriptorQueue& update_descriptor_queue; 154 VKUpdateDescriptorQueue& update_descriptor_queue;
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp
index 038760de3..7cadd5147 100644
--- a/src/video_core/renderer_vulkan/vk_query_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp
@@ -7,11 +7,11 @@
7#include <utility> 7#include <utility>
8#include <vector> 8#include <vector>
9 9
10#include "video_core/renderer_vulkan/vk_device.h"
11#include "video_core/renderer_vulkan/vk_query_cache.h" 10#include "video_core/renderer_vulkan/vk_query_cache.h"
12#include "video_core/renderer_vulkan/vk_resource_pool.h" 11#include "video_core/renderer_vulkan/vk_resource_pool.h"
13#include "video_core/renderer_vulkan/vk_scheduler.h" 12#include "video_core/renderer_vulkan/vk_scheduler.h"
14#include "video_core/renderer_vulkan/wrapper.h" 13#include "video_core/vulkan_common/vulkan_device.h"
14#include "video_core/vulkan_common/vulkan_wrapper.h"
15 15
16namespace Vulkan { 16namespace Vulkan {
17 17
@@ -27,7 +27,7 @@ constexpr VkQueryType GetTarget(QueryType type) {
27 27
28} // Anonymous namespace 28} // Anonymous namespace
29 29
30QueryPool::QueryPool(const VKDevice& device_, VKScheduler& scheduler, QueryType type_) 30QueryPool::QueryPool(const Device& device_, VKScheduler& scheduler, QueryType type_)
31 : ResourcePool{scheduler.GetMasterSemaphore(), GROW_STEP}, device{device_}, type{type_} {} 31 : ResourcePool{scheduler.GetMasterSemaphore(), GROW_STEP}, device{device_}, type{type_} {}
32 32
33QueryPool::~QueryPool() = default; 33QueryPool::~QueryPool() = default;
@@ -68,7 +68,7 @@ void QueryPool::Reserve(std::pair<VkQueryPool, u32> query) {
68 68
69VKQueryCache::VKQueryCache(VideoCore::RasterizerInterface& rasterizer_, 69VKQueryCache::VKQueryCache(VideoCore::RasterizerInterface& rasterizer_,
70 Tegra::Engines::Maxwell3D& maxwell3d_, Tegra::MemoryManager& gpu_memory_, 70 Tegra::Engines::Maxwell3D& maxwell3d_, Tegra::MemoryManager& gpu_memory_,
71 const VKDevice& device_, VKScheduler& scheduler_) 71 const Device& device_, VKScheduler& scheduler_)
72 : QueryCacheBase{rasterizer_, maxwell3d_, gpu_memory_}, device{device_}, scheduler{scheduler_}, 72 : QueryCacheBase{rasterizer_, maxwell3d_, gpu_memory_}, device{device_}, scheduler{scheduler_},
73 query_pools{ 73 query_pools{
74 QueryPool{device_, scheduler_, QueryType::SamplesPassed}, 74 QueryPool{device_, scheduler_, QueryType::SamplesPassed},
@@ -96,9 +96,9 @@ void VKQueryCache::Reserve(QueryType type, std::pair<VkQueryPool, u32> query) {
96HostCounter::HostCounter(VKQueryCache& cache_, std::shared_ptr<HostCounter> dependency_, 96HostCounter::HostCounter(VKQueryCache& cache_, std::shared_ptr<HostCounter> dependency_,
97 QueryType type_) 97 QueryType type_)
98 : HostCounterBase{std::move(dependency_)}, cache{cache_}, type{type_}, 98 : HostCounterBase{std::move(dependency_)}, cache{cache_}, type{type_},
99 query{cache_.AllocateQuery(type_)}, tick{cache_.Scheduler().CurrentTick()} { 99 query{cache_.AllocateQuery(type_)}, tick{cache_.GetScheduler().CurrentTick()} {
100 const vk::Device* logical = &cache_.Device().GetLogical(); 100 const vk::Device* logical = &cache.GetDevice().GetLogical();
101 cache_.Scheduler().Record([logical, query = query](vk::CommandBuffer cmdbuf) { 101 cache.GetScheduler().Record([logical, query = query](vk::CommandBuffer cmdbuf) {
102 logical->ResetQueryPoolEXT(query.first, query.second, 1); 102 logical->ResetQueryPoolEXT(query.first, query.second, 1);
103 cmdbuf.BeginQuery(query.first, query.second, VK_QUERY_CONTROL_PRECISE_BIT); 103 cmdbuf.BeginQuery(query.first, query.second, VK_QUERY_CONTROL_PRECISE_BIT);
104 }); 104 });
@@ -109,17 +109,17 @@ HostCounter::~HostCounter() {
109} 109}
110 110
111void HostCounter::EndQuery() { 111void HostCounter::EndQuery() {
112 cache.Scheduler().Record( 112 cache.GetScheduler().Record(
113 [query = query](vk::CommandBuffer cmdbuf) { cmdbuf.EndQuery(query.first, query.second); }); 113 [query = query](vk::CommandBuffer cmdbuf) { cmdbuf.EndQuery(query.first, query.second); });
114} 114}
115 115
116u64 HostCounter::BlockingQuery() const { 116u64 HostCounter::BlockingQuery() const {
117 if (tick >= cache.Scheduler().CurrentTick()) { 117 if (tick >= cache.GetScheduler().CurrentTick()) {
118 cache.Scheduler().Flush(); 118 cache.GetScheduler().Flush();
119 } 119 }
120 120
121 u64 data; 121 u64 data;
122 const VkResult query_result = cache.Device().GetLogical().GetQueryResults( 122 const VkResult query_result = cache.GetDevice().GetLogical().GetQueryResults(
123 query.first, query.second, 1, sizeof(data), &data, sizeof(data), 123 query.first, query.second, 1, sizeof(data), &data, sizeof(data),
124 VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT); 124 VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
125 125
@@ -127,7 +127,7 @@ u64 HostCounter::BlockingQuery() const {
127 case VK_SUCCESS: 127 case VK_SUCCESS:
128 return data; 128 return data;
129 case VK_ERROR_DEVICE_LOST: 129 case VK_ERROR_DEVICE_LOST:
130 cache.Device().ReportLoss(); 130 cache.GetDevice().ReportLoss();
131 [[fallthrough]]; 131 [[fallthrough]];
132 default: 132 default:
133 throw vk::Exception(query_result); 133 throw vk::Exception(query_result);
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.h b/src/video_core/renderer_vulkan/vk_query_cache.h
index 837fe9ebf..7190946b9 100644
--- a/src/video_core/renderer_vulkan/vk_query_cache.h
+++ b/src/video_core/renderer_vulkan/vk_query_cache.h
@@ -12,7 +12,7 @@
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "video_core/query_cache.h" 13#include "video_core/query_cache.h"
14#include "video_core/renderer_vulkan/vk_resource_pool.h" 14#include "video_core/renderer_vulkan/vk_resource_pool.h"
15#include "video_core/renderer_vulkan/wrapper.h" 15#include "video_core/vulkan_common/vulkan_wrapper.h"
16 16
17namespace VideoCore { 17namespace VideoCore {
18class RasterizerInterface; 18class RasterizerInterface;
@@ -21,8 +21,8 @@ class RasterizerInterface;
21namespace Vulkan { 21namespace Vulkan {
22 22
23class CachedQuery; 23class CachedQuery;
24class Device;
24class HostCounter; 25class HostCounter;
25class VKDevice;
26class VKQueryCache; 26class VKQueryCache;
27class VKScheduler; 27class VKScheduler;
28 28
@@ -30,7 +30,7 @@ using CounterStream = VideoCommon::CounterStreamBase<VKQueryCache, HostCounter>;
30 30
31class QueryPool final : public ResourcePool { 31class QueryPool final : public ResourcePool {
32public: 32public:
33 explicit QueryPool(const VKDevice& device, VKScheduler& scheduler, VideoCore::QueryType type); 33 explicit QueryPool(const Device& device, VKScheduler& scheduler, VideoCore::QueryType type);
34 ~QueryPool() override; 34 ~QueryPool() override;
35 35
36 std::pair<VkQueryPool, u32> Commit(); 36 std::pair<VkQueryPool, u32> Commit();
@@ -43,7 +43,7 @@ protected:
43private: 43private:
44 static constexpr std::size_t GROW_STEP = 512; 44 static constexpr std::size_t GROW_STEP = 512;
45 45
46 const VKDevice& device; 46 const Device& device;
47 const VideoCore::QueryType type; 47 const VideoCore::QueryType type;
48 48
49 std::vector<vk::QueryPool> pools; 49 std::vector<vk::QueryPool> pools;
@@ -55,23 +55,23 @@ class VKQueryCache final
55public: 55public:
56 explicit VKQueryCache(VideoCore::RasterizerInterface& rasterizer_, 56 explicit VKQueryCache(VideoCore::RasterizerInterface& rasterizer_,
57 Tegra::Engines::Maxwell3D& maxwell3d_, Tegra::MemoryManager& gpu_memory_, 57 Tegra::Engines::Maxwell3D& maxwell3d_, Tegra::MemoryManager& gpu_memory_,
58 const VKDevice& device_, VKScheduler& scheduler_); 58 const Device& device_, VKScheduler& scheduler_);
59 ~VKQueryCache(); 59 ~VKQueryCache();
60 60
61 std::pair<VkQueryPool, u32> AllocateQuery(VideoCore::QueryType type); 61 std::pair<VkQueryPool, u32> AllocateQuery(VideoCore::QueryType type);
62 62
63 void Reserve(VideoCore::QueryType type, std::pair<VkQueryPool, u32> query); 63 void Reserve(VideoCore::QueryType type, std::pair<VkQueryPool, u32> query);
64 64
65 const VKDevice& Device() const noexcept { 65 const Device& GetDevice() const noexcept {
66 return device; 66 return device;
67 } 67 }
68 68
69 VKScheduler& Scheduler() const noexcept { 69 VKScheduler& GetScheduler() const noexcept {
70 return scheduler; 70 return scheduler;
71 } 71 }
72 72
73private: 73private:
74 const VKDevice& device; 74 const Device& device;
75 VKScheduler& scheduler; 75 VKScheduler& scheduler;
76 std::array<QueryPool, VideoCore::NumQueryTypes> query_pools; 76 std::array<QueryPool, VideoCore::NumQueryTypes> query_pools;
77}; 77};
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 04c5c859c..f0a111829 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -27,7 +27,6 @@
27#include "video_core/renderer_vulkan/vk_compute_pass.h" 27#include "video_core/renderer_vulkan/vk_compute_pass.h"
28#include "video_core/renderer_vulkan/vk_compute_pipeline.h" 28#include "video_core/renderer_vulkan/vk_compute_pipeline.h"
29#include "video_core/renderer_vulkan/vk_descriptor_pool.h" 29#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
30#include "video_core/renderer_vulkan/vk_device.h"
31#include "video_core/renderer_vulkan/vk_graphics_pipeline.h" 30#include "video_core/renderer_vulkan/vk_graphics_pipeline.h"
32#include "video_core/renderer_vulkan/vk_pipeline_cache.h" 31#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
33#include "video_core/renderer_vulkan/vk_rasterizer.h" 32#include "video_core/renderer_vulkan/vk_rasterizer.h"
@@ -36,9 +35,10 @@
36#include "video_core/renderer_vulkan/vk_state_tracker.h" 35#include "video_core/renderer_vulkan/vk_state_tracker.h"
37#include "video_core/renderer_vulkan/vk_texture_cache.h" 36#include "video_core/renderer_vulkan/vk_texture_cache.h"
38#include "video_core/renderer_vulkan/vk_update_descriptor.h" 37#include "video_core/renderer_vulkan/vk_update_descriptor.h"
39#include "video_core/renderer_vulkan/wrapper.h"
40#include "video_core/shader_cache.h" 38#include "video_core/shader_cache.h"
41#include "video_core/texture_cache/texture_cache.h" 39#include "video_core/texture_cache/texture_cache.h"
40#include "video_core/vulkan_common/vulkan_device.h"
41#include "video_core/vulkan_common/vulkan_wrapper.h"
42 42
43namespace Vulkan { 43namespace Vulkan {
44 44
@@ -62,7 +62,7 @@ namespace {
62 62
63constexpr auto COMPUTE_SHADER_INDEX = static_cast<size_t>(Tegra::Engines::ShaderType::Compute); 63constexpr auto COMPUTE_SHADER_INDEX = static_cast<size_t>(Tegra::Engines::ShaderType::Compute);
64 64
65VkViewport GetViewportState(const VKDevice& device, const Maxwell& regs, size_t index) { 65VkViewport GetViewportState(const Device& device, const Maxwell& regs, size_t index) {
66 const auto& src = regs.viewport_transform[index]; 66 const auto& src = regs.viewport_transform[index];
67 const float width = src.scale_x * 2.0f; 67 const float width = src.scale_x * 2.0f;
68 const float height = src.scale_y * 2.0f; 68 const float height = src.scale_y * 2.0f;
@@ -239,7 +239,7 @@ public:
239 index.type = type; 239 index.type = type;
240 } 240 }
241 241
242 void Bind(const VKDevice& device, VKScheduler& scheduler) const { 242 void Bind(const Device& device, VKScheduler& scheduler) const {
243 // Use this large switch case to avoid dispatching more memory in the record lambda than 243 // Use this large switch case to avoid dispatching more memory in the record lambda than
244 // what we need. It looks horrible, but it's the best we can do on standard C++. 244 // what we need. It looks horrible, but it's the best we can do on standard C++.
245 switch (vertex.num_buffers) { 245 switch (vertex.num_buffers) {
@@ -330,7 +330,7 @@ private:
330 } index; 330 } index;
331 331
332 template <size_t N> 332 template <size_t N>
333 void BindStatic(const VKDevice& device, VKScheduler& scheduler) const { 333 void BindStatic(const Device& device, VKScheduler& scheduler) const {
334 if (device.IsExtExtendedDynamicStateSupported()) { 334 if (device.IsExtExtendedDynamicStateSupported()) {
335 if (index.buffer) { 335 if (index.buffer) {
336 BindStatic<N, true, true>(scheduler); 336 BindStatic<N, true, true>(scheduler);
@@ -409,27 +409,26 @@ void RasterizerVulkan::DrawParameters::Draw(vk::CommandBuffer cmdbuf) const {
409RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, 409RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_,
410 Tegra::MemoryManager& gpu_memory_, 410 Tegra::MemoryManager& gpu_memory_,
411 Core::Memory::Memory& cpu_memory_, VKScreenInfo& screen_info_, 411 Core::Memory::Memory& cpu_memory_, VKScreenInfo& screen_info_,
412 const VKDevice& device_, VKMemoryManager& memory_manager_, 412 const Device& device_, MemoryAllocator& memory_allocator_,
413 StateTracker& state_tracker_, VKScheduler& scheduler_) 413 StateTracker& state_tracker_, VKScheduler& scheduler_)
414 : RasterizerAccelerated{cpu_memory_}, gpu{gpu_}, 414 : RasterizerAccelerated{cpu_memory_}, gpu{gpu_},
415 gpu_memory{gpu_memory_}, maxwell3d{gpu.Maxwell3D()}, kepler_compute{gpu.KeplerCompute()}, 415 gpu_memory{gpu_memory_}, maxwell3d{gpu.Maxwell3D()}, kepler_compute{gpu.KeplerCompute()},
416 screen_info{screen_info_}, device{device_}, memory_manager{memory_manager_}, 416 screen_info{screen_info_}, device{device_}, memory_allocator{memory_allocator_},
417 state_tracker{state_tracker_}, scheduler{scheduler_}, stream_buffer(device, scheduler), 417 state_tracker{state_tracker_}, scheduler{scheduler_}, stream_buffer(device, scheduler),
418 staging_pool(device, memory_manager, scheduler), descriptor_pool(device, scheduler), 418 staging_pool(device, memory_allocator, scheduler), descriptor_pool(device, scheduler),
419 update_descriptor_queue(device, scheduler), 419 update_descriptor_queue(device, scheduler),
420 blit_image(device, scheduler, state_tracker, descriptor_pool), 420 blit_image(device, scheduler, state_tracker, descriptor_pool),
421 quad_array_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue), 421 quad_array_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
422 quad_indexed_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue), 422 quad_indexed_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
423 uint8_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue), 423 uint8_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
424 texture_cache_runtime{device, scheduler, memory_manager, staging_pool, blit_image}, 424 texture_cache_runtime{device, scheduler, memory_allocator, staging_pool, blit_image},
425 texture_cache(texture_cache_runtime, *this, maxwell3d, kepler_compute, gpu_memory), 425 texture_cache(texture_cache_runtime, *this, maxwell3d, kepler_compute, gpu_memory),
426 pipeline_cache(*this, gpu, maxwell3d, kepler_compute, gpu_memory, device, scheduler, 426 pipeline_cache(*this, gpu, maxwell3d, kepler_compute, gpu_memory, device, scheduler,
427 descriptor_pool, update_descriptor_queue), 427 descriptor_pool, update_descriptor_queue),
428 buffer_cache(*this, gpu_memory, cpu_memory_, device, memory_manager, scheduler, stream_buffer, 428 buffer_cache(*this, gpu_memory, cpu_memory_, device, memory_allocator, scheduler,
429 staging_pool), 429 stream_buffer, staging_pool),
430 query_cache{*this, maxwell3d, gpu_memory, device, scheduler}, 430 query_cache{*this, maxwell3d, gpu_memory, device, scheduler},
431 fence_manager(*this, gpu, gpu_memory, texture_cache, buffer_cache, query_cache, device, 431 fence_manager(*this, gpu, gpu_memory, texture_cache, buffer_cache, query_cache, scheduler),
432 scheduler),
433 wfi_event(device.GetLogical().CreateEvent()), async_shaders(emu_window_) { 432 wfi_event(device.GetLogical().CreateEvent()), async_shaders(emu_window_) {
434 scheduler.SetQueryCache(query_cache); 433 scheduler.SetQueryCache(query_cache);
435 if (device.UseAsynchronousShaders()) { 434 if (device.UseAsynchronousShaders()) {
@@ -628,8 +627,10 @@ void RasterizerVulkan::DispatchCompute(GPUVAddr code_addr) {
628 grid_z = launch_desc.grid_dim_z, pipeline_handle, pipeline_layout, 627 grid_z = launch_desc.grid_dim_z, pipeline_handle, pipeline_layout,
629 descriptor_set](vk::CommandBuffer cmdbuf) { 628 descriptor_set](vk::CommandBuffer cmdbuf) {
630 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, pipeline_handle); 629 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, pipeline_handle);
631 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, pipeline_layout, DESCRIPTOR_SET, 630 if (descriptor_set) {
632 descriptor_set, {}); 631 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, pipeline_layout,
632 DESCRIPTOR_SET, descriptor_set, nullptr);
633 }
633 cmdbuf.Dispatch(grid_x, grid_y, grid_z); 634 cmdbuf.Dispatch(grid_x, grid_y, grid_z);
634 }); 635 });
635} 636}
@@ -1444,7 +1445,7 @@ VkBuffer RasterizerVulkan::DefaultBuffer() {
1444 .queueFamilyIndexCount = 0, 1445 .queueFamilyIndexCount = 0,
1445 .pQueueFamilyIndices = nullptr, 1446 .pQueueFamilyIndices = nullptr,
1446 }); 1447 });
1447 default_buffer_commit = memory_manager.Commit(default_buffer, false); 1448 default_buffer_commit = memory_allocator.Commit(default_buffer, MemoryUsage::DeviceLocal);
1448 1449
1449 scheduler.RequestOutsideRenderPassOperationContext(); 1450 scheduler.RequestOutsideRenderPassOperationContext();
1450 scheduler.Record([buffer = *default_buffer](vk::CommandBuffer cmdbuf) { 1451 scheduler.Record([buffer = *default_buffer](vk::CommandBuffer cmdbuf) {
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index 990f9e031..8e261b9bd 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -21,7 +21,6 @@
21#include "video_core/renderer_vulkan/vk_compute_pass.h" 21#include "video_core/renderer_vulkan/vk_compute_pass.h"
22#include "video_core/renderer_vulkan/vk_descriptor_pool.h" 22#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
23#include "video_core/renderer_vulkan/vk_fence_manager.h" 23#include "video_core/renderer_vulkan/vk_fence_manager.h"
24#include "video_core/renderer_vulkan/vk_memory_manager.h"
25#include "video_core/renderer_vulkan/vk_pipeline_cache.h" 24#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
26#include "video_core/renderer_vulkan/vk_query_cache.h" 25#include "video_core/renderer_vulkan/vk_query_cache.h"
27#include "video_core/renderer_vulkan/vk_scheduler.h" 26#include "video_core/renderer_vulkan/vk_scheduler.h"
@@ -29,8 +28,9 @@
29#include "video_core/renderer_vulkan/vk_stream_buffer.h" 28#include "video_core/renderer_vulkan/vk_stream_buffer.h"
30#include "video_core/renderer_vulkan/vk_texture_cache.h" 29#include "video_core/renderer_vulkan/vk_texture_cache.h"
31#include "video_core/renderer_vulkan/vk_update_descriptor.h" 30#include "video_core/renderer_vulkan/vk_update_descriptor.h"
32#include "video_core/renderer_vulkan/wrapper.h"
33#include "video_core/shader/async_shaders.h" 31#include "video_core/shader/async_shaders.h"
32#include "video_core/vulkan_common/vulkan_memory_allocator.h"
33#include "video_core/vulkan_common/vulkan_wrapper.h"
34 34
35namespace Core { 35namespace Core {
36class System; 36class System;
@@ -55,8 +55,8 @@ class RasterizerVulkan final : public VideoCore::RasterizerAccelerated {
55public: 55public:
56 explicit RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, 56 explicit RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_,
57 Tegra::MemoryManager& gpu_memory_, Core::Memory::Memory& cpu_memory_, 57 Tegra::MemoryManager& gpu_memory_, Core::Memory::Memory& cpu_memory_,
58 VKScreenInfo& screen_info_, const VKDevice& device_, 58 VKScreenInfo& screen_info_, const Device& device_,
59 VKMemoryManager& memory_manager_, StateTracker& state_tracker_, 59 MemoryAllocator& memory_allocator_, StateTracker& state_tracker_,
60 VKScheduler& scheduler_); 60 VKScheduler& scheduler_);
61 ~RasterizerVulkan() override; 61 ~RasterizerVulkan() override;
62 62
@@ -212,13 +212,13 @@ private:
212 Tegra::Engines::KeplerCompute& kepler_compute; 212 Tegra::Engines::KeplerCompute& kepler_compute;
213 213
214 VKScreenInfo& screen_info; 214 VKScreenInfo& screen_info;
215 const VKDevice& device; 215 const Device& device;
216 VKMemoryManager& memory_manager; 216 MemoryAllocator& memory_allocator;
217 StateTracker& state_tracker; 217 StateTracker& state_tracker;
218 VKScheduler& scheduler; 218 VKScheduler& scheduler;
219 219
220 VKStreamBuffer stream_buffer; 220 VKStreamBuffer stream_buffer;
221 VKStagingBufferPool staging_pool; 221 StagingBufferPool staging_pool;
222 VKDescriptorPool descriptor_pool; 222 VKDescriptorPool descriptor_pool;
223 VKUpdateDescriptorQueue update_descriptor_queue; 223 VKUpdateDescriptorQueue update_descriptor_queue;
224 BlitImageHelper blit_image; 224 BlitImageHelper blit_image;
@@ -234,7 +234,7 @@ private:
234 VKFenceManager fence_manager; 234 VKFenceManager fence_manager;
235 235
236 vk::Buffer default_buffer; 236 vk::Buffer default_buffer;
237 VKMemoryCommit default_buffer_commit; 237 MemoryCommit default_buffer_commit;
238 vk::Event wfi_event; 238 vk::Event wfi_event;
239 VideoCommon::Shader::AsyncShaders async_shaders; 239 VideoCommon::Shader::AsyncShaders async_shaders;
240 240
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index c104c6fe3..66004f9c0 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -11,13 +11,13 @@
11#include "common/microprofile.h" 11#include "common/microprofile.h"
12#include "common/thread.h" 12#include "common/thread.h"
13#include "video_core/renderer_vulkan/vk_command_pool.h" 13#include "video_core/renderer_vulkan/vk_command_pool.h"
14#include "video_core/renderer_vulkan/vk_device.h"
15#include "video_core/renderer_vulkan/vk_master_semaphore.h" 14#include "video_core/renderer_vulkan/vk_master_semaphore.h"
16#include "video_core/renderer_vulkan/vk_query_cache.h" 15#include "video_core/renderer_vulkan/vk_query_cache.h"
17#include "video_core/renderer_vulkan/vk_scheduler.h" 16#include "video_core/renderer_vulkan/vk_scheduler.h"
18#include "video_core/renderer_vulkan/vk_state_tracker.h" 17#include "video_core/renderer_vulkan/vk_state_tracker.h"
19#include "video_core/renderer_vulkan/vk_texture_cache.h" 18#include "video_core/renderer_vulkan/vk_texture_cache.h"
20#include "video_core/renderer_vulkan/wrapper.h" 19#include "video_core/vulkan_common/vulkan_device.h"
20#include "video_core/vulkan_common/vulkan_wrapper.h"
21 21
22namespace Vulkan { 22namespace Vulkan {
23 23
@@ -37,7 +37,7 @@ void VKScheduler::CommandChunk::ExecuteAll(vk::CommandBuffer cmdbuf) {
37 last = nullptr; 37 last = nullptr;
38} 38}
39 39
40VKScheduler::VKScheduler(const VKDevice& device_, StateTracker& state_tracker_) 40VKScheduler::VKScheduler(const Device& device_, StateTracker& state_tracker_)
41 : device{device_}, state_tracker{state_tracker_}, 41 : device{device_}, state_tracker{state_tracker_},
42 master_semaphore{std::make_unique<MasterSemaphore>(device)}, 42 master_semaphore{std::make_unique<MasterSemaphore>(device)},
43 command_pool{std::make_unique<CommandPool>(*master_semaphore, device)} { 43 command_pool{std::make_unique<CommandPool>(*master_semaphore, device)} {
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h
index 0a36c8fad..4cd43e425 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.h
+++ b/src/video_core/renderer_vulkan/vk_scheduler.h
@@ -12,22 +12,22 @@
12#include <utility> 12#include <utility>
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "common/threadsafe_queue.h" 14#include "common/threadsafe_queue.h"
15#include "video_core/renderer_vulkan/wrapper.h" 15#include "video_core/vulkan_common/vulkan_wrapper.h"
16 16
17namespace Vulkan { 17namespace Vulkan {
18 18
19class CommandPool; 19class CommandPool;
20class Device;
20class Framebuffer; 21class Framebuffer;
21class MasterSemaphore; 22class MasterSemaphore;
22class StateTracker; 23class StateTracker;
23class VKDevice;
24class VKQueryCache; 24class VKQueryCache;
25 25
26/// The scheduler abstracts command buffer and fence management with an interface that's able to do 26/// The scheduler abstracts command buffer and fence management with an interface that's able to do
27/// OpenGL-like operations on Vulkan command buffers. 27/// OpenGL-like operations on Vulkan command buffers.
28class VKScheduler { 28class VKScheduler {
29public: 29public:
30 explicit VKScheduler(const VKDevice& device, StateTracker& state_tracker); 30 explicit VKScheduler(const Device& device, StateTracker& state_tracker);
31 ~VKScheduler(); 31 ~VKScheduler();
32 32
33 /// Returns the current command buffer tick. 33 /// Returns the current command buffer tick.
@@ -179,7 +179,7 @@ private:
179 179
180 void AcquireNewChunk(); 180 void AcquireNewChunk();
181 181
182 const VKDevice& device; 182 const Device& device;
183 StateTracker& state_tracker; 183 StateTracker& state_tracker;
184 184
185 std::unique_ptr<MasterSemaphore> master_semaphore; 185 std::unique_ptr<MasterSemaphore> master_semaphore;
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
index 09d6f9f35..89cbe01ad 100644
--- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
+++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
@@ -22,11 +22,11 @@
22#include "video_core/engines/shader_bytecode.h" 22#include "video_core/engines/shader_bytecode.h"
23#include "video_core/engines/shader_header.h" 23#include "video_core/engines/shader_header.h"
24#include "video_core/engines/shader_type.h" 24#include "video_core/engines/shader_type.h"
25#include "video_core/renderer_vulkan/vk_device.h"
26#include "video_core/renderer_vulkan/vk_shader_decompiler.h" 25#include "video_core/renderer_vulkan/vk_shader_decompiler.h"
27#include "video_core/shader/node.h" 26#include "video_core/shader/node.h"
28#include "video_core/shader/shader_ir.h" 27#include "video_core/shader/shader_ir.h"
29#include "video_core/shader/transform_feedback.h" 28#include "video_core/shader/transform_feedback.h"
29#include "video_core/vulkan_common/vulkan_device.h"
30 30
31namespace Vulkan { 31namespace Vulkan {
32 32
@@ -272,19 +272,12 @@ bool IsPrecise(Operation operand) {
272 return false; 272 return false;
273} 273}
274 274
275u32 ShaderVersion(const VKDevice& device) {
276 if (device.InstanceApiVersion() < VK_API_VERSION_1_1) {
277 return 0x00010000;
278 }
279 return 0x00010300;
280}
281
282class SPIRVDecompiler final : public Sirit::Module { 275class SPIRVDecompiler final : public Sirit::Module {
283public: 276public:
284 explicit SPIRVDecompiler(const VKDevice& device_, const ShaderIR& ir_, ShaderType stage_, 277 explicit SPIRVDecompiler(const Device& device_, const ShaderIR& ir_, ShaderType stage_,
285 const Registry& registry_, const Specialization& specialization_) 278 const Registry& registry_, const Specialization& specialization_)
286 : Module(ShaderVersion(device_)), device{device_}, ir{ir_}, stage{stage_}, 279 : Module(0x00010300), device{device_}, ir{ir_}, stage{stage_}, header{ir_.GetHeader()},
287 header{ir_.GetHeader()}, registry{registry_}, specialization{specialization_} { 280 registry{registry_}, specialization{specialization_} {
288 if (stage_ != ShaderType::Compute) { 281 if (stage_ != ShaderType::Compute) {
289 transform_feedback = BuildTransformFeedback(registry_.GetGraphicsInfo()); 282 transform_feedback = BuildTransformFeedback(registry_.GetGraphicsInfo());
290 } 283 }
@@ -2749,7 +2742,7 @@ private:
2749 }; 2742 };
2750 static_assert(operation_decompilers.size() == static_cast<std::size_t>(OperationCode::Amount)); 2743 static_assert(operation_decompilers.size() == static_cast<std::size_t>(OperationCode::Amount));
2751 2744
2752 const VKDevice& device; 2745 const Device& device;
2753 const ShaderIR& ir; 2746 const ShaderIR& ir;
2754 const ShaderType stage; 2747 const ShaderType stage;
2755 const Tegra::Shader::Header header; 2748 const Tegra::Shader::Header header;
@@ -3137,7 +3130,7 @@ ShaderEntries GenerateShaderEntries(const VideoCommon::Shader::ShaderIR& ir) {
3137 return entries; 3130 return entries;
3138} 3131}
3139 3132
3140std::vector<u32> Decompile(const VKDevice& device, const VideoCommon::Shader::ShaderIR& ir, 3133std::vector<u32> Decompile(const Device& device, const VideoCommon::Shader::ShaderIR& ir,
3141 ShaderType stage, const VideoCommon::Shader::Registry& registry, 3134 ShaderType stage, const VideoCommon::Shader::Registry& registry,
3142 const Specialization& specialization) { 3135 const Specialization& specialization) {
3143 return SPIRVDecompiler(device, ir, stage, registry, specialization).Assemble(); 3136 return SPIRVDecompiler(device, ir, stage, registry, specialization).Assemble();
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.h b/src/video_core/renderer_vulkan/vk_shader_decompiler.h
index ad91ad5de..26381e444 100644
--- a/src/video_core/renderer_vulkan/vk_shader_decompiler.h
+++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.h
@@ -15,10 +15,8 @@
15#include "video_core/shader/shader_ir.h" 15#include "video_core/shader/shader_ir.h"
16 16
17namespace Vulkan { 17namespace Vulkan {
18class VKDevice;
19}
20 18
21namespace Vulkan { 19class Device;
22 20
23using Maxwell = Tegra::Engines::Maxwell3D::Regs; 21using Maxwell = Tegra::Engines::Maxwell3D::Regs;
24using UniformTexelEntry = VideoCommon::Shader::SamplerEntry; 22using UniformTexelEntry = VideoCommon::Shader::SamplerEntry;
@@ -109,7 +107,7 @@ struct SPIRVShader {
109 107
110ShaderEntries GenerateShaderEntries(const VideoCommon::Shader::ShaderIR& ir); 108ShaderEntries GenerateShaderEntries(const VideoCommon::Shader::ShaderIR& ir);
111 109
112std::vector<u32> Decompile(const VKDevice& device, const VideoCommon::Shader::ShaderIR& ir, 110std::vector<u32> Decompile(const Device& device, const VideoCommon::Shader::ShaderIR& ir,
113 Tegra::Engines::ShaderType stage, 111 Tegra::Engines::ShaderType stage,
114 const VideoCommon::Shader::Registry& registry, 112 const VideoCommon::Shader::Registry& registry,
115 const Specialization& specialization); 113 const Specialization& specialization);
diff --git a/src/video_core/renderer_vulkan/vk_shader_util.cpp b/src/video_core/renderer_vulkan/vk_shader_util.cpp
index 38a0be7f2..aaad4f292 100644
--- a/src/video_core/renderer_vulkan/vk_shader_util.cpp
+++ b/src/video_core/renderer_vulkan/vk_shader_util.cpp
@@ -7,13 +7,13 @@
7 7
8#include "common/assert.h" 8#include "common/assert.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "video_core/renderer_vulkan/vk_device.h"
11#include "video_core/renderer_vulkan/vk_shader_util.h" 10#include "video_core/renderer_vulkan/vk_shader_util.h"
12#include "video_core/renderer_vulkan/wrapper.h" 11#include "video_core/vulkan_common/vulkan_device.h"
12#include "video_core/vulkan_common/vulkan_wrapper.h"
13 13
14namespace Vulkan { 14namespace Vulkan {
15 15
16vk::ShaderModule BuildShader(const VKDevice& device, std::span<const u32> code) { 16vk::ShaderModule BuildShader(const Device& device, std::span<const u32> code) {
17 return device.GetLogical().CreateShaderModule({ 17 return device.GetLogical().CreateShaderModule({
18 .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, 18 .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
19 .pNext = nullptr, 19 .pNext = nullptr,
diff --git a/src/video_core/renderer_vulkan/vk_shader_util.h b/src/video_core/renderer_vulkan/vk_shader_util.h
index dce34a140..9517cbe84 100644
--- a/src/video_core/renderer_vulkan/vk_shader_util.h
+++ b/src/video_core/renderer_vulkan/vk_shader_util.h
@@ -7,12 +7,12 @@
7#include <span> 7#include <span>
8 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "video_core/renderer_vulkan/wrapper.h" 10#include "video_core/vulkan_common/vulkan_wrapper.h"
11 11
12namespace Vulkan { 12namespace Vulkan {
13 13
14class VKDevice; 14class Device;
15 15
16vk::ShaderModule BuildShader(const VKDevice& device, std::span<const u32> code); 16vk::ShaderModule BuildShader(const Device& device, std::span<const u32> code);
17 17
18} // namespace Vulkan 18} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
index 2fd3b7f39..97fd41cc1 100644
--- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
+++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
@@ -3,58 +3,66 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <unordered_map>
7#include <utility> 6#include <utility>
8#include <vector> 7#include <vector>
9 8
9#include <fmt/format.h>
10
11#include "common/assert.h"
10#include "common/bit_util.h" 12#include "common/bit_util.h"
11#include "common/common_types.h" 13#include "common/common_types.h"
12#include "video_core/renderer_vulkan/vk_device.h"
13#include "video_core/renderer_vulkan/vk_scheduler.h" 14#include "video_core/renderer_vulkan/vk_scheduler.h"
14#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" 15#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
15#include "video_core/renderer_vulkan/wrapper.h" 16#include "video_core/vulkan_common/vulkan_device.h"
17#include "video_core/vulkan_common/vulkan_wrapper.h"
16 18
17namespace Vulkan { 19namespace Vulkan {
18 20
19VKStagingBufferPool::StagingBuffer::StagingBuffer(std::unique_ptr<VKBuffer> buffer_) 21StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& memory_allocator_,
20 : buffer{std::move(buffer_)} {} 22 VKScheduler& scheduler_)
21 23 : device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_} {}
22VKStagingBufferPool::VKStagingBufferPool(const VKDevice& device_, VKMemoryManager& memory_manager_,
23 VKScheduler& scheduler_)
24 : device{device_}, memory_manager{memory_manager_}, scheduler{scheduler_} {}
25 24
26VKStagingBufferPool::~VKStagingBufferPool() = default; 25StagingBufferPool::~StagingBufferPool() = default;
27 26
28VKBuffer& VKStagingBufferPool::GetUnusedBuffer(std::size_t size, bool host_visible) { 27StagingBufferRef StagingBufferPool::Request(size_t size, MemoryUsage usage) {
29 if (const auto buffer = TryGetReservedBuffer(size, host_visible)) { 28 if (const std::optional<StagingBufferRef> ref = TryGetReservedBuffer(size, usage)) {
30 return *buffer; 29 return *ref;
31 } 30 }
32 return CreateStagingBuffer(size, host_visible); 31 return CreateStagingBuffer(size, usage);
33} 32}
34 33
35void VKStagingBufferPool::TickFrame() { 34void StagingBufferPool::TickFrame() {
36 current_delete_level = (current_delete_level + 1) % NumLevels; 35 current_delete_level = (current_delete_level + 1) % NUM_LEVELS;
37 36
38 ReleaseCache(true); 37 ReleaseCache(MemoryUsage::DeviceLocal);
39 ReleaseCache(false); 38 ReleaseCache(MemoryUsage::Upload);
39 ReleaseCache(MemoryUsage::Download);
40} 40}
41 41
42VKBuffer* VKStagingBufferPool::TryGetReservedBuffer(std::size_t size, bool host_visible) { 42std::optional<StagingBufferRef> StagingBufferPool::TryGetReservedBuffer(size_t size,
43 for (StagingBuffer& entry : GetCache(host_visible)[Common::Log2Ceil64(size)].entries) { 43 MemoryUsage usage) {
44 if (!scheduler.IsFree(entry.tick)) { 44 StagingBuffers& cache_level = GetCache(usage)[Common::Log2Ceil64(size)];
45 continue; 45
46 const auto is_free = [this](const StagingBuffer& entry) {
47 return scheduler.IsFree(entry.tick);
48 };
49 auto& entries = cache_level.entries;
50 const auto hint_it = entries.begin() + cache_level.iterate_index;
51 auto it = std::find_if(entries.begin() + cache_level.iterate_index, entries.end(), is_free);
52 if (it == entries.end()) {
53 it = std::find_if(entries.begin(), hint_it, is_free);
54 if (it == hint_it) {
55 return std::nullopt;
46 } 56 }
47 entry.tick = scheduler.CurrentTick();
48 return &*entry.buffer;
49 } 57 }
50 return nullptr; 58 cache_level.iterate_index = std::distance(entries.begin(), it) + 1;
59 it->tick = scheduler.CurrentTick();
60 return it->Ref();
51} 61}
52 62
53VKBuffer& VKStagingBufferPool::CreateStagingBuffer(std::size_t size, bool host_visible) { 63StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage usage) {
54 const u32 log2 = Common::Log2Ceil64(size); 64 const u32 log2 = Common::Log2Ceil64(size);
55 65 vk::Buffer buffer = device.GetLogical().CreateBuffer({
56 auto buffer = std::make_unique<VKBuffer>();
57 buffer->handle = device.GetLogical().CreateBuffer({
58 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, 66 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
59 .pNext = nullptr, 67 .pNext = nullptr,
60 .flags = 0, 68 .flags = 0,
@@ -66,49 +74,63 @@ VKBuffer& VKStagingBufferPool::CreateStagingBuffer(std::size_t size, bool host_v
66 .queueFamilyIndexCount = 0, 74 .queueFamilyIndexCount = 0,
67 .pQueueFamilyIndices = nullptr, 75 .pQueueFamilyIndices = nullptr,
68 }); 76 });
69 buffer->commit = memory_manager.Commit(buffer->handle, host_visible); 77 if (device.HasDebuggingToolAttached()) {
70 78 ++buffer_index;
71 std::vector<StagingBuffer>& entries = GetCache(host_visible)[log2].entries; 79 buffer.SetObjectNameEXT(fmt::format("Staging Buffer {}", buffer_index).c_str());
72 StagingBuffer& entry = entries.emplace_back(std::move(buffer)); 80 }
73 entry.tick = scheduler.CurrentTick(); 81 MemoryCommit commit = memory_allocator.Commit(buffer, usage);
74 return *entry.buffer; 82 const std::span<u8> mapped_span = IsHostVisible(usage) ? commit.Map() : std::span<u8>{};
75} 83
76 84 StagingBuffer& entry = GetCache(usage)[log2].entries.emplace_back(StagingBuffer{
77VKStagingBufferPool::StagingBuffersCache& VKStagingBufferPool::GetCache(bool host_visible) { 85 .buffer = std::move(buffer),
78 return host_visible ? host_staging_buffers : device_staging_buffers; 86 .commit = std::move(commit),
87 .mapped_span = mapped_span,
88 .tick = scheduler.CurrentTick(),
89 });
90 return entry.Ref();
79} 91}
80 92
81void VKStagingBufferPool::ReleaseCache(bool host_visible) { 93StagingBufferPool::StagingBuffersCache& StagingBufferPool::GetCache(MemoryUsage usage) {
82 auto& cache = GetCache(host_visible); 94 switch (usage) {
83 const u64 size = ReleaseLevel(cache, current_delete_level); 95 case MemoryUsage::DeviceLocal:
84 if (size == 0) { 96 return device_local_cache;
85 return; 97 case MemoryUsage::Upload:
98 return upload_cache;
99 case MemoryUsage::Download:
100 return download_cache;
101 default:
102 UNREACHABLE_MSG("Invalid memory usage={}", usage);
103 return upload_cache;
86 } 104 }
87} 105}
88 106
89u64 VKStagingBufferPool::ReleaseLevel(StagingBuffersCache& cache, std::size_t log2) { 107void StagingBufferPool::ReleaseCache(MemoryUsage usage) {
90 static constexpr std::size_t deletions_per_tick = 16; 108 ReleaseLevel(GetCache(usage), current_delete_level);
109}
91 110
111void StagingBufferPool::ReleaseLevel(StagingBuffersCache& cache, size_t log2) {
112 constexpr size_t deletions_per_tick = 16;
92 auto& staging = cache[log2]; 113 auto& staging = cache[log2];
93 auto& entries = staging.entries; 114 auto& entries = staging.entries;
94 const std::size_t old_size = entries.size(); 115 const size_t old_size = entries.size();
95 116
96 const auto is_deleteable = [this](const StagingBuffer& entry) { 117 const auto is_deleteable = [this](const StagingBuffer& entry) {
97 return scheduler.IsFree(entry.tick); 118 return scheduler.IsFree(entry.tick);
98 }; 119 };
99 const std::size_t begin_offset = staging.delete_index; 120 const size_t begin_offset = staging.delete_index;
100 const std::size_t end_offset = std::min(begin_offset + deletions_per_tick, old_size); 121 const size_t end_offset = std::min(begin_offset + deletions_per_tick, old_size);
101 const auto begin = std::begin(entries) + begin_offset; 122 const auto begin = entries.begin() + begin_offset;
102 const auto end = std::begin(entries) + end_offset; 123 const auto end = entries.begin() + end_offset;
103 entries.erase(std::remove_if(begin, end, is_deleteable), end); 124 entries.erase(std::remove_if(begin, end, is_deleteable), end);
104 125
105 const std::size_t new_size = entries.size(); 126 const size_t new_size = entries.size();
106 staging.delete_index += deletions_per_tick; 127 staging.delete_index += deletions_per_tick;
107 if (staging.delete_index >= new_size) { 128 if (staging.delete_index >= new_size) {
108 staging.delete_index = 0; 129 staging.delete_index = 0;
109 } 130 }
110 131 if (staging.iterate_index > new_size) {
111 return (1ULL << log2) * (old_size - new_size); 132 staging.iterate_index = 0;
133 }
112} 134}
113 135
114} // namespace Vulkan 136} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h
index 2dd5049ac..d42918a47 100644
--- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h
+++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h
@@ -9,63 +9,73 @@
9 9
10#include "common/common_types.h" 10#include "common/common_types.h"
11 11
12#include "video_core/renderer_vulkan/vk_memory_manager.h" 12#include "video_core/vulkan_common/vulkan_memory_allocator.h"
13#include "video_core/renderer_vulkan/wrapper.h" 13#include "video_core/vulkan_common/vulkan_wrapper.h"
14 14
15namespace Vulkan { 15namespace Vulkan {
16 16
17class VKDevice; 17class Device;
18class VKScheduler; 18class VKScheduler;
19 19
20struct VKBuffer final { 20struct StagingBufferRef {
21 vk::Buffer handle; 21 VkBuffer buffer;
22 VKMemoryCommit commit; 22 std::span<u8> mapped_span;
23}; 23};
24 24
25class VKStagingBufferPool final { 25class StagingBufferPool {
26public: 26public:
27 explicit VKStagingBufferPool(const VKDevice& device, VKMemoryManager& memory_manager, 27 explicit StagingBufferPool(const Device& device, MemoryAllocator& memory_allocator,
28 VKScheduler& scheduler); 28 VKScheduler& scheduler);
29 ~VKStagingBufferPool(); 29 ~StagingBufferPool();
30 30
31 VKBuffer& GetUnusedBuffer(std::size_t size, bool host_visible); 31 StagingBufferRef Request(size_t size, MemoryUsage usage);
32 32
33 void TickFrame(); 33 void TickFrame();
34 34
35private: 35private:
36 struct StagingBuffer final { 36 struct StagingBuffer {
37 explicit StagingBuffer(std::unique_ptr<VKBuffer> buffer); 37 vk::Buffer buffer;
38 38 MemoryCommit commit;
39 std::unique_ptr<VKBuffer> buffer; 39 std::span<u8> mapped_span;
40 u64 tick = 0; 40 u64 tick = 0;
41
42 StagingBufferRef Ref() const noexcept {
43 return {
44 .buffer = *buffer,
45 .mapped_span = mapped_span,
46 };
47 }
41 }; 48 };
42 49
43 struct StagingBuffers final { 50 struct StagingBuffers {
44 std::vector<StagingBuffer> entries; 51 std::vector<StagingBuffer> entries;
45 std::size_t delete_index = 0; 52 size_t delete_index = 0;
53 size_t iterate_index = 0;
46 }; 54 };
47 55
48 static constexpr std::size_t NumLevels = sizeof(std::size_t) * CHAR_BIT; 56 static constexpr size_t NUM_LEVELS = sizeof(size_t) * CHAR_BIT;
49 using StagingBuffersCache = std::array<StagingBuffers, NumLevels>; 57 using StagingBuffersCache = std::array<StagingBuffers, NUM_LEVELS>;
50 58
51 VKBuffer* TryGetReservedBuffer(std::size_t size, bool host_visible); 59 std::optional<StagingBufferRef> TryGetReservedBuffer(size_t size, MemoryUsage usage);
52 60
53 VKBuffer& CreateStagingBuffer(std::size_t size, bool host_visible); 61 StagingBufferRef CreateStagingBuffer(size_t size, MemoryUsage usage);
54 62
55 StagingBuffersCache& GetCache(bool host_visible); 63 StagingBuffersCache& GetCache(MemoryUsage usage);
56 64
57 void ReleaseCache(bool host_visible); 65 void ReleaseCache(MemoryUsage usage);
58 66
59 u64 ReleaseLevel(StagingBuffersCache& cache, std::size_t log2); 67 void ReleaseLevel(StagingBuffersCache& cache, size_t log2);
60 68
61 const VKDevice& device; 69 const Device& device;
62 VKMemoryManager& memory_manager; 70 MemoryAllocator& memory_allocator;
63 VKScheduler& scheduler; 71 VKScheduler& scheduler;
64 72
65 StagingBuffersCache host_staging_buffers; 73 StagingBuffersCache device_local_cache;
66 StagingBuffersCache device_staging_buffers; 74 StagingBuffersCache upload_cache;
75 StagingBuffersCache download_cache;
67 76
68 std::size_t current_delete_level = 0; 77 size_t current_delete_level = 0;
78 u64 buffer_index = 0;
69}; 79};
70 80
71} // namespace Vulkan 81} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_stream_buffer.cpp b/src/video_core/renderer_vulkan/vk_stream_buffer.cpp
index 419cb154d..a09fe084e 100644
--- a/src/video_core/renderer_vulkan/vk_stream_buffer.cpp
+++ b/src/video_core/renderer_vulkan/vk_stream_buffer.cpp
@@ -10,10 +10,10 @@
10 10
11#include "common/alignment.h" 11#include "common/alignment.h"
12#include "common/assert.h" 12#include "common/assert.h"
13#include "video_core/renderer_vulkan/vk_device.h"
14#include "video_core/renderer_vulkan/vk_scheduler.h" 13#include "video_core/renderer_vulkan/vk_scheduler.h"
15#include "video_core/renderer_vulkan/vk_stream_buffer.h" 14#include "video_core/renderer_vulkan/vk_stream_buffer.h"
16#include "video_core/renderer_vulkan/wrapper.h" 15#include "video_core/vulkan_common/vulkan_device.h"
16#include "video_core/vulkan_common/vulkan_wrapper.h"
17 17
18namespace Vulkan { 18namespace Vulkan {
19 19
@@ -60,7 +60,7 @@ u32 GetMemoryType(const VkPhysicalDeviceMemoryProperties& properties,
60 60
61} // Anonymous namespace 61} // Anonymous namespace
62 62
63VKStreamBuffer::VKStreamBuffer(const VKDevice& device_, VKScheduler& scheduler_) 63VKStreamBuffer::VKStreamBuffer(const Device& device_, VKScheduler& scheduler_)
64 : device{device_}, scheduler{scheduler_} { 64 : device{device_}, scheduler{scheduler_} {
65 CreateBuffers(); 65 CreateBuffers();
66 ReserveWatches(current_watches, WATCHES_INITIAL_RESERVE); 66 ReserveWatches(current_watches, WATCHES_INITIAL_RESERVE);
diff --git a/src/video_core/renderer_vulkan/vk_stream_buffer.h b/src/video_core/renderer_vulkan/vk_stream_buffer.h
index 1428f77bf..2e9c8cb46 100644
--- a/src/video_core/renderer_vulkan/vk_stream_buffer.h
+++ b/src/video_core/renderer_vulkan/vk_stream_buffer.h
@@ -9,17 +9,17 @@
9#include <vector> 9#include <vector>
10 10
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "video_core/renderer_vulkan/wrapper.h" 12#include "video_core/vulkan_common/vulkan_wrapper.h"
13 13
14namespace Vulkan { 14namespace Vulkan {
15 15
16class VKDevice; 16class Device;
17class VKFenceWatch; 17class VKFenceWatch;
18class VKScheduler; 18class VKScheduler;
19 19
20class VKStreamBuffer final { 20class VKStreamBuffer final {
21public: 21public:
22 explicit VKStreamBuffer(const VKDevice& device, VKScheduler& scheduler); 22 explicit VKStreamBuffer(const Device& device, VKScheduler& scheduler);
23 ~VKStreamBuffer(); 23 ~VKStreamBuffer();
24 24
25 /** 25 /**
@@ -54,7 +54,7 @@ private:
54 54
55 void WaitPendingOperations(u64 requested_upper_bound); 55 void WaitPendingOperations(u64 requested_upper_bound);
56 56
57 const VKDevice& device; ///< Vulkan device manager. 57 const Device& device; ///< Vulkan device manager.
58 VKScheduler& scheduler; ///< Command scheduler. 58 VKScheduler& scheduler; ///< Command scheduler.
59 59
60 vk::Buffer buffer; ///< Mapped buffer. 60 vk::Buffer buffer; ///< Mapped buffer.
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp
index 9636a7c65..725a2a05d 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.cpp
+++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp
@@ -11,10 +11,10 @@
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "core/core.h" 12#include "core/core.h"
13#include "core/frontend/framebuffer_layout.h" 13#include "core/frontend/framebuffer_layout.h"
14#include "video_core/renderer_vulkan/vk_device.h"
15#include "video_core/renderer_vulkan/vk_scheduler.h" 14#include "video_core/renderer_vulkan/vk_scheduler.h"
16#include "video_core/renderer_vulkan/vk_swapchain.h" 15#include "video_core/renderer_vulkan/vk_swapchain.h"
17#include "video_core/renderer_vulkan/wrapper.h" 16#include "video_core/vulkan_common/vulkan_device.h"
17#include "video_core/vulkan_common/vulkan_wrapper.h"
18 18
19namespace Vulkan { 19namespace Vulkan {
20 20
@@ -56,7 +56,7 @@ VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, u32 wi
56 56
57} // Anonymous namespace 57} // Anonymous namespace
58 58
59VKSwapchain::VKSwapchain(VkSurfaceKHR surface_, const VKDevice& device_, VKScheduler& scheduler_) 59VKSwapchain::VKSwapchain(VkSurfaceKHR surface_, const Device& device_, VKScheduler& scheduler_)
60 : surface{surface_}, device{device_}, scheduler{scheduler_} {} 60 : surface{surface_}, device{device_}, scheduler{scheduler_} {}
61 61
62VKSwapchain::~VKSwapchain() = default; 62VKSwapchain::~VKSwapchain() = default;
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.h b/src/video_core/renderer_vulkan/vk_swapchain.h
index 6b39befdf..2eadd62b3 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.h
+++ b/src/video_core/renderer_vulkan/vk_swapchain.h
@@ -7,7 +7,7 @@
7#include <vector> 7#include <vector>
8 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "video_core/renderer_vulkan/wrapper.h" 10#include "video_core/vulkan_common/vulkan_wrapper.h"
11 11
12namespace Layout { 12namespace Layout {
13struct FramebufferLayout; 13struct FramebufferLayout;
@@ -15,12 +15,12 @@ struct FramebufferLayout;
15 15
16namespace Vulkan { 16namespace Vulkan {
17 17
18class VKDevice; 18class Device;
19class VKScheduler; 19class VKScheduler;
20 20
21class VKSwapchain { 21class VKSwapchain {
22public: 22public:
23 explicit VKSwapchain(VkSurfaceKHR surface, const VKDevice& device, VKScheduler& scheduler); 23 explicit VKSwapchain(VkSurfaceKHR surface, const Device& device, VKScheduler& scheduler);
24 ~VKSwapchain(); 24 ~VKSwapchain();
25 25
26 /// Creates (or recreates) the swapchain with a given size. 26 /// Creates (or recreates) the swapchain with a given size.
@@ -73,7 +73,7 @@ private:
73 void Destroy(); 73 void Destroy();
74 74
75 const VkSurfaceKHR surface; 75 const VkSurfaceKHR surface;
76 const VKDevice& device; 76 const Device& device;
77 VKScheduler& scheduler; 77 VKScheduler& scheduler;
78 78
79 vk::SwapchainKHR swapchain; 79 vk::SwapchainKHR swapchain;
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 261808391..ab14922d7 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -10,11 +10,13 @@
10#include "video_core/engines/fermi_2d.h" 10#include "video_core/engines/fermi_2d.h"
11#include "video_core/renderer_vulkan/blit_image.h" 11#include "video_core/renderer_vulkan/blit_image.h"
12#include "video_core/renderer_vulkan/maxwell_to_vk.h" 12#include "video_core/renderer_vulkan/maxwell_to_vk.h"
13#include "video_core/renderer_vulkan/vk_device.h" 13#include "video_core/renderer_vulkan/vk_rasterizer.h"
14#include "video_core/renderer_vulkan/vk_scheduler.h" 14#include "video_core/renderer_vulkan/vk_scheduler.h"
15#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" 15#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
16#include "video_core/renderer_vulkan/vk_texture_cache.h" 16#include "video_core/renderer_vulkan/vk_texture_cache.h"
17#include "video_core/renderer_vulkan/wrapper.h" 17#include "video_core/vulkan_common/vulkan_device.h"
18#include "video_core/vulkan_common/vulkan_memory_allocator.h"
19#include "video_core/vulkan_common/vulkan_wrapper.h"
18 20
19namespace Vulkan { 21namespace Vulkan {
20 22
@@ -93,7 +95,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
93 } 95 }
94} 96}
95 97
96[[nodiscard]] VkImageCreateInfo MakeImageCreateInfo(const VKDevice& device, const ImageInfo& info) { 98[[nodiscard]] VkImageCreateInfo MakeImageCreateInfo(const Device& device, const ImageInfo& info) {
97 const auto format_info = MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, info.format); 99 const auto format_info = MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, info.format);
98 VkImageCreateFlags flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; 100 VkImageCreateFlags flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
99 if (info.type == ImageType::e2D && info.resources.layers >= 6 && 101 if (info.type == ImageType::e2D && info.resources.layers >= 6 &&
@@ -146,14 +148,14 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
146 }; 148 };
147} 149}
148 150
149[[nodiscard]] vk::Image MakeImage(const VKDevice& device, const ImageInfo& info) { 151[[nodiscard]] vk::Image MakeImage(const Device& device, const ImageInfo& info) {
150 if (info.type == ImageType::Buffer) { 152 if (info.type == ImageType::Buffer) {
151 return vk::Image{}; 153 return vk::Image{};
152 } 154 }
153 return device.GetLogical().CreateImage(MakeImageCreateInfo(device, info)); 155 return device.GetLogical().CreateImage(MakeImageCreateInfo(device, info));
154} 156}
155 157
156[[nodiscard]] vk::Buffer MakeBuffer(const VKDevice& device, const ImageInfo& info) { 158[[nodiscard]] vk::Buffer MakeBuffer(const Device& device, const ImageInfo& info) {
157 if (info.type != ImageType::Buffer) { 159 if (info.type != ImageType::Buffer) {
158 return vk::Buffer{}; 160 return vk::Buffer{};
159 } 161 }
@@ -205,7 +207,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
205 } 207 }
206} 208}
207 209
208[[nodiscard]] VkAttachmentDescription AttachmentDescription(const VKDevice& device, 210[[nodiscard]] VkAttachmentDescription AttachmentDescription(const Device& device,
209 const ImageView* image_view) { 211 const ImageView* image_view) {
210 const auto pixel_format = image_view->format; 212 const auto pixel_format = image_view->format;
211 return VkAttachmentDescription{ 213 return VkAttachmentDescription{
@@ -552,10 +554,18 @@ void TextureCacheRuntime::Finish() {
552} 554}
553 555
554ImageBufferMap TextureCacheRuntime::MapUploadBuffer(size_t size) { 556ImageBufferMap TextureCacheRuntime::MapUploadBuffer(size_t size) {
555 const auto& buffer = staging_buffer_pool.GetUnusedBuffer(size, true); 557 const auto staging_ref = staging_buffer_pool.Request(size, MemoryUsage::Upload);
556 return ImageBufferMap{ 558 return {
557 .handle = *buffer.handle, 559 .handle = staging_ref.buffer,
558 .map = buffer.commit->Map(size), 560 .span = staging_ref.mapped_span,
561 };
562}
563
564ImageBufferMap TextureCacheRuntime::MapDownloadBuffer(size_t size) {
565 const auto staging_ref = staging_buffer_pool.Request(size, MemoryUsage::Download);
566 return {
567 .handle = staging_ref.buffer,
568 .span = staging_ref.mapped_span,
559 }; 569 };
560} 570}
561 571
@@ -786,9 +796,9 @@ Image::Image(TextureCacheRuntime& runtime, const ImageInfo& info_, GPUVAddr gpu_
786 image(MakeImage(runtime.device, info)), buffer(MakeBuffer(runtime.device, info)), 796 image(MakeImage(runtime.device, info)), buffer(MakeBuffer(runtime.device, info)),
787 aspect_mask(ImageAspectMask(info.format)) { 797 aspect_mask(ImageAspectMask(info.format)) {
788 if (image) { 798 if (image) {
789 commit = runtime.memory_manager.Commit(image, false); 799 commit = runtime.memory_allocator.Commit(image, MemoryUsage::DeviceLocal);
790 } else { 800 } else {
791 commit = runtime.memory_manager.Commit(buffer, false); 801 commit = runtime.memory_allocator.Commit(buffer, MemoryUsage::DeviceLocal);
792 } 802 }
793 if (IsPixelFormatASTC(info.format) && !runtime.device.IsOptimalAstcSupported()) { 803 if (IsPixelFormatASTC(info.format) && !runtime.device.IsOptimalAstcSupported()) {
794 flags |= VideoCommon::ImageFlagBits::Converted; 804 flags |= VideoCommon::ImageFlagBits::Converted;
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index edc3d80c0..a55d405d1 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -7,9 +7,9 @@
7#include <compare> 7#include <compare>
8#include <span> 8#include <span>
9 9
10#include "video_core/renderer_vulkan/vk_memory_manager.h"
11#include "video_core/renderer_vulkan/wrapper.h"
12#include "video_core/texture_cache/texture_cache.h" 10#include "video_core/texture_cache/texture_cache.h"
11#include "video_core/vulkan_common/vulkan_memory_allocator.h"
12#include "video_core/vulkan_common/vulkan_wrapper.h"
13 13
14namespace Vulkan { 14namespace Vulkan {
15 15
@@ -19,14 +19,13 @@ using VideoCommon::Offset2D;
19using VideoCommon::RenderTargets; 19using VideoCommon::RenderTargets;
20using VideoCore::Surface::PixelFormat; 20using VideoCore::Surface::PixelFormat;
21 21
22class VKDevice;
23class VKScheduler;
24class VKStagingBufferPool;
25
26class BlitImageHelper; 22class BlitImageHelper;
23class Device;
27class Image; 24class Image;
28class ImageView; 25class ImageView;
29class Framebuffer; 26class Framebuffer;
27class StagingBufferPool;
28class VKScheduler;
30 29
31struct RenderPassKey { 30struct RenderPassKey {
32 constexpr auto operator<=>(const RenderPassKey&) const noexcept = default; 31 constexpr auto operator<=>(const RenderPassKey&) const noexcept = default;
@@ -60,18 +59,18 @@ struct ImageBufferMap {
60 } 59 }
61 60
62 [[nodiscard]] std::span<u8> Span() const noexcept { 61 [[nodiscard]] std::span<u8> Span() const noexcept {
63 return map.Span(); 62 return span;
64 } 63 }
65 64
66 VkBuffer handle; 65 VkBuffer handle;
67 MemoryMap map; 66 std::span<u8> span;
68}; 67};
69 68
70struct TextureCacheRuntime { 69struct TextureCacheRuntime {
71 const VKDevice& device; 70 const Device& device;
72 VKScheduler& scheduler; 71 VKScheduler& scheduler;
73 VKMemoryManager& memory_manager; 72 MemoryAllocator& memory_allocator;
74 VKStagingBufferPool& staging_buffer_pool; 73 StagingBufferPool& staging_buffer_pool;
75 BlitImageHelper& blit_image_helper; 74 BlitImageHelper& blit_image_helper;
76 std::unordered_map<RenderPassKey, vk::RenderPass> renderpass_cache; 75 std::unordered_map<RenderPassKey, vk::RenderPass> renderpass_cache;
77 76
@@ -79,10 +78,7 @@ struct TextureCacheRuntime {
79 78
80 [[nodiscard]] ImageBufferMap MapUploadBuffer(size_t size); 79 [[nodiscard]] ImageBufferMap MapUploadBuffer(size_t size);
81 80
82 [[nodiscard]] ImageBufferMap MapDownloadBuffer(size_t size) { 81 [[nodiscard]] ImageBufferMap MapDownloadBuffer(size_t size);
83 // TODO: Have a special function for this
84 return MapUploadBuffer(size);
85 }
86 82
87 void BlitImage(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& src, 83 void BlitImage(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& src,
88 const std::array<Offset2D, 2>& dst_region, 84 const std::array<Offset2D, 2>& dst_region,
@@ -104,6 +100,11 @@ struct TextureCacheRuntime {
104 } 100 }
105 101
106 void InsertUploadMemoryBarrier() {} 102 void InsertUploadMemoryBarrier() {}
103
104 bool HasBrokenTextureViewFormats() const noexcept {
105 // No known Vulkan driver has broken image views
106 return false;
107 }
107}; 108};
108 109
109class Image : public VideoCommon::ImageBase { 110class Image : public VideoCommon::ImageBase {
@@ -136,7 +137,7 @@ private:
136 VKScheduler* scheduler; 137 VKScheduler* scheduler;
137 vk::Image image; 138 vk::Image image;
138 vk::Buffer buffer; 139 vk::Buffer buffer;
139 VKMemoryCommit commit; 140 MemoryCommit commit;
140 VkImageAspectFlags aspect_mask = 0; 141 VkImageAspectFlags aspect_mask = 0;
141 bool initialized = false; 142 bool initialized = false;
142}; 143};
@@ -177,7 +178,7 @@ public:
177private: 178private:
178 [[nodiscard]] vk::ImageView MakeDepthStencilView(VkImageAspectFlags aspect_mask); 179 [[nodiscard]] vk::ImageView MakeDepthStencilView(VkImageAspectFlags aspect_mask);
179 180
180 const VKDevice* device = nullptr; 181 const Device* device = nullptr;
181 std::array<vk::ImageView, VideoCommon::NUM_IMAGE_VIEW_TYPES> image_views; 182 std::array<vk::ImageView, VideoCommon::NUM_IMAGE_VIEW_TYPES> image_views;
182 vk::ImageView depth_view; 183 vk::ImageView depth_view;
183 vk::ImageView stencil_view; 184 vk::ImageView stencil_view;
diff --git a/src/video_core/renderer_vulkan/vk_update_descriptor.cpp b/src/video_core/renderer_vulkan/vk_update_descriptor.cpp
index 8826da325..f99273c6a 100644
--- a/src/video_core/renderer_vulkan/vk_update_descriptor.cpp
+++ b/src/video_core/renderer_vulkan/vk_update_descriptor.cpp
@@ -7,14 +7,14 @@
7 7
8#include "common/assert.h" 8#include "common/assert.h"
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "video_core/renderer_vulkan/vk_device.h"
11#include "video_core/renderer_vulkan/vk_scheduler.h" 10#include "video_core/renderer_vulkan/vk_scheduler.h"
12#include "video_core/renderer_vulkan/vk_update_descriptor.h" 11#include "video_core/renderer_vulkan/vk_update_descriptor.h"
13#include "video_core/renderer_vulkan/wrapper.h" 12#include "video_core/vulkan_common/vulkan_device.h"
13#include "video_core/vulkan_common/vulkan_wrapper.h"
14 14
15namespace Vulkan { 15namespace Vulkan {
16 16
17VKUpdateDescriptorQueue::VKUpdateDescriptorQueue(const VKDevice& device_, VKScheduler& scheduler_) 17VKUpdateDescriptorQueue::VKUpdateDescriptorQueue(const Device& device_, VKScheduler& scheduler_)
18 : device{device_}, scheduler{scheduler_} {} 18 : device{device_}, scheduler{scheduler_} {}
19 19
20VKUpdateDescriptorQueue::~VKUpdateDescriptorQueue() = default; 20VKUpdateDescriptorQueue::~VKUpdateDescriptorQueue() = default;
diff --git a/src/video_core/renderer_vulkan/vk_update_descriptor.h b/src/video_core/renderer_vulkan/vk_update_descriptor.h
index f098a8540..e214f7195 100644
--- a/src/video_core/renderer_vulkan/vk_update_descriptor.h
+++ b/src/video_core/renderer_vulkan/vk_update_descriptor.h
@@ -8,11 +8,11 @@
8#include <boost/container/static_vector.hpp> 8#include <boost/container/static_vector.hpp>
9 9
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "video_core/renderer_vulkan/wrapper.h" 11#include "video_core/vulkan_common/vulkan_wrapper.h"
12 12
13namespace Vulkan { 13namespace Vulkan {
14 14
15class VKDevice; 15class Device;
16class VKScheduler; 16class VKScheduler;
17 17
18struct DescriptorUpdateEntry { 18struct DescriptorUpdateEntry {
@@ -31,7 +31,7 @@ struct DescriptorUpdateEntry {
31 31
32class VKUpdateDescriptorQueue final { 32class VKUpdateDescriptorQueue final {
33public: 33public:
34 explicit VKUpdateDescriptorQueue(const VKDevice& device_, VKScheduler& scheduler_); 34 explicit VKUpdateDescriptorQueue(const Device& device_, VKScheduler& scheduler_);
35 ~VKUpdateDescriptorQueue(); 35 ~VKUpdateDescriptorQueue();
36 36
37 void TickFrame(); 37 void TickFrame();
@@ -69,7 +69,7 @@ public:
69 } 69 }
70 70
71private: 71private:
72 const VKDevice& device; 72 const Device& device;
73 VKScheduler& scheduler; 73 VKScheduler& scheduler;
74 74
75 const DescriptorUpdateEntry* upload_start = nullptr; 75 const DescriptorUpdateEntry* upload_start = nullptr;
diff --git a/src/video_core/shader/async_shaders.cpp b/src/video_core/shader/async_shaders.cpp
index 09f93463b..9707136e9 100644
--- a/src/video_core/shader/async_shaders.cpp
+++ b/src/video_core/shader/async_shaders.cpp
@@ -134,7 +134,7 @@ void AsyncShaders::QueueOpenGLShader(const OpenGL::Device& device,
134} 134}
135 135
136void AsyncShaders::QueueVulkanShader(Vulkan::VKPipelineCache* pp_cache, 136void AsyncShaders::QueueVulkanShader(Vulkan::VKPipelineCache* pp_cache,
137 const Vulkan::VKDevice& device, Vulkan::VKScheduler& scheduler, 137 const Vulkan::Device& device, Vulkan::VKScheduler& scheduler,
138 Vulkan::VKDescriptorPool& descriptor_pool, 138 Vulkan::VKDescriptorPool& descriptor_pool,
139 Vulkan::VKUpdateDescriptorQueue& update_descriptor_queue, 139 Vulkan::VKUpdateDescriptorQueue& update_descriptor_queue,
140 std::vector<VkDescriptorSetLayoutBinding> bindings, 140 std::vector<VkDescriptorSetLayoutBinding> bindings,
diff --git a/src/video_core/shader/async_shaders.h b/src/video_core/shader/async_shaders.h
index 004e214a8..0dbb1a31f 100644
--- a/src/video_core/shader/async_shaders.h
+++ b/src/video_core/shader/async_shaders.h
@@ -24,9 +24,9 @@
24#include "video_core/renderer_opengl/gl_device.h" 24#include "video_core/renderer_opengl/gl_device.h"
25#include "video_core/renderer_opengl/gl_resource_manager.h" 25#include "video_core/renderer_opengl/gl_resource_manager.h"
26#include "video_core/renderer_opengl/gl_shader_decompiler.h" 26#include "video_core/renderer_opengl/gl_shader_decompiler.h"
27#include "video_core/renderer_vulkan/vk_device.h"
28#include "video_core/renderer_vulkan/vk_pipeline_cache.h" 27#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
29#include "video_core/renderer_vulkan/vk_scheduler.h" 28#include "video_core/renderer_vulkan/vk_scheduler.h"
29#include "video_core/vulkan_common/vulkan_device.h"
30 30
31namespace Core::Frontend { 31namespace Core::Frontend {
32class EmuWindow; 32class EmuWindow;
@@ -94,7 +94,7 @@ public:
94 CompilerSettings compiler_settings, const Registry& registry, 94 CompilerSettings compiler_settings, const Registry& registry,
95 VAddr cpu_addr); 95 VAddr cpu_addr);
96 96
97 void QueueVulkanShader(Vulkan::VKPipelineCache* pp_cache, const Vulkan::VKDevice& device, 97 void QueueVulkanShader(Vulkan::VKPipelineCache* pp_cache, const Vulkan::Device& device,
98 Vulkan::VKScheduler& scheduler, 98 Vulkan::VKScheduler& scheduler,
99 Vulkan::VKDescriptorPool& descriptor_pool, 99 Vulkan::VKDescriptorPool& descriptor_pool,
100 Vulkan::VKUpdateDescriptorQueue& update_descriptor_queue, 100 Vulkan::VKUpdateDescriptorQueue& update_descriptor_queue,
@@ -123,7 +123,7 @@ private:
123 123
124 // For Vulkan 124 // For Vulkan
125 Vulkan::VKPipelineCache* pp_cache; 125 Vulkan::VKPipelineCache* pp_cache;
126 const Vulkan::VKDevice* vk_device; 126 const Vulkan::Device* vk_device;
127 Vulkan::VKScheduler* scheduler; 127 Vulkan::VKScheduler* scheduler;
128 Vulkan::VKDescriptorPool* descriptor_pool; 128 Vulkan::VKDescriptorPool* descriptor_pool;
129 Vulkan::VKUpdateDescriptorQueue* update_descriptor_queue; 129 Vulkan::VKUpdateDescriptorQueue* update_descriptor_queue;
diff --git a/src/video_core/texture_cache/accelerated_swizzle.cpp b/src/video_core/texture_cache/accelerated_swizzle.cpp
index a4fc1184b..15585caeb 100644
--- a/src/video_core/texture_cache/accelerated_swizzle.cpp
+++ b/src/video_core/texture_cache/accelerated_swizzle.cpp
@@ -27,7 +27,7 @@ BlockLinearSwizzle2DParams MakeBlockLinearSwizzle2DParams(const SwizzleParameter
27 const Extent3D num_tiles = swizzle.num_tiles; 27 const Extent3D num_tiles = swizzle.num_tiles;
28 const u32 bytes_per_block = BytesPerBlock(info.format); 28 const u32 bytes_per_block = BytesPerBlock(info.format);
29 const u32 stride_alignment = CalculateLevelStrideAlignment(info, swizzle.level); 29 const u32 stride_alignment = CalculateLevelStrideAlignment(info, swizzle.level);
30 const u32 stride = Common::AlignBits(num_tiles.width, stride_alignment) * bytes_per_block; 30 const u32 stride = Common::AlignUpLog2(num_tiles.width, stride_alignment) * bytes_per_block;
31 const u32 gobs_in_x = Common::DivCeilLog2(stride, GOB_SIZE_X_SHIFT); 31 const u32 gobs_in_x = Common::DivCeilLog2(stride, GOB_SIZE_X_SHIFT);
32 return BlockLinearSwizzle2DParams{ 32 return BlockLinearSwizzle2DParams{
33 .origin{0, 0, 0}, 33 .origin{0, 0, 0},
@@ -47,7 +47,7 @@ BlockLinearSwizzle3DParams MakeBlockLinearSwizzle3DParams(const SwizzleParameter
47 const Extent3D num_tiles = swizzle.num_tiles; 47 const Extent3D num_tiles = swizzle.num_tiles;
48 const u32 bytes_per_block = BytesPerBlock(info.format); 48 const u32 bytes_per_block = BytesPerBlock(info.format);
49 const u32 stride_alignment = CalculateLevelStrideAlignment(info, swizzle.level); 49 const u32 stride_alignment = CalculateLevelStrideAlignment(info, swizzle.level);
50 const u32 stride = Common::AlignBits(num_tiles.width, stride_alignment) * bytes_per_block; 50 const u32 stride = Common::AlignUpLog2(num_tiles.width, stride_alignment) * bytes_per_block;
51 51
52 const u32 gobs_in_x = (stride + GOB_SIZE_X - 1) >> GOB_SIZE_X_SHIFT; 52 const u32 gobs_in_x = (stride + GOB_SIZE_X - 1) >> GOB_SIZE_X_SHIFT;
53 const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block.height + block.depth); 53 const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block.height + block.depth);
diff --git a/src/video_core/texture_cache/image_base.cpp b/src/video_core/texture_cache/image_base.cpp
index 448a05fcc..959b3f115 100644
--- a/src/video_core/texture_cache/image_base.cpp
+++ b/src/video_core/texture_cache/image_base.cpp
@@ -120,7 +120,9 @@ void AddImageAlias(ImageBase& lhs, ImageBase& rhs, ImageId lhs_id, ImageId rhs_i
120 if (lhs.info.type == ImageType::Linear) { 120 if (lhs.info.type == ImageType::Linear) {
121 base = SubresourceBase{.level = 0, .layer = 0}; 121 base = SubresourceBase{.level = 0, .layer = 0};
122 } else { 122 } else {
123 base = FindSubresource(rhs.info, lhs, rhs.gpu_addr, OPTIONS); 123 // We are passing relaxed formats as an option, having broken views or not won't matter
124 static constexpr bool broken_views = false;
125 base = FindSubresource(rhs.info, lhs, rhs.gpu_addr, OPTIONS, broken_views);
124 } 126 }
125 if (!base) { 127 if (!base) {
126 LOG_ERROR(HW_GPU, "Image alias should have been flipped"); 128 LOG_ERROR(HW_GPU, "Image alias should have been flipped");
diff --git a/src/video_core/texture_cache/image_view_base.cpp b/src/video_core/texture_cache/image_view_base.cpp
index 076a4bcfd..18f72e508 100644
--- a/src/video_core/texture_cache/image_view_base.cpp
+++ b/src/video_core/texture_cache/image_view_base.cpp
@@ -24,7 +24,7 @@ ImageViewBase::ImageViewBase(const ImageViewInfo& info, const ImageInfo& image_i
24 .height = std::max(image_info.size.height >> range.base.level, 1u), 24 .height = std::max(image_info.size.height >> range.base.level, 1u),
25 .depth = std::max(image_info.size.depth >> range.base.level, 1u), 25 .depth = std::max(image_info.size.depth >> range.base.level, 1u),
26 } { 26 } {
27 ASSERT_MSG(VideoCore::Surface::IsViewCompatible(image_info.format, info.format), 27 ASSERT_MSG(VideoCore::Surface::IsViewCompatible(image_info.format, info.format, false),
28 "Image view format {} is incompatible with image format {}", info.format, 28 "Image view format {} is incompatible with image format {}", info.format,
29 image_info.format); 29 image_info.format);
30 const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue(); 30 const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue();
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 968059842..d1080300f 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -61,7 +61,7 @@ using VideoCore::Surface::SurfaceType;
61template <class P> 61template <class P>
62class TextureCache { 62class TextureCache {
63 /// Address shift for caching images into a hash table 63 /// Address shift for caching images into a hash table
64 static constexpr u64 PAGE_SHIFT = 20; 64 static constexpr u64 PAGE_BITS = 20;
65 65
66 /// Enables debugging features to the texture cache 66 /// Enables debugging features to the texture cache
67 static constexpr bool ENABLE_VALIDATION = P::ENABLE_VALIDATION; 67 static constexpr bool ENABLE_VALIDATION = P::ENABLE_VALIDATION;
@@ -184,8 +184,8 @@ private:
184 template <typename Func> 184 template <typename Func>
185 static void ForEachPage(VAddr addr, size_t size, Func&& func) { 185 static void ForEachPage(VAddr addr, size_t size, Func&& func) {
186 static constexpr bool RETURNS_BOOL = std::is_same_v<std::invoke_result<Func, u64>, bool>; 186 static constexpr bool RETURNS_BOOL = std::is_same_v<std::invoke_result<Func, u64>, bool>;
187 const u64 page_end = (addr + size - 1) >> PAGE_SHIFT; 187 const u64 page_end = (addr + size - 1) >> PAGE_BITS;
188 for (u64 page = addr >> PAGE_SHIFT; page <= page_end; ++page) { 188 for (u64 page = addr >> PAGE_BITS; page <= page_end; ++page) {
189 if constexpr (RETURNS_BOOL) { 189 if constexpr (RETURNS_BOOL) {
190 if (func(page)) { 190 if (func(page)) {
191 break; 191 break;
@@ -708,7 +708,7 @@ void TextureCache<P>::InvalidateDepthBuffer() {
708template <class P> 708template <class P>
709typename P::ImageView* TextureCache<P>::TryFindFramebufferImageView(VAddr cpu_addr) { 709typename P::ImageView* TextureCache<P>::TryFindFramebufferImageView(VAddr cpu_addr) {
710 // TODO: Properly implement this 710 // TODO: Properly implement this
711 const auto it = page_table.find(cpu_addr >> PAGE_SHIFT); 711 const auto it = page_table.find(cpu_addr >> PAGE_BITS);
712 if (it == page_table.end()) { 712 if (it == page_table.end()) {
713 return nullptr; 713 return nullptr;
714 } 714 }
@@ -883,6 +883,7 @@ ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr,
883 if (!cpu_addr) { 883 if (!cpu_addr) {
884 return ImageId{}; 884 return ImageId{};
885 } 885 }
886 const bool broken_views = runtime.HasBrokenTextureViewFormats();
886 ImageId image_id; 887 ImageId image_id;
887 const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) { 888 const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) {
888 if (info.type == ImageType::Linear || existing_image.info.type == ImageType::Linear) { 889 if (info.type == ImageType::Linear || existing_image.info.type == ImageType::Linear) {
@@ -892,11 +893,11 @@ ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr,
892 if (existing_image.gpu_addr == gpu_addr && existing.type == info.type && 893 if (existing_image.gpu_addr == gpu_addr && existing.type == info.type &&
893 existing.pitch == info.pitch && 894 existing.pitch == info.pitch &&
894 IsPitchLinearSameSize(existing, info, strict_size) && 895 IsPitchLinearSameSize(existing, info, strict_size) &&
895 IsViewCompatible(existing.format, info.format)) { 896 IsViewCompatible(existing.format, info.format, broken_views)) {
896 image_id = existing_image_id; 897 image_id = existing_image_id;
897 return true; 898 return true;
898 } 899 }
899 } else if (IsSubresource(info, existing_image, gpu_addr, options)) { 900 } else if (IsSubresource(info, existing_image, gpu_addr, options, broken_views)) {
900 image_id = existing_image_id; 901 image_id = existing_image_id;
901 return true; 902 return true;
902 } 903 }
@@ -926,6 +927,7 @@ template <class P>
926ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VAddr cpu_addr) { 927ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VAddr cpu_addr) {
927 ImageInfo new_info = info; 928 ImageInfo new_info = info;
928 const size_t size_bytes = CalculateGuestSizeInBytes(new_info); 929 const size_t size_bytes = CalculateGuestSizeInBytes(new_info);
930 const bool broken_views = runtime.HasBrokenTextureViewFormats();
929 std::vector<ImageId> overlap_ids; 931 std::vector<ImageId> overlap_ids;
930 std::vector<ImageId> left_aliased_ids; 932 std::vector<ImageId> left_aliased_ids;
931 std::vector<ImageId> right_aliased_ids; 933 std::vector<ImageId> right_aliased_ids;
@@ -940,7 +942,9 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
940 } 942 }
941 return; 943 return;
942 } 944 }
943 const auto solution = ResolveOverlap(new_info, gpu_addr, cpu_addr, overlap, true); 945 static constexpr bool strict_size = true;
946 const std::optional<OverlapResult> solution =
947 ResolveOverlap(new_info, gpu_addr, cpu_addr, overlap, strict_size, broken_views);
944 if (solution) { 948 if (solution) {
945 gpu_addr = solution->gpu_addr; 949 gpu_addr = solution->gpu_addr;
946 cpu_addr = solution->cpu_addr; 950 cpu_addr = solution->cpu_addr;
@@ -950,9 +954,10 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
950 } 954 }
951 static constexpr auto options = RelaxedOptions::Size | RelaxedOptions::Format; 955 static constexpr auto options = RelaxedOptions::Size | RelaxedOptions::Format;
952 const ImageBase new_image_base(new_info, gpu_addr, cpu_addr); 956 const ImageBase new_image_base(new_info, gpu_addr, cpu_addr);
953 if (IsSubresource(new_info, overlap, gpu_addr, options)) { 957 if (IsSubresource(new_info, overlap, gpu_addr, options, broken_views)) {
954 left_aliased_ids.push_back(overlap_id); 958 left_aliased_ids.push_back(overlap_id);
955 } else if (IsSubresource(overlap.info, new_image_base, overlap.gpu_addr, options)) { 959 } else if (IsSubresource(overlap.info, new_image_base, overlap.gpu_addr, options,
960 broken_views)) {
956 right_aliased_ids.push_back(overlap_id); 961 right_aliased_ids.push_back(overlap_id);
957 } 962 }
958 }); 963 });
@@ -1165,13 +1170,13 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) {
1165 ForEachPage(image.cpu_addr, image.guest_size_bytes, [this, image_id](u64 page) { 1170 ForEachPage(image.cpu_addr, image.guest_size_bytes, [this, image_id](u64 page) {
1166 const auto page_it = page_table.find(page); 1171 const auto page_it = page_table.find(page);
1167 if (page_it == page_table.end()) { 1172 if (page_it == page_table.end()) {
1168 UNREACHABLE_MSG("Unregistering unregistered page=0x{:x}", page << PAGE_SHIFT); 1173 UNREACHABLE_MSG("Unregistering unregistered page=0x{:x}", page << PAGE_BITS);
1169 return; 1174 return;
1170 } 1175 }
1171 std::vector<ImageId>& image_ids = page_it->second; 1176 std::vector<ImageId>& image_ids = page_it->second;
1172 const auto vector_it = std::ranges::find(image_ids, image_id); 1177 const auto vector_it = std::ranges::find(image_ids, image_id);
1173 if (vector_it == image_ids.end()) { 1178 if (vector_it == image_ids.end()) {
1174 UNREACHABLE_MSG("Unregistering unregistered image in page=0x{:x}", page << PAGE_SHIFT); 1179 UNREACHABLE_MSG("Unregistering unregistered image in page=0x{:x}", page << PAGE_BITS);
1175 return; 1180 return;
1176 } 1181 }
1177 image_ids.erase(vector_it); 1182 image_ids.erase(vector_it);
diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp
index 9ed1fc007..ce8fcfe0a 100644
--- a/src/video_core/texture_cache/util.cpp
+++ b/src/video_core/texture_cache/util.cpp
@@ -279,7 +279,7 @@ template <u32 GOB_EXTENT>
279 const bool is_small = IsSmallerThanGobSize(blocks, gob, info.block.depth); 279 const bool is_small = IsSmallerThanGobSize(blocks, gob, info.block.depth);
280 const u32 alignment = is_small ? 0 : info.tile_width_spacing; 280 const u32 alignment = is_small ? 0 : info.tile_width_spacing;
281 return Extent2D{ 281 return Extent2D{
282 .width = Common::AlignBits(gobs.width, alignment), 282 .width = Common::AlignUpLog2(gobs.width, alignment),
283 .height = gobs.height, 283 .height = gobs.height,
284 }; 284 };
285} 285}
@@ -352,7 +352,7 @@ template <u32 GOB_EXTENT>
352 // https://github.com/Ryujinx/Ryujinx/blob/1c9aba6de1520aea5480c032e0ff5664ac1bb36f/Ryujinx.Graphics.Texture/SizeCalculator.cs#L134 352 // https://github.com/Ryujinx/Ryujinx/blob/1c9aba6de1520aea5480c032e0ff5664ac1bb36f/Ryujinx.Graphics.Texture/SizeCalculator.cs#L134
353 if (tile_width_spacing > 0) { 353 if (tile_width_spacing > 0) {
354 const u32 alignment_log2 = GOB_SIZE_SHIFT + tile_width_spacing + block.height + block.depth; 354 const u32 alignment_log2 = GOB_SIZE_SHIFT + tile_width_spacing + block.height + block.depth;
355 return Common::AlignBits(size_bytes, alignment_log2); 355 return Common::AlignUpLog2(size_bytes, alignment_log2);
356 } 356 }
357 const u32 aligned_height = Common::AlignUp(size.height, tile_size_y); 357 const u32 aligned_height = Common::AlignUp(size.height, tile_size_y);
358 while (block.height != 0 && aligned_height <= (1U << (block.height - 1)) * GOB_SIZE_Y) { 358 while (block.height != 0 && aligned_height <= (1U << (block.height - 1)) * GOB_SIZE_Y) {
@@ -528,9 +528,9 @@ template <u32 GOB_EXTENT>
528 const u32 alignment = StrideAlignment(num_tiles, info.block, bpp_log2, info.tile_width_spacing); 528 const u32 alignment = StrideAlignment(num_tiles, info.block, bpp_log2, info.tile_width_spacing);
529 const Extent3D mip_block = AdjustMipBlockSize(num_tiles, info.block, 0); 529 const Extent3D mip_block = AdjustMipBlockSize(num_tiles, info.block, 0);
530 return Extent3D{ 530 return Extent3D{
531 .width = Common::AlignBits(num_tiles.width, alignment), 531 .width = Common::AlignUpLog2(num_tiles.width, alignment),
532 .height = Common::AlignBits(num_tiles.height, GOB_SIZE_Y_SHIFT + mip_block.height), 532 .height = Common::AlignUpLog2(num_tiles.height, GOB_SIZE_Y_SHIFT + mip_block.height),
533 .depth = Common::AlignBits(num_tiles.depth, GOB_SIZE_Z_SHIFT + mip_block.depth), 533 .depth = Common::AlignUpLog2(num_tiles.depth, GOB_SIZE_Z_SHIFT + mip_block.depth),
534 }; 534 };
535} 535}
536 536
@@ -1069,13 +1069,13 @@ bool IsPitchLinearSameSize(const ImageInfo& lhs, const ImageInfo& rhs, bool stri
1069 1069
1070std::optional<OverlapResult> ResolveOverlap(const ImageInfo& new_info, GPUVAddr gpu_addr, 1070std::optional<OverlapResult> ResolveOverlap(const ImageInfo& new_info, GPUVAddr gpu_addr,
1071 VAddr cpu_addr, const ImageBase& overlap, 1071 VAddr cpu_addr, const ImageBase& overlap,
1072 bool strict_size) { 1072 bool strict_size, bool broken_views) {
1073 ASSERT(new_info.type != ImageType::Linear); 1073 ASSERT(new_info.type != ImageType::Linear);
1074 ASSERT(overlap.info.type != ImageType::Linear); 1074 ASSERT(overlap.info.type != ImageType::Linear);
1075 if (!IsLayerStrideCompatible(new_info, overlap.info)) { 1075 if (!IsLayerStrideCompatible(new_info, overlap.info)) {
1076 return std::nullopt; 1076 return std::nullopt;
1077 } 1077 }
1078 if (!IsViewCompatible(overlap.info.format, new_info.format)) { 1078 if (!IsViewCompatible(overlap.info.format, new_info.format, broken_views)) {
1079 return std::nullopt; 1079 return std::nullopt;
1080 } 1080 }
1081 if (gpu_addr == overlap.gpu_addr) { 1081 if (gpu_addr == overlap.gpu_addr) {
@@ -1118,14 +1118,15 @@ bool IsLayerStrideCompatible(const ImageInfo& lhs, const ImageInfo& rhs) {
1118} 1118}
1119 1119
1120std::optional<SubresourceBase> FindSubresource(const ImageInfo& candidate, const ImageBase& image, 1120std::optional<SubresourceBase> FindSubresource(const ImageInfo& candidate, const ImageBase& image,
1121 GPUVAddr candidate_addr, RelaxedOptions options) { 1121 GPUVAddr candidate_addr, RelaxedOptions options,
1122 bool broken_views) {
1122 const std::optional<SubresourceBase> base = image.TryFindBase(candidate_addr); 1123 const std::optional<SubresourceBase> base = image.TryFindBase(candidate_addr);
1123 if (!base) { 1124 if (!base) {
1124 return std::nullopt; 1125 return std::nullopt;
1125 } 1126 }
1126 const ImageInfo& existing = image.info; 1127 const ImageInfo& existing = image.info;
1127 if (False(options & RelaxedOptions::Format)) { 1128 if (False(options & RelaxedOptions::Format)) {
1128 if (!IsViewCompatible(existing.format, candidate.format)) { 1129 if (!IsViewCompatible(existing.format, candidate.format, broken_views)) {
1129 return std::nullopt; 1130 return std::nullopt;
1130 } 1131 }
1131 } 1132 }
@@ -1162,8 +1163,8 @@ std::optional<SubresourceBase> FindSubresource(const ImageInfo& candidate, const
1162} 1163}
1163 1164
1164bool IsSubresource(const ImageInfo& candidate, const ImageBase& image, GPUVAddr candidate_addr, 1165bool IsSubresource(const ImageInfo& candidate, const ImageBase& image, GPUVAddr candidate_addr,
1165 RelaxedOptions options) { 1166 RelaxedOptions options, bool broken_views) {
1166 return FindSubresource(candidate, image, candidate_addr, options).has_value(); 1167 return FindSubresource(candidate, image, candidate_addr, options, broken_views).has_value();
1167} 1168}
1168 1169
1169void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* dst, 1170void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* dst,
diff --git a/src/video_core/texture_cache/util.h b/src/video_core/texture_cache/util.h
index dbbbd33cd..52a9207d6 100644
--- a/src/video_core/texture_cache/util.h
+++ b/src/video_core/texture_cache/util.h
@@ -87,17 +87,19 @@ void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const Ima
87[[nodiscard]] std::optional<OverlapResult> ResolveOverlap(const ImageInfo& new_info, 87[[nodiscard]] std::optional<OverlapResult> ResolveOverlap(const ImageInfo& new_info,
88 GPUVAddr gpu_addr, VAddr cpu_addr, 88 GPUVAddr gpu_addr, VAddr cpu_addr,
89 const ImageBase& overlap, 89 const ImageBase& overlap,
90 bool strict_size); 90 bool strict_size, bool broken_views);
91 91
92[[nodiscard]] bool IsLayerStrideCompatible(const ImageInfo& lhs, const ImageInfo& rhs); 92[[nodiscard]] bool IsLayerStrideCompatible(const ImageInfo& lhs, const ImageInfo& rhs);
93 93
94[[nodiscard]] std::optional<SubresourceBase> FindSubresource(const ImageInfo& candidate, 94[[nodiscard]] std::optional<SubresourceBase> FindSubresource(const ImageInfo& candidate,
95 const ImageBase& image, 95 const ImageBase& image,
96 GPUVAddr candidate_addr, 96 GPUVAddr candidate_addr,
97 RelaxedOptions options); 97 RelaxedOptions options,
98 bool broken_views);
98 99
99[[nodiscard]] bool IsSubresource(const ImageInfo& candidate, const ImageBase& image, 100[[nodiscard]] bool IsSubresource(const ImageInfo& candidate, const ImageBase& image,
100 GPUVAddr candidate_addr, RelaxedOptions options); 101 GPUVAddr candidate_addr, RelaxedOptions options,
102 bool broken_views);
101 103
102void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* dst, 104void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* dst,
103 const ImageBase* src); 105 const ImageBase* src);
diff --git a/src/video_core/textures/astc.cpp b/src/video_core/textures/astc.cpp
index acd5bdd78..3625b666c 100644
--- a/src/video_core/textures/astc.cpp
+++ b/src/video_core/textures/astc.cpp
@@ -42,21 +42,24 @@ constexpr u32 Popcnt(u32 n) {
42 42
43class InputBitStream { 43class InputBitStream {
44public: 44public:
45 constexpr explicit InputBitStream(const u8* ptr, std::size_t start_offset = 0) 45 constexpr explicit InputBitStream(std::span<const u8> data, size_t start_offset = 0)
46 : cur_byte{ptr}, next_bit{start_offset % 8} {} 46 : cur_byte{data.data()}, total_bits{data.size()}, next_bit{start_offset % 8} {}
47 47
48 constexpr std::size_t GetBitsRead() const { 48 constexpr size_t GetBitsRead() const {
49 return bits_read; 49 return bits_read;
50 } 50 }
51 51
52 constexpr bool ReadBit() { 52 constexpr bool ReadBit() {
53 const bool bit = (*cur_byte >> next_bit++) & 1; 53 if (bits_read >= total_bits * 8) {
54 return 0;
55 }
56 const bool bit = ((*cur_byte >> next_bit) & 1) != 0;
57 ++next_bit;
54 while (next_bit >= 8) { 58 while (next_bit >= 8) {
55 next_bit -= 8; 59 next_bit -= 8;
56 cur_byte++; 60 ++cur_byte;
57 } 61 }
58 62 ++bits_read;
59 bits_read++;
60 return bit; 63 return bit;
61 } 64 }
62 65
@@ -79,8 +82,9 @@ public:
79 82
80private: 83private:
81 const u8* cur_byte; 84 const u8* cur_byte;
82 std::size_t next_bit = 0; 85 size_t total_bits = 0;
83 std::size_t bits_read = 0; 86 size_t next_bit = 0;
87 size_t bits_read = 0;
84}; 88};
85 89
86class OutputBitStream { 90class OutputBitStream {
@@ -193,15 +197,15 @@ struct IntegerEncodedValue {
193 }; 197 };
194}; 198};
195using IntegerEncodedVector = boost::container::static_vector< 199using IntegerEncodedVector = boost::container::static_vector<
196 IntegerEncodedValue, 64, 200 IntegerEncodedValue, 256,
197 boost::container::static_vector_options< 201 boost::container::static_vector_options<
198 boost::container::inplace_alignment<alignof(IntegerEncodedValue)>, 202 boost::container::inplace_alignment<alignof(IntegerEncodedValue)>,
199 boost::container::throw_on_overflow<false>>::type>; 203 boost::container::throw_on_overflow<false>>::type>;
200 204
201static void DecodeTritBlock(InputBitStream& bits, IntegerEncodedVector& result, u32 nBitsPerValue) { 205static void DecodeTritBlock(InputBitStream& bits, IntegerEncodedVector& result, u32 nBitsPerValue) {
202 // Implement the algorithm in section C.2.12 206 // Implement the algorithm in section C.2.12
203 u32 m[5]; 207 std::array<u32, 5> m;
204 u32 t[5]; 208 std::array<u32, 5> t;
205 u32 T; 209 u32 T;
206 210
207 // Read the trit encoded block according to 211 // Read the trit encoded block according to
@@ -866,7 +870,7 @@ public:
866 } 870 }
867}; 871};
868 872
869static void DecodeColorValues(u32* out, u8* data, const u32* modes, const u32 nPartitions, 873static void DecodeColorValues(u32* out, std::span<u8> data, const u32* modes, const u32 nPartitions,
870 const u32 nBitsForColorData) { 874 const u32 nBitsForColorData) {
871 // First figure out how many color values we have 875 // First figure out how many color values we have
872 u32 nValues = 0; 876 u32 nValues = 0;
@@ -898,7 +902,7 @@ static void DecodeColorValues(u32* out, u8* data, const u32* modes, const u32 nP
898 // We now have enough to decode our integer sequence. 902 // We now have enough to decode our integer sequence.
899 IntegerEncodedVector decodedColorValues; 903 IntegerEncodedVector decodedColorValues;
900 904
901 InputBitStream colorStream(data); 905 InputBitStream colorStream(data, 0);
902 DecodeIntegerSequence(decodedColorValues, colorStream, range, nValues); 906 DecodeIntegerSequence(decodedColorValues, colorStream, range, nValues);
903 907
904 // Once we have the decoded values, we need to dequantize them to the 0-255 range 908 // Once we have the decoded values, we need to dequantize them to the 0-255 range
@@ -1441,7 +1445,7 @@ static void ComputeEndpos32s(Pixel& ep1, Pixel& ep2, const u32*& colorValues,
1441 1445
1442static void DecompressBlock(std::span<const u8, 16> inBuf, const u32 blockWidth, 1446static void DecompressBlock(std::span<const u8, 16> inBuf, const u32 blockWidth,
1443 const u32 blockHeight, std::span<u32, 12 * 12> outBuf) { 1447 const u32 blockHeight, std::span<u32, 12 * 12> outBuf) {
1444 InputBitStream strm(inBuf.data()); 1448 InputBitStream strm(inBuf);
1445 TexelWeightParams weightParams = DecodeBlockInfo(strm); 1449 TexelWeightParams weightParams = DecodeBlockInfo(strm);
1446 1450
1447 // Was there an error? 1451 // Was there an error?
@@ -1619,15 +1623,16 @@ static void DecompressBlock(std::span<const u8, 16> inBuf, const u32 blockWidth,
1619 1623
1620 // Make sure that higher non-texel bits are set to zero 1624 // Make sure that higher non-texel bits are set to zero
1621 const u32 clearByteStart = (weightParams.GetPackedBitSize() >> 3) + 1; 1625 const u32 clearByteStart = (weightParams.GetPackedBitSize() >> 3) + 1;
1622 if (clearByteStart > 0) { 1626 if (clearByteStart > 0 && clearByteStart <= texelWeightData.size()) {
1623 texelWeightData[clearByteStart - 1] &= 1627 texelWeightData[clearByteStart - 1] &=
1624 static_cast<u8>((1 << (weightParams.GetPackedBitSize() % 8)) - 1); 1628 static_cast<u8>((1 << (weightParams.GetPackedBitSize() % 8)) - 1);
1629 std::memset(texelWeightData.data() + clearByteStart, 0,
1630 std::min(16U - clearByteStart, 16U));
1625 } 1631 }
1626 std::memset(texelWeightData.data() + clearByteStart, 0, std::min(16U - clearByteStart, 16U));
1627 1632
1628 IntegerEncodedVector texelWeightValues; 1633 IntegerEncodedVector texelWeightValues;
1629 1634
1630 InputBitStream weightStream(texelWeightData.data()); 1635 InputBitStream weightStream(texelWeightData);
1631 1636
1632 DecodeIntegerSequence(texelWeightValues, weightStream, weightParams.m_MaxWeight, 1637 DecodeIntegerSequence(texelWeightValues, weightStream, weightParams.m_MaxWeight,
1633 weightParams.GetNumWeightValues()); 1638 weightParams.GetNumWeightValues());
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 9f5181318..62685a183 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -49,7 +49,7 @@ void Swizzle(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixe
49 // We can configure here a custom pitch 49 // We can configure here a custom pitch
50 // As it's not exposed 'width * bpp' will be the expected pitch. 50 // As it's not exposed 'width * bpp' will be the expected pitch.
51 const u32 pitch = width * bytes_per_pixel; 51 const u32 pitch = width * bytes_per_pixel;
52 const u32 stride = Common::AlignBits(width, stride_alignment) * bytes_per_pixel; 52 const u32 stride = Common::AlignUpLog2(width, stride_alignment) * bytes_per_pixel;
53 53
54 const u32 gobs_in_x = Common::DivCeilLog2(stride, GOB_SIZE_X_SHIFT); 54 const u32 gobs_in_x = Common::DivCeilLog2(stride, GOB_SIZE_X_SHIFT);
55 const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth); 55 const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth);
@@ -217,9 +217,9 @@ void SwizzleKepler(const u32 width, const u32 height, const u32 dst_x, const u32
217std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, 217std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
218 u32 block_height, u32 block_depth) { 218 u32 block_height, u32 block_depth) {
219 if (tiled) { 219 if (tiled) {
220 const u32 aligned_width = Common::AlignBits(width * bytes_per_pixel, GOB_SIZE_X_SHIFT); 220 const u32 aligned_width = Common::AlignUpLog2(width * bytes_per_pixel, GOB_SIZE_X_SHIFT);
221 const u32 aligned_height = Common::AlignBits(height, GOB_SIZE_Y_SHIFT + block_height); 221 const u32 aligned_height = Common::AlignUpLog2(height, GOB_SIZE_Y_SHIFT + block_height);
222 const u32 aligned_depth = Common::AlignBits(depth, GOB_SIZE_Z_SHIFT + block_depth); 222 const u32 aligned_depth = Common::AlignUpLog2(depth, GOB_SIZE_Z_SHIFT + block_depth);
223 return aligned_width * aligned_height * aligned_depth; 223 return aligned_width * aligned_height * aligned_depth;
224 } else { 224 } else {
225 return width * height * depth * bytes_per_pixel; 225 return width * height * depth * bytes_per_pixel;
diff --git a/src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp b/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp
index 5b01020ec..8d10ac29e 100644
--- a/src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp
+++ b/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp
@@ -32,20 +32,11 @@ namespace Vulkan {
32 32
33static constexpr char AFTERMATH_LIB_NAME[] = "GFSDK_Aftermath_Lib.x64.dll"; 33static constexpr char AFTERMATH_LIB_NAME[] = "GFSDK_Aftermath_Lib.x64.dll";
34 34
35NsightAftermathTracker::NsightAftermathTracker() = default; 35NsightAftermathTracker::NsightAftermathTracker() {
36
37NsightAftermathTracker::~NsightAftermathTracker() {
38 if (initialized) {
39 (void)GFSDK_Aftermath_DisableGpuCrashDumps();
40 }
41}
42
43bool NsightAftermathTracker::Initialize() {
44 if (!dl.Open(AFTERMATH_LIB_NAME)) { 36 if (!dl.Open(AFTERMATH_LIB_NAME)) {
45 LOG_ERROR(Render_Vulkan, "Failed to load Nsight Aftermath DLL"); 37 LOG_ERROR(Render_Vulkan, "Failed to load Nsight Aftermath DLL");
46 return false; 38 return;
47 } 39 }
48
49 if (!dl.GetSymbol("GFSDK_Aftermath_DisableGpuCrashDumps", 40 if (!dl.GetSymbol("GFSDK_Aftermath_DisableGpuCrashDumps",
50 &GFSDK_Aftermath_DisableGpuCrashDumps) || 41 &GFSDK_Aftermath_DisableGpuCrashDumps) ||
51 !dl.GetSymbol("GFSDK_Aftermath_EnableGpuCrashDumps", 42 !dl.GetSymbol("GFSDK_Aftermath_EnableGpuCrashDumps",
@@ -64,27 +55,28 @@ bool NsightAftermathTracker::Initialize() {
64 LOG_ERROR(Render_Vulkan, "Failed to load Nsight Aftermath function pointers"); 55 LOG_ERROR(Render_Vulkan, "Failed to load Nsight Aftermath function pointers");
65 return false; 56 return false;
66 } 57 }
67
68 dump_dir = Common::FS::GetUserPath(Common::FS::UserPath::LogDir) + "gpucrash"; 58 dump_dir = Common::FS::GetUserPath(Common::FS::UserPath::LogDir) + "gpucrash";
69 59
70 (void)Common::FS::DeleteDirRecursively(dump_dir); 60 void(Common::FS::DeleteDirRecursively(dump_dir));
71 if (!Common::FS::CreateDir(dump_dir)) { 61 if (!Common::FS::CreateDir(dump_dir)) {
72 LOG_ERROR(Render_Vulkan, "Failed to create Nsight Aftermath dump directory"); 62 LOG_ERROR(Render_Vulkan, "Failed to create Nsight Aftermath dump directory");
73 return false; 63 return;
74 } 64 }
75
76 if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_EnableGpuCrashDumps( 65 if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_EnableGpuCrashDumps(
77 GFSDK_Aftermath_Version_API, GFSDK_Aftermath_GpuCrashDumpWatchedApiFlags_Vulkan, 66 GFSDK_Aftermath_Version_API, GFSDK_Aftermath_GpuCrashDumpWatchedApiFlags_Vulkan,
78 GFSDK_Aftermath_GpuCrashDumpFeatureFlags_Default, GpuCrashDumpCallback, 67 GFSDK_Aftermath_GpuCrashDumpFeatureFlags_Default, GpuCrashDumpCallback,
79 ShaderDebugInfoCallback, CrashDumpDescriptionCallback, this))) { 68 ShaderDebugInfoCallback, CrashDumpDescriptionCallback, this))) {
80 LOG_ERROR(Render_Vulkan, "GFSDK_Aftermath_EnableGpuCrashDumps failed"); 69 LOG_ERROR(Render_Vulkan, "GFSDK_Aftermath_EnableGpuCrashDumps failed");
81 return false; 70 return;
82 } 71 }
83
84 LOG_INFO(Render_Vulkan, "Nsight Aftermath dump directory is \"{}\"", dump_dir); 72 LOG_INFO(Render_Vulkan, "Nsight Aftermath dump directory is \"{}\"", dump_dir);
85
86 initialized = true; 73 initialized = true;
87 return true; 74}
75
76NsightAftermathTracker::~NsightAftermathTracker() {
77 if (initialized) {
78 (void)GFSDK_Aftermath_DisableGpuCrashDumps();
79 }
88} 80}
89 81
90void NsightAftermathTracker::SaveShader(const std::vector<u32>& spirv) const { 82void NsightAftermathTracker::SaveShader(const std::vector<u32>& spirv) const {
diff --git a/src/video_core/renderer_vulkan/nsight_aftermath_tracker.h b/src/video_core/vulkan_common/nsight_aftermath_tracker.h
index afe7ae99e..cee3847fb 100644
--- a/src/video_core/renderer_vulkan/nsight_aftermath_tracker.h
+++ b/src/video_core/vulkan_common/nsight_aftermath_tracker.h
@@ -34,8 +34,6 @@ public:
34 NsightAftermathTracker(NsightAftermathTracker&&) = delete; 34 NsightAftermathTracker(NsightAftermathTracker&&) = delete;
35 NsightAftermathTracker& operator=(NsightAftermathTracker&&) = delete; 35 NsightAftermathTracker& operator=(NsightAftermathTracker&&) = delete;
36 36
37 bool Initialize();
38
39 void SaveShader(const std::vector<u32>& spirv) const; 37 void SaveShader(const std::vector<u32>& spirv) const;
40 38
41private: 39private:
@@ -78,9 +76,6 @@ private:
78#ifndef HAS_NSIGHT_AFTERMATH 76#ifndef HAS_NSIGHT_AFTERMATH
79inline NsightAftermathTracker::NsightAftermathTracker() = default; 77inline NsightAftermathTracker::NsightAftermathTracker() = default;
80inline NsightAftermathTracker::~NsightAftermathTracker() = default; 78inline NsightAftermathTracker::~NsightAftermathTracker() = default;
81inline bool NsightAftermathTracker::Initialize() {
82 return false;
83}
84inline void NsightAftermathTracker::SaveShader(const std::vector<u32>&) const {} 79inline void NsightAftermathTracker::SaveShader(const std::vector<u32>&) const {}
85#endif 80#endif
86 81
diff --git a/src/video_core/vulkan_common/vulkan_debug_callback.cpp b/src/video_core/vulkan_common/vulkan_debug_callback.cpp
new file mode 100644
index 000000000..ea7af8ad4
--- /dev/null
+++ b/src/video_core/vulkan_common/vulkan_debug_callback.cpp
@@ -0,0 +1,45 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <string_view>
6#include "common/logging/log.h"
7#include "video_core/vulkan_common/vulkan_debug_callback.h"
8
9namespace Vulkan {
10namespace {
11VkBool32 Callback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
12 VkDebugUtilsMessageTypeFlagsEXT type,
13 const VkDebugUtilsMessengerCallbackDataEXT* data,
14 [[maybe_unused]] void* user_data) {
15 const std::string_view message{data->pMessage};
16 if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
17 LOG_CRITICAL(Render_Vulkan, "{}", message);
18 } else if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
19 LOG_WARNING(Render_Vulkan, "{}", message);
20 } else if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) {
21 LOG_INFO(Render_Vulkan, "{}", message);
22 } else if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) {
23 LOG_DEBUG(Render_Vulkan, "{}", message);
24 }
25 return VK_FALSE;
26}
27} // Anonymous namespace
28
29vk::DebugUtilsMessenger CreateDebugCallback(const vk::Instance& instance) {
30 return instance.CreateDebugUtilsMessenger(VkDebugUtilsMessengerCreateInfoEXT{
31 .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
32 .pNext = nullptr,
33 .flags = 0,
34 .messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT |
35 VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
36 VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
37 VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT,
38 .messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
39 VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
40 VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT,
41 .pfnUserCallback = Callback,
42 });
43}
44
45} // namespace Vulkan
diff --git a/src/video_core/vulkan_common/vulkan_debug_callback.h b/src/video_core/vulkan_common/vulkan_debug_callback.h
new file mode 100644
index 000000000..2efcd244c
--- /dev/null
+++ b/src/video_core/vulkan_common/vulkan_debug_callback.h
@@ -0,0 +1,11 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "video_core/vulkan_common/vulkan_wrapper.h"
6
7namespace Vulkan {
8
9vk::DebugUtilsMessenger CreateDebugCallback(const vk::Instance& instance);
10
11} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 370a63f74..37d7b45a3 100644
--- a/src/video_core/renderer_vulkan/vk_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -13,8 +13,9 @@
13 13
14#include "common/assert.h" 14#include "common/assert.h"
15#include "core/settings.h" 15#include "core/settings.h"
16#include "video_core/renderer_vulkan/vk_device.h" 16#include "video_core/vulkan_common/nsight_aftermath_tracker.h"
17#include "video_core/renderer_vulkan/wrapper.h" 17#include "video_core/vulkan_common/vulkan_device.h"
18#include "video_core/vulkan_common/vulkan_wrapper.h"
18 19
19namespace Vulkan { 20namespace Vulkan {
20 21
@@ -98,8 +99,7 @@ VkFormatFeatureFlags GetFormatFeatures(VkFormatProperties properties, FormatType
98 }); 99 });
99} 100}
100 101
101std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties( 102std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::PhysicalDevice physical) {
102 vk::PhysicalDevice physical, const vk::InstanceDispatch& dld) {
103 static constexpr std::array formats{ 103 static constexpr std::array formats{
104 VK_FORMAT_A8B8G8R8_UNORM_PACK32, 104 VK_FORMAT_A8B8G8R8_UNORM_PACK32,
105 VK_FORMAT_A8B8G8R8_UINT_PACK32, 105 VK_FORMAT_A8B8G8R8_UINT_PACK32,
@@ -206,23 +206,21 @@ std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(
206 206
207} // Anonymous namespace 207} // Anonymous namespace
208 208
209VKDevice::VKDevice(VkInstance instance_, u32 instance_version_, vk::PhysicalDevice physical_, 209Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR surface,
210 VkSurfaceKHR surface, const vk::InstanceDispatch& dld_) 210 const vk::InstanceDispatch& dld_)
211 : instance{instance_}, dld{dld_}, physical{physical_}, properties{physical.GetProperties()}, 211 : instance{instance_}, dld{dld_}, physical{physical_}, properties{physical.GetProperties()},
212 instance_version{instance_version_}, format_properties{GetFormatProperties(physical, dld)} { 212 format_properties{GetFormatProperties(physical)} {
213 CheckSuitability();
213 SetupFamilies(surface); 214 SetupFamilies(surface);
214 SetupFeatures(); 215 SetupFeatures();
215}
216
217VKDevice::~VKDevice() = default;
218 216
219bool VKDevice::Create() {
220 const auto queue_cis = GetDeviceQueueCreateInfos(); 217 const auto queue_cis = GetDeviceQueueCreateInfos();
221 const std::vector extensions = LoadExtensions(); 218 const std::vector extensions = LoadExtensions();
222 219
223 VkPhysicalDeviceFeatures2 features2{ 220 VkPhysicalDeviceFeatures2 features2{
224 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, 221 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
225 .pNext = nullptr, 222 .pNext = nullptr,
223 .features{},
226 }; 224 };
227 const void* first_next = &features2; 225 const void* first_next = &features2;
228 void** next = &features2.pNext; 226 void** next = &features2.pNext;
@@ -258,7 +256,7 @@ bool VKDevice::Create() {
258 .shaderTessellationAndGeometryPointSize = false, 256 .shaderTessellationAndGeometryPointSize = false,
259 .shaderImageGatherExtended = true, 257 .shaderImageGatherExtended = true,
260 .shaderStorageImageExtendedFormats = false, 258 .shaderStorageImageExtendedFormats = false,
261 .shaderStorageImageMultisample = true, 259 .shaderStorageImageMultisample = is_shader_storage_image_multisample,
262 .shaderStorageImageReadWithoutFormat = is_formatless_image_load_supported, 260 .shaderStorageImageReadWithoutFormat = is_formatless_image_load_supported,
263 .shaderStorageImageWriteWithoutFormat = true, 261 .shaderStorageImageWriteWithoutFormat = true,
264 .shaderUniformBufferArrayDynamicIndexing = false, 262 .shaderUniformBufferArrayDynamicIndexing = false,
@@ -312,6 +310,7 @@ bool VKDevice::Create() {
312 310
313 VkPhysicalDeviceHostQueryResetFeaturesEXT host_query_reset{ 311 VkPhysicalDeviceHostQueryResetFeaturesEXT host_query_reset{
314 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES_EXT, 312 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES_EXT,
313 .pNext = nullptr,
315 .hostQueryReset = true, 314 .hostQueryReset = true,
316 }; 315 };
317 SetNext(next, host_query_reset); 316 SetNext(next, host_query_reset);
@@ -415,7 +414,7 @@ bool VKDevice::Create() {
415 414
416 VkDeviceDiagnosticsConfigCreateInfoNV diagnostics_nv; 415 VkDeviceDiagnosticsConfigCreateInfoNV diagnostics_nv;
417 if (nv_device_diagnostics_config) { 416 if (nv_device_diagnostics_config) {
418 nsight_aftermath_tracker.Initialize(); 417 nsight_aftermath_tracker = std::make_unique<NsightAftermathTracker>();
419 418
420 diagnostics_nv = { 419 diagnostics_nv = {
421 .sType = VK_STRUCTURE_TYPE_DEVICE_DIAGNOSTICS_CONFIG_CREATE_INFO_NV, 420 .sType = VK_STRUCTURE_TYPE_DEVICE_DIAGNOSTICS_CONFIG_CREATE_INFO_NV,
@@ -426,12 +425,7 @@ bool VKDevice::Create() {
426 }; 425 };
427 first_next = &diagnostics_nv; 426 first_next = &diagnostics_nv;
428 } 427 }
429
430 logical = vk::Device::Create(physical, queue_cis, extensions, first_next, dld); 428 logical = vk::Device::Create(physical, queue_cis, extensions, first_next, dld);
431 if (!logical) {
432 LOG_ERROR(Render_Vulkan, "Failed to create logical device");
433 return false;
434 }
435 429
436 CollectTelemetryParameters(); 430 CollectTelemetryParameters();
437 CollectToolingInfo(); 431 CollectToolingInfo();
@@ -455,11 +449,12 @@ bool VKDevice::Create() {
455 present_queue = logical.GetQueue(present_family); 449 present_queue = logical.GetQueue(present_family);
456 450
457 use_asynchronous_shaders = Settings::values.use_asynchronous_shaders.GetValue(); 451 use_asynchronous_shaders = Settings::values.use_asynchronous_shaders.GetValue();
458 return true;
459} 452}
460 453
461VkFormat VKDevice::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags wanted_usage, 454Device::~Device() = default;
462 FormatType format_type) const { 455
456VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags wanted_usage,
457 FormatType format_type) const {
463 if (IsFormatSupported(wanted_format, wanted_usage, format_type)) { 458 if (IsFormatSupported(wanted_format, wanted_usage, format_type)) {
464 return wanted_format; 459 return wanted_format;
465 } 460 }
@@ -490,18 +485,20 @@ VkFormat VKDevice::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFla
490 return wanted_format; 485 return wanted_format;
491} 486}
492 487
493void VKDevice::ReportLoss() const { 488void Device::ReportLoss() const {
494 LOG_CRITICAL(Render_Vulkan, "Device loss occured!"); 489 LOG_CRITICAL(Render_Vulkan, "Device loss occured!");
495 490
496 // Wait for the log to flush and for Nsight Aftermath to dump the results 491 // Wait for the log to flush and for Nsight Aftermath to dump the results
497 std::this_thread::sleep_for(std::chrono::seconds{15}); 492 std::this_thread::sleep_for(std::chrono::seconds{15});
498} 493}
499 494
500void VKDevice::SaveShader(const std::vector<u32>& spirv) const { 495void Device::SaveShader(const std::vector<u32>& spirv) const {
501 nsight_aftermath_tracker.SaveShader(spirv); 496 if (nsight_aftermath_tracker) {
497 nsight_aftermath_tracker->SaveShader(spirv);
498 }
502} 499}
503 500
504bool VKDevice::IsOptimalAstcSupported(const VkPhysicalDeviceFeatures& features) const { 501bool Device::IsOptimalAstcSupported(const VkPhysicalDeviceFeatures& features) const {
505 // Disable for now to avoid converting ASTC twice. 502 // Disable for now to avoid converting ASTC twice.
506 static constexpr std::array astc_formats = { 503 static constexpr std::array astc_formats = {
507 VK_FORMAT_ASTC_4x4_UNORM_BLOCK, VK_FORMAT_ASTC_4x4_SRGB_BLOCK, 504 VK_FORMAT_ASTC_4x4_UNORM_BLOCK, VK_FORMAT_ASTC_4x4_SRGB_BLOCK,
@@ -535,7 +532,7 @@ bool VKDevice::IsOptimalAstcSupported(const VkPhysicalDeviceFeatures& features)
535 return true; 532 return true;
536} 533}
537 534
538bool VKDevice::TestDepthStencilBlits() const { 535bool Device::TestDepthStencilBlits() const {
539 static constexpr VkFormatFeatureFlags required_features = 536 static constexpr VkFormatFeatureFlags required_features =
540 VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT; 537 VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT;
541 const auto test_features = [](VkFormatProperties props) { 538 const auto test_features = [](VkFormatProperties props) {
@@ -545,8 +542,8 @@ bool VKDevice::TestDepthStencilBlits() const {
545 test_features(format_properties.at(VK_FORMAT_D24_UNORM_S8_UINT)); 542 test_features(format_properties.at(VK_FORMAT_D24_UNORM_S8_UINT));
546} 543}
547 544
548bool VKDevice::IsFormatSupported(VkFormat wanted_format, VkFormatFeatureFlags wanted_usage, 545bool Device::IsFormatSupported(VkFormat wanted_format, VkFormatFeatureFlags wanted_usage,
549 FormatType format_type) const { 546 FormatType format_type) const {
550 const auto it = format_properties.find(wanted_format); 547 const auto it = format_properties.find(wanted_format);
551 if (it == format_properties.end()) { 548 if (it == format_properties.end()) {
552 UNIMPLEMENTED_MSG("Unimplemented format query={}", wanted_format); 549 UNIMPLEMENTED_MSG("Unimplemented format query={}", wanted_format);
@@ -556,64 +553,45 @@ bool VKDevice::IsFormatSupported(VkFormat wanted_format, VkFormatFeatureFlags wa
556 return (supported_usage & wanted_usage) == wanted_usage; 553 return (supported_usage & wanted_usage) == wanted_usage;
557} 554}
558 555
559bool VKDevice::IsSuitable(vk::PhysicalDevice physical, VkSurfaceKHR surface) { 556void Device::CheckSuitability() const {
560 bool is_suitable = true;
561 std::bitset<REQUIRED_EXTENSIONS.size()> available_extensions; 557 std::bitset<REQUIRED_EXTENSIONS.size()> available_extensions;
562 558 for (const VkExtensionProperties& property : physical.EnumerateDeviceExtensionProperties()) {
563 for (const auto& prop : physical.EnumerateDeviceExtensionProperties()) {
564 for (std::size_t i = 0; i < REQUIRED_EXTENSIONS.size(); ++i) { 559 for (std::size_t i = 0; i < REQUIRED_EXTENSIONS.size(); ++i) {
565 if (available_extensions[i]) { 560 if (available_extensions[i]) {
566 continue; 561 continue;
567 } 562 }
568 const std::string_view name{prop.extensionName}; 563 const std::string_view name{property.extensionName};
569 available_extensions[i] = name == REQUIRED_EXTENSIONS[i]; 564 available_extensions[i] = name == REQUIRED_EXTENSIONS[i];
570 } 565 }
571 } 566 }
572 if (!available_extensions.all()) { 567 for (size_t i = 0; i < REQUIRED_EXTENSIONS.size(); ++i) {
573 for (std::size_t i = 0; i < REQUIRED_EXTENSIONS.size(); ++i) { 568 if (available_extensions[i]) {
574 if (available_extensions[i]) {
575 continue;
576 }
577 LOG_ERROR(Render_Vulkan, "Missing required extension: {}", REQUIRED_EXTENSIONS[i]);
578 is_suitable = false;
579 }
580 }
581
582 bool has_graphics{}, has_present{};
583 const std::vector queue_family_properties = physical.GetQueueFamilyProperties();
584 for (u32 i = 0; i < static_cast<u32>(queue_family_properties.size()); ++i) {
585 const auto& family = queue_family_properties[i];
586 if (family.queueCount == 0) {
587 continue; 569 continue;
588 } 570 }
589 has_graphics |= family.queueFlags & VK_QUEUE_GRAPHICS_BIT; 571 LOG_ERROR(Render_Vulkan, "Missing required extension: {}", REQUIRED_EXTENSIONS[i]);
590 has_present |= physical.GetSurfaceSupportKHR(i, surface); 572 throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT);
591 } 573 }
592 if (!has_graphics || !has_present) { 574 struct LimitTuple {
593 LOG_ERROR(Render_Vulkan, "Device lacks a graphics and present queue"); 575 u32 minimum;
594 is_suitable = false; 576 u32 value;
595 } 577 const char* name;
596 578 };
597 // TODO(Rodrigo): Check if the device matches all requeriments. 579 const VkPhysicalDeviceLimits& limits{properties.limits};
598 const auto properties{physical.GetProperties()}; 580 const std::array limits_report{
599 const auto& limits{properties.limits}; 581 LimitTuple{65536, limits.maxUniformBufferRange, "maxUniformBufferRange"},
600 582 LimitTuple{16, limits.maxViewports, "maxViewports"},
601 constexpr u32 required_ubo_size = 65536; 583 LimitTuple{8, limits.maxColorAttachments, "maxColorAttachments"},
602 if (limits.maxUniformBufferRange < required_ubo_size) { 584 LimitTuple{8, limits.maxClipDistances, "maxClipDistances"},
603 LOG_ERROR(Render_Vulkan, "Device UBO size {} is too small, {} is required", 585 };
604 limits.maxUniformBufferRange, required_ubo_size); 586 for (const auto& tuple : limits_report) {
605 is_suitable = false; 587 if (tuple.value < tuple.minimum) {
606 } 588 LOG_ERROR(Render_Vulkan, "{} has to be {} or greater but it is {}", tuple.name,
607 589 tuple.minimum, tuple.value);
608 constexpr u32 required_num_viewports = 16; 590 throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT);
609 if (limits.maxViewports < required_num_viewports) { 591 }
610 LOG_INFO(Render_Vulkan, "Device number of viewports {} is too small, {} is required",
611 limits.maxViewports, required_num_viewports);
612 is_suitable = false;
613 } 592 }
614 593 const VkPhysicalDeviceFeatures features{physical.GetFeatures()};
615 const auto features{physical.GetFeatures()}; 594 const std::array feature_report{
616 const std::array feature_report = {
617 std::make_pair(features.vertexPipelineStoresAndAtomics, "vertexPipelineStoresAndAtomics"), 595 std::make_pair(features.vertexPipelineStoresAndAtomics, "vertexPipelineStoresAndAtomics"),
618 std::make_pair(features.imageCubeArray, "imageCubeArray"), 596 std::make_pair(features.imageCubeArray, "imageCubeArray"),
619 std::make_pair(features.independentBlend, "independentBlend"), 597 std::make_pair(features.independentBlend, "independentBlend"),
@@ -627,26 +605,19 @@ bool VKDevice::IsSuitable(vk::PhysicalDevice physical, VkSurfaceKHR surface) {
627 std::make_pair(features.occlusionQueryPrecise, "occlusionQueryPrecise"), 605 std::make_pair(features.occlusionQueryPrecise, "occlusionQueryPrecise"),
628 std::make_pair(features.fragmentStoresAndAtomics, "fragmentStoresAndAtomics"), 606 std::make_pair(features.fragmentStoresAndAtomics, "fragmentStoresAndAtomics"),
629 std::make_pair(features.shaderImageGatherExtended, "shaderImageGatherExtended"), 607 std::make_pair(features.shaderImageGatherExtended, "shaderImageGatherExtended"),
630 std::make_pair(features.shaderStorageImageMultisample, "shaderStorageImageMultisample"),
631 std::make_pair(features.shaderStorageImageWriteWithoutFormat, 608 std::make_pair(features.shaderStorageImageWriteWithoutFormat,
632 "shaderStorageImageWriteWithoutFormat"), 609 "shaderStorageImageWriteWithoutFormat"),
633 }; 610 };
634 for (const auto& [supported, name] : feature_report) { 611 for (const auto& [is_supported, name] : feature_report) {
635 if (supported) { 612 if (is_supported) {
636 continue; 613 continue;
637 } 614 }
638 LOG_ERROR(Render_Vulkan, "Missing required feature: {}", name); 615 LOG_ERROR(Render_Vulkan, "Missing required feature: {}", name);
639 is_suitable = false; 616 throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT);
640 } 617 }
641
642 if (!is_suitable) {
643 LOG_ERROR(Render_Vulkan, "{} is not suitable", properties.deviceName);
644 }
645
646 return is_suitable;
647} 618}
648 619
649std::vector<const char*> VKDevice::LoadExtensions() { 620std::vector<const char*> Device::LoadExtensions() {
650 std::vector<const char*> extensions; 621 std::vector<const char*> extensions;
651 extensions.reserve(7 + REQUIRED_EXTENSIONS.size()); 622 extensions.reserve(7 + REQUIRED_EXTENSIONS.size());
652 extensions.insert(extensions.begin(), REQUIRED_EXTENSIONS.begin(), REQUIRED_EXTENSIONS.end()); 623 extensions.insert(extensions.begin(), REQUIRED_EXTENSIONS.begin(), REQUIRED_EXTENSIONS.end());
@@ -685,9 +656,7 @@ std::vector<const char*> VKDevice::LoadExtensions() {
685 test(has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, false); 656 test(has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, false);
686 test(has_ext_extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, false); 657 test(has_ext_extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, false);
687 test(has_ext_robustness2, VK_EXT_ROBUSTNESS_2_EXTENSION_NAME, false); 658 test(has_ext_robustness2, VK_EXT_ROBUSTNESS_2_EXTENSION_NAME, false);
688 if (instance_version >= VK_API_VERSION_1_1) { 659 test(has_ext_subgroup_size_control, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, false);
689 test(has_ext_subgroup_size_control, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, false);
690 }
691 if (Settings::values.renderer_debug) { 660 if (Settings::values.renderer_debug) {
692 test(nv_device_diagnostics_config, VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, 661 test(nv_device_diagnostics_config, VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME,
693 true); 662 true);
@@ -801,39 +770,46 @@ std::vector<const char*> VKDevice::LoadExtensions() {
801 return extensions; 770 return extensions;
802} 771}
803 772
804void VKDevice::SetupFamilies(VkSurfaceKHR surface) { 773void Device::SetupFamilies(VkSurfaceKHR surface) {
805 std::optional<u32> graphics_family_, present_family_;
806
807 const std::vector queue_family_properties = physical.GetQueueFamilyProperties(); 774 const std::vector queue_family_properties = physical.GetQueueFamilyProperties();
808 for (u32 i = 0; i < static_cast<u32>(queue_family_properties.size()); ++i) { 775 std::optional<u32> graphics;
809 if (graphics_family_ && present_family_) 776 std::optional<u32> present;
777 for (u32 index = 0; index < static_cast<u32>(queue_family_properties.size()); ++index) {
778 if (graphics && (present || !surface)) {
810 break; 779 break;
811 780 }
812 const auto& queue_family = queue_family_properties[i]; 781 const VkQueueFamilyProperties& queue_family = queue_family_properties[index];
813 if (queue_family.queueCount == 0) 782 if (queue_family.queueCount == 0) {
814 continue; 783 continue;
815 784 }
816 if (queue_family.queueFlags & VK_QUEUE_GRAPHICS_BIT) { 785 if (queue_family.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
817 graphics_family_ = i; 786 graphics = index;
818 } 787 }
819 if (physical.GetSurfaceSupportKHR(i, surface)) { 788 if (surface && physical.GetSurfaceSupportKHR(index, surface)) {
820 present_family_ = i; 789 present = index;
821 } 790 }
822 } 791 }
823 ASSERT(graphics_family_ && present_family_); 792 if (!graphics) {
824 793 LOG_ERROR(Render_Vulkan, "Device lacks a graphics queue");
825 graphics_family = *graphics_family_; 794 throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT);
826 present_family = *present_family_; 795 }
796 if (surface && !present) {
797 LOG_ERROR(Render_Vulkan, "Device lacks a present queue");
798 throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT);
799 }
800 graphics_family = *graphics;
801 present_family = *present;
827} 802}
828 803
829void VKDevice::SetupFeatures() { 804void Device::SetupFeatures() {
830 const auto supported_features{physical.GetFeatures()}; 805 const auto supported_features{physical.GetFeatures()};
831 is_formatless_image_load_supported = supported_features.shaderStorageImageReadWithoutFormat; 806 is_formatless_image_load_supported = supported_features.shaderStorageImageReadWithoutFormat;
807 is_shader_storage_image_multisample = supported_features.shaderStorageImageMultisample;
832 is_blit_depth_stencil_supported = TestDepthStencilBlits(); 808 is_blit_depth_stencil_supported = TestDepthStencilBlits();
833 is_optimal_astc_supported = IsOptimalAstcSupported(supported_features); 809 is_optimal_astc_supported = IsOptimalAstcSupported(supported_features);
834} 810}
835 811
836void VKDevice::CollectTelemetryParameters() { 812void Device::CollectTelemetryParameters() {
837 VkPhysicalDeviceDriverPropertiesKHR driver{ 813 VkPhysicalDeviceDriverPropertiesKHR driver{
838 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR, 814 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR,
839 .pNext = nullptr, 815 .pNext = nullptr,
@@ -860,7 +836,7 @@ void VKDevice::CollectTelemetryParameters() {
860 } 836 }
861} 837}
862 838
863void VKDevice::CollectToolingInfo() { 839void Device::CollectToolingInfo() {
864 if (!ext_tooling_info) { 840 if (!ext_tooling_info) {
865 return; 841 return;
866 } 842 }
@@ -886,7 +862,7 @@ void VKDevice::CollectToolingInfo() {
886 } 862 }
887} 863}
888 864
889std::vector<VkDeviceQueueCreateInfo> VKDevice::GetDeviceQueueCreateInfos() const { 865std::vector<VkDeviceQueueCreateInfo> Device::GetDeviceQueueCreateInfos() const {
890 static constexpr float QUEUE_PRIORITY = 1.0f; 866 static constexpr float QUEUE_PRIORITY = 1.0f;
891 867
892 std::unordered_set<u32> unique_queue_families{graphics_family, present_family}; 868 std::unordered_set<u32> unique_queue_families{graphics_family, present_family};
diff --git a/src/video_core/renderer_vulkan/vk_device.h b/src/video_core/vulkan_common/vulkan_device.h
index 995dcfc0f..4b66dba7a 100644
--- a/src/video_core/renderer_vulkan/vk_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -10,11 +10,12 @@
10#include <vector> 10#include <vector>
11 11
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "video_core/renderer_vulkan/nsight_aftermath_tracker.h" 13#include "video_core/vulkan_common/vulkan_wrapper.h"
14#include "video_core/renderer_vulkan/wrapper.h"
15 14
16namespace Vulkan { 15namespace Vulkan {
17 16
17class NsightAftermathTracker;
18
18/// Format usage descriptor. 19/// Format usage descriptor.
19enum class FormatType { Linear, Optimal, Buffer }; 20enum class FormatType { Linear, Optimal, Buffer };
20 21
@@ -22,14 +23,11 @@ enum class FormatType { Linear, Optimal, Buffer };
22const u32 GuestWarpSize = 32; 23const u32 GuestWarpSize = 32;
23 24
24/// Handles data specific to a physical device. 25/// Handles data specific to a physical device.
25class VKDevice final { 26class Device final {
26public: 27public:
27 explicit VKDevice(VkInstance instance, u32 instance_version, vk::PhysicalDevice physical, 28 explicit Device(VkInstance instance, vk::PhysicalDevice physical, VkSurfaceKHR surface,
28 VkSurfaceKHR surface, const vk::InstanceDispatch& dld); 29 const vk::InstanceDispatch& dld);
29 ~VKDevice(); 30 ~Device();
30
31 /// Initializes the device. Returns true on success.
32 bool Create();
33 31
34 /** 32 /**
35 * Returns a format supported by the device for the passed requeriments. 33 * Returns a format supported by the device for the passed requeriments.
@@ -82,11 +80,6 @@ public:
82 return present_family; 80 return present_family;
83 } 81 }
84 82
85 /// Returns the current instance Vulkan API version in Vulkan-formatted version numbers.
86 u32 InstanceApiVersion() const {
87 return instance_version;
88 }
89
90 /// Returns the current Vulkan API version provided in Vulkan-formatted version numbers. 83 /// Returns the current Vulkan API version provided in Vulkan-formatted version numbers.
91 u32 ApiVersion() const { 84 u32 ApiVersion() const {
92 return properties.apiVersion; 85 return properties.apiVersion;
@@ -232,10 +225,10 @@ public:
232 return use_asynchronous_shaders; 225 return use_asynchronous_shaders;
233 } 226 }
234 227
228private:
235 /// Checks if the physical device is suitable. 229 /// Checks if the physical device is suitable.
236 static bool IsSuitable(vk::PhysicalDevice physical, VkSurfaceKHR surface); 230 void CheckSuitability() const;
237 231
238private:
239 /// Loads extensions into a vector and stores available ones in this object. 232 /// Loads extensions into a vector and stores available ones in this object.
240 std::vector<const char*> LoadExtensions(); 233 std::vector<const char*> LoadExtensions();
241 234
@@ -279,23 +272,24 @@ private:
279 bool is_optimal_astc_supported{}; ///< Support for native ASTC. 272 bool is_optimal_astc_supported{}; ///< Support for native ASTC.
280 bool is_float16_supported{}; ///< Support for float16 arithmetics. 273 bool is_float16_supported{}; ///< Support for float16 arithmetics.
281 bool is_warp_potentially_bigger{}; ///< Host warp size can be bigger than guest. 274 bool is_warp_potentially_bigger{}; ///< Host warp size can be bigger than guest.
282 bool is_formatless_image_load_supported{}; ///< Support for shader image read without format. 275 bool is_formatless_image_load_supported{}; ///< Support for shader image read without format.
283 bool is_blit_depth_stencil_supported{}; ///< Support for blitting from and to depth stencil. 276 bool is_shader_storage_image_multisample{}; ///< Support for image operations on MSAA images.
284 bool nv_viewport_swizzle{}; ///< Support for VK_NV_viewport_swizzle. 277 bool is_blit_depth_stencil_supported{}; ///< Support for blitting from and to depth stencil.
285 bool khr_uniform_buffer_standard_layout{}; ///< Support for std430 on UBOs. 278 bool nv_viewport_swizzle{}; ///< Support for VK_NV_viewport_swizzle.
286 bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8. 279 bool khr_uniform_buffer_standard_layout{}; ///< Support for std430 on UBOs.
287 bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax. 280 bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8.
288 bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted. 281 bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax.
289 bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer. 282 bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted.
290 bool ext_tooling_info{}; ///< Support for VK_EXT_tooling_info. 283 bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer.
291 bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback. 284 bool ext_tooling_info{}; ///< Support for VK_EXT_tooling_info.
292 bool ext_custom_border_color{}; ///< Support for VK_EXT_custom_border_color. 285 bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback.
293 bool ext_extended_dynamic_state{}; ///< Support for VK_EXT_extended_dynamic_state. 286 bool ext_custom_border_color{}; ///< Support for VK_EXT_custom_border_color.
294 bool ext_robustness2{}; ///< Support for VK_EXT_robustness2. 287 bool ext_extended_dynamic_state{}; ///< Support for VK_EXT_extended_dynamic_state.
295 bool ext_shader_stencil_export{}; ///< Support for VK_EXT_shader_stencil_export. 288 bool ext_robustness2{}; ///< Support for VK_EXT_robustness2.
296 bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config. 289 bool ext_shader_stencil_export{}; ///< Support for VK_EXT_shader_stencil_export.
297 bool has_renderdoc{}; ///< Has RenderDoc attached 290 bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config.
298 bool has_nsight_graphics{}; ///< Has Nsight Graphics attached 291 bool has_renderdoc{}; ///< Has RenderDoc attached
292 bool has_nsight_graphics{}; ///< Has Nsight Graphics attached
299 293
300 // Asynchronous Graphics Pipeline setting 294 // Asynchronous Graphics Pipeline setting
301 bool use_asynchronous_shaders{}; ///< Setting to use asynchronous shaders/graphics pipeline 295 bool use_asynchronous_shaders{}; ///< Setting to use asynchronous shaders/graphics pipeline
@@ -308,7 +302,7 @@ private:
308 std::unordered_map<VkFormat, VkFormatProperties> format_properties; 302 std::unordered_map<VkFormat, VkFormatProperties> format_properties;
309 303
310 /// Nsight Aftermath GPU crash tracker 304 /// Nsight Aftermath GPU crash tracker
311 NsightAftermathTracker nsight_aftermath_tracker; 305 std::unique_ptr<NsightAftermathTracker> nsight_aftermath_tracker;
312}; 306};
313 307
314} // namespace Vulkan 308} // namespace Vulkan
diff --git a/src/video_core/vulkan_common/vulkan_instance.cpp b/src/video_core/vulkan_common/vulkan_instance.cpp
new file mode 100644
index 000000000..889ecda0c
--- /dev/null
+++ b/src/video_core/vulkan_common/vulkan_instance.cpp
@@ -0,0 +1,151 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <optional>
7#include <span>
8#include <utility>
9#include <vector>
10
11#include "common/common_types.h"
12#include "common/dynamic_library.h"
13#include "common/logging/log.h"
14#include "core/frontend/emu_window.h"
15#include "video_core/vulkan_common/vulkan_instance.h"
16#include "video_core/vulkan_common/vulkan_wrapper.h"
17
18// Include these late to avoid polluting previous headers
19#ifdef _WIN32
20#include <windows.h>
21// ensure include order
22#include <vulkan/vulkan_win32.h>
23#endif
24
25#if !defined(_WIN32) && !defined(__APPLE__)
26#include <X11/Xlib.h>
27#include <vulkan/vulkan_wayland.h>
28#include <vulkan/vulkan_xlib.h>
29#endif
30
31namespace Vulkan {
32namespace {
33[[nodiscard]] std::vector<const char*> RequiredExtensions(
34 Core::Frontend::WindowSystemType window_type, bool enable_debug_utils) {
35 std::vector<const char*> extensions;
36 extensions.reserve(6);
37 switch (window_type) {
38 case Core::Frontend::WindowSystemType::Headless:
39 break;
40#ifdef _WIN32
41 case Core::Frontend::WindowSystemType::Windows:
42 extensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
43 break;
44#endif
45#if !defined(_WIN32) && !defined(__APPLE__)
46 case Core::Frontend::WindowSystemType::X11:
47 extensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
48 break;
49 case Core::Frontend::WindowSystemType::Wayland:
50 extensions.push_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME);
51 break;
52#endif
53 default:
54 LOG_ERROR(Render_Vulkan, "Presentation not supported on this platform");
55 break;
56 }
57 if (window_type != Core::Frontend::WindowSystemType::Headless) {
58 extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
59 }
60 if (enable_debug_utils) {
61 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
62 }
63 extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
64 return extensions;
65}
66
67[[nodiscard]] bool AreExtensionsSupported(const vk::InstanceDispatch& dld,
68 std::span<const char* const> extensions) {
69 const std::optional properties = vk::EnumerateInstanceExtensionProperties(dld);
70 if (!properties) {
71 LOG_ERROR(Render_Vulkan, "Failed to query extension properties");
72 return false;
73 }
74 for (const char* extension : extensions) {
75 const auto it = std::ranges::find_if(*properties, [extension](const auto& prop) {
76 return std::strcmp(extension, prop.extensionName) == 0;
77 });
78 if (it == properties->end()) {
79 LOG_ERROR(Render_Vulkan, "Required instance extension {} is not available", extension);
80 return false;
81 }
82 }
83 return true;
84}
85
86[[nodiscard]] std::vector<const char*> Layers(bool enable_layers) {
87 std::vector<const char*> layers;
88 if (enable_layers) {
89 layers.push_back("VK_LAYER_KHRONOS_validation");
90 }
91 return layers;
92}
93
94void RemoveUnavailableLayers(const vk::InstanceDispatch& dld, std::vector<const char*>& layers) {
95 const std::optional layer_properties = vk::EnumerateInstanceLayerProperties(dld);
96 if (!layer_properties) {
97 LOG_ERROR(Render_Vulkan, "Failed to query layer properties, disabling layers");
98 layers.clear();
99 }
100 std::erase_if(layers, [&layer_properties](const char* layer) {
101 const auto comp = [layer](const VkLayerProperties& layer_property) {
102 return std::strcmp(layer, layer_property.layerName) == 0;
103 };
104 const auto it = std::ranges::find_if(*layer_properties, comp);
105 if (it == layer_properties->end()) {
106 LOG_ERROR(Render_Vulkan, "Layer {} not available, removing it", layer);
107 return true;
108 }
109 return false;
110 });
111}
112} // Anonymous namespace
113
114vk::Instance CreateInstance(const Common::DynamicLibrary& library, vk::InstanceDispatch& dld,
115 u32 required_version, Core::Frontend::WindowSystemType window_type,
116 bool enable_debug_utils, bool enable_layers) {
117 if (!library.IsOpen()) {
118 LOG_ERROR(Render_Vulkan, "Vulkan library not available");
119 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
120 }
121 if (!library.GetSymbol("vkGetInstanceProcAddr", &dld.vkGetInstanceProcAddr)) {
122 LOG_ERROR(Render_Vulkan, "vkGetInstanceProcAddr not present in Vulkan");
123 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
124 }
125 if (!vk::Load(dld)) {
126 LOG_ERROR(Render_Vulkan, "Failed to load Vulkan function pointers");
127 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
128 }
129 const std::vector<const char*> extensions = RequiredExtensions(window_type, enable_debug_utils);
130 if (!AreExtensionsSupported(dld, extensions)) {
131 throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT);
132 }
133 std::vector<const char*> layers = Layers(enable_layers);
134 RemoveUnavailableLayers(dld, layers);
135
136 const u32 available_version = vk::AvailableVersion(dld);
137 if (available_version < required_version) {
138 LOG_ERROR(Render_Vulkan, "Vulkan {}.{} is not supported, {}.{} is required",
139 VK_VERSION_MAJOR(available_version), VK_VERSION_MINOR(available_version),
140 VK_VERSION_MAJOR(required_version), VK_VERSION_MINOR(required_version));
141 throw vk::Exception(VK_ERROR_INCOMPATIBLE_DRIVER);
142 }
143 vk::Instance instance = vk::Instance::Create(required_version, layers, extensions, dld);
144 if (!vk::Load(*instance, dld)) {
145 LOG_ERROR(Render_Vulkan, "Failed to load Vulkan instance function pointers");
146 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
147 }
148 return instance;
149}
150
151} // namespace Vulkan
diff --git a/src/video_core/vulkan_common/vulkan_instance.h b/src/video_core/vulkan_common/vulkan_instance.h
new file mode 100644
index 000000000..e5e3a7144
--- /dev/null
+++ b/src/video_core/vulkan_common/vulkan_instance.h
@@ -0,0 +1,32 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8#include "common/dynamic_library.h"
9#include "core/frontend/emu_window.h"
10#include "video_core/vulkan_common/vulkan_wrapper.h"
11
12namespace Vulkan {
13
14/**
15 * Create a Vulkan instance
16 *
17 * @param library Dynamic library to load the Vulkan instance from
18 * @param dld Dispatch table to load function pointers into
19 * @param required_version Required Vulkan version (for example, VK_API_VERSION_1_1)
20 * @param window_type Window system type's enabled extension
21 * @param enable_debug_utils Whether to enable VK_EXT_debug_utils_extension_name or not
22 * @param enable_layers Whether to enable Vulkan validation layers or not
23 *
24 * @return A new Vulkan instance
25 * @throw vk::Exception on failure
26 */
27[[nodiscard]] vk::Instance CreateInstance(
28 const Common::DynamicLibrary& library, vk::InstanceDispatch& dld, u32 required_version,
29 Core::Frontend::WindowSystemType window_type = Core::Frontend::WindowSystemType::Headless,
30 bool enable_debug_utils = false, bool enable_layers = false);
31
32} // namespace Vulkan
diff --git a/src/video_core/vulkan_common/vulkan_library.cpp b/src/video_core/vulkan_common/vulkan_library.cpp
new file mode 100644
index 000000000..557871d81
--- /dev/null
+++ b/src/video_core/vulkan_common/vulkan_library.cpp
@@ -0,0 +1,36 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstdlib>
6#include <string>
7
8#include "common/dynamic_library.h"
9#include "common/file_util.h"
10#include "video_core/vulkan_common/vulkan_library.h"
11
12namespace Vulkan {
13
14Common::DynamicLibrary OpenLibrary() {
15 Common::DynamicLibrary library;
16#ifdef __APPLE__
17 // Check if a path to a specific Vulkan library has been specified.
18 char* const libvulkan_env = std::getenv("LIBVULKAN_PATH");
19 if (!libvulkan_env || !library.Open(libvulkan_env)) {
20 // Use the libvulkan.dylib from the application bundle.
21 const std::string filename =
22 Common::FS::GetBundleDirectory() + "/Contents/Frameworks/libvulkan.dylib";
23 void(library.Open(filename.c_str()));
24 }
25#else
26 std::string filename = Common::DynamicLibrary::GetVersionedFilename("vulkan", 1);
27 if (!library.Open(filename.c_str())) {
28 // Android devices may not have libvulkan.so.1, only libvulkan.so.
29 filename = Common::DynamicLibrary::GetVersionedFilename("vulkan");
30 void(library.Open(filename.c_str()));
31 }
32#endif
33 return library;
34}
35
36} // namespace Vulkan
diff --git a/src/video_core/vulkan_common/vulkan_library.h b/src/video_core/vulkan_common/vulkan_library.h
new file mode 100644
index 000000000..8b28b0e17
--- /dev/null
+++ b/src/video_core/vulkan_common/vulkan_library.h
@@ -0,0 +1,13 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/dynamic_library.h"
8
9namespace Vulkan {
10
11Common::DynamicLibrary OpenLibrary();
12
13} // namespace Vulkan
diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
new file mode 100644
index 000000000..d6eb3af31
--- /dev/null
+++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
@@ -0,0 +1,268 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <bit>
7#include <optional>
8#include <vector>
9
10#include "common/alignment.h"
11#include "common/assert.h"
12#include "common/common_types.h"
13#include "common/logging/log.h"
14#include "video_core/vulkan_common/vulkan_device.h"
15#include "video_core/vulkan_common/vulkan_memory_allocator.h"
16#include "video_core/vulkan_common/vulkan_wrapper.h"
17
18namespace Vulkan {
19namespace {
20struct Range {
21 u64 begin;
22 u64 end;
23
24 [[nodiscard]] bool Contains(u64 iterator, u64 size) const noexcept {
25 return iterator < end && begin < iterator + size;
26 }
27};
28
29[[nodiscard]] u64 AllocationChunkSize(u64 required_size) {
30 static constexpr std::array sizes{
31 0x1000ULL << 10, 0x1400ULL << 10, 0x1800ULL << 10, 0x1c00ULL << 10, 0x2000ULL << 10,
32 0x3200ULL << 10, 0x4000ULL << 10, 0x6000ULL << 10, 0x8000ULL << 10, 0xA000ULL << 10,
33 0x10000ULL << 10, 0x18000ULL << 10, 0x20000ULL << 10,
34 };
35 static_assert(std::is_sorted(sizes.begin(), sizes.end()));
36
37 const auto it = std::ranges::lower_bound(sizes, required_size);
38 return it != sizes.end() ? *it : Common::AlignUp(required_size, 4ULL << 20);
39}
40
41[[nodiscard]] VkMemoryPropertyFlags MemoryUsagePropertyFlags(MemoryUsage usage) {
42 switch (usage) {
43 case MemoryUsage::DeviceLocal:
44 return VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
45 case MemoryUsage::Upload:
46 return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
47 case MemoryUsage::Download:
48 return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
49 VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
50 }
51 UNREACHABLE_MSG("Invalid memory usage={}", usage);
52 return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
53}
54} // Anonymous namespace
55
56class MemoryAllocation {
57public:
58 explicit MemoryAllocation(const Device& device_, vk::DeviceMemory memory_,
59 VkMemoryPropertyFlags properties, u64 allocation_size_, u32 type)
60 : device{device_}, memory{std::move(memory_)}, allocation_size{allocation_size_},
61 property_flags{properties}, shifted_memory_type{1U << type} {}
62
63 [[nodiscard]] std::optional<MemoryCommit> Commit(VkDeviceSize size, VkDeviceSize alignment) {
64 const std::optional<u64> alloc = FindFreeRegion(size, alignment);
65 if (!alloc) {
66 // Signal out of memory, it'll try to do more allocations.
67 return std::nullopt;
68 }
69 const Range range{
70 .begin = *alloc,
71 .end = *alloc + size,
72 };
73 commits.insert(std::ranges::upper_bound(commits, *alloc, {}, &Range::begin), range);
74 return std::make_optional<MemoryCommit>(this, *memory, *alloc, *alloc + size);
75 }
76
77 void Free(u64 begin) {
78 const auto it = std::ranges::find(commits, begin, &Range::begin);
79 ASSERT_MSG(it != commits.end(), "Invalid commit");
80 commits.erase(it);
81 }
82
83 [[nodiscard]] std::span<u8> Map() {
84 if (memory_mapped_span.empty()) {
85 u8* const raw_pointer = memory.Map(0, allocation_size);
86 memory_mapped_span = std::span<u8>(raw_pointer, allocation_size);
87 }
88 return memory_mapped_span;
89 }
90
91 /// Returns whether this allocation is compatible with the arguments.
92 [[nodiscard]] bool IsCompatible(VkMemoryPropertyFlags flags, u32 type_mask) const {
93 return (flags & property_flags) && (type_mask & shifted_memory_type) != 0;
94 }
95
96private:
97 [[nodiscard]] static constexpr u32 ShiftType(u32 type) {
98 return 1U << type;
99 }
100
101 [[nodiscard]] std::optional<u64> FindFreeRegion(u64 size, u64 alignment) noexcept {
102 ASSERT(std::has_single_bit(alignment));
103 const u64 alignment_log2 = std::countr_zero(alignment);
104 std::optional<u64> candidate;
105 u64 iterator = 0;
106 auto commit = commits.begin();
107 while (iterator + size <= allocation_size) {
108 candidate = candidate.value_or(iterator);
109 if (commit == commits.end()) {
110 break;
111 }
112 if (commit->Contains(*candidate, size)) {
113 candidate = std::nullopt;
114 }
115 iterator = Common::AlignUpLog2(commit->end, alignment_log2);
116 ++commit;
117 }
118 return candidate;
119 }
120
121 const Device& device; ///< Vulkan device.
122 const vk::DeviceMemory memory; ///< Vulkan memory allocation handler.
123 const u64 allocation_size; ///< Size of this allocation.
124 const VkMemoryPropertyFlags property_flags; ///< Vulkan memory property flags.
125 const u32 shifted_memory_type; ///< Shifted Vulkan memory type.
126 std::vector<Range> commits; ///< All commit ranges done from this allocation.
127 std::span<u8> memory_mapped_span; ///< Memory mapped span. Empty if not queried before.
128};
129
130MemoryCommit::MemoryCommit(MemoryAllocation* allocation_, VkDeviceMemory memory_, u64 begin_,
131 u64 end_) noexcept
132 : allocation{allocation_}, memory{memory_}, begin{begin_}, end{end_} {}
133
134MemoryCommit::~MemoryCommit() {
135 Release();
136}
137
138MemoryCommit& MemoryCommit::operator=(MemoryCommit&& rhs) noexcept {
139 Release();
140 allocation = std::exchange(rhs.allocation, nullptr);
141 memory = rhs.memory;
142 begin = rhs.begin;
143 end = rhs.end;
144 span = std::exchange(rhs.span, std::span<u8>{});
145 return *this;
146}
147
148MemoryCommit::MemoryCommit(MemoryCommit&& rhs) noexcept
149 : allocation{std::exchange(rhs.allocation, nullptr)}, memory{rhs.memory}, begin{rhs.begin},
150 end{rhs.end}, span{std::exchange(rhs.span, std::span<u8>{})} {}
151
152std::span<u8> MemoryCommit::Map() {
153 if (span.empty()) {
154 span = allocation->Map().subspan(begin, end - begin);
155 }
156 return span;
157}
158
159void MemoryCommit::Release() {
160 if (allocation) {
161 allocation->Free(begin);
162 }
163}
164
165MemoryAllocator::MemoryAllocator(const Device& device_)
166 : device{device_}, properties{device_.GetPhysical().GetMemoryProperties()} {}
167
168MemoryAllocator::~MemoryAllocator() = default;
169
170MemoryCommit MemoryAllocator::Commit(const VkMemoryRequirements& requirements, MemoryUsage usage) {
171 // Find the fastest memory flags we can afford with the current requirements
172 const VkMemoryPropertyFlags flags = MemoryPropertyFlags(requirements.memoryTypeBits, usage);
173 if (std::optional<MemoryCommit> commit = TryCommit(requirements, flags)) {
174 return std::move(*commit);
175 }
176 // Commit has failed, allocate more memory.
177 // TODO(Rodrigo): Handle out of memory situations in some way like flushing to guest memory.
178 AllocMemory(flags, requirements.memoryTypeBits, AllocationChunkSize(requirements.size));
179
180 // Commit again, this time it won't fail since there's a fresh allocation above.
181 // If it does, there's a bug.
182 return TryCommit(requirements, flags).value();
183}
184
185MemoryCommit MemoryAllocator::Commit(const vk::Buffer& buffer, MemoryUsage usage) {
186 auto commit = Commit(device.GetLogical().GetBufferMemoryRequirements(*buffer), usage);
187 buffer.BindMemory(commit.Memory(), commit.Offset());
188 return commit;
189}
190
191MemoryCommit MemoryAllocator::Commit(const vk::Image& image, MemoryUsage usage) {
192 auto commit = Commit(device.GetLogical().GetImageMemoryRequirements(*image), usage);
193 image.BindMemory(commit.Memory(), commit.Offset());
194 return commit;
195}
196
197void MemoryAllocator::AllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size) {
198 const u32 type = FindType(flags, type_mask).value();
199 vk::DeviceMemory memory = device.GetLogical().AllocateMemory({
200 .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
201 .pNext = nullptr,
202 .allocationSize = size,
203 .memoryTypeIndex = type,
204 });
205 allocations.push_back(
206 std::make_unique<MemoryAllocation>(device, std::move(memory), flags, size, type));
207}
208
209std::optional<MemoryCommit> MemoryAllocator::TryCommit(const VkMemoryRequirements& requirements,
210 VkMemoryPropertyFlags flags) {
211 for (auto& allocation : allocations) {
212 if (!allocation->IsCompatible(flags, requirements.memoryTypeBits)) {
213 continue;
214 }
215 if (auto commit = allocation->Commit(requirements.size, requirements.alignment)) {
216 return commit;
217 }
218 }
219 return std::nullopt;
220}
221
222VkMemoryPropertyFlags MemoryAllocator::MemoryPropertyFlags(u32 type_mask, MemoryUsage usage) const {
223 return MemoryPropertyFlags(type_mask, MemoryUsagePropertyFlags(usage));
224}
225
226VkMemoryPropertyFlags MemoryAllocator::MemoryPropertyFlags(u32 type_mask,
227 VkMemoryPropertyFlags flags) const {
228 if (FindType(flags, type_mask)) {
229 // Found a memory type with those requirements
230 return flags;
231 }
232 if (flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) {
233 // Remove host cached bit in case it's not supported
234 return MemoryPropertyFlags(type_mask, flags & ~VK_MEMORY_PROPERTY_HOST_CACHED_BIT);
235 }
236 if (flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {
237 // Remove device local, if it's not supported by the requested resource
238 return MemoryPropertyFlags(type_mask, flags & ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
239 }
240 UNREACHABLE_MSG("No compatible memory types found");
241 return 0;
242}
243
244std::optional<u32> MemoryAllocator::FindType(VkMemoryPropertyFlags flags, u32 type_mask) const {
245 for (u32 type_index = 0; type_index < properties.memoryTypeCount; ++type_index) {
246 const VkMemoryPropertyFlags type_flags = properties.memoryTypes[type_index].propertyFlags;
247 if ((type_mask & (1U << type_index)) && (type_flags & flags)) {
248 // The type matches in type and in the wanted properties.
249 return type_index;
250 }
251 }
252 // Failed to find index
253 return std::nullopt;
254}
255
256bool IsHostVisible(MemoryUsage usage) noexcept {
257 switch (usage) {
258 case MemoryUsage::DeviceLocal:
259 return false;
260 case MemoryUsage::Upload:
261 case MemoryUsage::Download:
262 return true;
263 }
264 UNREACHABLE_MSG("Invalid memory usage={}", usage);
265 return false;
266}
267
268} // namespace Vulkan
diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.h b/src/video_core/vulkan_common/vulkan_memory_allocator.h
new file mode 100644
index 000000000..53b3b275a
--- /dev/null
+++ b/src/video_core/vulkan_common/vulkan_memory_allocator.h
@@ -0,0 +1,118 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <span>
9#include <utility>
10#include <vector>
11#include "common/common_types.h"
12#include "video_core/vulkan_common/vulkan_wrapper.h"
13
14namespace Vulkan {
15
16class Device;
17class MemoryMap;
18class MemoryAllocation;
19
20/// Hints and requirements for the backing memory type of a commit
21enum class MemoryUsage {
22 DeviceLocal, ///< Hints device local usages, fastest memory type to read and write from the GPU
23 Upload, ///< Requires a host visible memory type optimized for CPU to GPU uploads
24 Download, ///< Requires a host visible memory type optimized for GPU to CPU readbacks
25};
26
27/// Ownership handle of a memory commitment.
28/// Points to a subregion of a memory allocation.
29class MemoryCommit {
30public:
31 explicit MemoryCommit() noexcept = default;
32 explicit MemoryCommit(MemoryAllocation* allocation_, VkDeviceMemory memory_, u64 begin_,
33 u64 end_) noexcept;
34 ~MemoryCommit();
35
36 MemoryCommit& operator=(MemoryCommit&&) noexcept;
37 MemoryCommit(MemoryCommit&&) noexcept;
38
39 MemoryCommit& operator=(const MemoryCommit&) = delete;
40 MemoryCommit(const MemoryCommit&) = delete;
41
42 /// Returns a host visible memory map.
43 /// It will map the backing allocation if it hasn't been mapped before.
44 std::span<u8> Map();
45
46 /// Returns the Vulkan memory handler.
47 VkDeviceMemory Memory() const {
48 return memory;
49 }
50
51 /// Returns the start position of the commit relative to the allocation.
52 VkDeviceSize Offset() const {
53 return static_cast<VkDeviceSize>(begin);
54 }
55
56private:
57 void Release();
58
59 MemoryAllocation* allocation{}; ///< Pointer to the large memory allocation.
60 VkDeviceMemory memory{}; ///< Vulkan device memory handler.
61 u64 begin{}; ///< Beginning offset in bytes to where the commit exists.
62 u64 end{}; ///< Offset in bytes where the commit ends.
63 std::span<u8> span; ///< Host visible memory span. Empty if not queried before.
64};
65
66/// Memory allocator container.
67/// Allocates and releases memory allocations on demand.
68class MemoryAllocator {
69public:
70 explicit MemoryAllocator(const Device& device_);
71 ~MemoryAllocator();
72
73 MemoryAllocator& operator=(const MemoryAllocator&) = delete;
74 MemoryAllocator(const MemoryAllocator&) = delete;
75
76 /**
77 * Commits a memory with the specified requeriments.
78 *
79 * @param requirements Requirements returned from a Vulkan call.
80 * @param host_visible Signals the allocator that it *must* use host visible and coherent
81 * memory. When passing false, it will try to allocate device local memory.
82 *
83 * @returns A memory commit.
84 */
85 MemoryCommit Commit(const VkMemoryRequirements& requirements, MemoryUsage usage);
86
87 /// Commits memory required by the buffer and binds it.
88 MemoryCommit Commit(const vk::Buffer& buffer, MemoryUsage usage);
89
90 /// Commits memory required by the image and binds it.
91 MemoryCommit Commit(const vk::Image& image, MemoryUsage usage);
92
93private:
94 /// Allocates a chunk of memory.
95 void AllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size);
96
97 /// Tries to allocate a memory commit.
98 std::optional<MemoryCommit> TryCommit(const VkMemoryRequirements& requirements,
99 VkMemoryPropertyFlags flags);
100
101 /// Returns the fastest compatible memory property flags from a wanted usage.
102 VkMemoryPropertyFlags MemoryPropertyFlags(u32 type_mask, MemoryUsage usage) const;
103
104 /// Returns the fastest compatible memory property flags from the wanted flags.
105 VkMemoryPropertyFlags MemoryPropertyFlags(u32 type_mask, VkMemoryPropertyFlags flags) const;
106
107 /// Returns index to the fastest memory type compatible with the passed requirements.
108 std::optional<u32> FindType(VkMemoryPropertyFlags flags, u32 type_mask) const;
109
110 const Device& device; ///< Device handle.
111 const VkPhysicalDeviceMemoryProperties properties; ///< Physical device properties.
112 std::vector<std::unique_ptr<MemoryAllocation>> allocations; ///< Current allocations.
113};
114
115/// Returns true when a memory usage is guaranteed to be host visible.
116bool IsHostVisible(MemoryUsage usage) noexcept;
117
118} // namespace Vulkan
diff --git a/src/video_core/vulkan_common/vulkan_surface.cpp b/src/video_core/vulkan_common/vulkan_surface.cpp
new file mode 100644
index 000000000..3c3238f96
--- /dev/null
+++ b/src/video_core/vulkan_common/vulkan_surface.cpp
@@ -0,0 +1,81 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/logging/log.h"
6#include "core/frontend/emu_window.h"
7#include "video_core/vulkan_common/vulkan_surface.h"
8#include "video_core/vulkan_common/vulkan_wrapper.h"
9
10// Include these late to avoid polluting previous headers
11#ifdef _WIN32
12#include <windows.h>
13// ensure include order
14#include <vulkan/vulkan_win32.h>
15#endif
16
17#if !defined(_WIN32) && !defined(__APPLE__)
18#include <X11/Xlib.h>
19#include <vulkan/vulkan_wayland.h>
20#include <vulkan/vulkan_xlib.h>
21#endif
22
23namespace Vulkan {
24
25vk::SurfaceKHR CreateSurface(const vk::Instance& instance,
26 const Core::Frontend::EmuWindow& emu_window) {
27 [[maybe_unused]] const vk::InstanceDispatch& dld = instance.Dispatch();
28 [[maybe_unused]] const auto& window_info = emu_window.GetWindowInfo();
29 VkSurfaceKHR unsafe_surface = nullptr;
30
31#ifdef _WIN32
32 if (window_info.type == Core::Frontend::WindowSystemType::Windows) {
33 const HWND hWnd = static_cast<HWND>(window_info.render_surface);
34 const VkWin32SurfaceCreateInfoKHR win32_ci{VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR,
35 nullptr, 0, nullptr, hWnd};
36 const auto vkCreateWin32SurfaceKHR = reinterpret_cast<PFN_vkCreateWin32SurfaceKHR>(
37 dld.vkGetInstanceProcAddr(*instance, "vkCreateWin32SurfaceKHR"));
38 if (!vkCreateWin32SurfaceKHR ||
39 vkCreateWin32SurfaceKHR(*instance, &win32_ci, nullptr, &unsafe_surface) != VK_SUCCESS) {
40 LOG_ERROR(Render_Vulkan, "Failed to initialize Win32 surface");
41 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
42 }
43 }
44#endif
45#if !defined(_WIN32) && !defined(__APPLE__)
46 if (window_info.type == Core::Frontend::WindowSystemType::X11) {
47 const VkXlibSurfaceCreateInfoKHR xlib_ci{
48 VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, nullptr, 0,
49 static_cast<Display*>(window_info.display_connection),
50 reinterpret_cast<Window>(window_info.render_surface)};
51 const auto vkCreateXlibSurfaceKHR = reinterpret_cast<PFN_vkCreateXlibSurfaceKHR>(
52 dld.vkGetInstanceProcAddr(*instance, "vkCreateXlibSurfaceKHR"));
53 if (!vkCreateXlibSurfaceKHR ||
54 vkCreateXlibSurfaceKHR(*instance, &xlib_ci, nullptr, &unsafe_surface) != VK_SUCCESS) {
55 LOG_ERROR(Render_Vulkan, "Failed to initialize Xlib surface");
56 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
57 }
58 }
59 if (window_info.type == Core::Frontend::WindowSystemType::Wayland) {
60 const VkWaylandSurfaceCreateInfoKHR wayland_ci{
61 VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR, nullptr, 0,
62 static_cast<wl_display*>(window_info.display_connection),
63 static_cast<wl_surface*>(window_info.render_surface)};
64 const auto vkCreateWaylandSurfaceKHR = reinterpret_cast<PFN_vkCreateWaylandSurfaceKHR>(
65 dld.vkGetInstanceProcAddr(*instance, "vkCreateWaylandSurfaceKHR"));
66 if (!vkCreateWaylandSurfaceKHR ||
67 vkCreateWaylandSurfaceKHR(*instance, &wayland_ci, nullptr, &unsafe_surface) !=
68 VK_SUCCESS) {
69 LOG_ERROR(Render_Vulkan, "Failed to initialize Wayland surface");
70 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
71 }
72 }
73#endif
74 if (!unsafe_surface) {
75 LOG_ERROR(Render_Vulkan, "Presentation not supported on this platform");
76 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
77 }
78 return vk::SurfaceKHR(unsafe_surface, *instance, dld);
79}
80
81} // namespace Vulkan
diff --git a/src/video_core/vulkan_common/vulkan_surface.h b/src/video_core/vulkan_common/vulkan_surface.h
new file mode 100644
index 000000000..05a169e32
--- /dev/null
+++ b/src/video_core/vulkan_common/vulkan_surface.h
@@ -0,0 +1,18 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "video_core/vulkan_common/vulkan_wrapper.h"
8
9namespace Core::Frontend {
10class EmuWindow;
11}
12
13namespace Vulkan {
14
15[[nodiscard]] vk::SurfaceKHR CreateSurface(const vk::Instance& instance,
16 const Core::Frontend::EmuWindow& emu_window);
17
18} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp
index 2a21e850d..5e15ad607 100644
--- a/src/video_core/renderer_vulkan/wrapper.cpp
+++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp
@@ -13,7 +13,7 @@
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "common/logging/log.h" 14#include "common/logging/log.h"
15 15
16#include "video_core/renderer_vulkan/wrapper.h" 16#include "video_core/vulkan_common/vulkan_wrapper.h"
17 17
18namespace Vulkan::vk { 18namespace Vulkan::vk {
19 19
@@ -435,7 +435,7 @@ VkResult Free(VkDevice device, VkCommandPool handle, Span<VkCommandBuffer> buffe
435} 435}
436 436
437Instance Instance::Create(u32 version, Span<const char*> layers, Span<const char*> extensions, 437Instance Instance::Create(u32 version, Span<const char*> layers, Span<const char*> extensions,
438 InstanceDispatch& dispatch) noexcept { 438 InstanceDispatch& dispatch) {
439 const VkApplicationInfo application_info{ 439 const VkApplicationInfo application_info{
440 .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, 440 .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
441 .pNext = nullptr, 441 .pNext = nullptr,
@@ -455,55 +455,30 @@ Instance Instance::Create(u32 version, Span<const char*> layers, Span<const char
455 .enabledExtensionCount = extensions.size(), 455 .enabledExtensionCount = extensions.size(),
456 .ppEnabledExtensionNames = extensions.data(), 456 .ppEnabledExtensionNames = extensions.data(),
457 }; 457 };
458
459 VkInstance instance; 458 VkInstance instance;
460 if (dispatch.vkCreateInstance(&ci, nullptr, &instance) != VK_SUCCESS) { 459 Check(dispatch.vkCreateInstance(&ci, nullptr, &instance));
461 // Failed to create the instance.
462 return {};
463 }
464 if (!Proc(dispatch.vkDestroyInstance, dispatch, "vkDestroyInstance", instance)) { 460 if (!Proc(dispatch.vkDestroyInstance, dispatch, "vkDestroyInstance", instance)) {
465 // We successfully created an instance but the destroy function couldn't be loaded. 461 // We successfully created an instance but the destroy function couldn't be loaded.
466 // This is a good moment to panic. 462 // This is a good moment to panic.
467 return {}; 463 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
468 } 464 }
469
470 return Instance(instance, dispatch); 465 return Instance(instance, dispatch);
471} 466}
472 467
473std::optional<std::vector<VkPhysicalDevice>> Instance::EnumeratePhysicalDevices() { 468std::vector<VkPhysicalDevice> Instance::EnumeratePhysicalDevices() const {
474 u32 num; 469 u32 num;
475 if (dld->vkEnumeratePhysicalDevices(handle, &num, nullptr) != VK_SUCCESS) { 470 Check(dld->vkEnumeratePhysicalDevices(handle, &num, nullptr));
476 return std::nullopt;
477 }
478 std::vector<VkPhysicalDevice> physical_devices(num); 471 std::vector<VkPhysicalDevice> physical_devices(num);
479 if (dld->vkEnumeratePhysicalDevices(handle, &num, physical_devices.data()) != VK_SUCCESS) { 472 Check(dld->vkEnumeratePhysicalDevices(handle, &num, physical_devices.data()));
480 return std::nullopt;
481 }
482 SortPhysicalDevices(physical_devices, *dld); 473 SortPhysicalDevices(physical_devices, *dld);
483 return std::make_optional(std::move(physical_devices)); 474 return physical_devices;
484} 475}
485 476
486DebugCallback Instance::TryCreateDebugCallback( 477DebugUtilsMessenger Instance::CreateDebugUtilsMessenger(
487 PFN_vkDebugUtilsMessengerCallbackEXT callback) noexcept { 478 const VkDebugUtilsMessengerCreateInfoEXT& create_info) const {
488 const VkDebugUtilsMessengerCreateInfoEXT ci{ 479 VkDebugUtilsMessengerEXT object;
489 .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, 480 Check(dld->vkCreateDebugUtilsMessengerEXT(handle, &create_info, nullptr, &object));
490 .pNext = nullptr, 481 return DebugUtilsMessenger(object, handle, *dld);
491 .flags = 0,
492 .messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT |
493 VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
494 VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
495 VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT,
496 .messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
497 VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT,
498 .pfnUserCallback = callback,
499 .pUserData = nullptr,
500 };
501
502 VkDebugUtilsMessengerEXT messenger;
503 if (dld->vkCreateDebugUtilsMessengerEXT(handle, &ci, nullptr, &messenger) != VK_SUCCESS) {
504 return {};
505 }
506 return DebugCallback(messenger, handle, *dld);
507} 482}
508 483
509void Buffer::BindMemory(VkDeviceMemory memory, VkDeviceSize offset) const { 484void Buffer::BindMemory(VkDeviceMemory memory, VkDeviceSize offset) const {
@@ -605,7 +580,7 @@ void Semaphore::SetObjectNameEXT(const char* name) const {
605 580
606Device Device::Create(VkPhysicalDevice physical_device, Span<VkDeviceQueueCreateInfo> queues_ci, 581Device Device::Create(VkPhysicalDevice physical_device, Span<VkDeviceQueueCreateInfo> queues_ci,
607 Span<const char*> enabled_extensions, const void* next, 582 Span<const char*> enabled_extensions, const void* next,
608 DeviceDispatch& dispatch) noexcept { 583 DeviceDispatch& dispatch) {
609 const VkDeviceCreateInfo ci{ 584 const VkDeviceCreateInfo ci{
610 .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, 585 .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
611 .pNext = next, 586 .pNext = next,
@@ -618,11 +593,8 @@ Device Device::Create(VkPhysicalDevice physical_device, Span<VkDeviceQueueCreate
618 .ppEnabledExtensionNames = enabled_extensions.data(), 593 .ppEnabledExtensionNames = enabled_extensions.data(),
619 .pEnabledFeatures = nullptr, 594 .pEnabledFeatures = nullptr,
620 }; 595 };
621
622 VkDevice device; 596 VkDevice device;
623 if (dispatch.vkCreateDevice(physical_device, &ci, nullptr, &device) != VK_SUCCESS) { 597 Check(dispatch.vkCreateDevice(physical_device, &ci, nullptr, &device));
624 return {};
625 }
626 Load(device, dispatch); 598 Load(device, dispatch);
627 return Device(device, dispatch); 599 return Device(device, dispatch);
628} 600}
diff --git a/src/video_core/renderer_vulkan/wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h
index f9a184e00..9689de0cb 100644
--- a/src/video_core/renderer_vulkan/wrapper.h
+++ b/src/video_core/vulkan_common/vulkan_wrapper.h
@@ -144,152 +144,152 @@ inline VkResult Filter(VkResult result) {
144 144
145/// Table holding Vulkan instance function pointers. 145/// Table holding Vulkan instance function pointers.
146struct InstanceDispatch { 146struct InstanceDispatch {
147 PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; 147 PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr{};
148 148
149 PFN_vkCreateInstance vkCreateInstance; 149 PFN_vkCreateInstance vkCreateInstance{};
150 PFN_vkDestroyInstance vkDestroyInstance; 150 PFN_vkDestroyInstance vkDestroyInstance{};
151 PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties; 151 PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties{};
152 PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties; 152 PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties{};
153 153
154 PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT; 154 PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT{};
155 PFN_vkCreateDevice vkCreateDevice; 155 PFN_vkCreateDevice vkCreateDevice{};
156 PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT; 156 PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT{};
157 PFN_vkDestroyDevice vkDestroyDevice; 157 PFN_vkDestroyDevice vkDestroyDevice{};
158 PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR; 158 PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR{};
159 PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties; 159 PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties{};
160 PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices; 160 PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices{};
161 PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr; 161 PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr{};
162 PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR; 162 PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR{};
163 PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties; 163 PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties{};
164 PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties; 164 PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties{};
165 PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties; 165 PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties{};
166 PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR; 166 PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR{};
167 PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties; 167 PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties{};
168 PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR; 168 PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR{};
169 PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR; 169 PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR{};
170 PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR; 170 PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR{};
171 PFN_vkGetPhysicalDeviceSurfaceSupportKHR vkGetPhysicalDeviceSurfaceSupportKHR; 171 PFN_vkGetPhysicalDeviceSurfaceSupportKHR vkGetPhysicalDeviceSurfaceSupportKHR{};
172 PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR; 172 PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR{};
173 PFN_vkQueuePresentKHR vkQueuePresentKHR; 173 PFN_vkQueuePresentKHR vkQueuePresentKHR{};
174}; 174};
175 175
176/// Table holding Vulkan device function pointers. 176/// Table holding Vulkan device function pointers.
177struct DeviceDispatch : public InstanceDispatch { 177struct DeviceDispatch : public InstanceDispatch {
178 PFN_vkAcquireNextImageKHR vkAcquireNextImageKHR; 178 PFN_vkAcquireNextImageKHR vkAcquireNextImageKHR{};
179 PFN_vkAllocateCommandBuffers vkAllocateCommandBuffers; 179 PFN_vkAllocateCommandBuffers vkAllocateCommandBuffers{};
180 PFN_vkAllocateDescriptorSets vkAllocateDescriptorSets; 180 PFN_vkAllocateDescriptorSets vkAllocateDescriptorSets{};
181 PFN_vkAllocateMemory vkAllocateMemory; 181 PFN_vkAllocateMemory vkAllocateMemory{};
182 PFN_vkBeginCommandBuffer vkBeginCommandBuffer; 182 PFN_vkBeginCommandBuffer vkBeginCommandBuffer{};
183 PFN_vkBindBufferMemory vkBindBufferMemory; 183 PFN_vkBindBufferMemory vkBindBufferMemory{};
184 PFN_vkBindImageMemory vkBindImageMemory; 184 PFN_vkBindImageMemory vkBindImageMemory{};
185 PFN_vkCmdBeginQuery vkCmdBeginQuery; 185 PFN_vkCmdBeginQuery vkCmdBeginQuery{};
186 PFN_vkCmdBeginRenderPass vkCmdBeginRenderPass; 186 PFN_vkCmdBeginRenderPass vkCmdBeginRenderPass{};
187 PFN_vkCmdBeginTransformFeedbackEXT vkCmdBeginTransformFeedbackEXT; 187 PFN_vkCmdBeginTransformFeedbackEXT vkCmdBeginTransformFeedbackEXT{};
188 PFN_vkCmdBeginDebugUtilsLabelEXT vkCmdBeginDebugUtilsLabelEXT; 188 PFN_vkCmdBeginDebugUtilsLabelEXT vkCmdBeginDebugUtilsLabelEXT{};
189 PFN_vkCmdBindDescriptorSets vkCmdBindDescriptorSets; 189 PFN_vkCmdBindDescriptorSets vkCmdBindDescriptorSets{};
190 PFN_vkCmdBindIndexBuffer vkCmdBindIndexBuffer; 190 PFN_vkCmdBindIndexBuffer vkCmdBindIndexBuffer{};
191 PFN_vkCmdBindPipeline vkCmdBindPipeline; 191 PFN_vkCmdBindPipeline vkCmdBindPipeline{};
192 PFN_vkCmdBindTransformFeedbackBuffersEXT vkCmdBindTransformFeedbackBuffersEXT; 192 PFN_vkCmdBindTransformFeedbackBuffersEXT vkCmdBindTransformFeedbackBuffersEXT{};
193 PFN_vkCmdBindVertexBuffers vkCmdBindVertexBuffers; 193 PFN_vkCmdBindVertexBuffers vkCmdBindVertexBuffers{};
194 PFN_vkCmdBlitImage vkCmdBlitImage; 194 PFN_vkCmdBlitImage vkCmdBlitImage{};
195 PFN_vkCmdClearAttachments vkCmdClearAttachments; 195 PFN_vkCmdClearAttachments vkCmdClearAttachments{};
196 PFN_vkCmdCopyBuffer vkCmdCopyBuffer; 196 PFN_vkCmdCopyBuffer vkCmdCopyBuffer{};
197 PFN_vkCmdCopyBufferToImage vkCmdCopyBufferToImage; 197 PFN_vkCmdCopyBufferToImage vkCmdCopyBufferToImage{};
198 PFN_vkCmdCopyImage vkCmdCopyImage; 198 PFN_vkCmdCopyImage vkCmdCopyImage{};
199 PFN_vkCmdCopyImageToBuffer vkCmdCopyImageToBuffer; 199 PFN_vkCmdCopyImageToBuffer vkCmdCopyImageToBuffer{};
200 PFN_vkCmdDispatch vkCmdDispatch; 200 PFN_vkCmdDispatch vkCmdDispatch{};
201 PFN_vkCmdDraw vkCmdDraw; 201 PFN_vkCmdDraw vkCmdDraw{};
202 PFN_vkCmdDrawIndexed vkCmdDrawIndexed; 202 PFN_vkCmdDrawIndexed vkCmdDrawIndexed{};
203 PFN_vkCmdEndQuery vkCmdEndQuery; 203 PFN_vkCmdEndQuery vkCmdEndQuery{};
204 PFN_vkCmdEndRenderPass vkCmdEndRenderPass; 204 PFN_vkCmdEndRenderPass vkCmdEndRenderPass{};
205 PFN_vkCmdEndTransformFeedbackEXT vkCmdEndTransformFeedbackEXT; 205 PFN_vkCmdEndTransformFeedbackEXT vkCmdEndTransformFeedbackEXT{};
206 PFN_vkCmdEndDebugUtilsLabelEXT vkCmdEndDebugUtilsLabelEXT; 206 PFN_vkCmdEndDebugUtilsLabelEXT vkCmdEndDebugUtilsLabelEXT{};
207 PFN_vkCmdFillBuffer vkCmdFillBuffer; 207 PFN_vkCmdFillBuffer vkCmdFillBuffer{};
208 PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier; 208 PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier{};
209 PFN_vkCmdPushConstants vkCmdPushConstants; 209 PFN_vkCmdPushConstants vkCmdPushConstants{};
210 PFN_vkCmdSetBlendConstants vkCmdSetBlendConstants; 210 PFN_vkCmdSetBlendConstants vkCmdSetBlendConstants{};
211 PFN_vkCmdSetDepthBias vkCmdSetDepthBias; 211 PFN_vkCmdSetDepthBias vkCmdSetDepthBias{};
212 PFN_vkCmdSetDepthBounds vkCmdSetDepthBounds; 212 PFN_vkCmdSetDepthBounds vkCmdSetDepthBounds{};
213 PFN_vkCmdSetEvent vkCmdSetEvent; 213 PFN_vkCmdSetEvent vkCmdSetEvent{};
214 PFN_vkCmdSetScissor vkCmdSetScissor; 214 PFN_vkCmdSetScissor vkCmdSetScissor{};
215 PFN_vkCmdSetStencilCompareMask vkCmdSetStencilCompareMask; 215 PFN_vkCmdSetStencilCompareMask vkCmdSetStencilCompareMask{};
216 PFN_vkCmdSetStencilReference vkCmdSetStencilReference; 216 PFN_vkCmdSetStencilReference vkCmdSetStencilReference{};
217 PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask; 217 PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask{};
218 PFN_vkCmdSetViewport vkCmdSetViewport; 218 PFN_vkCmdSetViewport vkCmdSetViewport{};
219 PFN_vkCmdWaitEvents vkCmdWaitEvents; 219 PFN_vkCmdWaitEvents vkCmdWaitEvents{};
220 PFN_vkCmdBindVertexBuffers2EXT vkCmdBindVertexBuffers2EXT; 220 PFN_vkCmdBindVertexBuffers2EXT vkCmdBindVertexBuffers2EXT{};
221 PFN_vkCmdSetCullModeEXT vkCmdSetCullModeEXT; 221 PFN_vkCmdSetCullModeEXT vkCmdSetCullModeEXT{};
222 PFN_vkCmdSetDepthBoundsTestEnableEXT vkCmdSetDepthBoundsTestEnableEXT; 222 PFN_vkCmdSetDepthBoundsTestEnableEXT vkCmdSetDepthBoundsTestEnableEXT{};
223 PFN_vkCmdSetDepthCompareOpEXT vkCmdSetDepthCompareOpEXT; 223 PFN_vkCmdSetDepthCompareOpEXT vkCmdSetDepthCompareOpEXT{};
224 PFN_vkCmdSetDepthTestEnableEXT vkCmdSetDepthTestEnableEXT; 224 PFN_vkCmdSetDepthTestEnableEXT vkCmdSetDepthTestEnableEXT{};
225 PFN_vkCmdSetDepthWriteEnableEXT vkCmdSetDepthWriteEnableEXT; 225 PFN_vkCmdSetDepthWriteEnableEXT vkCmdSetDepthWriteEnableEXT{};
226 PFN_vkCmdSetFrontFaceEXT vkCmdSetFrontFaceEXT; 226 PFN_vkCmdSetFrontFaceEXT vkCmdSetFrontFaceEXT{};
227 PFN_vkCmdSetPrimitiveTopologyEXT vkCmdSetPrimitiveTopologyEXT; 227 PFN_vkCmdSetPrimitiveTopologyEXT vkCmdSetPrimitiveTopologyEXT{};
228 PFN_vkCmdSetStencilOpEXT vkCmdSetStencilOpEXT; 228 PFN_vkCmdSetStencilOpEXT vkCmdSetStencilOpEXT{};
229 PFN_vkCmdSetStencilTestEnableEXT vkCmdSetStencilTestEnableEXT; 229 PFN_vkCmdSetStencilTestEnableEXT vkCmdSetStencilTestEnableEXT{};
230 PFN_vkCmdResolveImage vkCmdResolveImage; 230 PFN_vkCmdResolveImage vkCmdResolveImage{};
231 PFN_vkCreateBuffer vkCreateBuffer; 231 PFN_vkCreateBuffer vkCreateBuffer{};
232 PFN_vkCreateBufferView vkCreateBufferView; 232 PFN_vkCreateBufferView vkCreateBufferView{};
233 PFN_vkCreateCommandPool vkCreateCommandPool; 233 PFN_vkCreateCommandPool vkCreateCommandPool{};
234 PFN_vkCreateComputePipelines vkCreateComputePipelines; 234 PFN_vkCreateComputePipelines vkCreateComputePipelines{};
235 PFN_vkCreateDescriptorPool vkCreateDescriptorPool; 235 PFN_vkCreateDescriptorPool vkCreateDescriptorPool{};
236 PFN_vkCreateDescriptorSetLayout vkCreateDescriptorSetLayout; 236 PFN_vkCreateDescriptorSetLayout vkCreateDescriptorSetLayout{};
237 PFN_vkCreateDescriptorUpdateTemplateKHR vkCreateDescriptorUpdateTemplateKHR; 237 PFN_vkCreateDescriptorUpdateTemplateKHR vkCreateDescriptorUpdateTemplateKHR{};
238 PFN_vkCreateEvent vkCreateEvent; 238 PFN_vkCreateEvent vkCreateEvent{};
239 PFN_vkCreateFence vkCreateFence; 239 PFN_vkCreateFence vkCreateFence{};
240 PFN_vkCreateFramebuffer vkCreateFramebuffer; 240 PFN_vkCreateFramebuffer vkCreateFramebuffer{};
241 PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines; 241 PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines{};
242 PFN_vkCreateImage vkCreateImage; 242 PFN_vkCreateImage vkCreateImage{};
243 PFN_vkCreateImageView vkCreateImageView; 243 PFN_vkCreateImageView vkCreateImageView{};
244 PFN_vkCreatePipelineLayout vkCreatePipelineLayout; 244 PFN_vkCreatePipelineLayout vkCreatePipelineLayout{};
245 PFN_vkCreateQueryPool vkCreateQueryPool; 245 PFN_vkCreateQueryPool vkCreateQueryPool{};
246 PFN_vkCreateRenderPass vkCreateRenderPass; 246 PFN_vkCreateRenderPass vkCreateRenderPass{};
247 PFN_vkCreateSampler vkCreateSampler; 247 PFN_vkCreateSampler vkCreateSampler{};
248 PFN_vkCreateSemaphore vkCreateSemaphore; 248 PFN_vkCreateSemaphore vkCreateSemaphore{};
249 PFN_vkCreateShaderModule vkCreateShaderModule; 249 PFN_vkCreateShaderModule vkCreateShaderModule{};
250 PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR; 250 PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR{};
251 PFN_vkDestroyBuffer vkDestroyBuffer; 251 PFN_vkDestroyBuffer vkDestroyBuffer{};
252 PFN_vkDestroyBufferView vkDestroyBufferView; 252 PFN_vkDestroyBufferView vkDestroyBufferView{};
253 PFN_vkDestroyCommandPool vkDestroyCommandPool; 253 PFN_vkDestroyCommandPool vkDestroyCommandPool{};
254 PFN_vkDestroyDescriptorPool vkDestroyDescriptorPool; 254 PFN_vkDestroyDescriptorPool vkDestroyDescriptorPool{};
255 PFN_vkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout; 255 PFN_vkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout{};
256 PFN_vkDestroyDescriptorUpdateTemplateKHR vkDestroyDescriptorUpdateTemplateKHR; 256 PFN_vkDestroyDescriptorUpdateTemplateKHR vkDestroyDescriptorUpdateTemplateKHR{};
257 PFN_vkDestroyEvent vkDestroyEvent; 257 PFN_vkDestroyEvent vkDestroyEvent{};
258 PFN_vkDestroyFence vkDestroyFence; 258 PFN_vkDestroyFence vkDestroyFence{};
259 PFN_vkDestroyFramebuffer vkDestroyFramebuffer; 259 PFN_vkDestroyFramebuffer vkDestroyFramebuffer{};
260 PFN_vkDestroyImage vkDestroyImage; 260 PFN_vkDestroyImage vkDestroyImage{};
261 PFN_vkDestroyImageView vkDestroyImageView; 261 PFN_vkDestroyImageView vkDestroyImageView{};
262 PFN_vkDestroyPipeline vkDestroyPipeline; 262 PFN_vkDestroyPipeline vkDestroyPipeline{};
263 PFN_vkDestroyPipelineLayout vkDestroyPipelineLayout; 263 PFN_vkDestroyPipelineLayout vkDestroyPipelineLayout{};
264 PFN_vkDestroyQueryPool vkDestroyQueryPool; 264 PFN_vkDestroyQueryPool vkDestroyQueryPool{};
265 PFN_vkDestroyRenderPass vkDestroyRenderPass; 265 PFN_vkDestroyRenderPass vkDestroyRenderPass{};
266 PFN_vkDestroySampler vkDestroySampler; 266 PFN_vkDestroySampler vkDestroySampler{};
267 PFN_vkDestroySemaphore vkDestroySemaphore; 267 PFN_vkDestroySemaphore vkDestroySemaphore{};
268 PFN_vkDestroyShaderModule vkDestroyShaderModule; 268 PFN_vkDestroyShaderModule vkDestroyShaderModule{};
269 PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR; 269 PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR{};
270 PFN_vkDeviceWaitIdle vkDeviceWaitIdle; 270 PFN_vkDeviceWaitIdle vkDeviceWaitIdle{};
271 PFN_vkEndCommandBuffer vkEndCommandBuffer; 271 PFN_vkEndCommandBuffer vkEndCommandBuffer{};
272 PFN_vkFreeCommandBuffers vkFreeCommandBuffers; 272 PFN_vkFreeCommandBuffers vkFreeCommandBuffers{};
273 PFN_vkFreeDescriptorSets vkFreeDescriptorSets; 273 PFN_vkFreeDescriptorSets vkFreeDescriptorSets{};
274 PFN_vkFreeMemory vkFreeMemory; 274 PFN_vkFreeMemory vkFreeMemory{};
275 PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements; 275 PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements{};
276 PFN_vkGetDeviceQueue vkGetDeviceQueue; 276 PFN_vkGetDeviceQueue vkGetDeviceQueue{};
277 PFN_vkGetEventStatus vkGetEventStatus; 277 PFN_vkGetEventStatus vkGetEventStatus{};
278 PFN_vkGetFenceStatus vkGetFenceStatus; 278 PFN_vkGetFenceStatus vkGetFenceStatus{};
279 PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements; 279 PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements{};
280 PFN_vkGetQueryPoolResults vkGetQueryPoolResults; 280 PFN_vkGetQueryPoolResults vkGetQueryPoolResults{};
281 PFN_vkGetSemaphoreCounterValueKHR vkGetSemaphoreCounterValueKHR; 281 PFN_vkGetSemaphoreCounterValueKHR vkGetSemaphoreCounterValueKHR{};
282 PFN_vkMapMemory vkMapMemory; 282 PFN_vkMapMemory vkMapMemory{};
283 PFN_vkQueueSubmit vkQueueSubmit; 283 PFN_vkQueueSubmit vkQueueSubmit{};
284 PFN_vkResetFences vkResetFences; 284 PFN_vkResetFences vkResetFences{};
285 PFN_vkResetQueryPoolEXT vkResetQueryPoolEXT; 285 PFN_vkResetQueryPoolEXT vkResetQueryPoolEXT{};
286 PFN_vkSetDebugUtilsObjectNameEXT vkSetDebugUtilsObjectNameEXT; 286 PFN_vkSetDebugUtilsObjectNameEXT vkSetDebugUtilsObjectNameEXT{};
287 PFN_vkSetDebugUtilsObjectTagEXT vkSetDebugUtilsObjectTagEXT; 287 PFN_vkSetDebugUtilsObjectTagEXT vkSetDebugUtilsObjectTagEXT{};
288 PFN_vkUnmapMemory vkUnmapMemory; 288 PFN_vkUnmapMemory vkUnmapMemory{};
289 PFN_vkUpdateDescriptorSetWithTemplateKHR vkUpdateDescriptorSetWithTemplateKHR; 289 PFN_vkUpdateDescriptorSetWithTemplateKHR vkUpdateDescriptorSetWithTemplateKHR{};
290 PFN_vkUpdateDescriptorSets vkUpdateDescriptorSets; 290 PFN_vkUpdateDescriptorSets vkUpdateDescriptorSets{};
291 PFN_vkWaitForFences vkWaitForFences; 291 PFN_vkWaitForFences vkWaitForFences{};
292 PFN_vkWaitSemaphoresKHR vkWaitSemaphoresKHR; 292 PFN_vkWaitSemaphoresKHR vkWaitSemaphoresKHR{};
293}; 293};
294 294
295/// Loads instance agnostic function pointers. 295/// Loads instance agnostic function pointers.
@@ -555,7 +555,7 @@ private:
555 const DeviceDispatch* dld = nullptr; 555 const DeviceDispatch* dld = nullptr;
556}; 556};
557 557
558using DebugCallback = Handle<VkDebugUtilsMessengerEXT, VkInstance, InstanceDispatch>; 558using DebugUtilsMessenger = Handle<VkDebugUtilsMessengerEXT, VkInstance, InstanceDispatch>;
559using DescriptorSetLayout = Handle<VkDescriptorSetLayout, VkDevice, DeviceDispatch>; 559using DescriptorSetLayout = Handle<VkDescriptorSetLayout, VkDevice, DeviceDispatch>;
560using DescriptorUpdateTemplateKHR = Handle<VkDescriptorUpdateTemplateKHR, VkDevice, DeviceDispatch>; 560using DescriptorUpdateTemplateKHR = Handle<VkDescriptorUpdateTemplateKHR, VkDevice, DeviceDispatch>;
561using Pipeline = Handle<VkPipeline, VkDevice, DeviceDispatch>; 561using Pipeline = Handle<VkPipeline, VkDevice, DeviceDispatch>;
@@ -573,16 +573,25 @@ class Instance : public Handle<VkInstance, NoOwner, InstanceDispatch> {
573 using Handle<VkInstance, NoOwner, InstanceDispatch>::Handle; 573 using Handle<VkInstance, NoOwner, InstanceDispatch>::Handle;
574 574
575public: 575public:
576 /// Creates a Vulkan instance. Use "operator bool" for error handling. 576 /// Creates a Vulkan instance.
577 /// @throw Exception on initialization error.
577 static Instance Create(u32 version, Span<const char*> layers, Span<const char*> extensions, 578 static Instance Create(u32 version, Span<const char*> layers, Span<const char*> extensions,
578 InstanceDispatch& dispatch) noexcept; 579 InstanceDispatch& dispatch);
579 580
580 /// Enumerates physical devices. 581 /// Enumerates physical devices.
581 /// @return Physical devices and an empty handle on failure. 582 /// @return Physical devices and an empty handle on failure.
582 std::optional<std::vector<VkPhysicalDevice>> EnumeratePhysicalDevices(); 583 /// @throw Exception on Vulkan error.
584 std::vector<VkPhysicalDevice> EnumeratePhysicalDevices() const;
583 585
584 /// Tries to create a debug callback messenger. Returns an empty handle on failure. 586 /// Creates a debug callback messenger.
585 DebugCallback TryCreateDebugCallback(PFN_vkDebugUtilsMessengerCallbackEXT callback) noexcept; 587 /// @throw Exception on creation failure.
588 DebugUtilsMessenger CreateDebugUtilsMessenger(
589 const VkDebugUtilsMessengerCreateInfoEXT& create_info) const;
590
591 /// Returns dispatch table.
592 const InstanceDispatch& Dispatch() const noexcept {
593 return *dld;
594 }
586}; 595};
587 596
588class Queue { 597class Queue {
@@ -787,7 +796,7 @@ class Device : public Handle<VkDevice, NoOwner, DeviceDispatch> {
787public: 796public:
788 static Device Create(VkPhysicalDevice physical_device, Span<VkDeviceQueueCreateInfo> queues_ci, 797 static Device Create(VkPhysicalDevice physical_device, Span<VkDeviceQueueCreateInfo> queues_ci,
789 Span<const char*> enabled_extensions, const void* next, 798 Span<const char*> enabled_extensions, const void* next,
790 DeviceDispatch& dispatch) noexcept; 799 DeviceDispatch& dispatch);
791 800
792 Queue GetQueue(u32 family_index) const noexcept; 801 Queue GetQueue(u32 family_index) const noexcept;
793 802
diff --git a/src/yuzu/applets/controller.cpp b/src/yuzu/applets/controller.cpp
index a15e8ca2a..c680fd2c2 100644
--- a/src/yuzu/applets/controller.cpp
+++ b/src/yuzu/applets/controller.cpp
@@ -535,7 +535,7 @@ void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index)
535 // This emulates a delay between disconnecting and reconnecting controllers as some games 535 // This emulates a delay between disconnecting and reconnecting controllers as some games
536 // do not respond to a change in controller type if it was instantaneous. 536 // do not respond to a change in controller type if it was instantaneous.
537 using namespace std::chrono_literals; 537 using namespace std::chrono_literals;
538 std::this_thread::sleep_for(20ms); 538 std::this_thread::sleep_for(60ms);
539 539
540 UpdateController(controller_type, player_index, player_connected); 540 UpdateController(controller_type, player_index, player_connected);
541} 541}
diff --git a/src/yuzu/applets/error.cpp b/src/yuzu/applets/error.cpp
index 53a993cf6..8ee03ddb3 100644
--- a/src/yuzu/applets/error.cpp
+++ b/src/yuzu/applets/error.cpp
@@ -19,7 +19,7 @@ QtErrorDisplay::~QtErrorDisplay() = default;
19void QtErrorDisplay::ShowError(ResultCode error, std::function<void()> finished) const { 19void QtErrorDisplay::ShowError(ResultCode error, std::function<void()> finished) const {
20 callback = std::move(finished); 20 callback = std::move(finished);
21 emit MainWindowDisplayError( 21 emit MainWindowDisplayError(
22 tr("An error has occured.\nPlease try again or contact the developer of the " 22 tr("An error has occurred.\nPlease try again or contact the developer of the "
23 "software.\n\nError Code: %1-%2 (0x%3)") 23 "software.\n\nError Code: %1-%2 (0x%3)")
24 .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0')) 24 .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0'))
25 .arg(error.description, 4, 10, QChar::fromLatin1('0')) 25 .arg(error.description, 4, 10, QChar::fromLatin1('0'))
@@ -32,7 +32,7 @@ void QtErrorDisplay::ShowErrorWithTimestamp(ResultCode error, std::chrono::secon
32 32
33 const QDateTime date_time = QDateTime::fromSecsSinceEpoch(time.count()); 33 const QDateTime date_time = QDateTime::fromSecsSinceEpoch(time.count());
34 emit MainWindowDisplayError( 34 emit MainWindowDisplayError(
35 tr("An error occured on %1 at %2.\nPlease try again or contact the " 35 tr("An error occurred on %1 at %2.\nPlease try again or contact the "
36 "developer of the software.\n\nError Code: %3-%4 (0x%5)") 36 "developer of the software.\n\nError Code: %3-%4 (0x%5)")
37 .arg(date_time.toString(QStringLiteral("dddd, MMMM d, yyyy"))) 37 .arg(date_time.toString(QStringLiteral("dddd, MMMM d, yyyy")))
38 .arg(date_time.toString(QStringLiteral("h:mm:ss A"))) 38 .arg(date_time.toString(QStringLiteral("h:mm:ss A")))
@@ -46,7 +46,7 @@ void QtErrorDisplay::ShowCustomErrorText(ResultCode error, std::string dialog_te
46 std::function<void()> finished) const { 46 std::function<void()> finished) const {
47 callback = std::move(finished); 47 callback = std::move(finished);
48 emit MainWindowDisplayError( 48 emit MainWindowDisplayError(
49 tr("An error has occured.\nError Code: %1-%2 (0x%3)\n\n%4\n\n%5") 49 tr("An error has occurred.\nError Code: %1-%2 (0x%3)\n\n%4\n\n%5")
50 .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0')) 50 .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0'))
51 .arg(error.description, 4, 10, QChar::fromLatin1('0')) 51 .arg(error.description, 4, 10, QChar::fromLatin1('0'))
52 .arg(error.raw, 8, 16, QChar::fromLatin1('0')) 52 .arg(error.raw, 8, 16, QChar::fromLatin1('0'))
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index e124836b5..e6c8f18af 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -290,8 +290,8 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
290 QString::fromUtf8(Common::g_scm_branch), 290 QString::fromUtf8(Common::g_scm_branch),
291 QString::fromUtf8(Common::g_scm_desc))); 291 QString::fromUtf8(Common::g_scm_desc)));
292 setAttribute(Qt::WA_AcceptTouchEvents); 292 setAttribute(Qt::WA_AcceptTouchEvents);
293 auto layout = new QHBoxLayout(this); 293 auto* layout = new QHBoxLayout(this);
294 layout->setMargin(0); 294 layout->setContentsMargins(0, 0, 0, 0);
295 setLayout(layout); 295 setLayout(layout);
296 input_subsystem->Initialize(); 296 input_subsystem->Initialize();
297 297
@@ -397,7 +397,7 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) {
397 this->TouchPressed(x, y); 397 this->TouchPressed(x, y);
398 } 398 }
399 399
400 QWidget::mousePressEvent(event); 400 emit MouseActivity();
401} 401}
402 402
403void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { 403void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
@@ -411,7 +411,7 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
411 input_subsystem->GetMouse()->MouseMove(x, y); 411 input_subsystem->GetMouse()->MouseMove(x, y);
412 this->TouchMoved(x, y); 412 this->TouchMoved(x, y);
413 413
414 QWidget::mouseMoveEvent(event); 414 emit MouseActivity();
415} 415}
416 416
417void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { 417void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
@@ -688,3 +688,10 @@ void GRenderWindow::showEvent(QShowEvent* event) {
688 connect(windowHandle(), &QWindow::screenChanged, this, &GRenderWindow::OnFramebufferSizeChanged, 688 connect(windowHandle(), &QWindow::screenChanged, this, &GRenderWindow::OnFramebufferSizeChanged,
689 Qt::UniqueConnection); 689 Qt::UniqueConnection);
690} 690}
691
692bool GRenderWindow::eventFilter(QObject* object, QEvent* event) {
693 if (event->type() == QEvent::HoverMove) {
694 emit MouseActivity();
695 }
696 return false;
697}
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index ebe5cb965..339095509 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -184,6 +184,7 @@ signals:
184 void Closed(); 184 void Closed();
185 void FirstFrameDisplayed(); 185 void FirstFrameDisplayed();
186 void ExecuteProgramSignal(std::size_t program_index); 186 void ExecuteProgramSignal(std::size_t program_index);
187 void MouseActivity();
187 188
188private: 189private:
189 void TouchBeginEvent(const QTouchEvent* event); 190 void TouchBeginEvent(const QTouchEvent* event);
@@ -216,4 +217,5 @@ private:
216 217
217protected: 218protected:
218 void showEvent(QShowEvent* event) override; 219 void showEvent(QShowEvent* event) override;
220 bool eventFilter(QObject* object, QEvent* event) override;
219}; 221};
diff --git a/src/yuzu/compatdb.cpp b/src/yuzu/compatdb.cpp
index 649912557..a470056ef 100644
--- a/src/yuzu/compatdb.cpp
+++ b/src/yuzu/compatdb.cpp
@@ -72,7 +72,7 @@ void CompatDB::Submit() {
72void CompatDB::OnTestcaseSubmitted() { 72void CompatDB::OnTestcaseSubmitted() {
73 if (!testcase_watcher.result()) { 73 if (!testcase_watcher.result()) {
74 QMessageBox::critical(this, tr("Communication error"), 74 QMessageBox::critical(this, tr("Communication error"),
75 tr("An error occured while sending the Testcase")); 75 tr("An error occurred while sending the Testcase"));
76 button(NextButton)->setEnabled(true); 76 button(NextButton)->setEnabled(true);
77 button(NextButton)->setText(tr("Next")); 77 button(NextButton)->setText(tr("Next"));
78 button(CancelButton)->setVisible(true); 78 button(CancelButton)->setVisible(true);
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 34c2a5f8b..cda448718 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -514,7 +514,7 @@ void Config::ReadControlValues() {
514 Settings::values.emulate_analog_keyboard = 514 Settings::values.emulate_analog_keyboard =
515 ReadSetting(QStringLiteral("emulate_analog_keyboard"), false).toBool(); 515 ReadSetting(QStringLiteral("emulate_analog_keyboard"), false).toBool();
516 516
517 ReadSettingGlobal(Settings::values.use_docked_mode, QStringLiteral("use_docked_mode"), false); 517 ReadSettingGlobal(Settings::values.use_docked_mode, QStringLiteral("use_docked_mode"), true);
518 ReadSettingGlobal(Settings::values.vibration_enabled, QStringLiteral("vibration_enabled"), 518 ReadSettingGlobal(Settings::values.vibration_enabled, QStringLiteral("vibration_enabled"),
519 true); 519 true);
520 ReadSettingGlobal(Settings::values.enable_accurate_vibrations, 520 ReadSettingGlobal(Settings::values.enable_accurate_vibrations,
@@ -764,6 +764,8 @@ void Config::ReadCpuValues() {
764 ReadSetting(QStringLiteral("cpuopt_unsafe_unfuse_fma"), true).toBool(); 764 ReadSetting(QStringLiteral("cpuopt_unsafe_unfuse_fma"), true).toBool();
765 Settings::values.cpuopt_unsafe_reduce_fp_error = 765 Settings::values.cpuopt_unsafe_reduce_fp_error =
766 ReadSetting(QStringLiteral("cpuopt_unsafe_reduce_fp_error"), true).toBool(); 766 ReadSetting(QStringLiteral("cpuopt_unsafe_reduce_fp_error"), true).toBool();
767 Settings::values.cpuopt_unsafe_inaccurate_nan =
768 ReadSetting(QStringLiteral("cpuopt_unsafe_inaccurate_nan"), true).toBool();
767 } 769 }
768 770
769 qt_config->endGroup(); 771 qt_config->endGroup();
@@ -1174,7 +1176,7 @@ void Config::SaveControlValues() {
1174 SaveTouchscreenValues(); 1176 SaveTouchscreenValues();
1175 SaveMotionTouchValues(); 1177 SaveMotionTouchValues();
1176 1178
1177 WriteSettingGlobal(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false); 1179 WriteSettingGlobal(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, true);
1178 WriteSettingGlobal(QStringLiteral("vibration_enabled"), Settings::values.vibration_enabled, 1180 WriteSettingGlobal(QStringLiteral("vibration_enabled"), Settings::values.vibration_enabled,
1179 true); 1181 true);
1180 WriteSettingGlobal(QStringLiteral("enable_accurate_vibrations"), 1182 WriteSettingGlobal(QStringLiteral("enable_accurate_vibrations"),
@@ -1327,6 +1329,8 @@ void Config::SaveCpuValues() {
1327 Settings::values.cpuopt_unsafe_unfuse_fma, true); 1329 Settings::values.cpuopt_unsafe_unfuse_fma, true);
1328 WriteSetting(QStringLiteral("cpuopt_unsafe_reduce_fp_error"), 1330 WriteSetting(QStringLiteral("cpuopt_unsafe_reduce_fp_error"),
1329 Settings::values.cpuopt_unsafe_reduce_fp_error, true); 1331 Settings::values.cpuopt_unsafe_reduce_fp_error, true);
1332 WriteSetting(QStringLiteral("cpuopt_unsafe_inaccurate_nan"),
1333 Settings::values.cpuopt_unsafe_inaccurate_nan, true);
1330 } 1334 }
1331 1335
1332 qt_config->endGroup(); 1336 qt_config->endGroup();
diff --git a/src/yuzu/configuration/configure_cpu.cpp b/src/yuzu/configuration/configure_cpu.cpp
index 37fcd6adc..d055cbd60 100644
--- a/src/yuzu/configuration/configure_cpu.cpp
+++ b/src/yuzu/configuration/configure_cpu.cpp
@@ -36,6 +36,8 @@ void ConfigureCpu::SetConfiguration() {
36 ui->cpuopt_unsafe_unfuse_fma->setChecked(Settings::values.cpuopt_unsafe_unfuse_fma); 36 ui->cpuopt_unsafe_unfuse_fma->setChecked(Settings::values.cpuopt_unsafe_unfuse_fma);
37 ui->cpuopt_unsafe_reduce_fp_error->setEnabled(runtime_lock); 37 ui->cpuopt_unsafe_reduce_fp_error->setEnabled(runtime_lock);
38 ui->cpuopt_unsafe_reduce_fp_error->setChecked(Settings::values.cpuopt_unsafe_reduce_fp_error); 38 ui->cpuopt_unsafe_reduce_fp_error->setChecked(Settings::values.cpuopt_unsafe_reduce_fp_error);
39 ui->cpuopt_unsafe_inaccurate_nan->setEnabled(runtime_lock);
40 ui->cpuopt_unsafe_inaccurate_nan->setChecked(Settings::values.cpuopt_unsafe_inaccurate_nan);
39} 41}
40 42
41void ConfigureCpu::AccuracyUpdated(int index) { 43void ConfigureCpu::AccuracyUpdated(int index) {
@@ -61,6 +63,7 @@ void ConfigureCpu::ApplyConfiguration() {
61 static_cast<Settings::CPUAccuracy>(ui->accuracy->currentIndex()); 63 static_cast<Settings::CPUAccuracy>(ui->accuracy->currentIndex());
62 Settings::values.cpuopt_unsafe_unfuse_fma = ui->cpuopt_unsafe_unfuse_fma->isChecked(); 64 Settings::values.cpuopt_unsafe_unfuse_fma = ui->cpuopt_unsafe_unfuse_fma->isChecked();
63 Settings::values.cpuopt_unsafe_reduce_fp_error = ui->cpuopt_unsafe_reduce_fp_error->isChecked(); 65 Settings::values.cpuopt_unsafe_reduce_fp_error = ui->cpuopt_unsafe_reduce_fp_error->isChecked();
66 Settings::values.cpuopt_unsafe_inaccurate_nan = ui->cpuopt_unsafe_inaccurate_nan->isChecked();
64} 67}
65 68
66void ConfigureCpu::changeEvent(QEvent* event) { 69void ConfigureCpu::changeEvent(QEvent* event) {
diff --git a/src/yuzu/configuration/configure_cpu.ui b/src/yuzu/configuration/configure_cpu.ui
index ebdd2e6e9..bcd0962e9 100644
--- a/src/yuzu/configuration/configure_cpu.ui
+++ b/src/yuzu/configuration/configure_cpu.ui
@@ -109,6 +109,18 @@
109 </property> 109 </property>
110 </widget> 110 </widget>
111 </item> 111 </item>
112 <item>
113 <widget class="QCheckBox" name="cpuopt_unsafe_inaccurate_nan">
114 <property name="text">
115 <string>Inaccurate NaN handling</string>
116 </property>
117 <property name="toolTip">
118 <string>
119 &lt;div&gt;This option improves speed by removing NaN checking. Please note this also reduces accuracy of certain floating-point instructions.&lt;/div&gt;
120 </string>
121 </property>
122 </widget>
123 </item>
112 </layout> 124 </layout>
113 </widget> 125 </widget>
114 </item> 126 </item>
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index b33f8437a..d6b17a28d 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -117,31 +117,13 @@ void ConfigureDialog::UpdateVisibleTabs() {
117 return; 117 return;
118 } 118 }
119 119
120 const std::map<QWidget*, QString> widgets = {
121 {ui->generalTab, tr("General")},
122 {ui->systemTab, tr("System")},
123 {ui->profileManagerTab, tr("Profiles")},
124 {ui->inputTab, tr("Controls")},
125 {ui->hotkeysTab, tr("Hotkeys")},
126 {ui->cpuTab, tr("CPU")},
127 {ui->cpuDebugTab, tr("Debug")},
128 {ui->graphicsTab, tr("Graphics")},
129 {ui->graphicsAdvancedTab, tr("Advanced")},
130 {ui->audioTab, tr("Audio")},
131 {ui->debugTab, tr("Debug")},
132 {ui->webTab, tr("Web")},
133 {ui->uiTab, tr("UI")},
134 {ui->filesystemTab, tr("Filesystem")},
135 {ui->serviceTab, tr("Services")},
136 };
137
138 [[maybe_unused]] const QSignalBlocker blocker(ui->tabWidget); 120 [[maybe_unused]] const QSignalBlocker blocker(ui->tabWidget);
139 121
140 ui->tabWidget->clear(); 122 ui->tabWidget->clear();
141 123
142 const QList<QWidget*> tabs = qvariant_cast<QList<QWidget*>>(items[0]->data(Qt::UserRole)); 124 const auto tabs = qvariant_cast<QList<QWidget*>>(items[0]->data(Qt::UserRole));
143 125
144 for (const auto tab : tabs) { 126 for (auto* const tab : tabs) {
145 ui->tabWidget->addTab(tab, tab->accessibleName()); 127 ui->tabWidget->addTab(tab, tab->accessibleName());
146 } 128 }
147} 129}
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index d9009091b..567a36d9b 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -4,6 +4,7 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <memory> 6#include <memory>
7#include <thread>
7 8
8#include <QSignalBlocker> 9#include <QSignalBlocker>
9#include <QTimer> 10#include <QTimer>
@@ -181,8 +182,18 @@ QList<QWidget*> ConfigureInput::GetSubTabs() const {
181} 182}
182 183
183void ConfigureInput::ApplyConfiguration() { 184void ConfigureInput::ApplyConfiguration() {
184 for (auto controller : player_controllers) { 185 for (auto* controller : player_controllers) {
185 controller->ApplyConfiguration(); 186 controller->ApplyConfiguration();
187 controller->TryDisconnectSelectedController();
188 }
189
190 // This emulates a delay between disconnecting and reconnecting controllers as some games
191 // do not respond to a change in controller type if it was instantaneous.
192 using namespace std::chrono_literals;
193 std::this_thread::sleep_for(60ms);
194
195 for (auto* controller : player_controllers) {
196 controller->TryConnectSelectedController();
186 } 197 }
187 198
188 advanced->ApplyConfiguration(); 199 advanced->ApplyConfiguration();
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 3c7500ee3..46ea026e4 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -4,7 +4,6 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <memory> 6#include <memory>
7#include <thread>
8#include <utility> 7#include <utility>
9#include <QGridLayout> 8#include <QGridLayout>
10#include <QInputDialog> 9#include <QInputDialog>
@@ -576,6 +575,10 @@ void ConfigureInputPlayer::ApplyConfiguration() {
576 575
577 std::transform(motions_param.begin(), motions_param.end(), motions.begin(), 576 std::transform(motions_param.begin(), motions_param.end(), motions.begin(),
578 [](const Common::ParamPackage& param) { return param.Serialize(); }); 577 [](const Common::ParamPackage& param) { return param.Serialize(); });
578}
579
580void ConfigureInputPlayer::TryConnectSelectedController() {
581 auto& player = Settings::values.players.GetValue()[player_index];
579 582
580 const auto controller_type = 583 const auto controller_type =
581 GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); 584 GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
@@ -588,15 +591,12 @@ void ConfigureInputPlayer::ApplyConfiguration() {
588 return; 591 return;
589 } 592 }
590 593
591 // Disconnect the controller first.
592 UpdateController(controller_type, player_index, false);
593
594 player.controller_type = controller_type; 594 player.controller_type = controller_type;
595 player.connected = player_connected; 595 player.connected = player_connected;
596 596
597 ConfigureVibration::SetVibrationDevices(player_index); 597 ConfigureVibration::SetVibrationDevices(player_index);
598 598
599 // Handheld 599 // Connect/Disconnect Handheld depending on Player 1's controller configuration.
600 if (player_index == 0) { 600 if (player_index == 0) {
601 auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX]; 601 auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX];
602 if (controller_type == Settings::ControllerType::Handheld) { 602 if (controller_type == Settings::ControllerType::Handheld) {
@@ -611,14 +611,26 @@ void ConfigureInputPlayer::ApplyConfiguration() {
611 return; 611 return;
612 } 612 }
613 613
614 // This emulates a delay between disconnecting and reconnecting controllers as some games
615 // do not respond to a change in controller type if it was instantaneous.
616 using namespace std::chrono_literals;
617 std::this_thread::sleep_for(20ms);
618
619 UpdateController(controller_type, player_index, player_connected); 614 UpdateController(controller_type, player_index, player_connected);
620} 615}
621 616
617void ConfigureInputPlayer::TryDisconnectSelectedController() {
618 const auto& player = Settings::values.players.GetValue()[player_index];
619
620 const auto controller_type =
621 GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
622 const auto player_connected = ui->groupConnectedController->isChecked() &&
623 controller_type != Settings::ControllerType::Handheld;
624
625 // Do not do anything if the controller configuration has not changed.
626 if (player.controller_type == controller_type && player.connected == player_connected) {
627 return;
628 }
629
630 // Disconnect the controller first.
631 UpdateController(controller_type, player_index, false);
632}
633
622void ConfigureInputPlayer::showEvent(QShowEvent* event) { 634void ConfigureInputPlayer::showEvent(QShowEvent* event) {
623 if (bottom_row == nullptr) { 635 if (bottom_row == nullptr) {
624 return; 636 return;
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h
index 9c30879a2..c4ae50de7 100644
--- a/src/yuzu/configuration/configure_input_player.h
+++ b/src/yuzu/configuration/configure_input_player.h
@@ -54,6 +54,18 @@ public:
54 /// Save all button configurations to settings file. 54 /// Save all button configurations to settings file.
55 void ApplyConfiguration(); 55 void ApplyConfiguration();
56 56
57 /**
58 * Attempts to connect the currently selected controller in the HID backend.
59 * This function will not do anything if it is not connected in the frontend.
60 */
61 void TryConnectSelectedController();
62
63 /**
64 * Attempts to disconnect the currently selected controller in the HID backend.
65 * This function will not do anything if the configuration has not changed.
66 */
67 void TryDisconnectSelectedController();
68
57 /// Set the connection state checkbox (used to sync state). 69 /// Set the connection state checkbox (used to sync state).
58 void ConnectPlayer(bool connected); 70 void ConnectPlayer(bool connected);
59 71
diff --git a/src/yuzu/configuration/configure_motion_touch.cpp b/src/yuzu/configuration/configure_motion_touch.cpp
index c2a7113da..caaa85930 100644
--- a/src/yuzu/configuration/configure_motion_touch.cpp
+++ b/src/yuzu/configuration/configure_motion_touch.cpp
@@ -4,12 +4,15 @@
4 4
5#include <array> 5#include <array>
6#include <sstream> 6#include <sstream>
7
7#include <QCloseEvent> 8#include <QCloseEvent>
8#include <QLabel> 9#include <QLabel>
9#include <QMessageBox> 10#include <QMessageBox>
10#include <QPushButton> 11#include <QPushButton>
12#include <QRegularExpression>
11#include <QStringListModel> 13#include <QStringListModel>
12#include <QVBoxLayout> 14#include <QVBoxLayout>
15
13#include "common/logging/log.h" 16#include "common/logging/log.h"
14#include "core/settings.h" 17#include "core/settings.h"
15#include "input_common/main.h" 18#include "input_common/main.h"
@@ -51,6 +54,8 @@ CalibrationConfigurationDialog::CalibrationConfigurationDialog(QWidget* parent,
51 case CalibrationConfigurationJob::Status::Completed: 54 case CalibrationConfigurationJob::Status::Completed:
52 text = tr("Configuration completed!"); 55 text = tr("Configuration completed!");
53 break; 56 break;
57 default:
58 break;
54 } 59 }
55 QMetaObject::invokeMethod(this, "UpdateLabelText", Q_ARG(QString, text)); 60 QMetaObject::invokeMethod(this, "UpdateLabelText", Q_ARG(QString, text));
56 if (status == CalibrationConfigurationJob::Status::Completed) { 61 if (status == CalibrationConfigurationJob::Status::Completed) {
@@ -107,7 +112,6 @@ ConfigureMotionTouch::~ConfigureMotionTouch() = default;
107void ConfigureMotionTouch::SetConfiguration() { 112void ConfigureMotionTouch::SetConfiguration() {
108 const Common::ParamPackage motion_param(Settings::values.motion_device); 113 const Common::ParamPackage motion_param(Settings::values.motion_device);
109 const Common::ParamPackage touch_param(Settings::values.touch_device); 114 const Common::ParamPackage touch_param(Settings::values.touch_device);
110 const std::string motion_engine = motion_param.Get("engine", "motion_emu");
111 const std::string touch_engine = touch_param.Get("engine", "emu_window"); 115 const std::string touch_engine = touch_param.Get("engine", "emu_window");
112 116
113 ui->touch_provider->setCurrentIndex( 117 ui->touch_provider->setCurrentIndex(
@@ -183,14 +187,15 @@ void ConfigureMotionTouch::ConnectEvents() {
183} 187}
184 188
185void ConfigureMotionTouch::OnUDPAddServer() { 189void ConfigureMotionTouch::OnUDPAddServer() {
186 QRegExp re(tr(R"re(^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4]" 190 // Validator for IP address
187 "[0-9]|[01]?[0-9][0-9]?)$)re")); // a valid ip address 191 const QRegularExpression re(QStringLiteral(
192 R"re(^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$)re"));
188 bool ok; 193 bool ok;
189 QString port_text = ui->udp_port->text(); 194 const QString port_text = ui->udp_port->text();
190 QString server_text = ui->udp_server->text(); 195 const QString server_text = ui->udp_server->text();
191 const QString server_string = tr("%1:%2").arg(server_text, port_text); 196 const QString server_string = tr("%1:%2").arg(server_text, port_text);
192 int port_number = port_text.toInt(&ok, 10); 197 const int port_number = port_text.toInt(&ok, 10);
193 int row = udp_server_list_model->rowCount(); 198 const int row = udp_server_list_model->rowCount();
194 199
195 if (!ok) { 200 if (!ok) {
196 QMessageBox::warning(this, tr("yuzu"), tr("Port number has invalid characters")); 201 QMessageBox::warning(this, tr("yuzu"), tr("Port number has invalid characters"));
@@ -200,7 +205,7 @@ void ConfigureMotionTouch::OnUDPAddServer() {
200 QMessageBox::warning(this, tr("yuzu"), tr("Port has to be in range 0 and 65353")); 205 QMessageBox::warning(this, tr("yuzu"), tr("Port has to be in range 0 and 65353"));
201 return; 206 return;
202 } 207 }
203 if (!re.exactMatch(server_text)) { 208 if (!re.match(server_text).hasMatch()) {
204 QMessageBox::warning(this, tr("yuzu"), tr("IP address is not valid")); 209 QMessageBox::warning(this, tr("yuzu"), tr("IP address is not valid"));
205 return; 210 return;
206 } 211 }
@@ -325,14 +330,13 @@ void ConfigureMotionTouch::ApplyConfiguration() {
325 std::string touch_engine = ui->touch_provider->currentData().toString().toStdString(); 330 std::string touch_engine = ui->touch_provider->currentData().toString().toStdString();
326 331
327 Common::ParamPackage touch_param{}; 332 Common::ParamPackage touch_param{};
328 touch_param.Set("engine", std::move(touch_engine));
329
330 if (touch_engine == "cemuhookudp") { 333 if (touch_engine == "cemuhookudp") {
331 touch_param.Set("min_x", min_x); 334 touch_param.Set("min_x", min_x);
332 touch_param.Set("min_y", min_y); 335 touch_param.Set("min_y", min_y);
333 touch_param.Set("max_x", max_x); 336 touch_param.Set("max_x", max_x);
334 touch_param.Set("max_y", max_y); 337 touch_param.Set("max_y", max_y);
335 } 338 }
339 touch_param.Set("engine", std::move(touch_engine));
336 340
337 Settings::values.touch_device = touch_param.Serialize(); 341 Settings::values.touch_device = touch_param.Serialize();
338 Settings::values.use_touch_from_button = ui->touch_from_button_checkbox->isChecked(); 342 Settings::values.use_touch_from_button = ui->touch_from_button_checkbox->isChecked();
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index 0925c10b4..a93b5d3c2 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -14,10 +14,10 @@
14#include "core/core.h" 14#include "core/core.h"
15#include "core/hle/kernel/handle_table.h" 15#include "core/hle/kernel/handle_table.h"
16#include "core/hle/kernel/k_scheduler.h" 16#include "core/hle/kernel/k_scheduler.h"
17#include "core/hle/kernel/mutex.h" 17#include "core/hle/kernel/k_synchronization_object.h"
18#include "core/hle/kernel/process.h" 18#include "core/hle/kernel/process.h"
19#include "core/hle/kernel/readable_event.h" 19#include "core/hle/kernel/readable_event.h"
20#include "core/hle/kernel/synchronization_object.h" 20#include "core/hle/kernel/svc_common.h"
21#include "core/hle/kernel/thread.h" 21#include "core/hle/kernel/thread.h"
22#include "core/memory.h" 22#include "core/memory.h"
23 23
@@ -116,7 +116,7 @@ QString WaitTreeText::GetText() const {
116WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address, const Kernel::HandleTable& handle_table) 116WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address, const Kernel::HandleTable& handle_table)
117 : mutex_address(mutex_address) { 117 : mutex_address(mutex_address) {
118 mutex_value = Core::System::GetInstance().Memory().Read32(mutex_address); 118 mutex_value = Core::System::GetInstance().Memory().Read32(mutex_address);
119 owner_handle = static_cast<Kernel::Handle>(mutex_value & Kernel::Mutex::MutexOwnerMask); 119 owner_handle = static_cast<Kernel::Handle>(mutex_value & Kernel::Svc::HandleWaitMask);
120 owner = handle_table.Get<Kernel::Thread>(owner_handle); 120 owner = handle_table.Get<Kernel::Thread>(owner_handle);
121} 121}
122 122
@@ -127,7 +127,7 @@ QString WaitTreeMutexInfo::GetText() const {
127} 127}
128 128
129std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeMutexInfo::GetChildren() const { 129std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeMutexInfo::GetChildren() const {
130 const bool has_waiters = (mutex_value & Kernel::Mutex::MutexHasWaitersFlag) != 0; 130 const bool has_waiters = (mutex_value & Kernel::Svc::HandleWaitMask) != 0;
131 131
132 std::vector<std::unique_ptr<WaitTreeItem>> list; 132 std::vector<std::unique_ptr<WaitTreeItem>> list;
133 list.push_back(std::make_unique<WaitTreeText>(tr("has waiters: %1").arg(has_waiters))); 133 list.push_back(std::make_unique<WaitTreeText>(tr("has waiters: %1").arg(has_waiters)));
@@ -169,7 +169,8 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() cons
169 return list; 169 return list;
170} 170}
171 171
172WaitTreeSynchronizationObject::WaitTreeSynchronizationObject(const Kernel::SynchronizationObject& o) 172WaitTreeSynchronizationObject::WaitTreeSynchronizationObject(
173 const Kernel::KSynchronizationObject& o)
173 : object(o) {} 174 : object(o) {}
174WaitTreeSynchronizationObject::~WaitTreeSynchronizationObject() = default; 175WaitTreeSynchronizationObject::~WaitTreeSynchronizationObject() = default;
175 176
@@ -188,7 +189,7 @@ QString WaitTreeSynchronizationObject::GetText() const {
188} 189}
189 190
190std::unique_ptr<WaitTreeSynchronizationObject> WaitTreeSynchronizationObject::make( 191std::unique_ptr<WaitTreeSynchronizationObject> WaitTreeSynchronizationObject::make(
191 const Kernel::SynchronizationObject& object) { 192 const Kernel::KSynchronizationObject& object) {
192 switch (object.GetHandleType()) { 193 switch (object.GetHandleType()) {
193 case Kernel::HandleType::ReadableEvent: 194 case Kernel::HandleType::ReadableEvent:
194 return std::make_unique<WaitTreeEvent>(static_cast<const Kernel::ReadableEvent&>(object)); 195 return std::make_unique<WaitTreeEvent>(static_cast<const Kernel::ReadableEvent&>(object));
@@ -202,7 +203,7 @@ std::unique_ptr<WaitTreeSynchronizationObject> WaitTreeSynchronizationObject::ma
202std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeSynchronizationObject::GetChildren() const { 203std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeSynchronizationObject::GetChildren() const {
203 std::vector<std::unique_ptr<WaitTreeItem>> list; 204 std::vector<std::unique_ptr<WaitTreeItem>> list;
204 205
205 const auto& threads = object.GetWaitingThreads(); 206 const auto& threads = object.GetWaitingThreadsForDebugging();
206 if (threads.empty()) { 207 if (threads.empty()) {
207 list.push_back(std::make_unique<WaitTreeText>(tr("waited by no thread"))); 208 list.push_back(std::make_unique<WaitTreeText>(tr("waited by no thread")));
208 } else { 209 } else {
@@ -211,8 +212,8 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeSynchronizationObject::GetChi
211 return list; 212 return list;
212} 213}
213 214
214WaitTreeObjectList::WaitTreeObjectList( 215WaitTreeObjectList::WaitTreeObjectList(const std::vector<Kernel::KSynchronizationObject*>& list,
215 const std::vector<std::shared_ptr<Kernel::SynchronizationObject>>& list, bool w_all) 216 bool w_all)
216 : object_list(list), wait_all(w_all) {} 217 : object_list(list), wait_all(w_all) {}
217 218
218WaitTreeObjectList::~WaitTreeObjectList() = default; 219WaitTreeObjectList::~WaitTreeObjectList() = default;
@@ -237,8 +238,8 @@ WaitTreeThread::~WaitTreeThread() = default;
237QString WaitTreeThread::GetText() const { 238QString WaitTreeThread::GetText() const {
238 const auto& thread = static_cast<const Kernel::Thread&>(object); 239 const auto& thread = static_cast<const Kernel::Thread&>(object);
239 QString status; 240 QString status;
240 switch (thread.GetStatus()) { 241 switch (thread.GetState()) {
241 case Kernel::ThreadStatus::Ready: 242 case Kernel::ThreadState::Runnable:
242 if (!thread.IsPaused()) { 243 if (!thread.IsPaused()) {
243 if (thread.WasRunning()) { 244 if (thread.WasRunning()) {
244 status = tr("running"); 245 status = tr("running");
@@ -249,35 +250,39 @@ QString WaitTreeThread::GetText() const {
249 status = tr("paused"); 250 status = tr("paused");
250 } 251 }
251 break; 252 break;
252 case Kernel::ThreadStatus::Paused: 253 case Kernel::ThreadState::Waiting:
253 status = tr("paused"); 254 switch (thread.GetWaitReasonForDebugging()) {
254 break; 255 case Kernel::ThreadWaitReasonForDebugging::Sleep:
255 case Kernel::ThreadStatus::WaitHLEEvent: 256 status = tr("sleeping");
256 status = tr("waiting for HLE return"); 257 break;
257 break; 258 case Kernel::ThreadWaitReasonForDebugging::IPC:
258 case Kernel::ThreadStatus::WaitSleep: 259 status = tr("waiting for IPC reply");
259 status = tr("sleeping"); 260 break;
260 break; 261 case Kernel::ThreadWaitReasonForDebugging::Synchronization:
261 case Kernel::ThreadStatus::WaitIPC: 262 status = tr("waiting for objects");
262 status = tr("waiting for IPC reply"); 263 break;
263 break; 264 case Kernel::ThreadWaitReasonForDebugging::ConditionVar:
264 case Kernel::ThreadStatus::WaitSynch: 265 status = tr("waiting for condition variable");
265 status = tr("waiting for objects"); 266 break;
266 break; 267 case Kernel::ThreadWaitReasonForDebugging::Arbitration:
267 case Kernel::ThreadStatus::WaitMutex: 268 status = tr("waiting for address arbiter");
268 status = tr("waiting for mutex"); 269 break;
269 break; 270 case Kernel::ThreadWaitReasonForDebugging::Suspended:
270 case Kernel::ThreadStatus::WaitCondVar: 271 status = tr("waiting for suspend resume");
271 status = tr("waiting for condition variable"); 272 break;
273 default:
274 status = tr("waiting");
275 break;
276 }
272 break; 277 break;
273 case Kernel::ThreadStatus::WaitArb: 278 case Kernel::ThreadState::Initialized:
274 status = tr("waiting for address arbiter"); 279 status = tr("initialized");
275 break; 280 break;
276 case Kernel::ThreadStatus::Dormant: 281 case Kernel::ThreadState::Terminated:
277 status = tr("dormant"); 282 status = tr("terminated");
278 break; 283 break;
279 case Kernel::ThreadStatus::Dead: 284 default:
280 status = tr("dead"); 285 status = tr("unknown");
281 break; 286 break;
282 } 287 }
283 288
@@ -293,8 +298,8 @@ QColor WaitTreeThread::GetColor() const {
293 const std::size_t color_index = IsDarkTheme() ? 1 : 0; 298 const std::size_t color_index = IsDarkTheme() ? 1 : 0;
294 299
295 const auto& thread = static_cast<const Kernel::Thread&>(object); 300 const auto& thread = static_cast<const Kernel::Thread&>(object);
296 switch (thread.GetStatus()) { 301 switch (thread.GetState()) {
297 case Kernel::ThreadStatus::Ready: 302 case Kernel::ThreadState::Runnable:
298 if (!thread.IsPaused()) { 303 if (!thread.IsPaused()) {
299 if (thread.WasRunning()) { 304 if (thread.WasRunning()) {
300 return QColor(WaitTreeColors[0][color_index]); 305 return QColor(WaitTreeColors[0][color_index]);
@@ -304,21 +309,24 @@ QColor WaitTreeThread::GetColor() const {
304 } else { 309 } else {
305 return QColor(WaitTreeColors[2][color_index]); 310 return QColor(WaitTreeColors[2][color_index]);
306 } 311 }
307 case Kernel::ThreadStatus::Paused: 312 case Kernel::ThreadState::Waiting:
308 return QColor(WaitTreeColors[3][color_index]); 313 switch (thread.GetWaitReasonForDebugging()) {
309 case Kernel::ThreadStatus::WaitHLEEvent: 314 case Kernel::ThreadWaitReasonForDebugging::IPC:
310 case Kernel::ThreadStatus::WaitIPC: 315 return QColor(WaitTreeColors[4][color_index]);
311 return QColor(WaitTreeColors[4][color_index]); 316 case Kernel::ThreadWaitReasonForDebugging::Sleep:
312 case Kernel::ThreadStatus::WaitSleep: 317 return QColor(WaitTreeColors[5][color_index]);
313 return QColor(WaitTreeColors[5][color_index]); 318 case Kernel::ThreadWaitReasonForDebugging::Synchronization:
314 case Kernel::ThreadStatus::WaitSynch: 319 case Kernel::ThreadWaitReasonForDebugging::ConditionVar:
315 case Kernel::ThreadStatus::WaitMutex: 320 case Kernel::ThreadWaitReasonForDebugging::Arbitration:
316 case Kernel::ThreadStatus::WaitCondVar: 321 case Kernel::ThreadWaitReasonForDebugging::Suspended:
317 case Kernel::ThreadStatus::WaitArb: 322 return QColor(WaitTreeColors[6][color_index]);
318 return QColor(WaitTreeColors[6][color_index]); 323 break;
319 case Kernel::ThreadStatus::Dormant: 324 default:
325 return QColor(WaitTreeColors[3][color_index]);
326 }
327 case Kernel::ThreadState::Initialized:
320 return QColor(WaitTreeColors[7][color_index]); 328 return QColor(WaitTreeColors[7][color_index]);
321 case Kernel::ThreadStatus::Dead: 329 case Kernel::ThreadState::Terminated:
322 return QColor(WaitTreeColors[8][color_index]); 330 return QColor(WaitTreeColors[8][color_index]);
323 default: 331 default:
324 return WaitTreeItem::GetColor(); 332 return WaitTreeItem::GetColor();
@@ -354,11 +362,11 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const {
354 list.push_back(std::make_unique<WaitTreeText>(tr("thread id = %1").arg(thread.GetThreadID()))); 362 list.push_back(std::make_unique<WaitTreeText>(tr("thread id = %1").arg(thread.GetThreadID())));
355 list.push_back(std::make_unique<WaitTreeText>(tr("priority = %1(current) / %2(normal)") 363 list.push_back(std::make_unique<WaitTreeText>(tr("priority = %1(current) / %2(normal)")
356 .arg(thread.GetPriority()) 364 .arg(thread.GetPriority())
357 .arg(thread.GetNominalPriority()))); 365 .arg(thread.GetBasePriority())));
358 list.push_back(std::make_unique<WaitTreeText>( 366 list.push_back(std::make_unique<WaitTreeText>(
359 tr("last running ticks = %1").arg(thread.GetLastScheduledTick()))); 367 tr("last running ticks = %1").arg(thread.GetLastScheduledTick())));
360 368
361 const VAddr mutex_wait_address = thread.GetMutexWaitAddress(); 369 const VAddr mutex_wait_address = thread.GetMutexWaitAddressForDebugging();
362 if (mutex_wait_address != 0) { 370 if (mutex_wait_address != 0) {
363 const auto& handle_table = thread.GetOwnerProcess()->GetHandleTable(); 371 const auto& handle_table = thread.GetOwnerProcess()->GetHandleTable();
364 list.push_back(std::make_unique<WaitTreeMutexInfo>(mutex_wait_address, handle_table)); 372 list.push_back(std::make_unique<WaitTreeMutexInfo>(mutex_wait_address, handle_table));
@@ -366,9 +374,11 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const {
366 list.push_back(std::make_unique<WaitTreeText>(tr("not waiting for mutex"))); 374 list.push_back(std::make_unique<WaitTreeText>(tr("not waiting for mutex")));
367 } 375 }
368 376
369 if (thread.GetStatus() == Kernel::ThreadStatus::WaitSynch) { 377 if (thread.GetState() == Kernel::ThreadState::Waiting &&
370 list.push_back(std::make_unique<WaitTreeObjectList>(thread.GetSynchronizationObjects(), 378 thread.GetWaitReasonForDebugging() ==
371 thread.IsWaitingSync())); 379 Kernel::ThreadWaitReasonForDebugging::Synchronization) {
380 list.push_back(std::make_unique<WaitTreeObjectList>(thread.GetWaitObjectsForDebugging(),
381 thread.IsCancellable()));
372 } 382 }
373 383
374 list.push_back(std::make_unique<WaitTreeCallstack>(thread)); 384 list.push_back(std::make_unique<WaitTreeCallstack>(thread));
@@ -380,7 +390,7 @@ WaitTreeEvent::WaitTreeEvent(const Kernel::ReadableEvent& object)
380 : WaitTreeSynchronizationObject(object) {} 390 : WaitTreeSynchronizationObject(object) {}
381WaitTreeEvent::~WaitTreeEvent() = default; 391WaitTreeEvent::~WaitTreeEvent() = default;
382 392
383WaitTreeThreadList::WaitTreeThreadList(const std::vector<std::shared_ptr<Kernel::Thread>>& list) 393WaitTreeThreadList::WaitTreeThreadList(const std::vector<Kernel::Thread*>& list)
384 : thread_list(list) {} 394 : thread_list(list) {}
385WaitTreeThreadList::~WaitTreeThreadList() = default; 395WaitTreeThreadList::~WaitTreeThreadList() = default;
386 396
diff --git a/src/yuzu/debugger/wait_tree.h b/src/yuzu/debugger/wait_tree.h
index 8e3bc4b24..cf96911ea 100644
--- a/src/yuzu/debugger/wait_tree.h
+++ b/src/yuzu/debugger/wait_tree.h
@@ -18,8 +18,8 @@ class EmuThread;
18 18
19namespace Kernel { 19namespace Kernel {
20class HandleTable; 20class HandleTable;
21class KSynchronizationObject;
21class ReadableEvent; 22class ReadableEvent;
22class SynchronizationObject;
23class Thread; 23class Thread;
24} // namespace Kernel 24} // namespace Kernel
25 25
@@ -102,30 +102,29 @@ private:
102class WaitTreeSynchronizationObject : public WaitTreeExpandableItem { 102class WaitTreeSynchronizationObject : public WaitTreeExpandableItem {
103 Q_OBJECT 103 Q_OBJECT
104public: 104public:
105 explicit WaitTreeSynchronizationObject(const Kernel::SynchronizationObject& object); 105 explicit WaitTreeSynchronizationObject(const Kernel::KSynchronizationObject& object);
106 ~WaitTreeSynchronizationObject() override; 106 ~WaitTreeSynchronizationObject() override;
107 107
108 static std::unique_ptr<WaitTreeSynchronizationObject> make( 108 static std::unique_ptr<WaitTreeSynchronizationObject> make(
109 const Kernel::SynchronizationObject& object); 109 const Kernel::KSynchronizationObject& object);
110 QString GetText() const override; 110 QString GetText() const override;
111 std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override; 111 std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
112 112
113protected: 113protected:
114 const Kernel::SynchronizationObject& object; 114 const Kernel::KSynchronizationObject& object;
115}; 115};
116 116
117class WaitTreeObjectList : public WaitTreeExpandableItem { 117class WaitTreeObjectList : public WaitTreeExpandableItem {
118 Q_OBJECT 118 Q_OBJECT
119public: 119public:
120 WaitTreeObjectList(const std::vector<std::shared_ptr<Kernel::SynchronizationObject>>& list, 120 WaitTreeObjectList(const std::vector<Kernel::KSynchronizationObject*>& list, bool wait_all);
121 bool wait_all);
122 ~WaitTreeObjectList() override; 121 ~WaitTreeObjectList() override;
123 122
124 QString GetText() const override; 123 QString GetText() const override;
125 std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override; 124 std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
126 125
127private: 126private:
128 const std::vector<std::shared_ptr<Kernel::SynchronizationObject>>& object_list; 127 const std::vector<Kernel::KSynchronizationObject*>& object_list;
129 bool wait_all; 128 bool wait_all;
130}; 129};
131 130
@@ -150,14 +149,14 @@ public:
150class WaitTreeThreadList : public WaitTreeExpandableItem { 149class WaitTreeThreadList : public WaitTreeExpandableItem {
151 Q_OBJECT 150 Q_OBJECT
152public: 151public:
153 explicit WaitTreeThreadList(const std::vector<std::shared_ptr<Kernel::Thread>>& list); 152 explicit WaitTreeThreadList(const std::vector<Kernel::Thread*>& list);
154 ~WaitTreeThreadList() override; 153 ~WaitTreeThreadList() override;
155 154
156 QString GetText() const override; 155 QString GetText() const override;
157 std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override; 156 std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
158 157
159private: 158private:
160 const std::vector<std::shared_ptr<Kernel::Thread>>& thread_list; 159 const std::vector<Kernel::Thread*>& thread_list;
161}; 160};
162 161
163class WaitTreeModel : public QAbstractItemModel { 162class WaitTreeModel : public QAbstractItemModel {
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 70d865112..37b0d1a0e 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -119,7 +119,7 @@ void GameListSearchField::setFocus() {
119GameListSearchField::GameListSearchField(GameList* parent) : QWidget{parent} { 119GameListSearchField::GameListSearchField(GameList* parent) : QWidget{parent} {
120 auto* const key_release_eater = new KeyReleaseEater(parent, this); 120 auto* const key_release_eater = new KeyReleaseEater(parent, this);
121 layout_filter = new QHBoxLayout; 121 layout_filter = new QHBoxLayout;
122 layout_filter->setMargin(8); 122 layout_filter->setContentsMargins(8, 8, 8, 8);
123 label_filter = new QLabel; 123 label_filter = new QLabel;
124 label_filter->setText(tr("Filter:")); 124 label_filter->setText(tr("Filter:"));
125 edit_filter = new QLineEdit; 125 edit_filter = new QLineEdit;
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index ab66d7f93..2e74037d1 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -142,7 +142,7 @@ constexpr int default_mouse_timeout = 2500;
142/** 142/**
143 * "Callouts" are one-time instructional messages shown to the user. In the config settings, there 143 * "Callouts" are one-time instructional messages shown to the user. In the config settings, there
144 * is a bitfield "callout_flags" options, used to track if a message has already been shown to the 144 * is a bitfield "callout_flags" options, used to track if a message has already been shown to the
145 * user. This is 32-bits - if we have more than 32 callouts, we should retire and recyle old ones. 145 * user. This is 32-bits - if we have more than 32 callouts, we should retire and recycle old ones.
146 */ 146 */
147enum class CalloutFlag : uint32_t { 147enum class CalloutFlag : uint32_t {
148 Telemetry = 0x1, 148 Telemetry = 0x1,
@@ -292,12 +292,48 @@ GMainWindow::GMainWindow()
292 connect(&mouse_hide_timer, &QTimer::timeout, this, &GMainWindow::HideMouseCursor); 292 connect(&mouse_hide_timer, &QTimer::timeout, this, &GMainWindow::HideMouseCursor);
293 connect(ui.menubar, &QMenuBar::hovered, this, &GMainWindow::ShowMouseCursor); 293 connect(ui.menubar, &QMenuBar::hovered, this, &GMainWindow::ShowMouseCursor);
294 294
295 MigrateConfigFiles();
296
297 ui.action_Fullscreen->setChecked(false);
298
295 QStringList args = QApplication::arguments(); 299 QStringList args = QApplication::arguments();
296 if (args.length() >= 2) { 300
297 BootGame(args[1]); 301 if (args.size() < 2) {
302 return;
298 } 303 }
299 304
300 MigrateConfigFiles(); 305 QString game_path;
306
307 for (int i = 1; i < args.size(); ++i) {
308 // Preserves drag/drop functionality
309 if (args.size() == 2 && !args[1].startsWith(QChar::fromLatin1('-'))) {
310 game_path = args[1];
311 break;
312 }
313
314 // Launch game in fullscreen mode
315 if (args[i] == QStringLiteral("-f")) {
316 ui.action_Fullscreen->setChecked(true);
317 continue;
318 }
319
320 // Launch game at path
321 if (args[i] == QStringLiteral("-g")) {
322 if (i >= args.size() - 1) {
323 continue;
324 }
325
326 if (args[i + 1].startsWith(QChar::fromLatin1('-'))) {
327 continue;
328 }
329
330 game_path = args[++i];
331 }
332 }
333
334 if (!game_path.isEmpty()) {
335 BootGame(game_path);
336 }
301} 337}
302 338
303GMainWindow::~GMainWindow() { 339GMainWindow::~GMainWindow() {
@@ -1045,20 +1081,24 @@ bool GMainWindow::LoadROM(const QString& filename, std::size_t program_index) {
1045 break; 1081 break;
1046 1082
1047 default: 1083 default:
1048 if (static_cast<u32>(result) > 1084 if (result > Core::System::ResultStatus::ErrorLoader) {
1049 static_cast<u32>(Core::System::ResultStatus::ErrorLoader)) {
1050 const u16 loader_id = static_cast<u16>(Core::System::ResultStatus::ErrorLoader); 1085 const u16 loader_id = static_cast<u16>(Core::System::ResultStatus::ErrorLoader);
1051 const u16 error_id = static_cast<u16>(result) - loader_id; 1086 const u16 error_id = static_cast<u16>(result) - loader_id;
1052 const std::string error_code = fmt::format("({:04X}-{:04X})", loader_id, error_id); 1087 const std::string error_code = fmt::format("({:04X}-{:04X})", loader_id, error_id);
1053 LOG_CRITICAL(Frontend, "Failed to load ROM! {}", error_code); 1088 LOG_CRITICAL(Frontend, "Failed to load ROM! {}", error_code);
1054 QMessageBox::critical( 1089
1055 this, 1090 const auto title =
1056 tr("Error while loading ROM! ").append(QString::fromStdString(error_code)), 1091 tr("Error while loading ROM! %1", "%1 signifies a numeric error code.")
1057 QString::fromStdString(fmt::format( 1092 .arg(QString::fromStdString(error_code));
1058 "{}<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the " 1093 const auto description =
1059 "yuzu quickstart guide</a> to redump your files.<br>You can refer " 1094 tr("%1<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the "
1060 "to the yuzu wiki</a> or the yuzu Discord</a> for help.", 1095 "yuzu quickstart guide</a> to redump your files.<br>You can refer "
1061 static_cast<Loader::ResultStatus>(error_id)))); 1096 "to the yuzu wiki</a> or the yuzu Discord</a> for help.",
1097 "%1 signifies an error string.")
1098 .arg(QString::fromStdString(
1099 GetResultStatusString(static_cast<Loader::ResultStatus>(error_id))));
1100
1101 QMessageBox::critical(this, title, description);
1062 } else { 1102 } else {
1063 QMessageBox::critical( 1103 QMessageBox::critical(
1064 this, tr("Error while loading ROM!"), 1104 this, tr("Error while loading ROM!"),
@@ -1130,6 +1170,7 @@ void GMainWindow::BootGame(const QString& filename, std::size_t program_index) {
1130 [this](std::size_t program_index) { render_window->ExecuteProgram(program_index); }); 1170 [this](std::size_t program_index) { render_window->ExecuteProgram(program_index); });
1131 1171
1132 connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); 1172 connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame);
1173 connect(render_window, &GRenderWindow::MouseActivity, this, &GMainWindow::OnMouseActivity);
1133 // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views 1174 // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views
1134 // before the CPU continues 1175 // before the CPU continues
1135 connect(emu_thread.get(), &EmuThread::DebugModeEntered, waitTreeWidget, 1176 connect(emu_thread.get(), &EmuThread::DebugModeEntered, waitTreeWidget,
@@ -1153,8 +1194,8 @@ void GMainWindow::BootGame(const QString& filename, std::size_t program_index) {
1153 1194
1154 if (UISettings::values.hide_mouse) { 1195 if (UISettings::values.hide_mouse) {
1155 mouse_hide_timer.start(); 1196 mouse_hide_timer.start();
1156 setMouseTracking(true); 1197 render_window->installEventFilter(render_window);
1157 ui.centralwidget->setMouseTracking(true); 1198 render_window->setAttribute(Qt::WA_Hover, true);
1158 } 1199 }
1159 1200
1160 std::string title_name; 1201 std::string title_name;
@@ -1231,8 +1272,8 @@ void GMainWindow::ShutdownGame() {
1231 } 1272 }
1232 game_list->SetFilterFocus(); 1273 game_list->SetFilterFocus();
1233 1274
1234 setMouseTracking(false); 1275 render_window->removeEventFilter(render_window);
1235 ui.centralwidget->setMouseTracking(false); 1276 render_window->setAttribute(Qt::WA_Hover, false);
1236 1277
1237 UpdateWindowTitle(); 1278 UpdateWindowTitle();
1238 1279
@@ -2313,12 +2354,12 @@ void GMainWindow::OnConfigure() {
2313 config->Save(); 2354 config->Save();
2314 2355
2315 if (UISettings::values.hide_mouse && emulation_running) { 2356 if (UISettings::values.hide_mouse && emulation_running) {
2316 setMouseTracking(true); 2357 render_window->installEventFilter(render_window);
2317 ui.centralwidget->setMouseTracking(true); 2358 render_window->setAttribute(Qt::WA_Hover, true);
2318 mouse_hide_timer.start(); 2359 mouse_hide_timer.start();
2319 } else { 2360 } else {
2320 setMouseTracking(false); 2361 render_window->removeEventFilter(render_window);
2321 ui.centralwidget->setMouseTracking(false); 2362 render_window->setAttribute(Qt::WA_Hover, false);
2322 } 2363 }
2323 2364
2324 UpdateStatusButtons(); 2365 UpdateStatusButtons();
@@ -2558,21 +2599,17 @@ void GMainWindow::HideMouseCursor() {
2558 ShowMouseCursor(); 2599 ShowMouseCursor();
2559 return; 2600 return;
2560 } 2601 }
2561 setCursor(QCursor(Qt::BlankCursor)); 2602 render_window->setCursor(QCursor(Qt::BlankCursor));
2562} 2603}
2563 2604
2564void GMainWindow::ShowMouseCursor() { 2605void GMainWindow::ShowMouseCursor() {
2565 unsetCursor(); 2606 render_window->unsetCursor();
2566 if (emu_thread != nullptr && UISettings::values.hide_mouse) { 2607 if (emu_thread != nullptr && UISettings::values.hide_mouse) {
2567 mouse_hide_timer.start(); 2608 mouse_hide_timer.start();
2568 } 2609 }
2569} 2610}
2570 2611
2571void GMainWindow::mouseMoveEvent(QMouseEvent* event) { 2612void GMainWindow::OnMouseActivity() {
2572 ShowMouseCursor();
2573}
2574
2575void GMainWindow::mousePressEvent(QMouseEvent* event) {
2576 ShowMouseCursor(); 2613 ShowMouseCursor();
2577} 2614}
2578 2615
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index ea6d2c30d..31788ea62 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -248,6 +248,7 @@ private slots:
248 void OnCoreError(Core::System::ResultStatus, std::string); 248 void OnCoreError(Core::System::ResultStatus, std::string);
249 void OnReinitializeKeys(ReinitializeKeyBehavior behavior); 249 void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
250 void OnLanguageChanged(const QString& locale); 250 void OnLanguageChanged(const QString& locale);
251 void OnMouseActivity();
251 252
252private: 253private:
253 void RemoveBaseContent(u64 program_id, const QString& entry_type); 254 void RemoveBaseContent(u64 program_id, const QString& entry_type);
@@ -335,6 +336,4 @@ protected:
335 void dropEvent(QDropEvent* event) override; 336 void dropEvent(QDropEvent* event) override;
336 void dragEnterEvent(QDragEnterEvent* event) override; 337 void dragEnterEvent(QDragEnterEvent* event) override;
337 void dragMoveEvent(QDragMoveEvent* event) override; 338 void dragMoveEvent(QDragMoveEvent* event) override;
338 void mouseMoveEvent(QMouseEvent* event) override;
339 void mousePressEvent(QMouseEvent* event) override;
340}; 339};
diff --git a/src/yuzu/util/url_request_interceptor.cpp b/src/yuzu/util/url_request_interceptor.cpp
index 2d491d8c0..b637e771e 100644
--- a/src/yuzu/util/url_request_interceptor.cpp
+++ b/src/yuzu/util/url_request_interceptor.cpp
@@ -22,6 +22,8 @@ void UrlRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info) {
22 case QWebEngineUrlRequestInfo::ResourceTypeXhr: 22 case QWebEngineUrlRequestInfo::ResourceTypeXhr:
23 emit FrameChanged(); 23 emit FrameChanged();
24 break; 24 break;
25 default:
26 break;
25 } 27 }
26} 28}
27 29
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 38075c345..41ef6f6b8 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -344,7 +344,7 @@ void Config::ReadValues() {
344 344
345 // System 345 // System
346 Settings::values.use_docked_mode.SetValue( 346 Settings::values.use_docked_mode.SetValue(
347 sdl2_config->GetBoolean("System", "use_docked_mode", false)); 347 sdl2_config->GetBoolean("System", "use_docked_mode", true));
348 348
349 Settings::values.current_user = std::clamp<int>( 349 Settings::values.current_user = std::clamp<int>(
350 sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1); 350 sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1);
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 2d4b98d9a..3ee0e037d 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -274,7 +274,7 @@ gamecard_path =
274 274
275[System] 275[System]
276# Whether the system is docked 276# Whether the system is docked
277# 1: Yes, 0 (default): No 277# 1 (default): Yes, 0: No
278use_docked_mode = 278use_docked_mode =
279 279
280# Allow the use of NFC in games 280# Allow the use of NFC in games
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 2497c71ae..4faf62ede 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -95,8 +95,6 @@ int main(int argc, char** argv) {
95 int option_index = 0; 95 int option_index = 0;
96 96
97 InitializeLogging(); 97 InitializeLogging();
98
99 char* endarg;
100#ifdef _WIN32 98#ifdef _WIN32
101 int argc_w; 99 int argc_w;
102 auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w); 100 auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w);
@@ -202,7 +200,7 @@ int main(int argc, char** argv) {
202 const u16 loader_id = static_cast<u16>(Core::System::ResultStatus::ErrorLoader); 200 const u16 loader_id = static_cast<u16>(Core::System::ResultStatus::ErrorLoader);
203 const u16 error_id = static_cast<u16>(load_result) - loader_id; 201 const u16 error_id = static_cast<u16>(load_result) - loader_id;
204 LOG_CRITICAL(Frontend, 202 LOG_CRITICAL(Frontend,
205 "While attempting to load the ROM requested, an error occured. Please " 203 "While attempting to load the ROM requested, an error occurred. Please "
206 "refer to the yuzu wiki for more information or the yuzu discord for " 204 "refer to the yuzu wiki for more information or the yuzu discord for "
207 "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}", 205 "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}",
208 loader_id, error_id, static_cast<Loader::ResultStatus>(error_id)); 206 loader_id, error_id, static_cast<Loader::ResultStatus>(error_id));
diff --git a/src/yuzu_tester/CMakeLists.txt b/src/yuzu_tester/CMakeLists.txt
deleted file mode 100644
index d8a2a1511..000000000
--- a/src/yuzu_tester/CMakeLists.txt
+++ /dev/null
@@ -1,32 +0,0 @@
1set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules)
2
3add_executable(yuzu-tester
4 config.cpp
5 config.h
6 default_ini.h
7 emu_window/emu_window_sdl2_hide.cpp
8 emu_window/emu_window_sdl2_hide.h
9 resource.h
10 service/yuzutest.cpp
11 service/yuzutest.h
12 yuzu.cpp
13 yuzu.rc
14)
15
16create_target_directory_groups(yuzu-tester)
17
18target_link_libraries(yuzu-tester PRIVATE common core input_common)
19target_link_libraries(yuzu-tester PRIVATE inih glad)
20if (MSVC)
21 target_link_libraries(yuzu-tester PRIVATE getopt)
22endif()
23target_link_libraries(yuzu-tester PRIVATE ${PLATFORM_LIBRARIES} SDL2 Threads::Threads)
24
25if(UNIX AND NOT APPLE)
26 install(TARGETS yuzu-tester RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
27endif()
28
29if (MSVC)
30 include(CopyYuzuSDLDeps)
31 copy_yuzu_SDL_deps(yuzu-tester)
32endif()
diff --git a/src/yuzu_tester/config.cpp b/src/yuzu_tester/config.cpp
deleted file mode 100644
index 91684e96e..000000000
--- a/src/yuzu_tester/config.cpp
+++ /dev/null
@@ -1,194 +0,0 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <memory>
6#include <sstream>
7#include <SDL.h>
8#include <inih/cpp/INIReader.h>
9#include "common/file_util.h"
10#include "common/logging/log.h"
11#include "common/param_package.h"
12#include "core/hle/service/acc/profile_manager.h"
13#include "core/settings.h"
14#include "input_common/main.h"
15#include "yuzu_tester/config.h"
16#include "yuzu_tester/default_ini.h"
17
18namespace FS = Common::FS;
19
20Config::Config() {
21 // TODO: Don't hardcode the path; let the frontend decide where to put the config files.
22 sdl2_config_loc = FS::GetUserPath(FS::UserPath::ConfigDir) + "sdl2-tester-config.ini";
23 sdl2_config = std::make_unique<INIReader>(sdl2_config_loc);
24
25 Reload();
26}
27
28Config::~Config() = default;
29
30bool Config::LoadINI(const std::string& default_contents, bool retry) {
31 const char* location = this->sdl2_config_loc.c_str();
32 if (sdl2_config->ParseError() < 0) {
33 if (retry) {
34 LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", location);
35 FS::CreateFullPath(location);
36 FS::WriteStringToFile(true, default_contents, location);
37 sdl2_config = std::make_unique<INIReader>(location); // Reopen file
38
39 return LoadINI(default_contents, false);
40 }
41 LOG_ERROR(Config, "Failed.");
42 return false;
43 }
44 LOG_INFO(Config, "Successfully loaded {}", location);
45 return true;
46}
47
48void Config::ReadValues() {
49 // Controls
50 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
51 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
52 Settings::values.players.GetValue()[p].buttons[i] = "";
53 }
54
55 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
56 Settings::values.players.GetValue()[p].analogs[i] = "";
57 }
58 }
59
60 Settings::values.mouse_enabled = false;
61 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
62 Settings::values.mouse_buttons[i] = "";
63 }
64
65 Settings::values.motion_device = "";
66
67 Settings::values.keyboard_enabled = false;
68
69 Settings::values.debug_pad_enabled = false;
70 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
71 Settings::values.debug_pad_buttons[i] = "";
72 }
73
74 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
75 Settings::values.debug_pad_analogs[i] = "";
76 }
77
78 Settings::values.vibration_enabled.SetValue(true);
79 Settings::values.enable_accurate_vibrations.SetValue(false);
80 Settings::values.motion_enabled.SetValue(true);
81 Settings::values.touchscreen.enabled = "";
82 Settings::values.touchscreen.device = "";
83 Settings::values.touchscreen.finger = 0;
84 Settings::values.touchscreen.rotation_angle = 0;
85 Settings::values.touchscreen.diameter_x = 15;
86 Settings::values.touchscreen.diameter_y = 15;
87
88 Settings::values.use_docked_mode.SetValue(
89 sdl2_config->GetBoolean("Controls", "use_docked_mode", false));
90
91 // Data Storage
92 Settings::values.use_virtual_sd =
93 sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true);
94 FS::GetUserPath(Common::FS::UserPath::NANDDir,
95 sdl2_config->Get("Data Storage", "nand_directory",
96 Common::FS::GetUserPath(Common::FS::UserPath::NANDDir)));
97 FS::GetUserPath(Common::FS::UserPath::SDMCDir,
98 sdl2_config->Get("Data Storage", "sdmc_directory",
99 Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir)));
100
101 // System
102 Settings::values.current_user = std::clamp<int>(
103 sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1);
104
105 const auto rng_seed_enabled = sdl2_config->GetBoolean("System", "rng_seed_enabled", false);
106 if (rng_seed_enabled) {
107 Settings::values.rng_seed.SetValue(sdl2_config->GetInteger("System", "rng_seed", 0));
108 } else {
109 Settings::values.rng_seed.SetValue(std::nullopt);
110 }
111
112 const auto custom_rtc_enabled = sdl2_config->GetBoolean("System", "custom_rtc_enabled", false);
113 if (custom_rtc_enabled) {
114 Settings::values.custom_rtc.SetValue(
115 std::chrono::seconds(sdl2_config->GetInteger("System", "custom_rtc", 0)));
116 } else {
117 Settings::values.custom_rtc.SetValue(std::nullopt);
118 }
119
120 // Core
121 Settings::values.use_multi_core.SetValue(
122 sdl2_config->GetBoolean("Core", "use_multi_core", false));
123
124 // Renderer
125 Settings::values.aspect_ratio.SetValue(
126 static_cast<int>(sdl2_config->GetInteger("Renderer", "aspect_ratio", 0)));
127 Settings::values.max_anisotropy.SetValue(
128 static_cast<int>(sdl2_config->GetInteger("Renderer", "max_anisotropy", 0)));
129 Settings::values.use_frame_limit.SetValue(false);
130 Settings::values.frame_limit.SetValue(100);
131 Settings::values.use_disk_shader_cache.SetValue(
132 sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false));
133 const int gpu_accuracy_level = sdl2_config->GetInteger("Renderer", "gpu_accuracy", 0);
134 Settings::values.gpu_accuracy.SetValue(static_cast<Settings::GPUAccuracy>(gpu_accuracy_level));
135 Settings::values.use_asynchronous_gpu_emulation.SetValue(
136 sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false));
137 Settings::values.use_fast_gpu_time.SetValue(
138 sdl2_config->GetBoolean("Renderer", "use_fast_gpu_time", true));
139
140 Settings::values.bg_red.SetValue(
141 static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0)));
142 Settings::values.bg_green.SetValue(
143 static_cast<float>(sdl2_config->GetReal("Renderer", "bg_green", 0.0)));
144 Settings::values.bg_blue.SetValue(
145 static_cast<float>(sdl2_config->GetReal("Renderer", "bg_blue", 0.0)));
146
147 // Audio
148 Settings::values.sink_id = "null";
149 Settings::values.enable_audio_stretching.SetValue(false);
150 Settings::values.audio_device_id = "auto";
151 Settings::values.volume.SetValue(0);
152
153 Settings::values.language_index.SetValue(
154 sdl2_config->GetInteger("System", "language_index", 1));
155
156 // Miscellaneous
157 Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace");
158 Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false);
159
160 // Debugging
161 Settings::values.program_args = "";
162 Settings::values.dump_exefs = sdl2_config->GetBoolean("Debugging", "dump_exefs", false);
163 Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false);
164
165 const auto title_list = sdl2_config->Get("AddOns", "title_ids", "");
166 std::stringstream ss(title_list);
167 std::string line;
168 while (std::getline(ss, line, '|')) {
169 const auto title_id = std::stoul(line, nullptr, 16);
170 const auto disabled_list = sdl2_config->Get("AddOns", "disabled_" + line, "");
171
172 std::stringstream inner_ss(disabled_list);
173 std::string inner_line;
174 std::vector<std::string> out;
175 while (std::getline(inner_ss, inner_line, '|')) {
176 out.push_back(inner_line);
177 }
178
179 Settings::values.disabled_addons.insert_or_assign(title_id, out);
180 }
181
182 // Web Service
183 Settings::values.enable_telemetry =
184 sdl2_config->GetBoolean("WebService", "enable_telemetry", true);
185 Settings::values.web_api_url =
186 sdl2_config->Get("WebService", "web_api_url", "https://api.yuzu-emu.org");
187 Settings::values.yuzu_username = sdl2_config->Get("WebService", "yuzu_username", "");
188 Settings::values.yuzu_token = sdl2_config->Get("WebService", "yuzu_token", "");
189}
190
191void Config::Reload() {
192 LoadINI(DefaultINI::sdl2_config_file);
193 ReadValues();
194}
diff --git a/src/yuzu_tester/config.h b/src/yuzu_tester/config.h
deleted file mode 100644
index 3b68e5bc9..000000000
--- a/src/yuzu_tester/config.h
+++ /dev/null
@@ -1,24 +0,0 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <string>
9
10class INIReader;
11
12class Config {
13 std::unique_ptr<INIReader> sdl2_config;
14 std::string sdl2_config_loc;
15
16 bool LoadINI(const std::string& default_contents = "", bool retry = true);
17 void ReadValues();
18
19public:
20 Config();
21 ~Config();
22
23 void Reload();
24};
diff --git a/src/yuzu_tester/default_ini.h b/src/yuzu_tester/default_ini.h
deleted file mode 100644
index 3eb64e9d7..000000000
--- a/src/yuzu_tester/default_ini.h
+++ /dev/null
@@ -1,182 +0,0 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7namespace DefaultINI {
8
9const char* sdl2_config_file = R"(
10[Core]
11# Whether to use multi-core for CPU emulation
12# 0 (default): Disabled, 1: Enabled
13use_multi_core=
14
15[Cpu]
16# Enable inline page tables optimization (faster guest memory access)
17# 0: Disabled, 1 (default): Enabled
18cpuopt_page_tables =
19
20# Enable block linking CPU optimization (reduce block dispatcher use during predictable jumps)
21# 0: Disabled, 1 (default): Enabled
22cpuopt_block_linking =
23
24# Enable return stack buffer CPU optimization (reduce block dispatcher use during predictable returns)
25# 0: Disabled, 1 (default): Enabled
26cpuopt_return_stack_buffer =
27
28# Enable fast dispatcher CPU optimization (use a two-tiered dispatcher architecture)
29# 0: Disabled, 1 (default): Enabled
30cpuopt_fast_dispatcher =
31
32# Enable context elimination CPU Optimization (reduce host memory use for guest context)
33# 0: Disabled, 1 (default): Enabled
34cpuopt_context_elimination =
35
36# Enable constant propagation CPU optimization (basic IR optimization)
37# 0: Disabled, 1 (default): Enabled
38cpuopt_const_prop =
39
40# Enable miscellaneous CPU optimizations (basic IR optimization)
41# 0: Disabled, 1 (default): Enabled
42cpuopt_misc_ir =
43
44# Enable reduction of memory misalignment checks (reduce memory fallbacks for misaligned access)
45# 0: Disabled, 1 (default): Enabled
46cpuopt_reduce_misalign_checks =
47
48[Renderer]
49# Whether to use software or hardware rendering.
50# 0: Software, 1 (default): Hardware
51use_hw_renderer =
52
53# Whether to use the Just-In-Time (JIT) compiler for shader emulation
54# 0: Interpreter (slow), 1 (default): JIT (fast)
55use_shader_jit =
56
57# Aspect ratio
58# 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Stretch to Window
59aspect_ratio =
60
61# Anisotropic filtering
62# 0: Default, 1: 2x, 2: 4x, 3: 8x, 4: 16x
63max_anisotropy =
64
65# Whether to enable V-Sync (caps the framerate at 60FPS) or not.
66# 0 (default): Off, 1: On
67use_vsync =
68
69# Whether to use disk based shader cache
70# 0 (default): Off, 1 : On
71use_disk_shader_cache =
72
73# Whether to use accurate GPU emulation
74# 0 (default): Off (fast), 1 : On (slow)
75use_accurate_gpu_emulation =
76
77# Whether to use asynchronous GPU emulation
78# 0 : Off (slow), 1 (default): On (fast)
79use_asynchronous_gpu_emulation =
80
81# The clear color for the renderer. What shows up on the sides of the bottom screen.
82# Must be in range of 0.0-1.0. Defaults to 1.0 for all.
83bg_red =
84bg_blue =
85bg_green =
86
87[Layout]
88# Layout for the screen inside the render window.
89# 0 (default): Default Top Bottom Screen, 1: Single Screen Only, 2: Large Screen Small Screen
90layout_option =
91
92# Toggle custom layout (using the settings below) on or off.
93# 0 (default): Off, 1: On
94custom_layout =
95
96# Screen placement when using Custom layout option
97# 0x, 0y is the top left corner of the render window.
98custom_top_left =
99custom_top_top =
100custom_top_right =
101custom_top_bottom =
102custom_bottom_left =
103custom_bottom_top =
104custom_bottom_right =
105custom_bottom_bottom =
106
107# Swaps the prominent screen with the other screen.
108# For example, if Single Screen is chosen, setting this to 1 will display the bottom screen instead of the top screen.
109# 0 (default): Top Screen is prominent, 1: Bottom Screen is prominent
110swap_screen =
111
112[Data Storage]
113# Whether to create a virtual SD card.
114# 1 (default): Yes, 0: No
115use_virtual_sd =
116
117[System]
118# Whether the system is docked
119# 1: Yes, 0 (default): No
120use_docked_mode =
121
122# Allow the use of NFC in games
123# 1 (default): Yes, 0 : No
124enable_nfc =
125
126# Sets the seed for the RNG generator built into the switch
127# rng_seed will be ignored and randomly generated if rng_seed_enabled is false
128rng_seed_enabled =
129rng_seed =
130
131# Sets the current time (in seconds since 12:00 AM Jan 1, 1970) that will be used by the time service
132# This will auto-increment, with the time set being the time the game is started
133# This override will only occur if custom_rtc_enabled is true, otherwise the current time is used
134custom_rtc_enabled =
135custom_rtc =
136
137# Sets the account username, max length is 32 characters
138# yuzu (default)
139username = yuzu
140
141# Sets the systems language index
142# 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese,
143# 7: Korean, 8: Dutch, 9: Portuguese, 10: Russian, 11: Taiwanese, 12: British English, 13: Canadian French,
144# 14: Latin American Spanish, 15: Simplified Chinese, 16: Traditional Chinese
145language_index =
146
147# The system region that yuzu will use during emulation
148# -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan
149region_value =
150
151[Miscellaneous]
152# A filter which removes logs below a certain logging level.
153# Examples: *:Debug Kernel.SVC:Trace Service.*:Critical
154log_filter = *:Trace
155
156[Debugging]
157# Arguments to be passed to argv/argc in the emulated program. It is preferable to use the testing service datastring
158program_args=
159# Determines whether or not yuzu will dump the ExeFS of all games it attempts to load while loading them
160dump_exefs=false
161# Determines whether or not yuzu will dump all NSOs it attempts to load while loading them
162dump_nso=false
163
164[WebService]
165# Whether or not to enable telemetry
166# 0: No, 1 (default): Yes
167enable_telemetry =
168# URL for Web API
169web_api_url = https://api.yuzu-emu.org
170# Username and token for yuzu Web Service
171# See https://profile.yuzu-emu.org/ for more info
172yuzu_username =
173yuzu_token =
174
175[AddOns]
176# Used to disable add-ons
177# List of title IDs of games that will have add-ons disabled (separated by '|'):
178title_ids =
179# For each title ID, have a key/value pair called `disabled_<title_id>` equal to the names of the add-ons to disable (sep. by '|')
180# e.x. disabled_0100000000010000 = Update|DLC <- disables Updates and DLC on Super Mario Odyssey
181)";
182}
diff --git a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp
deleted file mode 100644
index 358e03870..000000000
--- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp
+++ /dev/null
@@ -1,146 +0,0 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <cstdlib>
7#include <string>
8
9#include <fmt/format.h>
10
11#define SDL_MAIN_HANDLED
12#include <SDL.h>
13
14#include <glad/glad.h>
15
16#include "common/logging/log.h"
17#include "common/scm_rev.h"
18#include "core/settings.h"
19#include "input_common/main.h"
20#include "yuzu_tester/emu_window/emu_window_sdl2_hide.h"
21
22bool EmuWindow_SDL2_Hide::SupportsRequiredGLExtensions() {
23 std::vector<std::string> unsupported_ext;
24
25 if (!GLAD_GL_ARB_direct_state_access)
26 unsupported_ext.push_back("ARB_direct_state_access");
27 if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev)
28 unsupported_ext.push_back("ARB_vertex_type_10f_11f_11f_rev");
29 if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge)
30 unsupported_ext.push_back("ARB_texture_mirror_clamp_to_edge");
31 if (!GLAD_GL_ARB_multi_bind)
32 unsupported_ext.push_back("ARB_multi_bind");
33
34 // Extensions required to support some texture formats.
35 if (!GLAD_GL_EXT_texture_compression_s3tc)
36 unsupported_ext.push_back("EXT_texture_compression_s3tc");
37 if (!GLAD_GL_ARB_texture_compression_rgtc)
38 unsupported_ext.push_back("ARB_texture_compression_rgtc");
39 if (!GLAD_GL_ARB_depth_buffer_float)
40 unsupported_ext.push_back("ARB_depth_buffer_float");
41
42 for (const std::string& ext : unsupported_ext)
43 LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext);
44
45 return unsupported_ext.empty();
46}
47
48EmuWindow_SDL2_Hide::EmuWindow_SDL2_Hide() {
49 // Initialize the window
50 if (SDL_Init(SDL_INIT_VIDEO) < 0) {
51 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting...");
52 exit(1);
53 }
54
55 input_subsystem->Initialize();
56
57 SDL_SetMainReady();
58
59 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
60 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
61 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
62 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
63 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
64 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
65 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
66 SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0);
67
68 std::string window_title = fmt::format("yuzu-tester {} | {}-{}", Common::g_build_fullname,
69 Common::g_scm_branch, Common::g_scm_desc);
70 render_window = SDL_CreateWindow(window_title.c_str(),
71 SDL_WINDOWPOS_UNDEFINED, // x position
72 SDL_WINDOWPOS_UNDEFINED, // y position
73 Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height,
74 SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE |
75 SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN);
76
77 if (render_window == nullptr) {
78 LOG_CRITICAL(Frontend, "Failed to create SDL2 window! {}", SDL_GetError());
79 exit(1);
80 }
81
82 gl_context = SDL_GL_CreateContext(render_window);
83
84 if (gl_context == nullptr) {
85 LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context! {}", SDL_GetError());
86 exit(1);
87 }
88
89 if (!gladLoadGLLoader(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) {
90 LOG_CRITICAL(Frontend, "Failed to initialize GL functions! {}", SDL_GetError());
91 exit(1);
92 }
93
94 if (!SupportsRequiredGLExtensions()) {
95 LOG_CRITICAL(Frontend, "GPU does not support all required OpenGL extensions! Exiting...");
96 exit(1);
97 }
98
99 SDL_PumpEvents();
100 SDL_GL_SetSwapInterval(false);
101 LOG_INFO(Frontend, "yuzu-tester Version: {} | {}-{}", Common::g_build_fullname,
102 Common::g_scm_branch, Common::g_scm_desc);
103 Settings::LogSettings();
104}
105
106EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() {
107 input_subsystem->Shutdown();
108 SDL_GL_DeleteContext(gl_context);
109 SDL_Quit();
110}
111
112bool EmuWindow_SDL2_Hide::IsShown() const {
113 return false;
114}
115
116class SDLGLContext : public Core::Frontend::GraphicsContext {
117public:
118 explicit SDLGLContext() {
119 // create a hidden window to make the shared context against
120 window = SDL_CreateWindow(NULL, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 0, 0,
121 SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL);
122 context = SDL_GL_CreateContext(window);
123 }
124
125 ~SDLGLContext() {
126 DoneCurrent();
127 SDL_GL_DeleteContext(context);
128 SDL_DestroyWindow(window);
129 }
130
131 void MakeCurrent() override {
132 SDL_GL_MakeCurrent(window, context);
133 }
134
135 void DoneCurrent() override {
136 SDL_GL_MakeCurrent(window, nullptr);
137 }
138
139private:
140 SDL_Window* window;
141 SDL_GLContext context;
142};
143
144std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_Hide::CreateSharedContext() const {
145 return std::make_unique<SDLGLContext>();
146}
diff --git a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h
deleted file mode 100644
index adccdf35e..000000000
--- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h
+++ /dev/null
@@ -1,37 +0,0 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/frontend/emu_window.h"
8
9struct SDL_Window;
10
11namespace InputCommon {
12class InputSubsystem;
13}
14
15class EmuWindow_SDL2_Hide : public Core::Frontend::EmuWindow {
16public:
17 explicit EmuWindow_SDL2_Hide();
18 ~EmuWindow_SDL2_Hide();
19
20 /// Whether the screen is being shown or not.
21 bool IsShown() const override;
22
23 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
24
25private:
26 /// Whether the GPU and driver supports the OpenGL extension required
27 bool SupportsRequiredGLExtensions();
28
29 std::unique_ptr<InputCommon::InputSubsystem> input_subsystem;
30
31 /// Internal SDL2 render window
32 SDL_Window* render_window;
33
34 using SDL_GLContext = void*;
35 /// The OpenGL context associated with the window
36 SDL_GLContext gl_context;
37};
diff --git a/src/yuzu_tester/resource.h b/src/yuzu_tester/resource.h
deleted file mode 100644
index df8e459e4..000000000
--- a/src/yuzu_tester/resource.h
+++ /dev/null
@@ -1,16 +0,0 @@
1//{{NO_DEPENDENCIES}}
2// Microsoft Visual C++ generated include file.
3// Used by pcafe.rc
4//
5#define IDI_ICON3 103
6
7// Next default values for new objects
8//
9#ifdef APSTUDIO_INVOKED
10#ifndef APSTUDIO_READONLY_SYMBOLS
11#define _APS_NEXT_RESOURCE_VALUE 105
12#define _APS_NEXT_COMMAND_VALUE 40001
13#define _APS_NEXT_CONTROL_VALUE 1001
14#define _APS_NEXT_SYMED_VALUE 101
15#endif
16#endif
diff --git a/src/yuzu_tester/service/yuzutest.cpp b/src/yuzu_tester/service/yuzutest.cpp
deleted file mode 100644
index e257fae25..000000000
--- a/src/yuzu_tester/service/yuzutest.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <memory>
6#include "common/string_util.h"
7#include "core/core.h"
8#include "core/hle/ipc_helpers.h"
9#include "core/hle/service/service.h"
10#include "core/hle/service/sm/sm.h"
11#include "yuzu_tester/service/yuzutest.h"
12
13namespace Service::Yuzu {
14
15constexpr u64 SERVICE_VERSION = 0x00000002;
16
17class YuzuTest final : public ServiceFramework<YuzuTest> {
18public:
19 explicit YuzuTest(Core::System& system_, std::string data_,
20 std::function<void(std::vector<TestResult>)> finish_callback_)
21 : ServiceFramework{system_, "yuzutest"}, data{std::move(data_)}, finish_callback{std::move(
22 finish_callback_)} {
23 static const FunctionInfo functions[] = {
24 {0, &YuzuTest::Initialize, "Initialize"},
25 {1, &YuzuTest::GetServiceVersion, "GetServiceVersion"},
26 {2, &YuzuTest::GetData, "GetData"},
27 {10, &YuzuTest::StartIndividual, "StartIndividual"},
28 {20, &YuzuTest::FinishIndividual, "FinishIndividual"},
29 {100, &YuzuTest::ExitProgram, "ExitProgram"},
30 };
31
32 RegisterHandlers(functions);
33 }
34
35private:
36 void Initialize(Kernel::HLERequestContext& ctx) {
37 LOG_DEBUG(Frontend, "called");
38 IPC::ResponseBuilder rb{ctx, 2};
39 rb.Push(RESULT_SUCCESS);
40 }
41
42 void GetServiceVersion(Kernel::HLERequestContext& ctx) {
43 LOG_DEBUG(Frontend, "called");
44 IPC::ResponseBuilder rb{ctx, 4};
45 rb.Push(RESULT_SUCCESS);
46 rb.Push(SERVICE_VERSION);
47 }
48
49 void GetData(Kernel::HLERequestContext& ctx) {
50 LOG_DEBUG(Frontend, "called");
51 const auto size = ctx.GetWriteBufferSize();
52 const auto write_size = std::min(size, data.size());
53 ctx.WriteBuffer(data.data(), write_size);
54
55 IPC::ResponseBuilder rb{ctx, 3};
56 rb.Push(RESULT_SUCCESS);
57 rb.Push<u32>(static_cast<u32>(write_size));
58 }
59
60 void StartIndividual(Kernel::HLERequestContext& ctx) {
61 const auto name_raw = ctx.ReadBuffer();
62
63 const auto name = Common::StringFromFixedZeroTerminatedBuffer(
64 reinterpret_cast<const char*>(name_raw.data()), name_raw.size());
65
66 LOG_DEBUG(Frontend, "called, name={}", name);
67
68 IPC::ResponseBuilder rb{ctx, 2};
69 rb.Push(RESULT_SUCCESS);
70 }
71
72 void FinishIndividual(Kernel::HLERequestContext& ctx) {
73 IPC::RequestParser rp{ctx};
74
75 const auto code = rp.PopRaw<u32>();
76
77 const auto result_data_raw = ctx.ReadBuffer();
78 const auto test_name_raw = ctx.ReadBuffer(1);
79
80 const auto data = Common::StringFromFixedZeroTerminatedBuffer(
81 reinterpret_cast<const char*>(result_data_raw.data()), result_data_raw.size());
82 const auto test_name = Common::StringFromFixedZeroTerminatedBuffer(
83 reinterpret_cast<const char*>(test_name_raw.data()), test_name_raw.size());
84
85 LOG_INFO(Frontend, "called, result_code={:08X}, data={}, name={}", code, data, test_name);
86
87 results.push_back({code, data, test_name});
88
89 IPC::ResponseBuilder rb{ctx, 2};
90 rb.Push(RESULT_SUCCESS);
91 }
92
93 void ExitProgram(Kernel::HLERequestContext& ctx) {
94 LOG_DEBUG(Frontend, "called");
95
96 IPC::ResponseBuilder rb{ctx, 2};
97 rb.Push(RESULT_SUCCESS);
98
99 finish_callback(std::move(results));
100 }
101
102 std::string data;
103
104 std::vector<TestResult> results;
105 std::function<void(std::vector<TestResult>)> finish_callback;
106};
107
108void InstallInterfaces(Core::System& system, std::string data,
109 std::function<void(std::vector<TestResult>)> finish_callback) {
110 auto& sm = system.ServiceManager();
111 std::make_shared<YuzuTest>(system, std::move(data), std::move(finish_callback))
112 ->InstallAsService(sm);
113}
114
115} // namespace Service::Yuzu
diff --git a/src/yuzu_tester/service/yuzutest.h b/src/yuzu_tester/service/yuzutest.h
deleted file mode 100644
index 7794814fa..000000000
--- a/src/yuzu_tester/service/yuzutest.h
+++ /dev/null
@@ -1,25 +0,0 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <functional>
8#include <string>
9
10namespace Core {
11class System;
12}
13
14namespace Service::Yuzu {
15
16struct TestResult {
17 u32 code;
18 std::string data;
19 std::string name;
20};
21
22void InstallInterfaces(Core::System& system, std::string data,
23 std::function<void(std::vector<TestResult>)> finish_callback);
24
25} // namespace Service::Yuzu
diff --git a/src/yuzu_tester/yuzu.cpp b/src/yuzu_tester/yuzu.cpp
deleted file mode 100644
index 6435ffabb..000000000
--- a/src/yuzu_tester/yuzu.cpp
+++ /dev/null
@@ -1,268 +0,0 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <chrono>
6#include <iostream>
7#include <memory>
8#include <string>
9#include <thread>
10
11#include <fmt/ostream.h>
12
13#include "common/common_paths.h"
14#include "common/detached_tasks.h"
15#include "common/file_util.h"
16#include "common/logging/backend.h"
17#include "common/logging/filter.h"
18#include "common/logging/log.h"
19#include "common/microprofile.h"
20#include "common/scm_rev.h"
21#include "common/scope_exit.h"
22#include "common/string_util.h"
23#include "common/telemetry.h"
24#include "core/core.h"
25#include "core/crypto/key_manager.h"
26#include "core/file_sys/registered_cache.h"
27#include "core/file_sys/vfs_real.h"
28#include "core/hle/service/filesystem/filesystem.h"
29#include "core/loader/loader.h"
30#include "core/settings.h"
31#include "core/telemetry_session.h"
32#include "video_core/renderer_base.h"
33#include "yuzu_tester/config.h"
34#include "yuzu_tester/emu_window/emu_window_sdl2_hide.h"
35#include "yuzu_tester/service/yuzutest.h"
36
37#ifdef _WIN32
38// windows.h needs to be included before shellapi.h
39#include <windows.h>
40
41#include <shellapi.h>
42#endif
43
44#undef _UNICODE
45#include <getopt.h>
46#ifndef _MSC_VER
47#include <unistd.h>
48#endif
49
50#ifdef _WIN32
51extern "C" {
52// tells Nvidia and AMD drivers to use the dedicated GPU by default on laptops with switchable
53// graphics
54__declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001;
55__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
56}
57#endif
58
59static void PrintHelp(const char* argv0) {
60 std::cout << "Usage: " << argv0
61 << " [options] <filename>\n"
62 "-h, --help Display this help and exit\n"
63 "-v, --version Output version information and exit\n"
64 "-d, --datastring Pass following string as data to test service command #2\n"
65 "-l, --log Log to console in addition to file (will log to file only "
66 "by default)\n";
67}
68
69static void PrintVersion() {
70 std::cout << "yuzu [Test Utility] " << Common::g_scm_branch << " " << Common::g_scm_desc
71 << std::endl;
72}
73
74static void InitializeLogging(bool console) {
75 Log::Filter log_filter(Log::Level::Debug);
76 log_filter.ParseFilterString(Settings::values.log_filter);
77 Log::SetGlobalFilter(log_filter);
78
79 if (console)
80 Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
81
82 const std::string& log_dir = Common::FS::GetUserPath(Common::FS::UserPath::LogDir);
83 Common::FS::CreateFullPath(log_dir);
84 Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE));
85#ifdef _WIN32
86 Log::AddBackend(std::make_unique<Log::DebuggerBackend>());
87#endif
88}
89
90/// Application entry point
91int main(int argc, char** argv) {
92 Common::DetachedTasks detached_tasks;
93 Config config;
94
95 int option_index = 0;
96
97#ifdef _WIN32
98 int argc_w;
99 auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w);
100
101 if (argv_w == nullptr) {
102 std::cout << "Failed to get command line arguments" << std::endl;
103 return -1;
104 }
105#endif
106 std::string filepath;
107
108 static struct option long_options[] = {
109 {"help", no_argument, 0, 'h'},
110 {"version", no_argument, 0, 'v'},
111 {"datastring", optional_argument, 0, 'd'},
112 {"log", no_argument, 0, 'l'},
113 {0, 0, 0, 0},
114 };
115
116 bool console_log = false;
117 std::string datastring;
118
119 while (optind < argc) {
120 int arg = getopt_long(argc, argv, "hvdl::", long_options, &option_index);
121 if (arg != -1) {
122 switch (static_cast<char>(arg)) {
123 case 'h':
124 PrintHelp(argv[0]);
125 return 0;
126 case 'v':
127 PrintVersion();
128 return 0;
129 case 'd':
130 datastring = argv[optind];
131 ++optind;
132 break;
133 case 'l':
134 console_log = true;
135 break;
136 }
137 } else {
138#ifdef _WIN32
139 filepath = Common::UTF16ToUTF8(argv_w[optind]);
140#else
141 filepath = argv[optind];
142#endif
143 optind++;
144 }
145 }
146
147 InitializeLogging(console_log);
148
149#ifdef _WIN32
150 LocalFree(argv_w);
151#endif
152
153 MicroProfileOnThreadCreate("EmuThread");
154 SCOPE_EXIT({ MicroProfileShutdown(); });
155
156 if (filepath.empty()) {
157 LOG_CRITICAL(Frontend, "Failed to load application: No application specified");
158 std::cout << "Failed to load application: No application specified" << std::endl;
159 PrintHelp(argv[0]);
160 return -1;
161 }
162
163 Core::System& system{Core::System::GetInstance()};
164
165 Settings::Apply(system);
166
167 const auto emu_window{std::make_unique<EmuWindow_SDL2_Hide>()};
168
169 bool finished = false;
170 int return_value = 0;
171 const auto callback = [&finished,
172 &return_value](std::vector<Service::Yuzu::TestResult> results) {
173 finished = true;
174 return_value = 0;
175
176 // Find the minimum length needed to fully enclose all test names (and the header field) in
177 // the fmt::format column by first finding the maximum size of any test name and comparing
178 // that to 9, the string length of 'Test Name'
179 const auto needed_length_name =
180 std::max<u64>(std::max_element(results.begin(), results.end(),
181 [](const auto& lhs, const auto& rhs) {
182 return lhs.name.size() < rhs.name.size();
183 })
184 ->name.size(),
185 9ull);
186
187 std::size_t passed = 0;
188 std::size_t failed = 0;
189
190 std::cout << fmt::format("Result [Res Code] | {:<{}} | Extra Data", "Test Name",
191 needed_length_name)
192 << std::endl;
193
194 for (const auto& res : results) {
195 const auto main_res = res.code == 0 ? "PASSED" : "FAILED";
196 if (res.code == 0)
197 ++passed;
198 else
199 ++failed;
200 std::cout << fmt::format("{} [{:08X}] | {:<{}} | {}", main_res, res.code, res.name,
201 needed_length_name, res.data)
202 << std::endl;
203 }
204
205 std::cout << std::endl
206 << fmt::format("{:4d} Passed | {:4d} Failed | {:4d} Total | {:2.2f} Passed Ratio",
207 passed, failed, passed + failed,
208 static_cast<float>(passed) / (passed + failed))
209 << std::endl
210 << (failed == 0 ? "PASSED" : "FAILED") << std::endl;
211
212 if (failed > 0)
213 return_value = -1;
214 };
215
216 system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
217 system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
218 system.GetFileSystemController().CreateFactories(*system.GetFilesystem());
219
220 SCOPE_EXIT({ system.Shutdown(); });
221
222 const Core::System::ResultStatus load_result{system.Load(*emu_window, filepath)};
223
224 switch (load_result) {
225 case Core::System::ResultStatus::ErrorGetLoader:
226 LOG_CRITICAL(Frontend, "Failed to obtain loader for {}!", filepath);
227 return -1;
228 case Core::System::ResultStatus::ErrorLoader:
229 LOG_CRITICAL(Frontend, "Failed to load ROM!");
230 return -1;
231 case Core::System::ResultStatus::ErrorNotInitialized:
232 LOG_CRITICAL(Frontend, "CPUCore not initialized");
233 return -1;
234 case Core::System::ResultStatus::ErrorVideoCore:
235 LOG_CRITICAL(Frontend, "Failed to initialize VideoCore!");
236 return -1;
237 case Core::System::ResultStatus::Success:
238 break; // Expected case
239 default:
240 if (static_cast<u32>(load_result) >
241 static_cast<u32>(Core::System::ResultStatus::ErrorLoader)) {
242 const u16 loader_id = static_cast<u16>(Core::System::ResultStatus::ErrorLoader);
243 const u16 error_id = static_cast<u16>(load_result) - loader_id;
244 LOG_CRITICAL(Frontend,
245 "While attempting to load the ROM requested, an error occured. Please "
246 "refer to the yuzu wiki for more information or the yuzu discord for "
247 "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}",
248 loader_id, error_id, static_cast<Loader::ResultStatus>(error_id));
249 }
250 break;
251 }
252
253 Service::Yuzu::InstallInterfaces(system, datastring, callback);
254
255 system.TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend",
256 "SDLHideTester");
257
258 system.GPU().Start();
259
260 void(system.Run());
261 while (!finished) {
262 std::this_thread::sleep_for(std::chrono::milliseconds(1));
263 }
264 void(system.Pause());
265
266 detached_tasks.WaitForAllTasks();
267 return return_value;
268}
diff --git a/src/yuzu_tester/yuzu.rc b/src/yuzu_tester/yuzu.rc
deleted file mode 100644
index 0cde75e2f..000000000
--- a/src/yuzu_tester/yuzu.rc
+++ /dev/null
@@ -1,17 +0,0 @@
1#include "winresrc.h"
2/////////////////////////////////////////////////////////////////////////////
3//
4// Icon
5//
6
7// Icon with lowest ID value placed first to ensure application icon
8// remains consistent on all systems.
9YUZU_ICON ICON "../../dist/yuzu.ico"
10
11
12/////////////////////////////////////////////////////////////////////////////
13//
14// RT_MANIFEST
15//
16
170 RT_MANIFEST "../../dist/yuzu.manifest"