Compare commits

..

1 Commits

Author SHA1 Message Date
Kewlan
dca2208248 Various input profile changes.
- Saves and loads the selected profile between sessions.
- Profiles only save when config window is confirmed.
- Can map multiple players at the same time if they have the same profile.
- Adds rename button.
2021-01-08 00:50:12 +01:00
375 changed files with 11278 additions and 17561 deletions

View File

@@ -22,10 +22,12 @@ rm -vf AppDir/usr/bin/yuzu-cmd AppDir/usr/bin/yuzu-tester
# Download tools needed to build an AppImage
wget -nc https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
wget -nc https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage
wget -nc https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
wget -nc https://github.com/darealshinji/AppImageKit-checkrt/releases/download/continuous/AppRun-patched-x86_64
wget -nc https://github.com/darealshinji/AppImageKit-checkrt/releases/download/continuous/exec-x86_64.so
# Set executable bit
chmod 755 \
appimagetool-x86_64.AppImage \
AppRun-patched-x86_64 \
exec-x86_64.so \
linuxdeploy-x86_64.AppImage \
@@ -47,3 +49,6 @@ cp exec-x86_64.so AppDir/usr/optional/exec.so
cp AppRun-patched-x86_64 AppDir/AppRun
cp --dereference /usr/lib/x86_64-linux-gnu/libstdc++.so.6 AppDir/usr/optional/libstdc++/libstdc++.so.6
cp --dereference /lib/x86_64-linux-gnu/libgcc_s.so.1 AppDir/usr/optional/libgcc_s/libgcc_s.so.1
# Build the AppImage
./appimagetool-x86_64.AppImage AppDir

View File

@@ -2,7 +2,8 @@
. .ci/scripts/common/pre-upload.sh
APPIMAGE_NAME="yuzu-${GITDATE}-${GITREV}.AppImage"
APPIMAGE_NAME="yuzu-x86_64.AppImage"
NEW_APPIMAGE_NAME="yuzu-${GITDATE}-${GITREV}-x86_64.AppImage"
REV_NAME="yuzu-linux-${GITDATE}-${GITREV}"
ARCHIVE_NAME="${REV_NAME}.tar.xz"
COMPRESSION_FLAGS="-cJvf"
@@ -18,24 +19,7 @@ mkdir "$DIR_NAME"
cp build/bin/yuzu-cmd "$DIR_NAME"
cp build/bin/yuzu "$DIR_NAME"
# Build an AppImage
cd build
wget -nc https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
chmod 755 appimagetool-x86_64.AppImage
if [ "${RELEASE_NAME}" = "mainline" ]; then
# Generate update information if releasing to mainline
./appimagetool-x86_64.AppImage -u "gh-releases-zsync|yuzu-emu|yuzu-${RELEASE_NAME}|latest|yuzu-*.AppImage.zsync" AppDir "${APPIMAGE_NAME}"
else
./appimagetool-x86_64.AppImage AppDir "${APPIMAGE_NAME}"
fi
cd ..
# Copy the AppImage and update info to the artifacts directory and avoid compressing it
cp "build/${APPIMAGE_NAME}" "${ARTIFACTS_DIR}/"
if [ -f "build/${APPIMAGE_NAME}.zsync" ]; then
cp "build/${APPIMAGE_NAME}.zsync" "${ARTIFACTS_DIR}/"
fi
# Copy the AppImage to the artifacts directory and avoid compressing it
cp "build/${APPIMAGE_NAME}" "${ARTIFACTS_DIR}/${NEW_APPIMAGE_NAME}"
. .ci/scripts/common/post-upload.sh

View File

@@ -42,8 +42,3 @@ 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/"
# copy FFmpeg libraries
EXTERNALS_PATH="$(pwd)/build/externals"
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/ ';'

3
.gitmodules vendored
View File

@@ -37,6 +37,3 @@
[submodule "opus"]
path = externals/opus/opus
url = https://github.com/xiph/opus.git
[submodule "externals/ffmpeg"]
path = externals/ffmpeg
url = https://git.ffmpeg.org/ffmpeg.git

View File

@@ -18,8 +18,6 @@ CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" ON "EN
option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_FFMPEG "Download/Build bundled yuzu" ON "WIN32" OFF)
option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF)
option(YUZU_ENABLE_BOXCAT "Enable the Boxcat service, a yuzu high-level implementation of BCAT" ON)
@@ -28,10 +26,6 @@ option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF)
if (NOT ENABLE_WEB_SERVICE)
set(YUZU_ENABLE_BOXCAT OFF)
endif()
# Default to a Release build
get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if (NOT IS_MULTI_CONFIG AND NOT CMAKE_BUILD_TYPE)
@@ -245,7 +239,7 @@ if(ENABLE_QT)
if (YUZU_USE_QT_WEB_ENGINE)
find_package(Qt5 COMPONENTS WebEngineCore WebEngineWidgets)
endif()
if (ENABLE_QT_TRANSLATION)
find_package(Qt5 REQUIRED COMPONENTS LinguistTools ${QT_PREFIX_HINT})
endif()
@@ -263,7 +257,7 @@ if(ENABLE_SDL2)
find_package(SDL2)
if (NOT SDL2_FOUND)
# otherwise add this to the list of libraries to install
list(APPEND CONAN_REQUIRED_LIBS "sdl2/2.0.14@bincrafters/stable")
list(APPEND CONAN_REQUIRED_LIBS "sdl2/2.0.12@bincrafters/stable")
endif()
endif()
@@ -328,7 +322,7 @@ if (CONAN_REQUIRED_LIBS)
list(APPEND Boost_LIBRARIES Boost::context)
endif()
endif()
# Due to issues with variable scopes in functions, we need to also find_package(qt5) outside of the function
if(ENABLE_QT)
list(APPEND CMAKE_MODULE_PATH "${CONAN_QT_ROOT_RELEASE}")
@@ -386,141 +380,19 @@ if (NOT LIBUSB_FOUND)
set(LIBUSB_LIBRARIES usb)
endif()
# List of all FFmpeg components required
set(FFmpeg_COMPONENTS
avcodec
avutil
swscale)
if (NOT YUZU_USE_BUNDLED_FFMPEG)
# Use system installed FFmpeg
find_package(FFmpeg REQUIRED COMPONENTS ${FFmpeg_COMPONENTS})
if (FFmpeg_FOUND)
# Overwrite aggregate defines from FFmpeg module to avoid over-linking libraries.
# Prevents shipping too many libraries with the AppImage.
set(FFmpeg_LIBRARIES "")
set(FFmpeg_INCLUDE_DIR "")
foreach(COMPONENT ${FFmpeg_COMPONENTS})
set(FFmpeg_LIBRARIES ${FFmpeg_LIBRARIES} ${FFmpeg_LIBRARY_${COMPONENT}} CACHE PATH "Paths to FFmpeg libraries" FORCE)
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")
set(YUZU_USE_BUNDLED_FFMPEG ON)
endif()
# Use system installed ffmpeg.
if (NOT MSVC)
find_package(FFmpeg REQUIRED)
else()
set(FFMPEG_EXT_NAME "ffmpeg-4.2.1")
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" FORCE)
set(FFMPEG_DLL_DIR "${FFMPEG_PATH}/bin" CACHE PATH "Path to FFmpeg dll's" FORCE)
endif()
if (YUZU_USE_BUNDLED_FFMPEG)
if (NOT WIN32)
# 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 REQUIRED)
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()
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()
set(FFmpeg_INCLUDE_DIR
${FFmpeg_PREFIX}
CACHE PATH "Path to FFmpeg headers" FORCE)
# `configure` parameters builds only exactly what yuzu needs from FFmpeg
# `--disable-{vaapi,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
--disable-vaapi
--disable-vdpau
--enable-decoder=h264
--enable-decoder=vp9
WORKING_DIRECTORY
${FFmpeg_BUILD_DIR}
)
# 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)
add_custom_command(
OUTPUT
${FFmpeg_LIBRARIES}
COMMAND
make -j${SYSTEM_THREADS}
WORKING_DIRECTORY
${FFmpeg_BUILD_DIR}
)
# 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-build ALL DEPENDS ${FFmpeg_LIBRARIES})
add_custom_target(ffmpeg-configure ALL DEPENDS ${FFmpeg_MAKEFILE})
if (FFmpeg_FOUND)
message(STATUS "Found FFmpeg version ${FFmpeg_VERSION}")
add_dependencies(ffmpeg-build ffmpeg-configure)
else()
message(FATAL_ERROR "FFmpeg not found")
endif()
else() # WIN32
# Use yuzu FFmpeg binaries
set(FFmpeg_EXT_NAME "ffmpeg-4.2.1")
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_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)

View File

@@ -1,7 +1,7 @@
function(copy_yuzu_FFmpeg_deps target_dir)
include(WindowsCopyFiles)
set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/")
windows_copy_files(${target_dir} ${FFmpeg_DLL_DIR} ${DLL_DEST}
windows_copy_files(${target_dir} ${FFMPEG_DLL_DIR} ${DLL_DEST}
avcodec-58.dll
avutil-56.dll
swresample-3.dll

View File

@@ -30,10 +30,11 @@ If you want to contribute to the user interface translation, please check out th
* __Windows__: [Windows Build](https://github.com/yuzu-emu/yuzu/wiki/Building-For-Windows)
* __Linux__: [Linux Build](https://github.com/yuzu-emu/yuzu/wiki/Building-For-Linux)
* __macOS__: [macOS Build](https://github.com/yuzu-emu/yuzu/wiki/Building-for-macOS)
### Support
We happily accept monetary donations, or donated games and hardware. Please see our [donations page](https://yuzu-emu.org/donate/) for more information on how you can contribute to yuzu. Any donations received will go towards things like:
We happily accept monetary donations or donated games and hardware. Please see our [donations page](https://yuzu-emu.org/donate/) for more information on how you can contribute to yuzu. Any donations received will go towards things like:
* Switch consoles to explore and reverse-engineer the hardware
* Switch games for testing, reverse-engineering, and implementing new features
* Web hosting and infrastructure setup

View File

@@ -1,5 +1,26 @@
<RCC>
<qresource prefix="controller">
<file alias="dual_joycon">dual_joycon.png</file>
<file alias="dual_joycon_dark">dual_joycon_dark.png</file>
<file alias="dual_joycon_midnight">dual_joycon_midnight.png</file>
<file alias="handheld">handheld.png</file>
<file alias="handheld_dark">handheld_dark.png</file>
<file alias="handheld_midnight">handheld_midnight.png</file>
<file alias="pro_controller">pro_controller.png</file>
<file alias="pro_controller_dark">pro_controller_dark.png</file>
<file alias="pro_controller_midnight">pro_controller_midnight.png</file>
<file alias="single_joycon_left">single_joycon_left.png</file>
<file alias="single_joycon_left_dark">single_joycon_left_dark.png</file>
<file alias="single_joycon_left_midnight">single_joycon_left_midnight.png</file>
<file alias="single_joycon_right">single_joycon_right.png</file>
<file alias="single_joycon_right_dark">single_joycon_right_dark.png</file>
<file alias="single_joycon_right_midnight">single_joycon_right_midnight.png</file>
<file alias="single_joycon_left_vertical">single_joycon_left_vertical.png</file>
<file alias="single_joycon_left_vertical_dark">single_joycon_left_vertical_dark.png</file>
<file alias="single_joycon_left_vertical_midnight">single_joycon_left_vertical_midnight.png</file>
<file alias="single_joycon_right_vertical">single_joycon_right_vertical.png</file>
<file alias="single_joycon_right_vertical_dark">single_joycon_right_vertical_dark.png</file>
<file alias="single_joycon_right_vertical_midnight">single_joycon_right_vertical_midnight.png</file>
<file alias="applet_dual_joycon">applet_dual_joycon.png</file>
<file alias="applet_dual_joycon_dark">applet_dual_joycon_dark.png</file>
<file alias="applet_dual_joycon_midnight">applet_dual_joycon_midnight.png</file>

BIN
dist/icons/controller/dual_joycon.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
dist/icons/controller/handheld.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
dist/icons/controller/handheld_dark.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
dist/icons/controller/pro_controller.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

1
externals/ffmpeg vendored

Submodule externals/ffmpeg deleted from 6b6b9e593d

View File

@@ -1,187 +1,100 @@
# FindFFmpeg
# ----------
# - Try to find ffmpeg libraries (libavcodec, libavformat and libavutil)
# Once done this will define
#
# Copyright 2019 Citra Emulator Project
# Licensed under GPLv2 or any later version
# FFMPEG_FOUND - system has ffmpeg or libav
# FFMPEG_INCLUDE_DIR - the ffmpeg include directory
# FFMPEG_LIBRARIES - Link these to use ffmpeg
# FFMPEG_LIBAVCODEC
# FFMPEG_LIBAVFORMAT
# FFMPEG_LIBAVUTIL
#
# Find the native FFmpeg includes and libraries
# Copyright (c) 2008 Andreas Schneider <mail@cynapses.org>
# Modified for other libraries by Lasse Kärkkäinen <tronic>
# Modified for Hedgewars by Stepik777
# Modified for FFmpeg-example Tuukka Pasanen 2018
# Modified for yuzu toastUnlimted 2020
#
# This module defines the following variables:
# Redistribution and use is allowed according to the terms of the New
# BSD license.
#
# FFmpeg_INCLUDE_<component>: where to find <component>.h
# FFmpeg_LIBRARY_<component>: where to find the <component> library
# FFmpeg_INCLUDE_DIR: aggregate all the include paths
# FFmpeg_LIBRARIES: aggregate all the paths to the libraries
# FFmpeg_FOUND: True if all components have been found
#
# This module defines the following targets, which are prefered over variables:
#
# FFmpeg::<component>: Target to use <component> directly, with include path,
# library and dependencies set up. If you are using a static build, you are
# responsible for adding any external dependencies (such as zlib, bzlib...).
#
# <component> can be one of:
# avcodec
# avdevice
# avfilter
# avformat
# avutil
# postproc
# swresample
# swscale
#
set(_FFmpeg_ALL_COMPONENTS
avcodec
avdevice
avfilter
avformat
avutil
postproc
swresample
swscale
)
set(_FFmpeg_DEPS_avcodec avutil)
set(_FFmpeg_DEPS_avdevice avcodec avformat avutil)
set(_FFmpeg_DEPS_avfilter avutil)
set(_FFmpeg_DEPS_avformat avcodec avutil)
set(_FFmpeg_DEPS_postproc avutil)
set(_FFmpeg_DEPS_swresample avutil)
set(_FFmpeg_DEPS_swscale avutil)
function(find_ffmpeg LIBNAME)
if(DEFINED ENV{FFMPEG_DIR})
set(FFMPEG_DIR $ENV{FFMPEG_DIR})
endif()
if(FFMPEG_DIR)
list(APPEND INCLUDE_PATHS
${FFMPEG_DIR}
${FFMPEG_DIR}/ffmpeg
${FFMPEG_DIR}/lib${LIBNAME}
${FFMPEG_DIR}/include/lib${LIBNAME}
${FFMPEG_DIR}/include/ffmpeg
${FFMPEG_DIR}/include
NO_DEFAULT_PATH
NO_CMAKE_FIND_ROOT_PATH
)
list(APPEND LIB_PATHS
${FFMPEG_DIR}
${FFMPEG_DIR}/lib
${FFMPEG_DIR}/lib${LIBNAME}
NO_DEFAULT_PATH
NO_CMAKE_FIND_ROOT_PATH
)
else()
list(APPEND INCLUDE_PATHS
/usr/local/include/ffmpeg
/usr/local/include/lib${LIBNAME}
/usr/include/ffmpeg
/usr/include/lib${LIBNAME}
/usr/include/ffmpeg/lib${LIBNAME}
)
list(APPEND LIB_PATHS
/usr/local/lib
/usr/lib
)
endif()
find_path(FFmpeg_INCLUDE_${LIBNAME} lib${LIBNAME}/${LIBNAME}.h
HINTS ${INCLUDE_PATHS}
)
find_library(FFmpeg_LIBRARY_${LIBNAME} ${LIBNAME}
HINTS ${LIB_PATHS}
)
if(NOT FFMPEG_DIR AND (NOT FFmpeg_LIBRARY_${LIBNAME} OR NOT FFmpeg_INCLUDE_${LIBNAME}))
# Didn't find it in the usual paths, try pkg-config
find_package(PkgConfig QUIET)
pkg_check_modules(FFmpeg_PKGCONFIG_${LIBNAME} QUIET lib${LIBNAME})
find_path(FFmpeg_INCLUDE_${LIBNAME} lib${LIBNAME}/${LIBNAME}.h
${FFmpeg_PKGCONFIG_${LIBNAME}_INCLUDE_DIRS}
)
find_library(FFmpeg_LIBRARY_${LIBNAME} ${LIBNAME}
${FFmpeg_PKGCONFIG_${LIBNAME}_LIBRARY_DIRS}
)
endif()
if(FFmpeg_INCLUDE_${LIBNAME} AND FFmpeg_LIBRARY_${LIBNAME})
set(FFmpeg_INCLUDE_${LIBNAME} "${FFmpeg_INCLUDE_${LIBNAME}}" PARENT_SCOPE)
set(FFmpeg_LIBRARY_${LIBNAME} "${FFmpeg_LIBRARY_${LIBNAME}}" PARENT_SCOPE)
# Extract FFmpeg version from version.h
foreach(v MAJOR MINOR MICRO)
set(FFmpeg_${LIBNAME}_VERSION_${v} 0)
endforeach()
string(TOUPPER ${LIBNAME} LIBNAME_UPPER)
file(STRINGS "${FFmpeg_INCLUDE_${LIBNAME}}/lib${LIBNAME}/version.h" _FFmpeg_VERSION_H_CONTENTS REGEX "#define LIB${LIBNAME_UPPER}_VERSION_(MAJOR|MINOR|MICRO) ")
set(_FFmpeg_VERSION_REGEX "([0-9]+)")
foreach(v MAJOR MINOR MICRO)
if("${_FFmpeg_VERSION_H_CONTENTS}" MATCHES "#define LIB${LIBNAME_UPPER}_VERSION_${v}[\\t ]+${_FFmpeg_VERSION_REGEX}")
set(FFmpeg_${LIBNAME}_VERSION_${v} "${CMAKE_MATCH_1}")
endif()
endforeach()
set(FFmpeg_${LIBNAME}_VERSION "${FFmpeg_${LIBNAME}_VERSION_MAJOR}.${FFmpeg_${LIBNAME}_VERSION_MINOR}.${FFmpeg_${LIBNAME}_VERSION_MICRO}")
set(FFmpeg_${c}_VERSION "${FFmpeg_${LIBNAME}_VERSION}" PARENT_SCOPE)
unset(_FFmpeg_VERSION_REGEX)
unset(_FFmpeg_VERSION_H_CONTENTS)
set(FFmpeg_${c}_FOUND TRUE PARENT_SCOPE)
if(NOT FFmpeg_FIND_QUIETLY)
message("-- Found ${LIBNAME}: ${FFmpeg_INCLUDE_${LIBNAME}} ${FFmpeg_LIBRARY_${LIBNAME}} (version: ${FFmpeg_${LIBNAME}_VERSION})")
endif()
endif()
endfunction()
foreach(c ${_FFmpeg_ALL_COMPONENTS})
find_ffmpeg(${c})
endforeach()
foreach(c ${_FFmpeg_ALL_COMPONENTS})
if(FFmpeg_${c}_FOUND)
list(APPEND FFmpeg_INCLUDE_DIR ${FFmpeg_INCLUDE_${c}})
list(APPEND FFmpeg_LIBRARIES ${FFmpeg_LIBRARY_${c}})
add_library(FFmpeg::${c} IMPORTED UNKNOWN)
set_target_properties(FFmpeg::${c} PROPERTIES
IMPORTED_LOCATION ${FFmpeg_LIBRARY_${c}}
INTERFACE_INCLUDE_DIRECTORIES ${FFmpeg_INCLUDE_${c}}
)
if(_FFmpeg_DEPS_${c})
set(deps)
foreach(dep ${_FFmpeg_DEPS_${c}})
list(APPEND deps FFmpeg::${dep})
endforeach()
set_target_properties(FFmpeg::${c} PROPERTIES
INTERFACE_LINK_LIBRARIES "${deps}"
)
unset(deps)
endif()
endif()
endforeach()
if(FFmpeg_INCLUDE_DIR)
list(REMOVE_DUPLICATES FFmpeg_INCLUDE_DIR)
endif()
foreach(c ${FFmpeg_FIND_COMPONENTS})
list(APPEND _FFmpeg_REQUIRED_VARS FFmpeg_INCLUDE_${c} FFmpeg_LIBRARY_${c})
endforeach()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(FFmpeg
REQUIRED_VARS ${_FFmpeg_REQUIRED_VARS}
HANDLE_COMPONENTS
find_package_handle_standard_args(FFMPEG
FOUND_VAR FFMPEG_FOUND
REQUIRED_VARS
FFMPEG_LIBRARY
FFMPEG_INCLUDE_DIR
VERSION_VAR FFMPEG_VERSION
)
foreach(c ${_FFmpeg_ALL_COMPONENTS})
unset(_FFmpeg_DEPS_${c})
endforeach()
unset(_FFmpeg_ALL_COMPONENTS)
unset(_FFmpeg_REQUIRED_VARS)
if(FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR)
# in cache already
set(FFMPEG_FOUND TRUE)
else()
# use pkg-config to get the directories and then use these values
# in the FIND_PATH() and FIND_LIBRARY() calls
find_package(PkgConfig)
if(PKG_CONFIG_FOUND)
pkg_check_modules(_FFMPEG_AVCODEC libavcodec)
pkg_check_modules(_FFMPEG_AVUTIL libavutil)
pkg_check_modules(_FFMPEG_SWSCALE libswscale)
endif()
find_path(FFMPEG_AVCODEC_INCLUDE_DIR
NAMES libavcodec/avcodec.h
PATHS ${_FFMPEG_AVCODEC_INCLUDE_DIRS}
/usr/include
/usr/local/include
/opt/local/include
/sw/include
PATH_SUFFIXES ffmpeg libav)
find_library(FFMPEG_LIBAVCODEC
NAMES avcodec
PATHS ${_FFMPEG_AVCODEC_LIBRARY_DIRS}
/usr/lib
/usr/local/lib
/opt/local/lib
/sw/lib)
find_library(FFMPEG_LIBAVUTIL
NAMES avutil
PATHS ${_FFMPEG_AVUTIL_LIBRARY_DIRS}
/usr/lib
/usr/local/lib
/opt/local/lib
/sw/lib)
find_library(FFMPEG_LIBSWSCALE
NAMES swscale
PATHS ${_FFMPEG_SWSCALE_LIBRARY_DIRS}
/usr/lib
/usr/local/lib
/opt/local/lib
/sw/lib)
if(FFMPEG_LIBAVCODEC AND FFMPEG_LIBAVUTIL AND FFMPEG_LIBSWSCALE)
set(FFMPEG_FOUND TRUE)
endif()
if(FFMPEG_FOUND)
set(FFMPEG_INCLUDE_DIR ${FFMPEG_AVCODEC_INCLUDE_DIR})
set(FFMPEG_LIBRARIES
${FFMPEG_LIBAVCODEC}
${FFMPEG_LIBAVUTIL}
${FFMPEG_LIBSWSCALE})
endif()
if(FFMPEG_FOUND)
if(NOT FFMPEG_FIND_QUIETLY)
message(STATUS
"Found FFMPEG or Libav: ${FFMPEG_LIBRARIES}, ${FFMPEG_INCLUDE_DIR}")
endif()
else()
if(FFMPEG_FIND_REQUIRED)
message(FATAL_ERROR
"Could not find libavcodec or libavutil or libswscale")
endif()
endif()
endif()

View File

@@ -45,15 +45,10 @@ if (MSVC)
# Warnings
/W3
/we4062 # enumerator 'identifier' in a switch of enum 'enumeration' is not handled
/we4101 # 'identifier': unreferenced local variable
/we4265 # 'class': class has virtual functions, but destructor is not virtual
/we4388 # signed/unsigned mismatch
/we4547 # 'operator' : operator before comma has no effect; expected operator with side-effect
/we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'?
/we4555 # Expression has no effect; expected expression with side-effect
/we4834 # Discarding return value of function with 'nodiscard' attribute
/we5038 # data member 'member1' will be initialized after data member 'member2'
)
# /GS- - No stack buffer overflow checks
@@ -64,16 +59,11 @@ if (MSVC)
else()
add_compile_options(
-Wall
-Werror=array-bounds
-Werror=implicit-fallthrough
-Werror=missing-declarations
-Werror=missing-field-initializers
-Werror=reorder
-Werror=switch
-Werror=uninitialized
-Werror=unused-function
-Werror=unused-result
-Werror=unused-variable
-Wextra
-Wmissing-declarations
-Wno-attributes
@@ -132,6 +122,7 @@ add_subdirectory(tests)
if (ENABLE_SDL2)
add_subdirectory(yuzu_cmd)
add_subdirectory(yuzu_tester)
endif()
if (ENABLE_QT)

View File

@@ -383,14 +383,11 @@ void CommandGenerator::GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, E
const auto channel_count = params.channel_count;
for (s32 i = 0; i < channel_count; i++) {
// TODO(ogniK): Actually implement reverb
/*
if (params.input[i] != params.output[i]) {
const auto* input = GetMixBuffer(mix_buffer_offset + params.input[i]);
auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]);
ApplyMix<1>(output, input, 32768, worker_params.sample_count);
}*/
auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]);
std::memset(output, 0, worker_params.sample_count * sizeof(s32));
}
}
}

View File

@@ -40,17 +40,17 @@ public:
SinkSampleFormat sample_format;
std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> input;
bool in_use;
INSERT_PADDING_BYTES_NOINIT(5);
INSERT_UNION_PADDING_BYTES(5);
};
static_assert(sizeof(CircularBufferIn) == 0x28,
"SinkInfo::CircularBufferIn is in invalid size");
struct DeviceIn {
std::array<u8, 255> device_name;
INSERT_PADDING_BYTES_NOINIT(1);
INSERT_UNION_PADDING_BYTES(1);
s32_le input_count;
std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> input;
INSERT_PADDING_BYTES_NOINIT(1);
INSERT_UNION_PADDING_BYTES(1);
bool down_matrix_enabled;
DownmixCoefficients down_matrix_coef;
};

View File

@@ -51,14 +51,6 @@ void Stream::Stop() {
UNIMPLEMENTED();
}
bool Stream::Flush() {
const bool had_buffers = !queued_buffers.empty();
while (!queued_buffers.empty()) {
queued_buffers.pop();
}
return had_buffers;
}
void Stream::SetVolume(float volume) {
game_volume = volume;
}
@@ -111,14 +103,7 @@ void Stream::PlayNextBuffer(std::chrono::nanoseconds ns_late) {
sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
const auto buffer_release_ns = GetBufferReleaseNS(*active_buffer);
// If ns_late is higher than the update rate ignore the delay
if (ns_late > buffer_release_ns) {
ns_late = {};
}
core_timing.ScheduleEvent(buffer_release_ns - ns_late, release_event, {});
core_timing.ScheduleEvent(GetBufferReleaseNS(*active_buffer) - ns_late, release_event, {});
}
void Stream::ReleaseActiveBuffer(std::chrono::nanoseconds ns_late) {

View File

@@ -56,9 +56,6 @@ public:
/// Queues a buffer into the audio stream, returns true on success
bool QueueBuffer(BufferPtr&& buffer);
/// Flush audio buffers
bool Flush();
/// Returns true if the audio stream contains a buffer with the specified tag
[[nodiscard]] bool ContainsBuffer(Buffer::Tag tag) const;

View File

@@ -86,28 +86,28 @@ struct BehaviorFlags {
static_assert(sizeof(BehaviorFlags) == 0x4, "BehaviorFlags is an invalid size");
struct ADPCMContext {
u16 header;
s16 yn1;
s16 yn2;
u16 header{};
s16 yn1{};
s16 yn2{};
};
static_assert(sizeof(ADPCMContext) == 0x6, "ADPCMContext is an invalid size");
struct VoiceState {
s64 played_sample_count;
s32 offset;
s32 wave_buffer_index;
std::array<bool, AudioCommon::MAX_WAVE_BUFFERS> is_wave_buffer_valid;
s32 wave_buffer_consumed;
std::array<s32, AudioCommon::MAX_SAMPLE_HISTORY> sample_history;
s32 fraction;
VAddr context_address;
Codec::ADPCM_Coeff coeff;
ADPCMContext context;
std::array<s64, 2> biquad_filter_state;
std::array<s32, AudioCommon::MAX_MIX_BUFFERS> previous_samples;
u32 external_context_size;
bool is_external_context_used;
bool voice_dropped;
s64 played_sample_count{};
s32 offset{};
s32 wave_buffer_index{};
std::array<bool, AudioCommon::MAX_WAVE_BUFFERS> is_wave_buffer_valid{};
s32 wave_buffer_consumed{};
std::array<s32, AudioCommon::MAX_SAMPLE_HISTORY> sample_history{};
s32 fraction{};
VAddr context_address{};
Codec::ADPCM_Coeff coeff{};
ADPCMContext context{};
std::array<s64, 2> biquad_filter_state{};
std::array<s32, AudioCommon::MAX_MIX_BUFFERS> previous_samples{};
u32 external_context_size{};
bool is_external_context_used{};
bool voice_dropped{};
};
class VoiceChannelResource {

View File

@@ -98,6 +98,7 @@ add_library(common STATIC
algorithm.h
alignment.h
assert.h
atomic_ops.cpp
atomic_ops.h
detached_tasks.cpp
detached_tasks.h
@@ -107,6 +108,7 @@ add_library(common STATIC
bit_util.h
cityhash.cpp
cityhash.h
color.h
common_funcs.h
common_paths.h
common_types.h
@@ -121,7 +123,6 @@ add_library(common STATIC
hash.h
hex_util.cpp
hex_util.h
intrusive_red_black_tree.h
logging/backend.cpp
logging/backend.h
logging/filter.cpp
@@ -138,13 +139,10 @@ add_library(common STATIC
microprofile.h
microprofileui.h
misc.cpp
nvidia_flags.cpp
nvidia_flags.h
page_table.cpp
page_table.h
param_package.cpp
param_package.h
parent_of_member.h
quaternion.h
ring_buffer.h
scm_rev.cpp
@@ -167,7 +165,8 @@ add_library(common STATIC
threadsafe_queue.h
time_zone.cpp
time_zone.h
tree.h
timer.cpp
timer.h
uint128.cpp
uint128.h
uuid.cpp

View File

@@ -9,45 +9,50 @@
namespace Common {
template <typename T>
requires std::is_unsigned_v<T>[[nodiscard]] constexpr T AlignUp(T value, size_t size) {
[[nodiscard]] constexpr T AlignUp(T value, std::size_t size) {
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
auto mod{static_cast<T>(value % size)};
value -= mod;
return static_cast<T>(mod == T{0} ? value : value + size);
}
template <typename T>
requires std::is_unsigned_v<T>[[nodiscard]] constexpr T AlignUpLog2(T value, size_t align_log2) {
return static_cast<T>((value + ((1ULL << align_log2) - 1)) >> align_log2 << align_log2);
}
template <typename T>
requires std::is_unsigned_v<T>[[nodiscard]] constexpr T AlignDown(T value, size_t size) {
[[nodiscard]] constexpr T AlignDown(T value, std::size_t size) {
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
return static_cast<T>(value - value % size);
}
template <typename T>
requires std::is_unsigned_v<T>[[nodiscard]] constexpr bool Is4KBAligned(T value) {
[[nodiscard]] constexpr T AlignBits(T value, std::size_t align) {
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
return static_cast<T>((value + ((1ULL << align) - 1)) >> align << align);
}
template <typename T>
[[nodiscard]] constexpr bool Is4KBAligned(T value) {
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
return (value & 0xFFF) == 0;
}
template <typename T>
requires std::is_unsigned_v<T>[[nodiscard]] constexpr bool IsWordAligned(T value) {
[[nodiscard]] constexpr bool IsWordAligned(T value) {
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
return (value & 0b11) == 0;
}
template <typename T>
requires std::is_integral_v<T>[[nodiscard]] constexpr bool IsAligned(T value, size_t alignment) {
using U = typename std::make_unsigned_t<T>;
[[nodiscard]] constexpr bool IsAligned(T value, std::size_t alignment) {
using U = typename std::make_unsigned<T>::type;
const U mask = static_cast<U>(alignment - 1);
return (value & mask) == 0;
}
template <typename T, size_t Align = 16>
template <typename T, std::size_t Align = 16>
class AlignmentAllocator {
public:
using value_type = T;
using size_type = size_t;
using difference_type = ptrdiff_t;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using propagate_on_container_copy_assignment = std::true_type;
using propagate_on_container_move_assignment = std::true_type;

75
src/common/atomic_ops.cpp Normal file
View File

@@ -0,0 +1,75 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include "common/atomic_ops.h"
#if _MSC_VER
#include <intrin.h>
#endif
namespace Common {
#if _MSC_VER
bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) {
const u8 result =
_InterlockedCompareExchange8(reinterpret_cast<volatile char*>(pointer), value, expected);
return result == expected;
}
bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) {
const u16 result =
_InterlockedCompareExchange16(reinterpret_cast<volatile short*>(pointer), value, expected);
return result == expected;
}
bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) {
const u32 result =
_InterlockedCompareExchange(reinterpret_cast<volatile long*>(pointer), value, expected);
return result == expected;
}
bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) {
const u64 result = _InterlockedCompareExchange64(reinterpret_cast<volatile __int64*>(pointer),
value, expected);
return result == expected;
}
bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) {
return _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), value[1],
value[0],
reinterpret_cast<__int64*>(expected.data())) != 0;
}
#else
bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) {
return __sync_bool_compare_and_swap(pointer, expected, value);
}
bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) {
return __sync_bool_compare_and_swap(pointer, expected, value);
}
bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) {
return __sync_bool_compare_and_swap(pointer, expected, value);
}
bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) {
return __sync_bool_compare_and_swap(pointer, expected, value);
}
bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) {
unsigned __int128 value_a;
unsigned __int128 expected_a;
std::memcpy(&value_a, value.data(), sizeof(u128));
std::memcpy(&expected_a, expected.data(), sizeof(u128));
return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
}
#endif
} // namespace Common

View File

@@ -4,75 +4,14 @@
#pragma once
#include <cstring>
#include <memory>
#include "common/common_types.h"
#if _MSC_VER
#include <intrin.h>
#endif
namespace Common {
#if _MSC_VER
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) {
const u8 result =
_InterlockedCompareExchange8(reinterpret_cast<volatile char*>(pointer), value, expected);
return result == expected;
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) {
const u16 result =
_InterlockedCompareExchange16(reinterpret_cast<volatile short*>(pointer), value, expected);
return result == expected;
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) {
const u32 result =
_InterlockedCompareExchange(reinterpret_cast<volatile long*>(pointer), value, expected);
return result == expected;
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) {
const u64 result = _InterlockedCompareExchange64(reinterpret_cast<volatile __int64*>(pointer),
value, expected);
return result == expected;
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) {
return _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), value[1],
value[0],
reinterpret_cast<__int64*>(expected.data())) != 0;
}
#else
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) {
return __sync_bool_compare_and_swap(pointer, expected, value);
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) {
return __sync_bool_compare_and_swap(pointer, expected, value);
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) {
return __sync_bool_compare_and_swap(pointer, expected, value);
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) {
return __sync_bool_compare_and_swap(pointer, expected, value);
}
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) {
unsigned __int128 value_a;
unsigned __int128 expected_a;
std::memcpy(&value_a, value.data(), sizeof(u128));
std::memcpy(&expected_a, expected.data(), sizeof(u128));
return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
}
#endif
[[nodiscard]] bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected);
[[nodiscard]] bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected);
[[nodiscard]] bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected);
[[nodiscard]] bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected);
[[nodiscard]] bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected);
} // namespace Common

