Compare commits
149 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f58ee3f15f | ||
|
|
da8e0f6571 | ||
|
|
3fa9702952 | ||
|
|
ae7da0b12d | ||
|
|
214b9fc9a7 | ||
|
|
951c61aeaa | ||
|
|
eb7d361657 | ||
|
|
af89f7683d | ||
|
|
667a8ae163 | ||
|
|
3a89723d97 | ||
|
|
9ee5c4ec56 | ||
|
|
5e58271903 | ||
|
|
8c907c620d | ||
|
|
b84d429c2e | ||
|
|
9f34be5a61 | ||
|
|
14ac0c2923 | ||
|
|
279c7bcc1a | ||
|
|
c7235e67ef | ||
|
|
840d5520d2 | ||
|
|
091463a429 | ||
|
|
f67605e6aa | ||
|
|
9a0648ff0a | ||
|
|
c9e4acc4e2 | ||
|
|
292dfac25e | ||
|
|
37addf7a94 | ||
|
|
640fc1418b | ||
|
|
8a48c4ed1c | ||
|
|
f9e0681d59 | ||
|
|
cbc0f0a66e | ||
|
|
481b210c0d | ||
|
|
4e7a6639d2 | ||
|
|
a0c7d93b84 | ||
|
|
516325eba8 | ||
|
|
e0193e2be5 | ||
|
|
b85f5b1332 | ||
|
|
648c7b4ed6 | ||
|
|
14fc1bec17 | ||
|
|
fa7abafa5f | ||
|
|
36df305b13 | ||
|
|
f1eff447bb | ||
|
|
49e3c073a5 | ||
|
|
6991d447d4 | ||
|
|
b30a1d49ff | ||
|
|
5e24f7ed31 | ||
|
|
cf221ca92d | ||
|
|
59c6f45e7a | ||
|
|
caf38725ae | ||
|
|
ee6d40d414 | ||
|
|
eb4ea7e5c7 | ||
|
|
3074b2eb93 | ||
|
|
39bb6851e4 | ||
|
|
55650c5b75 | ||
|
|
e57b13ad94 | ||
|
|
1a9576fdff | ||
|
|
8e33cf1c2b | ||
|
|
6fb212784e | ||
|
|
2030522d86 | ||
|
|
1490b49fa9 | ||
|
|
212b497d5c | ||
|
|
7feac8ba46 | ||
|
|
4908a07c20 | ||
|
|
6c00151d17 | ||
|
|
04b4f3b051 | ||
|
|
6ebc972c2b | ||
|
|
94652e122d | ||
|
|
e49184e606 | ||
|
|
14d2c77f91 | ||
|
|
6430fc29a9 | ||
|
|
77d06d5df0 | ||
|
|
c73841500a | ||
|
|
11f4bf8a9a | ||
|
|
9b3611eb8d | ||
|
|
e610485cd2 | ||
|
|
c82e6dc810 | ||
|
|
e242f16986 | ||
|
|
333ccf23f8 | ||
|
|
1cdddd17d2 | ||
|
|
7cf74abbf5 | ||
|
|
476637d143 | ||
|
|
3ae9258efe | ||
|
|
3f765ea9a4 | ||
|
|
b3fc36d989 | ||
|
|
2a24c415c1 | ||
|
|
2f32133ad5 | ||
|
|
44b3abdfc0 | ||
|
|
156215d1fa | ||
|
|
019ae82c94 | ||
|
|
822259a3f2 | ||
|
|
f0ed11e318 | ||
|
|
935fee18ed | ||
|
|
6aac5d4c27 | ||
|
|
5e732e7aec | ||
|
|
ac0c5be7c0 | ||
|
|
7f965172c5 | ||
|
|
dd72e4dce4 | ||
|
|
a2d73eaa10 | ||
|
|
9fa1cf3e01 | ||
|
|
e05d2a70b2 | ||
|
|
4af413623b | ||
|
|
54eafbaf17 | ||
|
|
5e7e38ac72 | ||
|
|
6497fbfa96 | ||
|
|
7783c0aaef | ||
|
|
ad45963b45 | ||
|
|
37f1c76613 | ||
|
|
f91b6fbbcb | ||
|
|
ccc0a1e621 | ||
|
|
7276aaf907 | ||
|
|
54ca48e8b7 | ||
|
|
734fb180bb | ||
|
|
ddda6ae776 | ||
|
|
db9320e754 | ||
|
|
26ef76213c | ||
|
|
a515ede2af | ||
|
|
6be730bdcd | ||
|
|
37a8e2a67e | ||
|
|
d52ad96ce3 | ||
|
|
c126b0718c | ||
|
|
e4de1783e1 | ||
|
|
a9d39b6895 | ||
|
|
4d4a234476 | ||
|
|
e51b852aee | ||
|
|
e826e6715a | ||
|
|
755822ceec | ||
|
|
a92dbec962 | ||
|
|
985599e485 | ||
|
|
38f3442ea5 | ||
|
|
2b92d22bda | ||
|
|
9a104e2b60 | ||
|
|
479369db43 | ||
|
|
3c618a3306 | ||
|
|
316f80af87 | ||
|
|
b64d28492a | ||
|
|
14110230c7 | ||
|
|
1598426493 | ||
|
|
ae4869650a | ||
|
|
60bdedc7dd | ||
|
|
228a381aed | ||
|
|
c22c4f5d59 | ||
|
|
218d790bd6 | ||
|
|
b8f3e5157b | ||
|
|
35d94dcb2b | ||
|
|
4a13f9eecd | ||
|
|
ad99bbf5fe | ||
|
|
b387a26f30 | ||
|
|
75c4aec8ab | ||
|
|
660c6bec22 | ||
|
|
04fa990b0c | ||
|
|
4dd85f86a8 |
@@ -41,12 +41,11 @@ for i in package/*.exe; do
|
||||
done
|
||||
|
||||
pip3 install pefile
|
||||
python3 .ci/scripts/windows/scan_dll.py package/*.exe "package/"
|
||||
python3 .ci/scripts/windows/scan_dll.py package/imageformats/*.dll "package/"
|
||||
python3 .ci/scripts/windows/scan_dll.py package/*.exe package/imageformats/*.dll "package/"
|
||||
|
||||
# copy FFmpeg libraries
|
||||
EXTERNALS_PATH="$(pwd)/build/externals"
|
||||
FFMPEG_DLL_PATH="$(find ${EXTERNALS_PATH} -maxdepth 1 -type d | grep ffmpeg)/bin"
|
||||
FFMPEG_DLL_PATH="$(find "${EXTERNALS_PATH}" -maxdepth 1 -type d | grep 'ffmpeg-')/bin"
|
||||
find ${FFMPEG_DLL_PATH} -type f -regex ".*\.dll" -exec cp -v {} package/ ';'
|
||||
|
||||
# copy libraries from yuzu.exe path
|
||||
|
||||
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -34,12 +34,12 @@
|
||||
[submodule "opus"]
|
||||
path = externals/opus/opus
|
||||
url = https://github.com/xiph/opus.git
|
||||
[submodule "ffmpeg"]
|
||||
path = externals/ffmpeg
|
||||
url = https://git.ffmpeg.org/ffmpeg.git
|
||||
[submodule "SDL"]
|
||||
path = externals/SDL
|
||||
url = https://github.com/libsdl-org/SDL.git
|
||||
[submodule "externals/cpp-httplib"]
|
||||
path = externals/cpp-httplib
|
||||
url = https://github.com/yhirose/cpp-httplib.git
|
||||
[submodule "externals/ffmpeg/ffmpeg"]
|
||||
path = externals/ffmpeg/ffmpeg
|
||||
url = https://git.ffmpeg.org/ffmpeg.git
|
||||
|
||||
224
CMakeLists.txt
224
CMakeLists.txt
@@ -229,7 +229,7 @@ elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR YUZU_USE_BUNDLED_BOOST)
|
||||
include_directories(SYSTEM "${Boost_INCLUDE_DIRS}")
|
||||
else()
|
||||
message(STATUS "Boost 1.73.0 or newer not found, falling back to Conan")
|
||||
list(APPEND CONAN_REQUIRED_LIBS "boost/1.73.0")
|
||||
list(APPEND CONAN_REQUIRED_LIBS "boost/1.78.0")
|
||||
endif()
|
||||
|
||||
# Attempt to locate any packages that are required and report the missing ones in CONAN_REQUIRED_LIBS
|
||||
@@ -249,7 +249,7 @@ if(ENABLE_QT)
|
||||
# Check for system Qt on Linux, fallback to bundled Qt
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
if (NOT YUZU_USE_BUNDLED_QT)
|
||||
find_package(Qt5 ${QT_VERSION} COMPONENTS Widgets)
|
||||
find_package(Qt5 ${QT_VERSION} COMPONENTS Widgets DBus)
|
||||
endif()
|
||||
if (NOT Qt5_FOUND OR YUZU_USE_BUNDLED_QT)
|
||||
# Check for dependencies, then enable bundled Qt download
|
||||
@@ -508,13 +508,13 @@ set(FFmpeg_COMPONENTS
|
||||
avutil
|
||||
swscale)
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
if (UNIX AND NOT APPLE)
|
||||
Include(FindPkgConfig REQUIRED)
|
||||
pkg_check_modules(LIBVA libva)
|
||||
endif()
|
||||
if (NOT YUZU_USE_BUNDLED_FFMPEG)
|
||||
# Use system installed FFmpeg
|
||||
find_package(FFmpeg QUIET COMPONENTS ${FFmpeg_COMPONENTS})
|
||||
find_package(FFmpeg 4.3 QUIET COMPONENTS ${FFmpeg_COMPONENTS})
|
||||
|
||||
if (FFmpeg_FOUND)
|
||||
# Overwrite aggregate defines from FFmpeg module to avoid over-linking libraries.
|
||||
@@ -527,225 +527,11 @@ if (NOT YUZU_USE_BUNDLED_FFMPEG)
|
||||
set(FFmpeg_INCLUDE_DIR ${FFmpeg_INCLUDE_DIR} ${FFmpeg_INCLUDE_${COMPONENT}} CACHE PATH "Path to FFmpeg headers" FORCE)
|
||||
endforeach()
|
||||
else()
|
||||
message(WARNING "FFmpeg not found, falling back to externals")
|
||||
message(WARNING "FFmpeg not found or too old, falling back to externals")
|
||||
set(YUZU_USE_BUNDLED_FFMPEG ON)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (YUZU_USE_BUNDLED_FFMPEG)
|
||||
if (NOT WIN32)
|
||||
# TODO(lat9nq): Move this to externals/ffmpeg/CMakeLists.txt (and move externals/ffmpeg to
|
||||
# externals/ffmpeg/ffmpeg)
|
||||
|
||||
# Build FFmpeg from externals
|
||||
message(STATUS "Using FFmpeg from externals")
|
||||
|
||||
# FFmpeg has source that requires one of nasm or yasm to assemble it.
|
||||
# REQUIRED throws an error if not found here during configuration rather than during compilation.
|
||||
find_program(ASSEMBLER NAMES nasm yasm)
|
||||
if ("${ASSEMBLER}" STREQUAL "ASSEMBLER-NOTFOUND")
|
||||
message(FATAL_ERROR "One of either `nasm` or `yasm` not found but is required.")
|
||||
endif()
|
||||
|
||||
find_program(AUTOCONF autoconf)
|
||||
if ("${AUTOCONF}" STREQUAL "AUTOCONF-NOTFOUND")
|
||||
message(FATAL_ERROR "Required program `autoconf` not found.")
|
||||
endif()
|
||||
|
||||
set(FFmpeg_PREFIX ${PROJECT_SOURCE_DIR}/externals/ffmpeg)
|
||||
set(FFmpeg_BUILD_DIR ${PROJECT_BINARY_DIR}/externals/ffmpeg)
|
||||
set(FFmpeg_MAKEFILE ${FFmpeg_BUILD_DIR}/Makefile)
|
||||
make_directory(${FFmpeg_BUILD_DIR})
|
||||
|
||||
# Read version string from external
|
||||
file(READ ${FFmpeg_PREFIX}/RELEASE FFmpeg_VERSION)
|
||||
set(FFmpeg_FOUND NO)
|
||||
if (NOT FFmpeg_VERSION STREQUAL "")
|
||||
set(FFmpeg_FOUND YES)
|
||||
endif()
|
||||
|
||||
unset(FFmpeg_LIBRARIES CACHE)
|
||||
foreach(COMPONENT ${FFmpeg_COMPONENTS})
|
||||
set(FFmpeg_${COMPONENT}_PREFIX "${FFmpeg_BUILD_DIR}/lib${COMPONENT}")
|
||||
set(FFmpeg_${COMPONENT}_LIB_NAME "lib${COMPONENT}.a")
|
||||
set(FFmpeg_${COMPONENT}_LIBRARY "${FFmpeg_${COMPONENT}_PREFIX}/${FFmpeg_${COMPONENT}_LIB_NAME}")
|
||||
|
||||
set(FFmpeg_LIBRARIES
|
||||
${FFmpeg_LIBRARIES}
|
||||
${FFmpeg_${COMPONENT}_LIBRARY}
|
||||
CACHE PATH "Paths to FFmpeg libraries" FORCE)
|
||||
endforeach()
|
||||
|
||||
Include(FindPkgConfig REQUIRED)
|
||||
pkg_check_modules(LIBVA libva)
|
||||
pkg_check_modules(CUDA cuda)
|
||||
pkg_check_modules(FFNVCODEC ffnvcodec)
|
||||
pkg_check_modules(VDPAU vdpau)
|
||||
|
||||
set(FFmpeg_HWACCEL_LIBRARIES)
|
||||
set(FFmpeg_HWACCEL_FLAGS)
|
||||
set(FFmpeg_HWACCEL_INCLUDE_DIRS)
|
||||
set(FFmpeg_HWACCEL_LDFLAGS)
|
||||
|
||||
if(LIBVA_FOUND)
|
||||
pkg_check_modules(LIBDRM libdrm REQUIRED)
|
||||
find_package(X11 REQUIRED)
|
||||
pkg_check_modules(LIBVA-DRM libva-drm REQUIRED)
|
||||
pkg_check_modules(LIBVA-X11 libva-x11 REQUIRED)
|
||||
list(APPEND FFmpeg_HWACCEL_LIBRARIES
|
||||
${LIBDRM_LIBRARIES}
|
||||
${X11_LIBRARIES}
|
||||
${LIBVA-DRM_LIBRARIES}
|
||||
${LIBVA-X11_LIBRARIES}
|
||||
${LIBVA_LIBRARIES})
|
||||
set(FFmpeg_HWACCEL_FLAGS
|
||||
--enable-hwaccel=h264_vaapi
|
||||
--enable-hwaccel=vp8_vaapi
|
||||
--enable-hwaccel=vp9_vaapi
|
||||
--enable-libdrm)
|
||||
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS
|
||||
${LIBDRM_INCLUDE_DIRS}
|
||||
${X11_INCLUDE_DIRS}
|
||||
${LIBVA-DRM_INCLUDE_DIRS}
|
||||
${LIBVA-X11_INCLUDE_DIRS}
|
||||
${LIBVA_INCLUDE_DIRS}
|
||||
)
|
||||
message(STATUS "VA-API found")
|
||||
else()
|
||||
set(FFmpeg_HWACCEL_FLAGS --disable-vaapi)
|
||||
endif()
|
||||
|
||||
if (FFNVCODEC_FOUND AND CUDA_FOUND)
|
||||
list(APPEND FFmpeg_HWACCEL_FLAGS
|
||||
--enable-cuvid
|
||||
--enable-ffnvcodec
|
||||
--enable-nvdec
|
||||
--enable-hwaccel=h264_nvdec
|
||||
--enable-hwaccel=vp8_nvdec
|
||||
--enable-hwaccel=vp9_nvdec
|
||||
--extra-cflags=-I${CUDA_INCLUDE_DIRS}
|
||||
)
|
||||
list(APPEND FFmpeg_HWACCEL_LIBRARIES
|
||||
${FFNVCODEC_LIBRARIES}
|
||||
${CUDA_LIBRARIES}
|
||||
)
|
||||
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS
|
||||
${FFNVCODEC_INCLUDE_DIRS}
|
||||
${CUDA_INCLUDE_DIRS}
|
||||
)
|
||||
list(APPEND FFmpeg_HWACCEL_LDFLAGS
|
||||
${FFNVCODEC_LDFLAGS}
|
||||
${CUDA_LDFLAGS}
|
||||
)
|
||||
message(STATUS "ffnvcodec libraries version ${FFNVCODEC_VERSION} found")
|
||||
endif()
|
||||
|
||||
if (VDPAU_FOUND)
|
||||
list(APPEND FFmpeg_HWACCEL_FLAGS
|
||||
--enable-vdpau
|
||||
--enable-hwaccel=h264_vdpau
|
||||
--enable-hwaccel=vp9_vdpau
|
||||
)
|
||||
list(APPEND FFmpeg_HWACCEL_LIBRARIES ${VDPAU_LIBRARIES})
|
||||
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS ${VDPAU_INCLUDE_DIRS})
|
||||
list(APPEND FFmpeg_HWACCEL_LDFLAGS ${VDPAU_LDFLAGS})
|
||||
message(STATUS "vdpau libraries version ${VDPAU_VERSION} found")
|
||||
else()
|
||||
list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vdpau)
|
||||
endif()
|
||||
|
||||
# `configure` parameters builds only exactly what yuzu needs from FFmpeg
|
||||
# `--disable-vdpau` is needed to avoid linking issues
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${FFmpeg_MAKEFILE}
|
||||
COMMAND
|
||||
/bin/bash ${FFmpeg_PREFIX}/configure
|
||||
--disable-avdevice
|
||||
--disable-avfilter
|
||||
--disable-avformat
|
||||
--disable-doc
|
||||
--disable-everything
|
||||
--disable-ffmpeg
|
||||
--disable-ffprobe
|
||||
--disable-network
|
||||
--disable-postproc
|
||||
--disable-swresample
|
||||
--enable-decoder=h264
|
||||
--enable-decoder=vp8
|
||||
--enable-decoder=vp9
|
||||
--cc="${CMAKE_C_COMPILER}"
|
||||
--cxx="${CMAKE_CXX_COMPILER}"
|
||||
${FFmpeg_HWACCEL_FLAGS}
|
||||
WORKING_DIRECTORY
|
||||
${FFmpeg_BUILD_DIR}
|
||||
)
|
||||
unset(FFmpeg_HWACCEL_FLAGS)
|
||||
|
||||
# Workaround for Ubuntu 18.04's older version of make not being able to call make as a child
|
||||
# with context of the jobserver. Also helps ninja users.
|
||||
execute_process(
|
||||
COMMAND
|
||||
nproc
|
||||
OUTPUT_VARIABLE
|
||||
SYSTEM_THREADS)
|
||||
|
||||
set(FFmpeg_BUILD_LIBRARIES ${FFmpeg_LIBRARIES})
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${FFmpeg_BUILD_LIBRARIES}
|
||||
COMMAND
|
||||
make -j${SYSTEM_THREADS}
|
||||
WORKING_DIRECTORY
|
||||
${FFmpeg_BUILD_DIR}
|
||||
)
|
||||
|
||||
set(FFmpeg_INCLUDE_DIR
|
||||
"${FFmpeg_PREFIX};${FFmpeg_BUILD_DIR};${FFmpeg_HWACCEL_INCLUDE_DIRS}"
|
||||
CACHE PATH "Path to FFmpeg headers" FORCE)
|
||||
|
||||
set(FFmpeg_LDFLAGS
|
||||
"${FFmpeg_HWACCEL_LDFLAGS}"
|
||||
CACHE STRING "FFmpeg linker flags" FORCE)
|
||||
|
||||
# ALL makes this custom target build every time
|
||||
# but it won't actually build if the DEPENDS parameter is up to date
|
||||
add_custom_target(ffmpeg-configure ALL DEPENDS ${FFmpeg_MAKEFILE})
|
||||
add_custom_target(ffmpeg-build ALL DEPENDS ${FFmpeg_BUILD_LIBRARIES} ffmpeg-configure)
|
||||
link_libraries(${FFmpeg_LIBVA_LIBRARIES})
|
||||
set(FFmpeg_LIBRARIES ${FFmpeg_BUILD_LIBRARIES} ${FFmpeg_HWACCEL_LIBRARIES}
|
||||
CACHE PATH "Paths to FFmpeg libraries" FORCE)
|
||||
unset(FFmpeg_BUILD_LIBRARIES)
|
||||
unset(FFmpeg_HWACCEL_FLAGS)
|
||||
unset(FFmpeg_HWACCEL_INCLUDE_DIRS)
|
||||
unset(FFmpeg_HWACCEL_LDFLAGS)
|
||||
unset(FFmpeg_HWACCEL_LIBRARIES)
|
||||
|
||||
if (FFmpeg_FOUND)
|
||||
message(STATUS "Found FFmpeg version ${FFmpeg_VERSION}")
|
||||
else()
|
||||
message(FATAL_ERROR "FFmpeg not found")
|
||||
endif()
|
||||
else() # WIN32
|
||||
# Use yuzu FFmpeg binaries
|
||||
set(FFmpeg_EXT_NAME "ffmpeg-4.4")
|
||||
set(FFmpeg_PATH "${CMAKE_BINARY_DIR}/externals/${FFmpeg_EXT_NAME}")
|
||||
download_bundled_external("ffmpeg/" ${FFmpeg_EXT_NAME} "")
|
||||
set(FFmpeg_FOUND YES)
|
||||
set(FFmpeg_INCLUDE_DIR "${FFmpeg_PATH}/include" CACHE PATH "Path to FFmpeg headers" FORCE)
|
||||
set(FFmpeg_LIBRARY_DIR "${FFmpeg_PATH}/bin" CACHE PATH "Path to FFmpeg library directory" FORCE)
|
||||
set(FFmpeg_LDFLAGS "" CACHE STRING "FFmpeg linker flags" FORCE)
|
||||
set(FFmpeg_DLL_DIR "${FFmpeg_PATH}/bin" CACHE PATH "Path to FFmpeg dll's" FORCE)
|
||||
set(FFmpeg_LIBRARIES
|
||||
${FFmpeg_LIBRARY_DIR}/swscale.lib
|
||||
${FFmpeg_LIBRARY_DIR}/avcodec.lib
|
||||
${FFmpeg_LIBRARY_DIR}/avutil.lib
|
||||
CACHE PATH "Paths to FFmpeg libraries" FORCE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
unset(FFmpeg_COMPONENTS)
|
||||
|
||||
# Prefer the -pthread flag on Linux.
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
@@ -11,9 +11,15 @@ find_package(Git QUIET PATHS "${GIT_EXECUTABLE}")
|
||||
|
||||
# generate git/build information
|
||||
include(GetGitRevisionDescription)
|
||||
get_git_head_revision(GIT_REF_SPEC GIT_REV)
|
||||
git_describe(GIT_DESC --always --long --dirty)
|
||||
git_branch_name(GIT_BRANCH)
|
||||
if(NOT GIT_REF_SPEC)
|
||||
get_git_head_revision(GIT_REF_SPEC GIT_REV)
|
||||
endif()
|
||||
if(NOT GIT_DESC)
|
||||
git_describe(GIT_DESC --always --long --dirty)
|
||||
endif()
|
||||
if (NOT GIT_BRANCH)
|
||||
git_branch_name(GIT_BRANCH)
|
||||
endif()
|
||||
get_timestamp(BUILD_DATE)
|
||||
|
||||
# Generate cpp with Git revision from template
|
||||
|
||||
2
dist/yuzu.desktop
vendored
2
dist/yuzu.desktop
vendored
@@ -8,5 +8,5 @@ Icon=yuzu
|
||||
TryExec=yuzu
|
||||
Exec=yuzu %f
|
||||
Categories=Game;Emulator;Qt;
|
||||
MimeType=application/x-nx-nro;application/x-nx-nso;
|
||||
MimeType=application/x-nx-nro;application/x-nx-nso;application/x-nx-nsp;application/x-nx-xci;
|
||||
Keywords=Switch;Nintendo;
|
||||
15
dist/yuzu.xml
vendored
15
dist/yuzu.xml
vendored
@@ -15,4 +15,19 @@
|
||||
<glob pattern="*.nso"/>
|
||||
<magic><match value="NSO" type="string" offset="0"/></magic>
|
||||
</mime-type>
|
||||
|
||||
<mime-type type="application/x-nx-nsp">
|
||||
<comment>Nintendo Switch Package</comment>
|
||||
<acronym>NSP</acronym>
|
||||
<icon name="yuzu"/>
|
||||
<glob pattern="*.nsp"/>
|
||||
<magic><match value="PFS" type="string" offset="0"/></magic>
|
||||
</mime-type>
|
||||
|
||||
<mime-type type="application/x-nx-xci">
|
||||
<comment>Nintendo Switch Card Image</comment>
|
||||
<acronym>XCI</acronym>
|
||||
<icon name="yuzu"/>
|
||||
<glob pattern="*.xci"/>
|
||||
</mime-type>
|
||||
</mime-info>
|
||||
16
externals/CMakeLists.txt
vendored
16
externals/CMakeLists.txt
vendored
@@ -52,11 +52,12 @@ endif()
|
||||
# SDL2
|
||||
if (YUZU_USE_EXTERNAL_SDL2)
|
||||
if (NOT WIN32)
|
||||
# Yuzu itself needs: Events Joystick Haptic Sensor Timers Audio
|
||||
# Yuzu itself needs: Atomic Audio Events Joystick Haptic Sensor Threads Timers
|
||||
# Since 2.0.18 Atomic+Threads required for HIDAPI/libusb (see https://github.com/libsdl-org/SDL/issues/5095)
|
||||
# Yuzu-cmd also needs: Video (depends on Loadso/Dlopen)
|
||||
set(SDL_UNUSED_SUBSYSTEMS
|
||||
Atomic Render Power Threads
|
||||
File CPUinfo Filesystem Locale)
|
||||
CPUinfo File Filesystem
|
||||
Locale Power Render)
|
||||
foreach(_SUB ${SDL_UNUSED_SUBSYSTEMS})
|
||||
string(TOUPPER ${_SUB} _OPT)
|
||||
option(SDL_${_OPT} "" OFF)
|
||||
@@ -121,3 +122,12 @@ if (NOT opus_FOUND)
|
||||
message(STATUS "opus 1.3 or newer not found, falling back to externals")
|
||||
add_subdirectory(opus EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
|
||||
# FFMpeg
|
||||
if (YUZU_USE_BUNDLED_FFMPEG)
|
||||
add_subdirectory(ffmpeg)
|
||||
set(FFmpeg_PATH "${FFmpeg_PATH}" PARENT_SCOPE)
|
||||
set(FFmpeg_LDFLAGS "${FFmpeg_LDFLAGS}" PARENT_SCOPE)
|
||||
set(FFmpeg_LIBRARIES "${FFmpeg_LIBRARIES}" PARENT_SCOPE)
|
||||
set(FFmpeg_INCLUDE_DIR "${FFmpeg_INCLUDE_DIR}" PARENT_SCOPE)
|
||||
endif()
|
||||
|
||||
2
externals/SDL
vendored
2
externals/SDL
vendored
Submodule externals/SDL updated: 2e9821423a...e2ade2bfc4
2
externals/Vulkan-Headers
vendored
2
externals/Vulkan-Headers
vendored
Submodule externals/Vulkan-Headers updated: 07c4a37bcf...e005e1f817
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: cce7e4ee5d...28714ee75a
1
externals/ffmpeg
vendored
1
externals/ffmpeg
vendored
Submodule externals/ffmpeg deleted from 79e8d17024
214
externals/ffmpeg/CMakeLists.txt
vendored
Normal file
214
externals/ffmpeg/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,214 @@
|
||||
if (NOT WIN32)
|
||||
# Build FFmpeg from externals
|
||||
message(STATUS "Using FFmpeg from externals")
|
||||
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "(x86_64|amd64)")
|
||||
# FFmpeg has source that requires one of nasm or yasm to assemble it.
|
||||
# REQUIRED throws an error if not found here during configuration rather than during compilation.
|
||||
find_program(ASSEMBLER NAMES nasm yasm)
|
||||
if ("${ASSEMBLER}" STREQUAL "ASSEMBLER-NOTFOUND")
|
||||
message(FATAL_ERROR "One of either `nasm` or `yasm` not found but is required.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_program(AUTOCONF autoconf)
|
||||
if ("${AUTOCONF}" STREQUAL "AUTOCONF-NOTFOUND")
|
||||
message(FATAL_ERROR "Required program `autoconf` not found.")
|
||||
endif()
|
||||
|
||||
set(FFmpeg_PREFIX ${PROJECT_SOURCE_DIR}/externals/ffmpeg/ffmpeg)
|
||||
set(FFmpeg_BUILD_DIR ${PROJECT_BINARY_DIR}/externals/ffmpeg-build)
|
||||
set(FFmpeg_MAKEFILE ${FFmpeg_BUILD_DIR}/Makefile)
|
||||
make_directory(${FFmpeg_BUILD_DIR})
|
||||
|
||||
# Read version string from external
|
||||
file(READ ${FFmpeg_PREFIX}/RELEASE FFmpeg_VERSION)
|
||||
set(FFmpeg_FOUND NO)
|
||||
if (NOT FFmpeg_VERSION STREQUAL "")
|
||||
set(FFmpeg_FOUND YES)
|
||||
endif()
|
||||
|
||||
unset(FFmpeg_LIBRARIES CACHE)
|
||||
foreach(COMPONENT ${FFmpeg_COMPONENTS})
|
||||
set(FFmpeg_${COMPONENT}_PREFIX "${FFmpeg_BUILD_DIR}/lib${COMPONENT}")
|
||||
set(FFmpeg_${COMPONENT}_LIB_NAME "lib${COMPONENT}.a")
|
||||
set(FFmpeg_${COMPONENT}_LIBRARY "${FFmpeg_${COMPONENT}_PREFIX}/${FFmpeg_${COMPONENT}_LIB_NAME}")
|
||||
|
||||
set(FFmpeg_LIBRARIES
|
||||
${FFmpeg_LIBRARIES}
|
||||
${FFmpeg_${COMPONENT}_LIBRARY}
|
||||
CACHE PATH "Paths to FFmpeg libraries" FORCE)
|
||||
endforeach()
|
||||
|
||||
Include(FindPkgConfig REQUIRED)
|
||||
pkg_check_modules(LIBVA libva)
|
||||
pkg_check_modules(CUDA cuda)
|
||||
pkg_check_modules(FFNVCODEC ffnvcodec)
|
||||
pkg_check_modules(VDPAU vdpau)
|
||||
|
||||
set(FFmpeg_HWACCEL_LIBRARIES)
|
||||
set(FFmpeg_HWACCEL_FLAGS)
|
||||
set(FFmpeg_HWACCEL_INCLUDE_DIRS)
|
||||
set(FFmpeg_HWACCEL_LDFLAGS)
|
||||
|
||||
if(LIBVA_FOUND)
|
||||
pkg_check_modules(LIBDRM libdrm REQUIRED)
|
||||
find_package(X11 REQUIRED)
|
||||
pkg_check_modules(LIBVA-DRM libva-drm REQUIRED)
|
||||
pkg_check_modules(LIBVA-X11 libva-x11 REQUIRED)
|
||||
list(APPEND FFmpeg_HWACCEL_LIBRARIES
|
||||
${LIBDRM_LIBRARIES}
|
||||
${X11_LIBRARIES}
|
||||
${LIBVA-DRM_LIBRARIES}
|
||||
${LIBVA-X11_LIBRARIES}
|
||||
${LIBVA_LIBRARIES})
|
||||
set(FFmpeg_HWACCEL_FLAGS
|
||||
--enable-hwaccel=h264_vaapi
|
||||
--enable-hwaccel=vp8_vaapi
|
||||
--enable-hwaccel=vp9_vaapi
|
||||
--enable-libdrm)
|
||||
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS
|
||||
${LIBDRM_INCLUDE_DIRS}
|
||||
${X11_INCLUDE_DIRS}
|
||||
${LIBVA-DRM_INCLUDE_DIRS}
|
||||
${LIBVA-X11_INCLUDE_DIRS}
|
||||
${LIBVA_INCLUDE_DIRS}
|
||||
)
|
||||
message(STATUS "VA-API found")
|
||||
else()
|
||||
set(FFmpeg_HWACCEL_FLAGS --disable-vaapi)
|
||||
endif()
|
||||
|
||||
if (FFNVCODEC_FOUND)
|
||||
list(APPEND FFmpeg_HWACCEL_FLAGS
|
||||
--enable-cuvid
|
||||
--enable-ffnvcodec
|
||||
--enable-nvdec
|
||||
--enable-hwaccel=h264_nvdec
|
||||
--enable-hwaccel=vp8_nvdec
|
||||
--enable-hwaccel=vp9_nvdec
|
||||
)
|
||||
list(APPEND FFmpeg_HWACCEL_LIBRARIES ${FFNVCODEC_LIBRARIES})
|
||||
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS ${FFNVCODEC_INCLUDE_DIRS})
|
||||
list(APPEND FFmpeg_HWACCEL_LDFLAGS ${FFNVCODEC_LDFLAGS})
|
||||
message(STATUS "ffnvcodec libraries version ${FFNVCODEC_VERSION} found")
|
||||
# ffnvenc could load CUDA libraries at the runtime using dlopen/dlsym or LoadLibrary/GetProcAddress
|
||||
# here we handle the hard-linking senario where CUDA is linked during compilation
|
||||
if (CUDA_FOUND)
|
||||
list(APPEND FFmpeg_HWACCEL_FLAGS --extra-cflags=-I${CUDA_INCLUDE_DIRS})
|
||||
list(APPEND FFmpeg_HWACCEL_LIBRARIES ${CUDA_LIBRARIES})
|
||||
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS ${CUDA_INCLUDE_DIRS})
|
||||
list(APPEND FFmpeg_HWACCEL_LDFLAGS ${CUDA_LDFLAGS})
|
||||
message(STATUS "CUDA libraries found, hard-linking will be performed")
|
||||
endif(CUDA_FOUND)
|
||||
endif()
|
||||
|
||||
if (VDPAU_FOUND)
|
||||
list(APPEND FFmpeg_HWACCEL_FLAGS
|
||||
--enable-vdpau
|
||||
--enable-hwaccel=h264_vdpau
|
||||
--enable-hwaccel=vp9_vdpau
|
||||
)
|
||||
list(APPEND FFmpeg_HWACCEL_LIBRARIES ${VDPAU_LIBRARIES})
|
||||
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS ${VDPAU_INCLUDE_DIRS})
|
||||
list(APPEND FFmpeg_HWACCEL_LDFLAGS ${VDPAU_LDFLAGS})
|
||||
message(STATUS "vdpau libraries version ${VDPAU_VERSION} found")
|
||||
else()
|
||||
list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vdpau)
|
||||
endif()
|
||||
|
||||
# `configure` parameters builds only exactly what yuzu needs from FFmpeg
|
||||
# `--disable-vdpau` is needed to avoid linking issues
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${FFmpeg_MAKEFILE}
|
||||
COMMAND
|
||||
/bin/bash ${FFmpeg_PREFIX}/configure
|
||||
--disable-avdevice
|
||||
--disable-avfilter
|
||||
--disable-avformat
|
||||
--disable-doc
|
||||
--disable-everything
|
||||
--disable-ffmpeg
|
||||
--disable-ffprobe
|
||||
--disable-network
|
||||
--disable-postproc
|
||||
--disable-swresample
|
||||
--enable-decoder=h264
|
||||
--enable-decoder=vp8
|
||||
--enable-decoder=vp9
|
||||
--cc="${CMAKE_C_COMPILER}"
|
||||
--cxx="${CMAKE_CXX_COMPILER}"
|
||||
${FFmpeg_HWACCEL_FLAGS}
|
||||
WORKING_DIRECTORY
|
||||
${FFmpeg_BUILD_DIR}
|
||||
)
|
||||
unset(FFmpeg_HWACCEL_FLAGS)
|
||||
|
||||
# Workaround for Ubuntu 18.04's older version of make not being able to call make as a child
|
||||
# with context of the jobserver. Also helps ninja users.
|
||||
execute_process(
|
||||
COMMAND
|
||||
nproc
|
||||
OUTPUT_VARIABLE
|
||||
SYSTEM_THREADS)
|
||||
|
||||
set(FFmpeg_BUILD_LIBRARIES ${FFmpeg_LIBRARIES})
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${FFmpeg_BUILD_LIBRARIES}
|
||||
COMMAND
|
||||
make -j${SYSTEM_THREADS}
|
||||
WORKING_DIRECTORY
|
||||
${FFmpeg_BUILD_DIR}
|
||||
)
|
||||
|
||||
set(FFmpeg_INCLUDE_DIR
|
||||
"${FFmpeg_PREFIX};${FFmpeg_BUILD_DIR};${FFmpeg_HWACCEL_INCLUDE_DIRS}"
|
||||
CACHE PATH "Path to FFmpeg headers" FORCE)
|
||||
|
||||
set(FFmpeg_LDFLAGS
|
||||
"${FFmpeg_HWACCEL_LDFLAGS}"
|
||||
CACHE STRING "FFmpeg linker flags" FORCE)
|
||||
|
||||
# ALL makes this custom target build every time
|
||||
# but it won't actually build if the DEPENDS parameter is up to date
|
||||
add_custom_target(ffmpeg-configure ALL DEPENDS ${FFmpeg_MAKEFILE})
|
||||
add_custom_target(ffmpeg-build ALL DEPENDS ${FFmpeg_BUILD_LIBRARIES} ffmpeg-configure)
|
||||
link_libraries(${FFmpeg_LIBVA_LIBRARIES})
|
||||
set(FFmpeg_LIBRARIES ${FFmpeg_BUILD_LIBRARIES} ${FFmpeg_HWACCEL_LIBRARIES}
|
||||
CACHE PATH "Paths to FFmpeg libraries" FORCE)
|
||||
unset(FFmpeg_BUILD_LIBRARIES)
|
||||
unset(FFmpeg_HWACCEL_FLAGS)
|
||||
unset(FFmpeg_HWACCEL_INCLUDE_DIRS)
|
||||
unset(FFmpeg_HWACCEL_LDFLAGS)
|
||||
unset(FFmpeg_HWACCEL_LIBRARIES)
|
||||
|
||||
if (FFmpeg_FOUND)
|
||||
message(STATUS "Found FFmpeg version ${FFmpeg_VERSION}")
|
||||
else()
|
||||
message(FATAL_ERROR "FFmpeg not found")
|
||||
endif()
|
||||
else(WIN32)
|
||||
# Use yuzu FFmpeg binaries
|
||||
set(FFmpeg_EXT_NAME "ffmpeg-4.4")
|
||||
set(FFmpeg_PATH "${CMAKE_BINARY_DIR}/externals/${FFmpeg_EXT_NAME}")
|
||||
download_bundled_external("ffmpeg/" ${FFmpeg_EXT_NAME} "")
|
||||
set(FFmpeg_FOUND YES)
|
||||
set(FFmpeg_INCLUDE_DIR "${FFmpeg_PATH}/include" CACHE PATH "Path to FFmpeg headers" FORCE)
|
||||
set(FFmpeg_LIBRARY_DIR "${FFmpeg_PATH}/bin" CACHE PATH "Path to FFmpeg library directory" FORCE)
|
||||
set(FFmpeg_LDFLAGS "" CACHE STRING "FFmpeg linker flags" FORCE)
|
||||
set(FFmpeg_DLL_DIR "${FFmpeg_PATH}/bin" CACHE PATH "Path to FFmpeg dll's" FORCE)
|
||||
set(FFmpeg_LIBRARIES
|
||||
${FFmpeg_LIBRARY_DIR}/swscale.lib
|
||||
${FFmpeg_LIBRARY_DIR}/avcodec.lib
|
||||
${FFmpeg_LIBRARY_DIR}/avutil.lib
|
||||
CACHE PATH "Paths to FFmpeg libraries" FORCE)
|
||||
# exported variables
|
||||
set(FFmpeg_PATH "${FFmpeg_PATH}" PARENT_SCOPE)
|
||||
set(FFmpeg_LDFLAGS "${FFmpeg_LDFLAGS}" PARENT_SCOPE)
|
||||
set(FFmpeg_LIBRARIES "${FFmpeg_LIBRARIES}" PARENT_SCOPE)
|
||||
set(FFmpeg_INCLUDE_DIR "${FFmpeg_INCLUDE_DIR}" PARENT_SCOPE)
|
||||
endif(WIN32)
|
||||
|
||||
unset(FFmpeg_COMPONENTS)
|
||||
1
externals/ffmpeg/ffmpeg
vendored
Submodule
1
externals/ffmpeg/ffmpeg
vendored
Submodule
Submodule externals/ffmpeg/ffmpeg added at dc91b913b6
@@ -22,6 +22,11 @@ add_custom_command(OUTPUT scm_rev.cpp
|
||||
-DTITLE_BAR_FORMAT_RUNNING=${TITLE_BAR_FORMAT_RUNNING}
|
||||
-DBUILD_TAG=${BUILD_TAG}
|
||||
-DBUILD_ID=${DISPLAY_VERSION}
|
||||
-DGIT_REF_SPEC=${GIT_REF_SPEC}
|
||||
-DGIT_REV=${GIT_REV}
|
||||
-DGIT_DESC=${GIT_DESC}
|
||||
-DGIT_BRANCH=${GIT_BRANCH}
|
||||
-DBUILD_FULLNAME=${BUILD_FULLNAME}
|
||||
-DGIT_EXECUTABLE=${GIT_EXECUTABLE}
|
||||
-P ${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake
|
||||
DEPENDS
|
||||
|
||||
@@ -227,7 +227,7 @@ struct CallbackStatus {
|
||||
|
||||
// Triggered once every input change
|
||||
struct InputCallback {
|
||||
std::function<void(CallbackStatus)> on_change;
|
||||
std::function<void(const CallbackStatus&)> on_change;
|
||||
};
|
||||
|
||||
/// An abstract class template for an input device (a button, an analog input, etc.).
|
||||
@@ -236,14 +236,10 @@ public:
|
||||
virtual ~InputDevice() = default;
|
||||
|
||||
// Request input device to update if necessary
|
||||
virtual void SoftUpdate() {
|
||||
return;
|
||||
}
|
||||
virtual void SoftUpdate() {}
|
||||
|
||||
// Force input device to update data regardless of the current state
|
||||
virtual void ForceUpdate() {
|
||||
return;
|
||||
}
|
||||
virtual void ForceUpdate() {}
|
||||
|
||||
// Sets the function to be triggered when input changes
|
||||
void SetCallback(InputCallback callback_) {
|
||||
@@ -251,7 +247,7 @@ public:
|
||||
}
|
||||
|
||||
// Triggers the function set in the callback
|
||||
void TriggerOnChange(CallbackStatus status) {
|
||||
void TriggerOnChange(const CallbackStatus& status) {
|
||||
if (callback.on_change) {
|
||||
callback.on_change(status);
|
||||
}
|
||||
@@ -266,11 +262,9 @@ class OutputDevice {
|
||||
public:
|
||||
virtual ~OutputDevice() = default;
|
||||
|
||||
virtual void SetLED([[maybe_unused]] LedStatus led_status) {
|
||||
return;
|
||||
}
|
||||
virtual void SetLED([[maybe_unused]] const LedStatus& led_status) {}
|
||||
|
||||
virtual VibrationError SetVibration([[maybe_unused]] VibrationStatus vibration_status) {
|
||||
virtual VibrationError SetVibration([[maybe_unused]] const VibrationStatus& vibration_status) {
|
||||
return VibrationError::NotSupported;
|
||||
}
|
||||
|
||||
|
||||
@@ -597,6 +597,7 @@ struct Values {
|
||||
BasicSetting<std::string> program_args{std::string(), "program_args"};
|
||||
BasicSetting<bool> dump_exefs{false, "dump_exefs"};
|
||||
BasicSetting<bool> dump_nso{false, "dump_nso"};
|
||||
BasicSetting<bool> dump_shaders{false, "dump_shaders"};
|
||||
BasicSetting<bool> enable_fs_access_log{false, "enable_fs_access_log"};
|
||||
BasicSetting<bool> reporting_services{false, "reporting_services"};
|
||||
BasicSetting<bool> quest_flag{false, "quest_flag"};
|
||||
|
||||
@@ -71,9 +71,6 @@ static CPUCaps Detect() {
|
||||
else
|
||||
caps.manufacturer = Manufacturer::Unknown;
|
||||
|
||||
u32 family = {};
|
||||
u32 model = {};
|
||||
|
||||
__cpuid(cpu_id, 0x80000000);
|
||||
|
||||
u32 max_ex_fn = cpu_id[0];
|
||||
@@ -84,15 +81,6 @@ static CPUCaps Detect() {
|
||||
// Detect family and other miscellaneous features
|
||||
if (max_std_fn >= 1) {
|
||||
__cpuid(cpu_id, 0x00000001);
|
||||
family = (cpu_id[0] >> 8) & 0xf;
|
||||
model = (cpu_id[0] >> 4) & 0xf;
|
||||
if (family == 0xf) {
|
||||
family += (cpu_id[0] >> 20) & 0xff;
|
||||
}
|
||||
if (family >= 6) {
|
||||
model += ((cpu_id[0] >> 16) & 0xf) << 4;
|
||||
}
|
||||
|
||||
if ((cpu_id[3] >> 25) & 1)
|
||||
caps.sse = true;
|
||||
if ((cpu_id[3] >> 26) & 1)
|
||||
|
||||
@@ -187,6 +187,8 @@ add_library(core STATIC
|
||||
hle/kernel/k_event.h
|
||||
hle/kernel/k_handle_table.cpp
|
||||
hle/kernel/k_handle_table.h
|
||||
hle/kernel/k_interrupt_manager.cpp
|
||||
hle/kernel/k_interrupt_manager.h
|
||||
hle/kernel/k_light_condition_variable.cpp
|
||||
hle/kernel/k_light_condition_variable.h
|
||||
hle/kernel/k_light_lock.cpp
|
||||
@@ -265,8 +267,6 @@ add_library(core STATIC
|
||||
hle/kernel/svc_wrap.h
|
||||
hle/kernel/time_manager.cpp
|
||||
hle/kernel/time_manager.h
|
||||
hle/lock.cpp
|
||||
hle/lock.h
|
||||
hle/result.h
|
||||
hle/service/acc/acc.cpp
|
||||
hle/service/acc/acc.h
|
||||
|
||||
@@ -45,26 +45,26 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb
|
||||
// Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld
|
||||
if (parameters.allow_pro_controller) {
|
||||
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
|
||||
controller->Connect();
|
||||
controller->Connect(true);
|
||||
} else if (parameters.allow_dual_joycons) {
|
||||
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconDual);
|
||||
controller->Connect();
|
||||
controller->Connect(true);
|
||||
} else if (parameters.allow_left_joycon && parameters.allow_right_joycon) {
|
||||
// Assign left joycons to even player indices and right joycons to odd player indices.
|
||||
// We do this since Captain Toad Treasure Tracker expects a left joycon for Player 1 and
|
||||
// a right Joycon for Player 2 in 2 Player Assist mode.
|
||||
if (index % 2 == 0) {
|
||||
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconLeft);
|
||||
controller->Connect();
|
||||
controller->Connect(true);
|
||||
} else {
|
||||
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconRight);
|
||||
controller->Connect();
|
||||
controller->Connect(true);
|
||||
}
|
||||
} else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld &&
|
||||
!Settings::values.use_docked_mode.GetValue()) {
|
||||
// We should *never* reach here under any normal circumstances.
|
||||
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
|
||||
controller->Connect();
|
||||
controller->Connect(true);
|
||||
} else {
|
||||
UNREACHABLE_MSG("Unable to add a new controller based on the given parameters!");
|
||||
}
|
||||
|
||||
@@ -66,9 +66,10 @@ void EmulatedConsole::ReloadInput() {
|
||||
|
||||
motion_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(motion_params);
|
||||
if (motion_devices) {
|
||||
Common::Input::InputCallback motion_callback{
|
||||
[this](Common::Input::CallbackStatus callback) { SetMotion(callback); }};
|
||||
motion_devices->SetCallback(motion_callback);
|
||||
motion_devices->SetCallback({
|
||||
.on_change =
|
||||
[this](const Common::Input::CallbackStatus& callback) { SetMotion(callback); },
|
||||
});
|
||||
}
|
||||
|
||||
// Unique index for identifying touch device source
|
||||
@@ -78,9 +79,12 @@ void EmulatedConsole::ReloadInput() {
|
||||
if (!touch_device) {
|
||||
continue;
|
||||
}
|
||||
Common::Input::InputCallback touch_callback{
|
||||
[this, index](Common::Input::CallbackStatus callback) { SetTouch(callback, index); }};
|
||||
touch_device->SetCallback(touch_callback);
|
||||
touch_device->SetCallback({
|
||||
.on_change =
|
||||
[this, index](const Common::Input::CallbackStatus& callback) {
|
||||
SetTouch(callback, index);
|
||||
},
|
||||
});
|
||||
index++;
|
||||
}
|
||||
}
|
||||
@@ -127,7 +131,7 @@ void EmulatedConsole::SetMotionParam(Common::ParamPackage param) {
|
||||
ReloadInput();
|
||||
}
|
||||
|
||||
void EmulatedConsole::SetMotion(Common::Input::CallbackStatus callback) {
|
||||
void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
|
||||
std::lock_guard lock{mutex};
|
||||
auto& raw_status = console.motion_values.raw_status;
|
||||
auto& emulated = console.motion_values.emulated;
|
||||
@@ -157,13 +161,15 @@ void EmulatedConsole::SetMotion(Common::Input::CallbackStatus callback) {
|
||||
motion.rotation = emulated.GetGyroscope();
|
||||
motion.orientation = emulated.GetOrientation();
|
||||
motion.quaternion = emulated.GetQuaternion();
|
||||
motion.gyro_bias = emulated.GetGyroBias();
|
||||
motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
|
||||
// Find what is this value
|
||||
motion.verticalization_error = 0.0f;
|
||||
|
||||
TriggerOnChange(ConsoleTriggerType::Motion);
|
||||
}
|
||||
|
||||
void EmulatedConsole::SetTouch(Common::Input::CallbackStatus callback,
|
||||
[[maybe_unused]] std::size_t index) {
|
||||
void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index) {
|
||||
if (index >= console.touch_values.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -50,6 +50,8 @@ struct ConsoleMotion {
|
||||
Common::Vec3f rotation{};
|
||||
std::array<Common::Vec3f, 3> orientation{};
|
||||
Common::Quaternion<f32> quaternion{};
|
||||
Common::Vec3f gyro_bias{};
|
||||
f32 verticalization_error{};
|
||||
bool is_at_rest{};
|
||||
};
|
||||
|
||||
@@ -155,14 +157,14 @@ private:
|
||||
* Updates the motion status of the console
|
||||
* @param callback A CallbackStatus containing gyro and accelerometer data
|
||||
*/
|
||||
void SetMotion(Common::Input::CallbackStatus callback);
|
||||
void SetMotion(const Common::Input::CallbackStatus& callback);
|
||||
|
||||
/**
|
||||
* Updates the touch status of the console
|
||||
* @param callback A CallbackStatus containing the touch position
|
||||
* @param index Finger ID to be updated
|
||||
*/
|
||||
void SetTouch(Common::Input::CallbackStatus callback, std::size_t index);
|
||||
void SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index);
|
||||
|
||||
/**
|
||||
* Triggers a callback that something has changed on the console status
|
||||
|
||||
@@ -205,11 +205,12 @@ void EmulatedController::ReloadInput() {
|
||||
continue;
|
||||
}
|
||||
const auto uuid = Common::UUID{button_params[index].Get("guid", "")};
|
||||
Common::Input::InputCallback button_callback{
|
||||
[this, index, uuid](Common::Input::CallbackStatus callback) {
|
||||
SetButton(callback, index, uuid);
|
||||
}};
|
||||
button_devices[index]->SetCallback(button_callback);
|
||||
button_devices[index]->SetCallback({
|
||||
.on_change =
|
||||
[this, index, uuid](const Common::Input::CallbackStatus& callback) {
|
||||
SetButton(callback, index, uuid);
|
||||
},
|
||||
});
|
||||
button_devices[index]->ForceUpdate();
|
||||
}
|
||||
|
||||
@@ -218,11 +219,12 @@ void EmulatedController::ReloadInput() {
|
||||
continue;
|
||||
}
|
||||
const auto uuid = Common::UUID{stick_params[index].Get("guid", "")};
|
||||
Common::Input::InputCallback stick_callback{
|
||||
[this, index, uuid](Common::Input::CallbackStatus callback) {
|
||||
SetStick(callback, index, uuid);
|
||||
}};
|
||||
stick_devices[index]->SetCallback(stick_callback);
|
||||
stick_devices[index]->SetCallback({
|
||||
.on_change =
|
||||
[this, index, uuid](const Common::Input::CallbackStatus& callback) {
|
||||
SetStick(callback, index, uuid);
|
||||
},
|
||||
});
|
||||
stick_devices[index]->ForceUpdate();
|
||||
}
|
||||
|
||||
@@ -231,11 +233,12 @@ void EmulatedController::ReloadInput() {
|
||||
continue;
|
||||
}
|
||||
const auto uuid = Common::UUID{trigger_params[index].Get("guid", "")};
|
||||
Common::Input::InputCallback trigger_callback{
|
||||
[this, index, uuid](Common::Input::CallbackStatus callback) {
|
||||
SetTrigger(callback, index, uuid);
|
||||
}};
|
||||
trigger_devices[index]->SetCallback(trigger_callback);
|
||||
trigger_devices[index]->SetCallback({
|
||||
.on_change =
|
||||
[this, index, uuid](const Common::Input::CallbackStatus& callback) {
|
||||
SetTrigger(callback, index, uuid);
|
||||
},
|
||||
});
|
||||
trigger_devices[index]->ForceUpdate();
|
||||
}
|
||||
|
||||
@@ -243,9 +246,12 @@ void EmulatedController::ReloadInput() {
|
||||
if (!battery_devices[index]) {
|
||||
continue;
|
||||
}
|
||||
Common::Input::InputCallback battery_callback{
|
||||
[this, index](Common::Input::CallbackStatus callback) { SetBattery(callback, index); }};
|
||||
battery_devices[index]->SetCallback(battery_callback);
|
||||
battery_devices[index]->SetCallback({
|
||||
.on_change =
|
||||
[this, index](const Common::Input::CallbackStatus& callback) {
|
||||
SetBattery(callback, index);
|
||||
},
|
||||
});
|
||||
battery_devices[index]->ForceUpdate();
|
||||
}
|
||||
|
||||
@@ -253,9 +259,12 @@ void EmulatedController::ReloadInput() {
|
||||
if (!motion_devices[index]) {
|
||||
continue;
|
||||
}
|
||||
Common::Input::InputCallback motion_callback{
|
||||
[this, index](Common::Input::CallbackStatus callback) { SetMotion(callback, index); }};
|
||||
motion_devices[index]->SetCallback(motion_callback);
|
||||
motion_devices[index]->SetCallback({
|
||||
.on_change =
|
||||
[this, index](const Common::Input::CallbackStatus& callback) {
|
||||
SetMotion(callback, index);
|
||||
},
|
||||
});
|
||||
motion_devices[index]->ForceUpdate();
|
||||
}
|
||||
|
||||
@@ -267,22 +276,24 @@ void EmulatedController::ReloadInput() {
|
||||
if (!tas_button_devices[index]) {
|
||||
continue;
|
||||
}
|
||||
Common::Input::InputCallback button_callback{
|
||||
[this, index, tas_uuid](Common::Input::CallbackStatus callback) {
|
||||
SetButton(callback, index, tas_uuid);
|
||||
}};
|
||||
tas_button_devices[index]->SetCallback(button_callback);
|
||||
tas_button_devices[index]->SetCallback({
|
||||
.on_change =
|
||||
[this, index, tas_uuid](const Common::Input::CallbackStatus& callback) {
|
||||
SetButton(callback, index, tas_uuid);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
for (std::size_t index = 0; index < tas_stick_devices.size(); ++index) {
|
||||
if (!tas_stick_devices[index]) {
|
||||
continue;
|
||||
}
|
||||
Common::Input::InputCallback stick_callback{
|
||||
[this, index, tas_uuid](Common::Input::CallbackStatus callback) {
|
||||
SetStick(callback, index, tas_uuid);
|
||||
}};
|
||||
tas_stick_devices[index]->SetCallback(stick_callback);
|
||||
tas_stick_devices[index]->SetCallback({
|
||||
.on_change =
|
||||
[this, index, tas_uuid](const Common::Input::CallbackStatus& callback) {
|
||||
SetStick(callback, index, tas_uuid);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -440,7 +451,7 @@ void EmulatedController::SetButtonParam(std::size_t index, Common::ParamPackage
|
||||
if (index >= button_params.size()) {
|
||||
return;
|
||||
}
|
||||
button_params[index] = param;
|
||||
button_params[index] = std::move(param);
|
||||
ReloadInput();
|
||||
}
|
||||
|
||||
@@ -448,7 +459,7 @@ void EmulatedController::SetStickParam(std::size_t index, Common::ParamPackage p
|
||||
if (index >= stick_params.size()) {
|
||||
return;
|
||||
}
|
||||
stick_params[index] = param;
|
||||
stick_params[index] = std::move(param);
|
||||
ReloadInput();
|
||||
}
|
||||
|
||||
@@ -456,11 +467,11 @@ void EmulatedController::SetMotionParam(std::size_t index, Common::ParamPackage
|
||||
if (index >= motion_params.size()) {
|
||||
return;
|
||||
}
|
||||
motion_params[index] = param;
|
||||
motion_params[index] = std::move(param);
|
||||
ReloadInput();
|
||||
}
|
||||
|
||||
void EmulatedController::SetButton(Common::Input::CallbackStatus callback, std::size_t index,
|
||||
void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback, std::size_t index,
|
||||
Common::UUID uuid) {
|
||||
if (index >= controller.button_values.size()) {
|
||||
return;
|
||||
@@ -600,7 +611,7 @@ void EmulatedController::SetButton(Common::Input::CallbackStatus callback, std::
|
||||
TriggerOnChange(ControllerTriggerType::Button, true);
|
||||
}
|
||||
|
||||
void EmulatedController::SetStick(Common::Input::CallbackStatus callback, std::size_t index,
|
||||
void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback, std::size_t index,
|
||||
Common::UUID uuid) {
|
||||
if (index >= controller.stick_values.size()) {
|
||||
return;
|
||||
@@ -650,8 +661,8 @@ void EmulatedController::SetStick(Common::Input::CallbackStatus callback, std::s
|
||||
TriggerOnChange(ControllerTriggerType::Stick, true);
|
||||
}
|
||||
|
||||
void EmulatedController::SetTrigger(Common::Input::CallbackStatus callback, std::size_t index,
|
||||
Common::UUID uuid) {
|
||||
void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callback,
|
||||
std::size_t index, Common::UUID uuid) {
|
||||
if (index >= controller.trigger_values.size()) {
|
||||
return;
|
||||
}
|
||||
@@ -659,7 +670,7 @@ void EmulatedController::SetTrigger(Common::Input::CallbackStatus callback, std:
|
||||
const auto trigger_value = TransformToTrigger(callback);
|
||||
|
||||
// Only read trigger values that have the same uuid or are pressed once
|
||||
if (controller.stick_values[index].uuid != uuid) {
|
||||
if (controller.trigger_values[index].uuid != uuid) {
|
||||
if (!trigger_value.pressed.value) {
|
||||
return;
|
||||
}
|
||||
@@ -675,7 +686,7 @@ void EmulatedController::SetTrigger(Common::Input::CallbackStatus callback, std:
|
||||
return;
|
||||
}
|
||||
|
||||
const auto trigger = controller.trigger_values[index];
|
||||
const auto& trigger = controller.trigger_values[index];
|
||||
|
||||
switch (index) {
|
||||
case Settings::NativeTrigger::LTrigger:
|
||||
@@ -692,7 +703,8 @@ void EmulatedController::SetTrigger(Common::Input::CallbackStatus callback, std:
|
||||
TriggerOnChange(ControllerTriggerType::Trigger, true);
|
||||
}
|
||||
|
||||
void EmulatedController::SetMotion(Common::Input::CallbackStatus callback, std::size_t index) {
|
||||
void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback,
|
||||
std::size_t index) {
|
||||
if (index >= controller.motion_values.size()) {
|
||||
return;
|
||||
}
|
||||
@@ -730,7 +742,8 @@ void EmulatedController::SetMotion(Common::Input::CallbackStatus callback, std::
|
||||
TriggerOnChange(ControllerTriggerType::Motion, true);
|
||||
}
|
||||
|
||||
void EmulatedController::SetBattery(Common::Input::CallbackStatus callback, std::size_t index) {
|
||||
void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callback,
|
||||
std::size_t index) {
|
||||
if (index >= controller.battery_values.size()) {
|
||||
return;
|
||||
}
|
||||
@@ -830,23 +843,18 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v
|
||||
}
|
||||
|
||||
bool EmulatedController::TestVibration(std::size_t device_index) {
|
||||
if (device_index >= output_devices.size()) {
|
||||
return false;
|
||||
}
|
||||
if (!output_devices[device_index]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Send a slight vibration to test for rumble support
|
||||
constexpr Common::Input::VibrationStatus status = {
|
||||
static constexpr VibrationValue test_vibration = {
|
||||
.low_amplitude = 0.001f,
|
||||
.low_frequency = 160.0f,
|
||||
.high_amplitude = 0.001f,
|
||||
.high_frequency = 320.0f,
|
||||
.type = Common::Input::VibrationAmplificationType::Linear,
|
||||
};
|
||||
return output_devices[device_index]->SetVibration(status) ==
|
||||
Common::Input::VibrationError::None;
|
||||
|
||||
// Send a slight vibration to test for rumble support
|
||||
SetVibration(device_index, test_vibration);
|
||||
|
||||
// Stop any vibration and return the result
|
||||
return SetVibration(device_index, DEFAULT_VIBRATION_VALUE);
|
||||
}
|
||||
|
||||
void EmulatedController::SetLedPattern() {
|
||||
@@ -878,8 +886,9 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
|
||||
}
|
||||
}
|
||||
|
||||
bool EmulatedController::IsControllerSupported() const {
|
||||
switch (npad_type) {
|
||||
bool EmulatedController::IsControllerSupported(bool use_temporary_value) const {
|
||||
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
|
||||
switch (type) {
|
||||
case NpadStyleIndex::ProController:
|
||||
return supported_style_tag.fullkey;
|
||||
case NpadStyleIndex::Handheld:
|
||||
@@ -907,9 +916,10 @@ bool EmulatedController::IsControllerSupported() const {
|
||||
}
|
||||
}
|
||||
|
||||
void EmulatedController::Connect() {
|
||||
if (!IsControllerSupported()) {
|
||||
LOG_ERROR(Service_HID, "Controller type {} is not supported", npad_type);
|
||||
void EmulatedController::Connect(bool use_temporary_value) {
|
||||
if (!IsControllerSupported(use_temporary_value)) {
|
||||
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
|
||||
LOG_ERROR(Service_HID, "Controller type {} is not supported", type);
|
||||
return;
|
||||
}
|
||||
{
|
||||
@@ -1110,7 +1120,7 @@ void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npa
|
||||
|
||||
int EmulatedController::SetCallback(ControllerUpdateCallback update_callback) {
|
||||
std::lock_guard lock{mutex};
|
||||
callback_list.insert_or_assign(last_callback_key, update_callback);
|
||||
callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
|
||||
return last_callback_key++;
|
||||
}
|
||||
|
||||
|
||||
@@ -167,8 +167,11 @@ public:
|
||||
*/
|
||||
void SetSupportedNpadStyleTag(NpadStyleTag supported_styles);
|
||||
|
||||
/// Sets the connected status to true
|
||||
void Connect();
|
||||
/**
|
||||
* Sets the connected status to true
|
||||
* @param use_temporary_value If true tmp_npad_type will be used
|
||||
*/
|
||||
void Connect(bool use_temporary_value = false);
|
||||
|
||||
/// Sets the connected status to false
|
||||
void Disconnect();
|
||||
@@ -319,44 +322,48 @@ private:
|
||||
|
||||
/**
|
||||
* Checks the current controller type against the supported_style_tag
|
||||
* @param use_temporary_value If true tmp_npad_type will be used
|
||||
* @return true if the controller is supported
|
||||
*/
|
||||
bool IsControllerSupported() const;
|
||||
bool IsControllerSupported(bool use_temporary_value = false) const;
|
||||
|
||||
/**
|
||||
* Updates the button status of the controller
|
||||
* @param callback A CallbackStatus containing the button status
|
||||
* @param index Button ID of the to be updated
|
||||
*/
|
||||
void SetButton(Common::Input::CallbackStatus callback, std::size_t index, Common::UUID uuid);
|
||||
void SetButton(const Common::Input::CallbackStatus& callback, std::size_t index,
|
||||
Common::UUID uuid);
|
||||
|
||||
/**
|
||||
* Updates the analog stick status of the controller
|
||||
* @param callback A CallbackStatus containing the analog stick status
|
||||
* @param index stick ID of the to be updated
|
||||
*/
|
||||
void SetStick(Common::Input::CallbackStatus callback, std::size_t index, Common::UUID uuid);
|
||||
void SetStick(const Common::Input::CallbackStatus& callback, std::size_t index,
|
||||
Common::UUID uuid);
|
||||
|
||||
/**
|
||||
* Updates the trigger status of the controller
|
||||
* @param callback A CallbackStatus containing the trigger status
|
||||
* @param index trigger ID of the to be updated
|
||||
*/
|
||||
void SetTrigger(Common::Input::CallbackStatus callback, std::size_t index, Common::UUID uuid);
|
||||
void SetTrigger(const Common::Input::CallbackStatus& callback, std::size_t index,
|
||||
Common::UUID uuid);
|
||||
|
||||
/**
|
||||
* Updates the motion status of the controller
|
||||
* @param callback A CallbackStatus containing gyro and accelerometer data
|
||||
* @param index motion ID of the to be updated
|
||||
*/
|
||||
void SetMotion(Common::Input::CallbackStatus callback, std::size_t index);
|
||||
void SetMotion(const Common::Input::CallbackStatus& callback, std::size_t index);
|
||||
|
||||
/**
|
||||
* Updates the battery status of the controller
|
||||
* @param callback A CallbackStatus containing the battery status
|
||||
* @param index Button ID of the to be updated
|
||||
*/
|
||||
void SetBattery(Common::Input::CallbackStatus callback, std::size_t index);
|
||||
void SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index);
|
||||
|
||||
/**
|
||||
* Triggers a callback that something has changed on the controller status
|
||||
|
||||
@@ -70,50 +70,55 @@ void EmulatedDevices::ReloadInput() {
|
||||
if (!mouse_button_devices[index]) {
|
||||
continue;
|
||||
}
|
||||
Common::Input::InputCallback button_callback{
|
||||
[this, index](Common::Input::CallbackStatus callback) {
|
||||
SetMouseButton(callback, index);
|
||||
}};
|
||||
mouse_button_devices[index]->SetCallback(button_callback);
|
||||
mouse_button_devices[index]->SetCallback({
|
||||
.on_change =
|
||||
[this, index](const Common::Input::CallbackStatus& callback) {
|
||||
SetMouseButton(callback, index);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
for (std::size_t index = 0; index < mouse_analog_devices.size(); ++index) {
|
||||
if (!mouse_analog_devices[index]) {
|
||||
continue;
|
||||
}
|
||||
Common::Input::InputCallback button_callback{
|
||||
[this, index](Common::Input::CallbackStatus callback) {
|
||||
SetMouseAnalog(callback, index);
|
||||
}};
|
||||
mouse_analog_devices[index]->SetCallback(button_callback);
|
||||
mouse_analog_devices[index]->SetCallback({
|
||||
.on_change =
|
||||
[this, index](const Common::Input::CallbackStatus& callback) {
|
||||
SetMouseAnalog(callback, index);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (mouse_stick_device) {
|
||||
Common::Input::InputCallback button_callback{
|
||||
[this](Common::Input::CallbackStatus callback) { SetMouseStick(callback); }};
|
||||
mouse_stick_device->SetCallback(button_callback);
|
||||
mouse_stick_device->SetCallback({
|
||||
.on_change =
|
||||
[this](const Common::Input::CallbackStatus& callback) { SetMouseStick(callback); },
|
||||
});
|
||||
}
|
||||
|
||||
for (std::size_t index = 0; index < keyboard_devices.size(); ++index) {
|
||||
if (!keyboard_devices[index]) {
|
||||
continue;
|
||||
}
|
||||
Common::Input::InputCallback button_callback{
|
||||
[this, index](Common::Input::CallbackStatus callback) {
|
||||
SetKeyboardButton(callback, index);
|
||||
}};
|
||||
keyboard_devices[index]->SetCallback(button_callback);
|
||||
keyboard_devices[index]->SetCallback({
|
||||
.on_change =
|
||||
[this, index](const Common::Input::CallbackStatus& callback) {
|
||||
SetKeyboardButton(callback, index);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
for (std::size_t index = 0; index < keyboard_modifier_devices.size(); ++index) {
|
||||
if (!keyboard_modifier_devices[index]) {
|
||||
continue;
|
||||
}
|
||||
Common::Input::InputCallback button_callback{
|
||||
[this, index](Common::Input::CallbackStatus callback) {
|
||||
SetKeyboardModifier(callback, index);
|
||||
}};
|
||||
keyboard_modifier_devices[index]->SetCallback(button_callback);
|
||||
keyboard_modifier_devices[index]->SetCallback({
|
||||
.on_change =
|
||||
[this, index](const Common::Input::CallbackStatus& callback) {
|
||||
SetKeyboardModifier(callback, index);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +164,8 @@ void EmulatedDevices::RestoreConfig() {
|
||||
ReloadFromSettings();
|
||||
}
|
||||
|
||||
void EmulatedDevices::SetKeyboardButton(Common::Input::CallbackStatus callback, std::size_t index) {
|
||||
void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& callback,
|
||||
std::size_t index) {
|
||||
if (index >= device_status.keyboard_values.size()) {
|
||||
return;
|
||||
}
|
||||
@@ -216,7 +222,7 @@ void EmulatedDevices::UpdateKey(std::size_t key_index, bool status) {
|
||||
}
|
||||
}
|
||||
|
||||
void EmulatedDevices::SetKeyboardModifier(Common::Input::CallbackStatus callback,
|
||||
void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& callback,
|
||||
std::size_t index) {
|
||||
if (index >= device_status.keyboard_moddifier_values.size()) {
|
||||
return;
|
||||
@@ -286,7 +292,8 @@ void EmulatedDevices::SetKeyboardModifier(Common::Input::CallbackStatus callback
|
||||
TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
|
||||
}
|
||||
|
||||
void EmulatedDevices::SetMouseButton(Common::Input::CallbackStatus callback, std::size_t index) {
|
||||
void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callback,
|
||||
std::size_t index) {
|
||||
if (index >= device_status.mouse_button_values.size()) {
|
||||
return;
|
||||
}
|
||||
@@ -347,7 +354,8 @@ void EmulatedDevices::SetMouseButton(Common::Input::CallbackStatus callback, std
|
||||
TriggerOnChange(DeviceTriggerType::Mouse);
|
||||
}
|
||||
|
||||
void EmulatedDevices::SetMouseAnalog(Common::Input::CallbackStatus callback, std::size_t index) {
|
||||
void EmulatedDevices::SetMouseAnalog(const Common::Input::CallbackStatus& callback,
|
||||
std::size_t index) {
|
||||
if (index >= device_status.mouse_analog_values.size()) {
|
||||
return;
|
||||
}
|
||||
@@ -374,7 +382,7 @@ void EmulatedDevices::SetMouseAnalog(Common::Input::CallbackStatus callback, std
|
||||
TriggerOnChange(DeviceTriggerType::Mouse);
|
||||
}
|
||||
|
||||
void EmulatedDevices::SetMouseStick(Common::Input::CallbackStatus callback) {
|
||||
void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callback) {
|
||||
std::lock_guard lock{mutex};
|
||||
const auto touch_value = TransformToTouch(callback);
|
||||
|
||||
@@ -435,7 +443,7 @@ void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
|
||||
|
||||
int EmulatedDevices::SetCallback(InterfaceUpdateCallback update_callback) {
|
||||
std::lock_guard lock{mutex};
|
||||
callback_list.insert_or_assign(last_callback_key, update_callback);
|
||||
callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
|
||||
return last_callback_key++;
|
||||
}
|
||||
|
||||
|
||||
@@ -156,35 +156,34 @@ private:
|
||||
* @param callback A CallbackStatus containing the key status
|
||||
* @param index key ID to be updated
|
||||
*/
|
||||
void SetKeyboardButton(Common::Input::CallbackStatus callback, std::size_t index);
|
||||
void SetKeyboardButton(const Common::Input::CallbackStatus& callback, std::size_t index);
|
||||
|
||||
/**
|
||||
* Updates the keyboard status of the keyboard device
|
||||
* @param callback A CallbackStatus containing the modifier key status
|
||||
* @param index modifier key ID to be updated
|
||||
*/
|
||||
void SetKeyboardModifier(Common::Input::CallbackStatus callback, std::size_t index);
|
||||
void SetKeyboardModifier(const Common::Input::CallbackStatus& callback, std::size_t index);
|
||||
|
||||
/**
|
||||
* Updates the mouse button status of the mouse device
|
||||
* @param callback A CallbackStatus containing the button status
|
||||
* @param index Button ID to be updated
|
||||
*/
|
||||
void SetMouseButton(Common::Input::CallbackStatus callback, std::size_t index);
|
||||
void SetMouseButton(const Common::Input::CallbackStatus& callback, std::size_t index);
|
||||
|
||||
/**
|
||||
* Updates the mouse wheel status of the mouse device
|
||||
* @param callback A CallbackStatus containing the wheel status
|
||||
* @param index wheel ID to be updated
|
||||
*/
|
||||
void SetMouseAnalog(Common::Input::CallbackStatus callback, std::size_t index);
|
||||
void SetMouseAnalog(const Common::Input::CallbackStatus& callback, std::size_t index);
|
||||
|
||||
/**
|
||||
* Updates the mouse position status of the mouse device
|
||||
* @param callback A CallbackStatus containing the position status
|
||||
* @param index stick ID to be updated
|
||||
*/
|
||||
void SetMouseStick(Common::Input::CallbackStatus callback);
|
||||
void SetMouseStick(const Common::Input::CallbackStatus& callback);
|
||||
|
||||
/**
|
||||
* Triggers a callback that something has changed on the device status
|
||||
|
||||
@@ -496,6 +496,13 @@ struct VibrationValue {
|
||||
};
|
||||
static_assert(sizeof(VibrationValue) == 0x10, "VibrationValue has incorrect size.");
|
||||
|
||||
constexpr VibrationValue DEFAULT_VIBRATION_VALUE{
|
||||
.low_amplitude = 0.0f,
|
||||
.low_frequency = 160.0f,
|
||||
.high_amplitude = 0.0f,
|
||||
.high_frequency = 320.0f,
|
||||
};
|
||||
|
||||
// This is nn::hid::VibrationDeviceInfo
|
||||
struct VibrationDeviceInfo {
|
||||
VibrationDeviceType type{};
|
||||
|
||||
@@ -23,11 +23,11 @@ void MotionInput::SetAcceleration(const Common::Vec3f& acceleration) {
|
||||
}
|
||||
|
||||
void MotionInput::SetGyroscope(const Common::Vec3f& gyroscope) {
|
||||
gyro = gyroscope - gyro_drift;
|
||||
gyro = gyroscope - gyro_bias;
|
||||
|
||||
// Auto adjust drift to minimize drift
|
||||
if (!IsMoving(0.1f)) {
|
||||
gyro_drift = (gyro_drift * 0.9999f) + (gyroscope * 0.0001f);
|
||||
gyro_bias = (gyro_bias * 0.9999f) + (gyroscope * 0.0001f);
|
||||
}
|
||||
|
||||
if (gyro.Length2() < gyro_threshold) {
|
||||
@@ -41,8 +41,8 @@ void MotionInput::SetQuaternion(const Common::Quaternion<f32>& quaternion) {
|
||||
quat = quaternion;
|
||||
}
|
||||
|
||||
void MotionInput::SetGyroDrift(const Common::Vec3f& drift) {
|
||||
gyro_drift = drift;
|
||||
void MotionInput::SetGyroBias(const Common::Vec3f& bias) {
|
||||
gyro_bias = bias;
|
||||
}
|
||||
|
||||
void MotionInput::SetGyroThreshold(f32 threshold) {
|
||||
@@ -192,6 +192,10 @@ Common::Vec3f MotionInput::GetGyroscope() const {
|
||||
return gyro;
|
||||
}
|
||||
|
||||
Common::Vec3f MotionInput::GetGyroBias() const {
|
||||
return gyro_bias;
|
||||
}
|
||||
|
||||
Common::Quaternion<f32> MotionInput::GetQuaternion() const {
|
||||
return quat;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ public:
|
||||
void SetAcceleration(const Common::Vec3f& acceleration);
|
||||
void SetGyroscope(const Common::Vec3f& gyroscope);
|
||||
void SetQuaternion(const Common::Quaternion<f32>& quaternion);
|
||||
void SetGyroDrift(const Common::Vec3f& drift);
|
||||
void SetGyroBias(const Common::Vec3f& bias);
|
||||
void SetGyroThreshold(f32 threshold);
|
||||
|
||||
void EnableReset(bool reset);
|
||||
@@ -36,6 +36,7 @@ public:
|
||||
[[nodiscard]] std::array<Common::Vec3f, 3> GetOrientation() const;
|
||||
[[nodiscard]] Common::Vec3f GetAcceleration() const;
|
||||
[[nodiscard]] Common::Vec3f GetGyroscope() const;
|
||||
[[nodiscard]] Common::Vec3f GetGyroBias() const;
|
||||
[[nodiscard]] Common::Vec3f GetRotations() const;
|
||||
[[nodiscard]] Common::Quaternion<f32> GetQuaternion() const;
|
||||
|
||||
@@ -69,7 +70,7 @@ private:
|
||||
Common::Vec3f gyro;
|
||||
|
||||
// Vector to be substracted from gyro measurements
|
||||
Common::Vec3f gyro_drift;
|
||||
Common::Vec3f gyro_bias;
|
||||
|
||||
// Minimum gyro amplitude to detect if the device is moving
|
||||
f32 gyro_threshold = 0.0f;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "core/hle/kernel/global_scheduler_context.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/physical_core.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@@ -42,6 +43,11 @@ void GlobalSchedulerContext::PreemptThreads() {
|
||||
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
|
||||
const u32 priority = preemption_priorities[core_id];
|
||||
kernel.Scheduler(core_id).RotateScheduledQueue(core_id, priority);
|
||||
|
||||
// Signal an interrupt occurred. For core 3, this is a certainty, as preemption will result
|
||||
// in the rotator thread being scheduled. For cores 0-2, this is to simulate or system
|
||||
// interrupts that may have occurred.
|
||||
kernel.PhysicalCore(core_id).Interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
34
src/core/hle/kernel/k_interrupt_manager.cpp
Normal file
34
src/core/hle/kernel/k_interrupt_manager.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/kernel/k_interrupt_manager.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
|
||||
namespace Kernel::KInterruptManager {
|
||||
|
||||
void HandleInterrupt(KernelCore& kernel, s32 core_id) {
|
||||
auto* process = kernel.CurrentProcess();
|
||||
if (!process) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& scheduler = kernel.Scheduler(core_id);
|
||||
auto& current_thread = *scheduler.GetCurrentThread();
|
||||
|
||||
// If the user disable count is set, we may need to pin the current thread.
|
||||
if (current_thread.GetUserDisableCount() && !process->GetPinnedThread(core_id)) {
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
// Pin the current thread.
|
||||
process->PinCurrentThread(core_id);
|
||||
|
||||
// Set the interrupt flag for the thread.
|
||||
scheduler.GetCurrentThread()->SetInterruptFlag();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Kernel::KInterruptManager
|
||||
17
src/core/hle/kernel/k_interrupt_manager.h
Normal file
17
src/core/hle/kernel/k_interrupt_manager.h
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KernelCore;
|
||||
|
||||
namespace KInterruptManager {
|
||||
void HandleInterrupt(KernelCore& kernel, s32 core_id);
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -120,7 +120,7 @@ static_assert(static_cast<u32>(KMemoryState::CodeOut) == 0x00402015);
|
||||
|
||||
enum class KMemoryPermission : u8 {
|
||||
None = 0,
|
||||
Mask = static_cast<u8>(~None),
|
||||
All = static_cast<u8>(~None),
|
||||
|
||||
Read = 1 << 0,
|
||||
Write = 1 << 1,
|
||||
|
||||
@@ -264,9 +264,9 @@ ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_
|
||||
ASSERT(heap_last < stack_start || stack_last < heap_start);
|
||||
ASSERT(heap_last < kmap_start || kmap_last < heap_start);
|
||||
|
||||
current_heap_addr = heap_region_start;
|
||||
heap_capacity = 0;
|
||||
physical_memory_usage = 0;
|
||||
current_heap_end = heap_region_start;
|
||||
max_heap_size = 0;
|
||||
mapped_physical_memory_size = 0;
|
||||
memory_pool = pool;
|
||||
|
||||
page_table_impl.Resize(address_space_width, PageBits);
|
||||
@@ -306,7 +306,7 @@ ResultCode KPageTable::MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std:
|
||||
KMemoryState state{};
|
||||
KMemoryPermission perm{};
|
||||
CASCADE_CODE(CheckMemoryState(&state, &perm, nullptr, src_addr, size, KMemoryState::All,
|
||||
KMemoryState::Normal, KMemoryPermission::Mask,
|
||||
KMemoryState::Normal, KMemoryPermission::All,
|
||||
KMemoryPermission::ReadAndWrite, KMemoryAttribute::Mask,
|
||||
KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
|
||||
|
||||
@@ -465,7 +465,7 @@ ResultCode KPageTable::MapPhysicalMemory(VAddr addr, std::size_t size) {
|
||||
|
||||
MapPhysicalMemory(page_linked_list, addr, end_addr);
|
||||
|
||||
physical_memory_usage += remaining_size;
|
||||
mapped_physical_memory_size += remaining_size;
|
||||
|
||||
const std::size_t num_pages{size / PageSize};
|
||||
block_manager->Update(addr, num_pages, KMemoryState::Free, KMemoryPermission::None,
|
||||
@@ -507,7 +507,7 @@ ResultCode KPageTable::UnmapPhysicalMemory(VAddr addr, std::size_t size) {
|
||||
|
||||
auto process{system.Kernel().CurrentProcess()};
|
||||
process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, mapped_size);
|
||||
physical_memory_usage -= mapped_size;
|
||||
mapped_physical_memory_size -= mapped_size;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
@@ -554,7 +554,7 @@ ResultCode KPageTable::Map(VAddr dst_addr, VAddr src_addr, std::size_t size) {
|
||||
KMemoryState src_state{};
|
||||
CASCADE_CODE(CheckMemoryState(
|
||||
&src_state, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias,
|
||||
KMemoryState::FlagCanAlias, KMemoryPermission::Mask, KMemoryPermission::ReadAndWrite,
|
||||
KMemoryState::FlagCanAlias, KMemoryPermission::All, KMemoryPermission::ReadAndWrite,
|
||||
KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
|
||||
|
||||
if (IsRegionMapped(dst_addr, size)) {
|
||||
@@ -593,7 +593,7 @@ ResultCode KPageTable::Unmap(VAddr dst_addr, VAddr src_addr, std::size_t size) {
|
||||
KMemoryState src_state{};
|
||||
CASCADE_CODE(CheckMemoryState(
|
||||
&src_state, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias,
|
||||
KMemoryState::FlagCanAlias, KMemoryPermission::Mask, KMemoryPermission::None,
|
||||
KMemoryState::FlagCanAlias, KMemoryPermission::All, KMemoryPermission::None,
|
||||
KMemoryAttribute::Mask, KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped));
|
||||
|
||||
KMemoryPermission dst_perm{};
|
||||
@@ -784,7 +784,7 @@ ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemo
|
||||
CASCADE_CODE(CheckMemoryState(
|
||||
&state, nullptr, &attribute, addr, size,
|
||||
KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted,
|
||||
KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, KMemoryPermission::Mask,
|
||||
KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, KMemoryPermission::All,
|
||||
KMemoryPermission::ReadAndWrite, KMemoryAttribute::Mask, KMemoryAttribute::None,
|
||||
KMemoryAttribute::IpcAndDeviceMapped));
|
||||
|
||||
@@ -806,6 +806,33 @@ ResultCode KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) {
|
||||
KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped));
|
||||
|
||||
block_manager->Update(addr, size / PageSize, state, KMemoryPermission::ReadAndWrite);
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::SetMemoryPermission(VAddr addr, std::size_t size,
|
||||
Svc::MemoryPermission svc_perm) {
|
||||
const size_t num_pages = size / PageSize;
|
||||
|
||||
// Lock the table.
|
||||
std::lock_guard lock{page_table_lock};
|
||||
|
||||
// Verify we can change the memory permission.
|
||||
KMemoryState old_state;
|
||||
KMemoryPermission old_perm;
|
||||
R_TRY(this->CheckMemoryState(
|
||||
std::addressof(old_state), std::addressof(old_perm), nullptr, addr, size,
|
||||
KMemoryState::FlagCanReprotect, KMemoryState::FlagCanReprotect, KMemoryPermission::None,
|
||||
KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None));
|
||||
|
||||
// Determine new perm.
|
||||
const KMemoryPermission new_perm = ConvertToKMemoryPermission(svc_perm);
|
||||
R_SUCCEED_IF(old_perm == new_perm);
|
||||
|
||||
// Perform mapping operation.
|
||||
R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions));
|
||||
|
||||
// Update the blocks.
|
||||
block_manager->Update(addr, num_pages, old_state, new_perm, KMemoryAttribute::None);
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
@@ -832,61 +859,125 @@ ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, KMemoryA
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::SetHeapCapacity(std::size_t new_heap_capacity) {
|
||||
ResultCode KPageTable::SetMaxHeapSize(std::size_t size) {
|
||||
// Lock the table.
|
||||
std::lock_guard lock{page_table_lock};
|
||||
heap_capacity = new_heap_capacity;
|
||||
|
||||
// Only process page tables are allowed to set heap size.
|
||||
ASSERT(!this->IsKernel());
|
||||
|
||||
max_heap_size = size;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultVal<VAddr> KPageTable::SetHeapSize(std::size_t size) {
|
||||
ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) {
|
||||
// Try to perform a reduction in heap, instead of an extension.
|
||||
VAddr cur_address{};
|
||||
std::size_t allocation_size{};
|
||||
{
|
||||
// Lock the table.
|
||||
std::lock_guard lk(page_table_lock);
|
||||
|
||||
if (size > heap_region_end - heap_region_start) {
|
||||
return ResultOutOfMemory;
|
||||
// Validate that setting heap size is possible at all.
|
||||
R_UNLESS(!is_kernel, ResultOutOfMemory);
|
||||
R_UNLESS(size <= static_cast<std::size_t>(heap_region_end - heap_region_start),
|
||||
ResultOutOfMemory);
|
||||
R_UNLESS(size <= max_heap_size, ResultOutOfMemory);
|
||||
|
||||
if (size < GetHeapSize()) {
|
||||
// The size being requested is less than the current size, so we need to free the end of
|
||||
// the heap.
|
||||
|
||||
// Validate memory state.
|
||||
std::size_t num_allocator_blocks;
|
||||
R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks),
|
||||
heap_region_start + size, GetHeapSize() - size,
|
||||
KMemoryState::All, KMemoryState::Normal,
|
||||
KMemoryPermission::All, KMemoryPermission::ReadAndWrite,
|
||||
KMemoryAttribute::All, KMemoryAttribute::None));
|
||||
|
||||
// Unmap the end of the heap.
|
||||
const auto num_pages = (GetHeapSize() - size) / PageSize;
|
||||
R_TRY(Operate(heap_region_start + size, num_pages, KMemoryPermission::None,
|
||||
OperationType::Unmap));
|
||||
|
||||
// Release the memory from the resource limit.
|
||||
system.Kernel().CurrentProcess()->GetResourceLimit()->Release(
|
||||
LimitableResource::PhysicalMemory, num_pages * PageSize);
|
||||
|
||||
// Apply the memory block update.
|
||||
block_manager->Update(heap_region_start + size, num_pages, KMemoryState::Free,
|
||||
KMemoryPermission::None, KMemoryAttribute::None);
|
||||
|
||||
// Update the current heap end.
|
||||
current_heap_end = heap_region_start + size;
|
||||
|
||||
// Set the output.
|
||||
*out = heap_region_start;
|
||||
return ResultSuccess;
|
||||
} else if (size == GetHeapSize()) {
|
||||
// The size requested is exactly the current size.
|
||||
*out = heap_region_start;
|
||||
return ResultSuccess;
|
||||
} else {
|
||||
// We have to allocate memory. Determine how much to allocate and where while the table
|
||||
// is locked.
|
||||
cur_address = current_heap_end;
|
||||
allocation_size = size - GetHeapSize();
|
||||
}
|
||||
}
|
||||
|
||||
const u64 previous_heap_size{GetHeapSize()};
|
||||
// Reserve memory for the heap extension.
|
||||
KScopedResourceReservation memory_reservation(
|
||||
system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory,
|
||||
allocation_size);
|
||||
R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
|
||||
|
||||
UNIMPLEMENTED_IF_MSG(previous_heap_size > size, "Heap shrink is unimplemented");
|
||||
// Allocate pages for the heap extension.
|
||||
KPageLinkedList page_linked_list;
|
||||
R_TRY(system.Kernel().MemoryManager().Allocate(page_linked_list, allocation_size / PageSize,
|
||||
memory_pool));
|
||||
|
||||
// Increase the heap size
|
||||
// Map the pages.
|
||||
{
|
||||
std::lock_guard lock{page_table_lock};
|
||||
// Lock the table.
|
||||
std::lock_guard lk(page_table_lock);
|
||||
|
||||
const u64 delta{size - previous_heap_size};
|
||||
// Ensure that the heap hasn't changed since we began executing.
|
||||
ASSERT(cur_address == current_heap_end);
|
||||
|
||||
// Reserve memory for the heap extension.
|
||||
KScopedResourceReservation memory_reservation(
|
||||
system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory,
|
||||
delta);
|
||||
// Check the memory state.
|
||||
std::size_t num_allocator_blocks{};
|
||||
R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), current_heap_end,
|
||||
allocation_size, KMemoryState::All, KMemoryState::Free,
|
||||
KMemoryPermission::None, KMemoryPermission::None,
|
||||
KMemoryAttribute::None, KMemoryAttribute::None));
|
||||
|
||||
if (!memory_reservation.Succeeded()) {
|
||||
LOG_ERROR(Kernel, "Could not reserve heap extension of size {:X} bytes", delta);
|
||||
return ResultLimitReached;
|
||||
// Map the pages.
|
||||
const auto num_pages = allocation_size / PageSize;
|
||||
R_TRY(Operate(current_heap_end, num_pages, page_linked_list, OperationType::MapGroup));
|
||||
|
||||
// Clear all the newly allocated pages.
|
||||
for (std::size_t cur_page = 0; cur_page < num_pages; ++cur_page) {
|
||||
std::memset(system.Memory().GetPointer(current_heap_end + (cur_page * PageSize)), 0,
|
||||
PageSize);
|
||||
}
|
||||
|
||||
KPageLinkedList page_linked_list;
|
||||
const std::size_t num_pages{delta / PageSize};
|
||||
|
||||
CASCADE_CODE(
|
||||
system.Kernel().MemoryManager().Allocate(page_linked_list, num_pages, memory_pool));
|
||||
|
||||
if (IsRegionMapped(current_heap_addr, delta)) {
|
||||
return ResultInvalidCurrentMemory;
|
||||
}
|
||||
|
||||
CASCADE_CODE(
|
||||
Operate(current_heap_addr, num_pages, page_linked_list, OperationType::MapGroup));
|
||||
|
||||
// Succeeded in allocation, commit the resource reservation
|
||||
// We succeeded, so commit our memory reservation.
|
||||
memory_reservation.Commit();
|
||||
|
||||
block_manager->Update(current_heap_addr, num_pages, KMemoryState::Normal,
|
||||
KMemoryPermission::ReadAndWrite);
|
||||
// Apply the memory block update.
|
||||
block_manager->Update(current_heap_end, num_pages, KMemoryState::Normal,
|
||||
KMemoryPermission::ReadAndWrite, KMemoryAttribute::None);
|
||||
|
||||
current_heap_addr = heap_region_start + size;
|
||||
// Update the current heap end.
|
||||
current_heap_end = heap_region_start + size;
|
||||
|
||||
// Set the output.
|
||||
*out = heap_region_start;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
return heap_region_start;
|
||||
}
|
||||
|
||||
ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align,
|
||||
@@ -978,7 +1069,7 @@ ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
|
||||
|
||||
if (const ResultCode result{CheckMemoryState(
|
||||
nullptr, &old_perm, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
|
||||
KMemoryState::FlagCanCodeMemory, KMemoryPermission::Mask,
|
||||
KMemoryState::FlagCanCodeMemory, KMemoryPermission::All,
|
||||
KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)};
|
||||
result.IsError()) {
|
||||
return result;
|
||||
@@ -1031,9 +1122,8 @@ ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
|
||||
|
||||
bool KPageTable::IsRegionMapped(VAddr address, u64 size) {
|
||||
return CheckMemoryState(address, size, KMemoryState::All, KMemoryState::Free,
|
||||
KMemoryPermission::Mask, KMemoryPermission::None,
|
||||
KMemoryAttribute::Mask, KMemoryAttribute::None,
|
||||
KMemoryAttribute::IpcAndDeviceMapped)
|
||||
KMemoryPermission::All, KMemoryPermission::None, KMemoryAttribute::Mask,
|
||||
KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)
|
||||
.IsError();
|
||||
}
|
||||
|
||||
|
||||
@@ -47,10 +47,11 @@ public:
|
||||
KMemoryInfo QueryInfo(VAddr addr);
|
||||
ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm);
|
||||
ResultCode ResetTransferMemory(VAddr addr, std::size_t size);
|
||||
ResultCode SetMemoryPermission(VAddr addr, std::size_t size, Svc::MemoryPermission perm);
|
||||
ResultCode SetMemoryAttribute(VAddr addr, std::size_t size, KMemoryAttribute mask,
|
||||
KMemoryAttribute value);
|
||||
ResultCode SetHeapCapacity(std::size_t new_heap_capacity);
|
||||
ResultVal<VAddr> SetHeapSize(std::size_t size);
|
||||
ResultCode SetMaxHeapSize(std::size_t size);
|
||||
ResultCode SetHeapSize(VAddr* out, std::size_t size);
|
||||
ResultVal<VAddr> AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align,
|
||||
bool is_map_only, VAddr region_start,
|
||||
std::size_t region_num_pages, KMemoryState state,
|
||||
@@ -182,14 +183,15 @@ public:
|
||||
constexpr VAddr GetAliasCodeRegionSize() const {
|
||||
return alias_code_region_end - alias_code_region_start;
|
||||
}
|
||||
size_t GetNormalMemorySize() {
|
||||
std::lock_guard lk(page_table_lock);
|
||||
return GetHeapSize() + mapped_physical_memory_size;
|
||||
}
|
||||
constexpr std::size_t GetAddressSpaceWidth() const {
|
||||
return address_space_width;
|
||||
}
|
||||
constexpr std::size_t GetHeapSize() {
|
||||
return current_heap_addr - heap_region_start;
|
||||
}
|
||||
constexpr std::size_t GetTotalHeapSize() {
|
||||
return GetHeapSize() + physical_memory_usage;
|
||||
constexpr std::size_t GetHeapSize() const {
|
||||
return current_heap_end - heap_region_start;
|
||||
}
|
||||
constexpr bool IsInsideAddressSpace(VAddr address, std::size_t size) const {
|
||||
return address_space_start <= address && address + size - 1 <= address_space_end - 1;
|
||||
@@ -269,10 +271,8 @@ private:
|
||||
VAddr code_region_end{};
|
||||
VAddr alias_code_region_start{};
|
||||
VAddr alias_code_region_end{};
|
||||
VAddr current_heap_addr{};
|
||||
|
||||
std::size_t heap_capacity{};
|
||||
std::size_t physical_memory_usage{};
|
||||
std::size_t mapped_physical_memory_size{};
|
||||
std::size_t max_heap_size{};
|
||||
std::size_t max_physical_memory_size{};
|
||||
std::size_t address_space_width{};
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/svc_results.h"
|
||||
#include "core/hle/lock.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Kernel {
|
||||
@@ -173,7 +172,7 @@ void KProcess::DecrementThreadCount() {
|
||||
|
||||
u64 KProcess::GetTotalPhysicalMemoryAvailable() const {
|
||||
const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemory) +
|
||||
page_table->GetTotalHeapSize() + GetSystemResourceSize() + image_size +
|
||||
page_table->GetNormalMemorySize() + GetSystemResourceSize() + image_size +
|
||||
main_thread_stack_size};
|
||||
if (const auto pool_size = kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application);
|
||||
capacity != pool_size) {
|
||||
@@ -190,7 +189,7 @@ u64 KProcess::GetTotalPhysicalMemoryAvailableWithoutSystemResource() const {
|
||||
}
|
||||
|
||||
u64 KProcess::GetTotalPhysicalMemoryUsed() const {
|
||||
return image_size + main_thread_stack_size + page_table->GetTotalHeapSize() +
|
||||
return image_size + main_thread_stack_size + page_table->GetNormalMemorySize() +
|
||||
GetSystemResourceSize();
|
||||
}
|
||||
|
||||
@@ -221,30 +220,28 @@ bool KProcess::ReleaseUserException(KThread* thread) {
|
||||
}
|
||||
}
|
||||
|
||||
void KProcess::PinCurrentThread() {
|
||||
void KProcess::PinCurrentThread(s32 core_id) {
|
||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||
|
||||
// Get the current thread.
|
||||
const s32 core_id = GetCurrentCoreId(kernel);
|
||||
KThread* cur_thread = GetCurrentThreadPointer(kernel);
|
||||
KThread* cur_thread = kernel.Scheduler(static_cast<std::size_t>(core_id)).GetCurrentThread();
|
||||
|
||||
// If the thread isn't terminated, pin it.
|
||||
if (!cur_thread->IsTerminationRequested()) {
|
||||
// Pin it.
|
||||
PinThread(core_id, cur_thread);
|
||||
cur_thread->Pin();
|
||||
cur_thread->Pin(core_id);
|
||||
|
||||
// An update is needed.
|
||||
KScheduler::SetSchedulerUpdateNeeded(kernel);
|
||||
}
|
||||
}
|
||||
|
||||
void KProcess::UnpinCurrentThread() {
|
||||
void KProcess::UnpinCurrentThread(s32 core_id) {
|
||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||
|
||||
// Get the current thread.
|
||||
const s32 core_id = GetCurrentCoreId(kernel);
|
||||
KThread* cur_thread = GetCurrentThreadPointer(kernel);
|
||||
KThread* cur_thread = kernel.Scheduler(static_cast<std::size_t>(core_id)).GetCurrentThread();
|
||||
|
||||
// Unpin it.
|
||||
cur_thread->Unpin();
|
||||
@@ -411,8 +408,8 @@ void KProcess::Run(s32 main_thread_priority, u64 stack_size) {
|
||||
resource_limit->Reserve(LimitableResource::Threads, 1);
|
||||
resource_limit->Reserve(LimitableResource::PhysicalMemory, main_thread_stack_size);
|
||||
|
||||
const std::size_t heap_capacity{memory_usage_capacity - main_thread_stack_size - image_size};
|
||||
ASSERT(!page_table->SetHeapCapacity(heap_capacity).IsError());
|
||||
const std::size_t heap_capacity{memory_usage_capacity - (main_thread_stack_size + image_size)};
|
||||
ASSERT(!page_table->SetMaxHeapSize(heap_capacity).IsError());
|
||||
|
||||
ChangeStatus(ProcessStatus::Running);
|
||||
|
||||
@@ -543,7 +540,6 @@ void KProcess::FreeTLSRegion(VAddr tls_address) {
|
||||
}
|
||||
|
||||
void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) {
|
||||
std::lock_guard lock{HLE::g_hle_lock};
|
||||
const auto ReprotectSegment = [&](const CodeSet::Segment& segment,
|
||||
KMemoryPermission permission) {
|
||||
page_table->SetProcessMemoryPermission(segment.addr + base_addr, segment.size, permission);
|
||||
|
||||
@@ -345,8 +345,8 @@ public:
|
||||
|
||||
bool IsSignaled() const override;
|
||||
|
||||
void PinCurrentThread();
|
||||
void UnpinCurrentThread();
|
||||
void PinCurrentThread(s32 core_id);
|
||||
void UnpinCurrentThread(s32 core_id);
|
||||
void UnpinThread(KThread* thread);
|
||||
|
||||
KLightLock& GetStateLock() {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/cpu_manager.h"
|
||||
#include "core/hle/kernel/k_interrupt_manager.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
|
||||
@@ -53,6 +54,13 @@ void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedul
|
||||
}
|
||||
cores_pending_reschedule &= ~(1ULL << core);
|
||||
}
|
||||
|
||||
for (std::size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; ++core_id) {
|
||||
if (kernel.PhysicalCore(core_id).IsInterrupted()) {
|
||||
KInterruptManager::HandleInterrupt(kernel, static_cast<s32>(core_id));
|
||||
}
|
||||
}
|
||||
|
||||
if (must_context_switch) {
|
||||
auto core_scheduler = kernel.CurrentScheduler();
|
||||
kernel.ExitSVCProfile();
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cinttypes>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
@@ -26,12 +27,14 @@
|
||||
#include "core/hle/kernel/k_resource_limit.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
|
||||
#include "core/hle/kernel/k_system_control.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/k_thread_queue.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/svc_results.h"
|
||||
#include "core/hle/kernel/time_manager.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#include "core/arm/dynarmic/arm_dynarmic_32.h"
|
||||
@@ -50,6 +53,7 @@ static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context,
|
||||
VAddr entry_point, u64 arg) {
|
||||
context = {};
|
||||
context.cpu_registers[0] = arg;
|
||||
context.cpu_registers[18] = Kernel::KSystemControl::GenerateRandomU64() | 1;
|
||||
context.pc = entry_point;
|
||||
context.sp = stack_top;
|
||||
// TODO(merry): Perform a hardware test to determine the below value.
|
||||
@@ -61,6 +65,13 @@ namespace Kernel {
|
||||
|
||||
namespace {
|
||||
|
||||
struct ThreadLocalRegion {
|
||||
static constexpr std::size_t MessageBufferSize = 0x100;
|
||||
std::array<u32, MessageBufferSize / sizeof(u32)> message_buffer;
|
||||
std::atomic_uint16_t disable_count;
|
||||
std::atomic_uint16_t interrupt_flag;
|
||||
};
|
||||
|
||||
class ThreadQueueImplForKThreadSleep final : public KThreadQueueWithoutEndWait {
|
||||
public:
|
||||
explicit ThreadQueueImplForKThreadSleep(KernelCore& kernel_)
|
||||
@@ -344,7 +355,7 @@ void KThread::StartTermination() {
|
||||
if (parent != nullptr) {
|
||||
parent->ReleaseUserException(this);
|
||||
if (parent->GetPinnedThread(GetCurrentCoreId(kernel)) == this) {
|
||||
parent->UnpinCurrentThread();
|
||||
parent->UnpinCurrentThread(core_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -370,7 +381,7 @@ void KThread::StartTermination() {
|
||||
this->Close();
|
||||
}
|
||||
|
||||
void KThread::Pin() {
|
||||
void KThread::Pin(s32 current_core) {
|
||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||
|
||||
// Set ourselves as pinned.
|
||||
@@ -387,7 +398,6 @@ void KThread::Pin() {
|
||||
|
||||
// Bind ourselves to this core.
|
||||
const s32 active_core = GetActiveCore();
|
||||
const s32 current_core = GetCurrentCoreId(kernel);
|
||||
|
||||
SetActiveCore(current_core);
|
||||
physical_ideal_core_id = current_core;
|
||||
@@ -480,6 +490,36 @@ void KThread::Unpin() {
|
||||
}
|
||||
}
|
||||
|
||||
u16 KThread::GetUserDisableCount() const {
|
||||
if (!IsUserThread()) {
|
||||
// We only emulate TLS for user threads
|
||||
return {};
|
||||
}
|
||||
|
||||
auto& memory = kernel.System().Memory();
|
||||
return memory.Read16(tls_address + offsetof(ThreadLocalRegion, disable_count));
|
||||
}
|
||||
|
||||
void KThread::SetInterruptFlag() {
|
||||
if (!IsUserThread()) {
|
||||
// We only emulate TLS for user threads
|
||||
return;
|
||||
}
|
||||
|
||||
auto& memory = kernel.System().Memory();
|
||||
memory.Write16(tls_address + offsetof(ThreadLocalRegion, interrupt_flag), 1);
|
||||
}
|
||||
|
||||
void KThread::ClearInterruptFlag() {
|
||||
if (!IsUserThread()) {
|
||||
// We only emulate TLS for user threads
|
||||
return;
|
||||
}
|
||||
|
||||
auto& memory = kernel.System().Memory();
|
||||
memory.Write16(tls_address + offsetof(ThreadLocalRegion, interrupt_flag), 0);
|
||||
}
|
||||
|
||||
ResultCode KThread::GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask) {
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
|
||||
@@ -307,6 +307,10 @@ public:
|
||||
return parent != nullptr;
|
||||
}
|
||||
|
||||
u16 GetUserDisableCount() const;
|
||||
void SetInterruptFlag();
|
||||
void ClearInterruptFlag();
|
||||
|
||||
[[nodiscard]] KThread* GetLockOwner() const {
|
||||
return lock_owner;
|
||||
}
|
||||
@@ -490,7 +494,7 @@ public:
|
||||
this->GetStackParameters().disable_count--;
|
||||
}
|
||||
|
||||
void Pin();
|
||||
void Pin(s32 current_core);
|
||||
|
||||
void Unpin();
|
||||
|
||||
|
||||
@@ -182,7 +182,10 @@ struct KernelCore::Impl {
|
||||
// Shutdown all processes.
|
||||
if (current_process) {
|
||||
current_process->Finalize();
|
||||
current_process->Close();
|
||||
// current_process->Close();
|
||||
// TODO: The current process should be destroyed based on accurate ref counting after
|
||||
// calling Close(). Adding a manual Destroy() call instead to avoid a memory leak.
|
||||
current_process->Destroy();
|
||||
current_process = nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
#include "core/hle/kernel/svc_results.h"
|
||||
#include "core/hle/kernel/svc_types.h"
|
||||
#include "core/hle/kernel/svc_wrap.h"
|
||||
#include "core/hle/lock.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/reporter.h"
|
||||
@@ -136,25 +135,15 @@ enum class ResourceLimitValueType {
|
||||
} // Anonymous namespace
|
||||
|
||||
/// Set the process heap to a given Size. It can both extend and shrink the heap.
|
||||
static ResultCode SetHeapSize(Core::System& system, VAddr* heap_addr, u64 heap_size) {
|
||||
std::lock_guard lock{HLE::g_hle_lock};
|
||||
LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", heap_size);
|
||||
static ResultCode SetHeapSize(Core::System& system, VAddr* out_address, u64 size) {
|
||||
LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", size);
|
||||
|
||||
// Size must be a multiple of 0x200000 (2MB) and be equal to or less than 8GB.
|
||||
if ((heap_size % 0x200000) != 0) {
|
||||
LOG_ERROR(Kernel_SVC, "The heap size is not a multiple of 2MB, heap_size=0x{:016X}",
|
||||
heap_size);
|
||||
return ResultInvalidSize;
|
||||
}
|
||||
// Validate size.
|
||||
R_UNLESS(Common::IsAligned(size, HeapSizeAlignment), ResultInvalidSize);
|
||||
R_UNLESS(size < MainMemorySizeMax, ResultInvalidSize);
|
||||
|
||||
if (heap_size >= 0x200000000) {
|
||||
LOG_ERROR(Kernel_SVC, "The heap size is not less than 8GB, heap_size=0x{:016X}", heap_size);
|
||||
return ResultInvalidSize;
|
||||
}
|
||||
|
||||
auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
|
||||
|
||||
CASCADE_RESULT(*heap_addr, page_table.SetHeapSize(heap_size));
|
||||
// Set the heap size.
|
||||
R_TRY(system.Kernel().CurrentProcess()->PageTable().SetHeapSize(out_address, size));
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
@@ -166,9 +155,38 @@ static ResultCode SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_s
|
||||
return result;
|
||||
}
|
||||
|
||||
constexpr bool IsValidSetMemoryPermission(MemoryPermission perm) {
|
||||
switch (perm) {
|
||||
case MemoryPermission::None:
|
||||
case MemoryPermission::Read:
|
||||
case MemoryPermission::ReadWrite:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static ResultCode SetMemoryPermission(Core::System& system, VAddr address, u64 size,
|
||||
MemoryPermission perm) {
|
||||
// Validate address / size.
|
||||
R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
|
||||
R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
|
||||
R_UNLESS(size > 0, ResultInvalidSize);
|
||||
R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
|
||||
|
||||
// Validate the permission.
|
||||
R_UNLESS(IsValidSetMemoryPermission(perm), ResultInvalidNewMemoryPermission);
|
||||
|
||||
// Validate that the region is in range for the current process.
|
||||
auto& page_table = system.Kernel().CurrentProcess()->PageTable();
|
||||
R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
|
||||
|
||||
// Set the memory attribute.
|
||||
return page_table.SetMemoryPermission(address, size, perm);
|
||||
}
|
||||
|
||||
static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask,
|
||||
u32 attribute) {
|
||||
std::lock_guard lock{HLE::g_hle_lock};
|
||||
LOG_DEBUG(Kernel_SVC,
|
||||
"called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address,
|
||||
size, mask, attribute);
|
||||
@@ -212,7 +230,6 @@ static ResultCode SetMemoryAttribute32(Core::System& system, u32 address, u32 si
|
||||
|
||||
/// Maps a memory range into a different range.
|
||||
static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
|
||||
std::lock_guard lock{HLE::g_hle_lock};
|
||||
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
|
||||
src_addr, size);
|
||||
|
||||
@@ -232,7 +249,6 @@ static ResultCode MapMemory32(Core::System& system, u32 dst_addr, u32 src_addr,
|
||||
|
||||
/// Unmaps a region that was previously mapped with svcMapMemory
|
||||
static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
|
||||
std::lock_guard lock{HLE::g_hle_lock};
|
||||
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
|
||||
src_addr, size);
|
||||
|
||||
@@ -642,7 +658,6 @@ static void OutputDebugString(Core::System& system, VAddr address, u64 len) {
|
||||
/// Gets system/memory information for the current process
|
||||
static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle handle,
|
||||
u64 info_sub_id) {
|
||||
std::lock_guard lock{HLE::g_hle_lock};
|
||||
LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id,
|
||||
info_sub_id, handle);
|
||||
|
||||
@@ -886,22 +901,17 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle
|
||||
return ResultSuccess;
|
||||
}
|
||||
case GetInfoType::IdleTickCount: {
|
||||
if (handle == 0) {
|
||||
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}",
|
||||
static_cast<Handle>(handle));
|
||||
return ResultInvalidHandle;
|
||||
}
|
||||
// Verify the input handle is invalid.
|
||||
R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
|
||||
|
||||
if (info_sub_id != 0xFFFFFFFFFFFFFFFF &&
|
||||
info_sub_id != system.Kernel().CurrentPhysicalCoreIndex()) {
|
||||
LOG_ERROR(Kernel_SVC, "Core is not the current core, got {}", info_sub_id);
|
||||
return ResultInvalidCombination;
|
||||
}
|
||||
// Verify the requested core is valid.
|
||||
const bool core_valid =
|
||||
(info_sub_id == static_cast<u64>(-1ULL)) ||
|
||||
(info_sub_id == static_cast<u64>(system.Kernel().CurrentPhysicalCoreIndex()));
|
||||
R_UNLESS(core_valid, ResultInvalidCombination);
|
||||
|
||||
const auto& scheduler = *system.Kernel().CurrentScheduler();
|
||||
const auto* const idle_thread = scheduler.GetIdleThread();
|
||||
|
||||
*result = idle_thread->GetCpuTime();
|
||||
// Get the idle tick count.
|
||||
*result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime();
|
||||
return ResultSuccess;
|
||||
}
|
||||
default:
|
||||
@@ -924,7 +934,6 @@ static ResultCode GetInfo32(Core::System& system, u32* result_low, u32* result_h
|
||||
|
||||
/// Maps memory at a desired address
|
||||
static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
|
||||
std::lock_guard lock{HLE::g_hle_lock};
|
||||
LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
|
||||
|
||||
if (!Common::Is4KBAligned(addr)) {
|
||||
@@ -978,7 +987,6 @@ static ResultCode MapPhysicalMemory32(Core::System& system, u32 addr, u32 size)
|
||||
|
||||
/// Unmaps memory previously mapped via MapPhysicalMemory
|
||||
static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
|
||||
std::lock_guard lock{HLE::g_hle_lock};
|
||||
LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
|
||||
|
||||
if (!Common::Is4KBAligned(addr)) {
|
||||
@@ -1520,7 +1528,6 @@ static ResultCode ControlCodeMemory(Core::System& system, Handle code_memory_han
|
||||
static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address,
|
||||
VAddr page_info_address, Handle process_handle,
|
||||
VAddr address) {
|
||||
std::lock_guard lock{HLE::g_hle_lock};
|
||||
LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address);
|
||||
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
|
||||
KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
|
||||
@@ -2020,6 +2027,25 @@ static ResultCode SignalToAddress(Core::System& system, VAddr address, Svc::Sign
|
||||
count);
|
||||
}
|
||||
|
||||
static void SynchronizePreemptionState(Core::System& system) {
|
||||
auto& kernel = system.Kernel();
|
||||
|
||||
// Lock the scheduler.
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
// If the current thread is pinned, unpin it.
|
||||
KProcess* cur_process = system.Kernel().CurrentProcess();
|
||||
const auto core_id = GetCurrentCoreId(kernel);
|
||||
|
||||
if (cur_process->GetPinnedThread(core_id) == GetCurrentThreadPointer(kernel)) {
|
||||
// Clear the current thread's interrupt flag.
|
||||
GetCurrentThread(kernel).ClearInterruptFlag();
|
||||
|
||||
// Unpin the current thread.
|
||||
cur_process->UnpinCurrentThread(core_id);
|
||||
}
|
||||
}
|
||||
|
||||
static ResultCode SignalToAddress32(Core::System& system, u32 address, Svc::SignalType signal_type,
|
||||
s32 value, s32 count) {
|
||||
return SignalToAddress(system, address, signal_type, value, count);
|
||||
@@ -2738,7 +2764,7 @@ static const FunctionDef SVC_Table_32[] = {
|
||||
static const FunctionDef SVC_Table_64[] = {
|
||||
{0x00, nullptr, "Unknown"},
|
||||
{0x01, SvcWrap64<SetHeapSize>, "SetHeapSize"},
|
||||
{0x02, nullptr, "SetMemoryPermission"},
|
||||
{0x02, SvcWrap64<SetMemoryPermission>, "SetMemoryPermission"},
|
||||
{0x03, SvcWrap64<SetMemoryAttribute>, "SetMemoryAttribute"},
|
||||
{0x04, SvcWrap64<MapMemory>, "MapMemory"},
|
||||
{0x05, SvcWrap64<UnmapMemory>, "UnmapMemory"},
|
||||
@@ -2790,7 +2816,7 @@ static const FunctionDef SVC_Table_64[] = {
|
||||
{0x33, SvcWrap64<GetThreadContext>, "GetThreadContext"},
|
||||
{0x34, SvcWrap64<WaitForAddress>, "WaitForAddress"},
|
||||
{0x35, SvcWrap64<SignalToAddress>, "SignalToAddress"},
|
||||
{0x36, nullptr, "SynchronizePreemptionState"},
|
||||
{0x36, SvcWrap64<SynchronizePreemptionState>, "SynchronizePreemptionState"},
|
||||
{0x37, nullptr, "Unknown"},
|
||||
{0x38, nullptr, "Unknown"},
|
||||
{0x39, nullptr, "Unknown"},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/literals.h"
|
||||
|
||||
namespace Kernel {
|
||||
using Handle = u32;
|
||||
@@ -12,9 +13,13 @@ using Handle = u32;
|
||||
|
||||
namespace Kernel::Svc {
|
||||
|
||||
using namespace Common::Literals;
|
||||
|
||||
constexpr s32 ArgumentHandleCountMax = 0x40;
|
||||
constexpr u32 HandleWaitMask{1u << 30};
|
||||
|
||||
constexpr inline std::size_t HeapSizeAlignment = 2_MiB;
|
||||
|
||||
constexpr inline Handle InvalidHandle = Handle(0);
|
||||
|
||||
enum PseudoHandle : Handle {
|
||||
|
||||
@@ -249,6 +249,14 @@ void SvcWrap64(Core::System& system) {
|
||||
func(system, Param(system, 0), Param(system, 1), static_cast<u32>(Param(system, 2))).raw);
|
||||
}
|
||||
|
||||
// Used by SetMemoryPermission
|
||||
template <ResultCode func(Core::System&, u64, u64, Svc::MemoryPermission)>
|
||||
void SvcWrap64(Core::System& system) {
|
||||
FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
|
||||
static_cast<Svc::MemoryPermission>(Param(system, 2)))
|
||||
.raw);
|
||||
}
|
||||
|
||||
// Used by MapSharedMemory
|
||||
template <ResultCode func(Core::System&, Handle, u64, u64, Svc::MemoryPermission)>
|
||||
void SvcWrap64(Core::System& system) {
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
// Copyright 2017 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <core/hle/lock.h>
|
||||
|
||||
namespace HLE {
|
||||
std::recursive_mutex g_hle_lock;
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
// Copyright 2017 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace HLE {
|
||||
/*
|
||||
* Synchronizes access to the internal HLE kernel structures, it is acquired when a guest
|
||||
* application thread performs a syscall. It should be acquired by any host threads that read or
|
||||
* modify the HLE kernel state. Note: Any operation that directly or indirectly reads from or writes
|
||||
* to the emulated memory is not protected by this mutex, and should be avoided in any threads other
|
||||
* than the CPU thread.
|
||||
*/
|
||||
extern std::recursive_mutex g_hle_lock;
|
||||
} // namespace HLE
|
||||
@@ -6,7 +6,6 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/lock.h"
|
||||
#include "core/hle/service/bcat/backend/backend.h"
|
||||
|
||||
namespace Service::BCAT {
|
||||
@@ -29,10 +28,6 @@ DeliveryCacheProgressImpl& ProgressServiceBackend::GetImpl() {
|
||||
return impl;
|
||||
}
|
||||
|
||||
void ProgressServiceBackend::SetNeedHLELock(bool need) {
|
||||
need_hle_lock = need;
|
||||
}
|
||||
|
||||
void ProgressServiceBackend::SetTotalSize(u64 size) {
|
||||
impl.total_bytes = size;
|
||||
SignalUpdate();
|
||||
@@ -88,12 +83,7 @@ void ProgressServiceBackend::FinishDownload(ResultCode result) {
|
||||
}
|
||||
|
||||
void ProgressServiceBackend::SignalUpdate() {
|
||||
if (need_hle_lock) {
|
||||
std::lock_guard lock(HLE::g_hle_lock);
|
||||
update_event->GetWritableEvent().Signal();
|
||||
} else {
|
||||
update_event->GetWritableEvent().Signal();
|
||||
}
|
||||
update_event->GetWritableEvent().Signal();
|
||||
}
|
||||
|
||||
Backend::Backend(DirectoryGetter getter) : dir_getter(std::move(getter)) {}
|
||||
|
||||
@@ -71,10 +71,6 @@ class ProgressServiceBackend {
|
||||
public:
|
||||
~ProgressServiceBackend();
|
||||
|
||||
// Clients should call this with true if any of the functions are going to be called from a
|
||||
// non-HLE thread and this class need to lock the hle mutex. (default is false)
|
||||
void SetNeedHLELock(bool need);
|
||||
|
||||
// Sets the number of bytes total in the entire download.
|
||||
void SetTotalSize(u64 size);
|
||||
|
||||
@@ -109,7 +105,6 @@ private:
|
||||
|
||||
DeliveryCacheProgressImpl impl{};
|
||||
Kernel::KEvent* update_event;
|
||||
bool need_hle_lock = false;
|
||||
};
|
||||
|
||||
// A class representing an abstract backend for BCAT functionality.
|
||||
|
||||
@@ -33,15 +33,14 @@ void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_ti
|
||||
const auto& last_entry = seven_sixaxis_lifo.ReadCurrentEntry().state;
|
||||
next_seven_sixaxis_state.sampling_number = last_entry.sampling_number + 1;
|
||||
|
||||
// Try to read sixaxis sensor states
|
||||
const auto motion_status = console->GetMotion();
|
||||
last_global_timestamp = core_timing.GetGlobalTimeNs().count();
|
||||
|
||||
console_six_axis.is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest;
|
||||
|
||||
// This value increments every time the switch goes to sleep
|
||||
next_seven_sixaxis_state.unknown = 1;
|
||||
next_seven_sixaxis_state.timestamp = last_global_timestamp - last_saved_timestamp;
|
||||
next_seven_sixaxis_state.accel = motion_status.accel;
|
||||
// Zero gyro values as they just mess up with the camera
|
||||
// Note: Probably a correct sensivity setting must be set
|
||||
next_seven_sixaxis_state.gyro = {};
|
||||
next_seven_sixaxis_state.gyro = motion_status.gyro;
|
||||
next_seven_sixaxis_state.quaternion = {
|
||||
{
|
||||
motion_status.quaternion.xyz.y,
|
||||
@@ -52,9 +51,9 @@ void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_ti
|
||||
};
|
||||
|
||||
console_six_axis.sampling_number++;
|
||||
// TODO(German77): Find the purpose of those values
|
||||
console_six_axis.verticalization_error = 0.0f;
|
||||
console_six_axis.gyro_bias = {0.0f, 0.0f, 0.0f};
|
||||
console_six_axis.is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest;
|
||||
console_six_axis.verticalization_error = motion_status.verticalization_error;
|
||||
console_six_axis.gyro_bias = motion_status.gyro_bias;
|
||||
|
||||
// Update console six axis shared memory
|
||||
std::memcpy(data + SHARED_MEMORY_OFFSET, &console_six_axis, sizeof(console_six_axis));
|
||||
@@ -69,7 +68,6 @@ void Controller_ConsoleSixAxis::SetTransferMemoryPointer(u8* t_mem) {
|
||||
}
|
||||
|
||||
void Controller_ConsoleSixAxis::ResetTimestamp() {
|
||||
seven_sixaxis_lifo.buffer_count = 0;
|
||||
seven_sixaxis_lifo.buffer_tail = 0;
|
||||
last_saved_timestamp = last_global_timestamp;
|
||||
}
|
||||
} // namespace Service::HID
|
||||
|
||||
@@ -39,8 +39,9 @@ public:
|
||||
|
||||
private:
|
||||
struct SevenSixAxisState {
|
||||
INSERT_PADDING_WORDS(4); // unused
|
||||
s64 sampling_number{};
|
||||
INSERT_PADDING_WORDS(2); // unused
|
||||
u64 timestamp{};
|
||||
u64 sampling_number{};
|
||||
u64 unknown{};
|
||||
Common::Vec3f accel{};
|
||||
Common::Vec3f gyro{};
|
||||
@@ -52,9 +53,10 @@ private:
|
||||
struct ConsoleSharedMemory {
|
||||
u64 sampling_number{};
|
||||
bool is_seven_six_axis_sensor_at_rest{};
|
||||
INSERT_PADDING_BYTES(4); // padding
|
||||
INSERT_PADDING_BYTES(3); // padding
|
||||
f32 verticalization_error{};
|
||||
Common::Vec3f gyro_bias{};
|
||||
INSERT_PADDING_BYTES(4); // padding
|
||||
};
|
||||
static_assert(sizeof(ConsoleSharedMemory) == 0x20, "ConsoleSharedMemory is an invalid size");
|
||||
|
||||
@@ -64,6 +66,8 @@ private:
|
||||
Core::HID::EmulatedConsole* console;
|
||||
u8* transfer_memory = nullptr;
|
||||
bool is_transfer_memory_set = false;
|
||||
u64 last_saved_timestamp{};
|
||||
u64 last_global_timestamp{};
|
||||
ConsoleSharedMemory console_six_axis{};
|
||||
SevenSixAxisState next_seven_sixaxis_state{};
|
||||
};
|
||||
|
||||
@@ -66,9 +66,9 @@ Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_,
|
||||
auto& controller = controller_data[i];
|
||||
controller.device = hid_core.GetEmulatedControllerByIndex(i);
|
||||
controller.vibration[Core::HID::EmulatedDeviceIndex::LeftIndex].latest_vibration_value =
|
||||
DEFAULT_VIBRATION_VALUE;
|
||||
Core::HID::DEFAULT_VIBRATION_VALUE;
|
||||
controller.vibration[Core::HID::EmulatedDeviceIndex::RightIndex].latest_vibration_value =
|
||||
DEFAULT_VIBRATION_VALUE;
|
||||
Core::HID::DEFAULT_VIBRATION_VALUE;
|
||||
Core::HID::ControllerUpdateCallback engine_callback{
|
||||
.on_change = [this,
|
||||
i](Core::HID::ControllerTriggerType type) { ControllerUpdate(type, i); },
|
||||
@@ -781,7 +781,8 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
|
||||
Core::HID::VibrationValue vibration{0.0f, 160.0f, 0.0f, 320.0f};
|
||||
controller.device->SetVibration(device_index, vibration);
|
||||
// Then reset the vibration value to its default value.
|
||||
controller.vibration[device_index].latest_vibration_value = DEFAULT_VIBRATION_VALUE;
|
||||
controller.vibration[device_index].latest_vibration_value =
|
||||
Core::HID::DEFAULT_VIBRATION_VALUE;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -90,13 +90,6 @@ public:
|
||||
Default = 3,
|
||||
};
|
||||
|
||||
static constexpr Core::HID::VibrationValue DEFAULT_VIBRATION_VALUE{
|
||||
.low_amplitude = 0.0f,
|
||||
.low_frequency = 160.0f,
|
||||
.high_amplitude = 0.0f,
|
||||
.high_frequency = 320.0f,
|
||||
};
|
||||
|
||||
void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set);
|
||||
Core::HID::NpadStyleTag GetSupportedStyleSet() const;
|
||||
|
||||
|
||||
@@ -1404,7 +1404,7 @@ void Hid::SendVibrationGcErmCommand(Kernel::HLERequestContext& ctx) {
|
||||
.high_frequency = 0.0f,
|
||||
};
|
||||
default:
|
||||
return Controller_NPad::DEFAULT_VIBRATION_VALUE;
|
||||
return Core::HID::DEFAULT_VIBRATION_VALUE;
|
||||
}
|
||||
}();
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/lock.h"
|
||||
#include "core/hle/service/nfp/nfp.h"
|
||||
#include "core/hle/service/nfp/nfp_user.h"
|
||||
|
||||
@@ -337,7 +336,6 @@ void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) {
|
||||
std::lock_guard lock{HLE::g_hle_lock};
|
||||
if (buffer.size() < sizeof(AmiiboFile)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -71,7 +71,6 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::KProcess& process,
|
||||
kip->GetTitleID(), 0xFFFFFFFFFFFFFFFF, 0x1FE00000,
|
||||
kip->GetKernelCapabilities());
|
||||
|
||||
const VAddr base_address = process.PageTable().GetCodeRegionStart();
|
||||
Kernel::CodeSet codeset;
|
||||
Kernel::PhysicalMemory program_image;
|
||||
|
||||
@@ -91,7 +90,14 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::KProcess& process,
|
||||
program_image.resize(PageAlignSize(kip->GetBSSOffset()) + kip->GetBSSSize());
|
||||
codeset.DataSegment().size += kip->GetBSSSize();
|
||||
|
||||
// Setup the process code layout
|
||||
if (process.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), program_image.size())
|
||||
.IsError()) {
|
||||
return {ResultStatus::ErrorNotInitialized, {}};
|
||||
}
|
||||
|
||||
codeset.memory = std::move(program_image);
|
||||
const VAddr base_address = process.PageTable().GetCodeRegionStart();
|
||||
process.LoadModule(std::move(codeset), base_address);
|
||||
|
||||
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", kip->GetName(), base_address);
|
||||
|
||||
@@ -69,7 +69,7 @@ private:
|
||||
libusb_device_handle* handle{};
|
||||
};
|
||||
|
||||
GCAdapter::GCAdapter(const std::string& input_engine_) : InputEngine(input_engine_) {
|
||||
GCAdapter::GCAdapter(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
|
||||
if (usb_adapter_handle) {
|
||||
return;
|
||||
}
|
||||
@@ -325,8 +325,8 @@ bool GCAdapter::GetGCEndpoint(libusb_device* device) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Common::Input::VibrationError GCAdapter::SetRumble(const PadIdentifier& identifier,
|
||||
const Common::Input::VibrationStatus vibration) {
|
||||
Common::Input::VibrationError GCAdapter::SetRumble(
|
||||
const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) {
|
||||
const auto mean_amplitude = (vibration.low_amplitude + vibration.high_amplitude) * 0.5f;
|
||||
const auto processed_amplitude =
|
||||
static_cast<u8>((mean_amplitude + std::pow(mean_amplitude, 0.3f)) * 0.5f * 0x8);
|
||||
|
||||
@@ -22,13 +22,13 @@ namespace InputCommon {
|
||||
class LibUSBContext;
|
||||
class LibUSBDeviceHandle;
|
||||
|
||||
class GCAdapter : public InputCommon::InputEngine {
|
||||
class GCAdapter : public InputEngine {
|
||||
public:
|
||||
explicit GCAdapter(const std::string& input_engine_);
|
||||
~GCAdapter();
|
||||
explicit GCAdapter(std::string input_engine_);
|
||||
~GCAdapter() override;
|
||||
|
||||
Common::Input::VibrationError SetRumble(
|
||||
const PadIdentifier& identifier, const Common::Input::VibrationStatus vibration) override;
|
||||
const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
|
||||
|
||||
/// Used for automapping features
|
||||
std::vector<Common::ParamPackage> GetInputDevices() const override;
|
||||
|
||||
@@ -24,7 +24,7 @@ constexpr PadIdentifier keyboard_modifier_identifier = {
|
||||
.pad = 1,
|
||||
};
|
||||
|
||||
Keyboard::Keyboard(const std::string& input_engine_) : InputEngine(input_engine_) {
|
||||
Keyboard::Keyboard(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
|
||||
// Keyboard is broken into 3 diferent sets:
|
||||
// key: Unfiltered intended for controllers.
|
||||
// keyboard_key: Allows only Settings::NativeKeyboard::Keys intended for keyboard emulation.
|
||||
|
||||
@@ -12,9 +12,9 @@ namespace InputCommon {
|
||||
* A button device factory representing a keyboard. It receives keyboard events and forward them
|
||||
* to all button devices it created.
|
||||
*/
|
||||
class Keyboard final : public InputCommon::InputEngine {
|
||||
class Keyboard final : public InputEngine {
|
||||
public:
|
||||
explicit Keyboard(const std::string& input_engine_);
|
||||
explicit Keyboard(std::string input_engine_);
|
||||
|
||||
/**
|
||||
* Sets the status of all buttons bound with the key to pressed
|
||||
|
||||
@@ -24,7 +24,7 @@ constexpr PadIdentifier identifier = {
|
||||
.pad = 0,
|
||||
};
|
||||
|
||||
Mouse::Mouse(const std::string& input_engine_) : InputEngine(input_engine_) {
|
||||
Mouse::Mouse(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
|
||||
PreSetController(identifier);
|
||||
PreSetAxis(identifier, mouse_axis_x);
|
||||
PreSetAxis(identifier, mouse_axis_y);
|
||||
|
||||
@@ -27,9 +27,9 @@ enum class MouseButton {
|
||||
* A button device factory representing a keyboard. It receives keyboard events and forward them
|
||||
* to all button devices it created.
|
||||
*/
|
||||
class Mouse final : public InputCommon::InputEngine {
|
||||
class Mouse final : public InputEngine {
|
||||
public:
|
||||
explicit Mouse(const std::string& input_engine_);
|
||||
explicit Mouse(std::string input_engine_);
|
||||
|
||||
/**
|
||||
* Signals that mouse has moved.
|
||||
|
||||
@@ -88,7 +88,7 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
BasicMotion GetMotion() {
|
||||
const BasicMotion& GetMotion() const {
|
||||
return motion;
|
||||
}
|
||||
|
||||
@@ -367,7 +367,7 @@ void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) {
|
||||
if (joystick->UpdateMotion(event.csensor)) {
|
||||
const PadIdentifier identifier = joystick->GetPadIdentifier();
|
||||
SetMotion(identifier, 0, joystick->GetMotion());
|
||||
};
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -387,7 +387,7 @@ void SDLDriver::CloseJoysticks() {
|
||||
joystick_map.clear();
|
||||
}
|
||||
|
||||
SDLDriver::SDLDriver(const std::string& input_engine_) : InputEngine(input_engine_) {
|
||||
SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
|
||||
if (!Settings::values.enable_raw_input) {
|
||||
// Disable raw input. When enabled this setting causes SDL to die when a web applet opens
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, "0");
|
||||
@@ -403,10 +403,11 @@ SDLDriver::SDLDriver(const std::string& input_engine_) : InputEngine(input_engin
|
||||
|
||||
// Use hidapi driver for joycons. This will allow joycons to be detected as a GameController and
|
||||
// not a generic one
|
||||
SDL_SetHint("SDL_JOYSTICK_HIDAPI_JOY_CONS", "1");
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1");
|
||||
|
||||
// Turn off Pro controller home led
|
||||
SDL_SetHint("SDL_JOYSTICK_HIDAPI_SWITCH_HOME_LED", "0");
|
||||
// Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native
|
||||
// driver on Linux.
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_XBOX, "0");
|
||||
|
||||
// If the frontend is going to manage the event loop, then we don't start one here
|
||||
start_thread = SDL_WasInit(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) == 0;
|
||||
@@ -491,8 +492,9 @@ std::vector<Common::ParamPackage> SDLDriver::GetInputDevices() const {
|
||||
}
|
||||
return devices;
|
||||
}
|
||||
Common::Input::VibrationError SDLDriver::SetRumble(const PadIdentifier& identifier,
|
||||
const Common::Input::VibrationStatus vibration) {
|
||||
|
||||
Common::Input::VibrationError SDLDriver::SetRumble(
|
||||
const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) {
|
||||
const auto joystick =
|
||||
GetSDLJoystickByGUID(identifier.guid.Format(), static_cast<int>(identifier.port));
|
||||
const auto process_amplitude_exp = [](f32 amplitude, f32 factor) {
|
||||
@@ -526,6 +528,7 @@ Common::Input::VibrationError SDLDriver::SetRumble(const PadIdentifier& identifi
|
||||
|
||||
return Common::Input::VibrationError::None;
|
||||
}
|
||||
|
||||
Common::ParamPackage SDLDriver::BuildAnalogParamPackageForButton(int port, std::string guid,
|
||||
s32 axis, float value) const {
|
||||
Common::ParamPackage params{};
|
||||
|
||||
@@ -19,19 +19,19 @@ using SDL_GameController = struct _SDL_GameController;
|
||||
using SDL_Joystick = struct _SDL_Joystick;
|
||||
using SDL_JoystickID = s32;
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
class SDLJoystick;
|
||||
|
||||
using ButtonBindings =
|
||||
std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 17>;
|
||||
using ZButtonBindings =
|
||||
std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>;
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
class SDLJoystick;
|
||||
|
||||
class SDLDriver : public InputCommon::InputEngine {
|
||||
class SDLDriver : public InputEngine {
|
||||
public:
|
||||
/// Initializes and registers SDL device factories
|
||||
SDLDriver(const std::string& input_engine_);
|
||||
explicit SDLDriver(std::string input_engine_);
|
||||
|
||||
/// Unregisters SDL device factories and shut them down.
|
||||
~SDLDriver() override;
|
||||
@@ -59,7 +59,7 @@ public:
|
||||
u8 GetHatButtonId(const std::string& direction_name) const override;
|
||||
|
||||
Common::Input::VibrationError SetRumble(
|
||||
const PadIdentifier& identifier, const Common::Input::VibrationStatus vibration) override;
|
||||
const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
|
||||
|
||||
private:
|
||||
void InitJoystick(int joystick_index);
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include <regex>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/fs/file.h"
|
||||
@@ -15,7 +14,7 @@
|
||||
|
||||
namespace InputCommon::TasInput {
|
||||
|
||||
enum TasAxes : u8 {
|
||||
enum class Tas::TasAxis : u8 {
|
||||
StickX,
|
||||
StickY,
|
||||
SubstickX,
|
||||
@@ -47,7 +46,7 @@ constexpr std::array<std::pair<std::string_view, TasButton>, 20> text_to_tas_but
|
||||
{"KEY_ZR", TasButton::TRIGGER_ZR},
|
||||
};
|
||||
|
||||
Tas::Tas(const std::string& input_engine_) : InputCommon::InputEngine(input_engine_) {
|
||||
Tas::Tas(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
|
||||
for (size_t player_index = 0; player_index < PLAYER_NUMBER; player_index++) {
|
||||
PadIdentifier identifier{
|
||||
.guid = Common::UUID{},
|
||||
@@ -66,7 +65,7 @@ Tas::Tas(const std::string& input_engine_) : InputCommon::InputEngine(input_engi
|
||||
|
||||
Tas::~Tas() {
|
||||
Stop();
|
||||
};
|
||||
}
|
||||
|
||||
void Tas::LoadTasFiles() {
|
||||
script_length = 0;
|
||||
@@ -79,43 +78,43 @@ void Tas::LoadTasFiles() {
|
||||
}
|
||||
|
||||
void Tas::LoadTasFile(size_t player_index, size_t file_index) {
|
||||
if (!commands[player_index].empty()) {
|
||||
commands[player_index].clear();
|
||||
}
|
||||
commands[player_index].clear();
|
||||
|
||||
std::string file = Common::FS::ReadStringFromFile(
|
||||
Common::FS::GetYuzuPath(Common::FS::YuzuPath::TASDir) /
|
||||
fmt::format("script{}-{}.txt", file_index, player_index + 1),
|
||||
Common::FS::FileType::BinaryFile);
|
||||
std::stringstream command_line(file);
|
||||
std::istringstream command_line(file);
|
||||
std::string line;
|
||||
int frame_no = 0;
|
||||
while (std::getline(command_line, line, '\n')) {
|
||||
if (line.empty()) {
|
||||
continue;
|
||||
}
|
||||
std::smatch m;
|
||||
|
||||
std::stringstream linestream(line);
|
||||
std::string segment;
|
||||
std::vector<std::string> seglist;
|
||||
|
||||
while (std::getline(linestream, segment, ' ')) {
|
||||
seglist.push_back(segment);
|
||||
std::vector<std::string> seg_list;
|
||||
{
|
||||
std::istringstream line_stream(line);
|
||||
std::string segment;
|
||||
while (std::getline(line_stream, segment, ' ')) {
|
||||
seg_list.push_back(std::move(segment));
|
||||
}
|
||||
}
|
||||
|
||||
if (seglist.size() < 4) {
|
||||
if (seg_list.size() < 4) {
|
||||
continue;
|
||||
}
|
||||
|
||||
while (frame_no < std::stoi(seglist.at(0))) {
|
||||
commands[player_index].push_back({});
|
||||
const auto num_frames = std::stoi(seg_list[0]);
|
||||
while (frame_no < num_frames) {
|
||||
commands[player_index].emplace_back();
|
||||
frame_no++;
|
||||
}
|
||||
|
||||
TASCommand command = {
|
||||
.buttons = ReadCommandButtons(seglist.at(1)),
|
||||
.l_axis = ReadCommandAxis(seglist.at(2)),
|
||||
.r_axis = ReadCommandAxis(seglist.at(3)),
|
||||
.buttons = ReadCommandButtons(seg_list[1]),
|
||||
.l_axis = ReadCommandAxis(seg_list[2]),
|
||||
.r_axis = ReadCommandAxis(seg_list[3]),
|
||||
};
|
||||
commands[player_index].push_back(command);
|
||||
frame_no++;
|
||||
@@ -123,16 +122,17 @@ void Tas::LoadTasFile(size_t player_index, size_t file_index) {
|
||||
LOG_INFO(Input, "TAS file loaded! {} frames", frame_no);
|
||||
}
|
||||
|
||||
void Tas::WriteTasFile(std::u8string file_name) {
|
||||
void Tas::WriteTasFile(std::u8string_view file_name) {
|
||||
std::string output_text;
|
||||
for (size_t frame = 0; frame < record_commands.size(); frame++) {
|
||||
const TASCommand& line = record_commands[frame];
|
||||
output_text += fmt::format("{} {} {} {}\n", frame, WriteCommandButtons(line.buttons),
|
||||
WriteCommandAxis(line.l_axis), WriteCommandAxis(line.r_axis));
|
||||
}
|
||||
const auto bytes_written = Common::FS::WriteStringToFile(
|
||||
Common::FS::GetYuzuPath(Common::FS::YuzuPath::TASDir) / file_name,
|
||||
Common::FS::FileType::TextFile, output_text);
|
||||
|
||||
const auto tas_file_name = Common::FS::GetYuzuPath(Common::FS::YuzuPath::TASDir) / file_name;
|
||||
const auto bytes_written =
|
||||
Common::FS::WriteStringToFile(tas_file_name, Common::FS::FileType::TextFile, output_text);
|
||||
if (bytes_written == output_text.size()) {
|
||||
LOG_INFO(Input, "TAS file written to file!");
|
||||
} else {
|
||||
@@ -205,10 +205,10 @@ void Tas::UpdateThread() {
|
||||
const int button = static_cast<int>(i);
|
||||
SetButton(identifier, button, button_status);
|
||||
}
|
||||
SetAxis(identifier, TasAxes::StickX, command.l_axis.x);
|
||||
SetAxis(identifier, TasAxes::StickY, command.l_axis.y);
|
||||
SetAxis(identifier, TasAxes::SubstickX, command.r_axis.x);
|
||||
SetAxis(identifier, TasAxes::SubstickY, command.r_axis.y);
|
||||
SetTasAxis(identifier, TasAxis::StickX, command.l_axis.x);
|
||||
SetTasAxis(identifier, TasAxis::StickY, command.l_axis.y);
|
||||
SetTasAxis(identifier, TasAxis::SubstickX, command.r_axis.x);
|
||||
SetTasAxis(identifier, TasAxis::SubstickY, command.r_axis.y);
|
||||
}
|
||||
} else {
|
||||
is_running = Settings::values.tas_loop.GetValue();
|
||||
@@ -224,27 +224,28 @@ void Tas::ClearInput() {
|
||||
}
|
||||
|
||||
TasAnalog Tas::ReadCommandAxis(const std::string& line) const {
|
||||
std::stringstream linestream(line);
|
||||
std::string segment;
|
||||
std::vector<std::string> seglist;
|
||||
|
||||
while (std::getline(linestream, segment, ';')) {
|
||||
seglist.push_back(segment);
|
||||
std::vector<std::string> seg_list;
|
||||
{
|
||||
std::istringstream line_stream(line);
|
||||
std::string segment;
|
||||
while (std::getline(line_stream, segment, ';')) {
|
||||
seg_list.push_back(std::move(segment));
|
||||
}
|
||||
}
|
||||
|
||||
const float x = std::stof(seglist.at(0)) / 32767.0f;
|
||||
const float y = std::stof(seglist.at(1)) / 32767.0f;
|
||||
const float x = std::stof(seg_list.at(0)) / 32767.0f;
|
||||
const float y = std::stof(seg_list.at(1)) / 32767.0f;
|
||||
|
||||
return {x, y};
|
||||
}
|
||||
|
||||
u64 Tas::ReadCommandButtons(const std::string& data) const {
|
||||
std::stringstream button_text(data);
|
||||
std::string line;
|
||||
u64 Tas::ReadCommandButtons(const std::string& line) const {
|
||||
std::istringstream button_text(line);
|
||||
std::string button_line;
|
||||
u64 buttons = 0;
|
||||
while (std::getline(button_text, line, ';')) {
|
||||
for (auto [text, tas_button] : text_to_tas_button) {
|
||||
if (text == line) {
|
||||
while (std::getline(button_text, button_line, ';')) {
|
||||
for (const auto& [text, tas_button] : text_to_tas_button) {
|
||||
if (text == button_line) {
|
||||
buttons |= static_cast<u64>(tas_button);
|
||||
break;
|
||||
}
|
||||
@@ -254,8 +255,8 @@ u64 Tas::ReadCommandButtons(const std::string& data) const {
|
||||
}
|
||||
|
||||
std::string Tas::WriteCommandButtons(u64 buttons) const {
|
||||
std::string returns = "";
|
||||
for (auto [text_button, tas_button] : text_to_tas_button) {
|
||||
std::string returns;
|
||||
for (const auto& [text_button, tas_button] : text_to_tas_button) {
|
||||
if ((buttons & static_cast<u64>(tas_button)) != 0) {
|
||||
returns += fmt::format("{};", text_button);
|
||||
}
|
||||
@@ -267,6 +268,10 @@ std::string Tas::WriteCommandAxis(TasAnalog analog) const {
|
||||
return fmt::format("{};{}", analog.x * 32767, analog.y * 32767);
|
||||
}
|
||||
|
||||
void Tas::SetTasAxis(const PadIdentifier& identifier, TasAxis axis, f32 value) {
|
||||
SetAxis(identifier, static_cast<int>(axis), value);
|
||||
}
|
||||
|
||||
void Tas::StartStop() {
|
||||
if (!Settings::values.tas_enable) {
|
||||
return;
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/settings_input.h"
|
||||
#include "input_common/input_engine.h"
|
||||
#include "input_common/main.h"
|
||||
|
||||
/*
|
||||
To play back TAS scripts on Yuzu, select the folder with scripts in the configuration menu below
|
||||
@@ -81,46 +81,46 @@ enum class TasState {
|
||||
Stopped,
|
||||
};
|
||||
|
||||
class Tas final : public InputCommon::InputEngine {
|
||||
class Tas final : public InputEngine {
|
||||
public:
|
||||
explicit Tas(const std::string& input_engine_);
|
||||
~Tas();
|
||||
explicit Tas(std::string input_engine_);
|
||||
~Tas() override;
|
||||
|
||||
/**
|
||||
* Changes the input status that will be stored in each frame
|
||||
* @param buttons: bitfield with the status of the buttons
|
||||
* @param left_axis: value of the left axis
|
||||
* @param right_axis: value of the right axis
|
||||
* @param buttons Bitfield with the status of the buttons
|
||||
* @param left_axis Value of the left axis
|
||||
* @param right_axis Value of the right axis
|
||||
*/
|
||||
void RecordInput(u64 buttons, TasAnalog left_axis, TasAnalog right_axis);
|
||||
|
||||
// Main loop that records or executes input
|
||||
void UpdateThread();
|
||||
|
||||
// Sets the flag to start or stop the TAS command excecution and swaps controllers profiles
|
||||
// Sets the flag to start or stop the TAS command execution and swaps controllers profiles
|
||||
void StartStop();
|
||||
|
||||
// Stop the TAS and reverts any controller profile
|
||||
// Stop the TAS and reverts any controller profile
|
||||
void Stop();
|
||||
|
||||
// Sets the flag to reload the file and start from the begining in the next update
|
||||
// Sets the flag to reload the file and start from the beginning in the next update
|
||||
void Reset();
|
||||
|
||||
/**
|
||||
* Sets the flag to enable or disable recording of inputs
|
||||
* @return Returns true if the current recording status is enabled
|
||||
* @returns true if the current recording status is enabled
|
||||
*/
|
||||
bool Record();
|
||||
|
||||
/**
|
||||
* Saves contents of record_commands on a file
|
||||
* @param overwrite_file: Indicates if player 1 should be overwritten
|
||||
* @param overwrite_file Indicates if player 1 should be overwritten
|
||||
*/
|
||||
void SaveRecording(bool overwrite_file);
|
||||
|
||||
/**
|
||||
* Returns the current status values of TAS playback/recording
|
||||
* @return Tuple of
|
||||
* @returns A Tuple of
|
||||
* TasState indicating the current state out of Running ;
|
||||
* Current playback progress ;
|
||||
* Total length of script file currently loaded or being recorded
|
||||
@@ -128,6 +128,8 @@ public:
|
||||
std::tuple<TasState, size_t, size_t> GetStatus() const;
|
||||
|
||||
private:
|
||||
enum class TasAxis : u8;
|
||||
|
||||
struct TASCommand {
|
||||
u64 buttons{};
|
||||
TasAnalog l_axis{};
|
||||
@@ -137,29 +139,31 @@ private:
|
||||
/// Loads TAS files from all players
|
||||
void LoadTasFiles();
|
||||
|
||||
/** Loads TAS file from the specified player
|
||||
* @param player_index: player number to save the script
|
||||
* @param file_index: script number of the file
|
||||
/**
|
||||
* Loads TAS file from the specified player
|
||||
* @param player_index Player number to save the script
|
||||
* @param file_index Script number of the file
|
||||
*/
|
||||
void LoadTasFile(size_t player_index, size_t file_index);
|
||||
|
||||
/** Writes a TAS file from the recorded commands
|
||||
* @param file_name: name of the file to be written
|
||||
/**
|
||||
* Writes a TAS file from the recorded commands
|
||||
* @param file_name Name of the file to be written
|
||||
*/
|
||||
void WriteTasFile(std::u8string file_name);
|
||||
void WriteTasFile(std::u8string_view file_name);
|
||||
|
||||
/**
|
||||
* Parses a string containing the axis values. X and Y have a range from -32767 to 32767
|
||||
* @param line: string containing axis values with the following format "x;y"
|
||||
* @return Returns a TAS analog object with axis values with range from -1.0 to 1.0
|
||||
* @param line String containing axis values with the following format "x;y"
|
||||
* @returns A TAS analog object with axis values with range from -1.0 to 1.0
|
||||
*/
|
||||
TasAnalog ReadCommandAxis(const std::string& line) const;
|
||||
|
||||
/**
|
||||
* Parses a string containing the button values. Each button is represented by it's text format
|
||||
* specified in text_to_tas_button array
|
||||
* @param line: string containing button name with the following format "a;b;c;d..."
|
||||
* @return Returns a u64 with each bit representing the status of a button
|
||||
* @param line string containing button name with the following format "a;b;c;d..."
|
||||
* @returns A u64 with each bit representing the status of a button
|
||||
*/
|
||||
u64 ReadCommandButtons(const std::string& line) const;
|
||||
|
||||
@@ -170,17 +174,20 @@ private:
|
||||
|
||||
/**
|
||||
* Converts an u64 containing the button status into the text equivalent
|
||||
* @param buttons: bitfield with the status of the buttons
|
||||
* @return Returns a string with the name of the buttons to be written to the file
|
||||
* @param buttons Bitfield with the status of the buttons
|
||||
* @returns A string with the name of the buttons to be written to the file
|
||||
*/
|
||||
std::string WriteCommandButtons(u64 buttons) const;
|
||||
|
||||
/**
|
||||
* Converts an TAS analog object containing the axis status into the text equivalent
|
||||
* @param data: value of the axis
|
||||
* @return A string with the value of the axis to be written to the file
|
||||
* @param analog Value of the axis
|
||||
* @returns A string with the value of the axis to be written to the file
|
||||
*/
|
||||
std::string WriteCommandAxis(TasAnalog data) const;
|
||||
std::string WriteCommandAxis(TasAnalog analog) const;
|
||||
|
||||
/// Sets an axis for a particular pad to the given value.
|
||||
void SetTasAxis(const PadIdentifier& identifier, TasAxis axis, f32 value);
|
||||
|
||||
size_t script_length{0};
|
||||
bool is_recording{false};
|
||||
|
||||
@@ -13,7 +13,7 @@ constexpr PadIdentifier identifier = {
|
||||
.pad = 0,
|
||||
};
|
||||
|
||||
TouchScreen::TouchScreen(const std::string& input_engine_) : InputEngine(input_engine_) {
|
||||
TouchScreen::TouchScreen(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
|
||||
PreSetController(identifier);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,9 +12,9 @@ namespace InputCommon {
|
||||
* A button device factory representing a keyboard. It receives keyboard events and forward them
|
||||
* to all button devices it created.
|
||||
*/
|
||||
class TouchScreen final : public InputCommon::InputEngine {
|
||||
class TouchScreen final : public InputEngine {
|
||||
public:
|
||||
explicit TouchScreen(const std::string& input_engine_);
|
||||
explicit TouchScreen(std::string input_engine_);
|
||||
|
||||
/**
|
||||
* Signals that mouse has moved.
|
||||
|
||||
@@ -136,7 +136,7 @@ static void SocketLoop(Socket* socket) {
|
||||
socket->Loop();
|
||||
}
|
||||
|
||||
UDPClient::UDPClient(const std::string& input_engine_) : InputEngine(input_engine_) {
|
||||
UDPClient::UDPClient(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
|
||||
LOG_INFO(Input, "Udp Initialization started");
|
||||
ReloadSockets();
|
||||
}
|
||||
@@ -536,42 +536,46 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
|
||||
std::function<void(u16, u16, u16, u16)> data_callback) {
|
||||
|
||||
std::thread([=, this] {
|
||||
u16 min_x{UINT16_MAX};
|
||||
u16 min_y{UINT16_MAX};
|
||||
u16 max_x{};
|
||||
u16 max_y{};
|
||||
|
||||
Status current_status{Status::Initialized};
|
||||
SocketCallback callback{
|
||||
[](Response::Version) {}, [](Response::PortInfo) {},
|
||||
[&](Response::PadData data) {
|
||||
static constexpr u16 CALIBRATION_THRESHOLD = 100;
|
||||
static constexpr u16 MAX_VALUE = UINT16_MAX;
|
||||
SocketCallback callback{[](Response::Version) {}, [](Response::PortInfo) {},
|
||||
[&](Response::PadData data) {
|
||||
constexpr u16 CALIBRATION_THRESHOLD = 100;
|
||||
|
||||
if (current_status == Status::Initialized) {
|
||||
// Receiving data means the communication is ready now
|
||||
current_status = Status::Ready;
|
||||
status_callback(current_status);
|
||||
}
|
||||
const auto& touchpad_0 = data.touch[0];
|
||||
if (touchpad_0.is_active == 0) {
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG(Input, "Current touch: {} {}", touchpad_0.x, touchpad_0.y);
|
||||
const u16 min_x = std::min(MAX_VALUE, static_cast<u16>(touchpad_0.x));
|
||||
const u16 min_y = std::min(MAX_VALUE, static_cast<u16>(touchpad_0.y));
|
||||
if (current_status == Status::Ready) {
|
||||
// First touch - min data (min_x/min_y)
|
||||
current_status = Status::Stage1Completed;
|
||||
status_callback(current_status);
|
||||
}
|
||||
if (touchpad_0.x - min_x > CALIBRATION_THRESHOLD &&
|
||||
touchpad_0.y - min_y > CALIBRATION_THRESHOLD) {
|
||||
// Set the current position as max value and finishes configuration
|
||||
const u16 max_x = touchpad_0.x;
|
||||
const u16 max_y = touchpad_0.y;
|
||||
current_status = Status::Completed;
|
||||
data_callback(min_x, min_y, max_x, max_y);
|
||||
status_callback(current_status);
|
||||
if (current_status == Status::Initialized) {
|
||||
// Receiving data means the communication is ready now
|
||||
current_status = Status::Ready;
|
||||
status_callback(current_status);
|
||||
}
|
||||
if (data.touch[0].is_active == 0) {
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG(Input, "Current touch: {} {}", data.touch[0].x,
|
||||
data.touch[0].y);
|
||||
min_x = std::min(min_x, static_cast<u16>(data.touch[0].x));
|
||||
min_y = std::min(min_y, static_cast<u16>(data.touch[0].y));
|
||||
if (current_status == Status::Ready) {
|
||||
// First touch - min data (min_x/min_y)
|
||||
current_status = Status::Stage1Completed;
|
||||
status_callback(current_status);
|
||||
}
|
||||
if (data.touch[0].x - min_x > CALIBRATION_THRESHOLD &&
|
||||
data.touch[0].y - min_y > CALIBRATION_THRESHOLD) {
|
||||
// Set the current position as max value and finishes
|
||||
// configuration
|
||||
max_x = data.touch[0].x;
|
||||
max_y = data.touch[0].y;
|
||||
current_status = Status::Completed;
|
||||
data_callback(min_x, min_y, max_x, max_y);
|
||||
status_callback(current_status);
|
||||
|
||||
complete_event.Set();
|
||||
}
|
||||
}};
|
||||
complete_event.Set();
|
||||
}
|
||||
}};
|
||||
Socket socket{host, port, std::move(callback)};
|
||||
std::thread worker_thread{SocketLoop, &socket};
|
||||
complete_event.Wait();
|
||||
|
||||
@@ -49,10 +49,10 @@ struct DeviceStatus {
|
||||
* A button device factory representing a keyboard. It receives keyboard events and forward them
|
||||
* to all button devices it created.
|
||||
*/
|
||||
class UDPClient final : public InputCommon::InputEngine {
|
||||
class UDPClient final : public InputEngine {
|
||||
public:
|
||||
explicit UDPClient(const std::string& input_engine_);
|
||||
~UDPClient();
|
||||
explicit UDPClient(std::string input_engine_);
|
||||
~UDPClient() override;
|
||||
|
||||
void ReloadSockets();
|
||||
|
||||
|
||||
@@ -19,23 +19,36 @@ public:
|
||||
: up(std::move(up_)), down(std::move(down_)), left(std::move(left_)),
|
||||
right(std::move(right_)), modifier(std::move(modifier_)), modifier_scale(modifier_scale_),
|
||||
modifier_angle(modifier_angle_) {
|
||||
Common::Input::InputCallback button_up_callback{
|
||||
[this](Common::Input::CallbackStatus callback_) { UpdateUpButtonStatus(callback_); }};
|
||||
Common::Input::InputCallback button_down_callback{
|
||||
[this](Common::Input::CallbackStatus callback_) { UpdateDownButtonStatus(callback_); }};
|
||||
Common::Input::InputCallback button_left_callback{
|
||||
[this](Common::Input::CallbackStatus callback_) { UpdateLeftButtonStatus(callback_); }};
|
||||
Common::Input::InputCallback button_right_callback{
|
||||
[this](Common::Input::CallbackStatus callback_) {
|
||||
UpdateRightButtonStatus(callback_);
|
||||
}};
|
||||
Common::Input::InputCallback button_modifier_callback{
|
||||
[this](Common::Input::CallbackStatus callback_) { UpdateModButtonStatus(callback_); }};
|
||||
up->SetCallback(button_up_callback);
|
||||
down->SetCallback(button_down_callback);
|
||||
left->SetCallback(button_left_callback);
|
||||
right->SetCallback(button_right_callback);
|
||||
modifier->SetCallback(button_modifier_callback);
|
||||
up->SetCallback({
|
||||
.on_change =
|
||||
[this](const Common::Input::CallbackStatus& callback_) {
|
||||
UpdateUpButtonStatus(callback_);
|
||||
},
|
||||
});
|
||||
down->SetCallback({
|
||||
.on_change =
|
||||
[this](const Common::Input::CallbackStatus& callback_) {
|
||||
UpdateDownButtonStatus(callback_);
|
||||
},
|
||||
});
|
||||
left->SetCallback({
|
||||
.on_change =
|
||||
[this](const Common::Input::CallbackStatus& callback_) {
|
||||
UpdateLeftButtonStatus(callback_);
|
||||
},
|
||||
});
|
||||
right->SetCallback({
|
||||
.on_change =
|
||||
[this](const Common::Input::CallbackStatus& callback_) {
|
||||
UpdateRightButtonStatus(callback_);
|
||||
},
|
||||
});
|
||||
modifier->SetCallback({
|
||||
.on_change =
|
||||
[this](const Common::Input::CallbackStatus& callback_) {
|
||||
UpdateModButtonStatus(callback_);
|
||||
},
|
||||
});
|
||||
last_x_axis_value = 0.0f;
|
||||
last_y_axis_value = 0.0f;
|
||||
}
|
||||
@@ -133,27 +146,27 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateUpButtonStatus(Common::Input::CallbackStatus button_callback) {
|
||||
void UpdateUpButtonStatus(const Common::Input::CallbackStatus& button_callback) {
|
||||
up_status = button_callback.button_status.value;
|
||||
UpdateStatus();
|
||||
}
|
||||
|
||||
void UpdateDownButtonStatus(Common::Input::CallbackStatus button_callback) {
|
||||
void UpdateDownButtonStatus(const Common::Input::CallbackStatus& button_callback) {
|
||||
down_status = button_callback.button_status.value;
|
||||
UpdateStatus();
|
||||
}
|
||||
|
||||
void UpdateLeftButtonStatus(Common::Input::CallbackStatus button_callback) {
|
||||
void UpdateLeftButtonStatus(const Common::Input::CallbackStatus& button_callback) {
|
||||
left_status = button_callback.button_status.value;
|
||||
UpdateStatus();
|
||||
}
|
||||
|
||||
void UpdateRightButtonStatus(Common::Input::CallbackStatus button_callback) {
|
||||
void UpdateRightButtonStatus(const Common::Input::CallbackStatus& button_callback) {
|
||||
right_status = button_callback.button_status.value;
|
||||
UpdateStatus();
|
||||
}
|
||||
|
||||
void UpdateModButtonStatus(Common::Input::CallbackStatus button_callback) {
|
||||
void UpdateModButtonStatus(const Common::Input::CallbackStatus& button_callback) {
|
||||
modifier_status = button_callback.button_status.value;
|
||||
UpdateStatus();
|
||||
}
|
||||
@@ -265,18 +278,18 @@ private:
|
||||
Button left;
|
||||
Button right;
|
||||
Button modifier;
|
||||
float modifier_scale;
|
||||
float modifier_angle;
|
||||
float modifier_scale{};
|
||||
float modifier_angle{};
|
||||
float angle{};
|
||||
float goal_angle{};
|
||||
float amplitude{};
|
||||
bool up_status;
|
||||
bool down_status;
|
||||
bool left_status;
|
||||
bool right_status;
|
||||
bool modifier_status;
|
||||
float last_x_axis_value;
|
||||
float last_y_axis_value;
|
||||
bool up_status{};
|
||||
bool down_status{};
|
||||
bool left_status{};
|
||||
bool right_status{};
|
||||
bool modifier_status{};
|
||||
float last_x_axis_value{};
|
||||
float last_y_axis_value{};
|
||||
const Common::Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false};
|
||||
std::chrono::time_point<std::chrono::steady_clock> last_update;
|
||||
};
|
||||
|
||||
@@ -14,10 +14,13 @@ public:
|
||||
using Button = std::unique_ptr<Common::Input::InputDevice>;
|
||||
TouchFromButtonDevice(Button button_, int touch_id_, float x_, float y_)
|
||||
: button(std::move(button_)), touch_id(touch_id_), x(x_), y(y_) {
|
||||
Common::Input::InputCallback button_up_callback{
|
||||
[this](Common::Input::CallbackStatus callback_) { UpdateButtonStatus(callback_); }};
|
||||
last_button_value = false;
|
||||
button->SetCallback(button_up_callback);
|
||||
button->SetCallback({
|
||||
.on_change =
|
||||
[this](const Common::Input::CallbackStatus& callback_) {
|
||||
UpdateButtonStatus(callback_);
|
||||
},
|
||||
});
|
||||
button->ForceUpdate();
|
||||
}
|
||||
|
||||
@@ -47,7 +50,7 @@ public:
|
||||
return status;
|
||||
}
|
||||
|
||||
void UpdateButtonStatus(Common::Input::CallbackStatus button_callback) {
|
||||
void UpdateButtonStatus(const Common::Input::CallbackStatus& button_callback) {
|
||||
const Common::Input::CallbackStatus status{
|
||||
.type = Common::Input::InputType::Touch,
|
||||
.touch_status = GetStatus(button_callback.button_status.value),
|
||||
|
||||
@@ -54,6 +54,18 @@ struct Message {
|
||||
template <typename T>
|
||||
constexpr Type GetMessageType();
|
||||
|
||||
template <typename T>
|
||||
Message<T> CreateMessage(const u32 magic, const T data, const u32 sender_id) {
|
||||
boost::crc_32_type crc;
|
||||
Header header{
|
||||
magic, PROTOCOL_VERSION, sizeof(T) + sizeof(Type), 0, sender_id, GetMessageType<T>(),
|
||||
};
|
||||
Message<T> message{header, data};
|
||||
crc.process_bytes(&message, sizeof(Message<T>));
|
||||
message.header.crc = crc.checksum();
|
||||
return message;
|
||||
}
|
||||
|
||||
namespace Request {
|
||||
|
||||
enum RegisterFlags : u8 {
|
||||
@@ -101,14 +113,7 @@ static_assert(std::is_trivially_copyable_v<PadData>,
|
||||
*/
|
||||
template <typename T>
|
||||
Message<T> Create(const T data, const u32 client_id = 0) {
|
||||
boost::crc_32_type crc;
|
||||
Header header{
|
||||
CLIENT_MAGIC, PROTOCOL_VERSION, sizeof(T) + sizeof(Type), 0, client_id, GetMessageType<T>(),
|
||||
};
|
||||
Message<T> message{header, data};
|
||||
crc.process_bytes(&message, sizeof(Message<T>));
|
||||
message.header.crc = crc.checksum();
|
||||
return message;
|
||||
return CreateMessage(CLIENT_MAGIC, data, client_id);
|
||||
}
|
||||
} // namespace Request
|
||||
|
||||
|
||||
@@ -10,41 +10,31 @@ namespace InputCommon {
|
||||
|
||||
void InputEngine::PreSetController(const PadIdentifier& identifier) {
|
||||
std::lock_guard lock{mutex};
|
||||
if (!controller_list.contains(identifier)) {
|
||||
controller_list.insert_or_assign(identifier, ControllerData{});
|
||||
}
|
||||
controller_list.try_emplace(identifier);
|
||||
}
|
||||
|
||||
void InputEngine::PreSetButton(const PadIdentifier& identifier, int button) {
|
||||
std::lock_guard lock{mutex};
|
||||
ControllerData& controller = controller_list.at(identifier);
|
||||
if (!controller.buttons.contains(button)) {
|
||||
controller.buttons.insert_or_assign(button, false);
|
||||
}
|
||||
controller.buttons.try_emplace(button, false);
|
||||
}
|
||||
|
||||
void InputEngine::PreSetHatButton(const PadIdentifier& identifier, int button) {
|
||||
std::lock_guard lock{mutex};
|
||||
ControllerData& controller = controller_list.at(identifier);
|
||||
if (!controller.hat_buttons.contains(button)) {
|
||||
controller.hat_buttons.insert_or_assign(button, u8{0});
|
||||
}
|
||||
controller.hat_buttons.try_emplace(button, u8{0});
|
||||
}
|
||||
|
||||
void InputEngine::PreSetAxis(const PadIdentifier& identifier, int axis) {
|
||||
std::lock_guard lock{mutex};
|
||||
ControllerData& controller = controller_list.at(identifier);
|
||||
if (!controller.axes.contains(axis)) {
|
||||
controller.axes.insert_or_assign(axis, 0.0f);
|
||||
}
|
||||
controller.axes.try_emplace(axis, 0.0f);
|
||||
}
|
||||
|
||||
void InputEngine::PreSetMotion(const PadIdentifier& identifier, int motion) {
|
||||
std::lock_guard lock{mutex};
|
||||
ControllerData& controller = controller_list.at(identifier);
|
||||
if (!controller.motions.contains(motion)) {
|
||||
controller.motions.insert_or_assign(motion, BasicMotion{});
|
||||
}
|
||||
controller.motions.try_emplace(motion);
|
||||
}
|
||||
|
||||
void InputEngine::SetButton(const PadIdentifier& identifier, int button, bool value) {
|
||||
@@ -91,7 +81,7 @@ void InputEngine::SetBattery(const PadIdentifier& identifier, BatteryLevel value
|
||||
TriggerOnBatteryChange(identifier, value);
|
||||
}
|
||||
|
||||
void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, BasicMotion value) {
|
||||
void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value) {
|
||||
{
|
||||
std::lock_guard lock{mutex};
|
||||
ControllerData& controller = controller_list.at(identifier);
|
||||
@@ -104,85 +94,93 @@ void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, BasicMo
|
||||
|
||||
bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const {
|
||||
std::lock_guard lock{mutex};
|
||||
if (!controller_list.contains(identifier)) {
|
||||
const auto controller_iter = controller_list.find(identifier);
|
||||
if (controller_iter == controller_list.cend()) {
|
||||
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(),
|
||||
identifier.pad, identifier.port);
|
||||
return false;
|
||||
}
|
||||
ControllerData controller = controller_list.at(identifier);
|
||||
if (!controller.buttons.contains(button)) {
|
||||
const ControllerData& controller = controller_iter->second;
|
||||
const auto button_iter = controller.buttons.find(button);
|
||||
if (button_iter == controller.buttons.cend()) {
|
||||
LOG_ERROR(Input, "Invalid button {}", button);
|
||||
return false;
|
||||
}
|
||||
return controller.buttons.at(button);
|
||||
return button_iter->second;
|
||||
}
|
||||
|
||||
bool InputEngine::GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const {
|
||||
std::lock_guard lock{mutex};
|
||||
if (!controller_list.contains(identifier)) {
|
||||
const auto controller_iter = controller_list.find(identifier);
|
||||
if (controller_iter == controller_list.cend()) {
|
||||
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(),
|
||||
identifier.pad, identifier.port);
|
||||
return false;
|
||||
}
|
||||
ControllerData controller = controller_list.at(identifier);
|
||||
if (!controller.hat_buttons.contains(button)) {
|
||||
const ControllerData& controller = controller_iter->second;
|
||||
const auto hat_iter = controller.hat_buttons.find(button);
|
||||
if (hat_iter == controller.hat_buttons.cend()) {
|
||||
LOG_ERROR(Input, "Invalid hat button {}", button);
|
||||
return false;
|
||||
}
|
||||
return (controller.hat_buttons.at(button) & direction) != 0;
|
||||
return (hat_iter->second & direction) != 0;
|
||||
}
|
||||
|
||||
f32 InputEngine::GetAxis(const PadIdentifier& identifier, int axis) const {
|
||||
std::lock_guard lock{mutex};
|
||||
if (!controller_list.contains(identifier)) {
|
||||
const auto controller_iter = controller_list.find(identifier);
|
||||
if (controller_iter == controller_list.cend()) {
|
||||
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(),
|
||||
identifier.pad, identifier.port);
|
||||
return 0.0f;
|
||||
}
|
||||
ControllerData controller = controller_list.at(identifier);
|
||||
if (!controller.axes.contains(axis)) {
|
||||
const ControllerData& controller = controller_iter->second;
|
||||
const auto axis_iter = controller.axes.find(axis);
|
||||
if (axis_iter == controller.axes.cend()) {
|
||||
LOG_ERROR(Input, "Invalid axis {}", axis);
|
||||
return 0.0f;
|
||||
}
|
||||
return controller.axes.at(axis);
|
||||
return axis_iter->second;
|
||||
}
|
||||
|
||||
BatteryLevel InputEngine::GetBattery(const PadIdentifier& identifier) const {
|
||||
std::lock_guard lock{mutex};
|
||||
if (!controller_list.contains(identifier)) {
|
||||
const auto controller_iter = controller_list.find(identifier);
|
||||
if (controller_iter == controller_list.cend()) {
|
||||
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(),
|
||||
identifier.pad, identifier.port);
|
||||
return BatteryLevel::Charging;
|
||||
}
|
||||
ControllerData controller = controller_list.at(identifier);
|
||||
const ControllerData& controller = controller_iter->second;
|
||||
return controller.battery;
|
||||
}
|
||||
|
||||
BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion) const {
|
||||
std::lock_guard lock{mutex};
|
||||
if (!controller_list.contains(identifier)) {
|
||||
const auto controller_iter = controller_list.find(identifier);
|
||||
if (controller_iter == controller_list.cend()) {
|
||||
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.Format(),
|
||||
identifier.pad, identifier.port);
|
||||
return {};
|
||||
}
|
||||
ControllerData controller = controller_list.at(identifier);
|
||||
const ControllerData& controller = controller_iter->second;
|
||||
return controller.motions.at(motion);
|
||||
}
|
||||
|
||||
void InputEngine::ResetButtonState() {
|
||||
for (std::pair<PadIdentifier, ControllerData> controller : controller_list) {
|
||||
for (std::pair<int, bool> button : controller.second.buttons) {
|
||||
for (const auto& controller : controller_list) {
|
||||
for (const auto& button : controller.second.buttons) {
|
||||
SetButton(controller.first, button.first, false);
|
||||
}
|
||||
for (std::pair<int, bool> button : controller.second.hat_buttons) {
|
||||
for (const auto& button : controller.second.hat_buttons) {
|
||||
SetHatButton(controller.first, button.first, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputEngine::ResetAnalogState() {
|
||||
for (std::pair<PadIdentifier, ControllerData> controller : controller_list) {
|
||||
for (std::pair<int, float> axis : controller.second.axes) {
|
||||
for (const auto& controller : controller_list) {
|
||||
for (const auto& axis : controller.second.axes) {
|
||||
SetAxis(controller.first, axis.first, 0.0);
|
||||
}
|
||||
}
|
||||
@@ -190,7 +188,7 @@ void InputEngine::ResetAnalogState() {
|
||||
|
||||
void InputEngine::TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value) {
|
||||
std::lock_guard lock{mutex_callback};
|
||||
for (const std::pair<int, InputIdentifier> poller_pair : callback_list) {
|
||||
for (const auto& poller_pair : callback_list) {
|
||||
const InputIdentifier& poller = poller_pair.second;
|
||||
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Button, button)) {
|
||||
continue;
|
||||
@@ -218,7 +216,7 @@ void InputEngine::TriggerOnButtonChange(const PadIdentifier& identifier, int but
|
||||
|
||||
void InputEngine::TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value) {
|
||||
std::lock_guard lock{mutex_callback};
|
||||
for (const std::pair<int, InputIdentifier> poller_pair : callback_list) {
|
||||
for (const auto& poller_pair : callback_list) {
|
||||
const InputIdentifier& poller = poller_pair.second;
|
||||
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::HatButton, button)) {
|
||||
continue;
|
||||
@@ -247,7 +245,7 @@ void InputEngine::TriggerOnHatButtonChange(const PadIdentifier& identifier, int
|
||||
|
||||
void InputEngine::TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value) {
|
||||
std::lock_guard lock{mutex_callback};
|
||||
for (const std::pair<int, InputIdentifier> poller_pair : callback_list) {
|
||||
for (const auto& poller_pair : callback_list) {
|
||||
const InputIdentifier& poller = poller_pair.second;
|
||||
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Analog, axis)) {
|
||||
continue;
|
||||
@@ -274,7 +272,7 @@ void InputEngine::TriggerOnAxisChange(const PadIdentifier& identifier, int axis,
|
||||
void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier,
|
||||
[[maybe_unused]] BatteryLevel value) {
|
||||
std::lock_guard lock{mutex_callback};
|
||||
for (const std::pair<int, InputIdentifier> poller_pair : callback_list) {
|
||||
for (const auto& poller_pair : callback_list) {
|
||||
const InputIdentifier& poller = poller_pair.second;
|
||||
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Battery, 0)) {
|
||||
continue;
|
||||
@@ -286,9 +284,9 @@ void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier,
|
||||
}
|
||||
|
||||
void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int motion,
|
||||
BasicMotion value) {
|
||||
const BasicMotion& value) {
|
||||
std::lock_guard lock{mutex_callback};
|
||||
for (const std::pair<int, InputIdentifier> poller_pair : callback_list) {
|
||||
for (const auto& poller_pair : callback_list) {
|
||||
const InputIdentifier& poller = poller_pair.second;
|
||||
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Motion, motion)) {
|
||||
continue;
|
||||
@@ -342,7 +340,7 @@ const std::string& InputEngine::GetEngineName() const {
|
||||
|
||||
int InputEngine::SetCallback(InputIdentifier input_identifier) {
|
||||
std::lock_guard lock{mutex_callback};
|
||||
callback_list.insert_or_assign(last_callback_key, input_identifier);
|
||||
callback_list.insert_or_assign(last_callback_key, std::move(input_identifier));
|
||||
return last_callback_key++;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,15 +23,15 @@ struct PadIdentifier {
|
||||
friend constexpr bool operator==(const PadIdentifier&, const PadIdentifier&) = default;
|
||||
};
|
||||
|
||||
// Basic motion data containing data from the sensors and a timestamp in microsecons
|
||||
// Basic motion data containing data from the sensors and a timestamp in microseconds
|
||||
struct BasicMotion {
|
||||
float gyro_x;
|
||||
float gyro_y;
|
||||
float gyro_z;
|
||||
float accel_x;
|
||||
float accel_y;
|
||||
float accel_z;
|
||||
u64 delta_timestamp;
|
||||
float gyro_x{};
|
||||
float gyro_y{};
|
||||
float gyro_z{};
|
||||
float accel_x{};
|
||||
float accel_y{};
|
||||
float accel_z{};
|
||||
u64 delta_timestamp{};
|
||||
};
|
||||
|
||||
// Stages of a battery charge
|
||||
@@ -102,9 +102,7 @@ struct InputIdentifier {
|
||||
|
||||
class InputEngine {
|
||||
public:
|
||||
explicit InputEngine(const std::string& input_engine_) : input_engine(input_engine_) {
|
||||
callback_list.clear();
|
||||
}
|
||||
explicit InputEngine(std::string input_engine_) : input_engine{std::move(input_engine_)} {}
|
||||
|
||||
virtual ~InputEngine() = default;
|
||||
|
||||
@@ -116,14 +114,12 @@ public:
|
||||
|
||||
// Sets a led pattern for a controller
|
||||
virtual void SetLeds([[maybe_unused]] const PadIdentifier& identifier,
|
||||
[[maybe_unused]] const Common::Input::LedStatus led_status) {
|
||||
return;
|
||||
}
|
||||
[[maybe_unused]] const Common::Input::LedStatus& led_status) {}
|
||||
|
||||
// Sets rumble to a controller
|
||||
virtual Common::Input::VibrationError SetRumble(
|
||||
[[maybe_unused]] const PadIdentifier& identifier,
|
||||
[[maybe_unused]] const Common::Input::VibrationStatus vibration) {
|
||||
[[maybe_unused]] const Common::Input::VibrationStatus& vibration) {
|
||||
return Common::Input::VibrationError::NotSupported;
|
||||
}
|
||||
|
||||
@@ -140,36 +136,36 @@ public:
|
||||
/// Used for automapping features
|
||||
virtual std::vector<Common::ParamPackage> GetInputDevices() const {
|
||||
return {};
|
||||
};
|
||||
}
|
||||
|
||||
/// Retrieves the button mappings for the given device
|
||||
virtual InputCommon::ButtonMapping GetButtonMappingForDevice(
|
||||
virtual ButtonMapping GetButtonMappingForDevice(
|
||||
[[maybe_unused]] const Common::ParamPackage& params) {
|
||||
return {};
|
||||
};
|
||||
}
|
||||
|
||||
/// Retrieves the analog mappings for the given device
|
||||
virtual InputCommon::AnalogMapping GetAnalogMappingForDevice(
|
||||
virtual AnalogMapping GetAnalogMappingForDevice(
|
||||
[[maybe_unused]] const Common::ParamPackage& params) {
|
||||
return {};
|
||||
};
|
||||
}
|
||||
|
||||
/// Retrieves the motion mappings for the given device
|
||||
virtual InputCommon::MotionMapping GetMotionMappingForDevice(
|
||||
virtual MotionMapping GetMotionMappingForDevice(
|
||||
[[maybe_unused]] const Common::ParamPackage& params) {
|
||||
return {};
|
||||
};
|
||||
}
|
||||
|
||||
/// Retrieves the name of the given input.
|
||||
virtual Common::Input::ButtonNames GetUIName(
|
||||
[[maybe_unused]] const Common::ParamPackage& params) const {
|
||||
return Common::Input::ButtonNames::Engine;
|
||||
};
|
||||
}
|
||||
|
||||
/// Retrieves the index number of the given hat button direction
|
||||
virtual u8 GetHatButtonId([[maybe_unused]] const std::string& direction_name) const {
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
void PreSetController(const PadIdentifier& identifier);
|
||||
void PreSetButton(const PadIdentifier& identifier, int button);
|
||||
@@ -194,7 +190,7 @@ protected:
|
||||
void SetHatButton(const PadIdentifier& identifier, int button, u8 value);
|
||||
void SetAxis(const PadIdentifier& identifier, int axis, f32 value);
|
||||
void SetBattery(const PadIdentifier& identifier, BatteryLevel value);
|
||||
void SetMotion(const PadIdentifier& identifier, int motion, BasicMotion value);
|
||||
void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value);
|
||||
|
||||
virtual std::string GetHatButtonName([[maybe_unused]] u8 direction_value) const {
|
||||
return "Unknown";
|
||||
@@ -206,14 +202,15 @@ private:
|
||||
std::unordered_map<int, u8> hat_buttons;
|
||||
std::unordered_map<int, float> axes;
|
||||
std::unordered_map<int, BasicMotion> motions;
|
||||
BatteryLevel battery;
|
||||
BatteryLevel battery{};
|
||||
};
|
||||
|
||||
void TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value);
|
||||
void TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value);
|
||||
void TriggerOnAxisChange(const PadIdentifier& identifier, int button, f32 value);
|
||||
void TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value);
|
||||
void TriggerOnBatteryChange(const PadIdentifier& identifier, BatteryLevel value);
|
||||
void TriggerOnMotionChange(const PadIdentifier& identifier, int motion, BasicMotion value);
|
||||
void TriggerOnMotionChange(const PadIdentifier& identifier, int motion,
|
||||
const BasicMotion& value);
|
||||
|
||||
bool IsInputIdentifierEqual(const InputIdentifier& input_identifier,
|
||||
const PadIdentifier& identifier, EngineInputType type,
|
||||
|
||||
@@ -14,8 +14,8 @@ public:
|
||||
MappingFactory();
|
||||
|
||||
/**
|
||||
* Resets all varables to beggin the mapping process
|
||||
* @param "type": type of input desired to be returned
|
||||
* Resets all variables to begin the mapping process
|
||||
* @param type type of input desired to be returned
|
||||
*/
|
||||
void BeginMapping(Polling::InputType type);
|
||||
|
||||
@@ -24,8 +24,8 @@ public:
|
||||
|
||||
/**
|
||||
* Registers mapping input data from the driver
|
||||
* @param "data": An struct containing all the information needed to create a proper
|
||||
* ParamPackage
|
||||
* @param data A struct containing all the information needed to create a proper
|
||||
* ParamPackage
|
||||
*/
|
||||
void RegisterInput(const MappingData& data);
|
||||
|
||||
@@ -34,42 +34,42 @@ public:
|
||||
|
||||
private:
|
||||
/**
|
||||
* If provided data satisfies the requeriments it will push an element to the input_queue
|
||||
* If provided data satisfies the requirements it will push an element to the input_queue
|
||||
* Supported input:
|
||||
* - Button: Creates a basic button ParamPackage
|
||||
* - HatButton: Creates a basic hat button ParamPackage
|
||||
* - Analog: Creates a basic analog ParamPackage
|
||||
* @param "data": An struct containing all the information needed to create a proper
|
||||
* @param data A struct containing all the information needed to create a proper
|
||||
* ParamPackage
|
||||
*/
|
||||
void RegisterButton(const MappingData& data);
|
||||
|
||||
/**
|
||||
* If provided data satisfies the requeriments it will push an element to the input_queue
|
||||
* If provided data satisfies the requirements it will push an element to the input_queue
|
||||
* Supported input:
|
||||
* - Button, HatButton: Pass the data to RegisterButton
|
||||
* - Analog: Stores the first axis and on the second axis creates a basic stick ParamPackage
|
||||
* @param "data": An struct containing all the information needed to create a proper
|
||||
* ParamPackage
|
||||
* @param data A struct containing all the information needed to create a proper
|
||||
* ParamPackage
|
||||
*/
|
||||
void RegisterStick(const MappingData& data);
|
||||
|
||||
/**
|
||||
* If provided data satisfies the requeriments it will push an element to the input_queue
|
||||
* If provided data satisfies the requirements it will push an element to the input_queue
|
||||
* Supported input:
|
||||
* - Button, HatButton: Pass the data to RegisterButton
|
||||
* - Analog: Stores the first two axis and on the third axis creates a basic Motion
|
||||
* ParamPackage
|
||||
* - Motion: Creates a basic Motion ParamPackage
|
||||
* @param "data": An struct containing all the information needed to create a proper
|
||||
* ParamPackage
|
||||
* @param data A struct containing all the information needed to create a proper
|
||||
* ParamPackage
|
||||
*/
|
||||
void RegisterMotion(const MappingData& data);
|
||||
|
||||
/**
|
||||
* Returns true if driver can be mapped
|
||||
* @param "data": An struct containing all the information needed to create a proper
|
||||
* ParamPackage
|
||||
* @param data A struct containing all the information needed to create a proper
|
||||
* ParamPackage
|
||||
*/
|
||||
bool IsDriverValid(const MappingData& data) const;
|
||||
|
||||
|
||||
@@ -12,8 +12,7 @@ namespace InputCommon {
|
||||
|
||||
class DummyInput final : public Common::Input::InputDevice {
|
||||
public:
|
||||
explicit DummyInput() {}
|
||||
~DummyInput() {}
|
||||
explicit DummyInput() = default;
|
||||
};
|
||||
|
||||
class InputFromButton final : public Common::Input::InputDevice {
|
||||
@@ -33,7 +32,7 @@ public:
|
||||
callback_key = input_engine->SetCallback(input_identifier);
|
||||
}
|
||||
|
||||
~InputFromButton() {
|
||||
~InputFromButton() override {
|
||||
input_engine->DeleteCallback(callback_key);
|
||||
}
|
||||
|
||||
@@ -45,7 +44,7 @@ public:
|
||||
};
|
||||
}
|
||||
|
||||
void ForceUpdate() {
|
||||
void ForceUpdate() override {
|
||||
const Common::Input::CallbackStatus status{
|
||||
.type = Common::Input::InputType::Button,
|
||||
.button_status = GetStatus(),
|
||||
@@ -94,7 +93,7 @@ public:
|
||||
callback_key = input_engine->SetCallback(input_identifier);
|
||||
}
|
||||
|
||||
~InputFromHatButton() {
|
||||
~InputFromHatButton() override {
|
||||
input_engine->DeleteCallback(callback_key);
|
||||
}
|
||||
|
||||
@@ -106,7 +105,7 @@ public:
|
||||
};
|
||||
}
|
||||
|
||||
void ForceUpdate() {
|
||||
void ForceUpdate() override {
|
||||
const Common::Input::CallbackStatus status{
|
||||
.type = Common::Input::InputType::Button,
|
||||
.button_status = GetStatus(),
|
||||
@@ -167,7 +166,7 @@ public:
|
||||
callback_key_y = input_engine->SetCallback(y_input_identifier);
|
||||
}
|
||||
|
||||
~InputFromStick() {
|
||||
~InputFromStick() override {
|
||||
input_engine->DeleteCallback(callback_key_x);
|
||||
input_engine->DeleteCallback(callback_key_y);
|
||||
}
|
||||
@@ -190,7 +189,7 @@ public:
|
||||
return status;
|
||||
}
|
||||
|
||||
void ForceUpdate() {
|
||||
void ForceUpdate() override {
|
||||
const Common::Input::CallbackStatus status{
|
||||
.type = Common::Input::InputType::Stick,
|
||||
.stick_status = GetStatus(),
|
||||
@@ -266,7 +265,7 @@ public:
|
||||
callback_key_y = input_engine->SetCallback(y_input_identifier);
|
||||
}
|
||||
|
||||
~InputFromTouch() {
|
||||
~InputFromTouch() override {
|
||||
input_engine->DeleteCallback(callback_key_button);
|
||||
input_engine->DeleteCallback(callback_key_x);
|
||||
input_engine->DeleteCallback(callback_key_y);
|
||||
@@ -352,7 +351,7 @@ public:
|
||||
axis_callback_key = input_engine->SetCallback(axis_input_identifier);
|
||||
}
|
||||
|
||||
~InputFromTrigger() {
|
||||
~InputFromTrigger() override {
|
||||
input_engine->DeleteCallback(callback_key_button);
|
||||
input_engine->DeleteCallback(axis_callback_key);
|
||||
}
|
||||
@@ -419,7 +418,7 @@ public:
|
||||
callback_key = input_engine->SetCallback(input_identifier);
|
||||
}
|
||||
|
||||
~InputFromAnalog() {
|
||||
~InputFromAnalog() override {
|
||||
input_engine->DeleteCallback(callback_key);
|
||||
}
|
||||
|
||||
@@ -466,7 +465,7 @@ public:
|
||||
callback_key = input_engine->SetCallback(input_identifier);
|
||||
}
|
||||
|
||||
~InputFromBattery() {
|
||||
~InputFromBattery() override {
|
||||
input_engine->DeleteCallback(callback_key);
|
||||
}
|
||||
|
||||
@@ -474,7 +473,7 @@ public:
|
||||
return static_cast<Common::Input::BatteryLevel>(input_engine->GetBattery(identifier));
|
||||
}
|
||||
|
||||
void ForceUpdate() {
|
||||
void ForceUpdate() override {
|
||||
const Common::Input::CallbackStatus status{
|
||||
.type = Common::Input::InputType::Battery,
|
||||
.battery_status = GetStatus(),
|
||||
@@ -518,7 +517,7 @@ public:
|
||||
callback_key = input_engine->SetCallback(input_identifier);
|
||||
}
|
||||
|
||||
~InputFromMotion() {
|
||||
~InputFromMotion() override {
|
||||
input_engine->DeleteCallback(callback_key);
|
||||
}
|
||||
|
||||
@@ -593,7 +592,7 @@ public:
|
||||
callback_key_z = input_engine->SetCallback(z_input_identifier);
|
||||
}
|
||||
|
||||
~InputFromAxisMotion() {
|
||||
~InputFromAxisMotion() override {
|
||||
input_engine->DeleteCallback(callback_key_x);
|
||||
input_engine->DeleteCallback(callback_key_y);
|
||||
input_engine->DeleteCallback(callback_key_z);
|
||||
@@ -618,7 +617,7 @@ public:
|
||||
return status;
|
||||
}
|
||||
|
||||
void ForceUpdate() {
|
||||
void ForceUpdate() override {
|
||||
const Common::Input::CallbackStatus status{
|
||||
.type = Common::Input::InputType::Motion,
|
||||
.motion_status = GetStatus(),
|
||||
@@ -668,16 +667,16 @@ public:
|
||||
explicit OutputFromIdentifier(PadIdentifier identifier_, InputEngine* input_engine_)
|
||||
: identifier(identifier_), input_engine(input_engine_) {}
|
||||
|
||||
virtual void SetLED(Common::Input::LedStatus led_status) {
|
||||
void SetLED(const Common::Input::LedStatus& led_status) override {
|
||||
input_engine->SetLeds(identifier, led_status);
|
||||
}
|
||||
|
||||
virtual Common::Input::VibrationError SetVibration(
|
||||
Common::Input::VibrationStatus vibration_status) {
|
||||
Common::Input::VibrationError SetVibration(
|
||||
const Common::Input::VibrationStatus& vibration_status) override {
|
||||
return input_engine->SetRumble(identifier, vibration_status);
|
||||
}
|
||||
|
||||
virtual Common::Input::PollingError SetPollingMode(Common::Input::PollingMode polling_mode) {
|
||||
Common::Input::PollingError SetPollingMode(Common::Input::PollingMode polling_mode) override {
|
||||
return input_engine->SetPollingMode(identifier, polling_mode);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,9 +13,6 @@ class Factory;
|
||||
|
||||
namespace InputCommon {
|
||||
class InputEngine;
|
||||
/**
|
||||
* An Input factory. It receives input events and forward them to all input devices it created.
|
||||
*/
|
||||
|
||||
class OutputFactory final : public Common::Input::Factory<Common::Input::OutputDevice> {
|
||||
public:
|
||||
@@ -24,10 +21,10 @@ public:
|
||||
/**
|
||||
* Creates an output device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* @param - "guid": text string for identifing controllers
|
||||
* @param - "port": port of the connected device
|
||||
* @param - "pad": slot of the connected controller
|
||||
* @return an unique ouput device with the parameters specified
|
||||
* - "guid" text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique output device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::OutputDevice> Create(
|
||||
const Common::ParamPackage& params) override;
|
||||
@@ -36,6 +33,9 @@ private:
|
||||
std::shared_ptr<InputEngine> input_engine;
|
||||
};
|
||||
|
||||
/**
|
||||
* An Input factory. It receives input events and forward them to all input devices it created.
|
||||
*/
|
||||
class InputFactory final : public Common::Input::Factory<Common::Input::InputDevice> {
|
||||
public:
|
||||
explicit InputFactory(std::shared_ptr<InputEngine> input_engine_);
|
||||
@@ -54,16 +54,16 @@ public:
|
||||
* - battery: Contains "battery"
|
||||
* - output: Contains "output"
|
||||
* @param params contains parameters for creating the device:
|
||||
* @param - "code": the code of the keyboard key to bind with the input
|
||||
* @param - "button": same as "code" but for controller buttons
|
||||
* @param - "hat": similar as "button" but it's a group of hat buttons from SDL
|
||||
* @param - "axis": the axis number of the axis to bind with the input
|
||||
* @param - "motion": the motion number of the motion to bind with the input
|
||||
* @param - "axis_x": same as axis but specifing horizontal direction
|
||||
* @param - "axis_y": same as axis but specifing vertical direction
|
||||
* @param - "axis_z": same as axis but specifing forward direction
|
||||
* @param - "battery": Only used as a placeholder to set the input type
|
||||
* @return an unique input device with the parameters specified
|
||||
* - "code": the code of the keyboard key to bind with the input
|
||||
* - "button": same as "code" but for controller buttons
|
||||
* - "hat": similar as "button" but it's a group of hat buttons from SDL
|
||||
* - "axis": the axis number of the axis to bind with the input
|
||||
* - "motion": the motion number of the motion to bind with the input
|
||||
* - "axis_x": same as axis but specifying horizontal direction
|
||||
* - "axis_y": same as axis but specifying vertical direction
|
||||
* - "axis_z": same as axis but specifying forward direction
|
||||
* - "battery": Only used as a placeholder to set the input type
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> Create(const Common::ParamPackage& params) override;
|
||||
|
||||
@@ -71,14 +71,14 @@ private:
|
||||
/**
|
||||
* Creates a button device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* @param - "code": the code of the keyboard key to bind with the input
|
||||
* @param - "button": same as "code" but for controller buttons
|
||||
* @param - "toggle": press once to enable, press again to disable
|
||||
* @param - "inverted": inverts the output of the button
|
||||
* @param - "guid": text string for identifing controllers
|
||||
* @param - "port": port of the connected device
|
||||
* @param - "pad": slot of the connected controller
|
||||
* @return an unique input device with the parameters specified
|
||||
* - "code": the code of the keyboard key to bind with the input
|
||||
* - "button": same as "code" but for controller buttons
|
||||
* - "toggle": press once to enable, press again to disable
|
||||
* - "inverted": inverts the output of the button
|
||||
* - "guid": text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> CreateButtonDevice(
|
||||
const Common::ParamPackage& params);
|
||||
@@ -86,14 +86,14 @@ private:
|
||||
/**
|
||||
* Creates a hat button device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* @param - "button": the controller hat id to bind with the input
|
||||
* @param - "direction": the direction id to be detected
|
||||
* @param - "toggle": press once to enable, press again to disable
|
||||
* @param - "inverted": inverts the output of the button
|
||||
* @param - "guid": text string for identifing controllers
|
||||
* @param - "port": port of the connected device
|
||||
* @param - "pad": slot of the connected controller
|
||||
* @return an unique input device with the parameters specified
|
||||
* - "button": the controller hat id to bind with the input
|
||||
* - "direction": the direction id to be detected
|
||||
* - "toggle": press once to enable, press again to disable
|
||||
* - "inverted": inverts the output of the button
|
||||
* - "guid": text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> CreateHatButtonDevice(
|
||||
const Common::ParamPackage& params);
|
||||
@@ -101,19 +101,19 @@ private:
|
||||
/**
|
||||
* Creates a stick device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* @param - "axis_x": the controller horizontal axis id to bind with the input
|
||||
* @param - "axis_y": the controller vertical axis id to bind with the input
|
||||
* @param - "deadzone": the mimimum required value to be detected
|
||||
* @param - "range": the maximum value required to reach 100%
|
||||
* @param - "threshold": the mimimum required value to considered pressed
|
||||
* @param - "offset_x": the amount of offset in the x axis
|
||||
* @param - "offset_y": the amount of offset in the y axis
|
||||
* @param - "invert_x": inverts the sign of the horizontal axis
|
||||
* @param - "invert_y": inverts the sign of the vertical axis
|
||||
* @param - "guid": text string for identifing controllers
|
||||
* @param - "port": port of the connected device
|
||||
* @param - "pad": slot of the connected controller
|
||||
* @return an unique input device with the parameters specified
|
||||
* - "axis_x": the controller horizontal axis id to bind with the input
|
||||
* - "axis_y": the controller vertical axis id to bind with the input
|
||||
* - "deadzone": the minimum required value to be detected
|
||||
* - "range": the maximum value required to reach 100%
|
||||
* - "threshold": the minimum required value to considered pressed
|
||||
* - "offset_x": the amount of offset in the x axis
|
||||
* - "offset_y": the amount of offset in the y axis
|
||||
* - "invert_x": inverts the sign of the horizontal axis
|
||||
* - "invert_y": inverts the sign of the vertical axis
|
||||
* - "guid": text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> CreateStickDevice(
|
||||
const Common::ParamPackage& params);
|
||||
@@ -121,16 +121,16 @@ private:
|
||||
/**
|
||||
* Creates an analog device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* @param - "axis": the controller axis id to bind with the input
|
||||
* @param - "deadzone": the mimimum required value to be detected
|
||||
* @param - "range": the maximum value required to reach 100%
|
||||
* @param - "threshold": the mimimum required value to considered pressed
|
||||
* @param - "offset": the amount of offset in the axis
|
||||
* @param - "invert": inverts the sign of the axis
|
||||
* @param - "guid": text string for identifing controllers
|
||||
* @param - "port": port of the connected device
|
||||
* @param - "pad": slot of the connected controller
|
||||
* @return an unique input device with the parameters specified
|
||||
* - "axis": the controller axis id to bind with the input
|
||||
* - "deadzone": the minimum required value to be detected
|
||||
* - "range": the maximum value required to reach 100%
|
||||
* - "threshold": the minimum required value to considered pressed
|
||||
* - "offset": the amount of offset in the axis
|
||||
* - "invert": inverts the sign of the axis
|
||||
* - "guid": text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> CreateAnalogDevice(
|
||||
const Common::ParamPackage& params);
|
||||
@@ -138,20 +138,20 @@ private:
|
||||
/**
|
||||
* Creates a trigger device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* @param - "button": the controller hat id to bind with the input
|
||||
* @param - "direction": the direction id to be detected
|
||||
* @param - "toggle": press once to enable, press again to disable
|
||||
* @param - "inverted": inverts the output of the button
|
||||
* @param - "axis": the controller axis id to bind with the input
|
||||
* @param - "deadzone": the mimimum required value to be detected
|
||||
* @param - "range": the maximum value required to reach 100%
|
||||
* @param - "threshold": the mimimum required value to considered pressed
|
||||
* @param - "offset": the amount of offset in the axis
|
||||
* @param - "invert": inverts the sign of the axis
|
||||
* @param - "guid": text string for identifing controllers
|
||||
* @param - "port": port of the connected device
|
||||
* @param - "pad": slot of the connected controller
|
||||
* @return an unique input device with the parameters specified
|
||||
* - "button": the controller hat id to bind with the input
|
||||
* - "direction": the direction id to be detected
|
||||
* - "toggle": press once to enable, press again to disable
|
||||
* - "inverted": inverts the output of the button
|
||||
* - "axis": the controller axis id to bind with the input
|
||||
* - "deadzone": the minimum required value to be detected
|
||||
* - "range": the maximum value required to reach 100%
|
||||
* - "threshold": the minimum required value to considered pressed
|
||||
* - "offset": the amount of offset in the axis
|
||||
* - "invert": inverts the sign of the axis
|
||||
* - "guid": text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> CreateTriggerDevice(
|
||||
const Common::ParamPackage& params);
|
||||
@@ -159,23 +159,23 @@ private:
|
||||
/**
|
||||
* Creates a touch device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* @param - "button": the controller hat id to bind with the input
|
||||
* @param - "direction": the direction id to be detected
|
||||
* @param - "toggle": press once to enable, press again to disable
|
||||
* @param - "inverted": inverts the output of the button
|
||||
* @param - "axis_x": the controller horizontal axis id to bind with the input
|
||||
* @param - "axis_y": the controller vertical axis id to bind with the input
|
||||
* @param - "deadzone": the mimimum required value to be detected
|
||||
* @param - "range": the maximum value required to reach 100%
|
||||
* @param - "threshold": the mimimum required value to considered pressed
|
||||
* @param - "offset_x": the amount of offset in the x axis
|
||||
* @param - "offset_y": the amount of offset in the y axis
|
||||
* @param - "invert_x": inverts the sign of the horizontal axis
|
||||
* @param - "invert_y": inverts the sign of the vertical axis
|
||||
* @param - "guid": text string for identifing controllers
|
||||
* @param - "port": port of the connected device
|
||||
* @param - "pad": slot of the connected controller
|
||||
* @return an unique input device with the parameters specified
|
||||
* - "button": the controller hat id to bind with the input
|
||||
* - "direction": the direction id to be detected
|
||||
* - "toggle": press once to enable, press again to disable
|
||||
* - "inverted": inverts the output of the button
|
||||
* - "axis_x": the controller horizontal axis id to bind with the input
|
||||
* - "axis_y": the controller vertical axis id to bind with the input
|
||||
* - "deadzone": the minimum required value to be detected
|
||||
* - "range": the maximum value required to reach 100%
|
||||
* - "threshold": the minimum required value to considered pressed
|
||||
* - "offset_x": the amount of offset in the x axis
|
||||
* - "offset_y": the amount of offset in the y axis
|
||||
* - "invert_x": inverts the sign of the horizontal axis
|
||||
* - "invert_y": inverts the sign of the vertical axis
|
||||
* - "guid": text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> CreateTouchDevice(
|
||||
const Common::ParamPackage& params);
|
||||
@@ -183,10 +183,10 @@ private:
|
||||
/**
|
||||
* Creates a battery device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* @param - "guid": text string for identifing controllers
|
||||
* @param - "port": port of the connected device
|
||||
* @param - "pad": slot of the connected controller
|
||||
* @return an unique input device with the parameters specified
|
||||
* - "guid": text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> CreateBatteryDevice(
|
||||
const Common::ParamPackage& params);
|
||||
@@ -194,21 +194,21 @@ private:
|
||||
/**
|
||||
* Creates a motion device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* @param - "axis_x": the controller horizontal axis id to bind with the input
|
||||
* @param - "axis_y": the controller vertical axis id to bind with the input
|
||||
* @param - "axis_z": the controller fordward axis id to bind with the input
|
||||
* @param - "deadzone": the mimimum required value to be detected
|
||||
* @param - "range": the maximum value required to reach 100%
|
||||
* @param - "offset_x": the amount of offset in the x axis
|
||||
* @param - "offset_y": the amount of offset in the y axis
|
||||
* @param - "offset_z": the amount of offset in the z axis
|
||||
* @param - "invert_x": inverts the sign of the horizontal axis
|
||||
* @param - "invert_y": inverts the sign of the vertical axis
|
||||
* @param - "invert_z": inverts the sign of the fordward axis
|
||||
* @param - "guid": text string for identifing controllers
|
||||
* @param - "port": port of the connected device
|
||||
* @param - "pad": slot of the connected controller
|
||||
* @return an unique input device with the parameters specified
|
||||
* - "axis_x": the controller horizontal axis id to bind with the input
|
||||
* - "axis_y": the controller vertical axis id to bind with the input
|
||||
* - "axis_z": the controller forward axis id to bind with the input
|
||||
* - "deadzone": the minimum required value to be detected
|
||||
* - "range": the maximum value required to reach 100%
|
||||
* - "offset_x": the amount of offset in the x axis
|
||||
* - "offset_y": the amount of offset in the y axis
|
||||
* - "offset_z": the amount of offset in the z axis
|
||||
* - "invert_x": inverts the sign of the horizontal axis
|
||||
* - "invert_y": inverts the sign of the vertical axis
|
||||
* - "invert_z": inverts the sign of the forward axis
|
||||
* - "guid": text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> CreateMotionDevice(Common::ParamPackage params);
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, Scal
|
||||
}
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
ctx.Add("MOV.S {}.x,primitive.id;", inst);
|
||||
ctx.Add("MOV.F {}.x,primitive.id;", inst);
|
||||
break;
|
||||
case IR::Attribute::PositionX:
|
||||
case IR::Attribute::PositionY:
|
||||
@@ -112,17 +112,33 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, Scal
|
||||
case IR::Attribute::TessellationEvaluationPointV:
|
||||
ctx.Add("MOV.F {}.x,vertex.tesscoord.{};", inst, swizzle);
|
||||
break;
|
||||
case IR::Attribute::InstanceId:
|
||||
ctx.Add("MOV.F {}.x,{}.instance;", inst, ctx.attrib_name);
|
||||
break;
|
||||
case IR::Attribute::VertexId:
|
||||
ctx.Add("MOV.F {}.x,{}.id;", inst, ctx.attrib_name);
|
||||
break;
|
||||
case IR::Attribute::FrontFace:
|
||||
ctx.Add("CMP.F {}.x,{}.facing.x,0,-1;", inst, ctx.attrib_name);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Get attribute {}", attr);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitGetAttributeU32(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, ScalarU32) {
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
ctx.Add("MOV.S {}.x,primitive.id;", inst);
|
||||
break;
|
||||
case IR::Attribute::InstanceId:
|
||||
ctx.Add("MOV.S {}.x,{}.instance;", inst, ctx.attrib_name);
|
||||
break;
|
||||
case IR::Attribute::VertexId:
|
||||
ctx.Add("MOV.S {}.x,{}.id;", inst, ctx.attrib_name);
|
||||
break;
|
||||
case IR::Attribute::FrontFace:
|
||||
ctx.Add("CMP.S {}.x,{}.facing.x,0,-1;", inst, ctx.attrib_name);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Get attribute {}", attr);
|
||||
throw NotImplementedException("Get U32 attribute {}", attr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ void EmitGetCbufU32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
void EmitGetCbufF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset);
|
||||
void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset);
|
||||
void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, ScalarU32 vertex);
|
||||
void EmitGetAttributeU32(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, ScalarU32 vertex);
|
||||
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, ScalarF32 value, ScalarU32 vertex);
|
||||
void EmitGetAttributeIndexed(EmitContext& ctx, IR::Inst& inst, ScalarS32 offset, ScalarU32 vertex);
|
||||
void EmitSetAttributeIndexed(EmitContext& ctx, ScalarU32 offset, ScalarF32 value, ScalarU32 vertex);
|
||||
|
||||
@@ -176,7 +176,7 @@ void EmitCode(EmitContext& ctx, const IR::Program& program) {
|
||||
}
|
||||
|
||||
std::string GlslVersionSpecifier(const EmitContext& ctx) {
|
||||
if (ctx.uses_y_direction || ctx.info.stores.Legacy() || ctx.info.loads.Legacy()) {
|
||||
if (ctx.uses_y_direction) {
|
||||
return " compatibility";
|
||||
}
|
||||
return "";
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
|
||||
#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
|
||||
namespace Shader::Backend::GLSL {
|
||||
namespace {
|
||||
@@ -30,8 +31,9 @@ void EmitConditionRef(EmitContext& ctx, IR::Inst& inst, const IR::Value& value)
|
||||
inst.DestructiveAddUsage(1);
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U1)};
|
||||
const auto input{ctx.var_alloc.Consume(value)};
|
||||
const auto suffix{ctx.profile.has_gl_bool_ref_bug ? "?true:false" : ""};
|
||||
if (ret != input) {
|
||||
ctx.Add("{}={};", ret, input);
|
||||
ctx.Add("{}={}{};", ret, input, suffix);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -98,47 +98,50 @@ void GetCbuf16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, const
|
||||
GetCbuf(ctx, ret, binding, offset, 16, cast, bit_offset);
|
||||
}
|
||||
}
|
||||
|
||||
u32 TexCoordIndex(IR::Attribute attr) {
|
||||
return (static_cast<u32>(attr) - static_cast<u32>(IR::Attribute::FixedFncTexture0S)) / 4;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void EmitGetCbufU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
GetCbuf8(ctx, inst, binding, offset, "ftou");
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
|
||||
GetCbuf8(ctx, inst, binding, offset, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
GetCbuf8(ctx, inst, binding, offset, "ftoi");
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "int" : "ftoi"};
|
||||
GetCbuf8(ctx, inst, binding, offset, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
GetCbuf16(ctx, inst, binding, offset, "ftou");
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
|
||||
GetCbuf16(ctx, inst, binding, offset, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
GetCbuf16(ctx, inst, binding, offset, "ftoi");
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "int" : "ftoi"};
|
||||
GetCbuf16(ctx, inst, binding, offset, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufU32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)};
|
||||
GetCbuf(ctx, ret, binding, offset, 32, "ftou");
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
|
||||
GetCbuf(ctx, ret, binding, offset, 32, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::F32)};
|
||||
GetCbuf(ctx, ret, binding, offset, 32);
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "utof" : ""};
|
||||
GetCbuf(ctx, ret, binding, offset, 32, cast);
|
||||
}
|
||||
|
||||
void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
|
||||
const IR::Value& offset) {
|
||||
const auto cbuf{fmt::format("{}_cbuf{}", ctx.stage_name, binding.U32())};
|
||||
const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"};
|
||||
if (offset.IsImmediate()) {
|
||||
static constexpr u32 cbuf_size{0x10000};
|
||||
const u32 u32_offset{offset.U32()};
|
||||
@@ -149,26 +152,26 @@ void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding
|
||||
return;
|
||||
}
|
||||
if (u32_offset % 2 == 0) {
|
||||
ctx.AddU32x2("{}=ftou({}[{}].{}{});", inst, cbuf, u32_offset / 16,
|
||||
ctx.AddU32x2("{}={}({}[{}].{}{});", inst, cast, cbuf, u32_offset / 16,
|
||||
OffsetSwizzle(u32_offset), OffsetSwizzle(u32_offset + 4));
|
||||
} else {
|
||||
ctx.AddU32x2("{}=uvec2(ftou({}[{}].{}),ftou({}[{}].{}));", inst, cbuf, u32_offset / 16,
|
||||
OffsetSwizzle(u32_offset), cbuf, (u32_offset + 4) / 16,
|
||||
OffsetSwizzle(u32_offset + 4));
|
||||
ctx.AddU32x2("{}=uvec2({}({}[{}].{}),{}({}[{}].{}));", inst, cast, cbuf,
|
||||
u32_offset / 16, OffsetSwizzle(u32_offset), cast, cbuf,
|
||||
(u32_offset + 4) / 16, OffsetSwizzle(u32_offset + 4));
|
||||
}
|
||||
return;
|
||||
}
|
||||
const auto offset_var{ctx.var_alloc.Consume(offset)};
|
||||
if (!ctx.profile.has_gl_component_indexing_bug) {
|
||||
ctx.AddU32x2("{}=uvec2(ftou({}[{}>>4][({}>>2)%4]),ftou({}[({}+4)>>4][(({}+4)>>2)%4]));",
|
||||
inst, cbuf, offset_var, offset_var, cbuf, offset_var, offset_var);
|
||||
ctx.AddU32x2("{}=uvec2({}({}[{}>>4][({}>>2)%4]),{}({}[({}+4)>>4][(({}+4)>>2)%4]));", inst,
|
||||
cast, cbuf, offset_var, offset_var, cast, cbuf, offset_var, offset_var);
|
||||
return;
|
||||
}
|
||||
const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x2)};
|
||||
const auto cbuf_offset{fmt::format("{}>>2", offset_var)};
|
||||
for (u32 swizzle = 0; swizzle < 4; ++swizzle) {
|
||||
ctx.Add("if(({}&3)=={}){}=uvec2(ftou({}[{}>>4].{}),ftou({}[({}+4)>>4].{}));", cbuf_offset,
|
||||
swizzle, ret, cbuf, offset_var, "xyzw"[swizzle], cbuf, offset_var,
|
||||
ctx.Add("if(({}&3)=={}){}=uvec2({}({}[{}>>4].{}),{}({}[({}+4)>>4].{}));", cbuf_offset,
|
||||
swizzle, ret, cast, cbuf, offset_var, "xyzw"[swizzle], cast, cbuf, offset_var,
|
||||
"xyzw"[(swizzle + 1) % 4]);
|
||||
}
|
||||
}
|
||||
@@ -190,18 +193,6 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
|
||||
ctx.AddF32("{}=in_attr{}{}.{};", inst, index, InputVertexIndex(ctx, vertex), swizzle);
|
||||
return;
|
||||
}
|
||||
// GLSL only exposes 8 legacy texcoords
|
||||
if (attr >= IR::Attribute::FixedFncTexture8S && attr <= IR::Attribute::FixedFncTexture9Q) {
|
||||
LOG_WARNING(Shader_GLSL, "GLSL does not allow access to gl_TexCoord[{}]",
|
||||
TexCoordIndex(attr));
|
||||
ctx.AddF32("{}=0.f;", inst);
|
||||
return;
|
||||
}
|
||||
if (attr >= IR::Attribute::FixedFncTexture0S && attr <= IR::Attribute::FixedFncTexture7Q) {
|
||||
const u32 index{TexCoordIndex(attr)};
|
||||
ctx.AddF32("{}=gl_TexCoord[{}].{};", inst, index, swizzle);
|
||||
return;
|
||||
}
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
ctx.AddF32("{}=itof(gl_PrimitiveID);", inst);
|
||||
@@ -215,16 +206,6 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
|
||||
ctx.AddF32("{}={}{}.{};", inst, input_decorator, ctx.position_name, swizzle);
|
||||
break;
|
||||
}
|
||||
case IR::Attribute::ColorFrontDiffuseR:
|
||||
case IR::Attribute::ColorFrontDiffuseG:
|
||||
case IR::Attribute::ColorFrontDiffuseB:
|
||||
case IR::Attribute::ColorFrontDiffuseA:
|
||||
if (ctx.stage == Stage::Fragment) {
|
||||
ctx.AddF32("{}=gl_Color.{};", inst, swizzle);
|
||||
} else {
|
||||
ctx.AddF32("{}=gl_FrontColor.{};", inst, swizzle);
|
||||
}
|
||||
break;
|
||||
case IR::Attribute::PointSpriteS:
|
||||
case IR::Attribute::PointSpriteT:
|
||||
ctx.AddF32("{}=gl_PointCoord.{};", inst, swizzle);
|
||||
@@ -247,6 +228,22 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
|
||||
}
|
||||
}
|
||||
|
||||
void EmitGetAttributeU32(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, std::string_view) {
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
ctx.AddU32("{}=uint(gl_PrimitiveID);", inst);
|
||||
break;
|
||||
case IR::Attribute::InstanceId:
|
||||
ctx.AddU32("{}=uint(gl_InstanceID);", inst);
|
||||
break;
|
||||
case IR::Attribute::VertexId:
|
||||
ctx.AddU32("{}=uint(gl_VertexID);", inst);
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Get U32 attribute {}", attr);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, std::string_view value,
|
||||
[[maybe_unused]] std::string_view vertex) {
|
||||
if (IR::IsGeneric(attr)) {
|
||||
@@ -264,17 +261,6 @@ void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, std::string_view val
|
||||
}
|
||||
const u32 element{static_cast<u32>(attr) % 4};
|
||||
const char swizzle{"xyzw"[element]};
|
||||
// GLSL only exposes 8 legacy texcoords
|
||||
if (attr >= IR::Attribute::FixedFncTexture8S && attr <= IR::Attribute::FixedFncTexture9Q) {
|
||||
LOG_WARNING(Shader_GLSL, "GLSL does not allow access to gl_TexCoord[{}]",
|
||||
TexCoordIndex(attr));
|
||||
return;
|
||||
}
|
||||
if (attr >= IR::Attribute::FixedFncTexture0S && attr <= IR::Attribute::FixedFncTexture7Q) {
|
||||
const u32 index{TexCoordIndex(attr)};
|
||||
ctx.Add("gl_TexCoord[{}].{}={};", index, swizzle, value);
|
||||
return;
|
||||
}
|
||||
switch (attr) {
|
||||
case IR::Attribute::Layer:
|
||||
if (ctx.stage != Stage::Geometry &&
|
||||
@@ -312,33 +298,6 @@ void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, std::string_view val
|
||||
case IR::Attribute::PositionW:
|
||||
ctx.Add("gl_Position.{}={};", swizzle, value);
|
||||
break;
|
||||
case IR::Attribute::ColorFrontDiffuseR:
|
||||
case IR::Attribute::ColorFrontDiffuseG:
|
||||
case IR::Attribute::ColorFrontDiffuseB:
|
||||
case IR::Attribute::ColorFrontDiffuseA:
|
||||
ctx.Add("gl_FrontColor.{}={};", swizzle, value);
|
||||
break;
|
||||
case IR::Attribute::ColorFrontSpecularR:
|
||||
case IR::Attribute::ColorFrontSpecularG:
|
||||
case IR::Attribute::ColorFrontSpecularB:
|
||||
case IR::Attribute::ColorFrontSpecularA:
|
||||
ctx.Add("gl_FrontSecondaryColor.{}={};", swizzle, value);
|
||||
break;
|
||||
case IR::Attribute::ColorBackDiffuseR:
|
||||
case IR::Attribute::ColorBackDiffuseG:
|
||||
case IR::Attribute::ColorBackDiffuseB:
|
||||
case IR::Attribute::ColorBackDiffuseA:
|
||||
ctx.Add("gl_BackColor.{}={};", swizzle, value);
|
||||
break;
|
||||
case IR::Attribute::ColorBackSpecularR:
|
||||
case IR::Attribute::ColorBackSpecularG:
|
||||
case IR::Attribute::ColorBackSpecularB:
|
||||
case IR::Attribute::ColorBackSpecularA:
|
||||
ctx.Add("gl_BackSecondaryColor.{}={};", swizzle, value);
|
||||
break;
|
||||
case IR::Attribute::FogCoordinate:
|
||||
ctx.Add("gl_FogFragCoord={};", value);
|
||||
break;
|
||||
case IR::Attribute::ClipDistance0:
|
||||
case IR::Attribute::ClipDistance1:
|
||||
case IR::Attribute::ClipDistance2:
|
||||
|
||||
@@ -125,11 +125,11 @@ void EmitFPNeg16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& i
|
||||
}
|
||||
|
||||
void EmitFPNeg32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF32("{}=-({});", inst, value);
|
||||
ctx.AddF32("{}=0.f-({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPNeg64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddF64("{}=-({});", inst, value);
|
||||
ctx.AddF64("{}=double(0.)-({});", inst, value);
|
||||
}
|
||||
|
||||
void EmitFPSin(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
|
||||
@@ -60,6 +60,8 @@ void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding
|
||||
const IR::Value& offset);
|
||||
void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
|
||||
std::string_view vertex);
|
||||
void EmitGetAttributeU32(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
|
||||
std::string_view vertex);
|
||||
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, std::string_view value,
|
||||
std::string_view vertex);
|
||||
void EmitGetAttributeIndexed(EmitContext& ctx, IR::Inst& inst, std::string_view offset,
|
||||
|
||||
@@ -87,11 +87,11 @@ void EmitUDiv32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::strin
|
||||
}
|
||||
|
||||
void EmitINeg32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU32("{}=uint(-({}));", inst, value);
|
||||
ctx.AddU32("{}=uint(int(0)-int({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitINeg64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
ctx.AddU64("{}=-({});", inst, value);
|
||||
ctx.AddU64("{}=uint64_t(int64_t(0)-int64_t({}));", inst, value);
|
||||
}
|
||||
|
||||
void EmitIAbs32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
|
||||
|
||||
@@ -90,7 +90,9 @@ void EmitPhiMove(EmitContext& ctx, const IR::Value& phi_value, const IR::Value&
|
||||
if (phi_reg == val_reg) {
|
||||
return;
|
||||
}
|
||||
ctx.Add("{}={};", phi_reg, val_reg);
|
||||
const bool needs_workaround{ctx.profile.has_gl_bool_ref_bug && phi_type == IR::Type::U1};
|
||||
const auto suffix{needs_workaround ? "?true:false" : ""};
|
||||
ctx.Add("{}={}{};", phi_reg, val_reg, suffix);
|
||||
}
|
||||
|
||||
void EmitPrologue(EmitContext& ctx) {
|
||||
|
||||
@@ -211,27 +211,6 @@ std::string_view OutputPrimitive(OutputTopology topology) {
|
||||
throw InvalidArgument("Invalid output topology {}", topology);
|
||||
}
|
||||
|
||||
void SetupLegacyOutPerVertex(EmitContext& ctx, std::string& header) {
|
||||
if (!ctx.info.stores.Legacy()) {
|
||||
return;
|
||||
}
|
||||
if (ctx.info.stores.FixedFunctionTexture()) {
|
||||
header += "vec4 gl_TexCoord[8];";
|
||||
}
|
||||
if (ctx.info.stores.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) {
|
||||
header += "vec4 gl_FrontColor;";
|
||||
}
|
||||
if (ctx.info.stores.AnyComponent(IR::Attribute::ColorFrontSpecularR)) {
|
||||
header += "vec4 gl_FrontSecondaryColor;";
|
||||
}
|
||||
if (ctx.info.stores.AnyComponent(IR::Attribute::ColorBackDiffuseR)) {
|
||||
header += "vec4 gl_BackColor;";
|
||||
}
|
||||
if (ctx.info.stores.AnyComponent(IR::Attribute::ColorBackSpecularR)) {
|
||||
header += "vec4 gl_BackSecondaryColor;";
|
||||
}
|
||||
}
|
||||
|
||||
void SetupOutPerVertex(EmitContext& ctx, std::string& header) {
|
||||
if (!StoresPerVertexAttributes(ctx.stage)) {
|
||||
return;
|
||||
@@ -250,7 +229,6 @@ void SetupOutPerVertex(EmitContext& ctx, std::string& header) {
|
||||
ctx.profile.support_viewport_index_layer_non_geometry && ctx.stage != Stage::Geometry) {
|
||||
header += "int gl_ViewportIndex;";
|
||||
}
|
||||
SetupLegacyOutPerVertex(ctx, header);
|
||||
header += "};";
|
||||
if (ctx.info.stores[IR::Attribute::ViewportIndex] && ctx.stage == Stage::Geometry) {
|
||||
header += "out int gl_ViewportIndex;";
|
||||
@@ -282,21 +260,6 @@ void SetupInPerVertex(EmitContext& ctx, std::string& header) {
|
||||
}
|
||||
header += "}gl_in[gl_MaxPatchVertices];";
|
||||
}
|
||||
|
||||
void SetupLegacyInPerFragment(EmitContext& ctx, std::string& header) {
|
||||
if (!ctx.info.loads.Legacy()) {
|
||||
return;
|
||||
}
|
||||
header += "in gl_PerFragment{";
|
||||
if (ctx.info.loads.FixedFunctionTexture()) {
|
||||
header += "vec4 gl_TexCoord[8];";
|
||||
}
|
||||
if (ctx.info.loads.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) {
|
||||
header += "vec4 gl_Color;";
|
||||
}
|
||||
header += "};";
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_,
|
||||
@@ -361,7 +324,6 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
|
||||
}
|
||||
SetupOutPerVertex(*this, header);
|
||||
SetupInPerVertex(*this, header);
|
||||
SetupLegacyInPerFragment(*this, header);
|
||||
|
||||
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
|
||||
if (!info.loads.Generic(index) || !runtime_info.previous_stage_stores.Generic(index)) {
|
||||
@@ -466,9 +428,10 @@ void EmitContext::DefineConstantBuffers(Bindings& bindings) {
|
||||
return;
|
||||
}
|
||||
for (const auto& desc : info.constant_buffer_descriptors) {
|
||||
header += fmt::format(
|
||||
"layout(std140,binding={}) uniform {}_cbuf_{}{{vec4 {}_cbuf{}[{}];}};",
|
||||
bindings.uniform_buffer, stage_name, desc.index, stage_name, desc.index, 4 * 1024);
|
||||
const auto cbuf_type{profile.has_gl_cbuf_ftou_bug ? "uvec4" : "vec4"};
|
||||
header += fmt::format("layout(std140,binding={}) uniform {}_cbuf_{}{{{} {}_cbuf{}[{}];}};",
|
||||
bindings.uniform_buffer, stage_name, desc.index, cbuf_type,
|
||||
stage_name, desc.index, 4 * 1024);
|
||||
bindings.uniform_buffer += desc.count;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,11 +30,20 @@ struct FuncTraits<ReturnType_ (*)(Args...)> {
|
||||
using ArgType = std::tuple_element_t<I, std::tuple<Args...>>;
|
||||
};
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4702) // Ignore unreachable code warning
|
||||
#endif
|
||||
|
||||
template <auto func, typename... Args>
|
||||
void SetDefinition(EmitContext& ctx, IR::Inst* inst, Args... args) {
|
||||
inst->SetDefinition<Id>(func(ctx, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
template <typename ArgType>
|
||||
ArgType Arg(EmitContext& ctx, const IR::Value& arg) {
|
||||
if constexpr (std::is_same_v<ArgType, Id>) {
|
||||
|
||||
@@ -44,14 +44,6 @@ Id AttrPointer(EmitContext& ctx, Id pointer_type, Id vertex, Id base, Args&&...
|
||||
}
|
||||
}
|
||||
|
||||
bool IsLegacyAttribute(IR::Attribute attribute) {
|
||||
return (attribute >= IR::Attribute::ColorFrontDiffuseR &&
|
||||
attribute <= IR::Attribute::ColorBackSpecularA) ||
|
||||
attribute == IR::Attribute::FogCoordinate ||
|
||||
(attribute >= IR::Attribute::FixedFncTexture0S &&
|
||||
attribute <= IR::Attribute::FixedFncTexture9Q);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
Id OutputAccessChain(EmitContext& ctx, Id result_type, Id base, Args&&... args) {
|
||||
if (ctx.stage == Stage::TessellationControl) {
|
||||
@@ -83,17 +75,6 @@ std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
|
||||
return OutputAccessChain(ctx, ctx.output_f32, info.id, index_id);
|
||||
}
|
||||
}
|
||||
if (IsLegacyAttribute(attr)) {
|
||||
if (attr == IR::Attribute::FogCoordinate) {
|
||||
return OutputAccessChain(ctx, ctx.output_f32, ctx.OutputLegacyAttribute(attr),
|
||||
ctx.Const(0u));
|
||||
} else {
|
||||
const u32 element{static_cast<u32>(attr) % 4};
|
||||
const Id element_id{ctx.Const(element)};
|
||||
return OutputAccessChain(ctx, ctx.output_f32, ctx.OutputLegacyAttribute(attr),
|
||||
element_id);
|
||||
}
|
||||
}
|
||||
switch (attr) {
|
||||
case IR::Attribute::PointSize:
|
||||
return ctx.output_point_size;
|
||||
@@ -327,18 +308,6 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
|
||||
const Id value{ctx.OpLoad(type->id, pointer)};
|
||||
return type->needs_cast ? ctx.OpBitcast(ctx.F32[1], value) : value;
|
||||
}
|
||||
if (IsLegacyAttribute(attr)) {
|
||||
if (attr == IR::Attribute::FogCoordinate) {
|
||||
const Id attr_ptr{AttrPointer(ctx, ctx.input_f32, vertex,
|
||||
ctx.InputLegacyAttribute(attr), ctx.Const(0u))};
|
||||
return ctx.OpLoad(ctx.F32[1], attr_ptr);
|
||||
} else {
|
||||
const Id element_id{ctx.Const(element)};
|
||||
const Id attr_ptr{AttrPointer(ctx, ctx.input_f32, vertex,
|
||||
ctx.InputLegacyAttribute(attr), element_id)};
|
||||
return ctx.OpLoad(ctx.F32[1], attr_ptr);
|
||||
}
|
||||
}
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.primitive_id));
|
||||
@@ -386,6 +355,31 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
|
||||
}
|
||||
}
|
||||
|
||||
Id EmitGetAttributeU32(EmitContext& ctx, IR::Attribute attr, Id) {
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
return ctx.OpLoad(ctx.U32[1], ctx.primitive_id);
|
||||
case IR::Attribute::InstanceId:
|
||||
if (ctx.profile.support_vertex_instance_id) {
|
||||
return ctx.OpLoad(ctx.U32[1], ctx.instance_id);
|
||||
} else {
|
||||
const Id index{ctx.OpLoad(ctx.U32[1], ctx.instance_index)};
|
||||
const Id base{ctx.OpLoad(ctx.U32[1], ctx.base_instance)};
|
||||
return ctx.OpISub(ctx.U32[1], index, base);
|
||||
}
|
||||
case IR::Attribute::VertexId:
|
||||
if (ctx.profile.support_vertex_instance_id) {
|
||||
return ctx.OpLoad(ctx.U32[1], ctx.vertex_id);
|
||||
} else {
|
||||
const Id index{ctx.OpLoad(ctx.U32[1], ctx.vertex_index)};
|
||||
const Id base{ctx.OpLoad(ctx.U32[1], ctx.base_vertex)};
|
||||
return ctx.OpISub(ctx.U32[1], index, base);
|
||||
}
|
||||
default:
|
||||
throw NotImplementedException("Read U32 attribute {}", attr);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, [[maybe_unused]] Id vertex) {
|
||||
const std::optional<OutAttr> output{OutputAttrPointer(ctx, attr)};
|
||||
if (!output) {
|
||||
|
||||
@@ -53,6 +53,7 @@ Id EmitGetCbufU32(EmitContext& ctx, const IR::Value& binding, const IR::Value& o
|
||||
Id EmitGetCbufF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
|
||||
Id EmitGetCbufU32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
|
||||
Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex);
|
||||
Id EmitGetAttributeU32(EmitContext& ctx, IR::Attribute attr, Id vertex);
|
||||
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, Id vertex);
|
||||
Id EmitGetAttributeIndexed(EmitContext& ctx, Id offset, Id vertex);
|
||||
void EmitSetAttributeIndexed(EmitContext& ctx, Id offset, Id value, Id vertex);
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
constexpr size_t NUM_FIXEDFNCTEXTURE = 10;
|
||||
|
||||
enum class Operation {
|
||||
Increment,
|
||||
Decrement,
|
||||
@@ -432,34 +430,6 @@ Id DescType(EmitContext& ctx, Id sampled_type, Id pointer_type, u32 count) {
|
||||
return pointer_type;
|
||||
}
|
||||
}
|
||||
|
||||
size_t FindAndSetNextUnusedLocation(std::bitset<IR::NUM_GENERICS>& used_locations,
|
||||
size_t& start_offset) {
|
||||
for (size_t location = start_offset; location < used_locations.size(); ++location) {
|
||||
if (!used_locations.test(location)) {
|
||||
start_offset = location;
|
||||
used_locations.set(location);
|
||||
return location;
|
||||
}
|
||||
}
|
||||
throw RuntimeError("Unable to get an unused location for legacy attribute");
|
||||
}
|
||||
|
||||
Id DefineLegacyInput(EmitContext& ctx, std::bitset<IR::NUM_GENERICS>& used_locations,
|
||||
size_t& start_offset) {
|
||||
const Id id{DefineInput(ctx, ctx.F32[4], true)};
|
||||
const size_t location = FindAndSetNextUnusedLocation(used_locations, start_offset);
|
||||
ctx.Decorate(id, spv::Decoration::Location, location);
|
||||
return id;
|
||||
}
|
||||
|
||||
Id DefineLegacyOutput(EmitContext& ctx, std::bitset<IR::NUM_GENERICS>& used_locations,
|
||||
size_t& start_offset, std::optional<u32> invocations) {
|
||||
const Id id{DefineOutput(ctx, ctx.F32[4], invocations)};
|
||||
const size_t location = FindAndSetNextUnusedLocation(used_locations, start_offset);
|
||||
ctx.Decorate(id, spv::Decoration::Location, location);
|
||||
return id;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name) {
|
||||
@@ -543,64 +513,6 @@ Id EmitContext::BitOffset16(const IR::Value& offset) {
|
||||
return OpBitwiseAnd(U32[1], OpShiftLeftLogical(U32[1], Def(offset), Const(3u)), Const(16u));
|
||||
}
|
||||
|
||||
Id EmitContext::InputLegacyAttribute(IR::Attribute attribute) {
|
||||
if (attribute >= IR::Attribute::ColorFrontDiffuseR &&
|
||||
attribute <= IR::Attribute::ColorFrontDiffuseA) {
|
||||
return input_front_color;
|
||||
}
|
||||
if (attribute >= IR::Attribute::ColorFrontSpecularR &&
|
||||
attribute <= IR::Attribute::ColorFrontSpecularA) {
|
||||
return input_front_secondary_color;
|
||||
}
|
||||
if (attribute >= IR::Attribute::ColorBackDiffuseR &&
|
||||
attribute <= IR::Attribute::ColorBackDiffuseA) {
|
||||
return input_back_color;
|
||||
}
|
||||
if (attribute >= IR::Attribute::ColorBackSpecularR &&
|
||||
attribute <= IR::Attribute::ColorBackSpecularA) {
|
||||
return input_back_secondary_color;
|
||||
}
|
||||
if (attribute == IR::Attribute::FogCoordinate) {
|
||||
return input_fog_frag_coord;
|
||||
}
|
||||
if (attribute >= IR::Attribute::FixedFncTexture0S &&
|
||||
attribute <= IR::Attribute::FixedFncTexture9Q) {
|
||||
u32 index =
|
||||
(static_cast<u32>(attribute) - static_cast<u32>(IR::Attribute::FixedFncTexture0S)) / 4;
|
||||
return input_fixed_fnc_textures[index];
|
||||
}
|
||||
throw InvalidArgument("Attribute is not legacy attribute {}", attribute);
|
||||
}
|
||||
|
||||
Id EmitContext::OutputLegacyAttribute(IR::Attribute attribute) {
|
||||
if (attribute >= IR::Attribute::ColorFrontDiffuseR &&
|
||||
attribute <= IR::Attribute::ColorFrontDiffuseA) {
|
||||
return output_front_color;
|
||||
}
|
||||
if (attribute >= IR::Attribute::ColorFrontSpecularR &&
|
||||
attribute <= IR::Attribute::ColorFrontSpecularA) {
|
||||
return output_front_secondary_color;
|
||||
}
|
||||
if (attribute >= IR::Attribute::ColorBackDiffuseR &&
|
||||
attribute <= IR::Attribute::ColorBackDiffuseA) {
|
||||
return output_back_color;
|
||||
}
|
||||
if (attribute >= IR::Attribute::ColorBackSpecularR &&
|
||||
attribute <= IR::Attribute::ColorBackSpecularA) {
|
||||
return output_back_secondary_color;
|
||||
}
|
||||
if (attribute == IR::Attribute::FogCoordinate) {
|
||||
return output_fog_frag_coord;
|
||||
}
|
||||
if (attribute >= IR::Attribute::FixedFncTexture0S &&
|
||||
attribute <= IR::Attribute::FixedFncTexture9Q) {
|
||||
u32 index =
|
||||
(static_cast<u32>(attribute) - static_cast<u32>(IR::Attribute::FixedFncTexture0S)) / 4;
|
||||
return output_fixed_fnc_textures[index];
|
||||
}
|
||||
throw InvalidArgument("Attribute is not legacy attribute {}", attribute);
|
||||
}
|
||||
|
||||
void EmitContext::DefineCommonTypes(const Info& info) {
|
||||
void_id = TypeVoid();
|
||||
|
||||
@@ -1389,7 +1301,6 @@ void EmitContext::DefineInputs(const IR::Program& program) {
|
||||
loads[IR::Attribute::TessellationEvaluationPointV]) {
|
||||
tess_coord = DefineInput(*this, F32[3], false, spv::BuiltIn::TessCoord);
|
||||
}
|
||||
std::bitset<IR::NUM_GENERICS> used_locations{};
|
||||
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
|
||||
const AttributeType input_type{runtime_info.generic_input_types[index]};
|
||||
if (!runtime_info.previous_stage_stores.Generic(index)) {
|
||||
@@ -1401,7 +1312,6 @@ void EmitContext::DefineInputs(const IR::Program& program) {
|
||||
if (input_type == AttributeType::Disabled) {
|
||||
continue;
|
||||
}
|
||||
used_locations.set(index);
|
||||
const Id type{GetAttributeType(*this, input_type)};
|
||||
const Id id{DefineInput(*this, type, true)};
|
||||
Decorate(id, spv::Decoration::Location, static_cast<u32>(index));
|
||||
@@ -1427,30 +1337,6 @@ void EmitContext::DefineInputs(const IR::Program& program) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
size_t previous_unused_location = 0;
|
||||
if (loads.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) {
|
||||
input_front_color = DefineLegacyInput(*this, used_locations, previous_unused_location);
|
||||
}
|
||||
if (loads.AnyComponent(IR::Attribute::ColorFrontSpecularR)) {
|
||||
input_front_secondary_color =
|
||||
DefineLegacyInput(*this, used_locations, previous_unused_location);
|
||||
}
|
||||
if (loads.AnyComponent(IR::Attribute::ColorBackDiffuseR)) {
|
||||
input_back_color = DefineLegacyInput(*this, used_locations, previous_unused_location);
|
||||
}
|
||||
if (loads.AnyComponent(IR::Attribute::ColorBackSpecularR)) {
|
||||
input_back_secondary_color =
|
||||
DefineLegacyInput(*this, used_locations, previous_unused_location);
|
||||
}
|
||||
if (loads.AnyComponent(IR::Attribute::FogCoordinate)) {
|
||||
input_fog_frag_coord = DefineLegacyInput(*this, used_locations, previous_unused_location);
|
||||
}
|
||||
for (size_t index = 0; index < NUM_FIXEDFNCTEXTURE; ++index) {
|
||||
if (loads.AnyComponent(IR::Attribute::FixedFncTexture0S + index * 4)) {
|
||||
input_fixed_fnc_textures[index] =
|
||||
DefineLegacyInput(*this, used_locations, previous_unused_location);
|
||||
}
|
||||
}
|
||||
if (stage == Stage::TessellationEval) {
|
||||
for (size_t index = 0; index < info.uses_patches.size(); ++index) {
|
||||
if (!info.uses_patches[index]) {
|
||||
@@ -1501,38 +1387,9 @@ void EmitContext::DefineOutputs(const IR::Program& program) {
|
||||
viewport_mask = DefineOutput(*this, TypeArray(U32[1], Const(1u)), std::nullopt,
|
||||
spv::BuiltIn::ViewportMaskNV);
|
||||
}
|
||||
std::bitset<IR::NUM_GENERICS> used_locations{};
|
||||
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
|
||||
if (info.stores.Generic(index)) {
|
||||
DefineGenericOutput(*this, index, invocations);
|
||||
used_locations.set(index);
|
||||
}
|
||||
}
|
||||
size_t previous_unused_location = 0;
|
||||
if (info.stores.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) {
|
||||
output_front_color =
|
||||
DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations);
|
||||
}
|
||||
if (info.stores.AnyComponent(IR::Attribute::ColorFrontSpecularR)) {
|
||||
output_front_secondary_color =
|
||||
DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations);
|
||||
}
|
||||
if (info.stores.AnyComponent(IR::Attribute::ColorBackDiffuseR)) {
|
||||
output_back_color =
|
||||
DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations);
|
||||
}
|
||||
if (info.stores.AnyComponent(IR::Attribute::ColorBackSpecularR)) {
|
||||
output_back_secondary_color =
|
||||
DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations);
|
||||
}
|
||||
if (info.stores.AnyComponent(IR::Attribute::FogCoordinate)) {
|
||||
output_fog_frag_coord =
|
||||
DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations);
|
||||
}
|
||||
for (size_t index = 0; index < NUM_FIXEDFNCTEXTURE; ++index) {
|
||||
if (info.stores.AnyComponent(IR::Attribute::FixedFncTexture0S + index * 4)) {
|
||||
output_fixed_fnc_textures[index] =
|
||||
DefineLegacyOutput(*this, used_locations, previous_unused_location, invocations);
|
||||
}
|
||||
}
|
||||
switch (stage) {
|
||||
|
||||
@@ -113,9 +113,6 @@ public:
|
||||
[[nodiscard]] Id BitOffset8(const IR::Value& offset);
|
||||
[[nodiscard]] Id BitOffset16(const IR::Value& offset);
|
||||
|
||||
Id InputLegacyAttribute(IR::Attribute attribute);
|
||||
Id OutputLegacyAttribute(IR::Attribute attribute);
|
||||
|
||||
Id Const(u32 value) {
|
||||
return Constant(U32[1], value);
|
||||
}
|
||||
@@ -281,22 +278,10 @@ public:
|
||||
Id write_global_func_u32x4{};
|
||||
|
||||
Id input_position{};
|
||||
Id input_front_color{};
|
||||
Id input_front_secondary_color{};
|
||||
Id input_back_color{};
|
||||
Id input_back_secondary_color{};
|
||||
Id input_fog_frag_coord{};
|
||||
std::array<Id, 10> input_fixed_fnc_textures{};
|
||||
std::array<Id, 32> input_generics{};
|
||||
|
||||
Id output_point_size{};
|
||||
Id output_position{};
|
||||
Id output_front_color{};
|
||||
Id output_front_secondary_color{};
|
||||
Id output_back_color{};
|
||||
Id output_back_secondary_color{};
|
||||
Id output_fog_frag_coord{};
|
||||
std::array<Id, 10> output_fixed_fnc_textures{};
|
||||
std::array<std::array<GenericElementInfo, 4>, 32> output_generics{};
|
||||
|
||||
Id output_tess_level_outer{};
|
||||
|
||||
@@ -31,6 +31,8 @@ public:
|
||||
|
||||
[[nodiscard]] virtual std::array<u32, 3> WorkgroupSize() const = 0;
|
||||
|
||||
virtual void Dump(u64 hash) = 0;
|
||||
|
||||
[[nodiscard]] const ProgramHeader& SPH() const noexcept {
|
||||
return sph;
|
||||
}
|
||||
|
||||
@@ -224,6 +224,8 @@ enum class Attribute : u64 {
|
||||
|
||||
constexpr size_t NUM_GENERICS = 32;
|
||||
|
||||
constexpr size_t NUM_FIXEDFNCTEXTURE = 10;
|
||||
|
||||
[[nodiscard]] bool IsGeneric(Attribute attribute) noexcept;
|
||||
|
||||
[[nodiscard]] u32 GenericAttributeIndex(Attribute attribute);
|
||||
|
||||
@@ -40,6 +40,7 @@ OPCODE(GetCbufU32, U32, U32,
|
||||
OPCODE(GetCbufF32, F32, U32, U32, )
|
||||
OPCODE(GetCbufU32x2, U32x2, U32, U32, )
|
||||
OPCODE(GetAttribute, F32, Attribute, U32, )
|
||||
OPCODE(GetAttributeU32, U32, Attribute, U32, )
|
||||
OPCODE(SetAttribute, Void, Attribute, F32, U32, )
|
||||
OPCODE(GetAttributeIndexed, F32, U32, U32, )
|
||||
OPCODE(SetAttributeIndexed, Void, U32, F32, U32, )
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "shader_recompiler/exception.h"
|
||||
@@ -127,6 +128,42 @@ void AddNVNStorageBuffers(IR::Program& program) {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool IsLegacyAttribute(IR::Attribute attribute) {
|
||||
return (attribute >= IR::Attribute::ColorFrontDiffuseR &&
|
||||
attribute <= IR::Attribute::ColorBackSpecularA) ||
|
||||
attribute == IR::Attribute::FogCoordinate ||
|
||||
(attribute >= IR::Attribute::FixedFncTexture0S &&
|
||||
attribute <= IR::Attribute::FixedFncTexture9Q);
|
||||
}
|
||||
|
||||
std::map<IR::Attribute, IR::Attribute> GenerateLegacyToGenericMappings(
|
||||
const VaryingState& state, std::queue<IR::Attribute> ununsed_generics) {
|
||||
std::map<IR::Attribute, IR::Attribute> mapping;
|
||||
for (size_t index = 0; index < 4; ++index) {
|
||||
auto attr = IR::Attribute::ColorFrontDiffuseR + index * 4;
|
||||
if (state.AnyComponent(attr)) {
|
||||
for (size_t i = 0; i < 4; ++i) {
|
||||
mapping.insert({attr + i, ununsed_generics.front() + i});
|
||||
}
|
||||
ununsed_generics.pop();
|
||||
}
|
||||
}
|
||||
if (state[IR::Attribute::FogCoordinate]) {
|
||||
mapping.insert({IR::Attribute::FogCoordinate, ununsed_generics.front()});
|
||||
ununsed_generics.pop();
|
||||
}
|
||||
for (size_t index = 0; index < IR::NUM_FIXEDFNCTEXTURE; ++index) {
|
||||
auto attr = IR::Attribute::FixedFncTexture0S + index * 4;
|
||||
if (state.AnyComponent(attr)) {
|
||||
for (size_t i = 0; i < 4; ++i) {
|
||||
mapping.insert({attr + i, ununsed_generics.front() + i});
|
||||
}
|
||||
ununsed_generics.pop();
|
||||
}
|
||||
}
|
||||
return mapping;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
|
||||
@@ -226,4 +263,62 @@ IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b
|
||||
return result;
|
||||
}
|
||||
|
||||
void ConvertLegacyToGeneric(IR::Program& program, const Shader::RuntimeInfo& runtime_info) {
|
||||
auto& stores = program.info.stores;
|
||||
if (stores.Legacy()) {
|
||||
std::queue<IR::Attribute> ununsed_output_generics{};
|
||||
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
|
||||
if (!stores.Generic(index)) {
|
||||
ununsed_output_generics.push(IR::Attribute::Generic0X + index * 4);
|
||||
}
|
||||
}
|
||||
auto mappings = GenerateLegacyToGenericMappings(stores, ununsed_output_generics);
|
||||
for (IR::Block* const block : program.post_order_blocks) {
|
||||
for (IR::Inst& inst : block->Instructions()) {
|
||||
switch (inst.GetOpcode()) {
|
||||
case IR::Opcode::SetAttribute: {
|
||||
const auto attr = inst.Arg(0).Attribute();
|
||||
if (IsLegacyAttribute(attr)) {
|
||||
stores.Set(mappings[attr], true);
|
||||
inst.SetArg(0, Shader::IR::Value(mappings[attr]));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto& loads = program.info.loads;
|
||||
if (loads.Legacy()) {
|
||||
std::queue<IR::Attribute> ununsed_input_generics{};
|
||||
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
|
||||
const AttributeType input_type{runtime_info.generic_input_types[index]};
|
||||
if (!runtime_info.previous_stage_stores.Generic(index) || !loads.Generic(index) ||
|
||||
input_type == AttributeType::Disabled) {
|
||||
ununsed_input_generics.push(IR::Attribute::Generic0X + index * 4);
|
||||
}
|
||||
}
|
||||
auto mappings = GenerateLegacyToGenericMappings(loads, ununsed_input_generics);
|
||||
for (IR::Block* const block : program.post_order_blocks) {
|
||||
for (IR::Inst& inst : block->Instructions()) {
|
||||
switch (inst.GetOpcode()) {
|
||||
case IR::Opcode::GetAttribute: {
|
||||
const auto attr = inst.Arg(0).Attribute();
|
||||
if (IsLegacyAttribute(attr)) {
|
||||
loads.Set(mappings[attr], true);
|
||||
inst.SetArg(0, Shader::IR::Value(mappings[attr]));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::Maxwell
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "shader_recompiler/frontend/maxwell/control_flow.h"
|
||||
#include "shader_recompiler/host_translate_info.h"
|
||||
#include "shader_recompiler/object_pool.h"
|
||||
#include "shader_recompiler/runtime_info.h"
|
||||
|
||||
namespace Shader::Maxwell {
|
||||
|
||||
@@ -20,4 +21,7 @@ namespace Shader::Maxwell {
|
||||
[[nodiscard]] IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b,
|
||||
Environment& env_vertex_b);
|
||||
|
||||
[[nodiscard]] void ConvertLegacyToGeneric(IR::Program& program,
|
||||
const Shader::RuntimeInfo& runtime_info);
|
||||
|
||||
} // namespace Shader::Maxwell
|
||||
|
||||
@@ -389,6 +389,7 @@ void VisitUsages(Info& info, IR::Inst& inst) {
|
||||
info.uses_demote_to_helper_invocation = true;
|
||||
break;
|
||||
case IR::Opcode::GetAttribute:
|
||||
case IR::Opcode::GetAttributeU32:
|
||||
info.loads.mask[static_cast<size_t>(inst.Arg(0).Attribute())] = true;
|
||||
break;
|
||||
case IR::Opcode::SetAttribute:
|
||||
|
||||
@@ -505,6 +505,29 @@ void FoldBitCast(IR::Inst& inst, IR::Opcode reverse) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if constexpr (op == IR::Opcode::BitCastU32F32) {
|
||||
// Workaround for new NVIDIA driver bug, where:
|
||||
// uint attr = ftou(itof(gl_InstanceID));
|
||||
// always returned 0.
|
||||
// We can instead manually optimize this and work around the driver bug:
|
||||
// uint attr = uint(gl_InstanceID);
|
||||
if (arg_inst->GetOpcode() == IR::Opcode::GetAttribute) {
|
||||
const IR::Attribute attr{arg_inst->Arg(0).Attribute()};
|
||||
switch (attr) {
|
||||
case IR::Attribute::PrimitiveId:
|
||||
case IR::Attribute::InstanceId:
|
||||
case IR::Attribute::VertexId:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
// Replace the bitcasts with an integer attribute get
|
||||
inst.ReplaceOpcode(IR::Opcode::GetAttributeU32);
|
||||
inst.SetArg(0, arg_inst->Arg(0));
|
||||
inst.SetArg(1, arg_inst->Arg(1));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FoldInverseFunc(IR::Inst& inst, IR::Opcode reverse) {
|
||||
|
||||
@@ -65,6 +65,10 @@ struct Profile {
|
||||
bool has_gl_component_indexing_bug{};
|
||||
/// The precise type qualifier is broken in the fragment stage of some drivers
|
||||
bool has_gl_precise_bug{};
|
||||
/// Some drivers do not properly support floatBitsToUint when used on cbufs
|
||||
bool has_gl_cbuf_ftou_bug{};
|
||||
/// Some drivers poorly optimize boolean variable references
|
||||
bool has_gl_bool_ref_bug{};
|
||||
/// Ignores SPIR-V ordered vs unordered using GLSL semantics
|
||||
bool ignore_nan_fp_comparisons{};
|
||||
|
||||
|
||||
@@ -53,7 +53,8 @@ struct VaryingState {
|
||||
return AnyComponent(IR::Attribute::ColorFrontDiffuseR) ||
|
||||
AnyComponent(IR::Attribute::ColorFrontSpecularR) ||
|
||||
AnyComponent(IR::Attribute::ColorBackDiffuseR) ||
|
||||
AnyComponent(IR::Attribute::ColorBackSpecularR) || FixedFunctionTexture();
|
||||
AnyComponent(IR::Attribute::ColorBackSpecularR) || FixedFunctionTexture() ||
|
||||
mask[static_cast<size_t>(IR::Attribute::FogCoordinate)];
|
||||
}
|
||||
|
||||
[[nodiscard]] bool FixedFunctionTexture() const noexcept {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user