summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x.ci/scripts/windows/docker.sh5
-rw-r--r--.gitmodules6
-rw-r--r--CMakeLists.txt224
-rw-r--r--CMakeModules/GenerateSCMRev.cmake12
-rw-r--r--dist/yuzu.desktop2
-rw-r--r--dist/yuzu.xml15
-rw-r--r--externals/CMakeLists.txt16
m---------externals/SDL0
m---------externals/Vulkan-Headers0
m---------externals/dynarmic0
m---------externals/ffmpeg0
-rw-r--r--externals/ffmpeg/CMakeLists.txt214
m---------externals/ffmpeg/ffmpeg0
-rw-r--r--src/common/CMakeLists.txt5
-rw-r--r--src/common/settings.h1
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/core.cpp2
-rw-r--r--src/core/frontend/applets/controller.cpp10
-rw-r--r--src/core/hid/emulated_console.cpp3
-rw-r--r--src/core/hid/emulated_console.h2
-rw-r--r--src/core/hid/emulated_controller.cpp65
-rw-r--r--src/core/hid/emulated_controller.h16
-rw-r--r--src/core/hid/hid_types.h7
-rw-r--r--src/core/hid/motion_input.cpp12
-rw-r--r--src/core/hid/motion_input.h5
-rw-r--r--src/core/hle/kernel/global_scheduler_context.cpp6
-rw-r--r--src/core/hle/kernel/k_interrupt_manager.cpp34
-rw-r--r--src/core/hle/kernel/k_interrupt_manager.h17
-rw-r--r--src/core/hle/kernel/k_memory_block.h2
-rw-r--r--src/core/hle/kernel/k_page_table.cpp194
-rw-r--r--src/core/hle/kernel/k_page_table.h20
-rw-r--r--src/core/hle/kernel/k_process.cpp22
-rw-r--r--src/core/hle/kernel/k_process.h4
-rw-r--r--src/core/hle/kernel/k_scheduler.cpp8
-rw-r--r--src/core/hle/kernel/k_thread.cpp46
-rw-r--r--src/core/hle/kernel/k_thread.h6
-rw-r--r--src/core/hle/kernel/kernel.cpp5
-rw-r--r--src/core/hle/kernel/svc.cpp108
-rw-r--r--src/core/hle/kernel/svc_common.h5
-rw-r--r--src/core/hle/kernel/svc_wrap.h8
-rw-r--r--src/core/hle/lock.cpp9
-rw-r--r--src/core/hle/lock.h18
-rw-r--r--src/core/hle/service/bcat/backend/backend.cpp12
-rw-r--r--src/core/hle/service/bcat/backend/backend.h5
-rw-r--r--src/core/hle/service/hid/controllers/console_sixaxis.cpp20
-rw-r--r--src/core/hle/service/hid/controllers/console_sixaxis.h10
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp7
-rw-r--r--src/core/hle/service/hid/controllers/npad.h7
-rw-r--r--src/core/hle/service/hid/hid.cpp2
-rw-r--r--src/core/hle/service/nfp/nfp.cpp2
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp4
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.h5
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp3
-rw-r--r--src/core/loader/kip.cpp8
-rw-r--r--src/input_common/drivers/udp_client.cpp74
-rw-r--r--src/input_common/helpers/udp_protocol.h21
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp24
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_instructions.h1
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp4
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp115
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_floating_point.cpp4
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_instructions.h2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_integer.cpp4
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_special.cpp4
-rw-r--r--src/shader_recompiler/backend/glsl/glsl_emit_context.cpp45
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.cpp9
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp56
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_instructions.h1
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp143
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.h15
-rw-r--r--src/shader_recompiler/environment.h2
-rw-r--r--src/shader_recompiler/frontend/ir/attribute.h2
-rw-r--r--src/shader_recompiler/frontend/ir/opcodes.inc1
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate_program.cpp95
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate_program.h4
-rw-r--r--src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp1
-rw-r--r--src/shader_recompiler/ir_opt/constant_propagation_pass.cpp23
-rw-r--r--src/shader_recompiler/profile.h4
-rw-r--r--src/shader_recompiler/varying_state.h3
-rw-r--r--src/tests/CMakeLists.txt3
-rw-r--r--src/tests/input_common/calibration_configuration_job.cpp136
-rw-r--r--src/video_core/command_classes/codecs/codec.cpp8
-rw-r--r--src/video_core/gpu.cpp11
-rw-r--r--src/video_core/gpu.h3
-rw-r--r--src/video_core/renderer_opengl/gl_device.cpp10
-rw-r--r--src/video_core/renderer_opengl/gl_device.h10
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp16
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h6
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp50
-rw-r--r--src/video_core/renderer_vulkan/blit_image.cpp168
-rw-r--r--src/video_core/renderer_vulkan/blit_image.h33
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp37
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp11
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp11
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp80
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h13
-rw-r--r--src/video_core/shader_environment.cpp54
-rw-r--r--src/video_core/shader_environment.h6
-rw-r--r--src/video_core/texture_cache/texture_cache.h18
-rw-r--r--src/video_core/texture_cache/util.cpp10
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp40
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h18
-rw-r--r--src/yuzu/CMakeLists.txt3
-rw-r--r--src/yuzu/applets/qt_controller.cpp73
-rw-r--r--src/yuzu/applets/qt_error.cpp3
-rw-r--r--src/yuzu/applets/qt_profile_select.cpp3
-rw-r--r--src/yuzu/configuration/config.cpp2
-rw-r--r--src/yuzu/configuration/configure_debug.cpp3
-rw-r--r--src/yuzu/configuration/configure_debug.ui13
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp59
-rw-r--r--src/yuzu/game_list.cpp19
-rw-r--r--src/yuzu/main.cpp97
-rw-r--r--src/yuzu/main.h9
-rw-r--r--src/yuzu/uisettings.h3
116 files changed, 1748 insertions, 1175 deletions
diff --git a/.ci/scripts/windows/docker.sh b/.ci/scripts/windows/docker.sh
index 155d8a5c8..584b9b39f 100755
--- a/.ci/scripts/windows/docker.sh
+++ b/.ci/scripts/windows/docker.sh
@@ -41,12 +41,11 @@ for i in package/*.exe; do
41done 41done
42 42
43pip3 install pefile 43pip3 install pefile
44python3 .ci/scripts/windows/scan_dll.py package/*.exe "package/" 44python3 .ci/scripts/windows/scan_dll.py package/*.exe package/imageformats/*.dll "package/"
45python3 .ci/scripts/windows/scan_dll.py package/imageformats/*.dll "package/"
46 45
47# copy FFmpeg libraries 46# copy FFmpeg libraries
48EXTERNALS_PATH="$(pwd)/build/externals" 47EXTERNALS_PATH="$(pwd)/build/externals"
49FFMPEG_DLL_PATH="$(find ${EXTERNALS_PATH} -maxdepth 1 -type d | grep ffmpeg)/bin" 48FFMPEG_DLL_PATH="$(find "${EXTERNALS_PATH}" -maxdepth 1 -type d | grep 'ffmpeg-')/bin"
50find ${FFMPEG_DLL_PATH} -type f -regex ".*\.dll" -exec cp -v {} package/ ';' 49find ${FFMPEG_DLL_PATH} -type f -regex ".*\.dll" -exec cp -v {} package/ ';'
51 50
52# copy libraries from yuzu.exe path 51# copy libraries from yuzu.exe path
diff --git a/.gitmodules b/.gitmodules
index dc6ed500f..a9cf9a24a 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -34,12 +34,12 @@
34[submodule "opus"] 34[submodule "opus"]
35 path = externals/opus/opus 35 path = externals/opus/opus
36 url = https://github.com/xiph/opus.git 36 url = https://github.com/xiph/opus.git
37[submodule "ffmpeg"]
38 path = externals/ffmpeg
39 url = https://git.ffmpeg.org/ffmpeg.git
40[submodule "SDL"] 37[submodule "SDL"]
41 path = externals/SDL 38 path = externals/SDL
42 url = https://github.com/libsdl-org/SDL.git 39 url = https://github.com/libsdl-org/SDL.git
43[submodule "externals/cpp-httplib"] 40[submodule "externals/cpp-httplib"]
44 path = externals/cpp-httplib 41 path = externals/cpp-httplib
45 url = https://github.com/yhirose/cpp-httplib.git 42 url = https://github.com/yhirose/cpp-httplib.git
43[submodule "externals/ffmpeg/ffmpeg"]
44 path = externals/ffmpeg/ffmpeg
45 url = https://git.ffmpeg.org/ffmpeg.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1a7b1d85a..707d514c3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -231,7 +231,7 @@ elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR YUZU_USE_BUNDLED_BOOST)
231 include_directories(SYSTEM "${Boost_INCLUDE_DIRS}") 231 include_directories(SYSTEM "${Boost_INCLUDE_DIRS}")
232else() 232else()
233 message(STATUS "Boost 1.73.0 or newer not found, falling back to Conan") 233 message(STATUS "Boost 1.73.0 or newer not found, falling back to Conan")
234 list(APPEND CONAN_REQUIRED_LIBS "boost/1.73.0") 234 list(APPEND CONAN_REQUIRED_LIBS "boost/1.78.0")
235endif() 235endif()
236 236
237# Attempt to locate any packages that are required and report the missing ones in CONAN_REQUIRED_LIBS 237# Attempt to locate any packages that are required and report the missing ones in CONAN_REQUIRED_LIBS
@@ -251,7 +251,7 @@ if(ENABLE_QT)
251 # Check for system Qt on Linux, fallback to bundled Qt 251 # Check for system Qt on Linux, fallback to bundled Qt
252 if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") 252 if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
253 if (NOT YUZU_USE_BUNDLED_QT) 253 if (NOT YUZU_USE_BUNDLED_QT)
254 find_package(Qt5 ${QT_VERSION} COMPONENTS Widgets) 254 find_package(Qt5 ${QT_VERSION} COMPONENTS Widgets DBus)
255 endif() 255 endif()
256 if (NOT Qt5_FOUND OR YUZU_USE_BUNDLED_QT) 256 if (NOT Qt5_FOUND OR YUZU_USE_BUNDLED_QT)
257 # Check for dependencies, then enable bundled Qt download 257 # Check for dependencies, then enable bundled Qt download
@@ -510,13 +510,13 @@ set(FFmpeg_COMPONENTS
510 avutil 510 avutil
511 swscale) 511 swscale)
512 512
513if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") 513if (UNIX AND NOT APPLE)
514 Include(FindPkgConfig REQUIRED) 514 Include(FindPkgConfig REQUIRED)
515 pkg_check_modules(LIBVA libva) 515 pkg_check_modules(LIBVA libva)
516endif() 516endif()
517if (NOT YUZU_USE_BUNDLED_FFMPEG) 517if (NOT YUZU_USE_BUNDLED_FFMPEG)
518 # Use system installed FFmpeg 518 # Use system installed FFmpeg
519 find_package(FFmpeg QUIET COMPONENTS ${FFmpeg_COMPONENTS}) 519 find_package(FFmpeg 4.3 QUIET COMPONENTS ${FFmpeg_COMPONENTS})
520 520
521 if (FFmpeg_FOUND) 521 if (FFmpeg_FOUND)
522 # Overwrite aggregate defines from FFmpeg module to avoid over-linking libraries. 522 # Overwrite aggregate defines from FFmpeg module to avoid over-linking libraries.
@@ -529,225 +529,11 @@ if (NOT YUZU_USE_BUNDLED_FFMPEG)
529 set(FFmpeg_INCLUDE_DIR ${FFmpeg_INCLUDE_DIR} ${FFmpeg_INCLUDE_${COMPONENT}} CACHE PATH "Path to FFmpeg headers" FORCE) 529 set(FFmpeg_INCLUDE_DIR ${FFmpeg_INCLUDE_DIR} ${FFmpeg_INCLUDE_${COMPONENT}} CACHE PATH "Path to FFmpeg headers" FORCE)
530 endforeach() 530 endforeach()
531 else() 531 else()
532 message(WARNING "FFmpeg not found, falling back to externals") 532 message(WARNING "FFmpeg not found or too old, falling back to externals")
533 set(YUZU_USE_BUNDLED_FFMPEG ON) 533 set(YUZU_USE_BUNDLED_FFMPEG ON)
534 endif() 534 endif()
535endif() 535endif()
536 536
537if (YUZU_USE_BUNDLED_FFMPEG)
538 if (NOT WIN32)
539 # TODO(lat9nq): Move this to externals/ffmpeg/CMakeLists.txt (and move externals/ffmpeg to
540 # externals/ffmpeg/ffmpeg)
541
542 # Build FFmpeg from externals
543 message(STATUS "Using FFmpeg from externals")
544
545 # FFmpeg has source that requires one of nasm or yasm to assemble it.
546 # REQUIRED throws an error if not found here during configuration rather than during compilation.
547 find_program(ASSEMBLER NAMES nasm yasm)
548 if ("${ASSEMBLER}" STREQUAL "ASSEMBLER-NOTFOUND")
549 message(FATAL_ERROR "One of either `nasm` or `yasm` not found but is required.")
550 endif()
551
552 find_program(AUTOCONF autoconf)
553 if ("${AUTOCONF}" STREQUAL "AUTOCONF-NOTFOUND")
554 message(FATAL_ERROR "Required program `autoconf` not found.")
555 endif()
556
557 set(FFmpeg_PREFIX ${PROJECT_SOURCE_DIR}/externals/ffmpeg)
558 set(FFmpeg_BUILD_DIR ${PROJECT_BINARY_DIR}/externals/ffmpeg)
559 set(FFmpeg_MAKEFILE ${FFmpeg_BUILD_DIR}/Makefile)
560 make_directory(${FFmpeg_BUILD_DIR})
561
562 # Read version string from external
563 file(READ ${FFmpeg_PREFIX}/RELEASE FFmpeg_VERSION)
564 set(FFmpeg_FOUND NO)
565 if (NOT FFmpeg_VERSION STREQUAL "")
566 set(FFmpeg_FOUND YES)
567 endif()
568
569 unset(FFmpeg_LIBRARIES CACHE)
570 foreach(COMPONENT ${FFmpeg_COMPONENTS})
571 set(FFmpeg_${COMPONENT}_PREFIX "${FFmpeg_BUILD_DIR}/lib${COMPONENT}")
572 set(FFmpeg_${COMPONENT}_LIB_NAME "lib${COMPONENT}.a")
573 set(FFmpeg_${COMPONENT}_LIBRARY "${FFmpeg_${COMPONENT}_PREFIX}/${FFmpeg_${COMPONENT}_LIB_NAME}")
574
575 set(FFmpeg_LIBRARIES
576 ${FFmpeg_LIBRARIES}
577 ${FFmpeg_${COMPONENT}_LIBRARY}
578 CACHE PATH "Paths to FFmpeg libraries" FORCE)
579 endforeach()
580
581 Include(FindPkgConfig REQUIRED)
582 pkg_check_modules(LIBVA libva)
583 pkg_check_modules(CUDA cuda)
584 pkg_check_modules(FFNVCODEC ffnvcodec)
585 pkg_check_modules(VDPAU vdpau)
586
587 set(FFmpeg_HWACCEL_LIBRARIES)
588 set(FFmpeg_HWACCEL_FLAGS)
589 set(FFmpeg_HWACCEL_INCLUDE_DIRS)
590 set(FFmpeg_HWACCEL_LDFLAGS)
591
592 if(LIBVA_FOUND)
593 pkg_check_modules(LIBDRM libdrm REQUIRED)
594 find_package(X11 REQUIRED)
595 pkg_check_modules(LIBVA-DRM libva-drm REQUIRED)
596 pkg_check_modules(LIBVA-X11 libva-x11 REQUIRED)
597 list(APPEND FFmpeg_HWACCEL_LIBRARIES
598 ${LIBDRM_LIBRARIES}
599 ${X11_LIBRARIES}
600 ${LIBVA-DRM_LIBRARIES}
601 ${LIBVA-X11_LIBRARIES}
602 ${LIBVA_LIBRARIES})
603 set(FFmpeg_HWACCEL_FLAGS
604 --enable-hwaccel=h264_vaapi
605 --enable-hwaccel=vp8_vaapi
606 --enable-hwaccel=vp9_vaapi
607 --enable-libdrm)
608 list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS
609 ${LIBDRM_INCLUDE_DIRS}
610 ${X11_INCLUDE_DIRS}
611 ${LIBVA-DRM_INCLUDE_DIRS}
612 ${LIBVA-X11_INCLUDE_DIRS}
613 ${LIBVA_INCLUDE_DIRS}
614 )
615 message(STATUS "VA-API found")
616 else()
617 set(FFmpeg_HWACCEL_FLAGS --disable-vaapi)
618 endif()
619
620 if (FFNVCODEC_FOUND AND CUDA_FOUND)
621 list(APPEND FFmpeg_HWACCEL_FLAGS
622 --enable-cuvid
623 --enable-ffnvcodec
624 --enable-nvdec
625 --enable-hwaccel=h264_nvdec
626 --enable-hwaccel=vp8_nvdec
627 --enable-hwaccel=vp9_nvdec
628 --extra-cflags=-I${CUDA_INCLUDE_DIRS}
629 )
630 list(APPEND FFmpeg_HWACCEL_LIBRARIES
631 ${FFNVCODEC_LIBRARIES}
632 ${CUDA_LIBRARIES}
633 )
634 list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS
635 ${FFNVCODEC_INCLUDE_DIRS}
636 ${CUDA_INCLUDE_DIRS}
637 )
638 list(APPEND FFmpeg_HWACCEL_LDFLAGS
639 ${FFNVCODEC_LDFLAGS}
640 ${CUDA_LDFLAGS}
641 )
642 message(STATUS "ffnvcodec libraries version ${FFNVCODEC_VERSION} found")
643 endif()
644
645 if (VDPAU_FOUND)
646 list(APPEND FFmpeg_HWACCEL_FLAGS
647 --enable-vdpau
648 --enable-hwaccel=h264_vdpau
649 --enable-hwaccel=vp9_vdpau
650 )
651 list(APPEND FFmpeg_HWACCEL_LIBRARIES ${VDPAU_LIBRARIES})
652 list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS ${VDPAU_INCLUDE_DIRS})
653 list(APPEND FFmpeg_HWACCEL_LDFLAGS ${VDPAU_LDFLAGS})
654 message(STATUS "vdpau libraries version ${VDPAU_VERSION} found")
655 else()
656 list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vdpau)
657 endif()
658
659 # `configure` parameters builds only exactly what yuzu needs from FFmpeg
660 # `--disable-vdpau` is needed to avoid linking issues
661 add_custom_command(
662 OUTPUT
663 ${FFmpeg_MAKEFILE}
664 COMMAND
665 /bin/bash ${FFmpeg_PREFIX}/configure
666 --disable-avdevice
667 --disable-avfilter
668 --disable-avformat
669 --disable-doc
670 --disable-everything
671 --disable-ffmpeg
672 --disable-ffprobe
673 --disable-network
674 --disable-postproc
675 --disable-swresample
676 --enable-decoder=h264
677 --enable-decoder=vp8
678 --enable-decoder=vp9
679 --cc="${CMAKE_C_COMPILER}"
680 --cxx="${CMAKE_CXX_COMPILER}"
681 ${FFmpeg_HWACCEL_FLAGS}
682 WORKING_DIRECTORY
683 ${FFmpeg_BUILD_DIR}
684 )
685 unset(FFmpeg_HWACCEL_FLAGS)
686
687 # Workaround for Ubuntu 18.04's older version of make not being able to call make as a child
688 # with context of the jobserver. Also helps ninja users.
689 execute_process(
690 COMMAND
691 nproc
692 OUTPUT_VARIABLE
693 SYSTEM_THREADS)
694
695 set(FFmpeg_BUILD_LIBRARIES ${FFmpeg_LIBRARIES})
696 add_custom_command(
697 OUTPUT
698 ${FFmpeg_BUILD_LIBRARIES}
699 COMMAND
700 make -j${SYSTEM_THREADS}
701 WORKING_DIRECTORY
702 ${FFmpeg_BUILD_DIR}
703 )
704
705 set(FFmpeg_INCLUDE_DIR
706 "${FFmpeg_PREFIX};${FFmpeg_BUILD_DIR};${FFmpeg_HWACCEL_INCLUDE_DIRS}"
707 CACHE PATH "Path to FFmpeg headers" FORCE)
708
709 set(FFmpeg_LDFLAGS
710 "${FFmpeg_HWACCEL_LDFLAGS}"
711 CACHE STRING "FFmpeg linker flags" FORCE)
712
713 # ALL makes this custom target build every time
714 # but it won't actually build if the DEPENDS parameter is up to date
715 add_custom_target(ffmpeg-configure ALL DEPENDS ${FFmpeg_MAKEFILE})
716 add_custom_target(ffmpeg-build ALL DEPENDS ${FFmpeg_BUILD_LIBRARIES} ffmpeg-configure)
717 link_libraries(${FFmpeg_LIBVA_LIBRARIES})
718 set(FFmpeg_LIBRARIES ${FFmpeg_BUILD_LIBRARIES} ${FFmpeg_HWACCEL_LIBRARIES}
719 CACHE PATH "Paths to FFmpeg libraries" FORCE)
720 unset(FFmpeg_BUILD_LIBRARIES)
721 unset(FFmpeg_HWACCEL_FLAGS)
722 unset(FFmpeg_HWACCEL_INCLUDE_DIRS)
723 unset(FFmpeg_HWACCEL_LDFLAGS)
724 unset(FFmpeg_HWACCEL_LIBRARIES)
725
726 if (FFmpeg_FOUND)
727 message(STATUS "Found FFmpeg version ${FFmpeg_VERSION}")
728 else()
729 message(FATAL_ERROR "FFmpeg not found")
730 endif()
731 else() # WIN32
732 # Use yuzu FFmpeg binaries
733 set(FFmpeg_EXT_NAME "ffmpeg-4.4")
734 set(FFmpeg_PATH "${CMAKE_BINARY_DIR}/externals/${FFmpeg_EXT_NAME}")
735 download_bundled_external("ffmpeg/" ${FFmpeg_EXT_NAME} "")
736 set(FFmpeg_FOUND YES)
737 set(FFmpeg_INCLUDE_DIR "${FFmpeg_PATH}/include" CACHE PATH "Path to FFmpeg headers" FORCE)
738 set(FFmpeg_LIBRARY_DIR "${FFmpeg_PATH}/bin" CACHE PATH "Path to FFmpeg library directory" FORCE)
739 set(FFmpeg_LDFLAGS "" CACHE STRING "FFmpeg linker flags" FORCE)
740 set(FFmpeg_DLL_DIR "${FFmpeg_PATH}/bin" CACHE PATH "Path to FFmpeg dll's" FORCE)
741 set(FFmpeg_LIBRARIES
742 ${FFmpeg_LIBRARY_DIR}/swscale.lib
743 ${FFmpeg_LIBRARY_DIR}/avcodec.lib
744 ${FFmpeg_LIBRARY_DIR}/avutil.lib
745 CACHE PATH "Paths to FFmpeg libraries" FORCE)
746 endif()
747endif()
748
749unset(FFmpeg_COMPONENTS)
750
751# Prefer the -pthread flag on Linux. 537# Prefer the -pthread flag on Linux.
752set(THREADS_PREFER_PTHREAD_FLAG ON) 538set(THREADS_PREFER_PTHREAD_FLAG ON)
753find_package(Threads REQUIRED) 539find_package(Threads REQUIRED)
diff --git a/CMakeModules/GenerateSCMRev.cmake b/CMakeModules/GenerateSCMRev.cmake
index 43ca730ec..c7da2b91d 100644
--- a/CMakeModules/GenerateSCMRev.cmake
+++ b/CMakeModules/GenerateSCMRev.cmake
@@ -11,9 +11,15 @@ find_package(Git QUIET PATHS "${GIT_EXECUTABLE}")
11 11
12# generate git/build information 12# generate git/build information
13include(GetGitRevisionDescription) 13include(GetGitRevisionDescription)
14get_git_head_revision(GIT_REF_SPEC GIT_REV) 14if(NOT GIT_REF_SPEC)
15git_describe(GIT_DESC --always --long --dirty) 15 get_git_head_revision(GIT_REF_SPEC GIT_REV)
16git_branch_name(GIT_BRANCH) 16endif()
17if(NOT GIT_DESC)
18 git_describe(GIT_DESC --always --long --dirty)
19endif()
20if (NOT GIT_BRANCH)
21 git_branch_name(GIT_BRANCH)
22endif()
17get_timestamp(BUILD_DATE) 23get_timestamp(BUILD_DATE)
18 24
19# Generate cpp with Git revision from template 25# Generate cpp with Git revision from template
diff --git a/dist/yuzu.desktop b/dist/yuzu.desktop
index 105080c73..6cc0704d2 100644
--- a/dist/yuzu.desktop
+++ b/dist/yuzu.desktop
@@ -8,5 +8,5 @@ Icon=yuzu
8TryExec=yuzu 8TryExec=yuzu
9Exec=yuzu %f 9Exec=yuzu %f
10Categories=Game;Emulator;Qt; 10Categories=Game;Emulator;Qt;
11MimeType=application/x-nx-nro;application/x-nx-nso; 11MimeType=application/x-nx-nro;application/x-nx-nso;application/x-nx-nsp;application/x-nx-xci;
12Keywords=Switch;Nintendo; \ No newline at end of file 12Keywords=Switch;Nintendo; \ No newline at end of file
diff --git a/dist/yuzu.xml b/dist/yuzu.xml
index f6e408321..dd1e30a6b 100644
--- a/dist/yuzu.xml
+++ b/dist/yuzu.xml
@@ -15,4 +15,19 @@
15 <glob pattern="*.nso"/> 15 <glob pattern="*.nso"/>
16 <magic><match value="NSO" type="string" offset="0"/></magic> 16 <magic><match value="NSO" type="string" offset="0"/></magic>
17 </mime-type> 17 </mime-type>
18
19 <mime-type type="application/x-nx-nsp">
20 <comment>Nintendo Switch Package</comment>
21 <acronym>NSP</acronym>
22 <icon name="yuzu"/>
23 <glob pattern="*.nsp"/>
24 <magic><match value="PFS" type="string" offset="0"/></magic>
25 </mime-type>
26
27 <mime-type type="application/x-nx-xci">
28 <comment>Nintendo Switch Card Image</comment>
29 <acronym>XCI</acronym>
30 <icon name="yuzu"/>
31 <glob pattern="*.xci"/>
32 </mime-type>
18</mime-info> \ No newline at end of file 33</mime-info> \ No newline at end of file
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt
index 26ead64f2..158113516 100644
--- a/externals/CMakeLists.txt
+++ b/externals/CMakeLists.txt
@@ -52,11 +52,12 @@ endif()
52# SDL2 52# SDL2
53if (YUZU_USE_EXTERNAL_SDL2) 53if (YUZU_USE_EXTERNAL_SDL2)
54 if (NOT WIN32) 54 if (NOT WIN32)
55 # Yuzu itself needs: Events Joystick Haptic Sensor Timers Audio 55 # Yuzu itself needs: Atomic Audio Events Joystick Haptic Sensor Threads Timers
56 # Since 2.0.18 Atomic+Threads required for HIDAPI/libusb (see https://github.com/libsdl-org/SDL/issues/5095)
56 # Yuzu-cmd also needs: Video (depends on Loadso/Dlopen) 57 # Yuzu-cmd also needs: Video (depends on Loadso/Dlopen)
57 set(SDL_UNUSED_SUBSYSTEMS 58 set(SDL_UNUSED_SUBSYSTEMS
58 Atomic Render Power Threads 59 CPUinfo File Filesystem
59 File CPUinfo Filesystem Locale) 60 Locale Power Render)
60 foreach(_SUB ${SDL_UNUSED_SUBSYSTEMS}) 61 foreach(_SUB ${SDL_UNUSED_SUBSYSTEMS})
61 string(TOUPPER ${_SUB} _OPT) 62 string(TOUPPER ${_SUB} _OPT)
62 option(SDL_${_OPT} "" OFF) 63 option(SDL_${_OPT} "" OFF)
@@ -121,3 +122,12 @@ if (YUZU_USE_BUNDLED_OPUS)
121else() 122else()
122 find_package(opus 1.3 REQUIRED) 123 find_package(opus 1.3 REQUIRED)
123endif() 124endif()
125
126# FFMpeg
127if (YUZU_USE_BUNDLED_FFMPEG)
128 add_subdirectory(ffmpeg)
129 set(FFmpeg_PATH "${FFmpeg_PATH}" PARENT_SCOPE)
130 set(FFmpeg_LDFLAGS "${FFmpeg_LDFLAGS}" PARENT_SCOPE)
131 set(FFmpeg_LIBRARIES "${FFmpeg_LIBRARIES}" PARENT_SCOPE)
132 set(FFmpeg_INCLUDE_DIR "${FFmpeg_INCLUDE_DIR}" PARENT_SCOPE)
133endif()
diff --git a/externals/SDL b/externals/SDL
Subproject 2e9821423a237a1206e3c09020778faacfe430b Subproject e2ade2bfc46d915cd306c63c830b81d800b2575
diff --git a/externals/Vulkan-Headers b/externals/Vulkan-Headers
Subproject 07c4a37bcf41ea50aef6e98236abdfe8089fb4c Subproject e005e1f8175d006adc3676b40ac3dd2212961a6
diff --git a/externals/dynarmic b/externals/dynarmic
Subproject cce7e4ee5d7b07a4609c73c053fbf57dc8c7845 Subproject 28714ee75aa079cbb706e38bdabc8ee1f6c6951
diff --git a/externals/ffmpeg b/externals/ffmpeg
deleted file mode 160000
Subproject 79e8d17024e6c6328a40fcee191ffd70798a9c6
diff --git a/externals/ffmpeg/CMakeLists.txt b/externals/ffmpeg/CMakeLists.txt
new file mode 100644
index 000000000..c57b54f77
--- /dev/null
+++ b/externals/ffmpeg/CMakeLists.txt
@@ -0,0 +1,214 @@
1if (NOT WIN32)
2 # Build FFmpeg from externals
3 message(STATUS "Using FFmpeg from externals")
4
5 if (CMAKE_SYSTEM_PROCESSOR MATCHES "(x86_64|amd64)")
6 # FFmpeg has source that requires one of nasm or yasm to assemble it.
7 # REQUIRED throws an error if not found here during configuration rather than during compilation.
8 find_program(ASSEMBLER NAMES nasm yasm)
9 if ("${ASSEMBLER}" STREQUAL "ASSEMBLER-NOTFOUND")
10 message(FATAL_ERROR "One of either `nasm` or `yasm` not found but is required.")
11 endif()
12 endif()
13
14 find_program(AUTOCONF autoconf)
15 if ("${AUTOCONF}" STREQUAL "AUTOCONF-NOTFOUND")
16 message(FATAL_ERROR "Required program `autoconf` not found.")
17 endif()
18
19 set(FFmpeg_PREFIX ${PROJECT_SOURCE_DIR}/externals/ffmpeg/ffmpeg)
20 set(FFmpeg_BUILD_DIR ${PROJECT_BINARY_DIR}/externals/ffmpeg-build)
21 set(FFmpeg_MAKEFILE ${FFmpeg_BUILD_DIR}/Makefile)
22 make_directory(${FFmpeg_BUILD_DIR})
23
24 # Read version string from external
25 file(READ ${FFmpeg_PREFIX}/RELEASE FFmpeg_VERSION)
26 set(FFmpeg_FOUND NO)
27 if (NOT FFmpeg_VERSION STREQUAL "")
28 set(FFmpeg_FOUND YES)
29 endif()
30
31 unset(FFmpeg_LIBRARIES CACHE)
32 foreach(COMPONENT ${FFmpeg_COMPONENTS})
33 set(FFmpeg_${COMPONENT}_PREFIX "${FFmpeg_BUILD_DIR}/lib${COMPONENT}")
34 set(FFmpeg_${COMPONENT}_LIB_NAME "lib${COMPONENT}.a")
35 set(FFmpeg_${COMPONENT}_LIBRARY "${FFmpeg_${COMPONENT}_PREFIX}/${FFmpeg_${COMPONENT}_LIB_NAME}")
36
37 set(FFmpeg_LIBRARIES
38 ${FFmpeg_LIBRARIES}
39 ${FFmpeg_${COMPONENT}_LIBRARY}
40 CACHE PATH "Paths to FFmpeg libraries" FORCE)
41 endforeach()
42
43 Include(FindPkgConfig REQUIRED)
44 pkg_check_modules(LIBVA libva)
45 pkg_check_modules(CUDA cuda)
46 pkg_check_modules(FFNVCODEC ffnvcodec)
47 pkg_check_modules(VDPAU vdpau)
48
49 set(FFmpeg_HWACCEL_LIBRARIES)
50 set(FFmpeg_HWACCEL_FLAGS)
51 set(FFmpeg_HWACCEL_INCLUDE_DIRS)
52 set(FFmpeg_HWACCEL_LDFLAGS)
53
54 if(LIBVA_FOUND)
55 pkg_check_modules(LIBDRM libdrm REQUIRED)
56 find_package(X11 REQUIRED)
57 pkg_check_modules(LIBVA-DRM libva-drm REQUIRED)
58 pkg_check_modules(LIBVA-X11 libva-x11 REQUIRED)
59 list(APPEND FFmpeg_HWACCEL_LIBRARIES
60 ${LIBDRM_LIBRARIES}
61 ${X11_LIBRARIES}
62 ${LIBVA-DRM_LIBRARIES}
63 ${LIBVA-X11_LIBRARIES}
64 ${LIBVA_LIBRARIES})
65 set(FFmpeg_HWACCEL_FLAGS
66 --enable-hwaccel=h264_vaapi
67 --enable-hwaccel=vp8_vaapi
68 --enable-hwaccel=vp9_vaapi
69 --enable-libdrm)
70 list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS
71 ${LIBDRM_INCLUDE_DIRS}
72 ${X11_INCLUDE_DIRS}
73 ${LIBVA-DRM_INCLUDE_DIRS}
74 ${LIBVA-X11_INCLUDE_DIRS}
75 ${LIBVA_INCLUDE_DIRS}
76 )
77 message(STATUS "VA-API found")
78 else()
79 set(FFmpeg_HWACCEL_FLAGS --disable-vaapi)
80 endif()
81
82 if (FFNVCODEC_FOUND)
83 list(APPEND FFmpeg_HWACCEL_FLAGS
84 --enable-cuvid
85 --enable-ffnvcodec
86 --enable-nvdec
87 --enable-hwaccel=h264_nvdec
88 --enable-hwaccel=vp8_nvdec
89 --enable-hwaccel=vp9_nvdec
90 )
91 list(APPEND FFmpeg_HWACCEL_LIBRARIES ${FFNVCODEC_LIBRARIES})
92 list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS ${FFNVCODEC_INCLUDE_DIRS})
93 list(APPEND FFmpeg_HWACCEL_LDFLAGS ${FFNVCODEC_LDFLAGS})
94 message(STATUS "ffnvcodec libraries version ${FFNVCODEC_VERSION} found")
95 # ffnvenc could load CUDA libraries at the runtime using dlopen/dlsym or LoadLibrary/GetProcAddress
96 # here we handle the hard-linking senario where CUDA is linked during compilation
97 if (CUDA_FOUND)
98 list(APPEND FFmpeg_HWACCEL_FLAGS --extra-cflags=-I${CUDA_INCLUDE_DIRS})
99 list(APPEND FFmpeg_HWACCEL_LIBRARIES ${CUDA_LIBRARIES})
100 list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS ${CUDA_INCLUDE_DIRS})
101 list(APPEND FFmpeg_HWACCEL_LDFLAGS ${CUDA_LDFLAGS})
102 message(STATUS "CUDA libraries found, hard-linking will be performed")
103 endif(CUDA_FOUND)
104 endif()
105
106 if (VDPAU_FOUND)
107 list(APPEND FFmpeg_HWACCEL_FLAGS
108 --enable-vdpau
109 --enable-hwaccel=h264_vdpau
110 --enable-hwaccel=vp9_vdpau
111 )
112 list(APPEND FFmpeg_HWACCEL_LIBRARIES ${VDPAU_LIBRARIES})
113 list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS ${VDPAU_INCLUDE_DIRS})
114 list(APPEND FFmpeg_HWACCEL_LDFLAGS ${VDPAU_LDFLAGS})
115 message(STATUS "vdpau libraries version ${VDPAU_VERSION} found")
116 else()
117 list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vdpau)
118 endif()
119
120 # `configure` parameters builds only exactly what yuzu needs from FFmpeg
121 # `--disable-vdpau` is needed to avoid linking issues
122 add_custom_command(
123 OUTPUT
124 ${FFmpeg_MAKEFILE}
125 COMMAND
126 /bin/bash ${FFmpeg_PREFIX}/configure
127 --disable-avdevice
128 --disable-avfilter
129 --disable-avformat
130 --disable-doc
131 --disable-everything
132 --disable-ffmpeg
133 --disable-ffprobe
134 --disable-network
135 --disable-postproc
136 --disable-swresample
137 --enable-decoder=h264
138 --enable-decoder=vp8
139 --enable-decoder=vp9
140 --cc="${CMAKE_C_COMPILER}"
141 --cxx="${CMAKE_CXX_COMPILER}"
142 ${FFmpeg_HWACCEL_FLAGS}
143 WORKING_DIRECTORY
144 ${FFmpeg_BUILD_DIR}
145 )
146 unset(FFmpeg_HWACCEL_FLAGS)
147
148 # Workaround for Ubuntu 18.04's older version of make not being able to call make as a child
149 # with context of the jobserver. Also helps ninja users.
150 execute_process(
151 COMMAND
152 nproc
153 OUTPUT_VARIABLE
154 SYSTEM_THREADS)
155
156 set(FFmpeg_BUILD_LIBRARIES ${FFmpeg_LIBRARIES})
157 add_custom_command(
158 OUTPUT
159 ${FFmpeg_BUILD_LIBRARIES}
160 COMMAND
161 make -j${SYSTEM_THREADS}
162 WORKING_DIRECTORY
163 ${FFmpeg_BUILD_DIR}
164 )
165
166 set(FFmpeg_INCLUDE_DIR
167 "${FFmpeg_PREFIX};${FFmpeg_BUILD_DIR};${FFmpeg_HWACCEL_INCLUDE_DIRS}"
168 CACHE PATH "Path to FFmpeg headers" FORCE)
169
170 set(FFmpeg_LDFLAGS
171 "${FFmpeg_HWACCEL_LDFLAGS}"
172 CACHE STRING "FFmpeg linker flags" FORCE)
173
174 # ALL makes this custom target build every time
175 # but it won't actually build if the DEPENDS parameter is up to date
176 add_custom_target(ffmpeg-configure ALL DEPENDS ${FFmpeg_MAKEFILE})
177 add_custom_target(ffmpeg-build ALL DEPENDS ${FFmpeg_BUILD_LIBRARIES} ffmpeg-configure)
178 link_libraries(${FFmpeg_LIBVA_LIBRARIES})
179 set(FFmpeg_LIBRARIES ${FFmpeg_BUILD_LIBRARIES} ${FFmpeg_HWACCEL_LIBRARIES}
180 CACHE PATH "Paths to FFmpeg libraries" FORCE)
181 unset(FFmpeg_BUILD_LIBRARIES)
182 unset(FFmpeg_HWACCEL_FLAGS)
183 unset(FFmpeg_HWACCEL_INCLUDE_DIRS)
184 unset(FFmpeg_HWACCEL_LDFLAGS)
185 unset(FFmpeg_HWACCEL_LIBRARIES)
186
187 if (FFmpeg_FOUND)
188 message(STATUS "Found FFmpeg version ${FFmpeg_VERSION}")
189 else()
190 message(FATAL_ERROR "FFmpeg not found")
191 endif()
192else(WIN32)
193 # Use yuzu FFmpeg binaries
194 set(FFmpeg_EXT_NAME "ffmpeg-4.4")
195 set(FFmpeg_PATH "${CMAKE_BINARY_DIR}/externals/${FFmpeg_EXT_NAME}")
196 download_bundled_external("ffmpeg/" ${FFmpeg_EXT_NAME} "")
197 set(FFmpeg_FOUND YES)
198 set(FFmpeg_INCLUDE_DIR "${FFmpeg_PATH}/include" CACHE PATH "Path to FFmpeg headers" FORCE)
199 set(FFmpeg_LIBRARY_DIR "${FFmpeg_PATH}/bin" CACHE PATH "Path to FFmpeg library directory" FORCE)
200 set(FFmpeg_LDFLAGS "" CACHE STRING "FFmpeg linker flags" FORCE)
201 set(FFmpeg_DLL_DIR "${FFmpeg_PATH}/bin" CACHE PATH "Path to FFmpeg dll's" FORCE)
202 set(FFmpeg_LIBRARIES
203 ${FFmpeg_LIBRARY_DIR}/swscale.lib
204 ${FFmpeg_LIBRARY_DIR}/avcodec.lib
205 ${FFmpeg_LIBRARY_DIR}/avutil.lib
206 CACHE PATH "Paths to FFmpeg libraries" FORCE)
207 # exported variables
208 set(FFmpeg_PATH "${FFmpeg_PATH}" PARENT_SCOPE)
209 set(FFmpeg_LDFLAGS "${FFmpeg_LDFLAGS}" PARENT_SCOPE)
210 set(FFmpeg_LIBRARIES "${FFmpeg_LIBRARIES}" PARENT_SCOPE)
211 set(FFmpeg_INCLUDE_DIR "${FFmpeg_INCLUDE_DIR}" PARENT_SCOPE)
212endif(WIN32)
213
214unset(FFmpeg_COMPONENTS)
diff --git a/externals/ffmpeg/ffmpeg b/externals/ffmpeg/ffmpeg
new file mode 160000
Subproject dc91b913b6260e85e1304c74ff7bb3c22a8c9fb
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 919da4a53..790193b00 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -22,6 +22,11 @@ add_custom_command(OUTPUT scm_rev.cpp
22 -DTITLE_BAR_FORMAT_RUNNING=${TITLE_BAR_FORMAT_RUNNING} 22 -DTITLE_BAR_FORMAT_RUNNING=${TITLE_BAR_FORMAT_RUNNING}
23 -DBUILD_TAG=${BUILD_TAG} 23 -DBUILD_TAG=${BUILD_TAG}
24 -DBUILD_ID=${DISPLAY_VERSION} 24 -DBUILD_ID=${DISPLAY_VERSION}
25 -DGIT_REF_SPEC=${GIT_REF_SPEC}
26 -DGIT_REV=${GIT_REV}
27 -DGIT_DESC=${GIT_DESC}
28 -DGIT_BRANCH=${GIT_BRANCH}
29 -DBUILD_FULLNAME=${BUILD_FULLNAME}
25 -DGIT_EXECUTABLE=${GIT_EXECUTABLE} 30 -DGIT_EXECUTABLE=${GIT_EXECUTABLE}
26 -P ${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake 31 -P ${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake
27 DEPENDS 32 DEPENDS
diff --git a/src/common/settings.h b/src/common/settings.h
index 313f1fa7f..d01c0448c 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -597,6 +597,7 @@ struct Values {
597 BasicSetting<std::string> program_args{std::string(), "program_args"}; 597 BasicSetting<std::string> program_args{std::string(), "program_args"};
598 BasicSetting<bool> dump_exefs{false, "dump_exefs"}; 598 BasicSetting<bool> dump_exefs{false, "dump_exefs"};
599 BasicSetting<bool> dump_nso{false, "dump_nso"}; 599 BasicSetting<bool> dump_nso{false, "dump_nso"};
600 BasicSetting<bool> dump_shaders{false, "dump_shaders"};
600 BasicSetting<bool> enable_fs_access_log{false, "enable_fs_access_log"}; 601 BasicSetting<bool> enable_fs_access_log{false, "enable_fs_access_log"};
601 BasicSetting<bool> reporting_services{false, "reporting_services"}; 602 BasicSetting<bool> reporting_services{false, "reporting_services"};
602 BasicSetting<bool> quest_flag{false, "quest_flag"}; 603 BasicSetting<bool> quest_flag{false, "quest_flag"};
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 49bed614a..b1a746727 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -187,6 +187,8 @@ add_library(core STATIC
187 hle/kernel/k_event.h 187 hle/kernel/k_event.h
188 hle/kernel/k_handle_table.cpp 188 hle/kernel/k_handle_table.cpp
189 hle/kernel/k_handle_table.h 189 hle/kernel/k_handle_table.h
190 hle/kernel/k_interrupt_manager.cpp
191 hle/kernel/k_interrupt_manager.h
190 hle/kernel/k_light_condition_variable.cpp 192 hle/kernel/k_light_condition_variable.cpp
191 hle/kernel/k_light_condition_variable.h 193 hle/kernel/k_light_condition_variable.h
192 hle/kernel/k_light_lock.cpp 194 hle/kernel/k_light_lock.cpp
@@ -265,8 +267,6 @@ add_library(core STATIC
265 hle/kernel/svc_wrap.h 267 hle/kernel/svc_wrap.h
266 hle/kernel/time_manager.cpp 268 hle/kernel/time_manager.cpp
267 hle/kernel/time_manager.h 269 hle/kernel/time_manager.h
268 hle/lock.cpp
269 hle/lock.h
270 hle/result.h 270 hle/result.h
271 hle/service/acc/acc.cpp 271 hle/service/acc/acc.cpp
272 hle/service/acc/acc.h 272 hle/service/acc/acc.h
diff --git a/src/core/core.cpp b/src/core/core.cpp
index aa96f709b..3f9a7f44b 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -317,6 +317,8 @@ struct System::Impl {
317 is_powered_on = false; 317 is_powered_on = false;
318 exit_lock = false; 318 exit_lock = false;
319 319
320 gpu_core->NotifyShutdown();
321
320 services.reset(); 322 services.reset();
321 service_manager.reset(); 323 service_manager.reset();
322 cheat_engine.reset(); 324 cheat_engine.reset();
diff --git a/src/core/frontend/applets/controller.cpp b/src/core/frontend/applets/controller.cpp
index 6dbd38ffa..e1033b634 100644
--- a/src/core/frontend/applets/controller.cpp
+++ b/src/core/frontend/applets/controller.cpp
@@ -45,26 +45,26 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb
45 // Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld 45 // Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld
46 if (parameters.allow_pro_controller) { 46 if (parameters.allow_pro_controller) {
47 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController); 47 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
48 controller->Connect(); 48 controller->Connect(true);
49 } else if (parameters.allow_dual_joycons) { 49 } else if (parameters.allow_dual_joycons) {
50 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconDual); 50 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconDual);
51 controller->Connect(); 51 controller->Connect(true);
52 } else if (parameters.allow_left_joycon && parameters.allow_right_joycon) { 52 } else if (parameters.allow_left_joycon && parameters.allow_right_joycon) {
53 // Assign left joycons to even player indices and right joycons to odd player indices. 53 // Assign left joycons to even player indices and right joycons to odd player indices.
54 // We do this since Captain Toad Treasure Tracker expects a left joycon for Player 1 and 54 // We do this since Captain Toad Treasure Tracker expects a left joycon for Player 1 and
55 // a right Joycon for Player 2 in 2 Player Assist mode. 55 // a right Joycon for Player 2 in 2 Player Assist mode.
56 if (index % 2 == 0) { 56 if (index % 2 == 0) {
57 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconLeft); 57 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconLeft);
58 controller->Connect(); 58 controller->Connect(true);
59 } else { 59 } else {
60 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconRight); 60 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconRight);
61 controller->Connect(); 61 controller->Connect(true);
62 } 62 }
63 } else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld && 63 } else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld &&
64 !Settings::values.use_docked_mode.GetValue()) { 64 !Settings::values.use_docked_mode.GetValue()) {
65 // We should *never* reach here under any normal circumstances. 65 // We should *never* reach here under any normal circumstances.
66 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); 66 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
67 controller->Connect(); 67 controller->Connect(true);
68 } else { 68 } else {
69 UNREACHABLE_MSG("Unable to add a new controller based on the given parameters!"); 69 UNREACHABLE_MSG("Unable to add a new controller based on the given parameters!");
70 } 70 }
diff --git a/src/core/hid/emulated_console.cpp b/src/core/hid/emulated_console.cpp
index 685ec080c..08f8af551 100644
--- a/src/core/hid/emulated_console.cpp
+++ b/src/core/hid/emulated_console.cpp
@@ -161,7 +161,10 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
161 motion.rotation = emulated.GetGyroscope(); 161 motion.rotation = emulated.GetGyroscope();
162 motion.orientation = emulated.GetOrientation(); 162 motion.orientation = emulated.GetOrientation();
163 motion.quaternion = emulated.GetQuaternion(); 163 motion.quaternion = emulated.GetQuaternion();
164 motion.gyro_bias = emulated.GetGyroBias();
164 motion.is_at_rest = !emulated.IsMoving(motion_sensitivity); 165 motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
166 // Find what is this value
167 motion.verticalization_error = 0.0f;
165 168
166 TriggerOnChange(ConsoleTriggerType::Motion); 169 TriggerOnChange(ConsoleTriggerType::Motion);
167} 170}
diff --git a/src/core/hid/emulated_console.h b/src/core/hid/emulated_console.h
index 3afd284d5..707419102 100644
--- a/src/core/hid/emulated_console.h
+++ b/src/core/hid/emulated_console.h
@@ -50,6 +50,8 @@ struct ConsoleMotion {
50 Common::Vec3f rotation{}; 50 Common::Vec3f rotation{};
51 std::array<Common::Vec3f, 3> orientation{}; 51 std::array<Common::Vec3f, 3> orientation{};
52 Common::Quaternion<f32> quaternion{}; 52 Common::Quaternion<f32> quaternion{};
53 Common::Vec3f gyro_bias{};
54 f32 verticalization_error{};
53 bool is_at_rest{}; 55 bool is_at_rest{};
54}; 56};
55 57
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index 93372445b..71fc05807 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -843,23 +843,18 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v
843} 843}
844 844
845bool EmulatedController::TestVibration(std::size_t device_index) { 845bool EmulatedController::TestVibration(std::size_t device_index) {
846 if (device_index >= output_devices.size()) { 846 static constexpr VibrationValue test_vibration = {
847 return false;
848 }
849 if (!output_devices[device_index]) {
850 return false;
851 }
852
853 // Send a slight vibration to test for rumble support
854 constexpr Common::Input::VibrationStatus status = {
855 .low_amplitude = 0.001f, 847 .low_amplitude = 0.001f,
856 .low_frequency = 160.0f, 848 .low_frequency = 160.0f,
857 .high_amplitude = 0.001f, 849 .high_amplitude = 0.001f,
858 .high_frequency = 320.0f, 850 .high_frequency = 320.0f,
859 .type = Common::Input::VibrationAmplificationType::Linear,
860 }; 851 };
861 return output_devices[device_index]->SetVibration(status) == 852
862 Common::Input::VibrationError::None; 853 // Send a slight vibration to test for rumble support
854 SetVibration(device_index, test_vibration);
855
856 // Stop any vibration and return the result
857 return SetVibration(device_index, DEFAULT_VIBRATION_VALUE);
863} 858}
864 859
865void EmulatedController::SetLedPattern() { 860void EmulatedController::SetLedPattern() {
@@ -884,15 +879,42 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
884 if (!is_connected) { 879 if (!is_connected) {
885 return; 880 return;
886 } 881 }
887 if (!IsControllerSupported()) { 882 if (IsControllerSupported()) {
888 LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller", 883 return;
889 npad_type);
890 Disconnect();
891 } 884 }
885
886 Disconnect();
887
888 // Fallback fullkey controllers to Pro controllers
889 if (IsControllerFullkey() && supported_style_tag.fullkey) {
890 LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type);
891 SetNpadStyleIndex(NpadStyleIndex::ProController);
892 Connect();
893 return;
894 }
895
896 LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller",
897 npad_type);
892} 898}
893 899
894bool EmulatedController::IsControllerSupported() const { 900bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const {
895 switch (npad_type) { 901 const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
902 switch (type) {
903 case NpadStyleIndex::ProController:
904 case NpadStyleIndex::GameCube:
905 case NpadStyleIndex::NES:
906 case NpadStyleIndex::SNES:
907 case NpadStyleIndex::N64:
908 case NpadStyleIndex::SegaGenesis:
909 return true;
910 default:
911 return false;
912 }
913}
914
915bool EmulatedController::IsControllerSupported(bool use_temporary_value) const {
916 const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
917 switch (type) {
896 case NpadStyleIndex::ProController: 918 case NpadStyleIndex::ProController:
897 return supported_style_tag.fullkey; 919 return supported_style_tag.fullkey;
898 case NpadStyleIndex::Handheld: 920 case NpadStyleIndex::Handheld:
@@ -920,9 +942,10 @@ bool EmulatedController::IsControllerSupported() const {
920 } 942 }
921} 943}
922 944
923void EmulatedController::Connect() { 945void EmulatedController::Connect(bool use_temporary_value) {
924 if (!IsControllerSupported()) { 946 if (!IsControllerSupported(use_temporary_value)) {
925 LOG_ERROR(Service_HID, "Controller type {} is not supported", npad_type); 947 const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
948 LOG_ERROR(Service_HID, "Controller type {} is not supported", type);
926 return; 949 return;
927 } 950 }
928 { 951 {
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
index e42aafebc..c0994ab4d 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -167,8 +167,11 @@ public:
167 */ 167 */
168 void SetSupportedNpadStyleTag(NpadStyleTag supported_styles); 168 void SetSupportedNpadStyleTag(NpadStyleTag supported_styles);
169 169
170 /// Sets the connected status to true 170 /**
171 void Connect(); 171 * Sets the connected status to true
172 * @param use_temporary_value If true tmp_npad_type will be used
173 */
174 void Connect(bool use_temporary_value = false);
172 175
173 /// Sets the connected status to false 176 /// Sets the connected status to false
174 void Disconnect(); 177 void Disconnect();
@@ -318,10 +321,17 @@ private:
318 void LoadTASParams(); 321 void LoadTASParams();
319 322
320 /** 323 /**
324 * @param use_temporary_value If true tmp_npad_type will be used
325 * @return true if the controller style is fullkey
326 */
327 bool IsControllerFullkey(bool use_temporary_value = false) const;
328
329 /**
321 * Checks the current controller type against the supported_style_tag 330 * Checks the current controller type against the supported_style_tag
331 * @param use_temporary_value If true tmp_npad_type will be used
322 * @return true if the controller is supported 332 * @return true if the controller is supported
323 */ 333 */
324 bool IsControllerSupported() const; 334 bool IsControllerSupported(bool use_temporary_value = false) const;
325 335
326 /** 336 /**
327 * Updates the button status of the controller 337 * Updates the button status of the controller
diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h
index 7c12f01fc..4eca68533 100644
--- a/src/core/hid/hid_types.h
+++ b/src/core/hid/hid_types.h
@@ -496,6 +496,13 @@ struct VibrationValue {
496}; 496};
497static_assert(sizeof(VibrationValue) == 0x10, "VibrationValue has incorrect size."); 497static_assert(sizeof(VibrationValue) == 0x10, "VibrationValue has incorrect size.");
498 498
499constexpr VibrationValue DEFAULT_VIBRATION_VALUE{
500 .low_amplitude = 0.0f,
501 .low_frequency = 160.0f,
502 .high_amplitude = 0.0f,
503 .high_frequency = 320.0f,
504};
505
499// This is nn::hid::VibrationDeviceInfo 506// This is nn::hid::VibrationDeviceInfo
500struct VibrationDeviceInfo { 507struct VibrationDeviceInfo {
501 VibrationDeviceType type{}; 508 VibrationDeviceType type{};
diff --git a/src/core/hid/motion_input.cpp b/src/core/hid/motion_input.cpp
index c25fea966..a23f192d7 100644
--- a/src/core/hid/motion_input.cpp
+++ b/src/core/hid/motion_input.cpp
@@ -23,11 +23,11 @@ void MotionInput::SetAcceleration(const Common::Vec3f& acceleration) {
23} 23}
24 24
25void MotionInput::SetGyroscope(const Common::Vec3f& gyroscope) { 25void MotionInput::SetGyroscope(const Common::Vec3f& gyroscope) {
26 gyro = gyroscope - gyro_drift; 26 gyro = gyroscope - gyro_bias;
27 27
28 // Auto adjust drift to minimize drift 28 // Auto adjust drift to minimize drift
29 if (!IsMoving(0.1f)) { 29 if (!IsMoving(0.1f)) {
30 gyro_drift = (gyro_drift * 0.9999f) + (gyroscope * 0.0001f); 30 gyro_bias = (gyro_bias * 0.9999f) + (gyroscope * 0.0001f);
31 } 31 }
32 32
33 if (gyro.Length2() < gyro_threshold) { 33 if (gyro.Length2() < gyro_threshold) {
@@ -41,8 +41,8 @@ void MotionInput::SetQuaternion(const Common::Quaternion<f32>& quaternion) {
41 quat = quaternion; 41 quat = quaternion;
42} 42}
43 43
44void MotionInput::SetGyroDrift(const Common::Vec3f& drift) { 44void MotionInput::SetGyroBias(const Common::Vec3f& bias) {
45 gyro_drift = drift; 45 gyro_bias = bias;
46} 46}
47 47
48void MotionInput::SetGyroThreshold(f32 threshold) { 48void MotionInput::SetGyroThreshold(f32 threshold) {
@@ -192,6 +192,10 @@ Common::Vec3f MotionInput::GetGyroscope() const {
192 return gyro; 192 return gyro;
193} 193}
194 194
195Common::Vec3f MotionInput::GetGyroBias() const {
196 return gyro_bias;
197}
198
195Common::Quaternion<f32> MotionInput::GetQuaternion() const { 199Common::Quaternion<f32> MotionInput::GetQuaternion() const {
196 return quat; 200 return quat;
197} 201}
diff --git a/src/core/hid/motion_input.h b/src/core/hid/motion_input.h
index 5b5b420bb..bca4520fa 100644
--- a/src/core/hid/motion_input.h
+++ b/src/core/hid/motion_input.h
@@ -24,7 +24,7 @@ public:
24 void SetAcceleration(const Common::Vec3f& acceleration); 24 void SetAcceleration(const Common::Vec3f& acceleration);
25 void SetGyroscope(const Common::Vec3f& gyroscope); 25 void SetGyroscope(const Common::Vec3f& gyroscope);
26 void SetQuaternion(const Common::Quaternion<f32>& quaternion); 26 void SetQuaternion(const Common::Quaternion<f32>& quaternion);
27 void SetGyroDrift(const Common::Vec3f& drift); 27 void SetGyroBias(const Common::Vec3f& bias);
28 void SetGyroThreshold(f32 threshold); 28 void SetGyroThreshold(f32 threshold);
29 29
30 void EnableReset(bool reset); 30 void EnableReset(bool reset);
@@ -36,6 +36,7 @@ public:
36 [[nodiscard]] std::array<Common::Vec3f, 3> GetOrientation() const; 36 [[nodiscard]] std::array<Common::Vec3f, 3> GetOrientation() const;
37 [[nodiscard]] Common::Vec3f GetAcceleration() const; 37 [[nodiscard]] Common::Vec3f GetAcceleration() const;
38 [[nodiscard]] Common::Vec3f GetGyroscope() const; 38 [[nodiscard]] Common::Vec3f GetGyroscope() const;
39 [[nodiscard]] Common::Vec3f GetGyroBias() const;
39 [[nodiscard]] Common::Vec3f GetRotations() const; 40 [[nodiscard]] Common::Vec3f GetRotations() const;
40 [[nodiscard]] Common::Quaternion<f32> GetQuaternion() const; 41 [[nodiscard]] Common::Quaternion<f32> GetQuaternion() const;
41 42
@@ -69,7 +70,7 @@ private:
69 Common::Vec3f gyro; 70 Common::Vec3f gyro;
70 71
71 // Vector to be substracted from gyro measurements 72 // Vector to be substracted from gyro measurements
72 Common::Vec3f gyro_drift; 73 Common::Vec3f gyro_bias;
73 74
74 // Minimum gyro amplitude to detect if the device is moving 75 // Minimum gyro amplitude to detect if the device is moving
75 f32 gyro_threshold = 0.0f; 76 f32 gyro_threshold = 0.0f;
diff --git a/src/core/hle/kernel/global_scheduler_context.cpp b/src/core/hle/kernel/global_scheduler_context.cpp
index 4f4e338e3..baad2c5d6 100644
--- a/src/core/hle/kernel/global_scheduler_context.cpp
+++ b/src/core/hle/kernel/global_scheduler_context.cpp
@@ -9,6 +9,7 @@
9#include "core/hle/kernel/global_scheduler_context.h" 9#include "core/hle/kernel/global_scheduler_context.h"
10#include "core/hle/kernel/k_scheduler.h" 10#include "core/hle/kernel/k_scheduler.h"
11#include "core/hle/kernel/kernel.h" 11#include "core/hle/kernel/kernel.h"
12#include "core/hle/kernel/physical_core.h"
12 13
13namespace Kernel { 14namespace Kernel {
14 15
@@ -42,6 +43,11 @@ void GlobalSchedulerContext::PreemptThreads() {
42 for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { 43 for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
43 const u32 priority = preemption_priorities[core_id]; 44 const u32 priority = preemption_priorities[core_id];
44 kernel.Scheduler(core_id).RotateScheduledQueue(core_id, priority); 45 kernel.Scheduler(core_id).RotateScheduledQueue(core_id, priority);
46
47 // Signal an interrupt occurred. For core 3, this is a certainty, as preemption will result
48 // in the rotator thread being scheduled. For cores 0-2, this is to simulate or system
49 // interrupts that may have occurred.
50 kernel.PhysicalCore(core_id).Interrupt();
45 } 51 }
46} 52}
47 53
diff --git a/src/core/hle/kernel/k_interrupt_manager.cpp b/src/core/hle/kernel/k_interrupt_manager.cpp
new file mode 100644
index 000000000..e5dd39751
--- /dev/null
+++ b/src/core/hle/kernel/k_interrupt_manager.cpp
@@ -0,0 +1,34 @@
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/hle/kernel/k_interrupt_manager.h"
6#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/k_scheduler.h"
8#include "core/hle/kernel/k_thread.h"
9#include "core/hle/kernel/kernel.h"
10
11namespace Kernel::KInterruptManager {
12
13void HandleInterrupt(KernelCore& kernel, s32 core_id) {
14 auto* process = kernel.CurrentProcess();
15 if (!process) {
16 return;
17 }
18
19 auto& scheduler = kernel.Scheduler(core_id);
20 auto& current_thread = *scheduler.GetCurrentThread();
21
22 // If the user disable count is set, we may need to pin the current thread.
23 if (current_thread.GetUserDisableCount() && !process->GetPinnedThread(core_id)) {
24 KScopedSchedulerLock sl{kernel};
25
26 // Pin the current thread.
27 process->PinCurrentThread(core_id);
28
29 // Set the interrupt flag for the thread.
30 scheduler.GetCurrentThread()->SetInterruptFlag();
31 }
32}
33
34} // namespace Kernel::KInterruptManager
diff --git a/src/core/hle/kernel/k_interrupt_manager.h b/src/core/hle/kernel/k_interrupt_manager.h
new file mode 100644
index 000000000..05924801e
--- /dev/null
+++ b/src/core/hle/kernel/k_interrupt_manager.h
@@ -0,0 +1,17 @@
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/common_types.h"
8
9namespace Kernel {
10
11class KernelCore;
12
13namespace KInterruptManager {
14void HandleInterrupt(KernelCore& kernel, s32 core_id);
15}
16
17} // namespace Kernel
diff --git a/src/core/hle/kernel/k_memory_block.h b/src/core/hle/kernel/k_memory_block.h
index fd491146f..9e51c33ce 100644
--- a/src/core/hle/kernel/k_memory_block.h
+++ b/src/core/hle/kernel/k_memory_block.h
@@ -120,7 +120,7 @@ static_assert(static_cast<u32>(KMemoryState::CodeOut) == 0x00402015);
120 120
121enum class KMemoryPermission : u8 { 121enum class KMemoryPermission : u8 {
122 None = 0, 122 None = 0,
123 Mask = static_cast<u8>(~None), 123 All = static_cast<u8>(~None),
124 124
125 Read = 1 << 0, 125 Read = 1 << 0,
126 Write = 1 << 1, 126 Write = 1 << 1,
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index 99982e5a3..4da509224 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -264,9 +264,9 @@ ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_
264 ASSERT(heap_last < stack_start || stack_last < heap_start); 264 ASSERT(heap_last < stack_start || stack_last < heap_start);
265 ASSERT(heap_last < kmap_start || kmap_last < heap_start); 265 ASSERT(heap_last < kmap_start || kmap_last < heap_start);
266 266
267 current_heap_addr = heap_region_start; 267 current_heap_end = heap_region_start;
268 heap_capacity = 0; 268 max_heap_size = 0;
269 physical_memory_usage = 0; 269 mapped_physical_memory_size = 0;
270 memory_pool = pool; 270 memory_pool = pool;
271 271
272 page_table_impl.Resize(address_space_width, PageBits); 272 page_table_impl.Resize(address_space_width, PageBits);
@@ -306,7 +306,7 @@ ResultCode KPageTable::MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std:
306 KMemoryState state{}; 306 KMemoryState state{};
307 KMemoryPermission perm{}; 307 KMemoryPermission perm{};
308 CASCADE_CODE(CheckMemoryState(&state, &perm, nullptr, src_addr, size, KMemoryState::All, 308 CASCADE_CODE(CheckMemoryState(&state, &perm, nullptr, src_addr, size, KMemoryState::All,
309 KMemoryState::Normal, KMemoryPermission::Mask, 309 KMemoryState::Normal, KMemoryPermission::All,
310 KMemoryPermission::ReadAndWrite, KMemoryAttribute::Mask, 310 KMemoryPermission::ReadAndWrite, KMemoryAttribute::Mask,
311 KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)); 311 KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
312 312
@@ -465,7 +465,7 @@ ResultCode KPageTable::MapPhysicalMemory(VAddr addr, std::size_t size) {
465 465
466 MapPhysicalMemory(page_linked_list, addr, end_addr); 466 MapPhysicalMemory(page_linked_list, addr, end_addr);
467 467
468 physical_memory_usage += remaining_size; 468 mapped_physical_memory_size += remaining_size;
469 469
470 const std::size_t num_pages{size / PageSize}; 470 const std::size_t num_pages{size / PageSize};
471 block_manager->Update(addr, num_pages, KMemoryState::Free, KMemoryPermission::None, 471 block_manager->Update(addr, num_pages, KMemoryState::Free, KMemoryPermission::None,
@@ -507,7 +507,7 @@ ResultCode KPageTable::UnmapPhysicalMemory(VAddr addr, std::size_t size) {
507 507
508 auto process{system.Kernel().CurrentProcess()}; 508 auto process{system.Kernel().CurrentProcess()};
509 process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, mapped_size); 509 process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, mapped_size);
510 physical_memory_usage -= mapped_size; 510 mapped_physical_memory_size -= mapped_size;
511 511
512 return ResultSuccess; 512 return ResultSuccess;
513} 513}
@@ -554,7 +554,7 @@ ResultCode KPageTable::Map(VAddr dst_addr, VAddr src_addr, std::size_t size) {
554 KMemoryState src_state{}; 554 KMemoryState src_state{};
555 CASCADE_CODE(CheckMemoryState( 555 CASCADE_CODE(CheckMemoryState(
556 &src_state, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias, 556 &src_state, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias,
557 KMemoryState::FlagCanAlias, KMemoryPermission::Mask, KMemoryPermission::ReadAndWrite, 557 KMemoryState::FlagCanAlias, KMemoryPermission::All, KMemoryPermission::ReadAndWrite,
558 KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)); 558 KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
559 559
560 if (IsRegionMapped(dst_addr, size)) { 560 if (IsRegionMapped(dst_addr, size)) {
@@ -593,7 +593,7 @@ ResultCode KPageTable::Unmap(VAddr dst_addr, VAddr src_addr, std::size_t size) {
593 KMemoryState src_state{}; 593 KMemoryState src_state{};
594 CASCADE_CODE(CheckMemoryState( 594 CASCADE_CODE(CheckMemoryState(
595 &src_state, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias, 595 &src_state, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias,
596 KMemoryState::FlagCanAlias, KMemoryPermission::Mask, KMemoryPermission::None, 596 KMemoryState::FlagCanAlias, KMemoryPermission::All, KMemoryPermission::None,
597 KMemoryAttribute::Mask, KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped)); 597 KMemoryAttribute::Mask, KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped));
598 598
599 KMemoryPermission dst_perm{}; 599 KMemoryPermission dst_perm{};
@@ -784,7 +784,7 @@ ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemo
784 CASCADE_CODE(CheckMemoryState( 784 CASCADE_CODE(CheckMemoryState(
785 &state, nullptr, &attribute, addr, size, 785 &state, nullptr, &attribute, addr, size,
786 KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, 786 KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted,
787 KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, KMemoryPermission::Mask, 787 KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, KMemoryPermission::All,
788 KMemoryPermission::ReadAndWrite, KMemoryAttribute::Mask, KMemoryAttribute::None, 788 KMemoryPermission::ReadAndWrite, KMemoryAttribute::Mask, KMemoryAttribute::None,
789 KMemoryAttribute::IpcAndDeviceMapped)); 789 KMemoryAttribute::IpcAndDeviceMapped));
790 790
@@ -806,6 +806,33 @@ ResultCode KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) {
806 KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped)); 806 KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped));
807 807
808 block_manager->Update(addr, size / PageSize, state, KMemoryPermission::ReadAndWrite); 808 block_manager->Update(addr, size / PageSize, state, KMemoryPermission::ReadAndWrite);
809 return ResultSuccess;
810}
811
812ResultCode KPageTable::SetMemoryPermission(VAddr addr, std::size_t size,
813 Svc::MemoryPermission svc_perm) {
814 const size_t num_pages = size / PageSize;
815
816 // Lock the table.
817 std::lock_guard lock{page_table_lock};
818
819 // Verify we can change the memory permission.
820 KMemoryState old_state;
821 KMemoryPermission old_perm;
822 R_TRY(this->CheckMemoryState(
823 std::addressof(old_state), std::addressof(old_perm), nullptr, addr, size,
824 KMemoryState::FlagCanReprotect, KMemoryState::FlagCanReprotect, KMemoryPermission::None,
825 KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None));
826
827 // Determine new perm.
828 const KMemoryPermission new_perm = ConvertToKMemoryPermission(svc_perm);
829 R_SUCCEED_IF(old_perm == new_perm);
830
831 // Perform mapping operation.
832 R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions));
833
834 // Update the blocks.
835 block_manager->Update(addr, num_pages, old_state, new_perm, KMemoryAttribute::None);
809 836
810 return ResultSuccess; 837 return ResultSuccess;
811} 838}
@@ -832,61 +859,125 @@ ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, KMemoryA
832 return ResultSuccess; 859 return ResultSuccess;
833} 860}
834 861
835ResultCode KPageTable::SetHeapCapacity(std::size_t new_heap_capacity) { 862ResultCode KPageTable::SetMaxHeapSize(std::size_t size) {
863 // Lock the table.
836 std::lock_guard lock{page_table_lock}; 864 std::lock_guard lock{page_table_lock};
837 heap_capacity = new_heap_capacity;
838 return ResultSuccess;
839}
840 865
841ResultVal<VAddr> KPageTable::SetHeapSize(std::size_t size) { 866 // Only process page tables are allowed to set heap size.
867 ASSERT(!this->IsKernel());
842 868
843 if (size > heap_region_end - heap_region_start) { 869 max_heap_size = size;
844 return ResultOutOfMemory;
845 }
846 870
847 const u64 previous_heap_size{GetHeapSize()}; 871 return ResultSuccess;
848 872}
849 UNIMPLEMENTED_IF_MSG(previous_heap_size > size, "Heap shrink is unimplemented");
850 873
851 // Increase the heap size 874ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) {
875 // Try to perform a reduction in heap, instead of an extension.
876 VAddr cur_address{};
877 std::size_t allocation_size{};
852 { 878 {
853 std::lock_guard lock{page_table_lock}; 879 // Lock the table.
854 880 std::lock_guard lk(page_table_lock);
855 const u64 delta{size - previous_heap_size}; 881
856 882 // Validate that setting heap size is possible at all.
857 // Reserve memory for the heap extension. 883 R_UNLESS(!is_kernel, ResultOutOfMemory);
858 KScopedResourceReservation memory_reservation( 884 R_UNLESS(size <= static_cast<std::size_t>(heap_region_end - heap_region_start),
859 system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory, 885 ResultOutOfMemory);
860 delta); 886 R_UNLESS(size <= max_heap_size, ResultOutOfMemory);
861 887
862 if (!memory_reservation.Succeeded()) { 888 if (size < GetHeapSize()) {
863 LOG_ERROR(Kernel, "Could not reserve heap extension of size {:X} bytes", delta); 889 // The size being requested is less than the current size, so we need to free the end of
864 return ResultLimitReached; 890 // the heap.
891
892 // Validate memory state.
893 std::size_t num_allocator_blocks;
894 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks),
895 heap_region_start + size, GetHeapSize() - size,
896 KMemoryState::All, KMemoryState::Normal,
897 KMemoryPermission::All, KMemoryPermission::ReadAndWrite,
898 KMemoryAttribute::All, KMemoryAttribute::None));
899
900 // Unmap the end of the heap.
901 const auto num_pages = (GetHeapSize() - size) / PageSize;
902 R_TRY(Operate(heap_region_start + size, num_pages, KMemoryPermission::None,
903 OperationType::Unmap));
904
905 // Release the memory from the resource limit.
906 system.Kernel().CurrentProcess()->GetResourceLimit()->Release(
907 LimitableResource::PhysicalMemory, num_pages * PageSize);
908
909 // Apply the memory block update.
910 block_manager->Update(heap_region_start + size, num_pages, KMemoryState::Free,
911 KMemoryPermission::None, KMemoryAttribute::None);
912
913 // Update the current heap end.
914 current_heap_end = heap_region_start + size;
915
916 // Set the output.
917 *out = heap_region_start;
918 return ResultSuccess;
919 } else if (size == GetHeapSize()) {
920 // The size requested is exactly the current size.
921 *out = heap_region_start;
922 return ResultSuccess;
923 } else {
924 // We have to allocate memory. Determine how much to allocate and where while the table
925 // is locked.
926 cur_address = current_heap_end;
927 allocation_size = size - GetHeapSize();
865 } 928 }
929 }
866 930
867 KPageLinkedList page_linked_list; 931 // Reserve memory for the heap extension.
868 const std::size_t num_pages{delta / PageSize}; 932 KScopedResourceReservation memory_reservation(
933 system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory,
934 allocation_size);
935 R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
869 936
870 CASCADE_CODE( 937 // Allocate pages for the heap extension.
871 system.Kernel().MemoryManager().Allocate(page_linked_list, num_pages, memory_pool)); 938 KPageLinkedList page_linked_list;
939 R_TRY(system.Kernel().MemoryManager().Allocate(page_linked_list, allocation_size / PageSize,
940 memory_pool));
872 941
873 if (IsRegionMapped(current_heap_addr, delta)) { 942 // Map the pages.
874 return ResultInvalidCurrentMemory; 943 {
944 // Lock the table.
945 std::lock_guard lk(page_table_lock);
946
947 // Ensure that the heap hasn't changed since we began executing.
948 ASSERT(cur_address == current_heap_end);
949
950 // Check the memory state.
951 std::size_t num_allocator_blocks{};
952 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), current_heap_end,
953 allocation_size, KMemoryState::All, KMemoryState::Free,
954 KMemoryPermission::None, KMemoryPermission::None,
955 KMemoryAttribute::None, KMemoryAttribute::None));
956
957 // Map the pages.
958 const auto num_pages = allocation_size / PageSize;
959 R_TRY(Operate(current_heap_end, num_pages, page_linked_list, OperationType::MapGroup));
960
961 // Clear all the newly allocated pages.
962 for (std::size_t cur_page = 0; cur_page < num_pages; ++cur_page) {
963 std::memset(system.Memory().GetPointer(current_heap_end + (cur_page * PageSize)), 0,
964 PageSize);
875 } 965 }
876 966
877 CASCADE_CODE( 967 // We succeeded, so commit our memory reservation.
878 Operate(current_heap_addr, num_pages, page_linked_list, OperationType::MapGroup));
879
880 // Succeeded in allocation, commit the resource reservation
881 memory_reservation.Commit(); 968 memory_reservation.Commit();
882 969
883 block_manager->Update(current_heap_addr, num_pages, KMemoryState::Normal, 970 // Apply the memory block update.
884 KMemoryPermission::ReadAndWrite); 971 block_manager->Update(current_heap_end, num_pages, KMemoryState::Normal,
972 KMemoryPermission::ReadAndWrite, KMemoryAttribute::None);
885 973
886 current_heap_addr = heap_region_start + size; 974 // Update the current heap end.
887 } 975 current_heap_end = heap_region_start + size;
888 976
889 return heap_region_start; 977 // Set the output.
978 *out = heap_region_start;
979 return ResultSuccess;
980 }
890} 981}
891 982
892ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align, 983ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align,
@@ -978,7 +1069,7 @@ ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
978 1069
979 if (const ResultCode result{CheckMemoryState( 1070 if (const ResultCode result{CheckMemoryState(
980 nullptr, &old_perm, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, 1071 nullptr, &old_perm, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
981 KMemoryState::FlagCanCodeMemory, KMemoryPermission::Mask, 1072 KMemoryState::FlagCanCodeMemory, KMemoryPermission::All,
982 KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)}; 1073 KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)};
983 result.IsError()) { 1074 result.IsError()) {
984 return result; 1075 return result;
@@ -1031,9 +1122,8 @@ ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
1031 1122
1032bool KPageTable::IsRegionMapped(VAddr address, u64 size) { 1123bool KPageTable::IsRegionMapped(VAddr address, u64 size) {
1033 return CheckMemoryState(address, size, KMemoryState::All, KMemoryState::Free, 1124 return CheckMemoryState(address, size, KMemoryState::All, KMemoryState::Free,
1034 KMemoryPermission::Mask, KMemoryPermission::None, 1125 KMemoryPermission::All, KMemoryPermission::None, KMemoryAttribute::Mask,
1035 KMemoryAttribute::Mask, KMemoryAttribute::None, 1126 KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)
1036 KMemoryAttribute::IpcAndDeviceMapped)
1037 .IsError(); 1127 .IsError();
1038} 1128}
1039 1129
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index d784aa67e..564410dca 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -47,10 +47,11 @@ public:
47 KMemoryInfo QueryInfo(VAddr addr); 47 KMemoryInfo QueryInfo(VAddr addr);
48 ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm); 48 ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm);
49 ResultCode ResetTransferMemory(VAddr addr, std::size_t size); 49 ResultCode ResetTransferMemory(VAddr addr, std::size_t size);
50 ResultCode SetMemoryPermission(VAddr addr, std::size_t size, Svc::MemoryPermission perm);
50 ResultCode SetMemoryAttribute(VAddr addr, std::size_t size, KMemoryAttribute mask, 51 ResultCode SetMemoryAttribute(VAddr addr, std::size_t size, KMemoryAttribute mask,
51 KMemoryAttribute value); 52 KMemoryAttribute value);
52 ResultCode SetHeapCapacity(std::size_t new_heap_capacity); 53 ResultCode SetMaxHeapSize(std::size_t size);
53 ResultVal<VAddr> SetHeapSize(std::size_t size); 54 ResultCode SetHeapSize(VAddr* out, std::size_t size);
54 ResultVal<VAddr> AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align, 55 ResultVal<VAddr> AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align,
55 bool is_map_only, VAddr region_start, 56 bool is_map_only, VAddr region_start,
56 std::size_t region_num_pages, KMemoryState state, 57 std::size_t region_num_pages, KMemoryState state,
@@ -182,14 +183,15 @@ public:
182 constexpr VAddr GetAliasCodeRegionSize() const { 183 constexpr VAddr GetAliasCodeRegionSize() const {
183 return alias_code_region_end - alias_code_region_start; 184 return alias_code_region_end - alias_code_region_start;
184 } 185 }
186 size_t GetNormalMemorySize() {
187 std::lock_guard lk(page_table_lock);
188 return GetHeapSize() + mapped_physical_memory_size;
189 }
185 constexpr std::size_t GetAddressSpaceWidth() const { 190 constexpr std::size_t GetAddressSpaceWidth() const {
186 return address_space_width; 191 return address_space_width;
187 } 192 }
188 constexpr std::size_t GetHeapSize() { 193 constexpr std::size_t GetHeapSize() const {
189 return current_heap_addr - heap_region_start; 194 return current_heap_end - heap_region_start;
190 }
191 constexpr std::size_t GetTotalHeapSize() {
192 return GetHeapSize() + physical_memory_usage;
193 } 195 }
194 constexpr bool IsInsideAddressSpace(VAddr address, std::size_t size) const { 196 constexpr bool IsInsideAddressSpace(VAddr address, std::size_t size) const {
195 return address_space_start <= address && address + size - 1 <= address_space_end - 1; 197 return address_space_start <= address && address + size - 1 <= address_space_end - 1;
@@ -269,10 +271,8 @@ private:
269 VAddr code_region_end{}; 271 VAddr code_region_end{};
270 VAddr alias_code_region_start{}; 272 VAddr alias_code_region_start{};
271 VAddr alias_code_region_end{}; 273 VAddr alias_code_region_end{};
272 VAddr current_heap_addr{};
273 274
274 std::size_t heap_capacity{}; 275 std::size_t mapped_physical_memory_size{};
275 std::size_t physical_memory_usage{};
276 std::size_t max_heap_size{}; 276 std::size_t max_heap_size{};
277 std::size_t max_physical_memory_size{}; 277 std::size_t max_physical_memory_size{};
278 std::size_t address_space_width{}; 278 std::size_t address_space_width{};
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index 90dda40dc..bf98a51e2 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -28,7 +28,6 @@
28#include "core/hle/kernel/k_thread.h" 28#include "core/hle/kernel/k_thread.h"
29#include "core/hle/kernel/kernel.h" 29#include "core/hle/kernel/kernel.h"
30#include "core/hle/kernel/svc_results.h" 30#include "core/hle/kernel/svc_results.h"
31#include "core/hle/lock.h"
32#include "core/memory.h" 31#include "core/memory.h"
33 32
34namespace Kernel { 33namespace Kernel {
@@ -173,7 +172,7 @@ void KProcess::DecrementThreadCount() {
173 172
174u64 KProcess::GetTotalPhysicalMemoryAvailable() const { 173u64 KProcess::GetTotalPhysicalMemoryAvailable() const {
175 const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemory) + 174 const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemory) +
176 page_table->GetTotalHeapSize() + GetSystemResourceSize() + image_size + 175 page_table->GetNormalMemorySize() + GetSystemResourceSize() + image_size +
177 main_thread_stack_size}; 176 main_thread_stack_size};
178 if (const auto pool_size = kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application); 177 if (const auto pool_size = kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application);
179 capacity != pool_size) { 178 capacity != pool_size) {
@@ -190,7 +189,7 @@ u64 KProcess::GetTotalPhysicalMemoryAvailableWithoutSystemResource() const {
190} 189}
191 190
192u64 KProcess::GetTotalPhysicalMemoryUsed() const { 191u64 KProcess::GetTotalPhysicalMemoryUsed() const {
193 return image_size + main_thread_stack_size + page_table->GetTotalHeapSize() + 192 return image_size + main_thread_stack_size + page_table->GetNormalMemorySize() +
194 GetSystemResourceSize(); 193 GetSystemResourceSize();
195} 194}
196 195
@@ -221,30 +220,28 @@ bool KProcess::ReleaseUserException(KThread* thread) {
221 } 220 }
222} 221}
223 222
224void KProcess::PinCurrentThread() { 223void KProcess::PinCurrentThread(s32 core_id) {
225 ASSERT(kernel.GlobalSchedulerContext().IsLocked()); 224 ASSERT(kernel.GlobalSchedulerContext().IsLocked());
226 225
227 // Get the current thread. 226 // Get the current thread.
228 const s32 core_id = GetCurrentCoreId(kernel); 227 KThread* cur_thread = kernel.Scheduler(static_cast<std::size_t>(core_id)).GetCurrentThread();
229 KThread* cur_thread = GetCurrentThreadPointer(kernel);
230 228
231 // If the thread isn't terminated, pin it. 229 // If the thread isn't terminated, pin it.
232 if (!cur_thread->IsTerminationRequested()) { 230 if (!cur_thread->IsTerminationRequested()) {
233 // Pin it. 231 // Pin it.
234 PinThread(core_id, cur_thread); 232 PinThread(core_id, cur_thread);
235 cur_thread->Pin(); 233 cur_thread->Pin(core_id);
236 234
237 // An update is needed. 235 // An update is needed.
238 KScheduler::SetSchedulerUpdateNeeded(kernel); 236 KScheduler::SetSchedulerUpdateNeeded(kernel);
239 } 237 }
240} 238}
241 239
242void KProcess::UnpinCurrentThread() { 240void KProcess::UnpinCurrentThread(s32 core_id) {
243 ASSERT(kernel.GlobalSchedulerContext().IsLocked()); 241 ASSERT(kernel.GlobalSchedulerContext().IsLocked());
244 242
245 // Get the current thread. 243 // Get the current thread.
246 const s32 core_id = GetCurrentCoreId(kernel); 244 KThread* cur_thread = kernel.Scheduler(static_cast<std::size_t>(core_id)).GetCurrentThread();
247 KThread* cur_thread = GetCurrentThreadPointer(kernel);
248 245
249 // Unpin it. 246 // Unpin it.
250 cur_thread->Unpin(); 247 cur_thread->Unpin();
@@ -411,8 +408,8 @@ void KProcess::Run(s32 main_thread_priority, u64 stack_size) {
411 resource_limit->Reserve(LimitableResource::Threads, 1); 408 resource_limit->Reserve(LimitableResource::Threads, 1);
412 resource_limit->Reserve(LimitableResource::PhysicalMemory, main_thread_stack_size); 409 resource_limit->Reserve(LimitableResource::PhysicalMemory, main_thread_stack_size);
413 410
414 const std::size_t heap_capacity{memory_usage_capacity - main_thread_stack_size - image_size}; 411 const std::size_t heap_capacity{memory_usage_capacity - (main_thread_stack_size + image_size)};
415 ASSERT(!page_table->SetHeapCapacity(heap_capacity).IsError()); 412 ASSERT(!page_table->SetMaxHeapSize(heap_capacity).IsError());
416 413
417 ChangeStatus(ProcessStatus::Running); 414 ChangeStatus(ProcessStatus::Running);
418 415
@@ -543,7 +540,6 @@ void KProcess::FreeTLSRegion(VAddr tls_address) {
543} 540}
544 541
545void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) { 542void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) {
546 std::lock_guard lock{HLE::g_hle_lock};
547 const auto ReprotectSegment = [&](const CodeSet::Segment& segment, 543 const auto ReprotectSegment = [&](const CodeSet::Segment& segment,
548 KMemoryPermission permission) { 544 KMemoryPermission permission) {
549 page_table->SetProcessMemoryPermission(segment.addr + base_addr, segment.size, permission); 545 page_table->SetProcessMemoryPermission(segment.addr + base_addr, segment.size, permission);
diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h
index cb93c7e24..e7c8b5838 100644
--- a/src/core/hle/kernel/k_process.h
+++ b/src/core/hle/kernel/k_process.h
@@ -345,8 +345,8 @@ public:
345 345
346 bool IsSignaled() const override; 346 bool IsSignaled() const override;
347 347
348 void PinCurrentThread(); 348 void PinCurrentThread(s32 core_id);
349 void UnpinCurrentThread(); 349 void UnpinCurrentThread(s32 core_id);
350 void UnpinThread(KThread* thread); 350 void UnpinThread(KThread* thread);
351 351
352 KLightLock& GetStateLock() { 352 KLightLock& GetStateLock() {
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp
index 277201de4..31cec990e 100644
--- a/src/core/hle/kernel/k_scheduler.cpp
+++ b/src/core/hle/kernel/k_scheduler.cpp
@@ -15,6 +15,7 @@
15#include "core/core.h" 15#include "core/core.h"
16#include "core/core_timing.h" 16#include "core/core_timing.h"
17#include "core/cpu_manager.h" 17#include "core/cpu_manager.h"
18#include "core/hle/kernel/k_interrupt_manager.h"
18#include "core/hle/kernel/k_process.h" 19#include "core/hle/kernel/k_process.h"
19#include "core/hle/kernel/k_scheduler.h" 20#include "core/hle/kernel/k_scheduler.h"
20#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" 21#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
@@ -53,6 +54,13 @@ void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedul
53 } 54 }
54 cores_pending_reschedule &= ~(1ULL << core); 55 cores_pending_reschedule &= ~(1ULL << core);
55 } 56 }
57
58 for (std::size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; ++core_id) {
59 if (kernel.PhysicalCore(core_id).IsInterrupted()) {
60 KInterruptManager::HandleInterrupt(kernel, static_cast<s32>(core_id));
61 }
62 }
63
56 if (must_context_switch) { 64 if (must_context_switch) {
57 auto core_scheduler = kernel.CurrentScheduler(); 65 auto core_scheduler = kernel.CurrentScheduler();
58 kernel.ExitSVCProfile(); 66 kernel.ExitSVCProfile();
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index 752592e2e..71e029a3f 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -3,6 +3,7 @@
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 <atomic>
6#include <cinttypes> 7#include <cinttypes>
7#include <optional> 8#include <optional>
8#include <vector> 9#include <vector>
@@ -26,12 +27,14 @@
26#include "core/hle/kernel/k_resource_limit.h" 27#include "core/hle/kernel/k_resource_limit.h"
27#include "core/hle/kernel/k_scheduler.h" 28#include "core/hle/kernel/k_scheduler.h"
28#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" 29#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
30#include "core/hle/kernel/k_system_control.h"
29#include "core/hle/kernel/k_thread.h" 31#include "core/hle/kernel/k_thread.h"
30#include "core/hle/kernel/k_thread_queue.h" 32#include "core/hle/kernel/k_thread_queue.h"
31#include "core/hle/kernel/kernel.h" 33#include "core/hle/kernel/kernel.h"
32#include "core/hle/kernel/svc_results.h" 34#include "core/hle/kernel/svc_results.h"
33#include "core/hle/kernel/time_manager.h" 35#include "core/hle/kernel/time_manager.h"
34#include "core/hle/result.h" 36#include "core/hle/result.h"
37#include "core/memory.h"
35 38
36#ifdef ARCHITECTURE_x86_64 39#ifdef ARCHITECTURE_x86_64
37#include "core/arm/dynarmic/arm_dynarmic_32.h" 40#include "core/arm/dynarmic/arm_dynarmic_32.h"
@@ -50,6 +53,7 @@ static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context,
50 VAddr entry_point, u64 arg) { 53 VAddr entry_point, u64 arg) {
51 context = {}; 54 context = {};
52 context.cpu_registers[0] = arg; 55 context.cpu_registers[0] = arg;
56 context.cpu_registers[18] = Kernel::KSystemControl::GenerateRandomU64() | 1;
53 context.pc = entry_point; 57 context.pc = entry_point;
54 context.sp = stack_top; 58 context.sp = stack_top;
55 // TODO(merry): Perform a hardware test to determine the below value. 59 // TODO(merry): Perform a hardware test to determine the below value.
@@ -61,6 +65,13 @@ namespace Kernel {
61 65
62namespace { 66namespace {
63 67
68struct ThreadLocalRegion {
69 static constexpr std::size_t MessageBufferSize = 0x100;
70 std::array<u32, MessageBufferSize / sizeof(u32)> message_buffer;
71 std::atomic_uint16_t disable_count;
72 std::atomic_uint16_t interrupt_flag;
73};
74
64class ThreadQueueImplForKThreadSleep final : public KThreadQueueWithoutEndWait { 75class ThreadQueueImplForKThreadSleep final : public KThreadQueueWithoutEndWait {
65public: 76public:
66 explicit ThreadQueueImplForKThreadSleep(KernelCore& kernel_) 77 explicit ThreadQueueImplForKThreadSleep(KernelCore& kernel_)
@@ -344,7 +355,7 @@ void KThread::StartTermination() {
344 if (parent != nullptr) { 355 if (parent != nullptr) {
345 parent->ReleaseUserException(this); 356 parent->ReleaseUserException(this);
346 if (parent->GetPinnedThread(GetCurrentCoreId(kernel)) == this) { 357 if (parent->GetPinnedThread(GetCurrentCoreId(kernel)) == this) {
347 parent->UnpinCurrentThread(); 358 parent->UnpinCurrentThread(core_id);
348 } 359 }
349 } 360 }
350 361
@@ -370,7 +381,7 @@ void KThread::StartTermination() {
370 this->Close(); 381 this->Close();
371} 382}
372 383
373void KThread::Pin() { 384void KThread::Pin(s32 current_core) {
374 ASSERT(kernel.GlobalSchedulerContext().IsLocked()); 385 ASSERT(kernel.GlobalSchedulerContext().IsLocked());
375 386
376 // Set ourselves as pinned. 387 // Set ourselves as pinned.
@@ -387,7 +398,6 @@ void KThread::Pin() {
387 398
388 // Bind ourselves to this core. 399 // Bind ourselves to this core.
389 const s32 active_core = GetActiveCore(); 400 const s32 active_core = GetActiveCore();
390 const s32 current_core = GetCurrentCoreId(kernel);
391 401
392 SetActiveCore(current_core); 402 SetActiveCore(current_core);
393 physical_ideal_core_id = current_core; 403 physical_ideal_core_id = current_core;
@@ -480,6 +490,36 @@ void KThread::Unpin() {
480 } 490 }
481} 491}
482 492
493u16 KThread::GetUserDisableCount() const {
494 if (!IsUserThread()) {
495 // We only emulate TLS for user threads
496 return {};
497 }
498
499 auto& memory = kernel.System().Memory();
500 return memory.Read16(tls_address + offsetof(ThreadLocalRegion, disable_count));
501}
502
503void KThread::SetInterruptFlag() {
504 if (!IsUserThread()) {
505 // We only emulate TLS for user threads
506 return;
507 }
508
509 auto& memory = kernel.System().Memory();
510 memory.Write16(tls_address + offsetof(ThreadLocalRegion, interrupt_flag), 1);
511}
512
513void KThread::ClearInterruptFlag() {
514 if (!IsUserThread()) {
515 // We only emulate TLS for user threads
516 return;
517 }
518
519 auto& memory = kernel.System().Memory();
520 memory.Write16(tls_address + offsetof(ThreadLocalRegion, interrupt_flag), 0);
521}
522
483ResultCode KThread::GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask) { 523ResultCode KThread::GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask) {
484 KScopedSchedulerLock sl{kernel}; 524 KScopedSchedulerLock sl{kernel};
485 525
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index c8a08bd71..83dfde69b 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -307,6 +307,10 @@ public:
307 return parent != nullptr; 307 return parent != nullptr;
308 } 308 }
309 309
310 u16 GetUserDisableCount() const;
311 void SetInterruptFlag();
312 void ClearInterruptFlag();
313
310 [[nodiscard]] KThread* GetLockOwner() const { 314 [[nodiscard]] KThread* GetLockOwner() const {
311 return lock_owner; 315 return lock_owner;
312 } 316 }
@@ -490,7 +494,7 @@ public:
490 this->GetStackParameters().disable_count--; 494 this->GetStackParameters().disable_count--;
491 } 495 }
492 496
493 void Pin(); 497 void Pin(s32 current_core);
494 498
495 void Unpin(); 499 void Unpin();
496 500
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 2e4e4cb1c..1225e1fba 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -182,7 +182,10 @@ struct KernelCore::Impl {
182 // Shutdown all processes. 182 // Shutdown all processes.
183 if (current_process) { 183 if (current_process) {
184 current_process->Finalize(); 184 current_process->Finalize();
185 current_process->Close(); 185 // current_process->Close();
186 // TODO: The current process should be destroyed based on accurate ref counting after
187 // calling Close(). Adding a manual Destroy() call instead to avoid a memory leak.
188 current_process->Destroy();
186 current_process = nullptr; 189 current_process = nullptr;
187 } 190 }
188 191
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index a9f7438ea..250ef9042 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -41,7 +41,6 @@
41#include "core/hle/kernel/svc_results.h" 41#include "core/hle/kernel/svc_results.h"
42#include "core/hle/kernel/svc_types.h" 42#include "core/hle/kernel/svc_types.h"
43#include "core/hle/kernel/svc_wrap.h" 43#include "core/hle/kernel/svc_wrap.h"
44#include "core/hle/lock.h"
45#include "core/hle/result.h" 44#include "core/hle/result.h"
46#include "core/memory.h" 45#include "core/memory.h"
47#include "core/reporter.h" 46#include "core/reporter.h"
@@ -136,25 +135,15 @@ enum class ResourceLimitValueType {
136} // Anonymous namespace 135} // Anonymous namespace
137 136
138/// Set the process heap to a given Size. It can both extend and shrink the heap. 137/// Set the process heap to a given Size. It can both extend and shrink the heap.
139static ResultCode SetHeapSize(Core::System& system, VAddr* heap_addr, u64 heap_size) { 138static ResultCode SetHeapSize(Core::System& system, VAddr* out_address, u64 size) {
140 std::lock_guard lock{HLE::g_hle_lock}; 139 LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", size);
141 LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", heap_size);
142
143 // Size must be a multiple of 0x200000 (2MB) and be equal to or less than 8GB.
144 if ((heap_size % 0x200000) != 0) {
145 LOG_ERROR(Kernel_SVC, "The heap size is not a multiple of 2MB, heap_size=0x{:016X}",
146 heap_size);
147 return ResultInvalidSize;
148 }
149
150 if (heap_size >= 0x200000000) {
151 LOG_ERROR(Kernel_SVC, "The heap size is not less than 8GB, heap_size=0x{:016X}", heap_size);
152 return ResultInvalidSize;
153 }
154 140
155 auto& page_table{system.Kernel().CurrentProcess()->PageTable()}; 141 // Validate size.
142 R_UNLESS(Common::IsAligned(size, HeapSizeAlignment), ResultInvalidSize);
143 R_UNLESS(size < MainMemorySizeMax, ResultInvalidSize);
156 144
157 CASCADE_RESULT(*heap_addr, page_table.SetHeapSize(heap_size)); 145 // Set the heap size.
146 R_TRY(system.Kernel().CurrentProcess()->PageTable().SetHeapSize(out_address, size));
158 147
159 return ResultSuccess; 148 return ResultSuccess;
160} 149}
@@ -166,9 +155,38 @@ static ResultCode SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_s
166 return result; 155 return result;
167} 156}
168 157
158constexpr bool IsValidSetMemoryPermission(MemoryPermission perm) {
159 switch (perm) {
160 case MemoryPermission::None:
161 case MemoryPermission::Read:
162 case MemoryPermission::ReadWrite:
163 return true;
164 default:
165 return false;
166 }
167}
168
169static ResultCode SetMemoryPermission(Core::System& system, VAddr address, u64 size,
170 MemoryPermission perm) {
171 // Validate address / size.
172 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
173 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
174 R_UNLESS(size > 0, ResultInvalidSize);
175 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
176
177 // Validate the permission.
178 R_UNLESS(IsValidSetMemoryPermission(perm), ResultInvalidNewMemoryPermission);
179
180 // Validate that the region is in range for the current process.
181 auto& page_table = system.Kernel().CurrentProcess()->PageTable();
182 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
183
184 // Set the memory attribute.
185 return page_table.SetMemoryPermission(address, size, perm);
186}
187
169static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask, 188static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask,
170 u32 attribute) { 189 u32 attribute) {
171 std::lock_guard lock{HLE::g_hle_lock};
172 LOG_DEBUG(Kernel_SVC, 190 LOG_DEBUG(Kernel_SVC,
173 "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address, 191 "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address,
174 size, mask, attribute); 192 size, mask, attribute);
@@ -212,7 +230,6 @@ static ResultCode SetMemoryAttribute32(Core::System& system, u32 address, u32 si
212 230
213/// Maps a memory range into a different range. 231/// Maps a memory range into a different range.
214static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { 232static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
215 std::lock_guard lock{HLE::g_hle_lock};
216 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, 233 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
217 src_addr, size); 234 src_addr, size);
218 235
@@ -232,7 +249,6 @@ static ResultCode MapMemory32(Core::System& system, u32 dst_addr, u32 src_addr,
232 249
233/// Unmaps a region that was previously mapped with svcMapMemory 250/// Unmaps a region that was previously mapped with svcMapMemory
234static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { 251static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
235 std::lock_guard lock{HLE::g_hle_lock};
236 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, 252 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
237 src_addr, size); 253 src_addr, size);
238 254
@@ -642,7 +658,6 @@ static void OutputDebugString(Core::System& system, VAddr address, u64 len) {
642/// Gets system/memory information for the current process 658/// Gets system/memory information for the current process
643static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle handle, 659static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle handle,
644 u64 info_sub_id) { 660 u64 info_sub_id) {
645 std::lock_guard lock{HLE::g_hle_lock};
646 LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, 661 LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id,
647 info_sub_id, handle); 662 info_sub_id, handle);
648 663
@@ -886,22 +901,17 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle
886 return ResultSuccess; 901 return ResultSuccess;
887 } 902 }
888 case GetInfoType::IdleTickCount: { 903 case GetInfoType::IdleTickCount: {
889 if (handle == 0) { 904 // Verify the input handle is invalid.
890 LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", 905 R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
891 static_cast<Handle>(handle));
892 return ResultInvalidHandle;
893 }
894
895 if (info_sub_id != 0xFFFFFFFFFFFFFFFF &&
896 info_sub_id != system.Kernel().CurrentPhysicalCoreIndex()) {
897 LOG_ERROR(Kernel_SVC, "Core is not the current core, got {}", info_sub_id);
898 return ResultInvalidCombination;
899 }
900 906
901 const auto& scheduler = *system.Kernel().CurrentScheduler(); 907 // Verify the requested core is valid.
902 const auto* const idle_thread = scheduler.GetIdleThread(); 908 const bool core_valid =
909 (info_sub_id == static_cast<u64>(-1ULL)) ||
910 (info_sub_id == static_cast<u64>(system.Kernel().CurrentPhysicalCoreIndex()));
911 R_UNLESS(core_valid, ResultInvalidCombination);
903 912
904 *result = idle_thread->GetCpuTime(); 913 // Get the idle tick count.
914 *result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime();
905 return ResultSuccess; 915 return ResultSuccess;
906 } 916 }
907 default: 917 default:
@@ -924,7 +934,6 @@ static ResultCode GetInfo32(Core::System& system, u32* result_low, u32* result_h
924 934
925/// Maps memory at a desired address 935/// Maps memory at a desired address
926static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { 936static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
927 std::lock_guard lock{HLE::g_hle_lock};
928 LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size); 937 LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
929 938
930 if (!Common::Is4KBAligned(addr)) { 939 if (!Common::Is4KBAligned(addr)) {
@@ -978,7 +987,6 @@ static ResultCode MapPhysicalMemory32(Core::System& system, u32 addr, u32 size)
978 987
979/// Unmaps memory previously mapped via MapPhysicalMemory 988/// Unmaps memory previously mapped via MapPhysicalMemory
980static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { 989static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
981 std::lock_guard lock{HLE::g_hle_lock};
982 LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size); 990 LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
983 991
984 if (!Common::Is4KBAligned(addr)) { 992 if (!Common::Is4KBAligned(addr)) {
@@ -1520,7 +1528,6 @@ static ResultCode ControlCodeMemory(Core::System& system, Handle code_memory_han
1520static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address, 1528static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address,
1521 VAddr page_info_address, Handle process_handle, 1529 VAddr page_info_address, Handle process_handle,
1522 VAddr address) { 1530 VAddr address) {
1523 std::lock_guard lock{HLE::g_hle_lock};
1524 LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address); 1531 LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address);
1525 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); 1532 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1526 KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle); 1533 KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
@@ -2020,6 +2027,25 @@ static ResultCode SignalToAddress(Core::System& system, VAddr address, Svc::Sign
2020 count); 2027 count);
2021} 2028}
2022 2029
2030static void SynchronizePreemptionState(Core::System& system) {
2031 auto& kernel = system.Kernel();
2032
2033 // Lock the scheduler.
2034 KScopedSchedulerLock sl{kernel};
2035
2036 // If the current thread is pinned, unpin it.
2037 KProcess* cur_process = system.Kernel().CurrentProcess();
2038 const auto core_id = GetCurrentCoreId(kernel);
2039
2040 if (cur_process->GetPinnedThread(core_id) == GetCurrentThreadPointer(kernel)) {
2041 // Clear the current thread's interrupt flag.
2042 GetCurrentThread(kernel).ClearInterruptFlag();
2043
2044 // Unpin the current thread.
2045 cur_process->UnpinCurrentThread(core_id);
2046 }
2047}
2048
2023static ResultCode SignalToAddress32(Core::System& system, u32 address, Svc::SignalType signal_type, 2049static ResultCode SignalToAddress32(Core::System& system, u32 address, Svc::SignalType signal_type,
2024 s32 value, s32 count) { 2050 s32 value, s32 count) {
2025 return SignalToAddress(system, address, signal_type, value, count); 2051 return SignalToAddress(system, address, signal_type, value, count);
@@ -2738,7 +2764,7 @@ static const FunctionDef SVC_Table_32[] = {
2738static const FunctionDef SVC_Table_64[] = { 2764static const FunctionDef SVC_Table_64[] = {
2739 {0x00, nullptr, "Unknown"}, 2765 {0x00, nullptr, "Unknown"},
2740 {0x01, SvcWrap64<SetHeapSize>, "SetHeapSize"}, 2766 {0x01, SvcWrap64<SetHeapSize>, "SetHeapSize"},
2741 {0x02, nullptr, "SetMemoryPermission"}, 2767 {0x02, SvcWrap64<SetMemoryPermission>, "SetMemoryPermission"},
2742 {0x03, SvcWrap64<SetMemoryAttribute>, "SetMemoryAttribute"}, 2768 {0x03, SvcWrap64<SetMemoryAttribute>, "SetMemoryAttribute"},
2743 {0x04, SvcWrap64<MapMemory>, "MapMemory"}, 2769 {0x04, SvcWrap64<MapMemory>, "MapMemory"},
2744 {0x05, SvcWrap64<UnmapMemory>, "UnmapMemory"}, 2770 {0x05, SvcWrap64<UnmapMemory>, "UnmapMemory"},
@@ -2790,7 +2816,7 @@ static const FunctionDef SVC_Table_64[] = {
2790 {0x33, SvcWrap64<GetThreadContext>, "GetThreadContext"}, 2816 {0x33, SvcWrap64<GetThreadContext>, "GetThreadContext"},
2791 {0x34, SvcWrap64<WaitForAddress>, "WaitForAddress"}, 2817 {0x34, SvcWrap64<WaitForAddress>, "WaitForAddress"},
2792 {0x35, SvcWrap64<SignalToAddress>, "SignalToAddress"}, 2818 {0x35, SvcWrap64<SignalToAddress>, "SignalToAddress"},
2793 {0x36, nullptr, "SynchronizePreemptionState"}, 2819 {0x36, SvcWrap64<SynchronizePreemptionState>, "SynchronizePreemptionState"},
2794 {0x37, nullptr, "Unknown"}, 2820 {0x37, nullptr, "Unknown"},
2795 {0x38, nullptr, "Unknown"}, 2821 {0x38, nullptr, "Unknown"},
2796 {0x39, nullptr, "Unknown"}, 2822 {0x39, nullptr, "Unknown"},
diff --git a/src/core/hle/kernel/svc_common.h b/src/core/hle/kernel/svc_common.h
index 60ea2c405..25de6e437 100644
--- a/src/core/hle/kernel/svc_common.h
+++ b/src/core/hle/kernel/svc_common.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "common/literals.h"
8 9
9namespace Kernel { 10namespace Kernel {
10using Handle = u32; 11using Handle = u32;
@@ -12,9 +13,13 @@ using Handle = u32;
12 13
13namespace Kernel::Svc { 14namespace Kernel::Svc {
14 15
16using namespace Common::Literals;
17
15constexpr s32 ArgumentHandleCountMax = 0x40; 18constexpr s32 ArgumentHandleCountMax = 0x40;
16constexpr u32 HandleWaitMask{1u << 30}; 19constexpr u32 HandleWaitMask{1u << 30};
17 20
21constexpr inline std::size_t HeapSizeAlignment = 2_MiB;
22
18constexpr inline Handle InvalidHandle = Handle(0); 23constexpr inline Handle InvalidHandle = Handle(0);
19 24
20enum PseudoHandle : Handle { 25enum PseudoHandle : Handle {
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index 86255fe6d..a60adfcab 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -249,6 +249,14 @@ void SvcWrap64(Core::System& system) {
249 func(system, Param(system, 0), Param(system, 1), static_cast<u32>(Param(system, 2))).raw); 249 func(system, Param(system, 0), Param(system, 1), static_cast<u32>(Param(system, 2))).raw);
250} 250}
251 251
252// Used by SetMemoryPermission
253template <ResultCode func(Core::System&, u64, u64, Svc::MemoryPermission)>
254void SvcWrap64(Core::System& system) {
255 FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
256 static_cast<Svc::MemoryPermission>(Param(system, 2)))
257 .raw);
258}
259
252// Used by MapSharedMemory 260// Used by MapSharedMemory
253template <ResultCode func(Core::System&, Handle, u64, u64, Svc::MemoryPermission)> 261template <ResultCode func(Core::System&, Handle, u64, u64, Svc::MemoryPermission)>
254void SvcWrap64(Core::System& system) { 262void SvcWrap64(Core::System& system) {
diff --git a/src/core/hle/lock.cpp b/src/core/hle/lock.cpp
deleted file mode 100644
index be4bfce3b..000000000
--- a/src/core/hle/lock.cpp
+++ /dev/null
@@ -1,9 +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 <core/hle/lock.h>
6
7namespace HLE {
8std::recursive_mutex g_hle_lock;
9}
diff --git a/src/core/hle/lock.h b/src/core/hle/lock.h
deleted file mode 100644
index 5c99fe996..000000000
--- a/src/core/hle/lock.h
+++ /dev/null
@@ -1,18 +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#pragma once
6
7#include <mutex>
8
9namespace HLE {
10/*
11 * Synchronizes access to the internal HLE kernel structures, it is acquired when a guest
12 * application thread performs a syscall. It should be acquired by any host threads that read or
13 * modify the HLE kernel state. Note: Any operation that directly or indirectly reads from or writes
14 * to the emulated memory is not protected by this mutex, and should be avoided in any threads other
15 * than the CPU thread.
16 */
17extern std::recursive_mutex g_hle_lock;
18} // namespace HLE
diff --git a/src/core/hle/service/bcat/backend/backend.cpp b/src/core/hle/service/bcat/backend/backend.cpp
index 4c7d3bb6e..ee49edbb9 100644
--- a/src/core/hle/service/bcat/backend/backend.cpp
+++ b/src/core/hle/service/bcat/backend/backend.cpp
@@ -6,7 +6,6 @@
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "core/core.h" 7#include "core/core.h"
8#include "core/hle/kernel/k_event.h" 8#include "core/hle/kernel/k_event.h"
9#include "core/hle/lock.h"
10#include "core/hle/service/bcat/backend/backend.h" 9#include "core/hle/service/bcat/backend/backend.h"
11 10
12namespace Service::BCAT { 11namespace Service::BCAT {
@@ -29,10 +28,6 @@ DeliveryCacheProgressImpl& ProgressServiceBackend::GetImpl() {
29 return impl; 28 return impl;
30} 29}
31 30
32void ProgressServiceBackend::SetNeedHLELock(bool need) {
33 need_hle_lock = need;
34}
35
36void ProgressServiceBackend::SetTotalSize(u64 size) { 31void ProgressServiceBackend::SetTotalSize(u64 size) {
37 impl.total_bytes = size; 32 impl.total_bytes = size;
38 SignalUpdate(); 33 SignalUpdate();
@@ -88,12 +83,7 @@ void ProgressServiceBackend::FinishDownload(ResultCode result) {
88} 83}
89 84
90void ProgressServiceBackend::SignalUpdate() { 85void ProgressServiceBackend::SignalUpdate() {
91 if (need_hle_lock) { 86 update_event->GetWritableEvent().Signal();
92 std::lock_guard lock(HLE::g_hle_lock);
93 update_event->GetWritableEvent().Signal();
94 } else {
95 update_event->GetWritableEvent().Signal();
96 }
97} 87}
98 88
99Backend::Backend(DirectoryGetter getter) : dir_getter(std::move(getter)) {} 89Backend::Backend(DirectoryGetter getter) : dir_getter(std::move(getter)) {}
diff --git a/src/core/hle/service/bcat/backend/backend.h b/src/core/hle/service/bcat/backend/backend.h
index 59c6d4740..63833c927 100644
--- a/src/core/hle/service/bcat/backend/backend.h
+++ b/src/core/hle/service/bcat/backend/backend.h
@@ -71,10 +71,6 @@ class ProgressServiceBackend {
71public: 71public:
72 ~ProgressServiceBackend(); 72 ~ProgressServiceBackend();
73 73
74 // Clients should call this with true if any of the functions are going to be called from a
75 // non-HLE thread and this class need to lock the hle mutex. (default is false)
76 void SetNeedHLELock(bool need);
77
78 // Sets the number of bytes total in the entire download. 74 // Sets the number of bytes total in the entire download.
79 void SetTotalSize(u64 size); 75 void SetTotalSize(u64 size);
80 76
@@ -109,7 +105,6 @@ private:
109 105
110 DeliveryCacheProgressImpl impl{}; 106 DeliveryCacheProgressImpl impl{};
111 Kernel::KEvent* update_event; 107 Kernel::KEvent* update_event;
112 bool need_hle_lock = false;
113}; 108};
114 109
115// A class representing an abstract backend for BCAT functionality. 110// A class representing an abstract backend for BCAT functionality.
diff --git a/src/core/hle/service/hid/controllers/console_sixaxis.cpp b/src/core/hle/service/hid/controllers/console_sixaxis.cpp
index f0f3105dc..a727b3582 100644
--- a/src/core/hle/service/hid/controllers/console_sixaxis.cpp
+++ b/src/core/hle/service/hid/controllers/console_sixaxis.cpp
@@ -33,15 +33,14 @@ void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_ti
33 const auto& last_entry = seven_sixaxis_lifo.ReadCurrentEntry().state; 33 const auto& last_entry = seven_sixaxis_lifo.ReadCurrentEntry().state;
34 next_seven_sixaxis_state.sampling_number = last_entry.sampling_number + 1; 34 next_seven_sixaxis_state.sampling_number = last_entry.sampling_number + 1;
35 35
36 // Try to read sixaxis sensor states
37 const auto motion_status = console->GetMotion(); 36 const auto motion_status = console->GetMotion();
37 last_global_timestamp = core_timing.GetGlobalTimeNs().count();
38 38
39 console_six_axis.is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest; 39 // This value increments every time the switch goes to sleep
40 40 next_seven_sixaxis_state.unknown = 1;
41 next_seven_sixaxis_state.timestamp = last_global_timestamp - last_saved_timestamp;
41 next_seven_sixaxis_state.accel = motion_status.accel; 42 next_seven_sixaxis_state.accel = motion_status.accel;
42 // Zero gyro values as they just mess up with the camera 43 next_seven_sixaxis_state.gyro = motion_status.gyro;
43 // Note: Probably a correct sensivity setting must be set
44 next_seven_sixaxis_state.gyro = {};
45 next_seven_sixaxis_state.quaternion = { 44 next_seven_sixaxis_state.quaternion = {
46 { 45 {
47 motion_status.quaternion.xyz.y, 46 motion_status.quaternion.xyz.y,
@@ -52,9 +51,9 @@ void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_ti
52 }; 51 };
53 52
54 console_six_axis.sampling_number++; 53 console_six_axis.sampling_number++;
55 // TODO(German77): Find the purpose of those values 54 console_six_axis.is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest;
56 console_six_axis.verticalization_error = 0.0f; 55 console_six_axis.verticalization_error = motion_status.verticalization_error;
57 console_six_axis.gyro_bias = {0.0f, 0.0f, 0.0f}; 56 console_six_axis.gyro_bias = motion_status.gyro_bias;
58 57
59 // Update console six axis shared memory 58 // Update console six axis shared memory
60 std::memcpy(data + SHARED_MEMORY_OFFSET, &console_six_axis, sizeof(console_six_axis)); 59 std::memcpy(data + SHARED_MEMORY_OFFSET, &console_six_axis, sizeof(console_six_axis));
@@ -69,7 +68,6 @@ void Controller_ConsoleSixAxis::SetTransferMemoryPointer(u8* t_mem) {
69} 68}
70 69
71void Controller_ConsoleSixAxis::ResetTimestamp() { 70void Controller_ConsoleSixAxis::ResetTimestamp() {
72 seven_sixaxis_lifo.buffer_count = 0; 71 last_saved_timestamp = last_global_timestamp;
73 seven_sixaxis_lifo.buffer_tail = 0;
74} 72}
75} // namespace Service::HID 73} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/console_sixaxis.h b/src/core/hle/service/hid/controllers/console_sixaxis.h
index 279241858..26d153f0c 100644
--- a/src/core/hle/service/hid/controllers/console_sixaxis.h
+++ b/src/core/hle/service/hid/controllers/console_sixaxis.h
@@ -39,8 +39,9 @@ public:
39 39
40private: 40private:
41 struct SevenSixAxisState { 41 struct SevenSixAxisState {
42 INSERT_PADDING_WORDS(4); // unused 42 INSERT_PADDING_WORDS(2); // unused
43 s64 sampling_number{}; 43 u64 timestamp{};
44 u64 sampling_number{};
44 u64 unknown{}; 45 u64 unknown{};
45 Common::Vec3f accel{}; 46 Common::Vec3f accel{};
46 Common::Vec3f gyro{}; 47 Common::Vec3f gyro{};
@@ -52,9 +53,10 @@ private:
52 struct ConsoleSharedMemory { 53 struct ConsoleSharedMemory {
53 u64 sampling_number{}; 54 u64 sampling_number{};
54 bool is_seven_six_axis_sensor_at_rest{}; 55 bool is_seven_six_axis_sensor_at_rest{};
55 INSERT_PADDING_BYTES(4); // padding 56 INSERT_PADDING_BYTES(3); // padding
56 f32 verticalization_error{}; 57 f32 verticalization_error{};
57 Common::Vec3f gyro_bias{}; 58 Common::Vec3f gyro_bias{};
59 INSERT_PADDING_BYTES(4); // padding
58 }; 60 };
59 static_assert(sizeof(ConsoleSharedMemory) == 0x20, "ConsoleSharedMemory is an invalid size"); 61 static_assert(sizeof(ConsoleSharedMemory) == 0x20, "ConsoleSharedMemory is an invalid size");
60 62
@@ -64,6 +66,8 @@ private:
64 Core::HID::EmulatedConsole* console; 66 Core::HID::EmulatedConsole* console;
65 u8* transfer_memory = nullptr; 67 u8* transfer_memory = nullptr;
66 bool is_transfer_memory_set = false; 68 bool is_transfer_memory_set = false;
69 u64 last_saved_timestamp{};
70 u64 last_global_timestamp{};
67 ConsoleSharedMemory console_six_axis{}; 71 ConsoleSharedMemory console_six_axis{};
68 SevenSixAxisState next_seven_sixaxis_state{}; 72 SevenSixAxisState next_seven_sixaxis_state{};
69}; 73};
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 2705e9dcb..e5c951e06 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -66,9 +66,9 @@ Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_,
66 auto& controller = controller_data[i]; 66 auto& controller = controller_data[i];
67 controller.device = hid_core.GetEmulatedControllerByIndex(i); 67 controller.device = hid_core.GetEmulatedControllerByIndex(i);
68 controller.vibration[Core::HID::EmulatedDeviceIndex::LeftIndex].latest_vibration_value = 68 controller.vibration[Core::HID::EmulatedDeviceIndex::LeftIndex].latest_vibration_value =
69 DEFAULT_VIBRATION_VALUE; 69 Core::HID::DEFAULT_VIBRATION_VALUE;
70 controller.vibration[Core::HID::EmulatedDeviceIndex::RightIndex].latest_vibration_value = 70 controller.vibration[Core::HID::EmulatedDeviceIndex::RightIndex].latest_vibration_value =
71 DEFAULT_VIBRATION_VALUE; 71 Core::HID::DEFAULT_VIBRATION_VALUE;
72 Core::HID::ControllerUpdateCallback engine_callback{ 72 Core::HID::ControllerUpdateCallback engine_callback{
73 .on_change = [this, 73 .on_change = [this,
74 i](Core::HID::ControllerTriggerType type) { ControllerUpdate(type, i); }, 74 i](Core::HID::ControllerTriggerType type) { ControllerUpdate(type, i); },
@@ -781,7 +781,8 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
781 Core::HID::VibrationValue vibration{0.0f, 160.0f, 0.0f, 320.0f}; 781 Core::HID::VibrationValue vibration{0.0f, 160.0f, 0.0f, 320.0f};
782 controller.device->SetVibration(device_index, vibration); 782 controller.device->SetVibration(device_index, vibration);
783 // Then reset the vibration value to its default value. 783 // Then reset the vibration value to its default value.
784 controller.vibration[device_index].latest_vibration_value = DEFAULT_VIBRATION_VALUE; 784 controller.vibration[device_index].latest_vibration_value =
785 Core::HID::DEFAULT_VIBRATION_VALUE;
785 } 786 }
786 787
787 return false; 788 return false;
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 63281cb35..6b2872bad 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -90,13 +90,6 @@ public:
90 Default = 3, 90 Default = 3,
91 }; 91 };
92 92
93 static constexpr Core::HID::VibrationValue DEFAULT_VIBRATION_VALUE{
94 .low_amplitude = 0.0f,
95 .low_frequency = 160.0f,
96 .high_amplitude = 0.0f,
97 .high_frequency = 320.0f,
98 };
99
100 void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set); 93 void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set);
101 Core::HID::NpadStyleTag GetSupportedStyleSet() const; 94 Core::HID::NpadStyleTag GetSupportedStyleSet() const;
102 95
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 7163e1a4e..6e12381fb 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -1404,7 +1404,7 @@ void Hid::SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
1404 .high_frequency = 0.0f, 1404 .high_frequency = 0.0f,
1405 }; 1405 };
1406 default: 1406 default:
1407 return Controller_NPad::DEFAULT_VIBRATION_VALUE; 1407 return Core::HID::DEFAULT_VIBRATION_VALUE;
1408 } 1408 }
1409 }(); 1409 }();
1410 1410
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index 693ffc71a..761d0d3c6 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -9,7 +9,6 @@
9#include "core/core.h" 9#include "core/core.h"
10#include "core/hle/ipc_helpers.h" 10#include "core/hle/ipc_helpers.h"
11#include "core/hle/kernel/k_event.h" 11#include "core/hle/kernel/k_event.h"
12#include "core/hle/lock.h"
13#include "core/hle/service/nfp/nfp.h" 12#include "core/hle/service/nfp/nfp.h"
14#include "core/hle/service/nfp/nfp_user.h" 13#include "core/hle/service/nfp/nfp_user.h"
15 14
@@ -337,7 +336,6 @@ void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
337} 336}
338 337
339bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) { 338bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) {
340 std::lock_guard lock{HLE::g_hle_lock};
341 if (buffer.size() < sizeof(AmiiboFile)) { 339 if (buffer.size() < sizeof(AmiiboFile)) {
342 return false; 340 return false;
343 } 341 }
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index b4c3a6099..5fead6d1b 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -186,6 +186,10 @@ u32 BufferQueue::Query(QueryType type) {
186 case QueryType::NativeWindowWidth: 186 case QueryType::NativeWindowWidth:
187 case QueryType::NativeWindowHeight: 187 case QueryType::NativeWindowHeight:
188 break; 188 break;
189 case QueryType::NativeWindowMinUndequeuedBuffers:
190 return 0;
191 case QueryType::NativeWindowConsumerUsageBits:
192 return 0;
189 } 193 }
190 UNIMPLEMENTED_MSG("Unimplemented query type={}", type); 194 UNIMPLEMENTED_MSG("Unimplemented query type={}", type);
191 return 0; 195 return 0;
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
index 7b7baeaea..f2a579133 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ b/src/core/hle/service/nvflinger/buffer_queue.h
@@ -57,6 +57,11 @@ public:
57 NativeWindowWidth = 0, 57 NativeWindowWidth = 0,
58 NativeWindowHeight = 1, 58 NativeWindowHeight = 1,
59 NativeWindowFormat = 2, 59 NativeWindowFormat = 2,
60 /// The minimum number of buffers that must remain un-dequeued after a buffer has been
61 /// queued
62 NativeWindowMinUndequeuedBuffers = 3,
63 /// The consumer gralloc usage bits currently set by the consumer
64 NativeWindowConsumerUsageBits = 10,
60 }; 65 };
61 66
62 explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_, 67 explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_,
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index a22811ec1..01e69de30 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -100,9 +100,6 @@ std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
100 100
101 LOG_DEBUG(Service, "Opening \"{}\" display", name); 101 LOG_DEBUG(Service, "Opening \"{}\" display", name);
102 102
103 // TODO(Subv): Currently we only support the Default display.
104 ASSERT(name == "Default");
105
106 const auto itr = 103 const auto itr =
107 std::find_if(displays.begin(), displays.end(), 104 std::find_if(displays.begin(), displays.end(),
108 [&](const VI::Display& display) { return display.GetName() == name; }); 105 [&](const VI::Display& display) { return display.GetName() == name; });
diff --git a/src/core/loader/kip.cpp b/src/core/loader/kip.cpp
index 3ae9e6e0e..99ed34b00 100644
--- a/src/core/loader/kip.cpp
+++ b/src/core/loader/kip.cpp
@@ -71,7 +71,6 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::KProcess& process,
71 kip->GetTitleID(), 0xFFFFFFFFFFFFFFFF, 0x1FE00000, 71 kip->GetTitleID(), 0xFFFFFFFFFFFFFFFF, 0x1FE00000,
72 kip->GetKernelCapabilities()); 72 kip->GetKernelCapabilities());
73 73
74 const VAddr base_address = process.PageTable().GetCodeRegionStart();
75 Kernel::CodeSet codeset; 74 Kernel::CodeSet codeset;
76 Kernel::PhysicalMemory program_image; 75 Kernel::PhysicalMemory program_image;
77 76
@@ -91,7 +90,14 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::KProcess& process,
91 program_image.resize(PageAlignSize(kip->GetBSSOffset()) + kip->GetBSSSize()); 90 program_image.resize(PageAlignSize(kip->GetBSSOffset()) + kip->GetBSSSize());
92 codeset.DataSegment().size += kip->GetBSSSize(); 91 codeset.DataSegment().size += kip->GetBSSSize();
93 92
93 // Setup the process code layout
94 if (process.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), program_image.size())
95 .IsError()) {
96 return {ResultStatus::ErrorNotInitialized, {}};
97 }
98
94 codeset.memory = std::move(program_image); 99 codeset.memory = std::move(program_image);
100 const VAddr base_address = process.PageTable().GetCodeRegionStart();
95 process.LoadModule(std::move(codeset), base_address); 101 process.LoadModule(std::move(codeset), base_address);
96 102
97 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", kip->GetName(), base_address); 103 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", kip->GetName(), base_address);
diff --git a/src/input_common/drivers/udp_client.cpp b/src/input_common/drivers/udp_client.cpp
index 4ab991a7d..a1ce4525d 100644
--- a/src/input_common/drivers/udp_client.cpp
+++ b/src/input_common/drivers/udp_client.cpp
@@ -536,42 +536,46 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
536 std::function<void(u16, u16, u16, u16)> data_callback) { 536 std::function<void(u16, u16, u16, u16)> data_callback) {
537 537
538 std::thread([=, this] { 538 std::thread([=, this] {
539 u16 min_x{UINT16_MAX};
540 u16 min_y{UINT16_MAX};
541 u16 max_x{};
542 u16 max_y{};
543
539 Status current_status{Status::Initialized}; 544 Status current_status{Status::Initialized};
540 SocketCallback callback{ 545 SocketCallback callback{[](Response::Version) {}, [](Response::PortInfo) {},
541 [](Response::Version) {}, [](Response::PortInfo) {}, 546 [&](Response::PadData data) {
542 [&](Response::PadData data) { 547 constexpr u16 CALIBRATION_THRESHOLD = 100;
543 static constexpr u16 CALIBRATION_THRESHOLD = 100; 548
544 static constexpr u16 MAX_VALUE = UINT16_MAX; 549 if (current_status == Status::Initialized) {
545 550 // Receiving data means the communication is ready now
546 if (current_status == Status::Initialized) { 551 current_status = Status::Ready;
547 // Receiving data means the communication is ready now 552 status_callback(current_status);
548 current_status = Status::Ready; 553 }
549 status_callback(current_status); 554 if (data.touch[0].is_active == 0) {
550 } 555 return;
551 const auto& touchpad_0 = data.touch[0]; 556 }
552 if (touchpad_0.is_active == 0) { 557 LOG_DEBUG(Input, "Current touch: {} {}", data.touch[0].x,
553 return; 558 data.touch[0].y);
554 } 559 min_x = std::min(min_x, static_cast<u16>(data.touch[0].x));
555 LOG_DEBUG(Input, "Current touch: {} {}", touchpad_0.x, touchpad_0.y); 560 min_y = std::min(min_y, static_cast<u16>(data.touch[0].y));
556 const u16 min_x = std::min(MAX_VALUE, static_cast<u16>(touchpad_0.x)); 561 if (current_status == Status::Ready) {
557 const u16 min_y = std::min(MAX_VALUE, static_cast<u16>(touchpad_0.y)); 562 // First touch - min data (min_x/min_y)
558 if (current_status == Status::Ready) { 563 current_status = Status::Stage1Completed;
559 // First touch - min data (min_x/min_y) 564 status_callback(current_status);
560 current_status = Status::Stage1Completed; 565 }
561 status_callback(current_status); 566 if (data.touch[0].x - min_x > CALIBRATION_THRESHOLD &&
562 } 567 data.touch[0].y - min_y > CALIBRATION_THRESHOLD) {
563 if (touchpad_0.x - min_x > CALIBRATION_THRESHOLD && 568 // Set the current position as max value and finishes
564 touchpad_0.y - min_y > CALIBRATION_THRESHOLD) { 569 // configuration
565 // Set the current position as max value and finishes configuration 570 max_x = data.touch[0].x;
566 const u16 max_x = touchpad_0.x; 571 max_y = data.touch[0].y;
567 const u16 max_y = touchpad_0.y; 572 current_status = Status::Completed;
568 current_status = Status::Completed; 573 data_callback(min_x, min_y, max_x, max_y);
569 data_callback(min_x, min_y, max_x, max_y); 574 status_callback(current_status);
570 status_callback(current_status); 575
571 576 complete_event.Set();
572 complete_event.Set(); 577 }
573 } 578 }};
574 }};
575 Socket socket{host, port, std::move(callback)}; 579 Socket socket{host, port, std::move(callback)};
576 std::thread worker_thread{SocketLoop, &socket}; 580 std::thread worker_thread{SocketLoop, &socket};
577 complete_event.Wait(); 581 complete_event.Wait();
diff --git a/src/input_common/helpers/udp_protocol.h b/src/input_common/helpers/udp_protocol.h
index bcba12c58..2d5d54ddb 100644
--- a/src/input_common/helpers/udp_protocol.h
+++ b/src/input_common/helpers/udp_protocol.h
@@ -54,6 +54,18 @@ struct Message {
54template <typename T> 54template <typename T>
55constexpr Type GetMessageType(); 55constexpr Type GetMessageType();
56 56
57template <typename T>
58Message<T> CreateMessage(const u32 magic, const T data, const u32 sender_id) {
59 boost::crc_32_type crc;
60 Header header{
61 magic, PROTOCOL_VERSION, sizeof(T) + sizeof(Type), 0, sender_id, GetMessageType<T>(),
62 };
63 Message<T> message{header, data};
64 crc.process_bytes(&message, sizeof(Message<T>));
65 message.header.crc = crc.checksum();
66 return message;
67}
68
57namespace Request { 69namespace Request {
58 70
59enum RegisterFlags : u8 { 71enum RegisterFlags : u8 {
@@ -101,14 +113,7 @@ static_assert(std::is_trivially_copyable_v<PadData>,
101 */ 113 */
102template <typename T> 114template <typename T>
103Message<T> Create(const T data, const u32 client_id = 0) { 115Message<T> Create(const T data, const u32 client_id = 0) {
104 boost::crc_32_type crc; 116 return CreateMessage(CLIENT_MAGIC, data, client_id);
105 Header header{
106 CLIENT_MAGIC, PROTOCOL_VERSION, sizeof(T) + sizeof(Type), 0, client_id, GetMessageType<T>(),
107 };
108 Message<T> message{header, data};
109 crc.process_bytes(&message, sizeof(Message<T>));
110 message.header.crc = crc.checksum();
111 return message;
112} 117}
113} // namespace Request 118} // namespace Request
114 119
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp
index 081b2c8e0..7434a1f92 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp
@@ -86,7 +86,7 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, Scal
86 } 86 }
87 switch (attr) { 87 switch (attr) {
88 case IR::Attribute::PrimitiveId: 88 case IR::Attribute::PrimitiveId:
89 ctx.Add("MOV.S {}.x,primitive.id;", inst); 89 ctx.Add("MOV.F {}.x,primitive.id;", inst);
90 break; 90 break;
91 case IR::Attribute::PositionX: 91 case IR::Attribute::PositionX:
92 case IR::Attribute::PositionY: 92 case IR::Attribute::PositionY:
@@ -113,19 +113,35 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, Scal
113 ctx.Add("MOV.F {}.x,vertex.tesscoord.{};", inst, swizzle); 113 ctx.Add("MOV.F {}.x,vertex.tesscoord.{};", inst, swizzle);
114 break; 114 break;
115 case IR::Attribute::InstanceId: 115 case IR::Attribute::InstanceId:
116 ctx.Add("MOV.S {}.x,{}.instance;", inst, ctx.attrib_name); 116 ctx.Add("MOV.F {}.x,{}.instance;", inst, ctx.attrib_name);
117 break; 117 break;
118 case IR::Attribute::VertexId: 118 case IR::Attribute::VertexId:
119 ctx.Add("MOV.S {}.x,{}.id;", inst, ctx.attrib_name); 119 ctx.Add("MOV.F {}.x,{}.id;", inst, ctx.attrib_name);
120 break; 120 break;
121 case IR::Attribute::FrontFace: 121 case IR::Attribute::FrontFace:
122 ctx.Add("CMP.S {}.x,{}.facing.x,0,-1;", inst, ctx.attrib_name); 122 ctx.Add("CMP.F {}.x,{}.facing.x,0,-1;", inst, ctx.attrib_name);
123 break; 123 break;
124 default: 124 default:
125 throw NotImplementedException("Get attribute {}", attr); 125 throw NotImplementedException("Get attribute {}", attr);
126 } 126 }
127} 127}
128 128
129void EmitGetAttributeU32(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, ScalarU32) {
130 switch (attr) {
131 case IR::Attribute::PrimitiveId:
132 ctx.Add("MOV.S {}.x,primitive.id;", inst);
133 break;
134 case IR::Attribute::InstanceId:
135 ctx.Add("MOV.S {}.x,{}.instance;", inst, ctx.attrib_name);
136 break;
137 case IR::Attribute::VertexId:
138 ctx.Add("MOV.S {}.x,{}.id;", inst, ctx.attrib_name);
139 break;
140 default:
141 throw NotImplementedException("Get U32 attribute {}", attr);
142 }
143}
144
129void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, ScalarF32 value, 145void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, ScalarF32 value,
130 [[maybe_unused]] ScalarU32 vertex) { 146 [[maybe_unused]] ScalarU32 vertex) {
131 const u32 element{static_cast<u32>(attr) % 4}; 147 const u32 element{static_cast<u32>(attr) % 4};
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
index 1f343bff5..b48007856 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
@@ -50,6 +50,7 @@ void EmitGetCbufU32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
50void EmitGetCbufF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset); 50void EmitGetCbufF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset);
51void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset); 51void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset);
52void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, ScalarU32 vertex); 52void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, ScalarU32 vertex);
53void EmitGetAttributeU32(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, ScalarU32 vertex);
53void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, ScalarF32 value, ScalarU32 vertex); 54void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, ScalarF32 value, ScalarU32 vertex);
54void EmitGetAttributeIndexed(EmitContext& ctx, IR::Inst& inst, ScalarS32 offset, ScalarU32 vertex); 55void EmitGetAttributeIndexed(EmitContext& ctx, IR::Inst& inst, ScalarS32 offset, ScalarU32 vertex);
55void EmitSetAttributeIndexed(EmitContext& ctx, ScalarU32 offset, ScalarF32 value, ScalarU32 vertex); 56void EmitSetAttributeIndexed(EmitContext& ctx, ScalarU32 offset, ScalarF32 value, ScalarU32 vertex);
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl.cpp b/src/shader_recompiler/backend/glsl/emit_glsl.cpp
index 78b2eeaa2..b6b17a330 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl.cpp
@@ -176,7 +176,7 @@ void EmitCode(EmitContext& ctx, const IR::Program& program) {
176} 176}
177 177
178std::string GlslVersionSpecifier(const EmitContext& ctx) { 178std::string GlslVersionSpecifier(const EmitContext& ctx) {
179 if (ctx.uses_y_direction || ctx.info.stores.Legacy() || ctx.info.loads.Legacy()) { 179 if (ctx.uses_y_direction) {
180 return " compatibility"; 180 return " compatibility";
181 } 181 }
182 return ""; 182 return "";
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp
index 0f2668d9e..e0ead7a53 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp
@@ -7,6 +7,7 @@
7#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" 7#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
8#include "shader_recompiler/backend/glsl/glsl_emit_context.h" 8#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
9#include "shader_recompiler/frontend/ir/value.h" 9#include "shader_recompiler/frontend/ir/value.h"
10#include "shader_recompiler/profile.h"
10 11
11namespace Shader::Backend::GLSL { 12namespace Shader::Backend::GLSL {
12namespace { 13namespace {
@@ -30,8 +31,9 @@ void EmitConditionRef(EmitContext& ctx, IR::Inst& inst, const IR::Value& value)
30 inst.DestructiveAddUsage(1); 31 inst.DestructiveAddUsage(1);
31 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U1)}; 32 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U1)};
32 const auto input{ctx.var_alloc.Consume(value)}; 33 const auto input{ctx.var_alloc.Consume(value)};
34 const auto suffix{ctx.profile.has_gl_bool_ref_bug ? "?true:false" : ""};
33 if (ret != input) { 35 if (ret != input) {
34 ctx.Add("{}={};", ret, input); 36 ctx.Add("{}={}{};", ret, input, suffix);
35 } 37 }
36} 38}
37 39
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
index 1920047f4..0c1fbc7b1 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
@@ -98,47 +98,50 @@ void GetCbuf16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, const
98 GetCbuf(ctx, ret, binding, offset, 16, cast, bit_offset); 98 GetCbuf(ctx, ret, binding, offset, 16, cast, bit_offset);
99 } 99 }
100} 100}
101
102u32 TexCoordIndex(IR::Attribute attr) {
103 return (static_cast<u32>(attr) - static_cast<u32>(IR::Attribute::FixedFncTexture0S)) / 4;
104}
105} // Anonymous namespace 101} // Anonymous namespace
106 102
107void EmitGetCbufU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, 103void EmitGetCbufU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
108 const IR::Value& offset) { 104 const IR::Value& offset) {
109 GetCbuf8(ctx, inst, binding, offset, "ftou"); 105 const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
106 GetCbuf8(ctx, inst, binding, offset, cast);
110} 107}
111 108
112void EmitGetCbufS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, 109void EmitGetCbufS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
113 const IR::Value& offset) { 110 const IR::Value& offset) {
114 GetCbuf8(ctx, inst, binding, offset, "ftoi"); 111 const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "int" : "ftoi"};
112 GetCbuf8(ctx, inst, binding, offset, cast);
115} 113}
116 114
117void EmitGetCbufU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, 115void EmitGetCbufU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
118 const IR::Value& offset) { 116 const IR::Value& offset) {
119 GetCbuf16(ctx, inst, binding, offset, "ftou"); 117 const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
118 GetCbuf16(ctx, inst, binding, offset, cast);
120} 119}
121 120
122void EmitGetCbufS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, 121void EmitGetCbufS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
123 const IR::Value& offset) { 122 const IR::Value& offset) {
124 GetCbuf16(ctx, inst, binding, offset, "ftoi"); 123 const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "int" : "ftoi"};
124 GetCbuf16(ctx, inst, binding, offset, cast);
125} 125}
126 126
127void EmitGetCbufU32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, 127void EmitGetCbufU32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
128 const IR::Value& offset) { 128 const IR::Value& offset) {
129 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)}; 129 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)};
130 GetCbuf(ctx, ret, binding, offset, 32, "ftou"); 130 const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
131 GetCbuf(ctx, ret, binding, offset, 32, cast);
131} 132}
132 133
133void EmitGetCbufF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, 134void EmitGetCbufF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
134 const IR::Value& offset) { 135 const IR::Value& offset) {
135 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::F32)}; 136 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::F32)};
136 GetCbuf(ctx, ret, binding, offset, 32); 137 const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "utof" : ""};
138 GetCbuf(ctx, ret, binding, offset, 32, cast);
137} 139}
138 140
139void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, 141void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
140 const IR::Value& offset) { 142 const IR::Value& offset) {
141 const auto cbuf{fmt::format("{}_cbuf{}", ctx.stage_name, binding.U32())}; 143 const auto cbuf{fmt::format("{}_cbuf{}", ctx.stage_name, binding.U32())};
144 const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
142 if (offset.IsImmediate()) { 145 if (offset.IsImmediate()) {
143 static constexpr u32 cbuf_size{0x10000}; 146 static constexpr u32 cbuf_size{0x10000};
144 const u32 u32_offset{offset.U32()}; 147 const u32 u32_offset{offset.U32()};
@@ -149,26 +152,26 @@ void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding
149 return; 152 return;
150 } 153 }
151 if (u32_offset % 2 == 0) { 154 if (u32_offset % 2 == 0) {
152 ctx.AddU32x2("{}=ftou({}[{}].{}{});", inst, cbuf, u32_offset / 16, 155 ctx.AddU32x2("{}={}({}[{}].{}{});", inst, cast, cbuf, u32_offset / 16,
153 OffsetSwizzle(u32_offset), OffsetSwizzle(u32_offset + 4)); 156 OffsetSwizzle(u32_offset), OffsetSwizzle(u32_offset + 4));
154 } else { 157 } else {
155 ctx.AddU32x2("{}=uvec2(ftou({}[{}].{}),ftou({}[{}].{}));", inst, cbuf, u32_offset / 16, 158 ctx.AddU32x2("{}=uvec2({}({}[{}].{}),{}({}[{}].{}));", inst, cast, cbuf,
156 OffsetSwizzle(u32_offset), cbuf, (u32_offset + 4) / 16, 159 u32_offset / 16, OffsetSwizzle(u32_offset), cast, cbuf,
157 OffsetSwizzle(u32_offset + 4)); 160 (u32_offset + 4) / 16, OffsetSwizzle(u32_offset + 4));
158 } 161 }
159 return; 162 return;
160 } 163 }
161 const auto offset_var{ctx.var_alloc.Consume(offset)}; 164 const auto offset_var{ctx.var_alloc.Consume(offset)};
162 if (!ctx.profile.has_gl_component_indexing_bug) { 165 if (!ctx.profile.has_gl_component_indexing_bug) {
163 ctx.AddU32x2("{}=uvec2(ftou({}[{}>>4][({}>>2)%4]),ftou({}[({}+4)>>4][(({}+4)>>2)%4]));", 166 ctx.AddU32x2("{}=uvec2({}({}[{}>>4][({}>>2)%4]),{}({}[({}+4)>>4][(({}+4)>>2)%4]));", inst,
164 inst, cbuf, offset_var, offset_var, cbuf, offset_var, offset_var); 167 cast, cbuf, offset_var, offset_var, cast, cbuf, offset_var, offset_var);
165 return; 168 return;
166 } 169 }
167 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x2)}; 170 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x2)};
168 const auto cbuf_offset{fmt::format("{}>>2", offset_var)}; 171 const auto cbuf_offset{fmt::format("{}>>2", offset_var)};
169 for (u32 swizzle = 0; swizzle < 4; ++swizzle) { 172 for (u32 swizzle = 0; swizzle < 4; ++swizzle) {
170 ctx.Add("if(({}&3)=={}){}=uvec2(ftou({}[{}>>4].{}),ftou({}[({}+4)>>4].{}));", cbuf_offset, 173 ctx.Add("if(({}&3)=={}){}=uvec2({}({}[{}>>4].{}),{}({}[({}+4)>>4].{}));", cbuf_offset,
171 swizzle, ret, cbuf, offset_var, "xyzw"[swizzle], cbuf, offset_var, 174 swizzle, ret, cast, cbuf, offset_var, "xyzw"[swizzle], cast, cbuf, offset_var,
172 "xyzw"[(swizzle + 1) % 4]); 175 "xyzw"[(swizzle + 1) % 4]);
173 } 176 }
174} 177}
@@ -190,18 +193,6 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
190 ctx.AddF32("{}=in_attr{}{}.{};", inst, index, InputVertexIndex(ctx, vertex), swizzle); 193 ctx.AddF32("{}=in_attr{}{}.{};", inst, index, InputVertexIndex(ctx, vertex), swizzle);
191 return; 194 return;
192 } 195 }
193 // GLSL only exposes 8 legacy texcoords
194 if (attr >= IR::Attribute::FixedFncTexture8S && attr <= IR::Attribute::FixedFncTexture9Q) {
195 LOG_WARNING(Shader_GLSL, "GLSL does not allow access to gl_TexCoord[{}]",
196 TexCoordIndex(attr));
197 ctx.AddF32("{}=0.f;", inst);
198 return;
199 }
200 if (attr >= IR::Attribute::FixedFncTexture0S && attr <= IR::Attribute::FixedFncTexture7Q) {
201 const u32 index{TexCoordIndex(attr)};
202 ctx.AddF32("{}=gl_TexCoord[{}].{};", inst, index, swizzle);
203 return;
204 }
205 switch (attr) { 196 switch (attr) {
206 case IR::Attribute::PrimitiveId: 197 case IR::Attribute::PrimitiveId:
207 ctx.AddF32("{}=itof(gl_PrimitiveID);", inst); 198 ctx.AddF32("{}=itof(gl_PrimitiveID);", inst);
@@ -215,16 +206,6 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
215 ctx.AddF32("{}={}{}.{};", inst, input_decorator, ctx.position_name, swizzle); 206 ctx.AddF32("{}={}{}.{};", inst, input_decorator, ctx.position_name, swizzle);
216 break; 207 break;
217 } 208 }
218 case IR::Attribute::ColorFrontDiffuseR:
219 case IR::Attribute::ColorFrontDiffuseG:
220 case IR::Attribute::ColorFrontDiffuseB:
221 case IR::Attribute::ColorFrontDiffuseA:
222 if (ctx.stage == Stage::Fragment) {
223 ctx.AddF32("{}=gl_Color.{};", inst, swizzle);
224 } else {
225 ctx.AddF32("{}=gl_FrontColor.{};", inst, swizzle);
226 }
227 break;
228 case IR::Attribute::PointSpriteS: 209 case IR::Attribute::PointSpriteS:
229 case IR::Attribute::PointSpriteT: 210 case IR::Attribute::PointSpriteT:
230 ctx.AddF32("{}=gl_PointCoord.{};", inst, swizzle); 211 ctx.AddF32("{}=gl_PointCoord.{};", inst, swizzle);
@@ -247,6 +228,22 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
247 } 228 }
248} 229}
249 230
231void EmitGetAttributeU32(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, std::string_view) {
232 switch (attr) {
233 case IR::Attribute::PrimitiveId:
234 ctx.AddU32("{}=uint(gl_PrimitiveID);", inst);
235 break;
236 case IR::Attribute::InstanceId:
237 ctx.AddU32("{}=uint(gl_InstanceID);", inst);
238 break;
239 case IR::Attribute::VertexId:
240 ctx.AddU32("{}=uint(gl_VertexID);", inst);
241 break;
242 default:
243 throw NotImplementedException("Get U32 attribute {}", attr);
244 }
245}
246
250void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, std::string_view value, 247void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, std::string_view value,
251 [[maybe_unused]] std::string_view vertex) { 248 [[maybe_unused]] std::string_view vertex) {
252 if (IR::IsGeneric(attr)) { 249 if (IR::IsGeneric(attr)) {
@@ -264,17 +261,6 @@ void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, std::string_view val
264 } 261 }
265 const u32 element{static_cast<u32>(attr) % 4}; 262 const u32 element{static_cast<u32>(attr) % 4};
266 const char swizzle{"xyzw"[element]}; 263 const char swizzle{"xyzw"[element]};
267 // GLSL only exposes 8 legacy texcoords
268 if (attr >= IR::Attribute::FixedFncTexture8S && attr <= IR::Attribute::FixedFncTexture9Q) {
269 LOG_WARNING(Shader_GLSL, "GLSL does not allow access to gl_TexCoord[{}]",
270 TexCoordIndex(attr));
271 return;
272 }
273 if (attr >= IR::Attribute::FixedFncTexture0S && attr <= IR::Attribute::FixedFncTexture7Q) {
274 const u32 index{TexCoordIndex(attr)};
275 ctx.Add("gl_TexCoord[{}].{}={};", index, swizzle, value);
276 return;
277 }
278 switch (attr) { 264 switch (attr) {
279 case IR::Attribute::Layer: 265 case IR::Attribute::Layer:
280 if (ctx.stage != Stage::Geometry && 266 if (ctx.stage != Stage::Geometry &&
@@ -312,33 +298,6 @@ void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, std::string_view val
312 case IR::Attribute::PositionW: 298 case IR::Attribute::PositionW:
313 ctx.Add("gl_Position.{}={};", swizzle, value); 299 ctx.Add("gl_Position.{}={};", swizzle, value);
314 break; 300 break;
315 case IR::Attribute::ColorFrontDiffuseR:
316 case IR::Attribute::ColorFrontDiffuseG:
317 case IR::Attribute::ColorFrontDiffuseB:
318 case IR::Attribute::ColorFrontDiffuseA:
319 ctx.Add("gl_FrontColor.{}={};", swizzle, value);
320 break;
321 case IR::Attribute::ColorFrontSpecularR:
322 case IR::Attribute::ColorFrontSpecularG:
323 case IR::Attribute::ColorFrontSpecularB:
324 case IR::Attribute::ColorFrontSpecularA:
325 ctx.Add("gl_FrontSecondaryColor.{}={};", swizzle, value);
326 break;
327 case IR::Attribute::ColorBackDiffuseR:
328 case IR::Attribute::ColorBackDiffuseG:
329 case IR::Attribute::ColorBackDiffuseB:
330 case IR::Attribute::ColorBackDiffuseA:
331 ctx.Add("gl_BackColor.{}={};", swizzle, value);
332 break;
333 case IR::Attribute::ColorBackSpecularR:
334 case IR::Attribute::ColorBackSpecularG:
335 case IR::Attribute::ColorBackSpecularB:
336 case IR::Attribute::ColorBackSpecularA:
337 ctx.Add("gl_BackSecondaryColor.{}={};", swizzle, value);
338 break;
339 case IR::Attribute::FogCoordinate:
340 ctx.Add("gl_FogFragCoord={};", value);
341 break;
342 case IR::Attribute::ClipDistance0: 301 case IR::Attribute::ClipDistance0:
343 case IR::Attribute::ClipDistance1: 302 case IR::Attribute::ClipDistance1:
344 case IR::Attribute::ClipDistance2: 303 case IR::Attribute::ClipDistance2:
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_floating_point.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_floating_point.cpp
index b765a251b..474189d87 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_floating_point.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_floating_point.cpp
@@ -125,11 +125,11 @@ void EmitFPNeg16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& i
125} 125}
126 126
127void EmitFPNeg32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { 127void EmitFPNeg32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
128 ctx.AddF32("{}=-({});", inst, value); 128 ctx.AddF32("{}=0.f-({});", inst, value);
129} 129}
130 130
131void EmitFPNeg64(EmitContext& ctx, IR::Inst& inst, std::string_view value) { 131void EmitFPNeg64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
132 ctx.AddF64("{}=-({});", inst, value); 132 ctx.AddF64("{}=double(0.)-({});", inst, value);
133} 133}
134 134
135void EmitFPSin(EmitContext& ctx, IR::Inst& inst, std::string_view value) { 135void EmitFPSin(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h
index f86502e4c..6cabbc717 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h
@@ -60,6 +60,8 @@ void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding
60 const IR::Value& offset); 60 const IR::Value& offset);
61void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, 61void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
62 std::string_view vertex); 62 std::string_view vertex);
63void EmitGetAttributeU32(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
64 std::string_view vertex);
63void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, std::string_view value, 65void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, std::string_view value,
64 std::string_view vertex); 66 std::string_view vertex);
65void EmitGetAttributeIndexed(EmitContext& ctx, IR::Inst& inst, std::string_view offset, 67void EmitGetAttributeIndexed(EmitContext& ctx, IR::Inst& inst, std::string_view offset,
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_integer.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_integer.cpp
index 44060df33..b0d85be99 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_integer.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_integer.cpp
@@ -87,11 +87,11 @@ void EmitUDiv32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::strin
87} 87}
88 88
89void EmitINeg32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { 89void EmitINeg32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
90 ctx.AddU32("{}=uint(-({}));", inst, value); 90 ctx.AddU32("{}=uint(int(0)-int({}));", inst, value);
91} 91}
92 92
93void EmitINeg64(EmitContext& ctx, IR::Inst& inst, std::string_view value) { 93void EmitINeg64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
94 ctx.AddU64("{}=-({});", inst, value); 94 ctx.AddU64("{}=uint64_t(int64_t(0)-int64_t({}));", inst, value);
95} 95}
96 96
97void EmitIAbs32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { 97void EmitIAbs32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp
index b8ddafe48..fcf620b79 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp
@@ -90,7 +90,9 @@ void EmitPhiMove(EmitContext& ctx, const IR::Value& phi_value, const IR::Value&
90 if (phi_reg == val_reg) { 90 if (phi_reg == val_reg) {
91 return; 91 return;
92 } 92 }
93 ctx.Add("{}={};", phi_reg, val_reg); 93 const bool needs_workaround{ctx.profile.has_gl_bool_ref_bug && phi_type == IR::Type::U1};
94 const auto suffix{needs_workaround ? "?true:false" : ""};
95 ctx.Add("{}={}{};", phi_reg, val_reg, suffix);
94} 96}
95 97
96void EmitPrologue(EmitContext& ctx) { 98void EmitPrologue(EmitContext& ctx) {
diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
index 1de017e76..bb7f1a0fd 100644
--- a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
+++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
@@ -211,27 +211,6 @@ std::string_view OutputPrimitive(OutputTopology topology) {
211 throw InvalidArgument("Invalid output topology {}", topology); 211 throw InvalidArgument("Invalid output topology {}", topology);
212} 212}
213 213
214void SetupLegacyOutPerVertex(EmitContext& ctx, std::string& header) {
215 if (!ctx.info.stores.Legacy()) {
216 return;
217 }
218 if (ctx.info.stores.FixedFunctionTexture()) {
219 header += "vec4 gl_TexCoord[8];";
220 }
221 if (ctx.info.stores.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) {
222 header += "vec4 gl_FrontColor;";
223 }
224 if (ctx.info.stores.AnyComponent(IR::Attribute::ColorFrontSpecularR)) {
225 header += "vec4 gl_FrontSecondaryColor;";
226 }
227 if (ctx.info.stores.AnyComponent(IR::Attribute::ColorBackDiffuseR)) {
228 header += "vec4 gl_BackColor;";
229 }
230 if (ctx.info.stores.AnyComponent(IR::Attribute::ColorBackSpecularR)) {
231 header += "vec4 gl_BackSecondaryColor;";
232 }
233}
234
235void SetupOutPerVertex(EmitContext& ctx, std::string& header) { 214void SetupOutPerVertex(EmitContext& ctx, std::string& header) {
236 if (!StoresPerVertexAttributes(ctx.stage)) { 215 if (!StoresPerVertexAttributes(ctx.stage)) {
237 return; 216 return;
@@ -250,7 +229,6 @@ void SetupOutPerVertex(EmitContext& ctx, std::string& header) {
250 ctx.profile.support_viewport_index_layer_non_geometry && ctx.stage != Stage::Geometry) { 229 ctx.profile.support_viewport_index_layer_non_geometry && ctx.stage != Stage::Geometry) {
251 header += "int gl_ViewportIndex;"; 230 header += "int gl_ViewportIndex;";
252 } 231 }
253 SetupLegacyOutPerVertex(ctx, header);
254 header += "};"; 232 header += "};";
255 if (ctx.info.stores[IR::Attribute::ViewportIndex] && ctx.stage == Stage::Geometry) { 233 if (ctx.info.stores[IR::Attribute::ViewportIndex] && ctx.stage == Stage::Geometry) {
256 header += "out int gl_ViewportIndex;"; 234 header += "out int gl_ViewportIndex;";
@@ -282,21 +260,6 @@ void SetupInPerVertex(EmitContext& ctx, std::string& header) {
282 } 260 }
283 header += "}gl_in[gl_MaxPatchVertices];"; 261 header += "}gl_in[gl_MaxPatchVertices];";
284} 262}
285
286void SetupLegacyInPerFragment(EmitContext& ctx, std::string& header) {
287 if (!ctx.info.loads.Legacy()) {
288 return;
289 }
290 header += "in gl_PerFragment{";
291 if (ctx.info.loads.FixedFunctionTexture()) {
292 header += "vec4 gl_TexCoord[8];";
293 }
294 if (ctx.info.loads.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) {
295 header += "vec4 gl_Color;";
296 }
297 header += "};";
298}
299
300} // Anonymous namespace 263} // Anonymous namespace
301 264
302EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_, 265EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_,
@@ -361,7 +324,6 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
361 } 324 }
362 SetupOutPerVertex(*this, header); 325 SetupOutPerVertex(*this, header);
363 SetupInPerVertex(*this, header); 326 SetupInPerVertex(*this, header);
364 SetupLegacyInPerFragment(*this, header);
365 327
366 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) { 328 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
367 if (!info.loads.Generic(index) || !runtime_info.previous_stage_stores.Generic(index)) { 329 if (!info.loads.Generic(index) || !runtime_info.previous_stage_stores.Generic(index)) {
@@ -466,9 +428,10 @@ void EmitContext::DefineConstantBuffers(Bindings& bindings) {
466 return; 428 return;
467 } 429 }
468 for (const auto& desc : info.constant_buffer_descriptors) { 430 for (const auto& desc : info.constant_buffer_descriptors) {
469 header += fmt::format( 431 const auto cbuf_type{profile.has_gl_cbuf_ftou_bug ? "uvec4" : "vec4"};
470 "layout(std140,binding={}) uniform {}_cbuf_{}{{vec4 {}_cbuf{}[{}];}};", 432 header += fmt::format("layout(std140,binding={}) uniform {}_cbuf_{}{{{} {}_cbuf{}[{}];}};",
471 bindings.uniform_buffer, stage_name, desc.index, stage_name, desc.index, 4 * 1024); 433 bindings.uniform_buffer, stage_name, desc.index, cbuf_type,
434 stage_name, desc.index, 4 * 1024);
472 bindings.uniform_buffer += desc.count; 435 bindings.uniform_buffer += desc.count;
473 } 436 }
474} 437}
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
index 6ce7ed12a..50918317f 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
@@ -30,11 +30,20 @@ struct FuncTraits<ReturnType_ (*)(Args...)> {
30 using ArgType = std::tuple_element_t<I, std::tuple<Args...>>; 30 using ArgType = std::tuple_element_t<I, std::tuple<Args...>>;
31}; 31};
32 32
33#ifdef _MSC_VER
34#pragma warning(push)
35#pragma warning(disable : 4702) // Ignore unreachable code warning
36#endif
37
33template <auto func, typename... Args> 38template <auto func, typename... Args>
34void SetDefinition(EmitContext& ctx, IR::Inst* inst, Args... args) { 39void SetDefinition(EmitContext& ctx, IR::Inst* inst, Args... args) {
35 inst->SetDefinition<Id>(func(ctx, std::forward<Args>(args)...)); 40 inst->SetDefinition<Id>(func(ctx, std::forward<Args>(args)...));
36} 41}
37 42
43#ifdef _MSC_VER
44#pragma warning(pop)
45#endif
46
38template <typename ArgType> 47template <typename ArgType>
39ArgType Arg(EmitContext& ctx, const IR::Value& arg) { 48ArgType Arg(EmitContext& ctx, const IR::Value& arg) {
40 if constexpr (std::is_same_v<ArgType, Id>) { 49 if constexpr (std::is_same_v<ArgType, Id>) {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
index ad84966b5..8ea730c80 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
@@ -44,14 +44,6 @@ Id AttrPointer(EmitContext& ctx, Id pointer_type, Id vertex, Id base, Args&&...
44 } 44 }
45} 45}
46 46
47bool IsLegacyAttribute(IR::Attribute attribute) {
48 return (attribute >= IR::Attribute::ColorFrontDiffuseR &&
49 attribute <= IR::Attribute::ColorBackSpecularA) ||
50 attribute == IR::Attribute::FogCoordinate ||
51 (attribute >= IR::Attribute::FixedFncTexture0S &&
52 attribute <= IR::Attribute::FixedFncTexture9Q);
53}
54
55template <typename... Args> 47template <typename... Args>
56Id OutputAccessChain(EmitContext& ctx, Id result_type, Id base, Args&&... args) { 48Id OutputAccessChain(EmitContext& ctx, Id result_type, Id base, Args&&... args) {
57 if (ctx.stage == Stage::TessellationControl) { 49 if (ctx.stage == Stage::TessellationControl) {
@@ -83,17 +75,6 @@ std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
83 return OutputAccessChain(ctx, ctx.output_f32, info.id, index_id); 75 return OutputAccessChain(ctx, ctx.output_f32, info.id, index_id);
84 } 76 }
85 } 77 }
86 if (IsLegacyAttribute(attr)) {
87 if (attr == IR::Attribute::FogCoordinate) {
88 return OutputAccessChain(ctx, ctx.output_f32, ctx.OutputLegacyAttribute(attr),
89 ctx.Const(0u));
90 } else {
91 const u32 element{static_cast<u32>(attr) % 4};
92 const Id element_id{ctx.Const(element)};
93 return OutputAccessChain(ctx, ctx.output_f32, ctx.OutputLegacyAttribute(attr),
94 element_id);
95 }
96 }
97 switch (attr) { 78 switch (attr) {
98 case IR::Attribute::PointSize: 79 case IR::Attribute::PointSize:
99 return ctx.output_point_size; 80 return ctx.output_point_size;
@@ -327,18 +308,6 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
327 const Id value{ctx.OpLoad(type->id, pointer)}; 308 const Id value{ctx.OpLoad(type->id, pointer)};
328 return type->needs_cast ? ctx.OpBitcast(ctx.F32[1], value) : value; 309 return type->needs_cast ? ctx.OpBitcast(ctx.F32[1], value) : value;
329 } 310 }
330 if (IsLegacyAttribute(attr)) {
331 if (attr == IR::Attribute::FogCoordinate) {
332 const Id attr_ptr{AttrPointer(ctx, ctx.input_f32, vertex,
333 ctx.InputLegacyAttribute(attr), ctx.Const(0u))};
334 return ctx.OpLoad(ctx.F32[1], attr_ptr);
335 } else {
336 const Id element_id{ctx.Const(element)};
337 const Id attr_ptr{AttrPointer(ctx, ctx.input_f32, vertex,
338 ctx.InputLegacyAttribute(attr), element_id)};
339 return ctx.OpLoad(ctx.F32[1], attr_ptr);
340 }
341 }
342 switch (attr) { 311 switch (attr) {
343 case IR::Attribute::PrimitiveId: 312 case IR::Attribute::PrimitiveId:
344 return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.primitive_id)); 313 return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.primitive_id));
@@ -386,6 +355,31 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
386 } 355 }
387} 356}
388 357
358Id EmitGetAttributeU32(EmitContext& ctx, IR::Attribute attr, Id) {
359 switch (attr) {
360 case IR::Attribute::PrimitiveId:
361 return ctx.OpLoad(ctx.U32[1], ctx.primitive_id);
362 case IR::Attribute::InstanceId:
363 if (ctx.profile.support_vertex_instance_id) {
364 return ctx.OpLoad(ctx.U32[1], ctx.instance_id);
365 } else {
366 const Id index{ctx.OpLoad(ctx.U32[1], ctx.instance_index)};
367 const Id base{ctx.OpLoad(ctx.U32[1], ctx.base_instance)};
368 return ctx.OpISub(ctx.U32[1], index, base);
369 }
370 case IR::Attribute::VertexId:
371 if (ctx.profile.support_vertex_instance_id) {
372 return ctx.OpLoad(ctx.U32[1], ctx.vertex_id);
373 } else {
374 const Id index{ctx.OpLoad(ctx.U32[1], ctx.vertex_index)};
375 const Id base{ctx.OpLoad(ctx.U32[1], ctx.base_vertex)};
376 return ctx.OpISub(ctx.U32[1], index, base);
377 }
378 default:
379 throw NotImplementedException("Read U32 attribute {}", attr);
380 }
381}
382
389void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, [[maybe_unused]] Id vertex) { 383void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, [[maybe_unused]] Id vertex) {
390 const std::optional<OutAttr> output{OutputAttrPointer(ctx, attr)}; 384 const std::optional<OutAttr> output{OutputAttrPointer(ctx, attr)};
391 if (!output) { 385 if (!output) {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
index 6cd22dd3e..887112deb 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
@@ -53,6 +53,7 @@ Id EmitGetCbufU32(EmitContext& ctx, const IR::Value& binding, const IR::Value& o
53Id EmitGetCbufF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); 53Id EmitGetCbufF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
54Id EmitGetCbufU32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); 54Id EmitGetCbufU32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
55Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex); 55Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex);
56Id EmitGetAttributeU32(EmitContext& ctx, IR::Attribute attr, Id vertex);
56void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, Id vertex); 57void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, Id vertex);
57Id EmitGetAttributeIndexed(EmitContext& ctx, Id offset, Id vertex); 58Id EmitGetAttributeIndexed(EmitContext& ctx, Id offset, Id vertex);
58void EmitSetAttributeIndexed(EmitContext& ctx, Id offset, Id value, Id vertex); 59void EmitSetAttributeIndexed(EmitContext& ctx, Id offset, Id value, Id vertex);
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
index 4b6f792bf..d3ba66569 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
@@ -18,8 +18,6 @@
18 18
19namespace Shader::Backend::SPIRV { 19namespace Shader::Backend::SPIRV {
20namespace { 20namespace {
21constexpr size_t NUM_FIXEDFNCTEXTURE = 10;
22
23enum class Operation { 21enum class Operation {
24 Increment, 22 Increment,
25 Decrement, 23 Decrement,
@@ -432,34 +430,6 @@ Id DescType(EmitContext& ctx, Id sampled_type, Id pointer_type, u32 count) {
432 return pointer_type; 430 return pointer_type;
433 } 431 }
434} 432}
435
436size_t FindAndSetNextUnusedLocation(std::bitset<IR::NUM_GENERICS>& used_locations,
437 size_t& start_offset) {
438 for (size_t location = start_offset; location < used_locations.size(); ++location) {
439 if (!used_locations.test(location)) {
440 start_offset = location;
441 used_locations.set(location);
442 return location;
443 }
444 }
445 throw RuntimeError("Unable to get an unused location for legacy attribute");
446}
447
448Id DefineLegacyInput(EmitContext& ctx, std::bitset<IR::NUM_GENERICS>& used_locations,
449 size_t& start_offset) {
450 const Id id{DefineInput(ctx, ctx.F32[4], true)};
451 const size_t location = FindAndSetNextUnusedLocation(used_locations, start_offset);
452 ctx.Decorate(id, spv::Decoration::Location, location);
453 return id;
454}
455
456Id DefineLegacyOutput(EmitContext& ctx, std::bitset<IR::NUM_GENERICS>& used_locations,
457 size_t& start_offset, std::optional<u32> invocations) {
458 const Id id{DefineOutput(ctx, ctx.F32[4], invocations)};
459 const size_t location = FindAndSetNextUnusedLocation(used_locations, start_offset);
460 ctx.Decorate(id, spv::Decoration::Location, location);
461 return id;
462}
463} // Anonymous namespace 433} // Anonymous namespace
464 434
465void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name) { 435void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name) {
@@ -543,64 +513,6 @@ Id EmitContext::BitOffset16(const IR::Value& offset) {
543 return OpBitwiseAnd(U32[1], OpShiftLeftLogical(U32[1], Def(offset), Const(3u)), Const(16u)); 513 return OpBitwiseAnd(U32[1], OpShiftLeftLogical(U32[1], Def(offset), Const(3u)), Const(16u));
544} 514}
545 515
546Id EmitContext::InputLegacyAttribute(IR::Attribute attribute) {
547 if (attribute >= IR::Attribute::ColorFrontDiffuseR &&
548 attribute <= IR::Attribute::ColorFrontDiffuseA) {
549 return input_front_color;
550 }
551 if (attribute >= IR::Attribute::ColorFrontSpecularR &&
552 attribute <= IR::Attribute::ColorFrontSpecularA) {
553 return input_front_secondary_color;
554 }
555 if (attribute >= IR::Attribute::ColorBackDiffuseR &&
556 attribute <= IR::Attribute::ColorBackDiffuseA) {
557 return input_back_color;
558 }
559 if (attribute >= IR::Attribute::ColorBackSpecularR &&
560 attribute <= IR::Attribute::ColorBackSpecularA) {
561 return input_back_secondary_color;
562 }
563 if (attribute == IR::Attribute::FogCoordinate) {
564 return input_fog_frag_coord;
565 }
566 if (attribute >= IR::Attribute::FixedFncTexture0S &&
567 attribute <= IR::Attribute::FixedFncTexture9Q) {
568 u32 index =
569 (static_cast<u32>(attribute) - static_cast<u32>(IR::Attribute::FixedFncTexture0S)) / 4;
570 return input_fixed_fnc_textures[index];
571 }
572 throw InvalidArgument("Attribute is not legacy attribute {}", attribute);
573}
574
575Id EmitContext::OutputLegacyAttribute(IR::Attribute attribute) {
576 if (attribute >= IR::Attribute::ColorFrontDiffuseR &&
577 attribute <= IR::Attribute::ColorFrontDiffuseA) {
578 return output_front_color;
579 }
580 if (attribute >= IR::Attribute::ColorFrontSpecularR &&
581 attribute <= IR::Attribute::ColorFrontSpecularA) {
582 return output_front_secondary_color;
583 }
584 if (attribute >= IR::Attribute::ColorBackDiffuseR &&
585 attribute <= IR::Attribute::ColorBackDiffuseA) {
586 return output_back_color;
587 }
588 if (attribute >= IR::Attribute::ColorBackSpecularR &&
589 attribute <= IR::Attribute::ColorBackSpecularA) {
590 return output_back_secondary_color;
591 }
592 if (attribute == IR::Attribute::FogCoordinate) {
593 return output_fog_frag_coord;
594 }
595 if (attribute >= IR::Attribute::FixedFncTexture0S &&
596 attribute <= IR::Attribute::FixedFncTexture9Q) {
597 u32 index =
598 (static_cast<u32>(attribute) - static_cast<u32>(IR::Attribute::FixedFncTexture0S)) / 4;
599 return output_fixed_fnc_textures[index];
600 }
601 throw InvalidArgument("Attribute is not legacy attribute {}", attribute);
602}
603
604void EmitContext::DefineCommonTypes(const Info& info) { 516void EmitContext::DefineCommonTypes(const Info& info) {
605 void_id = TypeVoid(); 517 void_id = TypeVoid();
606 518
@@ -1389,7 +1301,6 @@ void EmitContext::DefineInputs(const IR::Program& program) {
1389 loads[IR::Attribute::TessellationEvaluationPointV]) { 1301 loads[IR::Attribute::TessellationEvaluationPointV]) {
1390 tess_coord = DefineInput(*this, F32[3], false, spv::BuiltIn::TessCoord); 1302 tess_coord = DefineInput(*this, F32[3], false, spv::BuiltIn::TessCoord);
1391 } 1303 }
1392 std::bitset<IR::NUM_GENERICS> used_locations{};
1393 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) { 1304 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
1394 const AttributeType input_type{runtime_info.generic_input_types[index]}; 1305 const AttributeType input_type{runtime_info.generic_input_types[index]};
1395 if (!runtime_info.previous_stage_stores.Generic(index)) { 1306 if (!runtime_info.previous_stage_stores.Generic(index)) {
@@ -1401,7 +1312,6 @@ void EmitContext::DefineInputs(const IR::Program& program) {
1401 if (input_type == AttributeType::Disabled) { 1312 if (input_type == AttributeType::Disabled) {
1402 continue; 1313 continue;
1403 } 1314 }
1404 used_locations.set(index);
1405 const Id type{GetAttributeType(*this, input_type)}; 1315 const Id type{GetAttributeType(*this, input_type)};
1406 const Id id{DefineInput(*this, type, true)}; 1316 const Id id{DefineInput(*this, type, true)};
1407 Decorate(id, spv::Decoration::Location, static_cast<u32>(index)); 1317 Decorate(id, spv::Decoration::Location, static_cast<u32>(index));
@@ -1427,30 +1337,6 @@ void EmitContext::DefineInputs(const IR::Program& program) {
1427 break; 1337 break;
1428 } 1338 }
1429 } 1339 }
1430 size_t previous_unused_location = 0;
1431 if (loads.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) {
1432 input_front_color = DefineLegacyInput(*this, used_locations, previous_unused_location);
1433 }
1434 if (loads.AnyComponent(IR::Attribute::ColorFrontSpecularR)) {
1435 input_front_secondary_color =
1436 DefineLegacyInput(*this, used_locations, previous_unused_location);
1437 }
1438 if (loads.AnyComponent(IR::Attribute::ColorBackDiffuseR)) {
1439 input_back_color = DefineLegacyInput(*this, used_locations, previous_unused_location);
1440 }
1441 if (loads.AnyComponent(IR::Attribute::ColorBackSpecularR)) {
1442 input_back_secondary_color =
1443 DefineLegacyInput(*this, used_locations, previous_unused_location);
1444 }
1445 if (loads.AnyComponent(IR::Attribute::FogCoordinate)) {
1446 input_fog_frag_coord = DefineLegacyInput(*this, used_locations, previous_unused_location);
1447 }
1448 for (size_t index = 0; index < NUM_FIXEDFNCTEXTURE; ++index) {
1449 if (loads.AnyComponent(IR::Attribute::FixedFncTexture0S + index * 4)) {
1450 input_fixed_fnc_textures[index] =
1451 DefineLegacyInput(*this, used_locations, previous_unused_location);
1452 }
1453 }
1454 if (stage == Stage::TessellationEval) { 1340 if (stage == Stage::TessellationEval) {
1455 for (size_t index = 0; index < info.uses_patches.size(); ++index) { 1341 for (size_t index = 0; index < info.uses_patches.size(); ++index) {
1456 if (!info.uses_patches[index]) { 1342 if (!info.uses_patches[index]) {
@@ -1501,38 +1387,9 @@ void EmitContext::DefineOutputs(const IR::Program& program) {
1501 viewport_mask = DefineOutput(*this, TypeArray(U32[1], Const(1u)), std::nullopt, 1387 viewport_mask = DefineOutput(*this, TypeArray(U32[1], Const(1u)), std::nullopt,
1502 spv::BuiltIn::ViewportMaskNV); 1388 spv::BuiltIn::ViewportMaskNV);
1503 } 1389 }
1504 std::bitset<IR::NUM_GENERICS> used_locations{};
1505 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) { 1390 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
1506 if (info.stores.Generic(index)) { 1391 if (info.stores.Generic(index)) {
1507 DefineGenericOutput(*this, index, invocations); 1392 DefineGenericOutput(*this, index, invocations);
1508 used_locations.set(index);
1509 }
1510 }
1511 size_t previous_unused_location = 0;
1512 if (info.stores.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) {
1513 output_front_color =
1514 DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations);
1515 }
1516 if (info.stores.AnyComponent(IR::Attribute::ColorFrontSpecularR)) {
1517 output_front_secondary_color =
1518 DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations);
1519 }
1520 if (info.stores.AnyComponent(IR::Attribute::ColorBackDiffuseR)) {
1521 output_back_color =
1522 DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations);
1523 }
1524 if (info.stores.AnyComponent(IR::Attribute::ColorBackSpecularR)) {
1525 output_back_secondary_color =
1526 DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations);
1527 }
1528 if (info.stores.AnyComponent(IR::Attribute::FogCoordinate)) {
1529 output_fog_frag_coord =
1530 DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations);
1531 }
1532 for (size_t index = 0; index < NUM_FIXEDFNCTEXTURE; ++index) {
1533 if (info.stores.AnyComponent(IR::Attribute::FixedFncTexture0S + index * 4)) {
1534 output_fixed_fnc_textures[index] =
1535 DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations);
1536 } 1393 }
1537 } 1394 }
1538 switch (stage) { 1395 switch (stage) {
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
index 63f8185d9..f87138f7e 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
@@ -113,9 +113,6 @@ public:
113 [[nodiscard]] Id BitOffset8(const IR::Value& offset); 113 [[nodiscard]] Id BitOffset8(const IR::Value& offset);
114 [[nodiscard]] Id BitOffset16(const IR::Value& offset); 114 [[nodiscard]] Id BitOffset16(const IR::Value& offset);
115 115
116 Id InputLegacyAttribute(IR::Attribute attribute);
117 Id OutputLegacyAttribute(IR::Attribute attribute);
118
119 Id Const(u32 value) { 116 Id Const(u32 value) {
120 return Constant(U32[1], value); 117 return Constant(U32[1], value);
121 } 118 }
@@ -281,22 +278,10 @@ public:
281 Id write_global_func_u32x4{}; 278 Id write_global_func_u32x4{};
282 279
283 Id input_position{}; 280 Id input_position{};
284 Id input_front_color{};
285 Id input_front_secondary_color{};
286 Id input_back_color{};
287 Id input_back_secondary_color{};
288 Id input_fog_frag_coord{};
289 std::array<Id, 10> input_fixed_fnc_textures{};
290 std::array<Id, 32> input_generics{}; 281 std::array<Id, 32> input_generics{};
291 282
292 Id output_point_size{}; 283 Id output_point_size{};
293 Id output_position{}; 284 Id output_position{};
294 Id output_front_color{};
295 Id output_front_secondary_color{};
296 Id output_back_color{};
297 Id output_back_secondary_color{};
298 Id output_fog_frag_coord{};
299 std::array<Id, 10> output_fixed_fnc_textures{};
300 std::array<std::array<GenericElementInfo, 4>, 32> output_generics{}; 285 std::array<std::array<GenericElementInfo, 4>, 32> output_generics{};
301 286
302 Id output_tess_level_outer{}; 287 Id output_tess_level_outer{};
diff --git a/src/shader_recompiler/environment.h b/src/shader_recompiler/environment.h
index b4df73e8a..db16429d4 100644
--- a/src/shader_recompiler/environment.h
+++ b/src/shader_recompiler/environment.h
@@ -31,6 +31,8 @@ public:
31 31
32 [[nodiscard]] virtual std::array<u32, 3> WorkgroupSize() const = 0; 32 [[nodiscard]] virtual std::array<u32, 3> WorkgroupSize() const = 0;
33 33
34 virtual void Dump(u64 hash) = 0;
35
34 [[nodiscard]] const ProgramHeader& SPH() const noexcept { 36 [[nodiscard]] const ProgramHeader& SPH() const noexcept {
35 return sph; 37 return sph;
36 } 38 }
diff --git a/src/shader_recompiler/frontend/ir/attribute.h b/src/shader_recompiler/frontend/ir/attribute.h
index ca1199494..3bbd38a03 100644
--- a/src/shader_recompiler/frontend/ir/attribute.h
+++ b/src/shader_recompiler/frontend/ir/attribute.h
@@ -224,6 +224,8 @@ enum class Attribute : u64 {
224 224
225constexpr size_t NUM_GENERICS = 32; 225constexpr size_t NUM_GENERICS = 32;
226 226
227constexpr size_t NUM_FIXEDFNCTEXTURE = 10;
228
227[[nodiscard]] bool IsGeneric(Attribute attribute) noexcept; 229[[nodiscard]] bool IsGeneric(Attribute attribute) noexcept;
228 230
229[[nodiscard]] u32 GenericAttributeIndex(Attribute attribute); 231[[nodiscard]] u32 GenericAttributeIndex(Attribute attribute);
diff --git a/src/shader_recompiler/frontend/ir/opcodes.inc b/src/shader_recompiler/frontend/ir/opcodes.inc
index 6929919df..b94ce7406 100644
--- a/src/shader_recompiler/frontend/ir/opcodes.inc
+++ b/src/shader_recompiler/frontend/ir/opcodes.inc
@@ -40,6 +40,7 @@ OPCODE(GetCbufU32, U32, U32,
40OPCODE(GetCbufF32, F32, U32, U32, ) 40OPCODE(GetCbufF32, F32, U32, U32, )
41OPCODE(GetCbufU32x2, U32x2, U32, U32, ) 41OPCODE(GetCbufU32x2, U32x2, U32, U32, )
42OPCODE(GetAttribute, F32, Attribute, U32, ) 42OPCODE(GetAttribute, F32, Attribute, U32, )
43OPCODE(GetAttributeU32, U32, Attribute, U32, )
43OPCODE(SetAttribute, Void, Attribute, F32, U32, ) 44OPCODE(SetAttribute, Void, Attribute, F32, U32, )
44OPCODE(GetAttributeIndexed, F32, U32, U32, ) 45OPCODE(GetAttributeIndexed, F32, U32, U32, )
45OPCODE(SetAttributeIndexed, Void, U32, F32, U32, ) 46OPCODE(SetAttributeIndexed, Void, U32, F32, U32, )
diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
index 267ebe4af..248ad3ced 100644
--- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
@@ -5,6 +5,7 @@
5#include <algorithm> 5#include <algorithm>
6#include <memory> 6#include <memory>
7#include <vector> 7#include <vector>
8#include <queue>
8 9
9#include "common/settings.h" 10#include "common/settings.h"
10#include "shader_recompiler/exception.h" 11#include "shader_recompiler/exception.h"
@@ -127,6 +128,42 @@ void AddNVNStorageBuffers(IR::Program& program) {
127 }); 128 });
128 } 129 }
129} 130}
131
132bool IsLegacyAttribute(IR::Attribute attribute) {
133 return (attribute >= IR::Attribute::ColorFrontDiffuseR &&
134 attribute <= IR::Attribute::ColorBackSpecularA) ||
135 attribute == IR::Attribute::FogCoordinate ||
136 (attribute >= IR::Attribute::FixedFncTexture0S &&
137 attribute <= IR::Attribute::FixedFncTexture9Q);
138}
139
140std::map<IR::Attribute, IR::Attribute> GenerateLegacyToGenericMappings(
141 const VaryingState& state, std::queue<IR::Attribute> ununsed_generics) {
142 std::map<IR::Attribute, IR::Attribute> mapping;
143 for (size_t index = 0; index < 4; ++index) {
144 auto attr = IR::Attribute::ColorFrontDiffuseR + index * 4;
145 if (state.AnyComponent(attr)) {
146 for (size_t i = 0; i < 4; ++i) {
147 mapping.insert({attr + i, ununsed_generics.front() + i});
148 }
149 ununsed_generics.pop();
150 }
151 }
152 if (state[IR::Attribute::FogCoordinate]) {
153 mapping.insert({IR::Attribute::FogCoordinate, ununsed_generics.front()});
154 ununsed_generics.pop();
155 }
156 for (size_t index = 0; index < IR::NUM_FIXEDFNCTEXTURE; ++index) {
157 auto attr = IR::Attribute::FixedFncTexture0S + index * 4;
158 if (state.AnyComponent(attr)) {
159 for (size_t i = 0; i < 4; ++i) {
160 mapping.insert({attr + i, ununsed_generics.front() + i});
161 }
162 ununsed_generics.pop();
163 }
164 }
165 return mapping;
166}
130} // Anonymous namespace 167} // Anonymous namespace
131 168
132IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool, 169IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
@@ -226,4 +263,62 @@ IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b
226 return result; 263 return result;
227} 264}
228 265
266void ConvertLegacyToGeneric(IR::Program& program, const Shader::RuntimeInfo& runtime_info) {
267 auto& stores = program.info.stores;
268 if (stores.Legacy()) {
269 std::queue<IR::Attribute> ununsed_output_generics{};
270 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
271 if (!stores.Generic(index)) {
272 ununsed_output_generics.push(IR::Attribute::Generic0X + index * 4);
273 }
274 }
275 auto mappings = GenerateLegacyToGenericMappings(stores, ununsed_output_generics);
276 for (IR::Block* const block : program.post_order_blocks) {
277 for (IR::Inst& inst : block->Instructions()) {
278 switch (inst.GetOpcode()) {
279 case IR::Opcode::SetAttribute: {
280 const auto attr = inst.Arg(0).Attribute();
281 if (IsLegacyAttribute(attr)) {
282 stores.Set(mappings[attr], true);
283 inst.SetArg(0, Shader::IR::Value(mappings[attr]));
284 }
285 break;
286 }
287 default:
288 break;
289 }
290 }
291 }
292 }
293
294 auto& loads = program.info.loads;
295 if (loads.Legacy()) {
296 std::queue<IR::Attribute> ununsed_input_generics{};
297 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
298 const AttributeType input_type{runtime_info.generic_input_types[index]};
299 if (!runtime_info.previous_stage_stores.Generic(index) || !loads.Generic(index) ||
300 input_type == AttributeType::Disabled) {
301 ununsed_input_generics.push(IR::Attribute::Generic0X + index * 4);
302 }
303 }
304 auto mappings = GenerateLegacyToGenericMappings(loads, ununsed_input_generics);
305 for (IR::Block* const block : program.post_order_blocks) {
306 for (IR::Inst& inst : block->Instructions()) {
307 switch (inst.GetOpcode()) {
308 case IR::Opcode::GetAttribute: {
309 const auto attr = inst.Arg(0).Attribute();
310 if (IsLegacyAttribute(attr)) {
311 loads.Set(mappings[attr], true);
312 inst.SetArg(0, Shader::IR::Value(mappings[attr]));
313 }
314 break;
315 }
316 default:
317 break;
318 }
319 }
320 }
321 }
322}
323
229} // namespace Shader::Maxwell 324} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.h b/src/shader_recompiler/frontend/maxwell/translate_program.h
index a84814811..cd535f20d 100644
--- a/src/shader_recompiler/frontend/maxwell/translate_program.h
+++ b/src/shader_recompiler/frontend/maxwell/translate_program.h
@@ -10,6 +10,7 @@
10#include "shader_recompiler/frontend/maxwell/control_flow.h" 10#include "shader_recompiler/frontend/maxwell/control_flow.h"
11#include "shader_recompiler/host_translate_info.h" 11#include "shader_recompiler/host_translate_info.h"
12#include "shader_recompiler/object_pool.h" 12#include "shader_recompiler/object_pool.h"
13#include "shader_recompiler/runtime_info.h"
13 14
14namespace Shader::Maxwell { 15namespace Shader::Maxwell {
15 16
@@ -20,4 +21,7 @@ namespace Shader::Maxwell {
20[[nodiscard]] IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b, 21[[nodiscard]] IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b,
21 Environment& env_vertex_b); 22 Environment& env_vertex_b);
22 23
24[[nodiscard]] void ConvertLegacyToGeneric(IR::Program& program,
25 const Shader::RuntimeInfo& runtime_info);
26
23} // namespace Shader::Maxwell 27} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
index 1e476d83d..a78c469be 100644
--- a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
+++ b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
@@ -389,6 +389,7 @@ void VisitUsages(Info& info, IR::Inst& inst) {
389 info.uses_demote_to_helper_invocation = true; 389 info.uses_demote_to_helper_invocation = true;
390 break; 390 break;
391 case IR::Opcode::GetAttribute: 391 case IR::Opcode::GetAttribute:
392 case IR::Opcode::GetAttributeU32:
392 info.loads.mask[static_cast<size_t>(inst.Arg(0).Attribute())] = true; 393 info.loads.mask[static_cast<size_t>(inst.Arg(0).Attribute())] = true;
393 break; 394 break;
394 case IR::Opcode::SetAttribute: 395 case IR::Opcode::SetAttribute:
diff --git a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
index d089fdd12..c134a12bc 100644
--- a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
+++ b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
@@ -505,6 +505,29 @@ void FoldBitCast(IR::Inst& inst, IR::Opcode reverse) {
505 return; 505 return;
506 } 506 }
507 } 507 }
508 if constexpr (op == IR::Opcode::BitCastU32F32) {
509 // Workaround for new NVIDIA driver bug, where:
510 // uint attr = ftou(itof(gl_InstanceID));
511 // always returned 0.
512 // We can instead manually optimize this and work around the driver bug:
513 // uint attr = uint(gl_InstanceID);
514 if (arg_inst->GetOpcode() == IR::Opcode::GetAttribute) {
515 const IR::Attribute attr{arg_inst->Arg(0).Attribute()};
516 switch (attr) {
517 case IR::Attribute::PrimitiveId:
518 case IR::Attribute::InstanceId:
519 case IR::Attribute::VertexId:
520 break;
521 default:
522 return;
523 }
524 // Replace the bitcasts with an integer attribute get
525 inst.ReplaceOpcode(IR::Opcode::GetAttributeU32);
526 inst.SetArg(0, arg_inst->Arg(0));
527 inst.SetArg(1, arg_inst->Arg(1));
528 return;
529 }
530 }
508} 531}
509 532
510void FoldInverseFunc(IR::Inst& inst, IR::Opcode reverse) { 533void FoldInverseFunc(IR::Inst& inst, IR::Opcode reverse) {
diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h
index f0c3b3b17..dc4c806ff 100644
--- a/src/shader_recompiler/profile.h
+++ b/src/shader_recompiler/profile.h
@@ -65,6 +65,10 @@ struct Profile {
65 bool has_gl_component_indexing_bug{}; 65 bool has_gl_component_indexing_bug{};
66 /// The precise type qualifier is broken in the fragment stage of some drivers 66 /// The precise type qualifier is broken in the fragment stage of some drivers
67 bool has_gl_precise_bug{}; 67 bool has_gl_precise_bug{};
68 /// Some drivers do not properly support floatBitsToUint when used on cbufs
69 bool has_gl_cbuf_ftou_bug{};
70 /// Some drivers poorly optimize boolean variable references
71 bool has_gl_bool_ref_bug{};
68 /// Ignores SPIR-V ordered vs unordered using GLSL semantics 72 /// Ignores SPIR-V ordered vs unordered using GLSL semantics
69 bool ignore_nan_fp_comparisons{}; 73 bool ignore_nan_fp_comparisons{};
70 74
diff --git a/src/shader_recompiler/varying_state.h b/src/shader_recompiler/varying_state.h
index 9d7b24a76..bc4f273c8 100644
--- a/src/shader_recompiler/varying_state.h
+++ b/src/shader_recompiler/varying_state.h
@@ -53,7 +53,8 @@ struct VaryingState {
53 return AnyComponent(IR::Attribute::ColorFrontDiffuseR) || 53 return AnyComponent(IR::Attribute::ColorFrontDiffuseR) ||
54 AnyComponent(IR::Attribute::ColorFrontSpecularR) || 54 AnyComponent(IR::Attribute::ColorFrontSpecularR) ||
55 AnyComponent(IR::Attribute::ColorBackDiffuseR) || 55 AnyComponent(IR::Attribute::ColorBackDiffuseR) ||
56 AnyComponent(IR::Attribute::ColorBackSpecularR) || FixedFunctionTexture(); 56 AnyComponent(IR::Attribute::ColorBackSpecularR) || FixedFunctionTexture() ||
57 mask[static_cast<size_t>(IR::Attribute::FogCoordinate)];
57 } 58 }
58 59
59 [[nodiscard]] bool FixedFunctionTexture() const noexcept { 60 [[nodiscard]] bool FixedFunctionTexture() const noexcept {
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index c4c012f3d..4a20c0768 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -10,11 +10,12 @@ add_executable(tests
10 core/network/network.cpp 10 core/network/network.cpp
11 tests.cpp 11 tests.cpp
12 video_core/buffer_base.cpp 12 video_core/buffer_base.cpp
13 input_common/calibration_configuration_job.cpp
13) 14)
14 15
15create_target_directory_groups(tests) 16create_target_directory_groups(tests)
16 17
17target_link_libraries(tests PRIVATE common core) 18target_link_libraries(tests PRIVATE common core input_common)
18target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} catch-single-include Threads::Threads) 19target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} catch-single-include Threads::Threads)
19 20
20add_test(NAME tests COMMAND tests) 21add_test(NAME tests COMMAND tests)
diff --git a/src/tests/input_common/calibration_configuration_job.cpp b/src/tests/input_common/calibration_configuration_job.cpp
new file mode 100644
index 000000000..8c77d81e9
--- /dev/null
+++ b/src/tests/input_common/calibration_configuration_job.cpp
@@ -0,0 +1,136 @@
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 <array>
6#include <string>
7#include <thread>
8#include <boost/asio.hpp>
9#include <boost/crc.hpp>
10#include <catch2/catch.hpp>
11
12#include "input_common/drivers/udp_client.h"
13#include "input_common/helpers/udp_protocol.h"
14
15class FakeCemuhookServer {
16public:
17 FakeCemuhookServer()
18 : socket(io_service, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 0)) {}
19
20 ~FakeCemuhookServer() {
21 is_running = false;
22 boost::system::error_code error_code;
23 socket.shutdown(boost::asio::socket_base::shutdown_both, error_code);
24 socket.close();
25 if (handler.joinable()) {
26 handler.join();
27 }
28 }
29
30 u16 GetPort() {
31 return socket.local_endpoint().port();
32 }
33
34 std::string GetHost() {
35 return socket.local_endpoint().address().to_string();
36 }
37
38 void Run(const std::vector<InputCommon::CemuhookUDP::Response::TouchPad> touch_movement_path) {
39 constexpr size_t HeaderSize = sizeof(InputCommon::CemuhookUDP::Header);
40 constexpr size_t PadDataSize =
41 sizeof(InputCommon::CemuhookUDP::Message<InputCommon::CemuhookUDP::Response::PadData>);
42
43 REQUIRE(touch_movement_path.size() > 0);
44 is_running = true;
45 handler = std::thread([touch_movement_path, this]() {
46 auto current_touch_position = touch_movement_path.begin();
47 while (is_running) {
48 boost::asio::ip::udp::endpoint sender_endpoint;
49 boost::system::error_code error_code;
50 auto received_size = socket.receive_from(boost::asio::buffer(receive_buffer),
51 sender_endpoint, 0, error_code);
52
53 if (received_size < HeaderSize) {
54 continue;
55 }
56
57 InputCommon::CemuhookUDP::Header header{};
58 std::memcpy(&header, receive_buffer.data(), HeaderSize);
59 switch (header.type) {
60 case InputCommon::CemuhookUDP::Type::PadData: {
61 InputCommon::CemuhookUDP::Response::PadData pad_data{};
62 pad_data.touch[0] = *current_touch_position;
63 const auto pad_message = InputCommon::CemuhookUDP::CreateMessage(
64 InputCommon::CemuhookUDP::SERVER_MAGIC, pad_data, 0);
65 std::memcpy(send_buffer.data(), &pad_message, PadDataSize);
66 socket.send_to(boost::asio::buffer(send_buffer, PadDataSize), sender_endpoint,
67 0, error_code);
68
69 bool can_advance =
70 std::next(current_touch_position) != touch_movement_path.end();
71 if (can_advance) {
72 std::advance(current_touch_position, 1);
73 }
74 break;
75 }
76 case InputCommon::CemuhookUDP::Type::PortInfo:
77 case InputCommon::CemuhookUDP::Type::Version:
78 default:
79 break;
80 }
81 }
82 });
83 }
84
85private:
86 boost::asio::io_service io_service;
87 boost::asio::ip::udp::socket socket;
88 std::array<u8, InputCommon::CemuhookUDP::MAX_PACKET_SIZE> send_buffer;
89 std::array<u8, InputCommon::CemuhookUDP::MAX_PACKET_SIZE> receive_buffer;
90 bool is_running = false;
91 std::thread handler;
92};
93
94TEST_CASE("CalibrationConfigurationJob completed", "[input_common]") {
95 Common::Event complete_event;
96 FakeCemuhookServer server;
97 server.Run({{
98 .is_active = 1,
99 .x = 0,
100 .y = 0,
101 },
102 {
103 .is_active = 1,
104 .x = 200,
105 .y = 200,
106 }});
107
108 InputCommon::CemuhookUDP::CalibrationConfigurationJob::Status status{};
109 u16 min_x{};
110 u16 min_y{};
111 u16 max_x{};
112 u16 max_y{};
113 InputCommon::CemuhookUDP::CalibrationConfigurationJob job(
114 server.GetHost(), server.GetPort(),
115 [&status,
116 &complete_event](InputCommon::CemuhookUDP::CalibrationConfigurationJob::Status status_) {
117 status = status_;
118 if (status ==
119 InputCommon::CemuhookUDP::CalibrationConfigurationJob::Status::Completed) {
120 complete_event.Set();
121 }
122 },
123 [&](u16 min_x_, u16 min_y_, u16 max_x_, u16 max_y_) {
124 min_x = min_x_;
125 min_y = min_y_;
126 max_x = max_x_;
127 max_y = max_y_;
128 });
129
130 complete_event.WaitUntil(std::chrono::system_clock::now() + std::chrono::seconds(10));
131 REQUIRE(status == InputCommon::CemuhookUDP::CalibrationConfigurationJob::Status::Completed);
132 REQUIRE(min_x == 0);
133 REQUIRE(min_y == 0);
134 REQUIRE(max_x == 200);
135 REQUIRE(max_y == 200);
136}
diff --git a/src/video_core/command_classes/codecs/codec.cpp b/src/video_core/command_classes/codecs/codec.cpp
index 2a532b883..04d0f3a2f 100644
--- a/src/video_core/command_classes/codecs/codec.cpp
+++ b/src/video_core/command_classes/codecs/codec.cpp
@@ -32,7 +32,7 @@ constexpr std::array PREFERRED_GPU_DECODERS = {
32#ifdef _WIN32 32#ifdef _WIN32
33 AV_HWDEVICE_TYPE_D3D11VA, 33 AV_HWDEVICE_TYPE_D3D11VA,
34 AV_HWDEVICE_TYPE_DXVA2, 34 AV_HWDEVICE_TYPE_DXVA2,
35#elif defined(__linux__) 35#elif defined(__unix__)
36 AV_HWDEVICE_TYPE_VAAPI, 36 AV_HWDEVICE_TYPE_VAAPI,
37 AV_HWDEVICE_TYPE_VDPAU, 37 AV_HWDEVICE_TYPE_VDPAU,
38#endif 38#endif
@@ -130,6 +130,12 @@ bool Codec::CreateGpuAvDevice() {
130 } 130 }
131 if (config->methods & HW_CONFIG_METHOD && config->device_type == type) { 131 if (config->methods & HW_CONFIG_METHOD && config->device_type == type) {
132 av_codec_ctx->pix_fmt = config->pix_fmt; 132 av_codec_ctx->pix_fmt = config->pix_fmt;
133 if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX) {
134 // skip zero-copy decoders, we don't currently support them
135 LOG_DEBUG(Service_NVDRV, "Skipping decoder {} with unsupported capability {}.",
136 av_hwdevice_get_type_name(type), config->methods);
137 continue;
138 }
133 LOG_INFO(Service_NVDRV, "Using {} GPU decoder", av_hwdevice_get_type_name(type)); 139 LOG_INFO(Service_NVDRV, "Using {} GPU decoder", av_hwdevice_get_type_name(type));
134 return true; 140 return true;
135 } 141 }
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 8788f5148..705765c99 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -17,7 +17,6 @@
17#include "core/frontend/emu_window.h" 17#include "core/frontend/emu_window.h"
18#include "core/hardware_interrupt_manager.h" 18#include "core/hardware_interrupt_manager.h"
19#include "core/hle/service/nvdrv/nvdata.h" 19#include "core/hle/service/nvdrv/nvdata.h"
20#include "core/hle/service/nvflinger/buffer_queue.h"
21#include "core/perf_stats.h" 20#include "core/perf_stats.h"
22#include "video_core/cdma_pusher.h" 21#include "video_core/cdma_pusher.h"
23#include "video_core/dma_pusher.h" 22#include "video_core/dma_pusher.h"
@@ -312,6 +311,12 @@ struct GPU::Impl {
312 cpu_context->MakeCurrent(); 311 cpu_context->MakeCurrent();
313 } 312 }
314 313
314 void NotifyShutdown() {
315 std::unique_lock lk{sync_mutex};
316 shutting_down.store(true, std::memory_order::relaxed);
317 sync_cv.notify_all();
318 }
319
315 /// Obtain the CPU Context 320 /// Obtain the CPU Context
316 void ObtainContext() { 321 void ObtainContext() {
317 cpu_context->MakeCurrent(); 322 cpu_context->MakeCurrent();
@@ -859,6 +864,10 @@ void GPU::Start() {
859 impl->Start(); 864 impl->Start();
860} 865}
861 866
867void GPU::NotifyShutdown() {
868 impl->NotifyShutdown();
869}
870
862void GPU::ObtainContext() { 871void GPU::ObtainContext() {
863 impl->ObtainContext(); 872 impl->ObtainContext();
864} 873}
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 500411176..3188b83ed 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -232,6 +232,9 @@ public:
232 /// core timing events. 232 /// core timing events.
233 void Start(); 233 void Start();
234 234
235 /// Performs any additional necessary steps to shutdown GPU emulation.
236 void NotifyShutdown();
237
235 /// Obtain the CPU Context 238 /// Obtain the CPU Context
236 void ObtainContext(); 239 void ObtainContext();
237 240
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp
index 0764ea6e0..e62912a22 100644
--- a/src/video_core/renderer_opengl/gl_device.cpp
+++ b/src/video_core/renderer_opengl/gl_device.cpp
@@ -182,17 +182,13 @@ Device::Device() {
182 shader_backend = Settings::ShaderBackend::GLSL; 182 shader_backend = Settings::ShaderBackend::GLSL;
183 } 183 }
184 184
185 if (shader_backend == Settings::ShaderBackend::GLSL && is_nvidia && 185 if (shader_backend == Settings::ShaderBackend::GLSL && is_nvidia) {
186 !Settings::values.renderer_debug) {
187 const std::string_view driver_version = version.substr(13); 186 const std::string_view driver_version = version.substr(13);
188 const int version_major = 187 const int version_major =
189 std::atoi(driver_version.substr(0, driver_version.find(".")).data()); 188 std::atoi(driver_version.substr(0, driver_version.find(".")).data());
190
191 if (version_major >= 495) { 189 if (version_major >= 495) {
192 LOG_WARNING(Render_OpenGL, "NVIDIA drivers 495 and later causes significant problems " 190 has_cbuf_ftou_bug = true;
193 "with yuzu. Forcing GLASM as a mitigation."); 191 has_bool_ref_bug = true;
194 shader_backend = Settings::ShaderBackend::GLASM;
195 use_assembly_shaders = true;
196 } 192 }
197 } 193 }
198 194
diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h
index de9e41659..95c2e8d38 100644
--- a/src/video_core/renderer_opengl/gl_device.h
+++ b/src/video_core/renderer_opengl/gl_device.h
@@ -152,6 +152,14 @@ public:
152 return need_fastmath_off; 152 return need_fastmath_off;
153 } 153 }
154 154
155 bool HasCbufFtouBug() const {
156 return has_cbuf_ftou_bug;
157 }
158
159 bool HasBoolRefBug() const {
160 return has_bool_ref_bug;
161 }
162
155 Settings::ShaderBackend GetShaderBackend() const { 163 Settings::ShaderBackend GetShaderBackend() const {
156 return shader_backend; 164 return shader_backend;
157 } 165 }
@@ -200,6 +208,8 @@ private:
200 bool has_sparse_texture_2{}; 208 bool has_sparse_texture_2{};
201 bool warp_size_potentially_larger_than_guest{}; 209 bool warp_size_potentially_larger_than_guest{};
202 bool need_fastmath_off{}; 210 bool need_fastmath_off{};
211 bool has_cbuf_ftou_bug{};
212 bool has_bool_ref_bug{};
203 213
204 std::string vendor_name; 214 std::string vendor_name;
205}; 215};
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 42ef67628..f71e01a34 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -42,6 +42,7 @@ namespace {
42using Shader::Backend::GLASM::EmitGLASM; 42using Shader::Backend::GLASM::EmitGLASM;
43using Shader::Backend::GLSL::EmitGLSL; 43using Shader::Backend::GLSL::EmitGLSL;
44using Shader::Backend::SPIRV::EmitSPIRV; 44using Shader::Backend::SPIRV::EmitSPIRV;
45using Shader::Maxwell::ConvertLegacyToGeneric;
45using Shader::Maxwell::MergeDualVertexPrograms; 46using Shader::Maxwell::MergeDualVertexPrograms;
46using Shader::Maxwell::TranslateProgram; 47using Shader::Maxwell::TranslateProgram;
47using VideoCommon::ComputeEnvironment; 48using VideoCommon::ComputeEnvironment;
@@ -213,6 +214,8 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo
213 .has_broken_fp16_float_controls = false, 214 .has_broken_fp16_float_controls = false,
214 .has_gl_component_indexing_bug = device.HasComponentIndexingBug(), 215 .has_gl_component_indexing_bug = device.HasComponentIndexingBug(),
215 .has_gl_precise_bug = device.HasPreciseBug(), 216 .has_gl_precise_bug = device.HasPreciseBug(),
217 .has_gl_cbuf_ftou_bug = device.HasCbufFtouBug(),
218 .has_gl_bool_ref_bug = device.HasBoolRefBug(),
216 .ignore_nan_fp_comparisons = true, 219 .ignore_nan_fp_comparisons = true,
217 .gl_max_compute_smem_size = device.GetMaxComputeSharedMemorySize(), 220 .gl_max_compute_smem_size = device.GetMaxComputeSharedMemorySize(),
218 }, 221 },
@@ -422,6 +425,11 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
422 425
423 const u32 cfg_offset{static_cast<u32>(env.StartAddress() + sizeof(Shader::ProgramHeader))}; 426 const u32 cfg_offset{static_cast<u32>(env.StartAddress() + sizeof(Shader::ProgramHeader))};
424 Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0); 427 Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0);
428
429 if (Settings::values.dump_shaders) {
430 env.Dump(key.unique_hashes[index]);
431 }
432
425 if (!uses_vertex_a || index != 1) { 433 if (!uses_vertex_a || index != 1) {
426 // Normal path 434 // Normal path
427 programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info); 435 programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info);
@@ -462,12 +470,14 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
462 MakeRuntimeInfo(key, program, previous_program, glasm_use_storage_buffers, use_glasm)}; 470 MakeRuntimeInfo(key, program, previous_program, glasm_use_storage_buffers, use_glasm)};
463 switch (device.GetShaderBackend()) { 471 switch (device.GetShaderBackend()) {
464 case Settings::ShaderBackend::GLSL: 472 case Settings::ShaderBackend::GLSL:
473 ConvertLegacyToGeneric(program, runtime_info);
465 sources[stage_index] = EmitGLSL(profile, runtime_info, program, binding); 474 sources[stage_index] = EmitGLSL(profile, runtime_info, program, binding);
466 break; 475 break;
467 case Settings::ShaderBackend::GLASM: 476 case Settings::ShaderBackend::GLASM:
468 sources[stage_index] = EmitGLASM(profile, runtime_info, program, binding); 477 sources[stage_index] = EmitGLASM(profile, runtime_info, program, binding);
469 break; 478 break;
470 case Settings::ShaderBackend::SPIRV: 479 case Settings::ShaderBackend::SPIRV:
480 ConvertLegacyToGeneric(program, runtime_info);
471 sources_spirv[stage_index] = EmitSPIRV(profile, runtime_info, program, binding); 481 sources_spirv[stage_index] = EmitSPIRV(profile, runtime_info, program, binding);
472 break; 482 break;
473 } 483 }
@@ -506,8 +516,12 @@ std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
506 LOG_INFO(Render_OpenGL, "0x{:016x}", key.Hash()); 516 LOG_INFO(Render_OpenGL, "0x{:016x}", key.Hash());
507 517
508 Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()}; 518 Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
509 auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
510 519
520 if (Settings::values.dump_shaders) {
521 env.Dump(key.Hash());
522 }
523
524 auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
511 const u32 num_storage_buffers{Shader::NumDescriptors(program.info.storage_buffers_descriptors)}; 525 const u32 num_storage_buffers{Shader::NumDescriptors(program.info.storage_buffers_descriptors)};
512 Shader::RuntimeInfo info; 526 Shader::RuntimeInfo info;
513 info.glasm_use_storage_buffers = num_storage_buffers <= device.GetMaxGLASMStorageBufferBlocks(); 527 info.glasm_use_storage_buffers = num_storage_buffers <= device.GetMaxGLASMStorageBufferBlocks();
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 14e6522f2..3c1f79a27 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -1047,7 +1047,7 @@ bool Image::ScaleDown(bool ignore) {
1047} 1047}
1048 1048
1049ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewInfo& info, 1049ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewInfo& info,
1050 ImageId image_id_, Image& image) 1050 ImageId image_id_, Image& image, const SlotVector<Image>&)
1051 : VideoCommon::ImageViewBase{info, image.info, image_id_}, views{runtime.null_image_views} { 1051 : VideoCommon::ImageViewBase{info, image.info, image_id_}, views{runtime.null_image_views} {
1052 const Device& device = runtime.device; 1052 const Device& device = runtime.device;
1053 if (True(image.flags & ImageFlagBits::Converted)) { 1053 if (True(image.flags & ImageFlagBits::Converted)) {
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index 37d5e6a6b..7f425631f 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -36,6 +36,7 @@ using VideoCommon::ImageViewType;
36using VideoCommon::NUM_RT; 36using VideoCommon::NUM_RT;
37using VideoCommon::Region2D; 37using VideoCommon::Region2D;
38using VideoCommon::RenderTargets; 38using VideoCommon::RenderTargets;
39using VideoCommon::SlotVector;
39 40
40struct ImageBufferMap { 41struct ImageBufferMap {
41 ~ImageBufferMap(); 42 ~ImageBufferMap();
@@ -92,7 +93,7 @@ public:
92 93
93 void ReinterpretImage(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies); 94 void ReinterpretImage(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies);
94 95
95 void ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view, bool rescaled) { 96 void ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view) {
96 UNIMPLEMENTED(); 97 UNIMPLEMENTED();
97 } 98 }
98 99
@@ -234,7 +235,8 @@ class ImageView : public VideoCommon::ImageViewBase {
234 friend Image; 235 friend Image;
235 236
236public: 237public:
237 explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageViewInfo&, ImageId, Image&); 238 explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageViewInfo&, ImageId, Image&,
239 const SlotVector<Image>&);
238 explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo&, 240 explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo&,
239 const VideoCommon::ImageViewInfo&, GPUVAddr); 241 const VideoCommon::ImageViewInfo&, GPUVAddr);
240 explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo& info, 242 explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo& info,
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 28daacd82..f81c1b233 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -437,39 +437,29 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
437 437
438 glBindTextureUnit(0, fxaa_texture.handle); 438 glBindTextureUnit(0, fxaa_texture.handle);
439 } 439 }
440
441 // Set projection matrix
442 const std::array ortho_matrix = 440 const std::array ortho_matrix =
443 MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height)); 441 MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height));
444 442
445 GLuint fragment_handle; 443 const auto fragment_handle = [this]() {
446 const auto filter = Settings::values.scaling_filter.GetValue(); 444 switch (Settings::values.scaling_filter.GetValue()) {
447 switch (filter) { 445 case Settings::ScalingFilter::NearestNeighbor:
448 case Settings::ScalingFilter::NearestNeighbor: 446 case Settings::ScalingFilter::Bilinear:
449 fragment_handle = present_bilinear_fragment.handle; 447 return present_bilinear_fragment.handle;
450 break; 448 case Settings::ScalingFilter::Bicubic:
451 case Settings::ScalingFilter::Bilinear: 449 return present_bicubic_fragment.handle;
452 fragment_handle = present_bilinear_fragment.handle; 450 case Settings::ScalingFilter::Gaussian:
453 break; 451 return present_gaussian_fragment.handle;
454 case Settings::ScalingFilter::Bicubic: 452 case Settings::ScalingFilter::ScaleForce:
455 fragment_handle = present_bicubic_fragment.handle; 453 return present_scaleforce_fragment.handle;
456 break; 454 case Settings::ScalingFilter::Fsr:
457 case Settings::ScalingFilter::Gaussian: 455 LOG_WARNING(
458 fragment_handle = present_gaussian_fragment.handle; 456 Render_OpenGL,
459 break; 457 "FidelityFX Super Resolution is not supported in OpenGL, changing to ScaleForce");
460 case Settings::ScalingFilter::ScaleForce: 458 return present_scaleforce_fragment.handle;
461 fragment_handle = present_scaleforce_fragment.handle; 459 default:
462 break; 460 return present_bilinear_fragment.handle;
463 case Settings::ScalingFilter::Fsr: 461 }
464 LOG_WARNING( 462 }();
465 Render_OpenGL,
466 "FidelityFX FSR Super Sampling is not supported in OpenGL, changing to ScaleForce");
467 fragment_handle = present_scaleforce_fragment.handle;
468 break;
469 default:
470 fragment_handle = present_bilinear_fragment.handle;
471 break;
472 }
473 program_manager.BindPresentPrograms(present_vertex.handle, fragment_handle); 463 program_manager.BindPresentPrograms(present_vertex.handle, fragment_handle);
474 glProgramUniformMatrix3x2fv(present_vertex.handle, ModelViewMatrixLocation, 1, GL_FALSE, 464 glProgramUniformMatrix3x2fv(present_vertex.handle, ModelViewMatrixLocation, 1, GL_FALSE,
475 ortho_matrix.data()); 465 ortho_matrix.data());
diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp
index 9a38b6b34..2c3914459 100644
--- a/src/video_core/renderer_vulkan/blit_image.cpp
+++ b/src/video_core/renderer_vulkan/blit_image.cpp
@@ -4,6 +4,7 @@
4 4
5#include <algorithm> 5#include <algorithm>
6 6
7#include "common/settings.h"
7#include "video_core/host_shaders/convert_abgr8_to_d24s8_frag_spv.h" 8#include "video_core/host_shaders/convert_abgr8_to_d24s8_frag_spv.h"
8#include "video_core/host_shaders/convert_d24s8_to_abgr8_frag_spv.h" 9#include "video_core/host_shaders/convert_d24s8_to_abgr8_frag_spv.h"
9#include "video_core/host_shaders/convert_depth_to_float_frag_spv.h" 10#include "video_core/host_shaders/convert_depth_to_float_frag_spv.h"
@@ -335,6 +336,17 @@ void BindBlitState(vk::CommandBuffer cmdbuf, VkPipelineLayout layout, const Regi
335 cmdbuf.SetScissor(0, scissor); 336 cmdbuf.SetScissor(0, scissor);
336 cmdbuf.PushConstants(layout, VK_SHADER_STAGE_VERTEX_BIT, push_constants); 337 cmdbuf.PushConstants(layout, VK_SHADER_STAGE_VERTEX_BIT, push_constants);
337} 338}
339
340VkExtent2D GetConversionExtent(const ImageView& src_image_view) {
341 const auto& resolution = Settings::values.resolution_info;
342 const bool is_rescaled = src_image_view.IsRescaled();
343 u32 width = src_image_view.size.width;
344 u32 height = src_image_view.size.height;
345 return VkExtent2D{
346 .width = is_rescaled ? resolution.ScaleUp(width) : width,
347 .height = is_rescaled ? resolution.ScaleUp(height) : height,
348 };
349}
338} // Anonymous namespace 350} // Anonymous namespace
339 351
340BlitImageHelper::BlitImageHelper(const Device& device_, VKScheduler& scheduler_, 352BlitImageHelper::BlitImageHelper(const Device& device_, VKScheduler& scheduler_,
@@ -425,108 +437,52 @@ void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer,
425} 437}
426 438
427void BlitImageHelper::ConvertD32ToR32(const Framebuffer* dst_framebuffer, 439void BlitImageHelper::ConvertD32ToR32(const Framebuffer* dst_framebuffer,
428 const ImageView& src_image_view, u32 up_scale, 440 const ImageView& src_image_view) {
429 u32 down_shift) {
430 ConvertDepthToColorPipeline(convert_d32_to_r32_pipeline, dst_framebuffer->RenderPass()); 441 ConvertDepthToColorPipeline(convert_d32_to_r32_pipeline, dst_framebuffer->RenderPass());
431 Convert(*convert_d32_to_r32_pipeline, dst_framebuffer, src_image_view, up_scale, down_shift); 442 Convert(*convert_d32_to_r32_pipeline, dst_framebuffer, src_image_view);
432} 443}
433 444
434void BlitImageHelper::ConvertR32ToD32(const Framebuffer* dst_framebuffer, 445void BlitImageHelper::ConvertR32ToD32(const Framebuffer* dst_framebuffer,
435 const ImageView& src_image_view, u32 up_scale, 446 const ImageView& src_image_view) {
436 u32 down_shift) {
437 ConvertColorToDepthPipeline(convert_r32_to_d32_pipeline, dst_framebuffer->RenderPass()); 447 ConvertColorToDepthPipeline(convert_r32_to_d32_pipeline, dst_framebuffer->RenderPass());
438 Convert(*convert_r32_to_d32_pipeline, dst_framebuffer, src_image_view, up_scale, down_shift); 448 Convert(*convert_r32_to_d32_pipeline, dst_framebuffer, src_image_view);
439} 449}
440 450
441void BlitImageHelper::ConvertD16ToR16(const Framebuffer* dst_framebuffer, 451void BlitImageHelper::ConvertD16ToR16(const Framebuffer* dst_framebuffer,
442 const ImageView& src_image_view, u32 up_scale, 452 const ImageView& src_image_view) {
443 u32 down_shift) {
444 ConvertDepthToColorPipeline(convert_d16_to_r16_pipeline, dst_framebuffer->RenderPass()); 453 ConvertDepthToColorPipeline(convert_d16_to_r16_pipeline, dst_framebuffer->RenderPass());
445 Convert(*convert_d16_to_r16_pipeline, dst_framebuffer, src_image_view, up_scale, down_shift); 454 Convert(*convert_d16_to_r16_pipeline, dst_framebuffer, src_image_view);
446} 455}
447 456
448void BlitImageHelper::ConvertR16ToD16(const Framebuffer* dst_framebuffer, 457void BlitImageHelper::ConvertR16ToD16(const Framebuffer* dst_framebuffer,
449 const ImageView& src_image_view, u32 up_scale, 458 const ImageView& src_image_view) {
450 u32 down_shift) {
451 ConvertColorToDepthPipeline(convert_r16_to_d16_pipeline, dst_framebuffer->RenderPass()); 459 ConvertColorToDepthPipeline(convert_r16_to_d16_pipeline, dst_framebuffer->RenderPass());
452 Convert(*convert_r16_to_d16_pipeline, dst_framebuffer, src_image_view, up_scale, down_shift); 460 Convert(*convert_r16_to_d16_pipeline, dst_framebuffer, src_image_view);
453} 461}
454 462
455void BlitImageHelper::ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer, 463void BlitImageHelper::ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer,
456 ImageView& src_image_view, u32 up_scale, u32 down_shift) { 464 const ImageView& src_image_view) {
457 ConvertPipelineDepthTargetEx(convert_abgr8_to_d24s8_pipeline, dst_framebuffer->RenderPass(), 465 ConvertPipelineDepthTargetEx(convert_abgr8_to_d24s8_pipeline, dst_framebuffer->RenderPass(),
458 convert_abgr8_to_d24s8_frag, true); 466 convert_abgr8_to_d24s8_frag);
459 ConvertColor(*convert_abgr8_to_d24s8_pipeline, dst_framebuffer, src_image_view, up_scale, 467 Convert(*convert_abgr8_to_d24s8_pipeline, dst_framebuffer, src_image_view);
460 down_shift);
461} 468}
462 469
463void BlitImageHelper::ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer, 470void BlitImageHelper::ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer,
464 ImageView& src_image_view, u32 up_scale, u32 down_shift) { 471 ImageView& src_image_view) {
465 ConvertPipelineColorTargetEx(convert_d24s8_to_abgr8_pipeline, dst_framebuffer->RenderPass(), 472 ConvertPipelineColorTargetEx(convert_d24s8_to_abgr8_pipeline, dst_framebuffer->RenderPass(),
466 convert_d24s8_to_abgr8_frag, false); 473 convert_d24s8_to_abgr8_frag);
467 ConvertDepthStencil(*convert_d24s8_to_abgr8_pipeline, dst_framebuffer, src_image_view, up_scale, 474 ConvertDepthStencil(*convert_d24s8_to_abgr8_pipeline, dst_framebuffer, src_image_view);
468 down_shift);
469} 475}
470 476
471void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer, 477void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
472 const ImageView& src_image_view, u32 up_scale, u32 down_shift) { 478 const ImageView& src_image_view) {
473 const VkPipelineLayout layout = *one_texture_pipeline_layout; 479 const VkPipelineLayout layout = *one_texture_pipeline_layout;
474 const VkImageView src_view = src_image_view.Handle(Shader::TextureType::Color2D); 480 const VkImageView src_view = src_image_view.Handle(Shader::TextureType::Color2D);
475 const VkSampler sampler = *nearest_sampler; 481 const VkSampler sampler = *nearest_sampler;
476 const VkExtent2D extent{ 482 const VkExtent2D extent = GetConversionExtent(src_image_view);
477 .width = std::max((src_image_view.size.width * up_scale) >> down_shift, 1U),
478 .height = std::max((src_image_view.size.height * up_scale) >> down_shift, 1U),
479 };
480 scheduler.RequestRenderpass(dst_framebuffer);
481 scheduler.Record([pipeline, layout, sampler, src_view, extent, up_scale, down_shift,
482 this](vk::CommandBuffer cmdbuf) {
483 const VkOffset2D offset{
484 .x = 0,
485 .y = 0,
486 };
487 const VkViewport viewport{
488 .x = 0.0f,
489 .y = 0.0f,
490 .width = static_cast<float>(extent.width),
491 .height = static_cast<float>(extent.height),
492 .minDepth = 0.0f,
493 .maxDepth = 0.0f,
494 };
495 const VkRect2D scissor{
496 .offset = offset,
497 .extent = extent,
498 };
499 const PushConstants push_constants{
500 .tex_scale = {viewport.width, viewport.height},
501 .tex_offset = {0.0f, 0.0f},
502 };
503 const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit();
504 UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view);
505
506 // TODO: Barriers
507 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
508 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
509 nullptr);
510 cmdbuf.SetViewport(0, viewport);
511 cmdbuf.SetScissor(0, scissor);
512 cmdbuf.PushConstants(layout, VK_SHADER_STAGE_VERTEX_BIT, push_constants);
513 cmdbuf.Draw(3, 1, 0, 0);
514 });
515 scheduler.InvalidateState();
516}
517 483
518void BlitImageHelper::ConvertColor(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
519 ImageView& src_image_view, u32 up_scale, u32 down_shift) {
520 const VkPipelineLayout layout = *one_texture_pipeline_layout;
521 const VkImageView src_view = src_image_view.ColorView();
522 const VkSampler sampler = *nearest_sampler;
523 const VkExtent2D extent{
524 .width = std::max((src_image_view.size.width * up_scale) >> down_shift, 1U),
525 .height = std::max((src_image_view.size.height * up_scale) >> down_shift, 1U),
526 };
527 scheduler.RequestRenderpass(dst_framebuffer); 484 scheduler.RequestRenderpass(dst_framebuffer);
528 scheduler.Record([pipeline, layout, sampler, src_view, extent, up_scale, down_shift, 485 scheduler.Record([pipeline, layout, sampler, src_view, extent, this](vk::CommandBuffer cmdbuf) {
529 this](vk::CommandBuffer cmdbuf) {
530 const VkOffset2D offset{ 486 const VkOffset2D offset{
531 .x = 0, 487 .x = 0,
532 .y = 0, 488 .y = 0,
@@ -563,18 +519,16 @@ void BlitImageHelper::ConvertColor(VkPipeline pipeline, const Framebuffer* dst_f
563} 519}
564 520
565void BlitImageHelper::ConvertDepthStencil(VkPipeline pipeline, const Framebuffer* dst_framebuffer, 521void BlitImageHelper::ConvertDepthStencil(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
566 ImageView& src_image_view, u32 up_scale, u32 down_shift) { 522 ImageView& src_image_view) {
567 const VkPipelineLayout layout = *two_textures_pipeline_layout; 523 const VkPipelineLayout layout = *two_textures_pipeline_layout;
568 const VkImageView src_depth_view = src_image_view.DepthView(); 524 const VkImageView src_depth_view = src_image_view.DepthView();
569 const VkImageView src_stencil_view = src_image_view.StencilView(); 525 const VkImageView src_stencil_view = src_image_view.StencilView();
570 const VkSampler sampler = *nearest_sampler; 526 const VkSampler sampler = *nearest_sampler;
571 const VkExtent2D extent{ 527 const VkExtent2D extent = GetConversionExtent(src_image_view);
572 .width = std::max((src_image_view.size.width * up_scale) >> down_shift, 1U), 528
573 .height = std::max((src_image_view.size.height * up_scale) >> down_shift, 1U),
574 };
575 scheduler.RequestRenderpass(dst_framebuffer); 529 scheduler.RequestRenderpass(dst_framebuffer);
576 scheduler.Record([pipeline, layout, sampler, src_depth_view, src_stencil_view, extent, up_scale, 530 scheduler.Record([pipeline, layout, sampler, src_depth_view, src_stencil_view, extent,
577 down_shift, this](vk::CommandBuffer cmdbuf) { 531 this](vk::CommandBuffer cmdbuf) {
578 const VkOffset2D offset{ 532 const VkOffset2D offset{
579 .x = 0, 533 .x = 0,
580 .y = 0, 534 .y = 0,
@@ -695,11 +649,14 @@ VkPipeline BlitImageHelper::FindOrEmplaceDepthStencilPipeline(const BlitImagePip
695 return *blit_depth_stencil_pipelines.back(); 649 return *blit_depth_stencil_pipelines.back();
696} 650}
697 651
698void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass) { 652void BlitImageHelper::ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass,
653 bool is_target_depth) {
699 if (pipeline) { 654 if (pipeline) {
700 return; 655 return;
701 } 656 }
702 const std::array stages = MakeStages(*full_screen_vert, *convert_depth_to_float_frag); 657 VkShaderModule frag_shader =
658 is_target_depth ? *convert_float_to_depth_frag : *convert_depth_to_float_frag;
659 const std::array stages = MakeStages(*full_screen_vert, frag_shader);
703 pipeline = device.GetLogical().CreateGraphicsPipeline({ 660 pipeline = device.GetLogical().CreateGraphicsPipeline({
704 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, 661 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
705 .pNext = nullptr, 662 .pNext = nullptr,
@@ -712,8 +669,9 @@ void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRend
712 .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO, 669 .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
713 .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO, 670 .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
714 .pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, 671 .pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
715 .pDepthStencilState = nullptr, 672 .pDepthStencilState = is_target_depth ? &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO : nullptr,
716 .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO, 673 .pColorBlendState = is_target_depth ? &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO
674 : &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO,
717 .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO, 675 .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
718 .layout = *one_texture_pipeline_layout, 676 .layout = *one_texture_pipeline_layout,
719 .renderPass = renderpass, 677 .renderPass = renderpass,
@@ -723,37 +681,17 @@ void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRend
723 }); 681 });
724} 682}
725 683
684void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass) {
685 ConvertPipeline(pipeline, renderpass, false);
686}
687
726void BlitImageHelper::ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass) { 688void BlitImageHelper::ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass) {
727 if (pipeline) { 689 ConvertPipeline(pipeline, renderpass, true);
728 return;
729 }
730 const std::array stages = MakeStages(*full_screen_vert, *convert_float_to_depth_frag);
731 pipeline = device.GetLogical().CreateGraphicsPipeline({
732 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
733 .pNext = nullptr,
734 .flags = 0,
735 .stageCount = static_cast<u32>(stages.size()),
736 .pStages = stages.data(),
737 .pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
738 .pInputAssemblyState = &PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
739 .pTessellationState = nullptr,
740 .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
741 .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
742 .pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
743 .pDepthStencilState = &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
744 .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO,
745 .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
746 .layout = *one_texture_pipeline_layout,
747 .renderPass = renderpass,
748 .subpass = 0,
749 .basePipelineHandle = VK_NULL_HANDLE,
750 .basePipelineIndex = 0,
751 });
752} 690}
753 691
754void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass renderpass, 692void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
755 vk::ShaderModule& module, bool is_target_depth, 693 vk::ShaderModule& module, bool single_texture,
756 bool single_texture) { 694 bool is_target_depth) {
757 if (pipeline) { 695 if (pipeline) {
758 return; 696 return;
759 } 697 }
@@ -782,13 +720,13 @@ void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass ren
782} 720}
783 721
784void BlitImageHelper::ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass, 722void BlitImageHelper::ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
785 vk::ShaderModule& module, bool single_texture) { 723 vk::ShaderModule& module) {
786 ConvertPipelineEx(pipeline, renderpass, module, false, single_texture); 724 ConvertPipelineEx(pipeline, renderpass, module, false, false);
787} 725}
788 726
789void BlitImageHelper::ConvertPipelineDepthTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass, 727void BlitImageHelper::ConvertPipelineDepthTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
790 vk::ShaderModule& module, bool single_texture) { 728 vk::ShaderModule& module) {
791 ConvertPipelineEx(pipeline, renderpass, module, true, single_texture); 729 ConvertPipelineEx(pipeline, renderpass, module, true, true);
792} 730}
793 731
794} // namespace Vulkan 732} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/blit_image.h b/src/video_core/renderer_vulkan/blit_image.h
index b1a717090..85e7dca5b 100644
--- a/src/video_core/renderer_vulkan/blit_image.h
+++ b/src/video_core/renderer_vulkan/blit_image.h
@@ -44,50 +44,43 @@ public:
44 const Region2D& src_region, Tegra::Engines::Fermi2D::Filter filter, 44 const Region2D& src_region, Tegra::Engines::Fermi2D::Filter filter,
45 Tegra::Engines::Fermi2D::Operation operation); 45 Tegra::Engines::Fermi2D::Operation operation);
46 46
47 void ConvertD32ToR32(const Framebuffer* dst_framebuffer, const ImageView& src_image_view, 47 void ConvertD32ToR32(const Framebuffer* dst_framebuffer, const ImageView& src_image_view);
48 u32 up_scale, u32 down_shift);
49 48
50 void ConvertR32ToD32(const Framebuffer* dst_framebuffer, const ImageView& src_image_view, 49 void ConvertR32ToD32(const Framebuffer* dst_framebuffer, const ImageView& src_image_view);
51 u32 up_scale, u32 down_shift);
52 50
53 void ConvertD16ToR16(const Framebuffer* dst_framebuffer, const ImageView& src_image_view, 51 void ConvertD16ToR16(const Framebuffer* dst_framebuffer, const ImageView& src_image_view);
54 u32 up_scale, u32 down_shift);
55 52
56 void ConvertR16ToD16(const Framebuffer* dst_framebuffer, const ImageView& src_image_view, 53 void ConvertR16ToD16(const Framebuffer* dst_framebuffer, const ImageView& src_image_view);
57 u32 up_scale, u32 down_shift);
58 54
59 void ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer, ImageView& src_image_view, 55 void ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer, const ImageView& src_image_view);
60 u32 up_scale, u32 down_shift);
61 56
62 void ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view, 57 void ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view);
63 u32 up_scale, u32 down_shift);
64 58
65private: 59private:
66 void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer, 60 void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
67 const ImageView& src_image_view, u32 up_scale, u32 down_shift); 61 const ImageView& src_image_view);
68
69 void ConvertColor(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
70 ImageView& src_image_view, u32 up_scale, u32 down_shift);
71 62
72 void ConvertDepthStencil(VkPipeline pipeline, const Framebuffer* dst_framebuffer, 63 void ConvertDepthStencil(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
73 ImageView& src_image_view, u32 up_scale, u32 down_shift); 64 ImageView& src_image_view);
74 65
75 [[nodiscard]] VkPipeline FindOrEmplaceColorPipeline(const BlitImagePipelineKey& key); 66 [[nodiscard]] VkPipeline FindOrEmplaceColorPipeline(const BlitImagePipelineKey& key);
76 67
77 [[nodiscard]] VkPipeline FindOrEmplaceDepthStencilPipeline(const BlitImagePipelineKey& key); 68 [[nodiscard]] VkPipeline FindOrEmplaceDepthStencilPipeline(const BlitImagePipelineKey& key);
78 69
70 void ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass, bool is_target_depth);
71
79 void ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass); 72 void ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass);
80 73
81 void ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass); 74 void ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass);
82 75
83 void ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass renderpass, 76 void ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
84 vk::ShaderModule& module, bool is_target_depth, bool single_texture); 77 vk::ShaderModule& module, bool single_texture, bool is_target_depth);
85 78
86 void ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass, 79 void ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
87 vk::ShaderModule& module, bool single_texture); 80 vk::ShaderModule& module);
88 81
89 void ConvertPipelineDepthTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass, 82 void ConvertPipelineDepthTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
90 vk::ShaderModule& module, bool single_texture); 83 vk::ShaderModule& module);
91 84
92 const Device& device; 85 const Device& device;
93 VKScheduler& scheduler; 86 VKScheduler& scheduler;
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index 1e447e621..c71a1f44d 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -391,28 +391,23 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
391 .offset = {0, 0}, 391 .offset = {0, 0},
392 .extent = size, 392 .extent = size,
393 }; 393 };
394 const auto filter = Settings::values.scaling_filter.GetValue();
395 cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE); 394 cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
396 switch (filter) { 395 auto graphics_pipeline = [this]() {
397 case Settings::ScalingFilter::NearestNeighbor: 396 switch (Settings::values.scaling_filter.GetValue()) {
398 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *bilinear_pipeline); 397 case Settings::ScalingFilter::NearestNeighbor:
399 break; 398 case Settings::ScalingFilter::Bilinear:
400 case Settings::ScalingFilter::Bilinear: 399 return *bilinear_pipeline;
401 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *bilinear_pipeline); 400 case Settings::ScalingFilter::Bicubic:
402 break; 401 return *bicubic_pipeline;
403 case Settings::ScalingFilter::Bicubic: 402 case Settings::ScalingFilter::Gaussian:
404 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *bicubic_pipeline); 403 return *gaussian_pipeline;
405 break; 404 case Settings::ScalingFilter::ScaleForce:
406 case Settings::ScalingFilter::Gaussian: 405 return *scaleforce_pipeline;
407 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *gaussian_pipeline); 406 default:
408 break; 407 return *bilinear_pipeline;
409 case Settings::ScalingFilter::ScaleForce: 408 }
410 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *scaleforce_pipeline); 409 }();
411 break; 410 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline);
412 default:
413 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *bilinear_pipeline);
414 break;
415 }
416 cmdbuf.SetViewport(0, viewport); 411 cmdbuf.SetViewport(0, viewport);
417 cmdbuf.SetScissor(0, scissor); 412 cmdbuf.SetScissor(0, scissor);
418 413
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 616a7b457..d514b71d0 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -605,7 +605,11 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
605 .flags = 0, 605 .flags = 0,
606 .topology = input_assembly_topology, 606 .topology = input_assembly_topology,
607 .primitiveRestartEnable = key.state.primitive_restart_enable != 0 && 607 .primitiveRestartEnable = key.state.primitive_restart_enable != 0 &&
608 SupportsPrimitiveRestart(input_assembly_topology), 608 ((input_assembly_topology != VK_PRIMITIVE_TOPOLOGY_PATCH_LIST &&
609 device.IsTopologyListPrimitiveRestartSupported()) ||
610 SupportsPrimitiveRestart(input_assembly_topology) ||
611 (input_assembly_topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST &&
612 device.IsPatchListPrimitiveRestartSupported())),
609 }; 613 };
610 const VkPipelineTessellationStateCreateInfo tessellation_ci{ 614 const VkPipelineTessellationStateCreateInfo tessellation_ci{
611 .sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO, 615 .sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO,
@@ -613,7 +617,6 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
613 .flags = 0, 617 .flags = 0,
614 .patchControlPoints = key.state.patch_control_points_minus_one.Value() + 1, 618 .patchControlPoints = key.state.patch_control_points_minus_one.Value() + 1,
615 }; 619 };
616
617 std::array<VkViewportSwizzleNV, Maxwell::NumViewports> swizzles; 620 std::array<VkViewportSwizzleNV, Maxwell::NumViewports> swizzles;
618 std::ranges::transform(key.state.viewport_swizzles, swizzles.begin(), UnpackViewportSwizzle); 621 std::ranges::transform(key.state.viewport_swizzles, swizzles.begin(), UnpackViewportSwizzle);
619 const VkPipelineViewportSwizzleStateCreateInfoNV swizzle_ci{ 622 const VkPipelineViewportSwizzleStateCreateInfoNV swizzle_ci{
@@ -748,8 +751,8 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
748 .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, 751 .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
749 .pNext = nullptr, 752 .pNext = nullptr,
750 .flags = 0, 753 .flags = 0,
751 .logicOpEnable = VK_FALSE, 754 .logicOpEnable = key.state.logic_op_enable != 0,
752 .logicOp = VK_LOGIC_OP_COPY, 755 .logicOp = static_cast<VkLogicOp>(key.state.logic_op.Value()),
753 .attachmentCount = static_cast<u32>(cb_attachments.size()), 756 .attachmentCount = static_cast<u32>(cb_attachments.size()),
754 .pAttachments = cb_attachments.data(), 757 .pAttachments = cb_attachments.data(),
755 .blendConstants = {}, 758 .blendConstants = {},
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index eb8b4e08b..a633b73e5 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -48,6 +48,7 @@ MICROPROFILE_DECLARE(Vulkan_PipelineCache);
48 48
49namespace { 49namespace {
50using Shader::Backend::SPIRV::EmitSPIRV; 50using Shader::Backend::SPIRV::EmitSPIRV;
51using Shader::Maxwell::ConvertLegacyToGeneric;
51using Shader::Maxwell::MergeDualVertexPrograms; 52using Shader::Maxwell::MergeDualVertexPrograms;
52using Shader::Maxwell::TranslateProgram; 53using Shader::Maxwell::TranslateProgram;
53using VideoCommon::ComputeEnvironment; 54using VideoCommon::ComputeEnvironment;
@@ -516,6 +517,9 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
516 517
517 const u32 cfg_offset{static_cast<u32>(env.StartAddress() + sizeof(Shader::ProgramHeader))}; 518 const u32 cfg_offset{static_cast<u32>(env.StartAddress() + sizeof(Shader::ProgramHeader))};
518 Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0); 519 Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0);
520 if (Settings::values.dump_shaders) {
521 env.Dump(key.unique_hashes[index]);
522 }
519 if (!uses_vertex_a || index != 1) { 523 if (!uses_vertex_a || index != 1) {
520 // Normal path 524 // Normal path
521 programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info); 525 programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info);
@@ -543,6 +547,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
543 infos[stage_index] = &program.info; 547 infos[stage_index] = &program.info;
544 548
545 const auto runtime_info{MakeRuntimeInfo(programs, key, program, previous_stage)}; 549 const auto runtime_info{MakeRuntimeInfo(programs, key, program, previous_stage)};
550 ConvertLegacyToGeneric(program, runtime_info);
546 const std::vector<u32> code{EmitSPIRV(profile, runtime_info, program, binding)}; 551 const std::vector<u32> code{EmitSPIRV(profile, runtime_info, program, binding)};
547 device.SaveShader(code); 552 device.SaveShader(code);
548 modules[stage_index] = BuildShader(device, code); 553 modules[stage_index] = BuildShader(device, code);
@@ -611,6 +616,12 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
611 LOG_INFO(Render_Vulkan, "0x{:016x}", key.Hash()); 616 LOG_INFO(Render_Vulkan, "0x{:016x}", key.Hash());
612 617
613 Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()}; 618 Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
619
620 // Dump it before error.
621 if (Settings::values.dump_shaders) {
622 env.Dump(key.Hash());
623 }
624
614 auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)}; 625 auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
615 const std::vector<u32> code{EmitSPIRV(profile, program)}; 626 const std::vector<u32> code{EmitSPIRV(profile, program)};
616 device.SaveShader(code); 627 device.SaveShader(code);
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 197cba8e3..0ba56ff1e 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -1057,37 +1057,37 @@ void TextureCacheRuntime::BlitImage(Framebuffer* dst_framebuffer, ImageView& dst
1057 }); 1057 });
1058} 1058}
1059 1059
1060void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view, 1060void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view) {
1061 bool rescaled) {
1062 const u32 up_scale = rescaled ? resolution.up_scale : 1;
1063 const u32 down_shift = rescaled ? resolution.down_shift : 0;
1064 switch (dst_view.format) { 1061 switch (dst_view.format) {
1065 case PixelFormat::R16_UNORM: 1062 case PixelFormat::R16_UNORM:
1066 if (src_view.format == PixelFormat::D16_UNORM) { 1063 if (src_view.format == PixelFormat::D16_UNORM) {
1067 return blit_image_helper.ConvertD16ToR16(dst, src_view, up_scale, down_shift); 1064 return blit_image_helper.ConvertD16ToR16(dst, src_view);
1068 } 1065 }
1069 break; 1066 break;
1070 case PixelFormat::A8B8G8R8_UNORM: 1067 case PixelFormat::A8B8G8R8_UNORM:
1071 if (src_view.format == PixelFormat::S8_UINT_D24_UNORM) { 1068 if (src_view.format == PixelFormat::S8_UINT_D24_UNORM) {
1072 return blit_image_helper.ConvertD24S8ToABGR8(dst, src_view, up_scale, down_shift); 1069 return blit_image_helper.ConvertD24S8ToABGR8(dst, src_view);
1073 } 1070 }
1074 break; 1071 break;
1075 case PixelFormat::R32_FLOAT: 1072 case PixelFormat::R32_FLOAT:
1076 if (src_view.format == PixelFormat::D32_FLOAT) { 1073 if (src_view.format == PixelFormat::D32_FLOAT) {
1077 return blit_image_helper.ConvertD32ToR32(dst, src_view, up_scale, down_shift); 1074 return blit_image_helper.ConvertD32ToR32(dst, src_view);
1078 } 1075 }
1079 break; 1076 break;
1080 case PixelFormat::D16_UNORM: 1077 case PixelFormat::D16_UNORM:
1081 if (src_view.format == PixelFormat::R16_UNORM) { 1078 if (src_view.format == PixelFormat::R16_UNORM) {
1082 return blit_image_helper.ConvertR16ToD16(dst, src_view, up_scale, down_shift); 1079 return blit_image_helper.ConvertR16ToD16(dst, src_view);
1083 } 1080 }
1084 break; 1081 break;
1085 case PixelFormat::S8_UINT_D24_UNORM: 1082 case PixelFormat::S8_UINT_D24_UNORM:
1086 return blit_image_helper.ConvertABGR8ToD24S8(dst, src_view, up_scale, down_shift); 1083 if (src_view.format == PixelFormat::A8B8G8R8_UNORM ||
1084 src_view.format == PixelFormat::B8G8R8A8_UNORM) {
1085 return blit_image_helper.ConvertABGR8ToD24S8(dst, src_view);
1086 }
1087 break; 1087 break;
1088 case PixelFormat::D32_FLOAT: 1088 case PixelFormat::D32_FLOAT:
1089 if (src_view.format == PixelFormat::R32_FLOAT) { 1089 if (src_view.format == PixelFormat::R32_FLOAT) {
1090 return blit_image_helper.ConvertR32ToD32(dst, src_view, up_scale, down_shift); 1090 return blit_image_helper.ConvertR32ToD32(dst, src_view);
1091 } 1091 }
1092 break; 1092 break;
1093 default: 1093 default:
@@ -1329,6 +1329,10 @@ void Image::DownloadMemory(const StagingBufferRef& map, std::span<const BufferIm
1329 } 1329 }
1330} 1330}
1331 1331
1332bool Image::IsRescaled() const noexcept {
1333 return True(flags & ImageFlagBits::Rescaled);
1334}
1335
1332bool Image::ScaleUp(bool ignore) { 1336bool Image::ScaleUp(bool ignore) {
1333 if (True(flags & ImageFlagBits::Rescaled)) { 1337 if (True(flags & ImageFlagBits::Rescaled)) {
1334 return false; 1338 return false;
@@ -1340,7 +1344,6 @@ bool Image::ScaleUp(bool ignore) {
1340 return false; 1344 return false;
1341 } 1345 }
1342 has_scaled = true; 1346 has_scaled = true;
1343 const auto& device = runtime->device;
1344 if (!scaled_image) { 1347 if (!scaled_image) {
1345 const bool is_2d = info.type == ImageType::e2D; 1348 const bool is_2d = info.type == ImageType::e2D;
1346 const u32 scaled_width = resolution.ScaleUp(info.size.width); 1349 const u32 scaled_width = resolution.ScaleUp(info.size.width);
@@ -1348,7 +1351,7 @@ bool Image::ScaleUp(bool ignore) {
1348 auto scaled_info = info; 1351 auto scaled_info = info;
1349 scaled_info.size.width = scaled_width; 1352 scaled_info.size.width = scaled_width;
1350 scaled_info.size.height = scaled_height; 1353 scaled_info.size.height = scaled_height;
1351 scaled_image = MakeImage(device, scaled_info); 1354 scaled_image = MakeImage(runtime->device, scaled_info);
1352 auto& allocator = runtime->memory_allocator; 1355 auto& allocator = runtime->memory_allocator;
1353 scaled_commit = MemoryCommit(allocator.Commit(scaled_image, MemoryUsage::DeviceLocal)); 1356 scaled_commit = MemoryCommit(allocator.Commit(scaled_image, MemoryUsage::DeviceLocal));
1354 ignore = false; 1357 ignore = false;
@@ -1357,18 +1360,13 @@ bool Image::ScaleUp(bool ignore) {
1357 if (ignore) { 1360 if (ignore) {
1358 return true; 1361 return true;
1359 } 1362 }
1360
1361 if (aspect_mask == 0) { 1363 if (aspect_mask == 0) {
1362 aspect_mask = ImageAspectMask(info.format); 1364 aspect_mask = ImageAspectMask(info.format);
1363 } 1365 }
1364 static constexpr auto OPTIMAL_FORMAT = FormatType::Optimal; 1366 if (NeedsScaleHelper()) {
1365 const PixelFormat format = StorageFormat(info.format);
1366 const auto vk_format = MaxwellToVK::SurfaceFormat(device, OPTIMAL_FORMAT, false, format).format;
1367 const auto blit_usage = VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT;
1368 if (device.IsFormatSupported(vk_format, blit_usage, OPTIMAL_FORMAT)) {
1369 BlitScale(*scheduler, *original_image, *scaled_image, info, aspect_mask, resolution);
1370 } else {
1371 return BlitScaleHelper(true); 1367 return BlitScaleHelper(true);
1368 } else {
1369 BlitScale(*scheduler, *original_image, *scaled_image, info, aspect_mask, resolution);
1372 } 1370 }
1373 return true; 1371 return true;
1374} 1372}
@@ -1390,15 +1388,10 @@ bool Image::ScaleDown(bool ignore) {
1390 if (aspect_mask == 0) { 1388 if (aspect_mask == 0) {
1391 aspect_mask = ImageAspectMask(info.format); 1389 aspect_mask = ImageAspectMask(info.format);
1392 } 1390 }
1393 static constexpr auto OPTIMAL_FORMAT = FormatType::Optimal; 1391 if (NeedsScaleHelper()) {
1394 const PixelFormat format = StorageFormat(info.format);
1395 const auto& device = runtime->device;
1396 const auto vk_format = MaxwellToVK::SurfaceFormat(device, OPTIMAL_FORMAT, false, format).format;
1397 const auto blit_usage = VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT;
1398 if (device.IsFormatSupported(vk_format, blit_usage, OPTIMAL_FORMAT)) {
1399 BlitScale(*scheduler, *scaled_image, *original_image, info, aspect_mask, resolution, false);
1400 } else {
1401 return BlitScaleHelper(false); 1392 return BlitScaleHelper(false);
1393 } else {
1394 BlitScale(*scheduler, *scaled_image, *original_image, info, aspect_mask, resolution, false);
1402 } 1395 }
1403 return true; 1396 return true;
1404} 1397}
@@ -1466,10 +1459,24 @@ bool Image::BlitScaleHelper(bool scale_up) {
1466 return true; 1459 return true;
1467} 1460}
1468 1461
1462bool Image::NeedsScaleHelper() const {
1463 const auto& device = runtime->device;
1464 const bool needs_msaa_helper = info.num_samples > 1 && device.CantBlitMSAA();
1465 if (needs_msaa_helper) {
1466 return true;
1467 }
1468 static constexpr auto OPTIMAL_FORMAT = FormatType::Optimal;
1469 const PixelFormat format = StorageFormat(info.format);
1470 const auto vk_format = MaxwellToVK::SurfaceFormat(device, OPTIMAL_FORMAT, false, format).format;
1471 const auto blit_usage = VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT;
1472 const bool needs_blit_helper = !device.IsFormatSupported(vk_format, blit_usage, OPTIMAL_FORMAT);
1473 return needs_blit_helper;
1474}
1475
1469ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewInfo& info, 1476ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewInfo& info,
1470 ImageId image_id_, Image& image) 1477 ImageId image_id_, Image& image)
1471 : VideoCommon::ImageViewBase{info, image.info, image_id_}, device{&runtime.device}, 1478 : VideoCommon::ImageViewBase{info, image.info, image_id_}, device{&runtime.device},
1472 image_handle{image.Handle()}, samples{ConvertSampleCount(image.info.num_samples)} { 1479 image_handle{image.Handle()}, samples(ConvertSampleCount(image.info.num_samples)) {
1473 using Shader::TextureType; 1480 using Shader::TextureType;
1474 1481
1475 const VkImageAspectFlags aspect_mask = ImageViewAspectMask(info); 1482 const VkImageAspectFlags aspect_mask = ImageViewAspectMask(info);
@@ -1552,6 +1559,12 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
1552 } 1559 }
1553} 1560}
1554 1561
1562ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewInfo& info,
1563 ImageId image_id_, Image& image, const SlotVector<Image>& slot_imgs)
1564 : ImageView{runtime, info, image_id_, image} {
1565 slot_images = &slot_imgs;
1566}
1567
1555ImageView::ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo& info, 1568ImageView::ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo& info,
1556 const VideoCommon::ImageViewInfo& view_info, GPUVAddr gpu_addr_) 1569 const VideoCommon::ImageViewInfo& view_info, GPUVAddr gpu_addr_)
1557 : VideoCommon::ImageViewBase{info, view_info}, gpu_addr{gpu_addr_}, 1570 : VideoCommon::ImageViewBase{info, view_info}, gpu_addr{gpu_addr_},
@@ -1607,6 +1620,15 @@ VkImageView ImageView::StorageView(Shader::TextureType texture_type,
1607 return *view; 1620 return *view;
1608} 1621}
1609 1622
1623bool ImageView::IsRescaled() const noexcept {
1624 if (!slot_images) {
1625 return false;
1626 }
1627 const auto& slots = *slot_images;
1628 const auto& src_image = slots[image_id];
1629 return src_image.IsRescaled();
1630}
1631
1610vk::ImageView ImageView::MakeView(VkFormat vk_format, VkImageAspectFlags aspect_mask) { 1632vk::ImageView ImageView::MakeView(VkFormat vk_format, VkImageAspectFlags aspect_mask) {
1611 return device->GetLogical().CreateImageView({ 1633 return device->GetLogical().CreateImageView({
1612 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, 1634 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index 753e3e8a1..c81130dd2 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -23,6 +23,7 @@ using VideoCommon::ImageId;
23using VideoCommon::NUM_RT; 23using VideoCommon::NUM_RT;
24using VideoCommon::Region2D; 24using VideoCommon::Region2D;
25using VideoCommon::RenderTargets; 25using VideoCommon::RenderTargets;
26using VideoCommon::SlotVector;
26using VideoCore::Surface::PixelFormat; 27using VideoCore::Surface::PixelFormat;
27 28
28class ASTCDecoderPass; 29class ASTCDecoderPass;
@@ -65,7 +66,7 @@ public:
65 66
66 void ReinterpretImage(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies); 67 void ReinterpretImage(Image& dst, Image& src, std::span<const VideoCommon::ImageCopy> copies);
67 68
68 void ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view, bool rescaled); 69 void ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view);
69 70
70 bool CanAccelerateImageUpload(Image&) const noexcept { 71 bool CanAccelerateImageUpload(Image&) const noexcept {
71 return false; 72 return false;
@@ -139,6 +140,8 @@ public:
139 return std::exchange(initialized, true); 140 return std::exchange(initialized, true);
140 } 141 }
141 142
143 bool IsRescaled() const noexcept;
144
142 bool ScaleUp(bool ignore = false); 145 bool ScaleUp(bool ignore = false);
143 146
144 bool ScaleDown(bool ignore = false); 147 bool ScaleDown(bool ignore = false);
@@ -146,6 +149,8 @@ public:
146private: 149private:
147 bool BlitScaleHelper(bool scale_up); 150 bool BlitScaleHelper(bool scale_up);
148 151
152 bool NeedsScaleHelper() const;
153
149 VKScheduler* scheduler{}; 154 VKScheduler* scheduler{};
150 TextureCacheRuntime* runtime{}; 155 TextureCacheRuntime* runtime{};
151 156
@@ -168,6 +173,8 @@ private:
168class ImageView : public VideoCommon::ImageViewBase { 173class ImageView : public VideoCommon::ImageViewBase {
169public: 174public:
170 explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageViewInfo&, ImageId, Image&); 175 explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageViewInfo&, ImageId, Image&);
176 explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageViewInfo&, ImageId, Image&,
177 const SlotVector<Image>&);
171 explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo&, 178 explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo&,
172 const VideoCommon::ImageViewInfo&, GPUVAddr); 179 const VideoCommon::ImageViewInfo&, GPUVAddr);
173 explicit ImageView(TextureCacheRuntime&, const VideoCommon::NullImageViewParams&); 180 explicit ImageView(TextureCacheRuntime&, const VideoCommon::NullImageViewParams&);
@@ -189,6 +196,8 @@ public:
189 [[nodiscard]] VkImageView StorageView(Shader::TextureType texture_type, 196 [[nodiscard]] VkImageView StorageView(Shader::TextureType texture_type,
190 Shader::ImageFormat image_format); 197 Shader::ImageFormat image_format);
191 198
199 [[nodiscard]] bool IsRescaled() const noexcept;
200
192 [[nodiscard]] VkImageView Handle(Shader::TextureType texture_type) const noexcept { 201 [[nodiscard]] VkImageView Handle(Shader::TextureType texture_type) const noexcept {
193 return *image_views[static_cast<size_t>(texture_type)]; 202 return *image_views[static_cast<size_t>(texture_type)];
194 } 203 }
@@ -222,6 +231,8 @@ private:
222 [[nodiscard]] vk::ImageView MakeView(VkFormat vk_format, VkImageAspectFlags aspect_mask); 231 [[nodiscard]] vk::ImageView MakeView(VkFormat vk_format, VkImageAspectFlags aspect_mask);
223 232
224 const Device* device = nullptr; 233 const Device* device = nullptr;
234 const SlotVector<Image>* slot_images = nullptr;
235
225 std::array<vk::ImageView, Shader::NUM_TEXTURE_TYPES> image_views; 236 std::array<vk::ImageView, Shader::NUM_TEXTURE_TYPES> image_views;
226 std::unique_ptr<StorageViews> storage_views; 237 std::unique_ptr<StorageViews> storage_views;
227 vk::ImageView depth_view; 238 vk::ImageView depth_view;
diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp
index 05850afd0..7d3ae0de4 100644
--- a/src/video_core/shader_environment.cpp
+++ b/src/video_core/shader_environment.cpp
@@ -3,6 +3,7 @@
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 <bit>
6#include <filesystem> 7#include <filesystem>
7#include <fstream> 8#include <fstream>
8#include <memory> 9#include <memory>
@@ -14,6 +15,7 @@
14#include "common/common_types.h" 15#include "common/common_types.h"
15#include "common/div_ceil.h" 16#include "common/div_ceil.h"
16#include "common/fs/fs.h" 17#include "common/fs/fs.h"
18#include "common/fs/path_util.h"
17#include "common/logging/log.h" 19#include "common/logging/log.h"
18#include "shader_recompiler/environment.h" 20#include "shader_recompiler/environment.h"
19#include "video_core/engines/kepler_compute.h" 21#include "video_core/engines/kepler_compute.h"
@@ -57,6 +59,47 @@ static Shader::TextureType ConvertType(const Tegra::Texture::TICEntry& entry) {
57 } 59 }
58} 60}
59 61
62static std::string_view StageToPrefix(Shader::Stage stage) {
63 switch (stage) {
64 case Shader::Stage::VertexB:
65 return "VB";
66 case Shader::Stage::TessellationControl:
67 return "TC";
68 case Shader::Stage::TessellationEval:
69 return "TE";
70 case Shader::Stage::Geometry:
71 return "GS";
72 case Shader::Stage::Fragment:
73 return "FS";
74 case Shader::Stage::Compute:
75 return "CS";
76 case Shader::Stage::VertexA:
77 return "VA";
78 default:
79 return "UK";
80 }
81}
82
83static void DumpImpl(u64 hash, const u64* code, u32 read_highest, u32 read_lowest,
84 u32 initial_offset, Shader::Stage stage) {
85 const auto shader_dir{Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir)};
86 const auto base_dir{shader_dir / "shaders"};
87 if (!Common::FS::CreateDir(shader_dir) || !Common::FS::CreateDir(base_dir)) {
88 LOG_ERROR(Common_Filesystem, "Failed to create shader dump directories");
89 return;
90 }
91 const auto prefix = StageToPrefix(stage);
92 const auto name{base_dir / fmt::format("{}{:016x}.ash", prefix, hash)};
93 const size_t real_size = read_highest - read_lowest + initial_offset;
94 const size_t padding_needed = ((32 - (real_size % 32)) % 32);
95 std::fstream shader_file(name, std::ios::out | std::ios::binary);
96 const size_t jump_index = initial_offset / sizeof(u64);
97 shader_file.write(reinterpret_cast<const char*>(code + jump_index), real_size);
98 for (size_t i = 0; i < padding_needed; i++) {
99 shader_file.put(0);
100 }
101}
102
60GenericEnvironment::GenericEnvironment(Tegra::MemoryManager& gpu_memory_, GPUVAddr program_base_, 103GenericEnvironment::GenericEnvironment(Tegra::MemoryManager& gpu_memory_, GPUVAddr program_base_,
61 u32 start_address_) 104 u32 start_address_)
62 : gpu_memory{&gpu_memory_}, program_base{program_base_} { 105 : gpu_memory{&gpu_memory_}, program_base{program_base_} {
@@ -128,6 +171,10 @@ u64 GenericEnvironment::CalculateHash() const {
128 return Common::CityHash64(data.get(), size); 171 return Common::CityHash64(data.get(), size);
129} 172}
130 173
174void GenericEnvironment::Dump(u64 hash) {
175 DumpImpl(hash, code.data(), read_highest, read_lowest, initial_offset, stage);
176}
177
131void GenericEnvironment::Serialize(std::ofstream& file) const { 178void GenericEnvironment::Serialize(std::ofstream& file) const {
132 const u64 code_size{static_cast<u64>(CachedSize())}; 179 const u64 code_size{static_cast<u64>(CachedSize())};
133 const u64 num_texture_types{static_cast<u64>(texture_types.size())}; 180 const u64 num_texture_types{static_cast<u64>(texture_types.size())};
@@ -207,6 +254,7 @@ GraphicsEnvironment::GraphicsEnvironment(Tegra::Engines::Maxwell3D& maxwell3d_,
207 u32 start_address_) 254 u32 start_address_)
208 : GenericEnvironment{gpu_memory_, program_base_, start_address_}, maxwell3d{&maxwell3d_} { 255 : GenericEnvironment{gpu_memory_, program_base_, start_address_}, maxwell3d{&maxwell3d_} {
209 gpu_memory->ReadBlock(program_base + start_address, &sph, sizeof(sph)); 256 gpu_memory->ReadBlock(program_base + start_address, &sph, sizeof(sph));
257 initial_offset = sizeof(sph);
210 gp_passthrough_mask = maxwell3d->regs.gp_passthrough_mask; 258 gp_passthrough_mask = maxwell3d->regs.gp_passthrough_mask;
211 switch (program) { 259 switch (program) {
212 case Maxwell::ShaderProgram::VertexA: 260 case Maxwell::ShaderProgram::VertexA:
@@ -323,14 +371,20 @@ void FileEnvironment::Deserialize(std::ifstream& file) {
323 if (stage == Shader::Stage::Compute) { 371 if (stage == Shader::Stage::Compute) {
324 file.read(reinterpret_cast<char*>(&workgroup_size), sizeof(workgroup_size)) 372 file.read(reinterpret_cast<char*>(&workgroup_size), sizeof(workgroup_size))
325 .read(reinterpret_cast<char*>(&shared_memory_size), sizeof(shared_memory_size)); 373 .read(reinterpret_cast<char*>(&shared_memory_size), sizeof(shared_memory_size));
374 initial_offset = 0;
326 } else { 375 } else {
327 file.read(reinterpret_cast<char*>(&sph), sizeof(sph)); 376 file.read(reinterpret_cast<char*>(&sph), sizeof(sph));
377 initial_offset = sizeof(sph);
328 if (stage == Shader::Stage::Geometry) { 378 if (stage == Shader::Stage::Geometry) {
329 file.read(reinterpret_cast<char*>(&gp_passthrough_mask), sizeof(gp_passthrough_mask)); 379 file.read(reinterpret_cast<char*>(&gp_passthrough_mask), sizeof(gp_passthrough_mask));
330 } 380 }
331 } 381 }
332} 382}
333 383
384void FileEnvironment::Dump(u64 [[maybe_unused]] hash) {
385 DumpImpl(hash, code.get(), read_highest, read_lowest, initial_offset, stage);
386}
387
334u64 FileEnvironment::ReadInstruction(u32 address) { 388u64 FileEnvironment::ReadInstruction(u32 address) {
335 if (address < read_lowest || address > read_highest) { 389 if (address < read_lowest || address > read_highest) {
336 throw Shader::LogicError("Out of bounds address {}", address); 390 throw Shader::LogicError("Out of bounds address {}", address);
diff --git a/src/video_core/shader_environment.h b/src/video_core/shader_environment.h
index 6640e53d0..aae762b27 100644
--- a/src/video_core/shader_environment.h
+++ b/src/video_core/shader_environment.h
@@ -57,6 +57,8 @@ public:
57 57
58 [[nodiscard]] u64 CalculateHash() const; 58 [[nodiscard]] u64 CalculateHash() const;
59 59
60 void Dump(u64 hash) override;
61
60 void Serialize(std::ofstream& file) const; 62 void Serialize(std::ofstream& file) const;
61 63
62protected: 64protected:
@@ -82,6 +84,7 @@ protected:
82 84
83 u32 cached_lowest = std::numeric_limits<u32>::max(); 85 u32 cached_lowest = std::numeric_limits<u32>::max();
84 u32 cached_highest = 0; 86 u32 cached_highest = 0;
87 u32 initial_offset = 0;
85 88
86 bool has_unbound_instructions = false; 89 bool has_unbound_instructions = false;
87}; 90};
@@ -149,6 +152,8 @@ public:
149 152
150 [[nodiscard]] std::array<u32, 3> WorkgroupSize() const override; 153 [[nodiscard]] std::array<u32, 3> WorkgroupSize() const override;
151 154
155 void Dump(u64 hash) override;
156
152private: 157private:
153 std::unique_ptr<u64[]> code; 158 std::unique_ptr<u64[]> code;
154 std::unordered_map<u32, Shader::TextureType> texture_types; 159 std::unordered_map<u32, Shader::TextureType> texture_types;
@@ -159,6 +164,7 @@ private:
159 u32 texture_bound{}; 164 u32 texture_bound{};
160 u32 read_lowest{}; 165 u32 read_lowest{};
161 u32 read_highest{}; 166 u32 read_highest{};
167 u32 initial_offset{};
162}; 168};
163 169
164void SerializePipeline(std::span<const char> key, std::span<const GenericEnvironment* const> envs, 170void SerializePipeline(std::span<const char> key, std::span<const GenericEnvironment* const> envs,
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 5aaeb16ca..b494152b8 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -1397,7 +1397,8 @@ ImageViewId TextureCache<P>::FindOrEmplaceImageView(ImageId image_id, const Imag
1397 if (const ImageViewId image_view_id = image.FindView(info); image_view_id) { 1397 if (const ImageViewId image_view_id = image.FindView(info); image_view_id) {
1398 return image_view_id; 1398 return image_view_id;
1399 } 1399 }
1400 const ImageViewId image_view_id = slot_image_views.insert(runtime, info, image_id, image); 1400 const ImageViewId image_view_id =
1401 slot_image_views.insert(runtime, info, image_id, image, slot_images);
1401 image.InsertView(info, image_view_id); 1402 image.InsertView(info, image_view_id);
1402 return image_view_id; 1403 return image_view_id;
1403} 1404}
@@ -1855,9 +1856,20 @@ void TextureCache<P>::CopyImage(ImageId dst_id, ImageId src_id, std::vector<Imag
1855 .height = std::min(dst_view.size.height, src_view.size.height), 1856 .height = std::min(dst_view.size.height, src_view.size.height),
1856 .depth = std::min(dst_view.size.depth, src_view.size.depth), 1857 .depth = std::min(dst_view.size.depth, src_view.size.depth),
1857 }; 1858 };
1858 UNIMPLEMENTED_IF(copy.extent != expected_size); 1859 const Extent3D scaled_extent = [is_rescaled, expected_size]() {
1860 if (!is_rescaled) {
1861 return expected_size;
1862 }
1863 const auto& resolution = Settings::values.resolution_info;
1864 return Extent3D{
1865 .width = resolution.ScaleUp(expected_size.width),
1866 .height = resolution.ScaleUp(expected_size.height),
1867 .depth = expected_size.depth,
1868 };
1869 }();
1870 UNIMPLEMENTED_IF(copy.extent != scaled_extent);
1859 1871
1860 runtime.ConvertImage(dst_framebuffer, dst_view, src_view, is_rescaled); 1872 runtime.ConvertImage(dst_framebuffer, dst_view, src_view);
1861 } 1873 }
1862} 1874}
1863 1875
diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp
index 7bd31b211..d8e19cb2f 100644
--- a/src/video_core/texture_cache/util.cpp
+++ b/src/video_core/texture_cache/util.cpp
@@ -364,14 +364,14 @@ template <u32 GOB_EXTENT>
364 364
365[[nodiscard]] std::optional<SubresourceExtent> ResolveOverlapRightAddress2D( 365[[nodiscard]] std::optional<SubresourceExtent> ResolveOverlapRightAddress2D(
366 const ImageInfo& new_info, GPUVAddr gpu_addr, const ImageBase& overlap, bool strict_size) { 366 const ImageInfo& new_info, GPUVAddr gpu_addr, const ImageBase& overlap, bool strict_size) {
367 const u32 layer_stride = new_info.layer_stride; 367 const u64 layer_stride = new_info.layer_stride;
368 const s32 new_size = layer_stride * new_info.resources.layers; 368 const u64 new_size = layer_stride * new_info.resources.layers;
369 const s32 diff = static_cast<s32>(overlap.gpu_addr - gpu_addr); 369 const u64 diff = overlap.gpu_addr - gpu_addr;
370 if (diff > new_size) { 370 if (diff > new_size) {
371 return std::nullopt; 371 return std::nullopt;
372 } 372 }
373 const s32 base_layer = diff / layer_stride; 373 const s32 base_layer = static_cast<s32>(diff / layer_stride);
374 const s32 mip_offset = diff % layer_stride; 374 const s32 mip_offset = static_cast<s32>(diff % layer_stride);
375 const std::array offsets = CalculateMipLevelOffsets(new_info); 375 const std::array offsets = CalculateMipLevelOffsets(new_info);
376 const auto end = offsets.begin() + new_info.resources.levels; 376 const auto end = offsets.begin() + new_info.resources.levels;
377 const auto it = std::find(offsets.begin(), end, static_cast<u32>(mip_offset)); 377 const auto it = std::find(offsets.begin(), end, static_cast<u32>(mip_offset));
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 7bf5b6578..3d78efddc 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -271,7 +271,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
271 .tessellationShader = true, 271 .tessellationShader = true,
272 .sampleRateShading = true, 272 .sampleRateShading = true,
273 .dualSrcBlend = true, 273 .dualSrcBlend = true,
274 .logicOp = false, 274 .logicOp = true,
275 .multiDrawIndirect = false, 275 .multiDrawIndirect = false,
276 .drawIndirectFirstInstance = false, 276 .drawIndirectFirstInstance = false,
277 .depthClamp = true, 277 .depthClamp = true,
@@ -433,6 +433,19 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
433 LOG_INFO(Render_Vulkan, "Device doesn't support uint8 indexes"); 433 LOG_INFO(Render_Vulkan, "Device doesn't support uint8 indexes");
434 } 434 }
435 435
436 VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT primitive_topology_list_restart;
437 if (is_topology_list_restart_supported || is_patch_list_restart_supported) {
438 primitive_topology_list_restart = {
439 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT,
440 .pNext = nullptr,
441 .primitiveTopologyListRestart = is_topology_list_restart_supported,
442 .primitiveTopologyPatchListRestart = is_patch_list_restart_supported,
443 };
444 SetNext(next, primitive_topology_list_restart);
445 } else {
446 LOG_INFO(Render_Vulkan, "Device doesn't support list topology primitive restart");
447 }
448
436 VkPhysicalDeviceTransformFeedbackFeaturesEXT transform_feedback; 449 VkPhysicalDeviceTransformFeedbackFeaturesEXT transform_feedback;
437 if (ext_transform_feedback) { 450 if (ext_transform_feedback) {
438 transform_feedback = { 451 transform_feedback = {
@@ -625,15 +638,20 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
625 } 638 }
626 } 639 }
627 640
628 if (ext_vertex_input_dynamic_state && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) { 641 const bool is_intel_windows = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS;
642 if (ext_vertex_input_dynamic_state && is_intel_windows) {
629 LOG_WARNING(Render_Vulkan, "Blacklisting Intel for VK_EXT_vertex_input_dynamic_state"); 643 LOG_WARNING(Render_Vulkan, "Blacklisting Intel for VK_EXT_vertex_input_dynamic_state");
630 ext_vertex_input_dynamic_state = false; 644 ext_vertex_input_dynamic_state = false;
631 } 645 }
632 if (is_float16_supported && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) { 646 if (is_float16_supported && is_intel_windows) {
633 // Intel's compiler crashes when using fp16 on Astral Chain, disable it for the time being. 647 // Intel's compiler crashes when using fp16 on Astral Chain, disable it for the time being.
634 LOG_WARNING(Render_Vulkan, "Blacklisting Intel proprietary from float16 math"); 648 LOG_WARNING(Render_Vulkan, "Blacklisting Intel proprietary from float16 math");
635 is_float16_supported = false; 649 is_float16_supported = false;
636 } 650 }
651 if (is_intel_windows) {
652 LOG_WARNING(Render_Vulkan, "Intel proprietary drivers do not support MSAA image blits");
653 cant_blit_msaa = true;
654 }
637 655
638 supports_d24_depth = 656 supports_d24_depth =
639 IsFormatSupported(VK_FORMAT_D24_UNORM_S8_UINT, 657 IsFormatSupported(VK_FORMAT_D24_UNORM_S8_UINT,
@@ -891,6 +909,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
891 bool has_ext_provoking_vertex{}; 909 bool has_ext_provoking_vertex{};
892 bool has_ext_vertex_input_dynamic_state{}; 910 bool has_ext_vertex_input_dynamic_state{};
893 bool has_ext_line_rasterization{}; 911 bool has_ext_line_rasterization{};
912 bool has_ext_primitive_topology_list_restart{};
894 for (const std::string& extension : supported_extensions) { 913 for (const std::string& extension : supported_extensions) {
895 const auto test = [&](std::optional<std::reference_wrapper<bool>> status, const char* name, 914 const auto test = [&](std::optional<std::reference_wrapper<bool>> status, const char* name,
896 bool push) { 915 bool push) {
@@ -915,6 +934,8 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
915 test(has_khr_shader_float16_int8, VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, false); 934 test(has_khr_shader_float16_int8, VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, false);
916 test(ext_depth_range_unrestricted, VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME, true); 935 test(ext_depth_range_unrestricted, VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME, true);
917 test(ext_index_type_uint8, VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME, true); 936 test(ext_index_type_uint8, VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME, true);
937 test(has_ext_primitive_topology_list_restart,
938 VK_EXT_PRIMITIVE_TOPOLOGY_LIST_RESTART_EXTENSION_NAME, true);
918 test(ext_sampler_filter_minmax, VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME, true); 939 test(ext_sampler_filter_minmax, VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME, true);
919 test(ext_shader_viewport_index_layer, VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME, 940 test(ext_shader_viewport_index_layer, VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME,
920 true); 941 true);
@@ -1113,6 +1134,19 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
1113 khr_pipeline_executable_properties = true; 1134 khr_pipeline_executable_properties = true;
1114 } 1135 }
1115 } 1136 }
1137 if (has_ext_primitive_topology_list_restart) {
1138 VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT primitive_topology_list_restart{};
1139 primitive_topology_list_restart.sType =
1140 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT;
1141 primitive_topology_list_restart.pNext = nullptr;
1142 features.pNext = &primitive_topology_list_restart;
1143 physical.GetFeatures2KHR(features);
1144
1145 is_topology_list_restart_supported =
1146 primitive_topology_list_restart.primitiveTopologyListRestart;
1147 is_patch_list_restart_supported =
1148 primitive_topology_list_restart.primitiveTopologyPatchListRestart;
1149 }
1116 if (has_khr_image_format_list && has_khr_swapchain_mutable_format) { 1150 if (has_khr_image_format_list && has_khr_swapchain_mutable_format) {
1117 extensions.push_back(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME); 1151 extensions.push_back(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME);
1118 extensions.push_back(VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME); 1152 extensions.push_back(VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME);
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index 10653ac6b..37d140ebd 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -238,6 +238,16 @@ public:
238 return khr_workgroup_memory_explicit_layout; 238 return khr_workgroup_memory_explicit_layout;
239 } 239 }
240 240
241 /// Returns true if the device supports VK_EXT_primitive_topology_list_restart.
242 bool IsTopologyListPrimitiveRestartSupported() const {
243 return is_topology_list_restart_supported;
244 }
245
246 /// Returns true if the device supports VK_EXT_primitive_topology_list_restart.
247 bool IsPatchListPrimitiveRestartSupported() const {
248 return is_patch_list_restart_supported;
249 }
250
241 /// Returns true if the device supports VK_EXT_index_type_uint8. 251 /// Returns true if the device supports VK_EXT_index_type_uint8.
242 bool IsExtIndexTypeUint8Supported() const { 252 bool IsExtIndexTypeUint8Supported() const {
243 return ext_index_type_uint8; 253 return ext_index_type_uint8;
@@ -340,6 +350,10 @@ public:
340 return supports_d24_depth; 350 return supports_d24_depth;
341 } 351 }
342 352
353 bool CantBlitMSAA() const {
354 return cant_blit_msaa;
355 }
356
343private: 357private:
344 /// Checks if the physical device is suitable. 358 /// Checks if the physical device is suitable.
345 void CheckSuitability(bool requires_swapchain) const; 359 void CheckSuitability(bool requires_swapchain) const;
@@ -401,6 +415,9 @@ private:
401 bool is_shader_int16_supported{}; ///< Support for int16. 415 bool is_shader_int16_supported{}; ///< Support for int16.
402 bool is_shader_storage_image_multisample{}; ///< Support for image operations on MSAA images. 416 bool is_shader_storage_image_multisample{}; ///< Support for image operations on MSAA images.
403 bool is_blit_depth_stencil_supported{}; ///< Support for blitting from and to depth stencil. 417 bool is_blit_depth_stencil_supported{}; ///< Support for blitting from and to depth stencil.
418 bool is_topology_list_restart_supported{}; ///< Support for primitive restart with list
419 ///< topologies.
420 bool is_patch_list_restart_supported{}; ///< Support for primitive restart with list patch.
404 bool nv_viewport_swizzle{}; ///< Support for VK_NV_viewport_swizzle. 421 bool nv_viewport_swizzle{}; ///< Support for VK_NV_viewport_swizzle.
405 bool nv_viewport_array2{}; ///< Support for VK_NV_viewport_array2. 422 bool nv_viewport_array2{}; ///< Support for VK_NV_viewport_array2.
406 bool nv_geometry_shader_passthrough{}; ///< Support for VK_NV_geometry_shader_passthrough. 423 bool nv_geometry_shader_passthrough{}; ///< Support for VK_NV_geometry_shader_passthrough.
@@ -430,6 +447,7 @@ private:
430 bool has_renderdoc{}; ///< Has RenderDoc attached 447 bool has_renderdoc{}; ///< Has RenderDoc attached
431 bool has_nsight_graphics{}; ///< Has Nsight Graphics attached 448 bool has_nsight_graphics{}; ///< Has Nsight Graphics attached
432 bool supports_d24_depth{}; ///< Supports D24 depth buffers. 449 bool supports_d24_depth{}; ///< Supports D24 depth buffers.
450 bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting.
433 451
434 // Telemetry parameters 452 // Telemetry parameters
435 std::string vendor_name; ///< Device's driver name. 453 std::string vendor_name; ///< Device's driver name.
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 732e8c276..30902101d 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -251,6 +251,9 @@ target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include)
251if (NOT WIN32) 251if (NOT WIN32)
252 target_include_directories(yuzu PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) 252 target_include_directories(yuzu PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
253endif() 253endif()
254if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
255 target_link_libraries(yuzu PRIVATE Qt5::DBus)
256endif()
254 257
255target_compile_definitions(yuzu PRIVATE 258target_compile_definitions(yuzu PRIVATE
256 # Use QStringBuilder for string concatenation to reduce 259 # Use QStringBuilder for string concatenation to reduce
diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp
index c5685db2e..4239c17f5 100644
--- a/src/yuzu/applets/qt_controller.cpp
+++ b/src/yuzu/applets/qt_controller.cpp
@@ -12,7 +12,6 @@
12#include "core/hid/emulated_controller.h" 12#include "core/hid/emulated_controller.h"
13#include "core/hid/hid_core.h" 13#include "core/hid/hid_core.h"
14#include "core/hid/hid_types.h" 14#include "core/hid/hid_types.h"
15#include "core/hle/lock.h"
16#include "core/hle/service/hid/controllers/npad.h" 15#include "core/hle/service/hid/controllers/npad.h"
17#include "core/hle/service/hid/hid.h" 16#include "core/hle/service/hid/hid.h"
18#include "core/hle/service/sm/sm.h" 17#include "core/hle/service/sm/sm.h"
@@ -34,7 +33,7 @@ void UpdateController(Core::HID::EmulatedController* controller,
34 } 33 }
35 controller->SetNpadStyleIndex(controller_type); 34 controller->SetNpadStyleIndex(controller_type);
36 if (connected) { 35 if (connected) {
37 controller->Connect(); 36 controller->Connect(true);
38 } 37 }
39} 38}
40 39
@@ -401,36 +400,66 @@ void QtControllerSelectorDialog::SetSupportedControllers() {
401} 400}
402 401
403void QtControllerSelectorDialog::SetEmulatedControllers(std::size_t player_index) { 402void QtControllerSelectorDialog::SetEmulatedControllers(std::size_t player_index) {
403 const auto npad_style_set = system.HIDCore().GetSupportedStyleTag();
404 auto& pairs = index_controller_type_pairs[player_index]; 404 auto& pairs = index_controller_type_pairs[player_index];
405 405
406 pairs.clear(); 406 pairs.clear();
407 emulated_controllers[player_index]->clear(); 407 emulated_controllers[player_index]->clear();
408 408
409 pairs.emplace_back(emulated_controllers[player_index]->count(), 409 const auto add_item = [&](Core::HID::NpadStyleIndex controller_type,
410 Core::HID::NpadStyleIndex::ProController); 410 const QString& controller_name) {
411 emulated_controllers[player_index]->addItem(tr("Pro Controller")); 411 pairs.emplace_back(emulated_controllers[player_index]->count(), controller_type);
412 emulated_controllers[player_index]->addItem(controller_name);
413 };
412 414
413 pairs.emplace_back(emulated_controllers[player_index]->count(), 415 if (npad_style_set.fullkey == 1) {
414 Core::HID::NpadStyleIndex::JoyconDual); 416 add_item(Core::HID::NpadStyleIndex::ProController, tr("Pro Controller"));
415 emulated_controllers[player_index]->addItem(tr("Dual Joycons")); 417 }
416 418
417 pairs.emplace_back(emulated_controllers[player_index]->count(), 419 if (npad_style_set.joycon_dual == 1) {
418 Core::HID::NpadStyleIndex::JoyconLeft); 420 add_item(Core::HID::NpadStyleIndex::JoyconDual, tr("Dual Joycons"));
419 emulated_controllers[player_index]->addItem(tr("Left Joycon")); 421 }
420 422
421 pairs.emplace_back(emulated_controllers[player_index]->count(), 423 if (npad_style_set.joycon_left == 1) {
422 Core::HID::NpadStyleIndex::JoyconRight); 424 add_item(Core::HID::NpadStyleIndex::JoyconLeft, tr("Left Joycon"));
423 emulated_controllers[player_index]->addItem(tr("Right Joycon")); 425 }
424 426
425 if (player_index == 0) { 427 if (npad_style_set.joycon_right == 1) {
426 pairs.emplace_back(emulated_controllers[player_index]->count(), 428 add_item(Core::HID::NpadStyleIndex::JoyconRight, tr("Right Joycon"));
427 Core::HID::NpadStyleIndex::Handheld);
428 emulated_controllers[player_index]->addItem(tr("Handheld"));
429 } 429 }
430 430
431 pairs.emplace_back(emulated_controllers[player_index]->count(), 431 if (player_index == 0 && npad_style_set.handheld == 1) {
432 Core::HID::NpadStyleIndex::GameCube); 432 add_item(Core::HID::NpadStyleIndex::Handheld, tr("Handheld"));
433 emulated_controllers[player_index]->addItem(tr("GameCube Controller")); 433 }
434
435 if (npad_style_set.gamecube == 1) {
436 add_item(Core::HID::NpadStyleIndex::GameCube, tr("GameCube Controller"));
437 }
438
439 // Disable all unsupported controllers
440 if (!Settings::values.enable_all_controllers) {
441 return;
442 }
443
444 if (npad_style_set.palma == 1) {
445 add_item(Core::HID::NpadStyleIndex::Pokeball, tr("Poke Ball Plus"));
446 }
447
448 if (npad_style_set.lark == 1) {
449 add_item(Core::HID::NpadStyleIndex::NES, tr("NES Controller"));
450 }
451
452 if (npad_style_set.lucia == 1) {
453 add_item(Core::HID::NpadStyleIndex::SNES, tr("SNES Controller"));
454 }
455
456 if (npad_style_set.lagoon == 1) {
457 add_item(Core::HID::NpadStyleIndex::N64, tr("N64 Controller"));
458 }
459
460 if (npad_style_set.lager == 1) {
461 add_item(Core::HID::NpadStyleIndex::SegaGenesis, tr("Sega Genesis"));
462 }
434} 463}
435 464
436Core::HID::NpadStyleIndex QtControllerSelectorDialog::GetControllerTypeFromIndex( 465Core::HID::NpadStyleIndex QtControllerSelectorDialog::GetControllerTypeFromIndex(
@@ -664,7 +693,5 @@ void QtControllerSelector::ReconfigureControllers(
664} 693}
665 694
666void QtControllerSelector::MainWindowReconfigureFinished() { 695void QtControllerSelector::MainWindowReconfigureFinished() {
667 // Acquire the HLE mutex
668 std::lock_guard lock(HLE::g_hle_lock);
669 callback(); 696 callback();
670} 697}
diff --git a/src/yuzu/applets/qt_error.cpp b/src/yuzu/applets/qt_error.cpp
index 45cf64603..879e73660 100644
--- a/src/yuzu/applets/qt_error.cpp
+++ b/src/yuzu/applets/qt_error.cpp
@@ -3,7 +3,6 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <QDateTime> 5#include <QDateTime>
6#include "core/hle/lock.h"
7#include "yuzu/applets/qt_error.h" 6#include "yuzu/applets/qt_error.h"
8#include "yuzu/main.h" 7#include "yuzu/main.h"
9 8
@@ -57,7 +56,5 @@ void QtErrorDisplay::ShowCustomErrorText(ResultCode error, std::string dialog_te
57} 56}
58 57
59void QtErrorDisplay::MainWindowFinishedError() { 58void QtErrorDisplay::MainWindowFinishedError() {
60 // Acquire the HLE mutex
61 std::lock_guard lock{HLE::g_hle_lock};
62 callback(); 59 callback();
63} 60}
diff --git a/src/yuzu/applets/qt_profile_select.cpp b/src/yuzu/applets/qt_profile_select.cpp
index 7b19f1f8d..5b32da923 100644
--- a/src/yuzu/applets/qt_profile_select.cpp
+++ b/src/yuzu/applets/qt_profile_select.cpp
@@ -14,7 +14,6 @@
14#include "common/fs/path_util.h" 14#include "common/fs/path_util.h"
15#include "common/string_util.h" 15#include "common/string_util.h"
16#include "core/constants.h" 16#include "core/constants.h"
17#include "core/hle/lock.h"
18#include "yuzu/applets/qt_profile_select.h" 17#include "yuzu/applets/qt_profile_select.h"
19#include "yuzu/main.h" 18#include "yuzu/main.h"
20#include "yuzu/util/controller_navigation.h" 19#include "yuzu/util/controller_navigation.h"
@@ -170,7 +169,5 @@ void QtProfileSelector::SelectProfile(
170} 169}
171 170
172void QtProfileSelector::MainWindowFinishedSelection(std::optional<Common::UUID> uuid) { 171void QtProfileSelector::MainWindowFinishedSelection(std::optional<Common::UUID> uuid) {
173 // Acquire the HLE mutex
174 std::lock_guard lock{HLE::g_hle_lock};
175 callback(uuid); 172 callback(uuid);
176} 173}
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 463d500c2..0f679c37e 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -776,6 +776,7 @@ void Config::ReadUIGamelistValues() {
776 ReadBasicSetting(UISettings::values.row_1_text_id); 776 ReadBasicSetting(UISettings::values.row_1_text_id);
777 ReadBasicSetting(UISettings::values.row_2_text_id); 777 ReadBasicSetting(UISettings::values.row_2_text_id);
778 ReadBasicSetting(UISettings::values.cache_game_list); 778 ReadBasicSetting(UISettings::values.cache_game_list);
779 ReadBasicSetting(UISettings::values.favorites_expanded);
779 const int favorites_size = qt_config->beginReadArray(QStringLiteral("favorites")); 780 const int favorites_size = qt_config->beginReadArray(QStringLiteral("favorites"));
780 for (int i = 0; i < favorites_size; i++) { 781 for (int i = 0; i < favorites_size; i++) {
781 qt_config->setArrayIndex(i); 782 qt_config->setArrayIndex(i);
@@ -1300,6 +1301,7 @@ void Config::SaveUIGamelistValues() {
1300 WriteBasicSetting(UISettings::values.row_1_text_id); 1301 WriteBasicSetting(UISettings::values.row_1_text_id);
1301 WriteBasicSetting(UISettings::values.row_2_text_id); 1302 WriteBasicSetting(UISettings::values.row_2_text_id);
1302 WriteBasicSetting(UISettings::values.cache_game_list); 1303 WriteBasicSetting(UISettings::values.cache_game_list);
1304 WriteBasicSetting(UISettings::values.favorites_expanded);
1303 qt_config->beginWriteArray(QStringLiteral("favorites")); 1305 qt_config->beginWriteArray(QStringLiteral("favorites"));
1304 for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) { 1306 for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) {
1305 qt_config->setArrayIndex(i); 1307 qt_config->setArrayIndex(i);
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index 633fc295b..c1cf4050c 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -51,6 +51,8 @@ void ConfigureDebug::SetConfiguration() {
51 ui->enable_cpu_debugging->setChecked(Settings::values.cpu_debug_mode.GetValue()); 51 ui->enable_cpu_debugging->setChecked(Settings::values.cpu_debug_mode.GetValue());
52 ui->enable_nsight_aftermath->setEnabled(runtime_lock); 52 ui->enable_nsight_aftermath->setEnabled(runtime_lock);
53 ui->enable_nsight_aftermath->setChecked(Settings::values.enable_nsight_aftermath.GetValue()); 53 ui->enable_nsight_aftermath->setChecked(Settings::values.enable_nsight_aftermath.GetValue());
54 ui->dump_shaders->setEnabled(runtime_lock);
55 ui->dump_shaders->setChecked(Settings::values.dump_shaders.GetValue());
54 ui->disable_macro_jit->setEnabled(runtime_lock); 56 ui->disable_macro_jit->setEnabled(runtime_lock);
55 ui->disable_macro_jit->setChecked(Settings::values.disable_macro_jit.GetValue()); 57 ui->disable_macro_jit->setChecked(Settings::values.disable_macro_jit.GetValue());
56 ui->disable_loop_safety_checks->setEnabled(runtime_lock); 58 ui->disable_loop_safety_checks->setEnabled(runtime_lock);
@@ -73,6 +75,7 @@ void ConfigureDebug::ApplyConfiguration() {
73 Settings::values.renderer_shader_feedback = ui->enable_shader_feedback->isChecked(); 75 Settings::values.renderer_shader_feedback = ui->enable_shader_feedback->isChecked();
74 Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked(); 76 Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked();
75 Settings::values.enable_nsight_aftermath = ui->enable_nsight_aftermath->isChecked(); 77 Settings::values.enable_nsight_aftermath = ui->enable_nsight_aftermath->isChecked();
78 Settings::values.dump_shaders = ui->dump_shaders->isChecked();
76 Settings::values.disable_shader_loop_safety_checks = 79 Settings::values.disable_shader_loop_safety_checks =
77 ui->disable_loop_safety_checks->isChecked(); 80 ui->disable_loop_safety_checks->isChecked();
78 Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked(); 81 Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked();
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index 0f3b51c8d..4dd870855 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -105,6 +105,19 @@
105 </property> 105 </property>
106 </widget> 106 </widget>
107 </item> 107 </item>
108 <item row="2" column="1">
109 <widget class="QCheckBox" name="dump_shaders">
110 <property name="enabled">
111 <bool>true</bool>
112 </property>
113 <property name="toolTip">
114 <string>When checked, it will dump all the original assembler shaders from the disk shader cache or game as found</string>
115 </property>
116 <property name="text">
117 <string>Dump Game Shaders</string>
118 </property>
119 </widget>
120 </item>
108 <item row="0" column="1"> 121 <item row="0" column="1">
109 <widget class="QCheckBox" name="disable_macro_jit"> 122 <widget class="QCheckBox" name="disable_macro_jit">
110 <property name="enabled"> 123 <property name="enabled">
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 8a8be8e40..8c6249fc2 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -599,11 +599,11 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
599 if (is_connected) { 599 if (is_connected) {
600 if (type == Core::HID::NpadStyleIndex::Handheld) { 600 if (type == Core::HID::NpadStyleIndex::Handheld) {
601 emulated_controller_p1->Disconnect(); 601 emulated_controller_p1->Disconnect();
602 emulated_controller_handheld->Connect(); 602 emulated_controller_handheld->Connect(true);
603 emulated_controller = emulated_controller_handheld; 603 emulated_controller = emulated_controller_handheld;
604 } else { 604 } else {
605 emulated_controller_handheld->Disconnect(); 605 emulated_controller_handheld->Disconnect();
606 emulated_controller_p1->Connect(); 606 emulated_controller_p1->Connect(true);
607 emulated_controller = emulated_controller_p1; 607 emulated_controller = emulated_controller_p1;
608 } 608 }
609 } 609 }
@@ -718,7 +718,7 @@ void ConfigureInputPlayer::LoadConfiguration() {
718void ConfigureInputPlayer::ConnectPlayer(bool connected) { 718void ConfigureInputPlayer::ConnectPlayer(bool connected) {
719 ui->groupConnectedController->setChecked(connected); 719 ui->groupConnectedController->setChecked(connected);
720 if (connected) { 720 if (connected) {
721 emulated_controller->Connect(); 721 emulated_controller->Connect(true);
722 } else { 722 } else {
723 emulated_controller->Disconnect(); 723 emulated_controller->Disconnect();
724 } 724 }
@@ -907,78 +907,63 @@ void ConfigureInputPlayer::UpdateUI() {
907} 907}
908 908
909void ConfigureInputPlayer::SetConnectableControllers() { 909void ConfigureInputPlayer::SetConnectableControllers() {
910 Core::HID::NpadStyleTag npad_style_set = hid_core.GetSupportedStyleTag(); 910 const auto npad_style_set = hid_core.GetSupportedStyleTag();
911 index_controller_type_pairs.clear(); 911 index_controller_type_pairs.clear();
912 ui->comboControllerType->clear(); 912 ui->comboControllerType->clear();
913 913
914 const auto add_item = [&](Core::HID::NpadStyleIndex controller_type,
915 const QString& controller_name) {
916 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), controller_type);
917 ui->comboControllerType->addItem(controller_name);
918 };
919
914 if (npad_style_set.fullkey == 1) { 920 if (npad_style_set.fullkey == 1) {
915 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), 921 add_item(Core::HID::NpadStyleIndex::ProController, tr("Pro Controller"));
916 Core::HID::NpadStyleIndex::ProController);
917 ui->comboControllerType->addItem(tr("Pro Controller"));
918 } 922 }
919 923
920 if (npad_style_set.joycon_dual == 1) { 924 if (npad_style_set.joycon_dual == 1) {
921 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), 925 add_item(Core::HID::NpadStyleIndex::JoyconDual, tr("Dual Joycons"));
922 Core::HID::NpadStyleIndex::JoyconDual);
923 ui->comboControllerType->addItem(tr("Dual Joycons"));
924 } 926 }
925 927
926 if (npad_style_set.joycon_left == 1) { 928 if (npad_style_set.joycon_left == 1) {
927 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), 929 add_item(Core::HID::NpadStyleIndex::JoyconLeft, tr("Left Joycon"));
928 Core::HID::NpadStyleIndex::JoyconLeft);
929 ui->comboControllerType->addItem(tr("Left Joycon"));
930 } 930 }
931 931
932 if (npad_style_set.joycon_right == 1) { 932 if (npad_style_set.joycon_right == 1) {
933 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), 933 add_item(Core::HID::NpadStyleIndex::JoyconRight, tr("Right Joycon"));
934 Core::HID::NpadStyleIndex::JoyconRight);
935 ui->comboControllerType->addItem(tr("Right Joycon"));
936 } 934 }
937 935
938 if (player_index == 0 && npad_style_set.handheld == 1) { 936 if (player_index == 0 && npad_style_set.handheld == 1) {
939 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), 937 add_item(Core::HID::NpadStyleIndex::Handheld, tr("Handheld"));
940 Core::HID::NpadStyleIndex::Handheld);
941 ui->comboControllerType->addItem(tr("Handheld"));
942 } 938 }
943 939
944 if (npad_style_set.gamecube == 1) { 940 if (npad_style_set.gamecube == 1) {
945 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), 941 add_item(Core::HID::NpadStyleIndex::GameCube, tr("GameCube Controller"));
946 Core::HID::NpadStyleIndex::GameCube);
947 ui->comboControllerType->addItem(tr("GameCube Controller"));
948 } 942 }
949 943
950 // Disable all unsupported controllers 944 // Disable all unsupported controllers
951 if (!Settings::values.enable_all_controllers) { 945 if (!Settings::values.enable_all_controllers) {
952 return; 946 return;
953 } 947 }
948
954 if (npad_style_set.palma == 1) { 949 if (npad_style_set.palma == 1) {
955 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), 950 add_item(Core::HID::NpadStyleIndex::Pokeball, tr("Poke Ball Plus"));
956 Core::HID::NpadStyleIndex::Pokeball);
957 ui->comboControllerType->addItem(tr("Poke Ball Plus"));
958 } 951 }
959 952
960 if (npad_style_set.lark == 1) { 953 if (npad_style_set.lark == 1) {
961 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), 954 add_item(Core::HID::NpadStyleIndex::NES, tr("NES Controller"));
962 Core::HID::NpadStyleIndex::NES);
963 ui->comboControllerType->addItem(tr("NES Controller"));
964 } 955 }
965 956
966 if (npad_style_set.lucia == 1) { 957 if (npad_style_set.lucia == 1) {
967 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), 958 add_item(Core::HID::NpadStyleIndex::SNES, tr("SNES Controller"));
968 Core::HID::NpadStyleIndex::SNES);
969 ui->comboControllerType->addItem(tr("SNES Controller"));
970 } 959 }
971 960
972 if (npad_style_set.lagoon == 1) { 961 if (npad_style_set.lagoon == 1) {
973 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), 962 add_item(Core::HID::NpadStyleIndex::N64, tr("N64 Controller"));
974 Core::HID::NpadStyleIndex::N64);
975 ui->comboControllerType->addItem(tr("N64 Controller"));
976 } 963 }
977 964
978 if (npad_style_set.lager == 1) { 965 if (npad_style_set.lager == 1) {
979 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), 966 add_item(Core::HID::NpadStyleIndex::SegaGenesis, tr("Sega Genesis"));
980 Core::HID::NpadStyleIndex::SegaGenesis);
981 ui->comboControllerType->addItem(tr("Sega Genesis"));
982 } 967 }
983} 968}
984 969
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 1a5e41588..8b5c4a10a 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -173,13 +173,17 @@ void GameList::OnItemExpanded(const QModelIndex& item) {
173 const bool is_dir = type == GameListItemType::CustomDir || type == GameListItemType::SdmcDir || 173 const bool is_dir = type == GameListItemType::CustomDir || type == GameListItemType::SdmcDir ||
174 type == GameListItemType::UserNandDir || 174 type == GameListItemType::UserNandDir ||
175 type == GameListItemType::SysNandDir; 175 type == GameListItemType::SysNandDir;
176 176 const bool is_fave = type == GameListItemType::Favorites;
177 if (!is_dir) { 177 if (!is_dir && !is_fave) {
178 return; 178 return;
179 } 179 }
180 180 const bool is_expanded = tree_view->isExpanded(item);
181 UISettings::values.game_dirs[item.data(GameListDir::GameDirRole).toInt()].expanded = 181 if (is_fave) {
182 tree_view->isExpanded(item); 182 UISettings::values.favorites_expanded = is_expanded;
183 return;
184 }
185 const int item_dir_index = item.data(GameListDir::GameDirRole).toInt();
186 UISettings::values.game_dirs[item_dir_index].expanded = is_expanded;
183} 187}
184 188
185// Event in order to filter the gamelist after editing the searchfield 189// Event in order to filter the gamelist after editing the searchfield
@@ -458,10 +462,13 @@ void GameList::DonePopulating(const QStringList& watch_list) {
458 emit ShowList(!IsEmpty()); 462 emit ShowList(!IsEmpty());
459 463
460 item_model->invisibleRootItem()->appendRow(new GameListAddDir()); 464 item_model->invisibleRootItem()->appendRow(new GameListAddDir());
465
466 // Add favorites row
461 item_model->invisibleRootItem()->insertRow(0, new GameListFavorites()); 467 item_model->invisibleRootItem()->insertRow(0, new GameListFavorites());
462 tree_view->setRowHidden(0, item_model->invisibleRootItem()->index(), 468 tree_view->setRowHidden(0, item_model->invisibleRootItem()->index(),
463 UISettings::values.favorited_ids.size() == 0); 469 UISettings::values.favorited_ids.size() == 0);
464 tree_view->expand(item_model->invisibleRootItem()->child(0)->index()); 470 tree_view->setExpanded(item_model->invisibleRootItem()->child(0)->index(),
471 UISettings::values.favorites_expanded.GetValue());
465 for (const auto id : UISettings::values.favorited_ids) { 472 for (const auto id : UISettings::values.favorited_ids) {
466 AddFavorite(id); 473 AddFavorite(id);
467 } 474 }
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index b7bb43348..53f11a9ac 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -1236,11 +1236,58 @@ void GMainWindow::OnDisplayTitleBars(bool show) {
1236 } 1236 }
1237} 1237}
1238 1238
1239#ifdef __linux__
1240static std::optional<QDBusObjectPath> HoldWakeLockLinux(u32 window_id = 0) {
1241 if (!QDBusConnection::sessionBus().isConnected()) {
1242 return {};
1243 }
1244 // reference: https://flatpak.github.io/xdg-desktop-portal/#gdbus-org.freedesktop.portal.Inhibit
1245 QDBusInterface xdp(QString::fromLatin1("org.freedesktop.portal.Desktop"),
1246 QString::fromLatin1("/org/freedesktop/portal/desktop"),
1247 QString::fromLatin1("org.freedesktop.portal.Inhibit"));
1248 if (!xdp.isValid()) {
1249 LOG_WARNING(Frontend, "Couldn't connect to XDP D-Bus endpoint");
1250 return {};
1251 }
1252 QVariantMap options = {};
1253 //: TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the
1254 //: computer from sleeping
1255 options.insert(QString::fromLatin1("reason"),
1256 QCoreApplication::translate("GMainWindow", "yuzu is running a game"));
1257 // 0x4: Suspend lock; 0x8: Idle lock
1258 QDBusReply<QDBusObjectPath> reply =
1259 xdp.call(QString::fromLatin1("Inhibit"),
1260 QString::fromLatin1("x11:") + QString::number(window_id, 16), 12U, options);
1261
1262 if (reply.isValid()) {
1263 return reply.value();
1264 }
1265 LOG_WARNING(Frontend, "Couldn't read Inhibit reply from XDP: {}",
1266 reply.error().message().toStdString());
1267 return {};
1268}
1269
1270static void ReleaseWakeLockLinux(QDBusObjectPath lock) {
1271 if (!QDBusConnection::sessionBus().isConnected()) {
1272 return;
1273 }
1274 QDBusInterface unlocker(QString::fromLatin1("org.freedesktop.portal.Desktop"), lock.path(),
1275 QString::fromLatin1("org.freedesktop.portal.Request"));
1276 unlocker.call(QString::fromLatin1("Close"));
1277}
1278#endif // __linux__
1279
1239void GMainWindow::PreventOSSleep() { 1280void GMainWindow::PreventOSSleep() {
1240#ifdef _WIN32 1281#ifdef _WIN32
1241 SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED); 1282 SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED);
1242#elif defined(HAVE_SDL2) 1283#elif defined(HAVE_SDL2)
1243 SDL_DisableScreenSaver(); 1284 SDL_DisableScreenSaver();
1285#ifdef __linux__
1286 auto reply = HoldWakeLockLinux(winId());
1287 if (reply) {
1288 wake_lock = std::move(reply.value());
1289 }
1290#endif
1244#endif 1291#endif
1245} 1292}
1246 1293
@@ -1249,6 +1296,11 @@ void GMainWindow::AllowOSSleep() {
1249 SetThreadExecutionState(ES_CONTINUOUS); 1296 SetThreadExecutionState(ES_CONTINUOUS);
1250#elif defined(HAVE_SDL2) 1297#elif defined(HAVE_SDL2)
1251 SDL_EnableScreenSaver(); 1298 SDL_EnableScreenSaver();
1299#ifdef __linux__
1300 if (!wake_lock.path().isEmpty()) {
1301 ReleaseWakeLockLinux(wake_lock);
1302 }
1303#endif
1252#endif 1304#endif
1253} 1305}
1254 1306
@@ -1495,6 +1547,8 @@ void GMainWindow::ShutdownGame() {
1495 emu_thread->wait(); 1547 emu_thread->wait();
1496 emu_thread = nullptr; 1548 emu_thread = nullptr;
1497 1549
1550 emulation_running = false;
1551
1498 discord_rpc->Update(); 1552 discord_rpc->Update();
1499 1553
1500 // The emulation is stopped, so closing the window or not does not matter anymore 1554 // The emulation is stopped, so closing the window or not does not matter anymore
@@ -1533,8 +1587,6 @@ void GMainWindow::ShutdownGame() {
1533 emu_frametime_label->setVisible(false); 1587 emu_frametime_label->setVisible(false);
1534 renderer_status_button->setEnabled(true); 1588 renderer_status_button->setEnabled(true);
1535 1589
1536 emulation_running = false;
1537
1538 game_path.clear(); 1590 game_path.clear();
1539 1591
1540 // When closing the game, destroy the GLWindow to clear the context after the game is closed 1592 // When closing the game, destroy the GLWindow to clear the context after the game is closed
@@ -2546,39 +2598,30 @@ void GMainWindow::ToggleFullscreen() {
2546} 2598}
2547 2599
2548void GMainWindow::ShowFullscreen() { 2600void GMainWindow::ShowFullscreen() {
2601 const auto show_fullscreen = [](QWidget* window) {
2602 if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) {
2603 window->showFullScreen();
2604 return;
2605 }
2606 window->hide();
2607 window->setWindowFlags(window->windowFlags() | Qt::FramelessWindowHint);
2608 const auto screen_geometry = QApplication::desktop()->screenGeometry(window);
2609 window->setGeometry(screen_geometry.x(), screen_geometry.y(), screen_geometry.width(),
2610 screen_geometry.height() + 1);
2611 window->raise();
2612 window->showNormal();
2613 };
2614
2549 if (ui->action_Single_Window_Mode->isChecked()) { 2615 if (ui->action_Single_Window_Mode->isChecked()) {
2550 UISettings::values.geometry = saveGeometry(); 2616 UISettings::values.geometry = saveGeometry();
2551 2617
2552 ui->menubar->hide(); 2618 ui->menubar->hide();
2553 statusBar()->hide(); 2619 statusBar()->hide();
2554 2620
2555 if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) { 2621 show_fullscreen(this);
2556 showFullScreen();
2557 return;
2558 }
2559
2560 hide();
2561 setWindowFlags(windowFlags() | Qt::FramelessWindowHint);
2562 const auto screen_geometry = QApplication::desktop()->screenGeometry(this);
2563 setGeometry(screen_geometry.x(), screen_geometry.y(), screen_geometry.width(),
2564 screen_geometry.height() + 1);
2565 raise();
2566 showNormal();
2567 } else { 2622 } else {
2568 UISettings::values.renderwindow_geometry = render_window->saveGeometry(); 2623 UISettings::values.renderwindow_geometry = render_window->saveGeometry();
2569 2624 show_fullscreen(render_window);
2570 if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) {
2571 render_window->showFullScreen();
2572 return;
2573 }
2574
2575 render_window->hide();
2576 render_window->setWindowFlags(windowFlags() | Qt::FramelessWindowHint);
2577 const auto screen_geometry = QApplication::desktop()->screenGeometry(this);
2578 render_window->setGeometry(screen_geometry.x(), screen_geometry.y(),
2579 screen_geometry.width(), screen_geometry.height() + 1);
2580 render_window->raise();
2581 render_window->showNormal();
2582 } 2625 }
2583} 2626}
2584 2627
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 0fd41ed4f..7870bb963 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -17,6 +17,12 @@
17#include "yuzu/compatibility_list.h" 17#include "yuzu/compatibility_list.h"
18#include "yuzu/hotkeys.h" 18#include "yuzu/hotkeys.h"
19 19
20#ifdef __linux__
21#include <QVariant>
22#include <QtDBus/QDBusInterface>
23#include <QtDBus/QtDBus>
24#endif
25
20class Config; 26class Config;
21class EmuThread; 27class EmuThread;
22class GameList; 28class GameList;
@@ -394,6 +400,9 @@ private:
394 400
395 // Applets 401 // Applets
396 QtSoftwareKeyboardDialog* software_keyboard = nullptr; 402 QtSoftwareKeyboardDialog* software_keyboard = nullptr;
403#ifdef __linux__
404 QDBusObjectPath wake_lock{};
405#endif
397 406
398protected: 407protected:
399 void dropEvent(QDropEvent* event) override; 408 void dropEvent(QDropEvent* event) override;
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index 936914ef3..a610e7e25 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -74,7 +74,6 @@ struct Values {
74 QString game_dir_deprecated; 74 QString game_dir_deprecated;
75 bool game_dir_deprecated_deepscan; 75 bool game_dir_deprecated_deepscan;
76 QVector<UISettings::GameDir> game_dirs; 76 QVector<UISettings::GameDir> game_dirs;
77 QVector<u64> favorited_ids;
78 QStringList recent_files; 77 QStringList recent_files;
79 QString language; 78 QString language;
80 79
@@ -96,6 +95,8 @@ struct Values {
96 Settings::BasicSetting<uint8_t> row_2_text_id{2, "row_2_text_id"}; 95 Settings::BasicSetting<uint8_t> row_2_text_id{2, "row_2_text_id"};
97 std::atomic_bool is_game_list_reload_pending{false}; 96 std::atomic_bool is_game_list_reload_pending{false};
98 Settings::BasicSetting<bool> cache_game_list{true, "cache_game_list"}; 97 Settings::BasicSetting<bool> cache_game_list{true, "cache_game_list"};
98 Settings::BasicSetting<bool> favorites_expanded{true, "favorites_expanded"};
99 QVector<u64> favorited_ids;
99 100
100 bool configuration_applied; 101 bool configuration_applied;
101 bool reset_to_defaults; 102 bool reset_to_defaults;