View File

@@ -4,10 +4,13 @@
#pragma once
#include <bit>
#include <climits>
#include <cstddef>
#ifdef _MSC_VER
#include <intrin.h>
#endif
#include "common/common_types.h"
namespace Common {
@@ -18,30 +21,124 @@ template <typename T>
return sizeof(T) * CHAR_BIT;
}
[[nodiscard]] constexpr u32 MostSignificantBit32(const u32 value) {
return 31U - static_cast<u32>(std::countl_zero(value));
#ifdef _MSC_VER
[[nodiscard]] inline u32 CountLeadingZeroes32(u32 value) {
unsigned long leading_zero = 0;
if (_BitScanReverse(&leading_zero, value) != 0) {
return 31 - leading_zero;
}
return 32;
}
[[nodiscard]] constexpr u32 MostSignificantBit64(const u64 value) {
return 63U - static_cast<u32>(std::countl_zero(value));
[[nodiscard]] inline u32 CountLeadingZeroes64(u64 value) {
unsigned long leading_zero = 0;
if (_BitScanReverse64(&leading_zero, value) != 0) {
return 63 - leading_zero;
}
return 64;
}
#else
[[nodiscard]] inline u32 CountLeadingZeroes32(u32 value) {
if (value == 0) {
return 32;
}
return static_cast<u32>(__builtin_clz(value));
}
[[nodiscard]] constexpr u32 Log2Floor32(const u32 value) {
[[nodiscard]] inline u32 CountLeadingZeroes64(u64 value) {
if (value == 0) {
return 64;
}
return static_cast<u32>(__builtin_clzll(value));
}
#endif
#ifdef _MSC_VER
[[nodiscard]] inline u32 CountTrailingZeroes32(u32 value) {
unsigned long trailing_zero = 0;
if (_BitScanForward(&trailing_zero, value) != 0) {
return trailing_zero;
}
return 32;
}
[[nodiscard]] inline u32 CountTrailingZeroes64(u64 value) {
unsigned long trailing_zero = 0;
if (_BitScanForward64(&trailing_zero, value) != 0) {
return trailing_zero;
}
return 64;
}
#else
[[nodiscard]] inline u32 CountTrailingZeroes32(u32 value) {
if (value == 0) {
return 32;
}
return static_cast<u32>(__builtin_ctz(value));
}
[[nodiscard]] inline u32 CountTrailingZeroes64(u64 value) {
if (value == 0) {
return 64;
}
return static_cast<u32>(__builtin_ctzll(value));
}
#endif
#ifdef _MSC_VER
[[nodiscard]] inline u32 MostSignificantBit32(const u32 value) {
unsigned long result;
_BitScanReverse(&result, value);
return static_cast<u32>(result);
}
[[nodiscard]] inline u32 MostSignificantBit64(const u64 value) {
unsigned long result;
_BitScanReverse64(&result, value);
return static_cast<u32>(result);
}
#else
[[nodiscard]] inline u32 MostSignificantBit32(const u32 value) {
return 31U - static_cast<u32>(__builtin_clz(value));
}
[[nodiscard]] inline u32 MostSignificantBit64(const u64 value) {
return 63U - static_cast<u32>(__builtin_clzll(value));
}
#endif
[[nodiscard]] inline u32 Log2Floor32(const u32 value) {
return MostSignificantBit32(value);
}
[[nodiscard]] constexpr u32 Log2Floor64(const u64 value) {
[[nodiscard]] inline u32 Log2Ceil32(const u32 value) {
const u32 log2_f = Log2Floor32(value);
return log2_f + ((value ^ (1U << log2_f)) != 0U);
}
[[nodiscard]] inline u32 Log2Floor64(const u64 value) {
return MostSignificantBit64(value);
}
[[nodiscard]] constexpr u32 Log2Ceil32(const u32 value) {
const u32 log2_f = Log2Floor32(value);
return log2_f + static_cast<u32>((value ^ (1U << log2_f)) != 0U);
}
[[nodiscard]] constexpr u32 Log2Ceil64(const u64 value) {
const u64 log2_f = Log2Floor64(value);
return static_cast<u32>(log2_f + static_cast<u64>((value ^ (1ULL << log2_f)) != 0ULL));
[[nodiscard]] inline u32 Log2Ceil64(const u64 value) {
const u64 log2_f = static_cast<u64>(Log2Floor64(value));
return static_cast<u32>(log2_f + ((value ^ (1ULL << log2_f)) != 0ULL));
}
} // namespace Common

271
src/common/color.h Normal file
View File

@@ -0,0 +1,271 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <cstring>
#include "common/common_types.h"
#include "common/swap.h"
#include "common/vector_math.h"
namespace Common::Color {
/// Convert a 1-bit color component to 8 bit
[[nodiscard]] constexpr u8 Convert1To8(u8 value) {
return value * 255;
}
/// Convert a 4-bit color component to 8 bit
[[nodiscard]] constexpr u8 Convert4To8(u8 value) {
return (value << 4) | value;
}
/// Convert a 5-bit color component to 8 bit
[[nodiscard]] constexpr u8 Convert5To8(u8 value) {
return (value << 3) | (value >> 2);
}
/// Convert a 6-bit color component to 8 bit
[[nodiscard]] constexpr u8 Convert6To8(u8 value) {
return (value << 2) | (value >> 4);
}
/// Convert a 8-bit color component to 1 bit
[[nodiscard]] constexpr u8 Convert8To1(u8 value) {
return value >> 7;
}
/// Convert a 8-bit color component to 4 bit
[[nodiscard]] constexpr u8 Convert8To4(u8 value) {
return value >> 4;
}
/// Convert a 8-bit color component to 5 bit
[[nodiscard]] constexpr u8 Convert8To5(u8 value) {
return value >> 3;
}
/// Convert a 8-bit color component to 6 bit
[[nodiscard]] constexpr u8 Convert8To6(u8 value) {
return value >> 2;
}
/**
* Decode a color stored in RGBA8 format
* @param bytes Pointer to encoded source color
* @return Result color decoded as Common::Vec4<u8>
*/
[[nodiscard]] inline Common::Vec4<u8> DecodeRGBA8(const u8* bytes) {
return {bytes[3], bytes[2], bytes[1], bytes[0]};
}
/**
* Decode a color stored in RGB8 format
* @param bytes Pointer to encoded source color
* @return Result color decoded as Common::Vec4<u8>
*/
[[nodiscard]] inline Common::Vec4<u8> DecodeRGB8(const u8* bytes) {
return {bytes[2], bytes[1], bytes[0], 255};
}
/**
* Decode a color stored in RG8 (aka HILO8) format
* @param bytes Pointer to encoded source color
* @return Result color decoded as Common::Vec4<u8>
*/
[[nodiscard]] inline Common::Vec4<u8> DecodeRG8(const u8* bytes) {
return {bytes[1], bytes[0], 0, 255};
}
/**
* Decode a color stored in RGB565 format
* @param bytes Pointer to encoded source color
* @return Result color decoded as Common::Vec4<u8>
*/
[[nodiscard]] inline Common::Vec4<u8> DecodeRGB565(const u8* bytes) {
u16_le pixel;
std::memcpy(&pixel, bytes, sizeof(pixel));
return {Convert5To8((pixel >> 11) & 0x1F), Convert6To8((pixel >> 5) & 0x3F),
Convert5To8(pixel & 0x1F), 255};
}
/**
* Decode a color stored in RGB5A1 format
* @param bytes Pointer to encoded source color
* @return Result color decoded as Common::Vec4<u8>
*/
[[nodiscard]] inline Common::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
u16_le pixel;
std::memcpy(&pixel, bytes, sizeof(pixel));
return {Convert5To8((pixel >> 11) & 0x1F), Convert5To8((pixel >> 6) & 0x1F),
Convert5To8((pixel >> 1) & 0x1F), Convert1To8(pixel & 0x1)};
}
/**
* Decode a color stored in RGBA4 format
* @param bytes Pointer to encoded source color
* @return Result color decoded as Common::Vec4<u8>
*/
[[nodiscard]] inline Common::Vec4<u8> DecodeRGBA4(const u8* bytes) {
u16_le pixel;
std::memcpy(&pixel, bytes, sizeof(pixel));
return {Convert4To8((pixel >> 12) & 0xF), Convert4To8((pixel >> 8) & 0xF),
Convert4To8((pixel >> 4) & 0xF), Convert4To8(pixel & 0xF)};
}
/**
* Decode a depth value stored in D16 format
* @param bytes Pointer to encoded source value
* @return Depth value as an u32
*/
[[nodiscard]] inline u32 DecodeD16(const u8* bytes) {
u16_le data;
std::memcpy(&data, bytes, sizeof(data));
return data;
}
/**
* Decode a depth value stored in D24 format
* @param bytes Pointer to encoded source value
* @return Depth value as an u32
*/
[[nodiscard]] inline u32 DecodeD24(const u8* bytes) {
return (bytes[2] << 16) | (bytes[1] << 8) | bytes[0];
}
/**
* Decode a depth value and a stencil value stored in D24S8 format
* @param bytes Pointer to encoded source values
* @return Resulting values stored as a Common::Vec2
*/
[[nodiscard]] inline Common::Vec2<u32> DecodeD24S8(const u8* bytes) {
return {static_cast<u32>((bytes[2] << 16) | (bytes[1] << 8) | bytes[0]), bytes[3]};
}
/**
* Encode a color as RGBA8 format
* @param color Source color to encode
* @param bytes Destination pointer to store encoded color
*/
inline void EncodeRGBA8(const Common::Vec4<u8>& color, u8* bytes) {
bytes[3] = color.r();
bytes[2] = color.g();
bytes[1] = color.b();
bytes[0] = color.a();
}
/**
* Encode a color as RGB8 format
* @param color Source color to encode
* @param bytes Destination pointer to store encoded color
*/
inline void EncodeRGB8(const Common::Vec4<u8>& color, u8* bytes) {
bytes[2] = color.r();
bytes[1] = color.g();
bytes[0] = color.b();
}
/**
* Encode a color as RG8 (aka HILO8) format
* @param color Source color to encode
* @param bytes Destination pointer to store encoded color
*/
inline void EncodeRG8(const Common::Vec4<u8>& color, u8* bytes) {
bytes[1] = color.r();
bytes[0] = color.g();
}
/**
* Encode a color as RGB565 format
* @param color Source color to encode
* @param bytes Destination pointer to store encoded color
*/
inline void EncodeRGB565(const Common::Vec4<u8>& color, u8* bytes) {
const u16_le data =
(Convert8To5(color.r()) << 11) | (Convert8To6(color.g()) << 5) | Convert8To5(color.b());
std::memcpy(bytes, &data, sizeof(data));
}
/**
* Encode a color as RGB5A1 format
* @param color Source color to encode
* @param bytes Destination pointer to store encoded color
*/
inline void EncodeRGB5A1(const Common::Vec4<u8>& color, u8* bytes) {
const u16_le data = (Convert8To5(color.r()) << 11) | (Convert8To5(color.g()) << 6) |
(Convert8To5(color.b()) << 1) | Convert8To1(color.a());
std::memcpy(bytes, &data, sizeof(data));
}
/**
* Encode a color as RGBA4 format
* @param color Source color to encode
* @param bytes Destination pointer to store encoded color
*/
inline void EncodeRGBA4(const Common::Vec4<u8>& color, u8* bytes) {
const u16 data = (Convert8To4(color.r()) << 12) | (Convert8To4(color.g()) << 8) |
(Convert8To4(color.b()) << 4) | Convert8To4(color.a());
std::memcpy(bytes, &data, sizeof(data));
}
/**
* Encode a 16 bit depth value as D16 format
* @param value 16 bit source depth value to encode
* @param bytes Pointer where to store the encoded value
*/
inline void EncodeD16(u32 value, u8* bytes) {
const u16_le data = static_cast<u16>(value);
std::memcpy(bytes, &data, sizeof(data));
}
/**
* Encode a 24 bit depth value as D24 format
* @param value 24 bit source depth value to encode
* @param bytes Pointer where to store the encoded value
*/
inline void EncodeD24(u32 value, u8* bytes) {
bytes[0] = value & 0xFF;
bytes[1] = (value >> 8) & 0xFF;
bytes[2] = (value >> 16) & 0xFF;
}
/**
* Encode a 24 bit depth and 8 bit stencil values as D24S8 format
* @param depth 24 bit source depth value to encode
* @param stencil 8 bit source stencil value to encode
* @param bytes Pointer where to store the encoded value
*/
inline void EncodeD24S8(u32 depth, u8 stencil, u8* bytes) {
bytes[0] = depth & 0xFF;
bytes[1] = (depth >> 8) & 0xFF;
bytes[2] = (depth >> 16) & 0xFF;
bytes[3] = stencil;
}
/**
* Encode a 24 bit depth value as D24X8 format (32 bits per pixel with 8 bits unused)
* @param depth 24 bit source depth value to encode
* @param bytes Pointer where to store the encoded value
* @note unused bits will not be modified
*/
inline void EncodeD24X8(u32 depth, u8* bytes) {
bytes[0] = depth & 0xFF;
bytes[1] = (depth >> 8) & 0xFF;
bytes[2] = (depth >> 16) & 0xFF;
}
/**
* Encode an 8 bit stencil value as X24S8 format (32 bits per pixel with 24 bits unused)
* @param stencil 8 bit source stencil value to encode
* @param bytes Pointer where to store the encoded value
* @note unused bits will not be modified
*/
inline void EncodeX24S8(u8 stencil, u8* bytes) {
bytes[3] = stencil;
}
} // namespace Common::Color

View File

@@ -24,10 +24,10 @@
#define INSERT_PADDING_WORDS(num_words) \
std::array<u32, num_words> CONCAT2(pad, __LINE__) {}
/// These are similar to the INSERT_PADDING_* macros but do not zero-initialize the contents.
/// This keeps the structure trivial to construct.
#define INSERT_PADDING_BYTES_NOINIT(num_bytes) std::array<u8, num_bytes> CONCAT2(pad, __LINE__)
#define INSERT_PADDING_WORDS_NOINIT(num_words) std::array<u32, num_words> CONCAT2(pad, __LINE__)
/// These are similar to the INSERT_PADDING_* macros, but are needed for padding unions. This is
/// because unions can only be initialized by one member.
#define INSERT_UNION_PADDING_BYTES(num_bytes) std::array<u8, num_bytes> CONCAT2(pad, __LINE__)
#define INSERT_UNION_PADDING_WORDS(num_words) std::array<u32, num_words> CONCAT2(pad, __LINE__)
#ifndef _MSC_VER
@@ -93,31 +93,6 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
return static_cast<T>(key) == 0; \
}
/// Evaluates a boolean expression, and returns a result unless that expression is true.
#define R_UNLESS(expr, res) \
{ \
if (!(expr)) { \
if (res.IsError()) { \
LOG_ERROR(Kernel, "Failed with result: {}", res.raw); \
} \
return res; \
} \
}
#define R_SUCCEEDED(res) (res.IsSuccess())
/// Evaluates an expression that returns a result, and returns the result if it would fail.
#define R_TRY(res_expr) \
{ \
const auto _tmp_r_try_rc = (res_expr); \
if (_tmp_r_try_rc.IsError()) { \
return _tmp_r_try_rc; \
} \
}
/// Evaluates a boolean expression, and succeeds if that expression is true.
#define R_SUCCEED_IF(expr) R_UNLESS(!(expr), RESULT_SUCCESS)
namespace Common {
[[nodiscard]] constexpr u32 MakeMagic(char a, char b, char c, char d) {

View File

@@ -11,16 +11,16 @@ namespace Common {
/// Ceiled integer division.
template <typename N, typename D>
requires std::is_integral_v<N>&& std::is_unsigned_v<D>[[nodiscard]] constexpr N DivCeil(N number,
D divisor) {
return static_cast<N>((static_cast<D>(number) + divisor - 1) / divisor);
requires std::is_integral_v<N>&& std::is_unsigned_v<D>[[nodiscard]] constexpr auto DivCeil(
N number, D divisor) {
return (static_cast<D>(number) + divisor - 1) / divisor;
}
/// Ceiled integer division with logarithmic divisor in base 2
template <typename N, typename D>
requires std::is_integral_v<N>&& std::is_unsigned_v<D>[[nodiscard]] constexpr N DivCeilLog2(
requires std::is_integral_v<N>&& std::is_unsigned_v<D>[[nodiscard]] constexpr auto DivCeilLog2(
N value, D alignment_log2) {
return static_cast<N>((static_cast<D>(value) + (D(1) << alignment_log2) - 1) >> alignment_log2);
return (static_cast<D>(value) + (D(1) << alignment_log2) - 1) >> alignment_log2;
}
} // namespace Common

View File

@@ -1,602 +0,0 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/parent_of_member.h"
#include "common/tree.h"
namespace Common {
namespace impl {
class IntrusiveRedBlackTreeImpl;
}
struct IntrusiveRedBlackTreeNode {
public:
using EntryType = RBEntry<IntrusiveRedBlackTreeNode>;
constexpr IntrusiveRedBlackTreeNode() = default;
void SetEntry(const EntryType& new_entry) {
entry = new_entry;
}
[[nodiscard]] EntryType& GetEntry() {
return entry;
}
[[nodiscard]] const EntryType& GetEntry() const {
return entry;
}
private:
EntryType entry{};
friend class impl::IntrusiveRedBlackTreeImpl;
template <class, class, class>
friend class IntrusiveRedBlackTree;
};
template <class T, class Traits, class Comparator>
class IntrusiveRedBlackTree;
namespace impl {
class IntrusiveRedBlackTreeImpl {
private:
template <class, class, class>
friend class ::Common::IntrusiveRedBlackTree;
using RootType = RBHead<IntrusiveRedBlackTreeNode>;
RootType root;
public:
template <bool Const>
class Iterator;
using value_type = IntrusiveRedBlackTreeNode;
using size_type = size_t;
using difference_type = ptrdiff_t;
using pointer = value_type*;
using const_pointer = const value_type*;
using reference = value_type&;
using const_reference = const value_type&;
using iterator = Iterator<false>;
using const_iterator = Iterator<true>;
template <bool Const>
class Iterator {
public:
using iterator_category = std::bidirectional_iterator_tag;
using value_type = typename IntrusiveRedBlackTreeImpl::value_type;
using difference_type = typename IntrusiveRedBlackTreeImpl::difference_type;
using pointer = std::conditional_t<Const, IntrusiveRedBlackTreeImpl::const_pointer,
IntrusiveRedBlackTreeImpl::pointer>;
using reference = std::conditional_t<Const, IntrusiveRedBlackTreeImpl::const_reference,
IntrusiveRedBlackTreeImpl::reference>;
private:
pointer node;
public:
explicit Iterator(pointer n) : node(n) {}
bool operator==(const Iterator& rhs) const {
return this->node == rhs.node;
}
bool operator!=(const Iterator& rhs) const {
return !(*this == rhs);
}
pointer operator->() const {
return this->node;
}
reference operator*() const {
return *this->node;
}
Iterator& operator++() {
this->node = GetNext(this->node);
return *this;
}
Iterator& operator--() {
this->node = GetPrev(this->node);
return *this;
}
Iterator operator++(int) {
const Iterator it{*this};
++(*this);
return it;
}
Iterator operator--(int) {
const Iterator it{*this};
--(*this);
return it;
}
operator Iterator<true>() const {
return Iterator<true>(this->node);
}
};
private:
// Define accessors using RB_* functions.
bool EmptyImpl() const {
return root.IsEmpty();
}
IntrusiveRedBlackTreeNode* GetMinImpl() const {
return RB_MIN(const_cast<RootType*>(&root));
}
IntrusiveRedBlackTreeNode* GetMaxImpl() const {
return RB_MAX(const_cast<RootType*>(&root));
}
IntrusiveRedBlackTreeNode* RemoveImpl(IntrusiveRedBlackTreeNode* node) {
return RB_REMOVE(&root, node);
}
public:
static IntrusiveRedBlackTreeNode* GetNext(IntrusiveRedBlackTreeNode* node) {
return RB_NEXT(node);
}
static IntrusiveRedBlackTreeNode* GetPrev(IntrusiveRedBlackTreeNode* node) {
return RB_PREV(node);
}
static const IntrusiveRedBlackTreeNode* GetNext(const IntrusiveRedBlackTreeNode* node) {
return static_cast<const IntrusiveRedBlackTreeNode*>(
GetNext(const_cast<IntrusiveRedBlackTreeNode*>(node)));
}
static const IntrusiveRedBlackTreeNode* GetPrev(const IntrusiveRedBlackTreeNode* node) {
return static_cast<const IntrusiveRedBlackTreeNode*>(
GetPrev(const_cast<IntrusiveRedBlackTreeNode*>(node)));
}
public:
constexpr IntrusiveRedBlackTreeImpl() {}
// Iterator accessors.
iterator begin() {
return iterator(this->GetMinImpl());
}
const_iterator begin() const {
return const_iterator(this->GetMinImpl());
}
iterator end() {
return iterator(static_cast<IntrusiveRedBlackTreeNode*>(nullptr));
}
const_iterator end() const {
return const_iterator(static_cast<const IntrusiveRedBlackTreeNode*>(nullptr));
}
const_iterator cbegin() const {
return this->begin();
}
const_iterator cend() const {
return this->end();
}
iterator iterator_to(reference ref) {
return iterator(&ref);
}
const_iterator iterator_to(const_reference ref) const {
return const_iterator(&ref);
}
// Content management.
bool empty() const {
return this->EmptyImpl();
}
reference back() {
return *this->GetMaxImpl();
}
const_reference back() const {
return *this->GetMaxImpl();
}
reference front() {
return *this->GetMinImpl();
}
const_reference front() const {
return *this->GetMinImpl();
}
iterator erase(iterator it) {
auto cur = std::addressof(*it);
auto next = GetNext(cur);
this->RemoveImpl(cur);
return iterator(next);
}
};
} // namespace impl
template <typename T>
concept HasLightCompareType = requires {
{ std::is_same<typename T::LightCompareType, void>::value }
->std::convertible_to<bool>;
};
namespace impl {
template <typename T, typename Default>
consteval auto* GetLightCompareType() {
if constexpr (HasLightCompareType<T>) {
return static_cast<typename T::LightCompareType*>(nullptr);
} else {
return static_cast<Default*>(nullptr);
}
}
} // namespace impl
template <typename T, typename Default>
using LightCompareType = std::remove_pointer_t<decltype(impl::GetLightCompareType<T, Default>())>;
template <class T, class Traits, class Comparator>
class IntrusiveRedBlackTree {
public:
using ImplType = impl::IntrusiveRedBlackTreeImpl;
private:
ImplType impl{};
public:
template <bool Const>
class Iterator;
using value_type = T;
using size_type = size_t;
using difference_type = ptrdiff_t;
using pointer = T*;
using const_pointer = const T*;
using reference = T&;
using const_reference = const T&;
using iterator = Iterator<false>;
using const_iterator = Iterator<true>;
using light_value_type = LightCompareType<Comparator, value_type>;
using const_light_pointer = const light_value_type*;
using const_light_reference = const light_value_type&;
template <bool Const>
class Iterator {
public:
friend class IntrusiveRedBlackTree<T, Traits, Comparator>;
using ImplIterator =
std::conditional_t<Const, ImplType::const_iterator, ImplType::iterator>;
using iterator_category = std::bidirectional_iterator_tag;
using value_type = typename IntrusiveRedBlackTree::value_type;
using difference_type = typename IntrusiveRedBlackTree::difference_type;
using pointer = std::conditional_t<Const, IntrusiveRedBlackTree::const_pointer,
IntrusiveRedBlackTree::pointer>;
using reference = std::conditional_t<Const, IntrusiveRedBlackTree::const_reference,
IntrusiveRedBlackTree::reference>;
private:
ImplIterator iterator;
private:
explicit Iterator(ImplIterator it) : iterator(it) {}
explicit Iterator(typename std::conditional<Const, ImplType::const_iterator,
ImplType::iterator>::type::pointer ptr)
: iterator(ptr) {}
ImplIterator GetImplIterator() const {
return this->iterator;
}
public:
bool operator==(const Iterator& rhs) const {
return this->iterator == rhs.iterator;
}
bool operator!=(const Iterator& rhs) const {
return !(*this == rhs);
}
pointer operator->() const {
return Traits::GetParent(std::addressof(*this->iterator));
}
reference operator*() const {
return *Traits::GetParent(std::addressof(*this->iterator));
}
Iterator& operator++() {
++this->iterator;
return *this;
}
Iterator& operator--() {
--this->iterator;
return *this;
}
Iterator operator++(int) {
const Iterator it{*this};
++this->iterator;
return it;
}
Iterator operator--(int) {
const Iterator it{*this};
--this->iterator;
return it;
}
operator Iterator<true>() const {
return Iterator<true>(this->iterator);
}
};
private:
static int CompareImpl(const IntrusiveRedBlackTreeNode* lhs,
const IntrusiveRedBlackTreeNode* rhs) {
return Comparator::Compare(*Traits::GetParent(lhs), *Traits::GetParent(rhs));
}
static int LightCompareImpl(const void* elm, const IntrusiveRedBlackTreeNode* rhs) {
return Comparator::Compare(*static_cast<const_light_pointer>(elm), *Traits::GetParent(rhs));
}
// Define accessors using RB_* functions.
IntrusiveRedBlackTreeNode* InsertImpl(IntrusiveRedBlackTreeNode* node) {
return RB_INSERT(&impl.root, node, CompareImpl);
}
IntrusiveRedBlackTreeNode* FindImpl(const IntrusiveRedBlackTreeNode* node) const {
return RB_FIND(const_cast<ImplType::RootType*>(&impl.root),
const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl);
}
IntrusiveRedBlackTreeNode* NFindImpl(const IntrusiveRedBlackTreeNode* node) const {
return RB_NFIND(const_cast<ImplType::RootType*>(&impl.root),
const_cast<IntrusiveRedBlackTreeNode*>(node), CompareImpl);
}
IntrusiveRedBlackTreeNode* FindLightImpl(const_light_pointer lelm) const {
return RB_FIND_LIGHT(const_cast<ImplType::RootType*>(&impl.root),
static_cast<const void*>(lelm), LightCompareImpl);
}
IntrusiveRedBlackTreeNode* NFindLightImpl(const_light_pointer lelm) const {
return RB_NFIND_LIGHT(const_cast<ImplType::RootType*>(&impl.root),
static_cast<const void*>(lelm), LightCompareImpl);
}
public:
constexpr IntrusiveRedBlackTree() = default;
// Iterator accessors.
iterator begin() {
return iterator(this->impl.begin());
}
const_iterator begin() const {
return const_iterator(this->impl.begin());
}
iterator end() {
return iterator(this->impl.end());
}
const_iterator end() const {
return const_iterator(this->impl.end());
}
const_iterator cbegin() const {
return this->begin();
}
const_iterator cend() const {
return this->end();
}
iterator iterator_to(reference ref) {
return iterator(this->impl.iterator_to(*Traits::GetNode(std::addressof(ref))));
}
const_iterator iterator_to(const_reference ref) const {
return const_iterator(this->impl.iterator_to(*Traits::GetNode(std::addressof(ref))));
}
// Content management.
bool empty() const {
return this->impl.empty();
}
reference back() {
return *Traits::GetParent(std::addressof(this->impl.back()));
}
const_reference back() const {
return *Traits::GetParent(std::addressof(this->impl.back()));
}
reference front() {
return *Traits::GetParent(std::addressof(this->impl.front()));
}
const_reference front() const {
return *Traits::GetParent(std::addressof(this->impl.front()));
}
iterator erase(iterator it) {
return iterator(this->impl.erase(it.GetImplIterator()));
}
iterator insert(reference ref) {
ImplType::pointer node = Traits::GetNode(std::addressof(ref));
this->InsertImpl(node);
return iterator(node);
}
iterator find(const_reference ref) const {
return iterator(this->FindImpl(Traits::GetNode(std::addressof(ref))));
}
iterator nfind(const_reference ref) const {
return iterator(this->NFindImpl(Traits::GetNode(std::addressof(ref))));
}
iterator find_light(const_light_reference ref) const {
return iterator(this->FindLightImpl(std::addressof(ref)));
}
iterator nfind_light(const_light_reference ref) const {
return iterator(this->NFindLightImpl(std::addressof(ref)));
}
};
template <auto T, class Derived = impl::GetParentType<T>>
class IntrusiveRedBlackTreeMemberTraits;
template <class Parent, IntrusiveRedBlackTreeNode Parent::*Member, class Derived>
class IntrusiveRedBlackTreeMemberTraits<Member, Derived> {
public:
template <class Comparator>
using TreeType = IntrusiveRedBlackTree<Derived, IntrusiveRedBlackTreeMemberTraits, Comparator>;
using TreeTypeImpl = impl::IntrusiveRedBlackTreeImpl;
private:
template <class, class, class>
friend class IntrusiveRedBlackTree;
friend class impl::IntrusiveRedBlackTreeImpl;
static constexpr IntrusiveRedBlackTreeNode* GetNode(Derived* parent) {
return std::addressof(parent->*Member);
}
static constexpr IntrusiveRedBlackTreeNode const* GetNode(Derived const* parent) {
return std::addressof(parent->*Member);
}
static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) {
return GetParentPointer<Member, Derived>(node);
}
static constexpr Derived const* GetParent(const IntrusiveRedBlackTreeNode* node) {
return GetParentPointer<Member, Derived>(node);
}
private:
static constexpr TypedStorage<Derived> DerivedStorage = {};
static_assert(GetParent(GetNode(GetPointer(DerivedStorage))) == GetPointer(DerivedStorage));
};
template <auto T, class Derived = impl::GetParentType<T>>
class IntrusiveRedBlackTreeMemberTraitsDeferredAssert;
template <class Parent, IntrusiveRedBlackTreeNode Parent::*Member, class Derived>
class IntrusiveRedBlackTreeMemberTraitsDeferredAssert<Member, Derived> {
public:
template <class Comparator>
using TreeType =
IntrusiveRedBlackTree<Derived, IntrusiveRedBlackTreeMemberTraitsDeferredAssert, Comparator>;
using TreeTypeImpl = impl::IntrusiveRedBlackTreeImpl;
static constexpr bool IsValid() {
TypedStorage<Derived> DerivedStorage = {};
return GetParent(GetNode(GetPointer(DerivedStorage))) == GetPointer(DerivedStorage);
}
private:
template <class, class, class>
friend class IntrusiveRedBlackTree;
friend class impl::IntrusiveRedBlackTreeImpl;
static constexpr IntrusiveRedBlackTreeNode* GetNode(Derived* parent) {
return std::addressof(parent->*Member);
}
static constexpr IntrusiveRedBlackTreeNode const* GetNode(Derived const* parent) {
return std::addressof(parent->*Member);
}
static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) {
return GetParentPointer<Member, Derived>(node);
}
static constexpr Derived const* GetParent(const IntrusiveRedBlackTreeNode* node) {
return GetParentPointer<Member, Derived>(node);
}
};
template <class Derived>
class IntrusiveRedBlackTreeBaseNode : public IntrusiveRedBlackTreeNode {
public:
constexpr Derived* GetPrev() {
return static_cast<Derived*>(impl::IntrusiveRedBlackTreeImpl::GetPrev(this));
}
constexpr const Derived* GetPrev() const {
return static_cast<const Derived*>(impl::IntrusiveRedBlackTreeImpl::GetPrev(this));
}
constexpr Derived* GetNext() {
return static_cast<Derived*>(impl::IntrusiveRedBlackTreeImpl::GetNext(this));
}
constexpr const Derived* GetNext() const {
return static_cast<const Derived*>(impl::IntrusiveRedBlackTreeImpl::GetNext(this));
}
};
template <class Derived>
class IntrusiveRedBlackTreeBaseTraits {
public:
template <class Comparator>
using TreeType = IntrusiveRedBlackTree<Derived, IntrusiveRedBlackTreeBaseTraits, Comparator>;
using TreeTypeImpl = impl::IntrusiveRedBlackTreeImpl;
private:
template <class, class, class>
friend class IntrusiveRedBlackTree;
friend class impl::IntrusiveRedBlackTreeImpl;
static constexpr IntrusiveRedBlackTreeNode* GetNode(Derived* parent) {
return static_cast<IntrusiveRedBlackTreeNode*>(parent);
}
static constexpr IntrusiveRedBlackTreeNode const* GetNode(Derived const* parent) {
return static_cast<const IntrusiveRedBlackTreeNode*>(parent);
}
static constexpr Derived* GetParent(IntrusiveRedBlackTreeNode* node) {
return static_cast<Derived*>(node);
}
static constexpr Derived const* GetParent(const IntrusiveRedBlackTreeNode* node) {
return static_cast<const Derived*>(node);
}
};
} // namespace Common

View File

@@ -145,18 +145,10 @@ void ColorConsoleBackend::Write(const Entry& entry) {
PrintColoredMessage(entry);
}
FileBackend::FileBackend(const std::string& filename) : bytes_written(0) {
if (Common::FS::Exists(filename + ".old.txt")) {
Common::FS::Delete(filename + ".old.txt");
}
if (Common::FS::Exists(filename)) {
Common::FS::Rename(filename, filename + ".old.txt");
}
// _SH_DENYWR allows read only access to the file for other programs.
// It is #defined to 0 on other platforms
file = Common::FS::IOFile(filename, "w", _SH_DENYWR);
}
// _SH_DENYWR allows read only access to the file for other programs.
// It is #defined to 0 on other platforms
FileBackend::FileBackend(const std::string& filename)
: file(filename, "w", _SH_DENYWR), bytes_written(0) {}
void FileBackend::Write(const Entry& entry) {
// prevent logs from going over the maximum size (in case its spamming and the user doesn't

View File

@@ -1,27 +0,0 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <filesystem>
#include <stdlib.h>
#include <fmt/format.h>
#include "common/file_util.h"
#include "common/nvidia_flags.h"
namespace Common {
void ConfigureNvidiaEnvironmentFlags() {
#ifdef _WIN32
const std::string shader_path = Common::FS::SanitizePath(
fmt::format("{}/nvidia/", Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir)));
const std::string windows_path =
Common::FS::SanitizePath(shader_path, Common::FS::DirectorySeparator::BackwardSlash);
void(Common::FS::CreateFullPath(shader_path + '/'));
void(_putenv(fmt::format("__GL_SHADER_DISK_CACHE_PATH={}", windows_path).c_str()));
void(_putenv("__GL_SHADER_DISK_CACHE_SKIP_CLEANUP=1"));
#endif
}
} // namespace Common

View File

@@ -1,10 +0,0 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
namespace Common {
/// Configure platform specific flags for Nvidia's driver
void ConfigureNvidiaEnvironmentFlags();
} // namespace Common

View File

@@ -1,191 +0,0 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <type_traits>
#include "common/assert.h"
#include "common/common_types.h"
namespace Common {
namespace detail {
template <typename T, size_t Size, size_t Align>
struct TypedStorageImpl {
std::aligned_storage_t<Size, Align> storage_;
};
} // namespace detail
template <typename T>
using TypedStorage = detail::TypedStorageImpl<T, sizeof(T), alignof(T)>;
template <typename T>
static constexpr T* GetPointer(TypedStorage<T>& ts) {
return static_cast<T*>(static_cast<void*>(std::addressof(ts.storage_)));
}
template <typename T>
static constexpr const T* GetPointer(const TypedStorage<T>& ts) {
return static_cast<const T*>(static_cast<const void*>(std::addressof(ts.storage_)));
}
namespace impl {
template <size_t MaxDepth>
struct OffsetOfUnionHolder {
template <typename ParentType, typename MemberType, size_t Offset>
union UnionImpl {
using PaddingMember = char;
static constexpr size_t GetOffset() {
return Offset;
}
#pragma pack(push, 1)
struct {
PaddingMember padding[Offset];
MemberType members[(sizeof(ParentType) / sizeof(MemberType)) + 1];
} data;
#pragma pack(pop)
UnionImpl<ParentType, MemberType, Offset + 1> next_union;
};
template <typename ParentType, typename MemberType>
union UnionImpl<ParentType, MemberType, 0> {
static constexpr size_t GetOffset() {
return 0;
}
struct {
MemberType members[(sizeof(ParentType) / sizeof(MemberType)) + 1];
} data;
UnionImpl<ParentType, MemberType, 1> next_union;
};
template <typename ParentType, typename MemberType>
union UnionImpl<ParentType, MemberType, MaxDepth> {};
};
template <typename ParentType, typename MemberType>
struct OffsetOfCalculator {
using UnionHolder =
typename OffsetOfUnionHolder<sizeof(MemberType)>::template UnionImpl<ParentType, MemberType,
0>;
union Union {
char c{};
UnionHolder first_union;
TypedStorage<ParentType> parent;
constexpr Union() : c() {}
};
static constexpr Union U = {};
static constexpr const MemberType* GetNextAddress(const MemberType* start,
const MemberType* target) {
while (start < target) {
start++;
}
return start;
}
static constexpr std::ptrdiff_t GetDifference(const MemberType* start,
const MemberType* target) {
return (target - start) * sizeof(MemberType);
}
template <typename CurUnion>
static constexpr std::ptrdiff_t OffsetOfImpl(MemberType ParentType::*member,
CurUnion& cur_union) {
constexpr size_t Offset = CurUnion::GetOffset();
const auto target = std::addressof(GetPointer(U.parent)->*member);
const auto start = std::addressof(cur_union.data.members[0]);
const auto next = GetNextAddress(start, target);
if (next != target) {
if constexpr (Offset < sizeof(MemberType) - 1) {
return OffsetOfImpl(member, cur_union.next_union);
} else {
UNREACHABLE();
}
}
return (next - start) * sizeof(MemberType) + Offset;
}
static constexpr std::ptrdiff_t OffsetOf(MemberType ParentType::*member) {
return OffsetOfImpl(member, U.first_union);
}
};
template <typename T>
struct GetMemberPointerTraits;
template <typename P, typename M>
struct GetMemberPointerTraits<M P::*> {
using Parent = P;
using Member = M;
};
template <auto MemberPtr>
using GetParentType = typename GetMemberPointerTraits<decltype(MemberPtr)>::Parent;
template <auto MemberPtr>
using GetMemberType = typename GetMemberPointerTraits<decltype(MemberPtr)>::Member;
template <auto MemberPtr, typename RealParentType = GetParentType<MemberPtr>>
static inline std::ptrdiff_t OffsetOf = [] {
using DeducedParentType = GetParentType<MemberPtr>;
using MemberType = GetMemberType<MemberPtr>;
static_assert(std::is_base_of<DeducedParentType, RealParentType>::value ||
std::is_same<RealParentType, DeducedParentType>::value);
return OffsetOfCalculator<RealParentType, MemberType>::OffsetOf(MemberPtr);
}();
} // namespace impl
template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
constexpr RealParentType& GetParentReference(impl::GetMemberType<MemberPtr>* member) {
std::ptrdiff_t Offset = impl::OffsetOf<MemberPtr, RealParentType>;
return *static_cast<RealParentType*>(
static_cast<void*>(static_cast<uint8_t*>(static_cast<void*>(member)) - Offset));
}
template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
constexpr RealParentType const& GetParentReference(impl::GetMemberType<MemberPtr> const* member) {
std::ptrdiff_t Offset = impl::OffsetOf<MemberPtr, RealParentType>;
return *static_cast<const RealParentType*>(static_cast<const void*>(
static_cast<const uint8_t*>(static_cast<const void*>(member)) - Offset));
}
template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
constexpr RealParentType* GetParentPointer(impl::GetMemberType<MemberPtr>* member) {
return std::addressof(GetParentReference<MemberPtr, RealParentType>(member));
}
template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
constexpr RealParentType const* GetParentPointer(impl::GetMemberType<MemberPtr> const* member) {
return std::addressof(GetParentReference<MemberPtr, RealParentType>(member));
}
template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
constexpr RealParentType& GetParentReference(impl::GetMemberType<MemberPtr>& member) {
return GetParentReference<MemberPtr, RealParentType>(std::addressof(member));
}
template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
constexpr RealParentType const& GetParentReference(impl::GetMemberType<MemberPtr> const& member) {
return GetParentReference<MemberPtr, RealParentType>(std::addressof(member));
}
template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
constexpr RealParentType* GetParentPointer(impl::GetMemberType<MemberPtr>& member) {
return std::addressof(GetParentReference<MemberPtr, RealParentType>(member));
}
template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
constexpr RealParentType const* GetParentPointer(impl::GetMemberType<MemberPtr> const& member) {
return std::addressof(GetParentReference<MemberPtr, RealParentType>(member));
}
} // namespace Common

View File

@@ -19,14 +19,15 @@ namespace Common {
/// SPSC ring buffer
/// @tparam T Element type
/// @tparam capacity Number of slots in ring buffer
template <typename T, std::size_t capacity>
/// @tparam granularity Slot size in terms of number of elements
template <typename T, std::size_t capacity, std::size_t granularity = 1>
class RingBuffer {
/// A "slot" is made of a single `T`.
static constexpr std::size_t slot_size = sizeof(T);
/// A "slot" is made of `granularity` elements of `T`.
static constexpr std::size_t slot_size = granularity * sizeof(T);
// T must be safely memcpy-able and have a trivial default constructor.
static_assert(std::is_trivial_v<T>);
// Ensure capacity is sensible.
static_assert(capacity < std::numeric_limits<std::size_t>::max() / 2);
static_assert(capacity < std::numeric_limits<std::size_t>::max() / 2 / granularity);
static_assert((capacity & (capacity - 1)) == 0, "capacity must be a power of two");
// Ensure lock-free.
static_assert(std::atomic_size_t::is_always_lock_free);
@@ -46,7 +47,7 @@ public:
const std::size_t second_copy = push_count - first_copy;
const char* in = static_cast<const char*>(new_slots);
std::memcpy(m_data.data() + pos, in, first_copy * slot_size);
std::memcpy(m_data.data() + pos * granularity, in, first_copy * slot_size);
in += first_copy * slot_size;
std::memcpy(m_data.data(), in, second_copy * slot_size);
@@ -73,7 +74,7 @@ public:
const std::size_t second_copy = pop_count - first_copy;
char* out = static_cast<char*>(output);
std::memcpy(out, m_data.data() + pos, first_copy * slot_size);
std::memcpy(out, m_data.data() + pos * granularity, first_copy * slot_size);
out += first_copy * slot_size;
std::memcpy(out, m_data.data(), second_copy * slot_size);
@@ -83,9 +84,9 @@ public:
}
std::vector<T> Pop(std::size_t max_slots = ~std::size_t(0)) {
std::vector<T> out(std::min(max_slots, capacity));
const std::size_t count = Pop(out.data(), out.size());
out.resize(count);
std::vector<T> out(std::min(max_slots, capacity) * granularity);
const std::size_t count = Pop(out.data(), out.size() / granularity);
out.resize(count * granularity);
return out;
}
@@ -112,7 +113,7 @@ private:
alignas(128) std::atomic_size_t m_write_index{0};
#endif
std::array<T, capacity> m_data;
std::array<T, granularity * capacity> m_data;
};
} // namespace Common

View File

@@ -49,9 +49,3 @@ ScopeExitHelper<Func> ScopeExit(Func&& func) {
* \endcode
*/
#define SCOPE_EXIT(body) auto CONCAT2(scope_exit_helper_, __LINE__) = detail::ScopeExit([&]() body)
/**
* This macro is similar to SCOPE_EXIT, except the object is caller managed. This is intended to be
* used when the caller might want to cancel the ScopeExit.
*/
#define SCOPE_GUARD(body) detail::ScopeExit([&]() body)

View File

@@ -141,13 +141,27 @@ std::string ReplaceAll(std::string result, const std::string& src, const std::st
}
std::string UTF16ToUTF8(const std::u16string& input) {
#ifdef _MSC_VER
// Workaround for missing char16_t/char32_t instantiations in MSVC2017
std::wstring_convert<std::codecvt_utf8_utf16<__int16>, __int16> convert;
std::basic_string<__int16> tmp_buffer(input.cbegin(), input.cend());
return convert.to_bytes(tmp_buffer);
#else
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
return convert.to_bytes(input);
#endif
}
std::u16string UTF8ToUTF16(const std::string& input) {
#ifdef _MSC_VER
// Workaround for missing char16_t/char32_t instantiations in MSVC2017
std::wstring_convert<std::codecvt_utf8_utf16<__int16>, __int16> convert;
auto tmp_buffer = convert.from_bytes(input);
return std::u16string(tmp_buffer.cbegin(), tmp_buffer.cend());
#else
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
return convert.from_bytes(input);
#endif
}
#ifdef _WIN32

159
src/common/timer.cpp Normal file
View File

@@ -0,0 +1,159 @@
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <ctime>
#include <fmt/format.h>
#include "common/common_types.h"
#include "common/string_util.h"
#include "common/timer.h"
namespace Common {
std::chrono::milliseconds Timer::GetTimeMs() {
return std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch());
}
// --------------------------------------------
// Initiate, Start, Stop, and Update the time
// --------------------------------------------
// Set initial values for the class
Timer::Timer() : m_LastTime(0), m_StartTime(0), m_Running(false) {
Update();
}
// Write the starting time
void Timer::Start() {
m_StartTime = GetTimeMs();
m_Running = true;
}
// Stop the timer
void Timer::Stop() {
// Write the final time
m_LastTime = GetTimeMs();
m_Running = false;
}
// Update the last time variable
void Timer::Update() {
m_LastTime = GetTimeMs();
// TODO(ector) - QPF
}
// -------------------------------------
// Get time difference and elapsed time
// -------------------------------------
// Get the number of milliseconds since the last Update()
std::chrono::milliseconds Timer::GetTimeDifference() {
return GetTimeMs() - m_LastTime;
}
// Add the time difference since the last Update() to the starting time.
// This is used to compensate for a paused game.
void Timer::AddTimeDifference() {
m_StartTime += GetTimeDifference();
}
// Get the time elapsed since the Start()
std::chrono::milliseconds Timer::GetTimeElapsed() {
// If we have not started yet, return 1 (because then I don't
// have to change the FPS calculation in CoreRerecording.cpp .
if (m_StartTime.count() == 0)
return std::chrono::milliseconds(1);
// Return the final timer time if the timer is stopped
if (!m_Running)
return (m_LastTime - m_StartTime);
return (GetTimeMs() - m_StartTime);
}
// Get the formatted time elapsed since the Start()
std::string Timer::GetTimeElapsedFormatted() const {
// If we have not started yet, return zero
if (m_StartTime.count() == 0)
return "00:00:00:000";
// The number of milliseconds since the start.
// Use a different value if the timer is stopped.
std::chrono::milliseconds Milliseconds;
if (m_Running)
Milliseconds = GetTimeMs() - m_StartTime;
else
Milliseconds = m_LastTime - m_StartTime;
// Seconds
std::chrono::seconds Seconds = std::chrono::duration_cast<std::chrono::seconds>(Milliseconds);
// Minutes
std::chrono::minutes Minutes = std::chrono::duration_cast<std::chrono::minutes>(Milliseconds);
// Hours
std::chrono::hours Hours = std::chrono::duration_cast<std::chrono::hours>(Milliseconds);
std::string TmpStr = fmt::format("{:02}:{:02}:{:02}:{:03}", Hours.count(), Minutes.count() % 60,
Seconds.count() % 60, Milliseconds.count() % 1000);
return TmpStr;
}
// Get the number of seconds since January 1 1970
std::chrono::seconds Timer::GetTimeSinceJan1970() {
return std::chrono::duration_cast<std::chrono::seconds>(GetTimeMs());
}
std::chrono::seconds Timer::GetLocalTimeSinceJan1970() {
time_t sysTime, tzDiff, tzDST;
struct tm* gmTime;
time(&sysTime);
// Account for DST where needed
gmTime = localtime(&sysTime);
if (gmTime->tm_isdst == 1)
tzDST = 3600;
else
tzDST = 0;
// Lazy way to get local time in sec
gmTime = gmtime(&sysTime);
tzDiff = sysTime - mktime(gmTime);
return std::chrono::seconds(sysTime + tzDiff + tzDST);
}
// Return the current time formatted as Minutes:Seconds:Milliseconds
// in the form 00:00:000.
std::string Timer::GetTimeFormatted() {
time_t sysTime;
struct tm* gmTime;
char tmp[13];
time(&sysTime);
gmTime = localtime(&sysTime);
strftime(tmp, 6, "%M:%S", gmTime);
u64 milliseconds = static_cast<u64>(GetTimeMs().count()) % 1000;
return fmt::format("{}:{:03}", tmp, milliseconds);
}
// Returns a timestamp with decimals for precise time comparisons
// ----------------
double Timer::GetDoubleTime() {
// Get continuous timestamp
auto tmp_seconds = static_cast<u64>(GetTimeSinceJan1970().count());
const auto ms = static_cast<double>(static_cast<u64>(GetTimeMs().count()) % 1000);
// Remove a few years. We only really want enough seconds to make
// sure that we are detecting actual actions, perhaps 60 seconds is
// enough really, but I leave a year of seconds anyway, in case the
// user's clock is incorrect or something like that.
tmp_seconds = tmp_seconds - (38 * 365 * 24 * 60 * 60);
// Make a smaller integer that fits in the double
const auto seconds = static_cast<u32>(tmp_seconds);
return seconds + ms;
}
} // Namespace Common

41
src/common/timer.h Normal file
View File

@@ -0,0 +1,41 @@
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <chrono>
#include <string>
#include "common/common_types.h"
namespace Common {
class Timer {
public:
Timer();
void Start();
void Stop();
void Update();
// The time difference is always returned in milliseconds, regardless of alternative internal
// representation
[[nodiscard]] std::chrono::milliseconds GetTimeDifference();
void AddTimeDifference();
[[nodiscard]] static std::chrono::seconds GetTimeSinceJan1970();
[[nodiscard]] static std::chrono::seconds GetLocalTimeSinceJan1970();
[[nodiscard]] static double GetDoubleTime();
[[nodiscard]] static std::string GetTimeFormatted();
[[nodiscard]] std::string GetTimeElapsedFormatted() const;
[[nodiscard]] std::chrono::milliseconds GetTimeElapsed();
[[nodiscard]] static std::chrono::milliseconds GetTimeMs();
private:
std::chrono::milliseconds m_LastTime;
std::chrono::milliseconds m_StartTime;
bool m_Running;
};
} // Namespace Common

View File

@@ -1,674 +0,0 @@
/* $NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $ */
/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */
/* $FreeBSD$ */
/*-
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
/*
* This file defines data structures for red-black trees.
*
* A red-black tree is a binary search tree with the node color as an
* extra attribute. It fulfills a set of conditions:
* - every search path from the root to a leaf consists of the
* same number of black nodes,
* - each red node (except for the root) has a black parent,
* - each leaf node is black.
*
* Every operation on a red-black tree is bounded as O(lg n).
* The maximum height of a red-black tree is 2lg (n+1).
*/
namespace Common {
template <typename T>
class RBHead {
public:
[[nodiscard]] T* Root() {
return rbh_root;
}
[[nodiscard]] const T* Root() const {
return rbh_root;
}
void SetRoot(T* root) {
rbh_root = root;
}
[[nodiscard]] bool IsEmpty() const {
return Root() == nullptr;
}
private:
T* rbh_root = nullptr;
};
enum class EntryColor {
Black,
Red,
};
template <typename T>
class RBEntry {
public:
[[nodiscard]] T* Left() {
return rbe_left;
}
[[nodiscard]] const T* Left() const {
return rbe_left;
}
void SetLeft(T* left) {
rbe_left = left;
}
[[nodiscard]] T* Right() {
return rbe_right;
}
[[nodiscard]] const T* Right() const {
return rbe_right;
}
void SetRight(T* right) {
rbe_right = right;
}
[[nodiscard]] T* Parent() {
return rbe_parent;
}
[[nodiscard]] const T* Parent() const {
return rbe_parent;
}
void SetParent(T* parent) {
rbe_parent = parent;
}
[[nodiscard]] bool IsBlack() const {
return rbe_color == EntryColor::Black;
}
[[nodiscard]] bool IsRed() const {
return rbe_color == EntryColor::Red;
}
[[nodiscard]] EntryColor Color() const {
return rbe_color;
}
void SetColor(EntryColor color) {
rbe_color = color;
}
private:
T* rbe_left = nullptr;
T* rbe_right = nullptr;
T* rbe_parent = nullptr;
EntryColor rbe_color{};
};
template <typename Node>
[[nodiscard]] RBEntry<Node>& RB_ENTRY(Node* node) {
return node->GetEntry();
}
template <typename Node>
[[nodiscard]] const RBEntry<Node>& RB_ENTRY(const Node* node) {
return node->GetEntry();
}
template <typename Node>
[[nodiscard]] Node* RB_PARENT(Node* node) {
return RB_ENTRY(node).Parent();
}
template <typename Node>
[[nodiscard]] const Node* RB_PARENT(const Node* node) {
return RB_ENTRY(node).Parent();
}
template <typename Node>
void RB_SET_PARENT(Node* node, Node* parent) {
return RB_ENTRY(node).SetParent(parent);
}
template <typename Node>
[[nodiscard]] Node* RB_LEFT(Node* node) {
return RB_ENTRY(node).Left();
}
template <typename Node>
[[nodiscard]] const Node* RB_LEFT(const Node* node) {
return RB_ENTRY(node).Left();
}
template <typename Node>
void RB_SET_LEFT(Node* node, Node* left) {
return RB_ENTRY(node).SetLeft(left);
}
template <typename Node>
[[nodiscard]] Node* RB_RIGHT(Node* node) {
return RB_ENTRY(node).Right();
}
template <typename Node>
[[nodiscard]] const Node* RB_RIGHT(const Node* node) {
return RB_ENTRY(node).Right();
}
template <typename Node>
void RB_SET_RIGHT(Node* node, Node* right) {
return RB_ENTRY(node).SetRight(right);
}
template <typename Node>
[[nodiscard]] bool RB_IS_BLACK(const Node* node) {
return RB_ENTRY(node).IsBlack();
}
template <typename Node>
[[nodiscard]] bool RB_IS_RED(const Node* node) {
return RB_ENTRY(node).IsRed();
}
template <typename Node>
[[nodiscard]] EntryColor RB_COLOR(const Node* node) {
return RB_ENTRY(node).Color();
}
template <typename Node>
void RB_SET_COLOR(Node* node, EntryColor color) {
return RB_ENTRY(node).SetColor(color);
}
template <typename Node>
void RB_SET(Node* node, Node* parent) {
auto& entry = RB_ENTRY(node);
entry.SetParent(parent);
entry.SetLeft(nullptr);
entry.SetRight(nullptr);
entry.SetColor(EntryColor::Red);
}
template <typename Node>
void RB_SET_BLACKRED(Node* black, Node* red) {
RB_SET_COLOR(black, EntryColor::Black);
RB_SET_COLOR(red, EntryColor::Red);
}
template <typename Node>
void RB_ROTATE_LEFT(RBHead<Node>* head, Node* elm, Node*& tmp) {
tmp = RB_RIGHT(elm);
RB_SET_RIGHT(elm, RB_LEFT(tmp));
if (RB_RIGHT(elm) != nullptr) {
RB_SET_PARENT(RB_LEFT(tmp), elm);
}
RB_SET_PARENT(tmp, RB_PARENT(elm));
if (RB_PARENT(tmp) != nullptr) {
if (elm == RB_LEFT(RB_PARENT(elm))) {
RB_SET_LEFT(RB_PARENT(elm), tmp);
} else {
RB_SET_RIGHT(RB_PARENT(elm), tmp);
}
} else {
head->SetRoot(tmp);
}
RB_SET_LEFT(tmp, elm);
RB_SET_PARENT(elm, tmp);
}
template <typename Node>
void RB_ROTATE_RIGHT(RBHead<Node>* head, Node* elm, Node*& tmp) {
tmp = RB_LEFT(elm);
RB_SET_LEFT(elm, RB_RIGHT(tmp));
if (RB_LEFT(elm) != nullptr) {
RB_SET_PARENT(RB_RIGHT(tmp), elm);
}
RB_SET_PARENT(tmp, RB_PARENT(elm));
if (RB_PARENT(tmp) != nullptr) {
if (elm == RB_LEFT(RB_PARENT(elm))) {
RB_SET_LEFT(RB_PARENT(elm), tmp);
} else {
RB_SET_RIGHT(RB_PARENT(elm), tmp);
}
} else {
head->SetRoot(tmp);
}
RB_SET_RIGHT(tmp, elm);
RB_SET_PARENT(elm, tmp);
}
template <typename Node>
void RB_INSERT_COLOR(RBHead<Node>* head, Node* elm) {
Node* parent = nullptr;
Node* tmp = nullptr;
while ((parent = RB_PARENT(elm)) != nullptr && RB_IS_RED(parent)) {
Node* gparent = RB_PARENT(parent);
if (parent == RB_LEFT(gparent)) {
tmp = RB_RIGHT(gparent);
if (tmp && RB_IS_RED(tmp)) {
RB_SET_COLOR(tmp, EntryColor::Black);
RB_SET_BLACKRED(parent, gparent);
elm = gparent;
continue;
}
if (RB_RIGHT(parent) == elm) {
RB_ROTATE_LEFT(head, parent, tmp);
tmp = parent;
parent = elm;
elm = tmp;
}
RB_SET_BLACKRED(parent, gparent);
RB_ROTATE_RIGHT(head, gparent, tmp);
} else {
tmp = RB_LEFT(gparent);
if (tmp && RB_IS_RED(tmp)) {
RB_SET_COLOR(tmp, EntryColor::Black);
RB_SET_BLACKRED(parent, gparent);
elm = gparent;
continue;
}
if (RB_LEFT(parent) == elm) {
RB_ROTATE_RIGHT(head, parent, tmp);
tmp = parent;
parent = elm;
elm = tmp;
}
RB_SET_BLACKRED(parent, gparent);
RB_ROTATE_LEFT(head, gparent, tmp);
}
}
RB_SET_COLOR(head->Root(), EntryColor::Black);
}
template <typename Node>
void RB_REMOVE_COLOR(RBHead<Node>* head, Node* parent, Node* elm) {
Node* tmp;
while ((elm == nullptr || RB_IS_BLACK(elm)) && elm != head->Root()) {
if (RB_LEFT(parent) == elm) {
tmp = RB_RIGHT(parent);
if (RB_IS_RED(tmp)) {
RB_SET_BLACKRED(tmp, parent);
RB_ROTATE_LEFT(head, parent, tmp);
tmp = RB_RIGHT(parent);
}
if ((RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) &&
(RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp)))) {
RB_SET_COLOR(tmp, EntryColor::Red);
elm = parent;
parent = RB_PARENT(elm);
} else {
if (RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp))) {
Node* oleft;
if ((oleft = RB_LEFT(tmp)) != nullptr) {
RB_SET_COLOR(oleft, EntryColor::Black);
}
RB_SET_COLOR(tmp, EntryColor::Red);
RB_ROTATE_RIGHT(head, tmp, oleft);
tmp = RB_RIGHT(parent);
}
RB_SET_COLOR(tmp, RB_COLOR(parent));
RB_SET_COLOR(parent, EntryColor::Black);
if (RB_RIGHT(tmp)) {
RB_SET_COLOR(RB_RIGHT(tmp), EntryColor::Black);
}
RB_ROTATE_LEFT(head, parent, tmp);
elm = head->Root();
break;
}
} else {
tmp = RB_LEFT(parent);
if (RB_IS_RED(tmp)) {
RB_SET_BLACKRED(tmp, parent);
RB_ROTATE_RIGHT(head, parent, tmp);
tmp = RB_LEFT(parent);
}
if ((RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) &&
(RB_RIGHT(tmp) == nullptr || RB_IS_BLACK(RB_RIGHT(tmp)))) {
RB_SET_COLOR(tmp, EntryColor::Red);
elm = parent;
parent = RB_PARENT(elm);
} else {
if (RB_LEFT(tmp) == nullptr || RB_IS_BLACK(RB_LEFT(tmp))) {
Node* oright;
if ((oright = RB_RIGHT(tmp)) != nullptr) {
RB_SET_COLOR(oright, EntryColor::Black);
}
RB_SET_COLOR(tmp, EntryColor::Red);
RB_ROTATE_LEFT(head, tmp, oright);
tmp = RB_LEFT(parent);
}
RB_SET_COLOR(tmp, RB_COLOR(parent));
RB_SET_COLOR(parent, EntryColor::Black);
if (RB_LEFT(tmp)) {
RB_SET_COLOR(RB_LEFT(tmp), EntryColor::Black);
}
RB_ROTATE_RIGHT(head, parent, tmp);
elm = head->Root();
break;
}
}
}
if (elm) {
RB_SET_COLOR(elm, EntryColor::Black);
}
}
template <typename Node>
Node* RB_REMOVE(RBHead<Node>* head, Node* elm) {
Node* child = nullptr;
Node* parent = nullptr;
Node* old = elm;
EntryColor color{};
const auto finalize = [&] {
if (color == EntryColor::Black) {
RB_REMOVE_COLOR(head, parent, child);
}
return old;
};
if (RB_LEFT(elm) == nullptr) {
child = RB_RIGHT(elm);
} else if (RB_RIGHT(elm) == nullptr) {
child = RB_LEFT(elm);
} else {
Node* left;
elm = RB_RIGHT(elm);
while ((left = RB_LEFT(elm)) != nullptr) {
elm = left;
}
child = RB_RIGHT(elm);
parent = RB_PARENT(elm);
color = RB_COLOR(elm);
if (child) {
RB_SET_PARENT(child, parent);
}
if (parent) {
if (RB_LEFT(parent) == elm) {
RB_SET_LEFT(parent, child);
} else {
RB_SET_RIGHT(parent, child);
}
} else {
head->SetRoot(child);
}
if (RB_PARENT(elm) == old) {
parent = elm;
}
elm->SetEntry(old->GetEntry());
if (RB_PARENT(old)) {
if (RB_LEFT(RB_PARENT(old)) == old) {
RB_SET_LEFT(RB_PARENT(old), elm);
} else {
RB_SET_RIGHT(RB_PARENT(old), elm);
}
} else {
head->SetRoot(elm);
}
RB_SET_PARENT(RB_LEFT(old), elm);
if (RB_RIGHT(old)) {
RB_SET_PARENT(RB_RIGHT(old), elm);
}
if (parent) {
left = parent;
}
return finalize();
}
parent = RB_PARENT(elm);
color = RB_COLOR(elm);
if (child) {
RB_SET_PARENT(child, parent);
}
if (parent) {
if (RB_LEFT(parent) == elm) {
RB_SET_LEFT(parent, child);
} else {
RB_SET_RIGHT(parent, child);
}
} else {
head->SetRoot(child);
}
return finalize();
}
// Inserts a node into the RB tree
template <typename Node, typename CompareFunction>
Node* RB_INSERT(RBHead<Node>* head, Node* elm, CompareFunction cmp) {
Node* parent = nullptr;
Node* tmp = head->Root();
int comp = 0;
while (tmp) {
parent = tmp;
comp = cmp(elm, parent);
if (comp < 0) {
tmp = RB_LEFT(tmp);
} else if (comp > 0) {
tmp = RB_RIGHT(tmp);
} else {
return tmp;
}
}
RB_SET(elm, parent);
if (parent != nullptr) {
if (comp < 0) {
RB_SET_LEFT(parent, elm);
} else {
RB_SET_RIGHT(parent, elm);
}
} else {
head->SetRoot(elm);
}
RB_INSERT_COLOR(head, elm);
return nullptr;
}
// Finds the node with the same key as elm
template <typename Node, typename CompareFunction>
Node* RB_FIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) {
Node* tmp = head->Root();
while (tmp) {
const int comp = cmp(elm, tmp);
if (comp < 0) {
tmp = RB_LEFT(tmp);
} else if (comp > 0) {
tmp = RB_RIGHT(tmp);
} else {
return tmp;
}
}
return nullptr;
}
// Finds the first node greater than or equal to the search key
template <typename Node, typename CompareFunction>
Node* RB_NFIND(RBHead<Node>* head, Node* elm, CompareFunction cmp) {
Node* tmp = head->Root();
Node* res = nullptr;
while (tmp) {
const int comp = cmp(elm, tmp);
if (comp < 0) {
res = tmp;
tmp = RB_LEFT(tmp);
} else if (comp > 0) {
tmp = RB_RIGHT(tmp);
} else {
return tmp;
}
}
return res;
}
// Finds the node with the same key as lelm
template <typename Node, typename CompareFunction>
Node* RB_FIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp) {
Node* tmp = head->Root();
while (tmp) {
const int comp = lcmp(lelm, tmp);
if (comp < 0) {
tmp = RB_LEFT(tmp);
} else if (comp > 0) {
tmp = RB_RIGHT(tmp);
} else {
return tmp;
}
}
return nullptr;
}
// Finds the first node greater than or equal to the search key
template <typename Node, typename CompareFunction>
Node* RB_NFIND_LIGHT(RBHead<Node>* head, const void* lelm, CompareFunction lcmp) {
Node* tmp = head->Root();
Node* res = nullptr;
while (tmp) {
const int comp = lcmp(lelm, tmp);
if (comp < 0) {
res = tmp;
tmp = RB_LEFT(tmp);
} else if (comp > 0) {
tmp = RB_RIGHT(tmp);
} else {
return tmp;
}
}
return res;
}
template <typename Node>
Node* RB_NEXT(Node* elm) {
if (RB_RIGHT(elm)) {
elm = RB_RIGHT(elm);
while (RB_LEFT(elm)) {
elm = RB_LEFT(elm);
}
} else {
if (RB_PARENT(elm) && (elm == RB_LEFT(RB_PARENT(elm)))) {
elm = RB_PARENT(elm);
} else {
while (RB_PARENT(elm) && (elm == RB_RIGHT(RB_PARENT(elm)))) {
elm = RB_PARENT(elm);
}
elm = RB_PARENT(elm);
}
}
return elm;
}
template <typename Node>
Node* RB_PREV(Node* elm) {
if (RB_LEFT(elm)) {
elm = RB_LEFT(elm);
while (RB_RIGHT(elm)) {
elm = RB_RIGHT(elm);
}
} else {
if (RB_PARENT(elm) && (elm == RB_RIGHT(RB_PARENT(elm)))) {
elm = RB_PARENT(elm);
} else {
while (RB_PARENT(elm) && (elm == RB_LEFT(RB_PARENT(elm)))) {
elm = RB_PARENT(elm);
}
elm = RB_PARENT(elm);
}
}
return elm;
}
template <typename Node>
Node* RB_MINMAX(RBHead<Node>* head, bool is_min) {
Node* tmp = head->Root();
Node* parent = nullptr;
while (tmp) {
parent = tmp;
if (is_min) {
tmp = RB_LEFT(tmp);
} else {
tmp = RB_RIGHT(tmp);
}
}
return parent;
}
template <typename Node>
Node* RB_MIN(RBHead<Node>* head) {
return RB_MINMAX(head, true);
}
template <typename Node>
Node* RB_MAX(RBHead<Node>* head) {
return RB_MINMAX(head, false);
}
} // namespace Common

View File

@@ -14,8 +14,8 @@ constexpr u128 INVALID_UUID{{0, 0}};
struct UUID {
// UUIDs which are 0 are considered invalid!
u128 uuid;
UUID() = default;
u128 uuid = INVALID_UUID;
constexpr UUID() = default;
constexpr explicit UUID(const u128& id) : uuid{id} {}
constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {}

View File

@@ -2,74 +2,19 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <chrono>
#include <limits>
#include <mutex>
#include <thread>
#ifdef _MSC_VER
#include <intrin.h>
#pragma intrinsic(__umulh)
#pragma intrinsic(_udiv128)
#else
#include <x86intrin.h>
#endif
#include "common/atomic_ops.h"
#include "common/uint128.h"
#include "common/x64/native_clock.h"
namespace {
[[nodiscard]] u64 GetFixedPoint64Factor(u64 numerator, u64 divisor) {
#ifdef __SIZEOF_INT128__
const auto base = static_cast<unsigned __int128>(numerator) << 64ULL;
return static_cast<u64>(base / divisor);
#elif defined(_M_X64) || defined(_M_ARM64)
std::array<u64, 2> r = {0, numerator};
u64 remainder;
#if _MSC_VER < 1923
return udiv128(r[1], r[0], divisor, &remainder);
#else
return _udiv128(r[1], r[0], divisor, &remainder);
#endif
#else
// This one is bit more inaccurate.
return MultiplyAndDivide64(std::numeric_limits<u64>::max(), numerator, divisor);
#endif
}
[[nodiscard]] u64 MultiplyHigh(u64 a, u64 b) {
#ifdef __SIZEOF_INT128__
return (static_cast<unsigned __int128>(a) * static_cast<unsigned __int128>(b)) >> 64;
#elif defined(_M_X64) || defined(_M_ARM64)
return __umulh(a, b); // MSVC
#else
// Generic fallback
const u64 a_lo = u32(a);
const u64 a_hi = a >> 32;
const u64 b_lo = u32(b);
const u64 b_hi = b >> 32;
const u64 a_x_b_hi = a_hi * b_hi;
const u64 a_x_b_mid = a_hi * b_lo;
const u64 b_x_a_mid = b_hi * a_lo;
const u64 a_x_b_lo = a_lo * b_lo;
const u64 carry_bit = (static_cast<u64>(static_cast<u32>(a_x_b_mid)) +
static_cast<u64>(static_cast<u32>(b_x_a_mid)) + (a_x_b_lo >> 32)) >>
32;
const u64 multhi = a_x_b_hi + (a_x_b_mid >> 32) + (b_x_a_mid >> 32) + carry_bit;
return multhi;
#endif
}
} // namespace
namespace Common {
u64 EstimateRDTSCFrequency() {
@@ -103,71 +48,54 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequen
: WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, true), rtsc_frequency{
rtsc_frequency_} {
_mm_mfence();
time_point.inner.last_measure = __rdtsc();
time_point.inner.accumulated_ticks = 0U;
ns_rtsc_factor = GetFixedPoint64Factor(1000000000, rtsc_frequency);
us_rtsc_factor = GetFixedPoint64Factor(1000000, rtsc_frequency);
ms_rtsc_factor = GetFixedPoint64Factor(1000, rtsc_frequency);
clock_rtsc_factor = GetFixedPoint64Factor(emulated_clock_frequency, rtsc_frequency);
cpu_rtsc_factor = GetFixedPoint64Factor(emulated_cpu_frequency, rtsc_frequency);
last_measure = __rdtsc();
accumulated_ticks = 0U;
}
u64 NativeClock::GetRTSC() {
TimePoint new_time_point{};
TimePoint current_time_point{};
do {
current_time_point.pack = time_point.pack;
_mm_mfence();
const u64 current_measure = __rdtsc();
u64 diff = current_measure - current_time_point.inner.last_measure;
diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0)
new_time_point.inner.last_measure = current_measure > current_time_point.inner.last_measure
? current_measure
: current_time_point.inner.last_measure;
new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff;
} while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
current_time_point.pack));
std::scoped_lock scope{rtsc_serialize};
_mm_mfence();
const u64 current_measure = __rdtsc();
u64 diff = current_measure - last_measure;
diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0)
if (current_measure > last_measure) {
last_measure = current_measure;
}
accumulated_ticks += diff;
/// The clock cannot be more precise than the guest timer, remove the lower bits
return new_time_point.inner.accumulated_ticks & inaccuracy_mask;
return accumulated_ticks & inaccuracy_mask;
}
void NativeClock::Pause(bool is_paused) {
if (!is_paused) {
TimePoint current_time_point{};
TimePoint new_time_point{};
do {
current_time_point.pack = time_point.pack;
new_time_point.pack = current_time_point.pack;
_mm_mfence();
new_time_point.inner.last_measure = __rdtsc();
} while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
current_time_point.pack));
_mm_mfence();
last_measure = __rdtsc();
}
}
std::chrono::nanoseconds NativeClock::GetTimeNS() {
const u64 rtsc_value = GetRTSC();
return std::chrono::nanoseconds{MultiplyHigh(rtsc_value, ns_rtsc_factor)};
return std::chrono::nanoseconds{MultiplyAndDivide64(rtsc_value, 1000000000, rtsc_frequency)};
}
std::chrono::microseconds NativeClock::GetTimeUS() {
const u64 rtsc_value = GetRTSC();
return std::chrono::microseconds{MultiplyHigh(rtsc_value, us_rtsc_factor)};
return std::chrono::microseconds{MultiplyAndDivide64(rtsc_value, 1000000, rtsc_frequency)};
}
std::chrono::milliseconds NativeClock::GetTimeMS() {
const u64 rtsc_value = GetRTSC();
return std::chrono::milliseconds{MultiplyHigh(rtsc_value, ms_rtsc_factor)};
return std::chrono::milliseconds{MultiplyAndDivide64(rtsc_value, 1000, rtsc_frequency)};
}
u64 NativeClock::GetClockCycles() {
const u64 rtsc_value = GetRTSC();
return MultiplyHigh(rtsc_value, clock_rtsc_factor);
return MultiplyAndDivide64(rtsc_value, emulated_clock_frequency, rtsc_frequency);
}
u64 NativeClock::GetCPUCycles() {
const u64 rtsc_value = GetRTSC();
return MultiplyHigh(rtsc_value, cpu_rtsc_factor);
return MultiplyAndDivide64(rtsc_value, emulated_cpu_frequency, rtsc_frequency);
}
} // namespace X64

View File

@@ -6,6 +6,7 @@
#include <optional>
#include "common/spin_lock.h"
#include "common/wall_clock.h"
namespace Common {
@@ -31,28 +32,14 @@ public:
private:
u64 GetRTSC();
union alignas(16) TimePoint {
TimePoint() : pack{} {}
u128 pack{};
struct Inner {
u64 last_measure{};
u64 accumulated_ticks{};
} inner;
};
/// value used to reduce the native clocks accuracy as some apss rely on
/// undefined behavior where the level of accuracy in the clock shouldn't
/// be higher.
static constexpr u64 inaccuracy_mask = ~(UINT64_C(0x400) - 1);
TimePoint time_point;
// factors
u64 clock_rtsc_factor{};
u64 cpu_rtsc_factor{};
u64 ns_rtsc_factor{};
u64 us_rtsc_factor{};
u64 ms_rtsc_factor{};
SpinLock rtsc_serialize{};
u64 last_measure{};
u64 accumulated_ticks{};
u64 rtsc_frequency;
};
} // namespace X64

View File

@@ -142,6 +142,8 @@ add_library(core STATIC
hardware_interrupt_manager.h
hle/ipc.h
hle/ipc_helpers.h
hle/kernel/address_arbiter.cpp
hle/kernel/address_arbiter.h
hle/kernel/client_port.cpp
hle/kernel/client_port.h
hle/kernel/client_session.cpp
@@ -155,33 +157,13 @@ add_library(core STATIC
hle/kernel/handle_table.h
hle/kernel/hle_ipc.cpp
hle/kernel/hle_ipc.h
hle/kernel/k_address_arbiter.cpp
hle/kernel/k_address_arbiter.h
hle/kernel/k_affinity_mask.h
hle/kernel/k_condition_variable.cpp
hle/kernel/k_condition_variable.h
hle/kernel/k_event.cpp
hle/kernel/k_event.h
hle/kernel/k_light_condition_variable.h
hle/kernel/k_light_lock.cpp
hle/kernel/k_light_lock.h
hle/kernel/k_priority_queue.h
hle/kernel/k_readable_event.cpp
hle/kernel/k_readable_event.h
hle/kernel/k_resource_limit.cpp
hle/kernel/k_resource_limit.h
hle/kernel/k_scheduler.cpp
hle/kernel/k_scheduler.h
hle/kernel/k_scheduler_lock.h
hle/kernel/k_scoped_lock.h
hle/kernel/k_scoped_scheduler_lock_and_sleep.h
hle/kernel/k_synchronization_object.cpp
hle/kernel/k_synchronization_object.h
hle/kernel/k_thread.cpp
hle/kernel/k_thread.h
hle/kernel/k_thread_queue.h
hle/kernel/k_writable_event.cpp
hle/kernel/k_writable_event.h
hle/kernel/kernel.cpp
hle/kernel/kernel.h
hle/kernel/memory/address_space_info.cpp
@@ -201,6 +183,8 @@ add_library(core STATIC
hle/kernel/memory/slab_heap.h
hle/kernel/memory/system_control.cpp
hle/kernel/memory/system_control.h
hle/kernel/mutex.cpp
hle/kernel/mutex.h
hle/kernel/object.cpp
hle/kernel/object.h
hle/kernel/physical_core.cpp
@@ -210,6 +194,10 @@ add_library(core STATIC
hle/kernel/process.h
hle/kernel/process_capability.cpp
hle/kernel/process_capability.h
hle/kernel/readable_event.cpp
hle/kernel/readable_event.h
hle/kernel/resource_limit.cpp
hle/kernel/resource_limit.h
hle/kernel/server_port.cpp
hle/kernel/server_port.h
hle/kernel/server_session.cpp
@@ -222,14 +210,20 @@ add_library(core STATIC
hle/kernel/shared_memory.h
hle/kernel/svc.cpp
hle/kernel/svc.h
hle/kernel/svc_common.h
hle/kernel/svc_results.h
hle/kernel/svc_types.h
hle/kernel/svc_wrap.h
hle/kernel/synchronization_object.cpp
hle/kernel/synchronization_object.h
hle/kernel/synchronization.cpp
hle/kernel/synchronization.h
hle/kernel/thread.cpp
hle/kernel/thread.h
hle/kernel/time_manager.cpp
hle/kernel/time_manager.h
hle/kernel/transfer_memory.cpp
hle/kernel/transfer_memory.h
hle/kernel/writable_event.cpp
hle/kernel/writable_event.h
hle/lock.cpp
hle/lock.h
hle/result.h
@@ -406,6 +400,8 @@ add_library(core STATIC
hle/service/ldr/ldr.h
hle/service/lm/lm.cpp
hle/service/lm/lm.h
hle/service/lm/manager.cpp
hle/service/lm/manager.h
hle/service/mig/mig.cpp
hle/service/mig/mig.h
hle/service/mii/manager.cpp
@@ -647,9 +643,10 @@ else()
-Werror=conversion
-Werror=ignored-qualifiers
-Werror=implicit-fallthrough
-Werror=reorder
-Werror=sign-compare
-Werror=unused-variable
$<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess>
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>

View File

@@ -26,10 +26,9 @@ using CPUInterrupts = std::array<CPUInterruptHandler, Core::Hardware::NUM_CPU_CO
/// Generic ARMv8 CPU interface
class ARM_Interface : NonCopyable {
public:
explicit ARM_Interface(System& system_, CPUInterrupts& interrupt_handlers_,
bool uses_wall_clock_)
: system{system_}, interrupt_handlers{interrupt_handlers_}, uses_wall_clock{
uses_wall_clock_} {}
explicit ARM_Interface(System& system_, CPUInterrupts& interrupt_handlers, bool uses_wall_clock)
: system{system_}, interrupt_handlers{interrupt_handlers}, uses_wall_clock{
uses_wall_clock} {}
virtual ~ARM_Interface() = default;
struct ThreadContext32 {

View File

@@ -71,9 +71,15 @@ public:
}
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
LOG_CRITICAL(Core_ARM,
"ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})",
exception, pc, MemoryReadCode(pc), parent.IsInThumbMode());
switch (exception) {
case Dynarmic::A32::Exception::UndefinedInstruction:
case Dynarmic::A32::Exception::UnpredictableInstruction:
break;
case Dynarmic::A32::Exception::Breakpoint:
break;
}
LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
static_cast<std::size_t>(exception), pc, MemoryReadCode(pc));
UNIMPLEMENTED();
}
@@ -256,9 +262,6 @@ void ARM_Dynarmic_32::ChangeProcessorID(std::size_t new_core_id) {
}
void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) {
if (!jit) {
return;
}
Dynarmic::A32::Context context;
jit->SaveContext(context);
ctx.cpu_registers = context.Regs();
@@ -268,9 +271,6 @@ void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) {
}
void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) {
if (!jit) {
return;
}
Dynarmic::A32::Context context;
context.Regs() = ctx.cpu_registers;
context.ExtRegs() = ctx.extension_registers;

View File

@@ -50,10 +50,6 @@ public:
u64 GetTPIDR_EL0() const override;
void ChangeProcessorID(std::size_t new_core_id) override;
bool IsInThumbMode() const {
return (GetPSTATE() & 0x20) != 0;
}
void SaveContext(ThreadContext32& ctx) override;
void SaveContext(ThreadContext64& ctx) override {}
void LoadContext(const ThreadContext32& ctx) override;

View File

@@ -294,9 +294,6 @@ void ARM_Dynarmic_64::ChangeProcessorID(std::size_t new_core_id) {
}
void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) {
if (!jit) {
return;
}
ctx.cpu_registers = jit->GetRegisters();
ctx.sp = jit->GetSP();
ctx.pc = jit->GetPC();
@@ -308,9 +305,6 @@ void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) {
}
void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) {
if (!jit) {
return;
}
jit->SetRegisters(ctx.cpu_registers);
jit->SetSP(ctx.sp);
jit->SetPC(ctx.pc);

View File

@@ -28,14 +28,15 @@
#include "core/hardware_interrupt_manager.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_core.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/service/am/applets/applets.h"
#include "core/hle/service/apm/controller.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/glue/manager.h"
#include "core/hle/service/lm/manager.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
#include "core/hle/service/time/time_manager.h"
@@ -292,6 +293,8 @@ struct System::Impl {
perf_stats->GetMeanFrametime());
}
lm_manager.Flush();
is_powered_on = false;
exit_lock = false;
@@ -395,6 +398,7 @@ struct System::Impl {
/// Service State
Service::Glue::ARPManager arp_manager;
Service::LM::Manager lm_manager{reporter};
Service::Time::TimeManager time_manager;
/// Service manager
@@ -716,6 +720,14 @@ const Service::APM::Controller& System::GetAPMController() const {
return impl->apm_controller;
}
Service::LM::Manager& System::GetLogManager() {
return impl->lm_manager;
}
const Service::LM::Manager& System::GetLogManager() const {
return impl->lm_manager;
}
Service::Time::TimeManager& System::GetTimeManager() {
return impl->time_manager;
}

View File

@@ -62,6 +62,10 @@ namespace Glue {
class ARPManager;
}
namespace LM {
class Manager;
} // namespace LM
namespace SM {
class ServiceManager;
} // namespace SM
@@ -347,6 +351,9 @@ public:
[[nodiscard]] Service::APM::Controller& GetAPMController();
[[nodiscard]] const Service::APM::Controller& GetAPMController() const;
[[nodiscard]] Service::LM::Manager& GetLogManager();
[[nodiscard]] const Service::LM::Manager& GetLogManager() const;
[[nodiscard]] Service::Time::TimeManager& GetTimeManager();
[[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const;

View File

@@ -49,7 +49,6 @@ void CoreTiming::ThreadEntry(CoreTiming& instance) {
Common::SetCurrentThreadPriority(Common::ThreadPriority::VeryHigh);
instance.on_thread_init();
instance.ThreadLoop();
MicroProfileOnThreadExit();
}
void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {

View File

@@ -11,9 +11,9 @@
#include "core/core_timing.h"
#include "core/cpu_manager.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_core.h"
#include "core/hle/kernel/thread.h"
#include "video_core/gpu.h"
namespace Core {
@@ -147,7 +147,7 @@ void CpuManager::MultiCoreRunSuspendThread() {
while (true) {
auto core = kernel.GetCurrentHostThreadID();
auto& scheduler = *kernel.CurrentScheduler();
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
Kernel::Thread* current_thread = scheduler.GetCurrentThread();
Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[core].host_context);
ASSERT(scheduler.ContextSwitchPending());
ASSERT(core == kernel.GetCurrentHostThreadID());
@@ -208,6 +208,7 @@ void CpuManager::SingleCoreRunGuestThread() {
void CpuManager::SingleCoreRunGuestLoop() {
auto& kernel = system.Kernel();
auto* thread = kernel.CurrentScheduler()->GetCurrentThread();
while (true) {
auto* physical_core = &kernel.CurrentPhysicalCore();
system.EnterDynarmicProfile();
@@ -216,9 +217,9 @@ void CpuManager::SingleCoreRunGuestLoop() {
physical_core = &kernel.CurrentPhysicalCore();
}
system.ExitDynarmicProfile();
kernel.SetIsPhantomModeForSingleCore(true);
thread->SetPhantomMode(true);
system.CoreTiming().Advance();
kernel.SetIsPhantomModeForSingleCore(false);
thread->SetPhantomMode(false);
physical_core->ArmInterface().ClearExclusiveState();
PreemptSingleCore();
auto& scheduler = kernel.Scheduler(current_core);
@@ -244,7 +245,7 @@ void CpuManager::SingleCoreRunSuspendThread() {
while (true) {
auto core = kernel.GetCurrentHostThreadID();
auto& scheduler = *kernel.CurrentScheduler();
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
Kernel::Thread* current_thread = scheduler.GetCurrentThread();
Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[0].host_context);
ASSERT(scheduler.ContextSwitchPending());
ASSERT(core == kernel.GetCurrentHostThreadID());
@@ -254,23 +255,22 @@ void CpuManager::SingleCoreRunSuspendThread() {
void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
{
auto& kernel = system.Kernel();
auto& scheduler = kernel.Scheduler(current_core);
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
auto& scheduler = system.Kernel().Scheduler(current_core);
Kernel::Thread* current_thread = scheduler.GetCurrentThread();
if (idle_count >= 4 || from_running_enviroment) {
if (!from_running_enviroment) {
system.CoreTiming().Idle();
idle_count = 0;
}
kernel.SetIsPhantomModeForSingleCore(true);
current_thread->SetPhantomMode(true);
system.CoreTiming().Advance();
kernel.SetIsPhantomModeForSingleCore(false);
current_thread->SetPhantomMode(false);
}
current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES);
system.CoreTiming().ResetTicks();
scheduler.Unload(scheduler.GetCurrentThread());
auto& next_scheduler = kernel.Scheduler(current_core);
auto& next_scheduler = system.Kernel().Scheduler(current_core);
Common::Fiber::YieldTo(current_thread->GetHostContext(), next_scheduler.ControlContext());
}
@@ -278,7 +278,8 @@ void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
{
auto& scheduler = system.Kernel().Scheduler(current_core);
scheduler.Reload(scheduler.GetCurrentThread());
if (!scheduler.IsIdle()) {
auto* currrent_thread2 = scheduler.GetCurrentThread();
if (!currrent_thread2->IsIdleThread()) {
idle_count = 0;
}
}

View File

@@ -568,11 +568,6 @@ KeyManager::KeyManager() {
// Initialize keys
const std::string hactool_keys_dir = Common::FS::GetHactoolConfigurationPath();
const std::string yuzu_keys_dir = Common::FS::GetUserPath(Common::FS::UserPath::KeysDir);
if (!Common::FS::Exists(yuzu_keys_dir)) {
Common::FS::CreateDir(yuzu_keys_dir);
}
if (Settings::values.use_dev_keys) {
dev_mode = true;
AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "dev.keys", false);

View File

@@ -43,17 +43,17 @@ static_assert(sizeof(IVFCLevel) == 0x18, "IVFCLevel has incorrect size.");
struct IVFCHeader {
u32_le magic;
u32_le magic_number;
INSERT_PADDING_BYTES_NOINIT(8);
INSERT_UNION_PADDING_BYTES(8);
std::array<IVFCLevel, 6> levels;
INSERT_PADDING_BYTES_NOINIT(64);
INSERT_UNION_PADDING_BYTES(64);
};
static_assert(sizeof(IVFCHeader) == 0xE0, "IVFCHeader has incorrect size.");
struct NCASectionHeaderBlock {
INSERT_PADDING_BYTES_NOINIT(3);
INSERT_UNION_PADDING_BYTES(3);
NCASectionFilesystemType filesystem_type;
NCASectionCryptoType crypto_type;
INSERT_PADDING_BYTES_NOINIT(3);
INSERT_UNION_PADDING_BYTES(3);
};
static_assert(sizeof(NCASectionHeaderBlock) == 0x8, "NCASectionHeaderBlock has incorrect size.");
@@ -61,7 +61,7 @@ struct NCASectionRaw {
NCASectionHeaderBlock header;
std::array<u8, 0x138> block_data;
std::array<u8, 0x8> section_ctr;
INSERT_PADDING_BYTES_NOINIT(0xB8);
INSERT_UNION_PADDING_BYTES(0xB8);
};
static_assert(sizeof(NCASectionRaw) == 0x200, "NCASectionRaw has incorrect size.");
@@ -69,19 +69,19 @@ struct PFS0Superblock {
NCASectionHeaderBlock header_block;
std::array<u8, 0x20> hash;
u32_le size;
INSERT_PADDING_BYTES_NOINIT(4);
INSERT_UNION_PADDING_BYTES(4);
u64_le hash_table_offset;
u64_le hash_table_size;
u64_le pfs0_header_offset;
u64_le pfs0_size;
INSERT_PADDING_BYTES_NOINIT(0x1B0);
INSERT_UNION_PADDING_BYTES(0x1B0);
};
static_assert(sizeof(PFS0Superblock) == 0x200, "PFS0Superblock has incorrect size.");
struct RomFSSuperblock {
NCASectionHeaderBlock header_block;
IVFCHeader ivfc;
INSERT_PADDING_BYTES_NOINIT(0x118);
INSERT_UNION_PADDING_BYTES(0x118);
};
static_assert(sizeof(RomFSSuperblock) == 0x200, "RomFSSuperblock has incorrect size.");
@@ -89,19 +89,19 @@ struct BKTRHeader {
u64_le offset;
u64_le size;
u32_le magic;
INSERT_PADDING_BYTES_NOINIT(0x4);
INSERT_UNION_PADDING_BYTES(0x4);
u32_le number_entries;
INSERT_PADDING_BYTES_NOINIT(0x4);
INSERT_UNION_PADDING_BYTES(0x4);
};
static_assert(sizeof(BKTRHeader) == 0x20, "BKTRHeader has incorrect size.");
struct BKTRSuperblock {
NCASectionHeaderBlock header_block;
IVFCHeader ivfc;
INSERT_PADDING_BYTES_NOINIT(0x18);
INSERT_UNION_PADDING_BYTES(0x18);
BKTRHeader relocation;
BKTRHeader subsection;
INSERT_PADDING_BYTES_NOINIT(0xC0);
INSERT_UNION_PADDING_BYTES(0xC0);
};
static_assert(sizeof(BKTRSuperblock) == 0x200, "BKTRSuperblock has incorrect size.");

View File

@@ -67,18 +67,18 @@ public:
virtual void Refresh() = 0;
virtual bool HasEntry(u64 title_id, ContentRecordType type) const = 0;
bool HasEntry(ContentProviderEntry entry) const;
virtual bool HasEntry(ContentProviderEntry entry) const;
virtual std::optional<u32> GetEntryVersion(u64 title_id) const = 0;
virtual VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const = 0;
VirtualFile GetEntryUnparsed(ContentProviderEntry entry) const;
virtual VirtualFile GetEntryUnparsed(ContentProviderEntry entry) const;
virtual VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const = 0;
VirtualFile GetEntryRaw(ContentProviderEntry entry) const;
virtual VirtualFile GetEntryRaw(ContentProviderEntry entry) const;
virtual std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const = 0;
std::unique_ptr<NCA> GetEntry(ContentProviderEntry entry) const;
virtual std::unique_ptr<NCA> GetEntry(ContentProviderEntry entry) const;
virtual std::vector<ContentProviderEntry> ListEntries() const;

View File

@@ -58,7 +58,7 @@ struct SaveDataAttribute {
SaveDataType type;
SaveDataRank rank;
u16 index;
INSERT_PADDING_BYTES_NOINIT(4);
INSERT_PADDING_BYTES(4);
u64 zero_1;
u64 zero_2;
u64 zero_3;
@@ -72,7 +72,7 @@ struct SaveDataExtraData {
u64 owner_id;
s64 timestamp;
SaveDataFlags flags;
INSERT_PADDING_BYTES_NOINIT(4);
INSERT_PADDING_BYTES(4);
s64 available_size;
s64 journal_size;
s64 commit_id;

View File

@@ -133,11 +133,8 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_
}
cache.erase(old_path);
if (file->Open(new_path, "r+b")) {
cache.insert_or_assign(new_path, std::move(file));
} else {
LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", new_path);
}
file->Open(new_path, "r+b");
cache.insert_or_assign(new_path, std::move(file));
} else {
UNREACHABLE();
return nullptr;
@@ -217,12 +214,9 @@ VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_,
}
auto file = cached.lock();
file->Open(file_new_path, "r+b");
cache.erase(file_old_path);
if (file->Open(file_new_path, "r+b")) {
cache.insert_or_assign(std::move(file_new_path), std::move(file));
} else {
LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", file_new_path);
}
cache.insert_or_assign(std::move(file_new_path), std::move(file));
}
return OpenDirectory(new_path, Mode::ReadWrite);

View File

@@ -21,18 +21,21 @@ public:
std::mutex mutex;
Input::TouchStatus status;
bool touch_pressed = false; ///< True if touchpad area is currently pressed, otherwise false
float touch_x = 0.0f; ///< Touchpad X-position
float touch_y = 0.0f; ///< Touchpad Y-position
private:
class Device : public Input::TouchDevice {
public:
explicit Device(std::weak_ptr<TouchState>&& touch_state) : touch_state(touch_state) {}
Input::TouchStatus GetStatus() const override {
std::tuple<float, float, bool> GetStatus() const override {
if (auto state = touch_state.lock()) {
std::lock_guard guard{state->mutex};
return state->status;
return std::make_tuple(state->touch_x, state->touch_y, state->touch_pressed);
}
return {};
return std::make_tuple(0.0f, 0.0f, false);
}
private:
@@ -76,44 +79,36 @@ std::tuple<unsigned, unsigned> EmuWindow::ClipToTouchScreen(unsigned new_x, unsi
return std::make_tuple(new_x, new_y);
}
void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id) {
if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) {
void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
return;
}
if (id >= touch_state->status.size()) {
return;
}
std::lock_guard guard{touch_state->mutex};
const float x =
touch_state->touch_x =
static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) /
static_cast<float>(framebuffer_layout.screen.right - framebuffer_layout.screen.left);
const float y =
touch_state->touch_y =
static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) /
static_cast<float>(framebuffer_layout.screen.bottom - framebuffer_layout.screen.top);
touch_state->status[id] = std::make_tuple(x, y, true);
touch_state->touch_pressed = true;
}
void EmuWindow::TouchReleased(std::size_t id) {
if (id >= touch_state->status.size()) {
return;
}
void EmuWindow::TouchReleased() {
std::lock_guard guard{touch_state->mutex};
touch_state->status[id] = std::make_tuple(0.0f, 0.0f, false);
touch_state->touch_pressed = false;
touch_state->touch_x = 0;
touch_state->touch_y = 0;
}
void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id) {
if (id >= touch_state->status.size()) {
return;
}
if (!std::get<2>(touch_state->status[id]))
void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
if (!touch_state->touch_pressed)
return;
if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
std::tie(framebuffer_x, framebuffer_y) = ClipToTouchScreen(framebuffer_x, framebuffer_y);
TouchPressed(framebuffer_x, framebuffer_y, id);
TouchPressed(framebuffer_x, framebuffer_y);
}
void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) {

View File

@@ -117,23 +117,18 @@ public:
* Signal that a touch pressed event has occurred (e.g. mouse click pressed)
* @param framebuffer_x Framebuffer x-coordinate that was pressed
* @param framebuffer_y Framebuffer y-coordinate that was pressed
* @param id Touch event ID
*/
void TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id);
void TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y);
/**
* Signal that a touch released event has occurred (e.g. mouse click released)
* @param id Touch event ID
*/
void TouchReleased(std::size_t id);
/// Signal that a touch released event has occurred (e.g. mouse click released)
void TouchReleased();
/**
* Signal that a touch movement event has occurred (e.g. mouse was moved over the emu window)
* @param framebuffer_x Framebuffer x-coordinate
* @param framebuffer_y Framebuffer y-coordinate
* @param id Touch event ID
*/
void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id);
void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y);
/**
* Returns currently active configuration.

View File

@@ -21,11 +21,6 @@ enum class AnalogDirection : u8 {
UP,
DOWN,
};
struct AnalogProperties {
float deadzone;
float range;
float threshold;
};
/// An abstract class template for an input device (a button, an analog input, etc.).
template <typename StatusType>
@@ -35,12 +30,6 @@ public:
virtual StatusType GetStatus() const {
return {};
}
virtual StatusType GetRawStatus() const {
return GetStatus();
}
virtual AnalogProperties GetAnalogProperties() const {
return {};
}
virtual bool GetAnalogDirectionStatus([[maybe_unused]] AnalogDirection direction) const {
return {};
}
@@ -174,11 +163,10 @@ using MotionStatus = std::tuple<Common::Vec3<float>, Common::Vec3<float>, Common
using MotionDevice = InputDevice<MotionStatus>;
/**
* A touch status is an object that returns an array of 16 tuple elements of two floats and a bool.
* The floats are x and y coordinates in the range 0.0 - 1.0, and the bool indicates whether it is
* pressed.
* A touch status is an object that returns a tuple of two floats and a bool. The floats are
* x and y coordinates in the range 0.0 - 1.0, and the bool indicates whether it is pressed.
*/
using TouchStatus = std::array<std::tuple<float, float, bool>, 16>;
using TouchStatus = std::tuple<float, float, bool>;
/**
* A touch device is an input device that returns a touch status object

View File

@@ -25,10 +25,6 @@ void InputInterpreter::PollInput() {
button_states[current_index] = button_state;
}
bool InputInterpreter::IsButtonPressed(HIDButton button) const {
return (button_states[current_index] & (1U << static_cast<u8>(button))) != 0;
}
bool InputInterpreter::IsButtonPressedOnce(HIDButton button) const {
const bool current_press =
(button_states[current_index] & (1U << static_cast<u8>(button))) != 0;

View File

@@ -66,27 +66,6 @@ public:
/// Gets a button state from HID and inserts it into the array of button states.
void PollInput();
/**
* Checks whether the button is pressed.
*
* @param button The button to check.
*
* @returns True when the button is pressed.
*/
[[nodiscard]] bool IsButtonPressed(HIDButton button) const;
/**
* Checks whether any of the buttons in the parameter list is pressed.
*
* @tparam HIDButton The buttons to check.
*
* @returns True when at least one of the buttons is pressed.
*/
template <HIDButton... T>
[[nodiscard]] bool IsAnyButtonPressed() {
return (IsButtonPressed(T) || ...);
}
/**
* The specified button is considered to be pressed once
* if it is currently pressed and not pressed previously.
@@ -100,12 +79,12 @@ public:
/**
* Checks whether any of the buttons in the parameter list is pressed once.
*
* @tparam T The buttons to check.
* @tparam HIDButton The buttons to check.
*
* @returns True when at least one of the buttons is pressed once.
*/
template <HIDButton... T>
[[nodiscard]] bool IsAnyButtonPressedOnce() const {
[[nodiscard]] bool IsAnyButtonPressedOnce() {
return (IsButtonPressedOnce(T) || ...);
}
@@ -121,12 +100,12 @@ public:
/**
* Checks whether any of the buttons in the parameter list is held down.
*
* @tparam T The buttons to check.
* @tparam HIDButton The buttons to check.
*
* @returns True when at least one of the buttons is held down.
*/
template <HIDButton... T>
[[nodiscard]] bool IsAnyButtonHeld() const {
[[nodiscard]] bool IsAnyButtonHeld() {
return (IsButtonHeld(T) || ...);
}

View File

@@ -4,10 +4,8 @@
#pragma once
#include <array>
#include <tuple>
#include "common/bit_util.h"
#include "common/common_types.h"
namespace Core {
@@ -20,12 +18,34 @@ constexpr u64 BASE_CLOCK_RATE = 1019215872; // Switch cpu frequency is 1020MHz u
constexpr u64 CNTFREQ = 19200000; // Switch's hardware clock speed
constexpr u32 NUM_CPU_CORES = 4; // Number of CPU Cores
// Virtual to Physical core map.
constexpr std::array<s32, Common::BitSize<u64>()> VirtualToPhysicalCoreMap{
0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
};
} // namespace Hardware
constexpr u32 INVALID_HOST_THREAD_ID = 0xFFFFFFFF;
struct EmuThreadHandle {
u32 host_handle;
u32 guest_handle;
u64 GetRaw() const {
return (static_cast<u64>(host_handle) << 32) | guest_handle;
}
bool operator==(const EmuThreadHandle& rhs) const {
return std::tie(host_handle, guest_handle) == std::tie(rhs.host_handle, rhs.guest_handle);
}
bool operator!=(const EmuThreadHandle& rhs) const {
return !operator==(rhs);
}
static constexpr EmuThreadHandle InvalidHandle() {
constexpr u32 invalid_handle = 0xFFFFFFFF;
return {invalid_handle, invalid_handle};
}
bool IsInvalid() const {
return (*this) == InvalidHandle();
}
};
} // namespace Core

View File

@@ -146,7 +146,7 @@ static_assert(sizeof(BufferDescriptorC) == 8, "BufferDescriptorC size is incorre
struct DataPayloadHeader {
u32_le magic;
INSERT_PADDING_WORDS_NOINIT(1);
INSERT_PADDING_WORDS(1);
};
static_assert(sizeof(DataPayloadHeader) == 8, "DataPayloadHeader size is incorrect");
@@ -160,7 +160,7 @@ struct DomainMessageHeader {
// Used when responding to an IPC request, Server -> Client.
struct {
u32_le num_objects;
INSERT_PADDING_WORDS_NOINIT(3);
INSERT_UNION_PADDING_WORDS(3);
};
// Used when performing an IPC request, Client -> Server.
@@ -171,10 +171,10 @@ struct DomainMessageHeader {
BitField<16, 16, u32> size;
};
u32_le object_id;
INSERT_PADDING_WORDS_NOINIT(2);
INSERT_UNION_PADDING_WORDS(2);
};
std::array<u32, 4> raw;
std::array<u32, 4> raw{};
};
};
static_assert(sizeof(DomainMessageHeader) == 16, "DomainMessageHeader size is incorrect");

View File

@@ -0,0 +1,317 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <vector>
#include "common/assert.h"
#include "common/common_types.h"
#include "core/arm/exclusive_monitor.h"
#include "core/core.h"
#include "core/hle/kernel/address_arbiter.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/time_manager.h"
#include "core/hle/result.h"
#include "core/memory.h"
namespace Kernel {
// Wake up num_to_wake (or all) threads in a vector.
void AddressArbiter::WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads,
s32 num_to_wake) {
// Only process up to 'target' threads, unless 'target' is <= 0, in which case process
// them all.
std::size_t last = waiting_threads.size();
if (num_to_wake > 0) {
last = std::min(last, static_cast<std::size_t>(num_to_wake));
}
// Signal the waiting threads.
for (std::size_t i = 0; i < last; i++) {
waiting_threads[i]->SetSynchronizationResults(nullptr, RESULT_SUCCESS);
RemoveThread(waiting_threads[i]);
waiting_threads[i]->WaitForArbitration(false);
waiting_threads[i]->ResumeFromWait();
}
}
AddressArbiter::AddressArbiter(Core::System& system) : system{system} {}
AddressArbiter::~AddressArbiter() = default;
ResultCode AddressArbiter::SignalToAddress(VAddr address, SignalType type, s32 value,
s32 num_to_wake) {
switch (type) {
case SignalType::Signal:
return SignalToAddressOnly(address, num_to_wake);
case SignalType::IncrementAndSignalIfEqual:
return IncrementAndSignalToAddressIfEqual(address, value, num_to_wake);
case SignalType::ModifyByWaitingCountAndSignalIfEqual:
return ModifyByWaitingCountAndSignalToAddressIfEqual(address, value, num_to_wake);
default:
return ERR_INVALID_ENUM_VALUE;
}
}
ResultCode AddressArbiter::SignalToAddressOnly(VAddr address, s32 num_to_wake) {
KScopedSchedulerLock lock(system.Kernel());
const std::vector<std::shared_ptr<Thread>> waiting_threads =
GetThreadsWaitingOnAddress(address);
WakeThreads(waiting_threads, num_to_wake);
return RESULT_SUCCESS;
}
ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 value,
s32 num_to_wake) {
KScopedSchedulerLock lock(system.Kernel());
auto& memory = system.Memory();
// Ensure that we can write to the address.
if (!memory.IsValidVirtualAddress(address)) {
return ERR_INVALID_ADDRESS_STATE;
}
const std::size_t current_core = system.CurrentCoreIndex();
auto& monitor = system.Monitor();
u32 current_value;
do {
current_value = monitor.ExclusiveRead32(current_core, address);
if (current_value != static_cast<u32>(value)) {
return ERR_INVALID_STATE;
}
current_value++;
} while (!monitor.ExclusiveWrite32(current_core, address, current_value));
return SignalToAddressOnly(address, num_to_wake);
}
ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value,
s32 num_to_wake) {
KScopedSchedulerLock lock(system.Kernel());
auto& memory = system.Memory();
// Ensure that we can write to the address.
if (!memory.IsValidVirtualAddress(address)) {
return ERR_INVALID_ADDRESS_STATE;
}
// Get threads waiting on the address.
const std::vector<std::shared_ptr<Thread>> waiting_threads =
GetThreadsWaitingOnAddress(address);
const std::size_t current_core = system.CurrentCoreIndex();
auto& monitor = system.Monitor();
s32 updated_value;
do {
updated_value = monitor.ExclusiveRead32(current_core, address);
if (updated_value != value) {
return ERR_INVALID_STATE;
}
// Determine the modified value depending on the waiting count.
if (num_to_wake <= 0) {
if (waiting_threads.empty()) {
updated_value = value + 1;
} else {
updated_value = value - 1;
}
} else {
if (waiting_threads.empty()) {
updated_value = value + 1;
} else if (waiting_threads.size() <= static_cast<u32>(num_to_wake)) {
updated_value = value - 1;
} else {
updated_value = value;
}
}
} while (!monitor.ExclusiveWrite32(current_core, address, updated_value));
WakeThreads(waiting_threads, num_to_wake);
return RESULT_SUCCESS;
}
ResultCode AddressArbiter::WaitForAddress(VAddr address, ArbitrationType type, s32 value,
s64 timeout_ns) {
switch (type) {
case ArbitrationType::WaitIfLessThan:
return WaitForAddressIfLessThan(address, value, timeout_ns, false);
case ArbitrationType::DecrementAndWaitIfLessThan:
return WaitForAddressIfLessThan(address, value, timeout_ns, true);
case ArbitrationType::WaitIfEqual:
return WaitForAddressIfEqual(address, value, timeout_ns);
default:
return ERR_INVALID_ENUM_VALUE;
}
}
ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout,
bool should_decrement) {
auto& memory = system.Memory();
auto& kernel = system.Kernel();
Thread* current_thread = kernel.CurrentScheduler()->GetCurrentThread();
Handle event_handle = InvalidHandle;
{
KScopedSchedulerLockAndSleep lock(kernel, event_handle, current_thread, timeout);
if (current_thread->IsPendingTermination()) {
lock.CancelSleep();
return ERR_THREAD_TERMINATING;
}
// Ensure that we can read the address.
if (!memory.IsValidVirtualAddress(address)) {
lock.CancelSleep();
return ERR_INVALID_ADDRESS_STATE;
}
s32 current_value = static_cast<s32>(memory.Read32(address));
if (current_value >= value) {
lock.CancelSleep();
return ERR_INVALID_STATE;
}
current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT);
s32 decrement_value;
const std::size_t current_core = system.CurrentCoreIndex();
auto& monitor = system.Monitor();
do {
current_value = static_cast<s32>(monitor.ExclusiveRead32(current_core, address));
if (should_decrement) {
decrement_value = current_value - 1;
} else {
decrement_value = current_value;
}
} while (
!monitor.ExclusiveWrite32(current_core, address, static_cast<u32>(decrement_value)));
// Short-circuit without rescheduling, if timeout is zero.
if (timeout == 0) {
lock.CancelSleep();
return RESULT_TIMEOUT;
}
current_thread->SetArbiterWaitAddress(address);
InsertThread(SharedFrom(current_thread));
current_thread->SetStatus(ThreadStatus::WaitArb);
current_thread->WaitForArbitration(true);
}
if (event_handle != InvalidHandle) {
auto& time_manager = kernel.TimeManager();
time_manager.UnscheduleTimeEvent(event_handle);
}
{
KScopedSchedulerLock lock(kernel);
if (current_thread->IsWaitingForArbitration()) {
RemoveThread(SharedFrom(current_thread));
current_thread->WaitForArbitration(false);
}
}
return current_thread->GetSignalingResult();
}
ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) {
auto& memory = system.Memory();
auto& kernel = system.Kernel();
Thread* current_thread = kernel.CurrentScheduler()->GetCurrentThread();
Handle event_handle = InvalidHandle;
{
KScopedSchedulerLockAndSleep lock(kernel, event_handle, current_thread, timeout);
if (current_thread->IsPendingTermination()) {
lock.CancelSleep();
return ERR_THREAD_TERMINATING;
}
// Ensure that we can read the address.
if (!memory.IsValidVirtualAddress(address)) {
lock.CancelSleep();
return ERR_INVALID_ADDRESS_STATE;
}
s32 current_value = static_cast<s32>(memory.Read32(address));
if (current_value != value) {
lock.CancelSleep();
return ERR_INVALID_STATE;
}
// Short-circuit without rescheduling, if timeout is zero.
if (timeout == 0) {
lock.CancelSleep();
return RESULT_TIMEOUT;
}
current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT);
current_thread->SetArbiterWaitAddress(address);
InsertThread(SharedFrom(current_thread));
current_thread->SetStatus(ThreadStatus::WaitArb);
current_thread->WaitForArbitration(true);
}
if (event_handle != InvalidHandle) {
auto& time_manager = kernel.TimeManager();
time_manager.UnscheduleTimeEvent(event_handle);
}
{
KScopedSchedulerLock lock(kernel);
if (current_thread->IsWaitingForArbitration()) {
RemoveThread(SharedFrom(current_thread));
current_thread->WaitForArbitration(false);
}
}
return current_thread->GetSignalingResult();
}
void AddressArbiter::InsertThread(std::shared_ptr<Thread> thread) {
const VAddr arb_addr = thread->GetArbiterWaitAddress();
std::list<std::shared_ptr<Thread>>& thread_list = arb_threads[arb_addr];
const auto iter =
std::find_if(thread_list.cbegin(), thread_list.cend(), [&thread](const auto& entry) {
return entry->GetPriority() >= thread->GetPriority();
});
if (iter == thread_list.cend()) {
thread_list.push_back(std::move(thread));
} else {
thread_list.insert(iter, std::move(thread));
}
}
void AddressArbiter::RemoveThread(std::shared_ptr<Thread> thread) {
const VAddr arb_addr = thread->GetArbiterWaitAddress();
std::list<std::shared_ptr<Thread>>& thread_list = arb_threads[arb_addr];
const auto iter = std::find_if(thread_list.cbegin(), thread_list.cend(),
[&thread](const auto& entry) { return thread == entry; });
if (iter != thread_list.cend()) {
thread_list.erase(iter);
}
}
std::vector<std::shared_ptr<Thread>> AddressArbiter::GetThreadsWaitingOnAddress(
VAddr address) const {
const auto iter = arb_threads.find(address);
if (iter == arb_threads.cend()) {
return {};
}
const std::list<std::shared_ptr<Thread>>& thread_list = iter->second;
return {thread_list.cbegin(), thread_list.cend()};
}
} // namespace Kernel

View File

@@ -0,0 +1,91 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <list>
#include <memory>
#include <unordered_map>
#include <vector>
#include "common/common_types.h"
union ResultCode;
namespace Core {
class System;
}
namespace Kernel {
class Thread;
class AddressArbiter {
public:
enum class ArbitrationType {
WaitIfLessThan = 0,
DecrementAndWaitIfLessThan = 1,
WaitIfEqual = 2,
};
enum class SignalType {
Signal = 0,
IncrementAndSignalIfEqual = 1,
ModifyByWaitingCountAndSignalIfEqual = 2,
};
explicit AddressArbiter(Core::System& system);
~AddressArbiter();
AddressArbiter(const AddressArbiter&) = delete;
AddressArbiter& operator=(const AddressArbiter&) = delete;
AddressArbiter(AddressArbiter&&) = default;
AddressArbiter& operator=(AddressArbiter&&) = delete;
/// Signals an address being waited on with a particular signaling type.
ResultCode SignalToAddress(VAddr address, SignalType type, s32 value, s32 num_to_wake);
/// Waits on an address with a particular arbitration type.
ResultCode WaitForAddress(VAddr address, ArbitrationType type, s32 value, s64 timeout_ns);
private:
/// Signals an address being waited on.
ResultCode SignalToAddressOnly(VAddr address, s32 num_to_wake);
/// Signals an address being waited on and increments its value if equal to the value argument.
ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake);
/// Signals an address being waited on and modifies its value based on waiting thread count if
/// equal to the value argument.
ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value,
s32 num_to_wake);
/// Waits on an address if the value passed is less than the argument value,
/// optionally decrementing.
ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout,
bool should_decrement);
/// Waits on an address if the value passed is equal to the argument value.
ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout);
/// Wake up num_to_wake (or all) threads in a vector.
void WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads, s32 num_to_wake);
/// Insert a thread into the address arbiter container
void InsertThread(std::shared_ptr<Thread> thread);
/// Removes a thread from the address arbiter container
void RemoveThread(std::shared_ptr<Thread> thread);
// Gets the threads waiting on an address.
std::vector<std::shared_ptr<Thread>> GetThreadsWaitingOnAddress(VAddr address) const;
/// List of threads waiting for a address arbiter
std::unordered_map<VAddr, std::list<std::shared_ptr<Thread>>> arb_threads;
Core::System& system;
};
} // namespace Kernel

View File

@@ -33,6 +33,9 @@ ResultVal<std::shared_ptr<ClientSession>> ClientPort::Connect() {
server_port->AppendPendingSession(std::move(server));
}
// Wake the threads waiting on the ServerPort
server_port->Signal();
return MakeResult(std::move(client));
}

View File

@@ -51,8 +51,6 @@ public:
*/
void ConnectionClosed();
void Finalize() override {}
private:
std::shared_ptr<ServerPort> server_port; ///< ServerPort associated with this client port.
u32 max_sessions = 0; ///< Maximum number of simultaneous sessions the port can have

View File

@@ -5,14 +5,14 @@
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/session.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/result.h"
namespace Kernel {
ClientSession::ClientSession(KernelCore& kernel) : KSynchronizationObject{kernel} {}
ClientSession::ClientSession(KernelCore& kernel) : SynchronizationObject{kernel} {}
ClientSession::~ClientSession() {
// This destructor will be called automatically when the last ClientSession handle is closed by
@@ -22,6 +22,15 @@ ClientSession::~ClientSession() {
}
}
bool ClientSession::ShouldWait(const Thread* thread) const {
UNIMPLEMENTED();
return {};
}
void ClientSession::Acquire(Thread* thread) {
UNIMPLEMENTED();
}
bool ClientSession::IsSignaled() const {
UNIMPLEMENTED();
return true;
@@ -38,7 +47,7 @@ ResultVal<std::shared_ptr<ClientSession>> ClientSession::Create(KernelCore& kern
return MakeResult(std::move(client_session));
}
ResultCode ClientSession::SendSyncRequest(std::shared_ptr<KThread> thread,
ResultCode ClientSession::SendSyncRequest(std::shared_ptr<Thread> thread,
Core::Memory::Memory& memory,
Core::Timing::CoreTiming& core_timing) {
// Keep ServerSession alive until we're done working with it.

View File

@@ -7,7 +7,7 @@
#include <memory>
#include <string>
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/synchronization_object.h"
#include "core/hle/result.h"
union ResultCode;
@@ -24,9 +24,9 @@ namespace Kernel {
class KernelCore;
class Session;
class KThread;
class Thread;
class ClientSession final : public KSynchronizationObject {
class ClientSession final : public SynchronizationObject {
public:
explicit ClientSession(KernelCore& kernel);
~ClientSession() override;
@@ -46,12 +46,14 @@ public:
return HANDLE_TYPE;
}
ResultCode SendSyncRequest(std::shared_ptr<KThread> thread, Core::Memory::Memory& memory,
ResultCode SendSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory,
Core::Timing::CoreTiming& core_timing);
bool IsSignaled() const override;
bool ShouldWait(const Thread* thread) const override;
void Finalize() override {}
void Acquire(Thread* thread) override;
bool IsSignaled() const override;
private:
static ResultVal<std::shared_ptr<ClientSession>> Create(KernelCore& kernel,

View File

@@ -13,14 +13,12 @@ namespace Kernel {
constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7};
constexpr ResultCode ERR_INVALID_CAPABILITY_DESCRIPTOR{ErrorModule::Kernel, 14};
constexpr ResultCode ERR_THREAD_TERMINATING{ErrorModule::Kernel, 59};
constexpr ResultCode ERR_TERMINATION_REQUESTED{ErrorModule::Kernel, 59};
constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101};
constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102};
constexpr ResultCode ERR_OUT_OF_RESOURCES{ErrorModule::Kernel, 103};
constexpr ResultCode ERR_OUT_OF_MEMORY{ErrorModule::Kernel, 104};
constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105};
constexpr ResultCode ERR_INVALID_ADDRESS_STATE{ErrorModule::Kernel, 106};
constexpr ResultCode ERR_INVALID_CURRENT_MEMORY{ErrorModule::Kernel, 106};
constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS{ErrorModule::Kernel, 108};
constexpr ResultCode ERR_INVALID_MEMORY_RANGE{ErrorModule::Kernel, 110};
constexpr ResultCode ERR_INVALID_PROCESSOR_ID{ErrorModule::Kernel, 113};
@@ -30,7 +28,6 @@ constexpr ResultCode ERR_INVALID_POINTER{ErrorModule::Kernel, 115};
constexpr ResultCode ERR_INVALID_COMBINATION{ErrorModule::Kernel, 116};
constexpr ResultCode RESULT_TIMEOUT{ErrorModule::Kernel, 117};
constexpr ResultCode ERR_SYNCHRONIZATION_CANCELED{ErrorModule::Kernel, 118};
constexpr ResultCode ERR_CANCELLED{ErrorModule::Kernel, 118};
constexpr ResultCode ERR_OUT_OF_RANGE{ErrorModule::Kernel, 119};
constexpr ResultCode ERR_INVALID_ENUM_VALUE{ErrorModule::Kernel, 120};
constexpr ResultCode ERR_NOT_FOUND{ErrorModule::Kernel, 121};

View File

@@ -17,12 +17,12 @@ GlobalSchedulerContext::GlobalSchedulerContext(KernelCore& kernel)
GlobalSchedulerContext::~GlobalSchedulerContext() = default;
void GlobalSchedulerContext::AddThread(std::shared_ptr<KThread> thread) {
void GlobalSchedulerContext::AddThread(std::shared_ptr<Thread> thread) {
std::scoped_lock lock{global_list_guard};
thread_list.push_back(std::move(thread));
}
void GlobalSchedulerContext::RemoveThread(std::shared_ptr<KThread> thread) {
void GlobalSchedulerContext::RemoveThread(std::shared_ptr<Thread> thread) {
std::scoped_lock lock{global_list_guard};
thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
thread_list.end());

View File

@@ -12,8 +12,7 @@
#include "core/hardware_properties.h"
#include "core/hle/kernel/k_priority_queue.h"
#include "core/hle/kernel/k_scheduler_lock.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/svc_types.h"
#include "core/hle/kernel/thread.h"
namespace Kernel {
@@ -21,12 +20,8 @@ class KernelCore;
class SchedulerLock;
using KSchedulerPriorityQueue =
KPriorityQueue<KThread, Core::Hardware::NUM_CPU_CORES, Svc::LowestThreadPriority,
Svc::HighestThreadPriority>;
static constexpr s32 HighestCoreMigrationAllowedPriority = 2;
static_assert(Svc::LowestThreadPriority >= HighestCoreMigrationAllowedPriority);
static_assert(Svc::HighestThreadPriority <= HighestCoreMigrationAllowedPriority);
KPriorityQueue<Thread, Core::Hardware::NUM_CPU_CORES, THREADPRIO_LOWEST, THREADPRIO_HIGHEST>;
constexpr s32 HighestCoreMigrationAllowedPriority = 2;
class GlobalSchedulerContext final {
friend class KScheduler;
@@ -38,13 +33,13 @@ public:
~GlobalSchedulerContext();
/// Adds a new thread to the scheduler
void AddThread(std::shared_ptr<KThread> thread);
void AddThread(std::shared_ptr<Thread> thread);
/// Removes a thread from the scheduler
void RemoveThread(std::shared_ptr<KThread> thread);
void RemoveThread(std::shared_ptr<Thread> thread);
/// Returns a list of all threads managed by the scheduler
[[nodiscard]] const std::vector<std::shared_ptr<KThread>>& GetThreadList() const {
[[nodiscard]] const std::vector<std::shared_ptr<Thread>>& GetThreadList() const {
return thread_list;
}
@@ -79,7 +74,7 @@ private:
LockType scheduler_lock;
/// Lists all thread ids that aren't deleted/etc.
std::vector<std::shared_ptr<KThread>> thread_list;
std::vector<std::shared_ptr<Thread>> thread_list;
Common::SpinLock global_list_guard{};
};

View File

@@ -9,9 +9,9 @@
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/thread.h"
namespace Kernel {
namespace {
@@ -89,10 +89,6 @@ ResultCode HandleTable::Close(Handle handle) {
const u16 slot = GetSlot(handle);
if (objects[slot].use_count() == 1) {
objects[slot]->Finalize();
}
objects[slot] = nullptr;
generations[slot] = next_free_slot;

View File

@@ -17,16 +17,16 @@
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_writable_event.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/time_manager.h"
#include "core/hle/kernel/writable_event.h"
#include "core/memory.h"
namespace Kernel {
@@ -48,7 +48,7 @@ void SessionRequestHandler::ClientDisconnected(
HLERequestContext::HLERequestContext(KernelCore& kernel, Core::Memory::Memory& memory,
std::shared_ptr<ServerSession> server_session,
std::shared_ptr<KThread> thread)
std::shared_ptr<Thread> thread)
: server_session(std::move(server_session)),
thread(std::move(thread)), kernel{kernel}, memory{memory} {
cmd_buf[0] = 0;
@@ -182,7 +182,7 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const HandleTabl
return RESULT_SUCCESS;
}
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(KThread& thread) {
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
auto& owner_process = *thread.GetOwnerProcess();
auto& handle_table = owner_process.GetHandleTable();
@@ -338,28 +338,6 @@ std::size_t HLERequestContext::GetWriteBufferSize(std::size_t buffer_index) cons
return 0;
}
bool HLERequestContext::CanReadBuffer(std::size_t buffer_index) const {
const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
BufferDescriptorA()[buffer_index].Size()};
if (is_buffer_a) {
return BufferDescriptorA().size() > buffer_index;
} else {
return BufferDescriptorX().size() > buffer_index;
}
}
bool HLERequestContext::CanWriteBuffer(std::size_t buffer_index) const {
const bool is_buffer_b{BufferDescriptorB().size() > buffer_index &&
BufferDescriptorB()[buffer_index].Size()};
if (is_buffer_b) {
return BufferDescriptorB().size() > buffer_index;
} else {
return BufferDescriptorC().size() > buffer_index;
}
}
std::string HLERequestContext::Description() const {
if (!command_header) {
return "No command header available";

View File

@@ -40,9 +40,9 @@ class HLERequestContext;
class KernelCore;
class Process;
class ServerSession;
class KThread;
class KReadableEvent;
class KWritableEvent;
class Thread;
class ReadableEvent;
class WritableEvent;
enum class ThreadWakeupReason;
@@ -110,7 +110,7 @@ class HLERequestContext {
public:
explicit HLERequestContext(KernelCore& kernel, Core::Memory::Memory& memory,
std::shared_ptr<ServerSession> session,
std::shared_ptr<KThread> thread);
std::shared_ptr<Thread> thread);
~HLERequestContext();
/// Returns a pointer to the IPC command buffer for this request.
@@ -126,12 +126,15 @@ public:
return server_session;
}
using WakeupCallback = std::function<void(
std::shared_ptr<Thread> thread, HLERequestContext& context, ThreadWakeupReason reason)>;
/// Populates this context with data from the requesting process/thread.
ResultCode PopulateFromIncomingCommandBuffer(const HandleTable& handle_table,
u32_le* src_cmdbuf);
/// Writes data from this context back to the requesting process/thread.
ResultCode WriteToOutgoingCommandBuffer(KThread& thread);
ResultCode WriteToOutgoingCommandBuffer(Thread& thread);
u32_le GetCommand() const {
return command;
@@ -204,12 +207,6 @@ public:
/// Helper function to get the size of the output buffer
std::size_t GetWriteBufferSize(std::size_t buffer_index = 0) const;
/// Helper function to test whether the input buffer at buffer_index can be read
bool CanReadBuffer(std::size_t buffer_index = 0) const;
/// Helper function to test whether the output buffer at buffer_index can be written
bool CanWriteBuffer(std::size_t buffer_index = 0) const;
template <typename T>
std::shared_ptr<T> GetCopyObject(std::size_t index) {
return DynamicObjectCast<T>(copy_objects.at(index));
@@ -264,11 +261,11 @@ public:
std::string Description() const;
KThread& GetThread() {
Thread& GetThread() {
return *thread;
}
const KThread& GetThread() const {
const Thread& GetThread() const {
return *thread;
}
@@ -283,7 +280,7 @@ private:
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
std::shared_ptr<Kernel::ServerSession> server_session;
std::shared_ptr<KThread> thread;
std::shared_ptr<Thread> thread;
// TODO(yuriks): Check common usage of this and optimize size accordingly
boost::container::small_vector<std::shared_ptr<Object>, 8> move_objects;
boost::container::small_vector<std::shared_ptr<Object>, 8> copy_objects;

View File

@@ -1,341 +0,0 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/arm/exclusive_monitor.h"
#include "core/core.h"
#include "core/hle/kernel/k_address_arbiter.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/time_manager.h"
#include "core/memory.h"
namespace Kernel {
KAddressArbiter::KAddressArbiter(Core::System& system_)
: system{system_}, kernel{system.Kernel()} {}
KAddressArbiter::~KAddressArbiter() = default;
namespace {
bool ReadFromUser(Core::System& system, s32* out, VAddr address) {
*out = system.Memory().Read32(address);
return true;
}
bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 value) {
auto& monitor = system.Monitor();
const auto current_core = system.CurrentCoreIndex();
// TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
// TODO(bunnei): We should call CanAccessAtomic(..) here.
// Load the value from the address.
const s32 current_value = static_cast<s32>(monitor.ExclusiveRead32(current_core, address));
// Compare it to the desired one.
if (current_value < value) {
// If less than, we want to try to decrement.
const s32 decrement_value = current_value - 1;
// Decrement and try to store.
if (!monitor.ExclusiveWrite32(current_core, address, static_cast<u32>(decrement_value))) {
// If we failed to store, try again.
DecrementIfLessThan(system, out, address, value);
}
} else {
// Otherwise, clear our exclusive hold and finish
monitor.ClearExclusive();
}
// We're done.
*out = current_value;
return true;
}
bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32 new_value) {
auto& monitor = system.Monitor();
const auto current_core = system.CurrentCoreIndex();
// TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
// TODO(bunnei): We should call CanAccessAtomic(..) here.
// Load the value from the address.
const s32 current_value = static_cast<s32>(monitor.ExclusiveRead32(current_core, address));
// Compare it to the desired one.
if (current_value == value) {
// If equal, we want to try to write the new value.
// Try to store.
if (!monitor.ExclusiveWrite32(current_core, address, static_cast<u32>(new_value))) {
// If we failed to store, try again.
UpdateIfEqual(system, out, address, value, new_value);
}
} else {
// Otherwise, clear our exclusive hold and finish.
monitor.ClearExclusive();
}
// We're done.
*out = current_value;
return true;
}
} // namespace
ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) {
// Perform signaling.
s32 num_waiters{};
{
KScopedSchedulerLock sl(kernel);
auto it = thread_tree.nfind_light({addr, -1});
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) {
KThread* target_thread = std::addressof(*it);
target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
ASSERT(target_thread->IsWaitingForAddressArbiter());
target_thread->Wakeup();
it = thread_tree.erase(it);
target_thread->ClearAddressArbiter();
++num_waiters;
}
}
return RESULT_SUCCESS;
}
ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 count) {
// Perform signaling.
s32 num_waiters{};
{
KScopedSchedulerLock sl(kernel);
// Check the userspace value.
s32 user_value{};
if (!UpdateIfEqual(system, &user_value, addr, value, value + 1)) {
LOG_ERROR(Kernel, "Invalid current memory!");
return Svc::ResultInvalidCurrentMemory;
}
if (user_value != value) {
return Svc::ResultInvalidState;
}
auto it = thread_tree.nfind_light({addr, -1});
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) {
KThread* target_thread = std::addressof(*it);
target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
ASSERT(target_thread->IsWaitingForAddressArbiter());
target_thread->Wakeup();
it = thread_tree.erase(it);
target_thread->ClearAddressArbiter();
++num_waiters;
}
}
return RESULT_SUCCESS;
}
ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 value, s32 count) {
// Perform signaling.
s32 num_waiters{};
{
[[maybe_unused]] const KScopedSchedulerLock sl(kernel);
auto it = thread_tree.nfind_light({addr, -1});
// Determine the updated value.
s32 new_value{};
if (count <= 0) {
if (it != thread_tree.end() && it->GetAddressArbiterKey() == addr) {
new_value = value - 2;
} else {
new_value = value + 1;
}
} else {
if (it != thread_tree.end() && it->GetAddressArbiterKey() == addr) {
auto tmp_it = it;
s32 tmp_num_waiters{};
while (++tmp_it != thread_tree.end() && tmp_it->GetAddressArbiterKey() == addr) {
if (tmp_num_waiters++ >= count) {
break;
}
}
if (tmp_num_waiters < count) {
new_value = value - 1;
} else {
new_value = value;
}
} else {
new_value = value + 1;
}
}
// Check the userspace value.
s32 user_value{};
bool succeeded{};
if (value != new_value) {
succeeded = UpdateIfEqual(system, &user_value, addr, value, new_value);
} else {
succeeded = ReadFromUser(system, &user_value, addr);
}
if (!succeeded) {
LOG_ERROR(Kernel, "Invalid current memory!");
return Svc::ResultInvalidCurrentMemory;
}
if (user_value != value) {
return Svc::ResultInvalidState;
}
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) {
KThread* target_thread = std::addressof(*it);
target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
ASSERT(target_thread->IsWaitingForAddressArbiter());
target_thread->Wakeup();
it = thread_tree.erase(it);
target_thread->ClearAddressArbiter();
++num_waiters;
}
}
return RESULT_SUCCESS;
}
ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) {
// Prepare to wait.
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
{
KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
// Check that the thread isn't terminating.
if (cur_thread->IsTerminationRequested()) {
slp.CancelSleep();
return Svc::ResultTerminationRequested;
}
// Set the synced object.
cur_thread->SetSyncedObject(nullptr, Svc::ResultTimedOut);
// Read the value from userspace.
s32 user_value{};
bool succeeded{};
if (decrement) {
succeeded = DecrementIfLessThan(system, &user_value, addr, value);
} else {
succeeded = ReadFromUser(system, &user_value, addr);
}
if (!succeeded) {
slp.CancelSleep();
return Svc::ResultInvalidCurrentMemory;
}
// Check that the value is less than the specified one.
if (user_value >= value) {
slp.CancelSleep();
return Svc::ResultInvalidState;
}
// Check that the timeout is non-zero.
if (timeout == 0) {
slp.CancelSleep();
return Svc::ResultTimedOut;
}
// Set the arbiter.
cur_thread->SetAddressArbiter(&thread_tree, addr);
thread_tree.insert(*cur_thread);
cur_thread->SetState(ThreadState::Waiting);
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
}
// Cancel the timer wait.
kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
// Remove from the address arbiter.
{
KScopedSchedulerLock sl(kernel);
if (cur_thread->IsWaitingForAddressArbiter()) {
thread_tree.erase(thread_tree.iterator_to(*cur_thread));
cur_thread->ClearAddressArbiter();
}
}
// Get the result.
KSynchronizationObject* dummy{};
return cur_thread->GetWaitResult(&dummy);
}
ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
// Prepare to wait.
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
{
KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
// Check that the thread isn't terminating.
if (cur_thread->IsTerminationRequested()) {
slp.CancelSleep();
return Svc::ResultTerminationRequested;
}
// Set the synced object.
cur_thread->SetSyncedObject(nullptr, Svc::ResultTimedOut);
// Read the value from userspace.
s32 user_value{};
if (!ReadFromUser(system, &user_value, addr)) {
slp.CancelSleep();
return Svc::ResultInvalidCurrentMemory;
}
// Check that the value is equal.
if (value != user_value) {
slp.CancelSleep();
return Svc::ResultInvalidState;
}
// Check that the timeout is non-zero.
if (timeout == 0) {
slp.CancelSleep();
return Svc::ResultTimedOut;
}
// Set the arbiter.
cur_thread->SetAddressArbiter(&thread_tree, addr);
thread_tree.insert(*cur_thread);
cur_thread->SetState(ThreadState::Waiting);
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
}
// Cancel the timer wait.
kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
// Remove from the address arbiter.
{
KScopedSchedulerLock sl(kernel);
if (cur_thread->IsWaitingForAddressArbiter()) {
thread_tree.erase(thread_tree.iterator_to(*cur_thread));
cur_thread->ClearAddressArbiter();
}
}
// Get the result.
KSynchronizationObject* dummy{};
return cur_thread->GetWaitResult(&dummy);
}
} // namespace Kernel

View File

@@ -1,70 +0,0 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/assert.h"
#include "common/common_types.h"
#include "core/hle/kernel/k_condition_variable.h"
#include "core/hle/kernel/svc_types.h"
union ResultCode;
namespace Core {
class System;
}
namespace Kernel {
class KernelCore;
class KAddressArbiter {
public:
using ThreadTree = KConditionVariable::ThreadTree;
explicit KAddressArbiter(Core::System& system_);
~KAddressArbiter();
[[nodiscard]] ResultCode SignalToAddress(VAddr addr, Svc::SignalType type, s32 value,
s32 count) {
switch (type) {
case Svc::SignalType::Signal:
return Signal(addr, count);
case Svc::SignalType::SignalAndIncrementIfEqual:
return SignalAndIncrementIfEqual(addr, value, count);
case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual:
return SignalAndModifyByWaitingCountIfEqual(addr, value, count);
}
UNREACHABLE();
return RESULT_UNKNOWN;
}
[[nodiscard]] ResultCode WaitForAddress(VAddr addr, Svc::ArbitrationType type, s32 value,
s64 timeout) {
switch (type) {
case Svc::ArbitrationType::WaitIfLessThan:
return WaitIfLessThan(addr, value, false, timeout);
case Svc::ArbitrationType::DecrementAndWaitIfLessThan:
return WaitIfLessThan(addr, value, true, timeout);
case Svc::ArbitrationType::WaitIfEqual:
return WaitIfEqual(addr, value, timeout);
}
UNREACHABLE();
return RESULT_UNKNOWN;
}
private:
[[nodiscard]] ResultCode Signal(VAddr addr, s32 count);
[[nodiscard]] ResultCode SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 count);
[[nodiscard]] ResultCode SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 value, s32 count);
[[nodiscard]] ResultCode WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout);
[[nodiscard]] ResultCode WaitIfEqual(VAddr addr, s32 value, s64 timeout);
ThreadTree thread_tree;
Core::System& system;
KernelCore& kernel;
};
} // namespace Kernel

View File

@@ -27,7 +27,7 @@ public:
}
[[nodiscard]] constexpr bool GetAffinity(s32 core) const {
return (this->mask & GetCoreBit(core)) != 0;
return this->mask & GetCoreBit(core);
}
constexpr void SetAffinity(s32 core, bool set) {

View File

@@ -1,345 +0,0 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <vector>
#include "core/arm/exclusive_monitor.h"
#include "core/core.h"
#include "core/hle/kernel/k_condition_variable.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/svc_common.h"
#include "core/hle/kernel/svc_results.h"
#include "core/memory.h"
namespace Kernel {
namespace {
bool ReadFromUser(Core::System& system, u32* out, VAddr address) {
*out = system.Memory().Read32(address);
return true;
}
bool WriteToUser(Core::System& system, VAddr address, const u32* p) {
system.Memory().Write32(address, *p);
return true;
}
bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero,
u32 new_orr_mask) {
auto& monitor = system.Monitor();
const auto current_core = system.CurrentCoreIndex();
// Load the value from the address.
const auto expected = monitor.ExclusiveRead32(current_core, address);
// Orr in the new mask.
u32 value = expected | new_orr_mask;
// If the value is zero, use the if_zero value, otherwise use the newly orr'd value.
if (!expected) {
value = if_zero;
}
// Try to store.
if (!monitor.ExclusiveWrite32(current_core, address, value)) {
// If we failed to store, try again.
return UpdateLockAtomic(system, out, address, if_zero, new_orr_mask);
}
// We're done.
*out = expected;
return true;
}
} // namespace
KConditionVariable::KConditionVariable(Core::System& system_)
: system{system_}, kernel{system.Kernel()} {}
KConditionVariable::~KConditionVariable() = default;
ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
KThread* owner_thread = kernel.CurrentScheduler()->GetCurrentThread();
// Signal the address.
{
KScopedSchedulerLock sl(kernel);
// Remove waiter thread.
s32 num_waiters{};
KThread* next_owner_thread =
owner_thread->RemoveWaiterByKey(std::addressof(num_waiters), addr);
// Determine the next tag.
u32 next_value{};
if (next_owner_thread) {
next_value = next_owner_thread->GetAddressKeyValue();
if (num_waiters > 1) {
next_value |= Svc::HandleWaitMask;
}
next_owner_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
next_owner_thread->Wakeup();
}
// Write the value to userspace.
if (!WriteToUser(system, addr, std::addressof(next_value))) {
if (next_owner_thread) {
next_owner_thread->SetSyncedObject(nullptr, Svc::ResultInvalidCurrentMemory);
}
return Svc::ResultInvalidCurrentMemory;
}
}
return RESULT_SUCCESS;
}
ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) {
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
// Wait for the address.
{
std::shared_ptr<KThread> owner_thread;
ASSERT(!owner_thread);
{
KScopedSchedulerLock sl(kernel);
cur_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
// Check if the thread should terminate.
R_UNLESS(!cur_thread->IsTerminationRequested(), Svc::ResultTerminationRequested);
{
// Read the tag from userspace.
u32 test_tag{};
R_UNLESS(ReadFromUser(system, std::addressof(test_tag), addr),
Svc::ResultInvalidCurrentMemory);
// If the tag isn't the handle (with wait mask), we're done.
R_UNLESS(test_tag == (handle | Svc::HandleWaitMask), RESULT_SUCCESS);
// Get the lock owner thread.
owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<KThread>(handle);
R_UNLESS(owner_thread, Svc::ResultInvalidHandle);
// Update the lock.
cur_thread->SetAddressKey(addr, value);
owner_thread->AddWaiter(cur_thread);
cur_thread->SetState(ThreadState::Waiting);
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
cur_thread->SetMutexWaitAddressForDebugging(addr);
}
}
ASSERT(owner_thread);
}
// Remove the thread as a waiter from the lock owner.
{
KScopedSchedulerLock sl(kernel);
KThread* owner_thread = cur_thread->GetLockOwner();
if (owner_thread != nullptr) {
owner_thread->RemoveWaiter(cur_thread);
}
}
// Get the wait result.
KSynchronizationObject* dummy{};
return cur_thread->GetWaitResult(std::addressof(dummy));
}
KThread* KConditionVariable::SignalImpl(KThread* thread) {
// Check pre-conditions.
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// Update the tag.
VAddr address = thread->GetAddressKey();
u32 own_tag = thread->GetAddressKeyValue();
u32 prev_tag{};
bool can_access{};
{
// TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
// TODO(bunnei): We should call CanAccessAtomic(..) here.
can_access = true;
if (can_access) {
UpdateLockAtomic(system, std::addressof(prev_tag), address, own_tag,
Svc::HandleWaitMask);
}
}
KThread* thread_to_close = nullptr;
if (can_access) {
if (prev_tag == InvalidHandle) {
// If nobody held the lock previously, we're all good.
thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
thread->Wakeup();
} else {
// Get the previous owner.
auto owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<KThread>(
prev_tag & ~Svc::HandleWaitMask);
if (owner_thread) {
// Add the thread as a waiter on the owner.
owner_thread->AddWaiter(thread);
thread_to_close = owner_thread.get();
} else {
// The lock was tagged with a thread that doesn't exist.
thread->SetSyncedObject(nullptr, Svc::ResultInvalidState);
thread->Wakeup();
}
}
} else {
// If the address wasn't accessible, note so.
thread->SetSyncedObject(nullptr, Svc::ResultInvalidCurrentMemory);
thread->Wakeup();
}
return thread_to_close;
}
void KConditionVariable::Signal(u64 cv_key, s32 count) {
// Prepare for signaling.
constexpr int MaxThreads = 16;
// TODO(bunnei): This should just be Thread once we implement KAutoObject instead of using
// std::shared_ptr.
std::vector<std::shared_ptr<KThread>> thread_list;
std::array<KThread*, MaxThreads> thread_array;
s32 num_to_close{};
// Perform signaling.
s32 num_waiters{};
{
KScopedSchedulerLock sl(kernel);
auto it = thread_tree.nfind_light({cv_key, -1});
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetConditionVariableKey() == cv_key)) {
KThread* target_thread = std::addressof(*it);
if (KThread* thread = SignalImpl(target_thread); thread != nullptr) {
if (num_to_close < MaxThreads) {
thread_array[num_to_close++] = thread;
} else {
thread_list.push_back(SharedFrom(thread));
}
}
it = thread_tree.erase(it);
target_thread->ClearConditionVariable();
++num_waiters;
}
// If we have no waiters, clear the has waiter flag.
if (it == thread_tree.end() || it->GetConditionVariableKey() != cv_key) {
const u32 has_waiter_flag{};
WriteToUser(system, cv_key, std::addressof(has_waiter_flag));
}
}
// Close threads in the array.
for (auto i = 0; i < num_to_close; ++i) {
thread_array[i]->Close();
}
// Close threads in the list.
for (auto it = thread_list.begin(); it != thread_list.end(); it = thread_list.erase(it)) {
(*it)->Close();
}
}
ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) {
// Prepare to wait.
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
{
KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
// Set the synced object.
cur_thread->SetSyncedObject(nullptr, Svc::ResultTimedOut);
// Check that the thread isn't terminating.
if (cur_thread->IsTerminationRequested()) {
slp.CancelSleep();
return Svc::ResultTerminationRequested;
}
// Update the value and process for the next owner.
{
// Remove waiter thread.
s32 num_waiters{};
KThread* next_owner_thread =
cur_thread->RemoveWaiterByKey(std::addressof(num_waiters), addr);
// Update for the next owner thread.
u32 next_value{};
if (next_owner_thread != nullptr) {
// Get the next tag value.
next_value = next_owner_thread->GetAddressKeyValue();
if (num_waiters > 1) {
next_value |= Svc::HandleWaitMask;
}
// Wake up the next owner.
next_owner_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
next_owner_thread->Wakeup();
}
// Write to the cv key.
{
const u32 has_waiter_flag = 1;
WriteToUser(system, key, std::addressof(has_waiter_flag));
// TODO(bunnei): We should call DataMemoryBarrier(..) here.
}
// Write the value to userspace.
if (!WriteToUser(system, addr, std::addressof(next_value))) {
slp.CancelSleep();
return Svc::ResultInvalidCurrentMemory;
}
}
// Update condition variable tracking.
{
cur_thread->SetConditionVariable(std::addressof(thread_tree), addr, key, value);
thread_tree.insert(*cur_thread);
}
// If the timeout is non-zero, set the thread as waiting.
if (timeout != 0) {
cur_thread->SetState(ThreadState::Waiting);
cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::ConditionVar);
cur_thread->SetMutexWaitAddressForDebugging(addr);
}
}
// Cancel the timer wait.
kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
// Remove from the condition variable.
{
KScopedSchedulerLock sl(kernel);
if (KThread* owner = cur_thread->GetLockOwner(); owner != nullptr) {
owner->RemoveWaiter(cur_thread);
}
if (cur_thread->IsWaitingForConditionVariable()) {
thread_tree.erase(thread_tree.iterator_to(*cur_thread));
cur_thread->ClearConditionVariable();
}
}
// Get the result.
KSynchronizationObject* dummy{};
return cur_thread->GetWaitResult(std::addressof(dummy));
}
} // namespace Kernel

View File

@@ -1,59 +0,0 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/assert.h"
#include "common/common_types.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/result.h"
namespace Core {
class System;
}
namespace Kernel {
class KConditionVariable {
public:
using ThreadTree = typename KThread::ConditionVariableThreadTreeType;
explicit KConditionVariable(Core::System& system_);
~KConditionVariable();
// Arbitration
[[nodiscard]] ResultCode SignalToAddress(VAddr addr);
[[nodiscard]] ResultCode WaitForAddress(Handle handle, VAddr addr, u32 value);
// Condition variable
void Signal(u64 cv_key, s32 count);
[[nodiscard]] ResultCode Wait(VAddr addr, u64 key, u32 value, s64 timeout);
private:
[[nodiscard]] KThread* SignalImpl(KThread* thread);
ThreadTree thread_tree;
Core::System& system;
KernelCore& kernel;
};
inline void BeforeUpdatePriority(const KernelCore& kernel, KConditionVariable::ThreadTree* tree,
KThread* thread) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
tree->erase(tree->iterator_to(*thread));
}
inline void AfterUpdatePriority(const KernelCore& kernel, KConditionVariable::ThreadTree* tree,
KThread* thread) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
tree->insert(*thread);
}
} // namespace Kernel

View File

@@ -1,32 +0,0 @@
// Copyright 2021 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_writable_event.h"
namespace Kernel {
KEvent::KEvent(KernelCore& kernel, std::string&& name) : Object{kernel, std::move(name)} {}
KEvent::~KEvent() = default;
std::shared_ptr<KEvent> KEvent::Create(KernelCore& kernel, std::string&& name) {
return std::make_shared<KEvent>(kernel, std::move(name));
}
void KEvent::Initialize() {
// Create our sub events.
readable_event = std::make_shared<KReadableEvent>(kernel, GetName() + ":Readable");
writable_event = std::make_shared<KWritableEvent>(kernel, GetName() + ":Writable");
// Initialize our sub sessions.
readable_event->Initialize(this);
writable_event->Initialize(this);
// Mark initialized.
initialized = true;
}
} // namespace Kernel

View File

@@ -1,57 +0,0 @@
// Copyright 2021 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/kernel/object.h"
namespace Kernel {
class KernelCore;
class KReadableEvent;
class KWritableEvent;
class KEvent final : public Object {
public:
explicit KEvent(KernelCore& kernel, std::string&& name);
~KEvent() override;
static std::shared_ptr<KEvent> Create(KernelCore& kernel, std::string&& name);
void Initialize();
void Finalize() override {}
std::string GetTypeName() const override {
return "KEvent";
}
static constexpr HandleType HANDLE_TYPE = HandleType::Event;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
std::shared_ptr<KReadableEvent>& GetReadableEvent() {
return readable_event;
}
std::shared_ptr<KWritableEvent>& GetWritableEvent() {
return writable_event;
}
const std::shared_ptr<KReadableEvent>& GetReadableEvent() const {
return readable_event;
}
const std::shared_ptr<KWritableEvent>& GetWritableEvent() const {
return writable_event;
}
private:
std::shared_ptr<KReadableEvent> readable_event;
std::shared_ptr<KWritableEvent> writable_event;
bool initialized{};
};
} // namespace Kernel

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