Compare commits

..

6 Commits

Author SHA1 Message Date
Zangetsu38 3f3d08a9d4 hack bleach 2017-10-15 01:04:42 +02:00
Zangetsu38 f614151133 Update LLVM 5.0 2017-10-15 01:03:40 +02:00
Zion Nimchuk 7e7d3e658e Initial LLVM 5 port 2017-10-15 01:03:40 +02:00
Megamouse 7b81d19103 GUI: add some visual studio filters (#3375) 2017-10-15 01:03:40 +02:00
Zangetsu38 9425e22ad0 Update Project to Visual Studio 2017. 2017-10-15 01:03:40 +02:00
kd-11 c8b39fe7dd update vulkan submodules 2017-10-14 16:29:36 +03:00
1029 changed files with 84261 additions and 155990 deletions
+5 -3
View File
@@ -11,11 +11,11 @@ BreakConstructorInitializersBeforeComma: true
BreakBeforeBinaryOperators: false
BreakBeforeTernaryOperators: false
AlwaysBreakTemplateDeclarations: true
AllowShortIfStatementsOnASingleLine: false
AllowShortBlocksOnASingleLine: false
AllowShortIfStatementsOnASingleLine: true
AllowShortBlocksOnASingleLine: true
AllowShortCaseLabelsOnASingleLine: true
AllowShortFunctionsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AllowShortLoopsOnASingleLine: true
Cpp11BracedListStyle: true
IndentCaseLabels: false
SortIncludes: false
@@ -29,3 +29,5 @@ BinPackParameters: true
AlwaysBreakAfterReturnType: None
KeepEmptyLinesAtTheStartOfBlocks: true
IndentWrappedFunctionNames: false
#SpaceAfterTemplateKeyword: false
+1 -1
View File
@@ -1,7 +1,7 @@
root = true
[*.{h,cpp,hpp}]
charset = utf-8-bom
charset = utf-8
indent_style = tab
indent_size = 4
trim_trailing_whitespace = true
-2
View File
@@ -1,2 +0,0 @@
patreon: Nekotekina
custom: https://rpcs3.net/alipay
+1 -1
View File
@@ -6,7 +6,7 @@ or our discord:
https://discord.me/RPCS3
PLEASE READ THE GUIDELINES BEFORE OPENING AN ISSUE:
https://github.com/RPCS3/rpcs3/blob/master/.github/CONTRIBUTING.md
https://github.com/RPCS3/rpcs3/blob/master/CONTRIBUTING.md
====================================================
+28 -27
View File
@@ -31,6 +31,7 @@
*.wav
/build
/bin
/lib
/tmp
/ipch
@@ -40,31 +41,47 @@
/Vulkan/Vulkan-build
/Vulkan/glslang-build
!/bin
/bin/*
# Themes
!/bin/GuiConfigs/
/bin/GuiConfigs/*.ini
/bin/GuiConfigs/*.ini.*
/bin/GuiConfigs/*.dat
/bin/GuiConfigs/*.dat.*
/bin/rpcs3.ini
/bin/rpcs3.ipdb
/bin/rpcs3.iobj
/bin/FragmentProgram.txt
/bin/VertexProgram.txt
/bin/*.hlsl
/bin/BreakPoints.dat
/bin/textures
/bin/*.lib
/bin/*.exp
rpcs3/git-version.h
# Visual Studio Files
.vs/*
.vscode/*
*.ipch
*.vspx
*.psess
*.VC.*
*.vcxproj.user
enc_temp_folder/*
CMakeSettings.json
# Copyrighted files
/bin/data/
/bin/dev_flash/data/font
/bin/dev_flash/sys
/bin/dev_flash/vsh
# Ignore installed games except test homebrews
!/bin/dev_hdd0/game/
/bin/dev_hdd0/game/*
!/bin/dev_hdd0/game/TEST12345/
/bin/dev_hdd0/disc/*
# Ignore other system generated files
bin/dev_hdd0/*.txt
x64/*
rpcs3/x64/*
rpcs3/git-version.h
rpcs3-tests/x64/*
# cmake
Makefile
@@ -76,24 +93,8 @@ CMakeCache.txt
rpcs3/cotire/*
rpcs3/rpcs3_*_cotire.cmake
# kdevelop
*.kdev4
.kdev4/*
# Qt
moc_*.cpp
qrc_*.cpp
qrc_resources.cpp
rpcs3_automoc.cpp
ui_*.h
rpcs3/rpcs3_autogen/*
# QtCreator
CMakeLists.txt.user
# CLion
/.idea/*
/cmake-build-*/
# macOS
.DS_Store
+18 -29
View File
@@ -1,54 +1,43 @@
[submodule "rpcs3-ffmpeg"]
path = 3rdparty/ffmpeg
url = https://github.com/hrydgard/ppsspp-ffmpeg
ignore = dirty
[submodule "asmjit"]
path = asmjit
url = https://github.com/kobalicek/asmjit
ignore = dirty
[submodule "llvm"]
path = llvm
url = https://github.com/RPCS3/llvm
branch = release_60
ignore = dirty
url = https://github.com/llvm-mirror/llvm
branch = release_50
[submodule "GSL"]
path = 3rdparty/GSL
url = https://github.com/Microsoft/GSL.git
[submodule "libpng"]
path = 3rdparty/libpng
url = https://github.com/Zangetsu38/libpng
ignore = dirty
[submodule "Vulkan/glslang"]
path = Vulkan/glslang
url = https://github.com/KhronosGroup/glslang.git
ignore = dirty
[submodule "Vulkan/Vulkan-LoaderAndValidationLayers"]
path = Vulkan/Vulkan-LoaderAndValidationLayers
url = https://github.com/KhronosGroup/Vulkan-LoaderAndValidationLayers
[submodule "Utilities/yaml-cpp"]
path = Utilities/yaml-cpp
url = https://github.com/RPCS3/yaml-cpp
[submodule "3rdparty/pugixml"]
path = 3rdparty/pugixml
url = https://github.com/RPCS3/pugixml
[submodule "3rdparty/cereal"]
path = 3rdparty/cereal
url = https://github.com/USCiLab/cereal.git
ignore = dirty
[submodule "3rdparty/zlib"]
path = 3rdparty/zlib
url = https://github.com/madler/zlib
ignore = dirty
[submodule "3rdparty/hidapi"]
path = 3rdparty/hidapi
url = https://github.com/RPCS3/hidapi
url = https://github.com/Zangetsu38/hidapi
branch = master
ignore = dirty
[submodule "3rdparty/pugixml"]
path = 3rdparty/pugixml
url = https://github.com/zeux/pugixml
ignore = dirty
[submodule "3rdparty/xxHash"]
path = 3rdparty/xxHash
url = https://github.com/Cyan4973/xxHash
ignore = dirty
[submodule "3rdparty/yaml-cpp"]
path = 3rdparty/yaml-cpp
url = https://github.com/jbeder/yaml-cpp.git
ignore = dirty
[submodule "3rdparty/libpng"]
path = 3rdparty/libpng
url = https://github.com/glennrp/libpng.git
ignore = dirty
[submodule "3rdparty/libusb"]
path = 3rdparty/libusb
url = https://github.com/RPCS3/libusb.git
ignore = dirty
[submodule "3rdparty/Optional"]
path = 3rdparty/Optional
url = https://github.com/akrzemi1/Optional.git
+117 -30
View File
@@ -1,35 +1,122 @@
language: cpp
sudo: required
os:
- linux
#- osx
# osx_image: xcode6.4
compiler:
- clang
- gcc
cache:
ccache: true
directories:
- $HOME/hombebrew_cache
matrix:
include:
# - os: linux
# env:
# - NAME="Linux build"
# - COMPILER="gcc"
# services: docker
# cache: ccache
# install: "docker pull rpcs3/rpcs3-travis-trusty:1.1"
# script: 'travis_wait docker run -v $(pwd):/rpcs3 -v "$HOME/.ccache":/root/.ccache --env-file .travis/travis.env rpcs3/rpcs3-travis-trusty:1.1 /bin/bash -ex /rpcs3/.travis/build-linux.bash'
- os: linux
env:
- NAME="Linux build"
- COMPILER="clang"
- DEPLOY_APPIMAGE="true"
services: docker
cache: ccache
install: "docker pull rpcs3/rpcs3-travis-trusty:1.1"
script: 'travis_wait docker run -v $(pwd):/rpcs3 -v "$HOME/.ccache":/root/.ccache --env-file .travis/travis.env rpcs3/rpcs3-travis-trusty:1.1 /bin/bash -ex /rpcs3/.travis/build-linux.bash'
- os: osx
osx_image: xcode10.2
addons:
homebrew:
packages:
- ccache
- glew
- ninja
- qt
script: "/bin/bash -ex .travis/build-mac.bash"
cache: ccache
exclude:
- os: osx
compiler: gcc
git:
depth: false # Unshallow clone to obtain proper GIT_VERSION
submodules: false
before_install:
- if [ "$CC" = "clang" ]; then
export CXX="clang++-4.0" CC="clang-4.0";
fi;
- if [ "$TRAVIS_OS_NAME" = "linux" ] && [ "$CXX" = "g++" ]; then
export CXX="g++-5" CC="gcc-5" CXXFLAGS="-Wno-format-security";
fi;
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then
wget http://download.qt.io/official_releases/online_installers/qt-unified-linux-x64-online.run ;
chmod a+x ./qt-unified-linux-x64-online.run ;
export QT_QPA_PLATFORM=minimal ;
./qt-unified-linux-x64-online.run --script qt-installer-noninteractive.qs --no-force-installations ;
fi;
# Install updated libglew-dev since the version provided by trusty is outdated
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then
wget http://mirrors.kernel.org/ubuntu/pool/main/g/glew/libglew-dev_1.13.0-2_amd64.deb;
wget http://mirrors.kernel.org/ubuntu/pool/main/g/glew/libglew1.13_1.13.0-2_amd64.deb;
wget http://mirrors.kernel.org/ubuntu/pool/universe/v/vulkan/libvulkan1_1.0.42.0+dfsg1-1ubuntu1~16.04.1_amd64.deb;
wget http://mirrors.kernel.org/ubuntu/pool/universe/v/vulkan/libvulkan-dev_1.0.42.0+dfsg1-1ubuntu1~16.04.1_amd64.deb;
sudo dpkg -i libglew1.13_1.13.0-2_amd64.deb libglew-dev_1.13.0-2_amd64.deb libvulkan1_1.0.42.0+dfsg1-1ubuntu1~16.04.1_amd64.deb libvulkan-dev_1.0.42.0+dfsg1-1ubuntu1~16.04.1_amd64.deb;
else
brew update;
brew install ccache glew llvm40;
fi;
before_script:
- git submodule update --init asmjit 3rdparty/ffmpeg 3rdparty/pugixml 3rdparty/GSL 3rdparty/libpng Utilities/yaml-cpp 3rdparty/cereal 3rdparty/hidapi 3rdparty/Optional Vulkan/glslang Vulkan/Vulkan-LoaderAndValidationLayers
- mkdir build
- cd build
- export CMAKE_PREFIX_PATH=~/Qt/5.9.1/gcc_64/lib/cmake
- if [ "$TRAVIS_PULL_REQUEST" = false ]; then
export CXXFLAGS="$CXXFLAGS -DBRANCH=$TRAVIS_REPO_SLUG/$TRAVIS_BRANCH";
else
export CXXFLAGS="$CXXFLAGS -DBRANCH=$TRAVIS_REPO_SLUG/$TRAVIS_BRANCH/#$TRAVIS_PULL_REQUEST";
fi;
- cmake .. -DCMAKE_INSTALL_PREFIX=/usr;
- make -j 3
- # AppImage generation
- if [ -n "$UPLOAD_URL" ] && [ "$TRAVIS_BRANCH" = "master" ] && [ "$CC" = "clang-4.0" ] && [ "$TRAVIS_PULL_REQUEST" = false ]; then
export LD_LIBRARY_PATH=~/Qt/5.9.1/gcc_64/lib;
make DESTDIR=appdir install ; find appdir/ ;
find ../bin ;
wget -c "https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage" ;
chmod a+x linuxdeployqt*.AppImage ;
export PATH=~/Qt/5.9.1/gcc_64/bin/:${PATH} ;
./linuxdeployqt*.AppImage --appimage-extract ;
./linuxdeployqt*.AppImage ./appdir/usr/share/applications/*.desktop -bundle-non-qt-libs ;
mkdir ./appdir/usr/plugins/xcbglintegrations/ ;
mkdir ./appdir/usr/plugins/imageformats/ ;
cp ~/Qt/5.9.1/gcc_64/lib/libQt5Svg.so.5 ./appdir/usr/lib/ ;
cp ~/Qt/5.9.1/gcc_64/plugins/xcbglintegrations/* ./appdir/usr/plugins/xcbglintegrations/ ;
cp ~/Qt/5.9.1/gcc_64/plugins/imageformats/* ./appdir/usr/plugins/imageformats/ ;
cp ~/Qt/5.9.1/gcc_64/plugins/platforms/* ./appdir/usr/plugins/platforms/ ;
export PATH=${TRAVIS_BUILD_DIR}/build/squashfs-root/usr/bin/:${PATH} ;
./squashfs-root/usr/bin/appimagetool ${TRAVIS_BUILD_DIR}/build/appdir ;
find ./appdir -executable -type f -exec ldd {} \; | grep " => /usr" | cut -d " " -f 2-3 | sort | uniq ;
curl ${UPLOAD_URL}${TRAVIS_COMMIT:0:7}-${TRAVIS_BUILD_NUMBER}_linux64 --upload-file ./RPCS3*.AppImage;
fi;
script:
# Add a command to show all the variables. May be useful for debugging Travis.
#- echo "--Shell Export Lists START--" ; export -p; echo "--Shell Export Lists STOP--";
# And to ensure the versions of toolchain
- echo "--CXX version?"; "$CXX" --version; echo "--CXX version confirmed";
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty-4.0
- sourceline: 'ppa:jonathonf/binutils' # We need to update binutils to a newer version to link against the ffmpeg libs on.
packages:
- binutils
- cmake
- libasound2-dev
- libopenal-dev
- freeglut3-dev
#- libglew-dev
#- libvulkan1
#- libvulkan-dev
- libc6-dev
- llvm-4.0
- llvm-4.0-dev
- clang-4.0
- libedit-dev
- g++-5
- gcc-5
- libstdc++-5-dev
- lib32stdc++6
- zlib1g-dev
# We need to install qt 5.9.1 manually because the version trusty provides is too old.
#- qtbase5-dev
- libudev-dev
- libevdev-dev
- libpulse-dev
-35
View File
@@ -1,35 +0,0 @@
#!/bin/env bash -ex
shopt -s nocasematch
# Setup Qt variables
export QT_BASE_DIR=/opt/qt${QTVERMIN}
export PATH=$QT_BASE_DIR/bin:$PATH
export LD_LIBRARY_PATH=$QT_BASE_DIR/lib/x86_64-linux-gnu:$QT_BASE_DIR/lib
cd rpcs3
git submodule update --quiet --init asmjit 3rdparty/ffmpeg 3rdparty/pugixml 3rdparty/GSL 3rdparty/libpng 3rdparty/cereal 3rdparty/hidapi 3rdparty/xxHash 3rdparty/yaml-cpp 3rdparty/libusb Vulkan/glslang
# Download pre-compiled llvm libs
curl -sLO https://github.com/RPCS3/llvm/releases/download/continuous-linux-master/llvmlibs-linux.tar.gz
mkdir llvmlibs
tar -xzf ./llvmlibs-linux.tar.gz -C llvmlibs
mkdir build ; cd build
if [ $COMPILER = "gcc" ]; then
# These are set in the dockerfile
export CC=${GCC_BINARY}
export CXX=${GXX_BINARY}
else
export CC=${CLANG_BINARY}
export CXX=${CLANGXX_BINARY}
fi
cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_LLVM_SUBMODULE=OFF -DLLVM_DIR=llvmlibs/lib/cmake/llvm/ -DUSE_NATIVE_INSTRUCTIONS=OFF -G Ninja
ninja; build_status=$?;
cd ..
# If it compiled succesfully let's deploy
if [ $build_status -eq 0 ] && [ -n "$GITHUB_TOKEN" ] && [ "$TRAVIS_BRANCH" = "master" ] && [ "$TRAVIS_PULL_REQUEST" = false ]; then /bin/bash -ex .travis/deploy-linux.bash ; fi
-22
View File
@@ -1,22 +0,0 @@
export CCACHE_SLOPPINESS=pch_defines,time_macros
export CMAKE_PREFIX_PATH=/usr/local/opt/qt5/
export PATH="/usr/local/opt/ccache/libexec:$PATH"
# Setup vulkan and gfx-rs/portability
curl -sLO https://github.com/gfx-rs/portability/releases/download/latest/gfx-portability-macos-latest.zip
unzip -: gfx-portability-macos-latest.zip
curl -sLO https://github.com/KhronosGroup/Vulkan-Headers/archive/sdk-1.1.106.0.zip
unzip -: sdk-*.zip
mkdir vulkan-sdk
ln -s ${PWD}/Vulkan-Headers*/include vulkan-sdk/include
mkdir vulkan-sdk/lib
cp target/release/libportability.dylib vulkan-sdk/lib/libVulkan.dylib
# Let macdeployqt locate and install Vulkan library
install_name_tool -id ${PWD}/vulkan-sdk/lib/libVulkan.dylib vulkan-sdk/lib/libVulkan.dylib
export VULKAN_SDK=${PWD}/vulkan-sdk
git submodule update --quiet --init asmjit 3rdparty/ffmpeg 3rdparty/pugixml 3rdparty/GSL 3rdparty/libpng 3rdparty/cereal 3rdparty/hidapi 3rdparty/libusb 3rdparty/xxHash 3rdparty/yaml-cpp Vulkan/glslang
mkdir build; cd build
cmake .. -DWITH_LLVM=OFF -DUSE_NATIVE_INSTRUCTIONS=OFF -G Ninja
ninja
-53
View File
@@ -1,53 +0,0 @@
#!/bin/env bash -ex
shopt -s nocasematch
cd build
if [ "$DEPLOY_APPIMAGE" = "true" ]; then
DESTDIR=appdir ninja install
curl -sLO "https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage"
chmod a+x linuxdeployqt*.AppImage
./linuxdeployqt*.AppImage --appimage-extract
./squashfs-root/AppRun ./appdir/usr/share/applications/*.desktop -bundle-non-qt-libs
ls ./appdir/usr/lib/
rm -r ./appdir/usr/share/doc
rm ./appdir/usr/lib/libxcb*
cp $(readlink -f /lib/x86_64-linux-gnu/libnsl.so.1) ./appdir/usr/lib/libnsl.so.1
export PATH=/rpcs3/build/squashfs-root/usr/bin/:${PATH}
# Embed newer libstdc++ for distros that don't come with it (ubuntu 14.04, 16.04)
mkdir -p appdir/usr/optional/ ; mkdir -p appdir/usr/optional/libstdc++/
cp /usr/lib/x86_64-linux-gnu/libstdc++.so.6 ./appdir/usr/optional/libstdc++/
rm ./appdir/AppRun
curl -sL https://github.com/RPCS3/AppImageKit-checkrt/releases/download/continuous2/AppRun-patched-x86_64 -o ./appdir/AppRun
chmod a+x ./appdir/AppRun
curl -sL https://github.com/RPCS3/AppImageKit-checkrt/releases/download/continuous2/exec-x86_64.so -o ./appdir/usr/optional/exec.so
# Compile checker binary for AppImageKit-checkrt
# This may need updating if you update the compiler or rpcs3 uses newer c++ features
# See https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/config/abi/pre/gnu.ver
# for which definitions correlate to which CXXABI version.
printf "#include <memory>\nint main(){std::make_exception_ptr(0);}" | $CXX -x c++ -o ./appdir/usr/optional/checker -
# Package it up and send it off
./squashfs-root/usr/bin/appimagetool /rpcs3/build/appdir
ls
COMM_TAG="$(git describe --tags $(git rev-list --tags --max-count=1))"
COMM_COUNT="$(git rev-list --count HEAD)"
curl -sLO https://github.com/hcorion/uploadtool/raw/master/upload.sh
mv ./RPCS3*.AppImage rpcs3-${COMM_TAG}-${COMM_COUNT}-${TRAVIS_COMMIT:0:8}_linux64.AppImage
FILESIZEMB=$(bc <<< "scale=6; $(stat -c %s ./rpcs3*.AppImage)/1000000")
SHA256SUM=($(sha256sum ./rpcs3*.AppImage))
unset TRAVIS_REPO_SLUG
REPO_SLUG=RPCS3/rpcs3-binaries-linux \
UPLOADTOOL_BODY="$SHA256SUM;${FILESIZEMB}MB"\
RELEASE_NAME=build-${TRAVIS_COMMIT}\
RELEASE_TITLE=${COMM_TAG}-${COMM_COUNT}\
REPO_COMMIT=d812f1254a1157c80fd402f94446310560f54e5f\
bash upload.sh rpcs3*.AppImage
fi
if [ "$DEPLOY_PPA" = "true" ]; then
export DEBFULLNAME="RPCS3 Build Bot"
fi
-9
View File
@@ -1,9 +0,0 @@
# Variables set by Travis CI
TRAVIS_PULL_REQUEST
TRAVIS_BRANCH
TRAVIS_COMMIT
# Variables for Travis build matrix
COMPILER
DEPLOY_APPIMAGE
# Private Travis CI variables
GITHUB_TOKEN
-378
View File
@@ -1,378 +0,0 @@
find_package(PkgConfig)
include(ExternalProject)
if(APPLE)
set(PLATFORM_ARCH "macosx/x86_64")
elseif(WIN32)
set(PLATFORM_ARCH "Windows/x86_64")
else()
set(PLATFORM_ARCH "linux/x86_64")
endif()
# Dummy target to use when lib isn't available
add_library(3rdparty_dummy_lib INTERFACE)
# ZLib
if (USE_SYSTEM_ZLIB)
find_package(ZLIB QUIET)
endif()
if (NOT ZLIB_FOUND)
message(STATUS "Using builtin ZLIB")
set(SKIP_INSTALL_ALL ON)
add_subdirectory(zlib EXCLUDE_FROM_ALL)
set(ZLIB_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/zlib" "${CMAKE_CURRENT_BINARY_DIR}/zlib")
set(ZLIB_LIBRARY zlibstatic)
endif()
add_library(3rdparty_zlib INTERFACE)
target_link_libraries(3rdparty_zlib INTERFACE ${ZLIB_LIBRARY})
target_include_directories(3rdparty_zlib INTERFACE ${ZLIB_INCLUDE_DIR})
# libPNG
# Select the version of libpng to use, default is builtin
if (NOT USE_SYSTEM_LIBPNG)
# We use libpng's static library and don't need to build the shared library and run the tests
set(PNG_SHARED OFF CACHE BOOL "Build shared lib" FORCE)
set(PNG_TESTS OFF CACHE BOOL "Build libpng tests" FORCE)
set(PNG_BUILD_ZLIB ON CACHE BOOL "ZLIB is already build or package is found" FORCE)
set(SKIP_INSTALL_ALL ON)
add_subdirectory(libpng EXCLUDE_FROM_ALL)
set(LIBPNG_TARGET png_static)
target_include_directories(png_static INTERFACE "${libpng_BINARY_DIR}" "${libpng_SOURCE_DIR}")
else()
find_package(PNG REQUIRED)
add_library(3rdparty_system_libpng INTERFACE)
target_include_directories(3rdparty_system_libpng INTERFACE ${PNG_INCLUDE_DIR})
target_link_libraries(3rdparty_system_libpng INTERFACE ${PNG_LIBRARY})
target_compile_definitions(3rdparty_system_libpng INTERFACE ${PNG_DEFINITIONS})
set(LIBPNG_TARGET 3rdparty_system_libpng)
endif()
# pugixml
add_subdirectory(pugixml EXCLUDE_FROM_ALL)
# hidapi
add_library(3rdparty_hidapi INTERFACE)
target_include_directories(3rdparty_hidapi INTERFACE hidapi/hidapi)
if(APPLE)
add_subdirectory(hidapi/mac EXCLUDE_FROM_ALL)
target_include_directories(hidapi-mac PUBLIC hidapi/hidapi)
target_link_libraries(3rdparty_hidapi INTERFACE hidapi-mac "-framework CoreFoundation" "-framework IOKit")
elseif(CMAKE_SYSTEM MATCHES "Linux")
add_subdirectory(hidapi/linux EXCLUDE_FROM_ALL)
target_include_directories(hidapi-hidraw PUBLIC hidapi/hidapi)
target_link_libraries(3rdparty_hidapi INTERFACE hidapi-hidraw udev)
elseif(WIN32)
add_subdirectory(hidapi/windows EXCLUDE_FROM_ALL)
target_include_directories(hidapi-hid PUBLIC hidapi/hidapi)
target_link_libraries(3rdparty_hidapi INTERFACE hidapi-hid Shlwapi.lib)
else()
add_subdirectory(hidapi/libusb EXCLUDE_FROM_ALL)
target_include_directories(hidapi-libusb PUBLIC hidapi/hidapi)
target_link_libraries(3rdparty_hidapi INTERFACE hidapi-libusb usb)
endif()
# libusb
if(CMAKE_SYSTEM MATCHES "DragonFly|FreeBSD")
# Always use system libusb as reference implementation isn't supported
add_library(usb-1.0-static INTERFACE)
target_link_libraries(usb-1.0-static INTERFACE usb)
elseif(MSVC)
# Windows time.h defines timespec but doesn't add any flag for it, which makes libusb attempt to define it again
add_definitions(-DHAVE_STRUCT_TIMESPEC=1)
add_subdirectory(libusb EXCLUDE_FROM_ALL)
else()
add_subdirectory(libusb EXCLUDE_FROM_ALL)
endif()
# yaml-cpp
# We don't want to install yaml-cpp but its cmake file doesn't have option
# to disable it...
# So we just install it to a different directory
set(CMAKE_INSTALL_PREFIX_OLD ${CMAKE_INSTALL_PREFIX})
set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/yaml-cpp_install)
set(YAML_CPP_BUILD_TESTS OFF CACHE BOOL "Enable testing" FORCE)
set(YAML_CPP_BUILD_TOOLS OFF CACHE BOOL "Enable parse tools" FORCE)
set(YAML_CPP_BUILD_CONTRIB OFF CACHE BOOL "Enable contrib stuff in library" FORCE)
add_subdirectory(yaml-cpp EXCLUDE_FROM_ALL)
set(CMAKE_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX_OLD})
# xxHash
set(XXHASH_BUNDLED_MODE ON)
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Make xxHash build static libs")
add_subdirectory(xxHash/cmake_unofficial EXCLUDE_FROM_ALL)
target_include_directories(xxhash INTERFACE xxHash)
# cereal
add_library(3rdparty_cereal INTERFACE)
target_include_directories(3rdparty_cereal INTERFACE cereal/include)
# OpenGL
# Prefer GLVND for OpenGL rather than legacy
set(OpenGL_GL_PREFERENCE GLVND)
find_package(OpenGL REQUIRED)
add_library(3rdparty_opengl INTERFACE)
target_include_directories(3rdparty_opengl INTERFACE GL)
if (WIN32)
if(NOT MSVC)
target_link_libraries(3rdparty_opengl INTERFACE ${OPENGL_LIBRARIES} opengl32.lib glu32.lib)
else()
target_link_libraries(3rdparty_opengl INTERFACE dxgi.lib d2d1.lib dwrite.lib)
endif()
else()
target_link_libraries(3rdparty_opengl INTERFACE ${OPENGL_LIBRARIES})
target_compile_definitions(3rdparty_opengl
INTERFACE
-DGL_GLEXT_PROTOTYPES
-DGLX_GLXEXT_PROTOTYPES)
endif()
# GSL
add_library(3rdparty_gsl INTERFACE)
target_include_directories(3rdparty_gsl INTERFACE GSL/include)
# stblib
add_library(3rdparty_stblib INTERFACE)
target_include_directories(3rdparty_stblib INTERFACE stblib)
# discord_rpc
add_library(3rdparty_discord-rpc INTERFACE)
# We don't want Discord Rich Presence on the BSDs and other OSes
if (USE_DISCORD_RPC AND (WIN32 OR CMAKE_SYSTEM MATCHES "Linux" OR APPLE))
if (WIN32 AND NOT MSVC)
ExternalProject_Add(discord-rpc
GIT_REPOSITORY https://github.com/discordapp/discord-rpc
BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/discord-rpc
INSTALL_COMMAND ""
)
endif()
target_include_directories(3rdparty_discord-rpc INTERFACE discord-rpc/include)
target_compile_definitions(3rdparty_discord-rpc INTERFACE -DWITH_DISCORD_RPC)
set(DISCORD_RPC_LIB NOTFOUND)
if (WIN32)
if (NOT MSVC)
set(DISCORD_RPC_LIB ${CMAKE_CURRENT_BINARY_DIR}/discord-rpc/src/libdiscord-rpc.a)
else()
find_library(DISCORD_RPC_LIB discord-rpc PATHS discord-rpc/lib/ NO_DEFAULT_PATH)
endif()
elseif(CMAKE_SYSTEM MATCHES "Linux")
find_library(DISCORD_RPC_LIB discord-rpc-linux PATHS discord-rpc/lib/ NO_DEFAULT_PATH)
elseif(APPLE)
find_library(DISCORD_RPC_LIB discord-rpc-mac PATHS discord-rpc/lib/ NO_DEFAULT_PATH)
endif()
target_link_libraries(3rdparty_discord-rpc INTERFACE ${DISCORD_RPC_LIB})
if(APPLE)
target_link_libraries(3rdparty_discord-rpc INTERFACE "objc" "-framework Foundation" "-framework CoreServices")
endif()
endif()
# ALSA
set(ALSA_TARGET 3rdparty_dummy_lib)
if(USE_ALSA)
find_package(ALSA)
if(ALSA_FOUND)
add_library(3rdparty_alsa INTERFACE)
target_compile_definitions(3rdparty_alsa INTERFACE -DHAVE_ALSA)
target_include_directories(3rdparty_alsa SYSTEM INTERFACE ${ALSA_INCLUDE_DIRS})
target_link_libraries(3rdparty_alsa INTERFACE ${ALSA_LIBRARIES})
set(ALSA_TARGET 3rdparty_alsa)
endif()
endif()
# Pulse
set(PULSE_TARGET 3rdparty_dummy_lib)
if(USE_PULSE)
pkg_check_modules(PULSE libpulse-simple)
if(PULSE_FOUND)
add_library(3rdparty_pulse INTERFACE)
target_compile_definitions(3rdparty_pulse INTERFACE -DHAVE_PULSE)
target_include_directories(3rdparty_pulse SYSTEM
INTERFACE ${PULSE_INCLUDE_DIRS})
target_link_libraries(3rdparty_pulse INTERFACE ${PULSE_LDFLAGS})
set(PULSE_TARGET 3rdparty_pulse)
endif()
endif()
# libevdev
set(LIBEVDEV_TARGET 3rdparty_dummy_lib)
if(USE_LIBEVDEV)
pkg_check_modules(LIBEVDEV libevdev)
if(LIBEVDEV_FOUND)
add_library(3rdparty_libevdev INTERFACE)
target_compile_definitions(3rdparty_libevdev INTERFACE -DHAVE_LIBEVDEV)
target_include_directories(3rdparty_libevdev SYSTEM
INTERFACE ${LIBEVDEV_INCLUDE_DIRS})
target_link_libraries(3rdparty_libevdev INTERFACE ${LIBEVDEV_LDFLAGS})
set(LIBEVDEV_TARGET 3rdparty_libevdev)
endif()
endif()
# Vulkan
set(VULKAN_TARGET 3rdparty_dummy_lib)
if(USE_VULKAN)
find_package(Vulkan)
if(VULKAN_FOUND)
add_library(3rdparty_vulkan INTERFACE)
target_compile_definitions(3rdparty_vulkan INTERFACE -DHAVE_VULKAN)
target_link_libraries(3rdparty_vulkan INTERFACE SPIRV Vulkan::Vulkan)
if(UNIX AND NOT APPLE)
find_package(Wayland)
if (WAYLAND_FOUND)
target_include_directories(3rdparty_vulkan
INTERFACE ${WAYLAND_INCLUDE_DIR})
target_compile_definitions(3rdparty_vulkan
INTERFACE -DVK_USE_PLATFORM_WAYLAND_KHR)
endif()
endif()
set(VULKAN_TARGET 3rdparty_vulkan)
else()
message("WARNING! USE_VULKAN was enabled, but libvulkan was not found. RPCS3 will be compiled without Vulkan support.")
endif()
endif()
# OpenAL
if (MSVC)
find_path(OPENAL_INCLUDE_DIR al.h PATHS OpenAL/include)
find_library(OPENAL_LIBRARY OpenAL32 PATHS OpenAL/libs/Win64/)
else()
find_package(OpenAL REQUIRED)
endif()
add_library(3rdparty_openal INTERFACE)
target_include_directories(3rdparty_openal INTERFACE ${OPENAL_INCLUDE_DIR})
target_link_libraries(3rdparty_openal INTERFACE ${OPENAL_LIBRARY})
# FFMPEG
add_library(3rdparty_ffmpeg INTERFACE)
# Select the version of ffmpeg to use, default is builtin
if(USE_SYSTEM_FFMPEG)
message("-- RPCS3: using shared ffmpeg")
find_package(FFMPEG REQUIRED)
target_include_directories(3rdparty_ffmpeg INTERFACE ${FFMPEG_INCLUDE_DIR})
target_link_libraries(3rdparty_ffmpeg INTERFACE ${FFMPEG_LIBRARIES})
else()
set(FFMPEG_PLATFORM_DIR "ffmpeg/${PLATFORM_ARCH}")
if (NOT MSVC AND WIN32)
message("-- RPCS3: building ffmpeg submodule")
ExternalProject_Add(ffmpeg-mingw
DOWNLOAD_COMMAND ""
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg
BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/ffmpeg
CONFIGURE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg/configure --prefix=./Windows/x86_64 --arch=x86_64 --disable-avdevice --disable-programs --disable-avfilter --disable-postproc --disable-doc --disable-pthreads --enable-w32threads --disable-network --disable-everything --disable-encoders --disable-muxers --disable-hwaccels --disable-parsers --disable-protocols --enable-dxva2 --enable-static --disable-shared --enable-decoder=aac --enable-decoder=aac_latm --enable-decoder=atrac3 --enable-decoder=atrac3p --enable-decoder=mp3 --enable-decoder=pcm_s16le --enable-decoder=pcm_s8 --enable-decoder=h264 --enable-decoder=mpeg4 --enable-decoder=mpeg2video --enable-decoder=mjpeg --enable-decoder=mjpegb --enable-encoder=pcm_s16le --enable-encoder=ffv1 --enable-encoder=mpeg4 --enable-parser=h264 --enable-parser=mpeg4video --enable-parser=mpegaudio --enable-parser=mpegvideo --enable-parser=mjpeg --enable-parser=aac --enable-parser=aac_latm --enable-muxer=avi --enable-demuxer=h264 --enable-demuxer=m4v --enable-demuxer=mp3 --enable-demuxer=mpegvideo --enable-demuxer=mpegps --enable-demuxer=mjpeg --enable-demuxer=avi --enable-demuxer=aac --enable-demuxer=pmp --enable-demuxer=oma --enable-demuxer=pcm_s16le --enable-demuxer=pcm_s8 --enable-demuxer=wav --enable-hwaccel=h264_dxva2 --enable-indev=dshow --enable-protocol=file
BUILD_COMMAND make -j 4
INSTALL_COMMAND make install
)
set(FFMPEG_LIB_AVFORMAT "${CMAKE_CURRENT_BINARY_DIR}/ffmpeg/${PLATFORM_ARCH}/lib/libavformat.a")
set(FFMPEG_LIB_AVCODEC "${CMAKE_CURRENT_BINARY_DIR}/ffmpeg/${PLATFORM_ARCH}/lib/libavcodec.a")
set(FFMPEG_LIB_AVUTIL "${CMAKE_CURRENT_BINARY_DIR}/ffmpeg/${PLATFORM_ARCH}/lib/libavutil.a")
set(FFMPEG_LIB_SWSCALE "${CMAKE_CURRENT_BINARY_DIR}/ffmpeg/${PLATFORM_ARCH}/lib/libswscale.a")
else()
message("-- RPCS3: using builtin ffmpeg")
set(FFMPEG_LIB_DIR "${FFMPEG_PLATFORM_DIR}/lib")
find_library(FFMPEG_LIB_AVFORMAT avformat PATHS ${FFMPEG_LIB_DIR} NO_DEFAULT_PATH)
find_library(FFMPEG_LIB_AVCODEC avcodec PATHS ${FFMPEG_LIB_DIR} NO_DEFAULT_PATH)
find_library(FFMPEG_LIB_AVUTIL avutil PATHS ${FFMPEG_LIB_DIR} NO_DEFAULT_PATH)
find_library(FFMPEG_LIB_SWSCALE swscale PATHS ${FFMPEG_LIB_DIR} NO_DEFAULT_PATH)
endif()
target_include_directories(3rdparty_ffmpeg INTERFACE "${FFMPEG_PLATFORM_DIR}/include")
target_link_libraries(3rdparty_ffmpeg
INTERFACE
${FFMPEG_LIB_AVFORMAT}
${FFMPEG_LIB_AVCODEC}
${FFMPEG_LIB_AVUTIL}
${FFMPEG_LIB_SWSCALE})
endif()
# dx12
add_library(3rdparty_dx12 INTERFACE)
if (WIN32)
target_link_libraries(3rdparty_dx12 INTERFACE dxgi.lib d2d1.lib dwrite.lib)
endif()
# GLEW
add_library(3rdparty_glew INTERFACE)
if(NOT MSVC)
find_package(GLEW 1.13.0 REQUIRED)
target_link_libraries(3rdparty_glew INTERFACE GLEW::GLEW)
endif()
# LLVM
include(llvm.cmake)
# add nice ALIAS targets for ease of use
add_library(3rdparty::libusb ALIAS usb-1.0-static)
add_library(3rdparty::zlib ALIAS 3rdparty_zlib)
add_library(3rdparty::pugixml ALIAS pugixml)
add_library(3rdparty::yaml-cpp ALIAS yaml-cpp)
add_library(3rdparty::xxhash ALIAS xxhash)
add_library(3rdparty::hidapi ALIAS 3rdparty_hidapi)
add_library(3rdparty::libpng ALIAS ${LIBPNG_TARGET})
add_library(3rdparty::cereal ALIAS 3rdparty_cereal)
add_library(3rdparty::opengl ALIAS 3rdparty_opengl)
add_library(3rdparty::gsl ALIAS 3rdparty_gsl)
add_library(3rdparty::stblib ALIAS 3rdparty_stblib)
add_library(3rdparty::discord-rpc ALIAS 3rdparty_discord-rpc)
add_library(3rdparty::alsa ALIAS ${ALSA_TARGET})
add_library(3rdparty::pulse ALIAS ${PULSE_TARGET})
add_library(3rdparty::libevdev ALIAS ${LIBEVDEV_TARGET})
add_library(3rdparty::vulkan ALIAS ${VULKAN_TARGET})
add_library(3rdparty::openal ALIAS 3rdparty_openal)
add_library(3rdparty::ffmpeg ALIAS 3rdparty_ffmpeg)
add_library(3rdparty::dx12 ALIAS 3rdparty_dx12)
add_library(3rdparty::glew ALIAS 3rdparty_glew)
+25 -699
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1 -1
+2 -118
View File
@@ -13,8 +13,8 @@
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
@@ -97,31 +97,6 @@ extern "C" {
#ifndef AL_EXT_MCFORMATS
#define AL_EXT_MCFORMATS 1
/* Provides support for surround sound buffer formats with 8, 16, and 32-bit
* samples.
*
* QUAD8: Unsigned 8-bit, Quadraphonic (Front Left, Front Right, Rear Left,
* Rear Right).
* QUAD16: Signed 16-bit, Quadraphonic.
* QUAD32: 32-bit float, Quadraphonic.
* REAR8: Unsigned 8-bit, Rear Stereo (Rear Left, Rear Right).
* REAR16: Signed 16-bit, Rear Stereo.
* REAR32: 32-bit float, Rear Stereo.
* 51CHN8: Unsigned 8-bit, 5.1 Surround (Front Left, Front Right, Front Center,
* LFE, Side Left, Side Right). Note that some audio systems may label
* 5.1's Side channels as Rear or Surround; they are equivalent for the
* purposes of this extension.
* 51CHN16: Signed 16-bit, 5.1 Surround.
* 51CHN32: 32-bit float, 5.1 Surround.
* 61CHN8: Unsigned 8-bit, 6.1 Surround (Front Left, Front Right, Front Center,
* LFE, Rear Center, Side Left, Side Right).
* 61CHN16: Signed 16-bit, 6.1 Surround.
* 61CHN32: 32-bit float, 6.1 Surround.
* 71CHN8: Unsigned 8-bit, 7.1 Surround (Front Left, Front Right, Front Center,
* LFE, Rear Left, Rear Right, Side Left, Side Right).
* 71CHN16: Signed 16-bit, 7.1 Surround.
* 71CHN32: 32-bit float, 7.1 Surround.
*/
#define AL_FORMAT_QUAD8 0x1204
#define AL_FORMAT_QUAD16 0x1205
#define AL_FORMAT_QUAD32 0x1206
@@ -418,97 +393,6 @@ ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device);
#endif
#endif
#ifndef AL_EXT_BFORMAT
#define AL_EXT_BFORMAT 1
/* Provides support for B-Format ambisonic buffers (first-order, FuMa scaling
* and layout).
*
* BFORMAT2D_8: Unsigned 8-bit, 3-channel non-periphonic (WXY).
* BFORMAT2D_16: Signed 16-bit, 3-channel non-periphonic (WXY).
* BFORMAT2D_FLOAT32: 32-bit float, 3-channel non-periphonic (WXY).
* BFORMAT3D_8: Unsigned 8-bit, 4-channel periphonic (WXYZ).
* BFORMAT3D_16: Signed 16-bit, 4-channel periphonic (WXYZ).
* BFORMAT3D_FLOAT32: 32-bit float, 4-channel periphonic (WXYZ).
*/
#define AL_FORMAT_BFORMAT2D_8 0x20021
#define AL_FORMAT_BFORMAT2D_16 0x20022
#define AL_FORMAT_BFORMAT2D_FLOAT32 0x20023
#define AL_FORMAT_BFORMAT3D_8 0x20031
#define AL_FORMAT_BFORMAT3D_16 0x20032
#define AL_FORMAT_BFORMAT3D_FLOAT32 0x20033
#endif
#ifndef AL_EXT_MULAW_BFORMAT
#define AL_EXT_MULAW_BFORMAT 1
#define AL_FORMAT_BFORMAT2D_MULAW 0x10031
#define AL_FORMAT_BFORMAT3D_MULAW 0x10032
#endif
#ifndef ALC_SOFT_HRTF
#define ALC_SOFT_HRTF 1
#define ALC_HRTF_SOFT 0x1992
#define ALC_DONT_CARE_SOFT 0x0002
#define ALC_HRTF_STATUS_SOFT 0x1993
#define ALC_HRTF_DISABLED_SOFT 0x0000
#define ALC_HRTF_ENABLED_SOFT 0x0001
#define ALC_HRTF_DENIED_SOFT 0x0002
#define ALC_HRTF_REQUIRED_SOFT 0x0003
#define ALC_HRTF_HEADPHONES_DETECTED_SOFT 0x0004
#define ALC_HRTF_UNSUPPORTED_FORMAT_SOFT 0x0005
#define ALC_NUM_HRTF_SPECIFIERS_SOFT 0x1994
#define ALC_HRTF_SPECIFIER_SOFT 0x1995
#define ALC_HRTF_ID_SOFT 0x1996
typedef const ALCchar* (ALC_APIENTRY*LPALCGETSTRINGISOFT)(ALCdevice *device, ALCenum paramName, ALCsizei index);
typedef ALCboolean (ALC_APIENTRY*LPALCRESETDEVICESOFT)(ALCdevice *device, const ALCint *attribs);
#ifdef AL_ALEXT_PROTOTYPES
ALC_API const ALCchar* ALC_APIENTRY alcGetStringiSOFT(ALCdevice *device, ALCenum paramName, ALCsizei index);
ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCint *attribs);
#endif
#endif
#ifndef AL_SOFT_gain_clamp_ex
#define AL_SOFT_gain_clamp_ex 1
#define AL_GAIN_LIMIT_SOFT 0x200E
#endif
#ifndef AL_SOFT_source_resampler
#define AL_SOFT_source_resampler
#define AL_NUM_RESAMPLERS_SOFT 0x1210
#define AL_DEFAULT_RESAMPLER_SOFT 0x1211
#define AL_SOURCE_RESAMPLER_SOFT 0x1212
#define AL_RESAMPLER_NAME_SOFT 0x1213
typedef const ALchar* (AL_APIENTRY*LPALGETSTRINGISOFT)(ALenum pname, ALsizei index);
#ifdef AL_ALEXT_PROTOTYPES
AL_API const ALchar* AL_APIENTRY alGetStringiSOFT(ALenum pname, ALsizei index);
#endif
#endif
#ifndef AL_SOFT_source_spatialize
#define AL_SOFT_source_spatialize
#define AL_SOURCE_SPATIALIZE_SOFT 0x1214
#define AL_AUTO_SOFT 0x0002
#endif
#ifndef ALC_SOFT_output_limiter
#define ALC_SOFT_output_limiter
#define ALC_OUTPUT_LIMITER_SOFT 0x199A
#endif
#ifndef ALC_SOFT_device_clock
#define ALC_SOFT_device_clock 1
typedef int64_t ALCint64SOFT;
typedef uint64_t ALCuint64SOFT;
#define ALC_DEVICE_CLOCK_SOFT 0x1600
#define ALC_DEVICE_LATENCY_SOFT 0x1601
#define ALC_DEVICE_CLOCK_LATENCY_SOFT 0x1602
#define AL_SAMPLE_OFFSET_CLOCK_SOFT 0x1202
#define AL_SEC_OFFSET_CLOCK_SOFT 0x1203
typedef void (ALC_APIENTRY*LPALCGETINTEGER64VSOFT)(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values);
#ifdef AL_ALEXT_PROTOTYPES
ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values);
#endif
#endif
#ifdef __cplusplus
}
#endif
+1 -1
View File
@@ -345,7 +345,7 @@ typedef struct {
/* Driving Presets */
#define EFX_REVERB_PRESET_DRIVING_COMMENTATOR \
{ 1.0000f, 0.0000f, 0.3162f, 0.5623f, 0.5012f, 2.4200f, 0.8800f, 0.6800f, 0.1995f, 0.0930f, { 0.0000f, 0.0000f, 0.0000f }, 0.2512f, 0.0170f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9886f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
{ 1.0000f, 0.0000f, 3.1623f, 0.5623f, 0.5012f, 2.4200f, 0.8800f, 0.6800f, 0.1995f, 0.0930f, { 0.0000f, 0.0000f, 0.0000f }, 0.2512f, 0.0170f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9886f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
#define EFX_REVERB_PRESET_DRIVING_PITGARAGE \
{ 0.4287f, 0.5900f, 0.3162f, 0.7079f, 0.5623f, 1.7200f, 0.9300f, 0.8700f, 0.5623f, 0.0000f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0160f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.1100f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }
+91
View File
@@ -1,35 +1,84 @@
EXPORTS
alAuxiliaryEffectSlotf
alAuxiliaryEffectSlotfv
alAuxiliaryEffectSloti
alAuxiliaryEffectSlotiv
alBuffer3f
alBuffer3i
alBufferData
alBufferSamplesSOFT
alBufferSubDataSOFT
alBufferSubSamplesSOFT
alBufferf
alBufferfv
alBufferi
alBufferiv
alDeferUpdatesSOFT
alDeleteAuxiliaryEffectSlots
alDeleteBuffers
alDeleteEffects
alDeleteFilters
alDeleteFontsoundsSOFT
alDeletePresetsSOFT
alDeleteSoundfontsSOFT
alDeleteSources
alDisable
alDistanceModel
alDopplerFactor
alDopplerVelocity
alEffectf
alEffectfv
alEffecti
alEffectiv
alEnable
alFilterf
alFilterfv
alFilteri
alFilteriv
alFontsound2iSOFT
alFontsoundModulatoriSOFT
alFontsoundiSOFT
alFontsoundivSOFT
alGenAuxiliaryEffectSlots
alGenBuffers
alGenEffects
alGenFilters
alGenFontsoundsSOFT
alGenPresetsSOFT
alGenSoundfontsSOFT
alGenSources
alGetAuxiliaryEffectSlotf
alGetAuxiliaryEffectSlotfv
alGetAuxiliaryEffectSloti
alGetAuxiliaryEffectSlotiv
alGetBoolean
alGetBooleanv
alGetBuffer3f
alGetBuffer3i
alGetBufferSamplesSOFT
alGetBufferf
alGetBufferfv
alGetBufferi
alGetBufferiv
alGetDouble
alGetDoublev
alGetEffectf
alGetEffectfv
alGetEffecti
alGetEffectiv
alGetEnumValue
alGetError
alGetFilterf
alGetFilterfv
alGetFilteri
alGetFilteriv
alGetFloat
alGetFloatv
alGetFontsoundModulatorivSOFT
alGetFontsoundivSOFT
alGetInteger
alGetInteger64SOFT
alGetInteger64vSOFT
alGetIntegerv
alGetListener3f
alGetListener3i
@@ -37,17 +86,32 @@ EXPORTS
alGetListenerfv
alGetListeneri
alGetListeneriv
alGetPresetivSOFT
alGetProcAddress
alGetSoundfontivSOFT
alGetSource3dSOFT
alGetSource3f
alGetSource3i
alGetSource3i64SOFT
alGetSourcedSOFT
alGetSourcedvSOFT
alGetSourcef
alGetSourcefv
alGetSourcei
alGetSourcei64SOFT
alGetSourcei64vSOFT
alGetSourceiv
alGetString
alIsAuxiliaryEffectSlot
alIsBuffer
alIsBufferFormatSupportedSOFT
alIsEffect
alIsEnabled
alIsExtensionPresent
alIsFilter
alIsFontsoundSOFT
alIsPresetSOFT
alIsSoundfontSOFT
alIsSource
alListener3f
alListener3i
@@ -55,8 +119,25 @@ EXPORTS
alListenerfv
alListeneri
alListeneriv
alLoadSoundfontSOFT
alMidiEventSOFT
alMidiGainSOFT
alMidiPauseSOFT
alMidiPlaySOFT
alMidiResetSOFT
alMidiSoundfontSOFT
alMidiSoundfontvSOFT
alMidiStopSOFT
alMidiSysExSOFT
alPresetFontsoundsSOFT
alPresetiSOFT
alPresetivSOFT
alProcessUpdatesSOFT
alSoundfontPresetsSOFT
alSource3dSOFT
alSource3f
alSource3i
alSource3i64SOFT
alSourcePause
alSourcePausev
alSourcePlay
@@ -67,9 +148,13 @@ EXPORTS
alSourceStop
alSourceStopv
alSourceUnqueueBuffers
alSourcedSOFT
alSourcedvSOFT
alSourcef
alSourcefv
alSourcei
alSourcei64SOFT
alSourcei64vSOFT
alSourceiv
alSpeedOfSound
alcCaptureCloseDevice
@@ -80,17 +165,23 @@ EXPORTS
alcCloseDevice
alcCreateContext
alcDestroyContext
alcDevicePauseSOFT
alcDeviceResumeSOFT
alcGetContextsDevice
alcGetCurrentContext
alcGetEnumValue
alcGetError
alcGetInteger64vSOFT
alcGetIntegerv
alcGetProcAddress
alcGetString
alcGetThreadContext
alcIsExtensionPresent
alcIsRenderFormatSupportedSOFT
alcLoopbackOpenDeviceSOFT
alcMakeContextCurrent
alcOpenDevice
alcProcessContext
alcRenderSamplesSOFT
alcSetThreadContext
alcSuspendContext
Binary file not shown.
Vendored Submodule
+1
Submodule 3rdparty/Optional added at f27e79084a
-26
View File
@@ -1,26 +0,0 @@
#pragma once
#if defined(DISCORD_DYNAMIC_LIB)
# if defined(_WIN32)
# if defined(DISCORD_BUILDING_SDK)
# define DISCORD_EXPORT __declspec(dllexport)
# else
# define DISCORD_EXPORT __declspec(dllimport)
# endif
# else
# define DISCORD_EXPORT __attribute__((visibility("default")))
# endif
#else
# define DISCORD_EXPORT
#endif
#ifdef __cplusplus
extern "C" {
#endif
DISCORD_EXPORT void Discord_Register(const char* applicationId, const char* command);
DISCORD_EXPORT void Discord_RegisterSteamGame(const char* applicationId, const char* steamId);
#ifdef __cplusplus
}
#endif
-87
View File
@@ -1,87 +0,0 @@
#pragma once
#include <stdint.h>
// clang-format off
#if defined(DISCORD_DYNAMIC_LIB)
# if defined(_WIN32)
# if defined(DISCORD_BUILDING_SDK)
# define DISCORD_EXPORT __declspec(dllexport)
# else
# define DISCORD_EXPORT __declspec(dllimport)
# endif
# else
# define DISCORD_EXPORT __attribute__((visibility("default")))
# endif
#else
# define DISCORD_EXPORT
#endif
// clang-format on
#ifdef __cplusplus
extern "C" {
#endif
typedef struct DiscordRichPresence {
const char* state; /* max 128 bytes */
const char* details; /* max 128 bytes */
int64_t startTimestamp;
int64_t endTimestamp;
const char* largeImageKey; /* max 32 bytes */
const char* largeImageText; /* max 128 bytes */
const char* smallImageKey; /* max 32 bytes */
const char* smallImageText; /* max 128 bytes */
const char* partyId; /* max 128 bytes */
int partySize;
int partyMax;
const char* matchSecret; /* max 128 bytes */
const char* joinSecret; /* max 128 bytes */
const char* spectateSecret; /* max 128 bytes */
int8_t instance;
} DiscordRichPresence;
typedef struct DiscordJoinRequest {
const char* userId;
const char* username;
const char* discriminator;
const char* avatar;
} DiscordJoinRequest;
typedef struct DiscordEventHandlers {
void (*ready)(void);
void (*disconnected)(int errorCode, const char* message);
void (*errored)(int errorCode, const char* message);
void (*joinGame)(const char* joinSecret);
void (*spectateGame)(const char* spectateSecret);
void (*joinRequest)(const DiscordJoinRequest* request);
} DiscordEventHandlers;
#define DISCORD_REPLY_NO 0
#define DISCORD_REPLY_YES 1
#define DISCORD_REPLY_IGNORE 2
DISCORD_EXPORT void Discord_Initialize(const char* applicationId,
DiscordEventHandlers* handlers,
int autoRegister,
const char* optionalSteamId);
DISCORD_EXPORT void Discord_Shutdown(void);
/* checks for incoming messages, dispatches callbacks */
DISCORD_EXPORT void Discord_RunCallbacks(void);
/* If you disable the lib starting its own io thread, you'll need to call this from your own */
#ifdef DISCORD_DISABLE_IO_THREAD
DISCORD_EXPORT void Discord_UpdateConnection(void);
#endif
DISCORD_EXPORT void Discord_UpdatePresence(const DiscordRichPresence* presence);
DISCORD_EXPORT void Discord_ClearPresence(void);
DISCORD_EXPORT void Discord_Respond(const char* userid, /* DISCORD_REPLY_ */ int reply);
DISCORD_EXPORT void Discord_UpdateHandlers(DiscordEventHandlers* handlers);
#ifdef __cplusplus
} /* extern "C" */
#endif
Binary file not shown.
Binary file not shown.
Binary file not shown.
-105
View File
@@ -1,105 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{A107C21C-418A-4697-BB10-20C3AA60E2E4}</ProjectGuid>
<RootNamespace>hidapi</RootNamespace>
<Keyword>Win32Proj</Keyword>
</PropertyGroup>
<Import Project="..\common_default.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<Import Project="..\common_default_macros.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<_ProjectFileVersion>15.0.26323.1</_ProjectFileVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)lib/</OutDir>
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)/</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)lib/</OutDir>
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)/</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>hidapi\hidapi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
<Link>
<AdditionalDependencies>setupapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
</Link>
<Lib>
<AdditionalDependencies>setupapi.lib</AdditionalDependencies>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<AdditionalIncludeDirectories>hidapi\hidapi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
<Link>
<AdditionalDependencies>setupapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
</Link>
<Lib>
<AdditionalDependencies>setupapi.lib</AdditionalDependencies>
</Lib>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="hidapi\windows\hid.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="hidapi\hidapi\hidapi.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
-27
View File
@@ -1,27 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="hidapi\windows\hid.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="hidapi\hidapi\hidapi.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>
-142
View File
@@ -1,142 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug Library|x64">
<Configuration>Debug Library</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release Library|x64">
<Configuration>Release Library</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{D6973076-9317-4EF2-A0B8-B7A18AC0713E}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>libpng</RootNamespace>
</PropertyGroup>
<Import Project="$(SolutionDir)\3rdparty\zlib.props" />
<Import Project="..\common_default.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<Import Project="..\common_default_macros.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'">
<LinkIncremental>false</LinkIncremental>
<CustomBuildBeforeTargets />
<TargetName>$(ProjectName)16</TargetName>
<OutDir>$(SolutionDir)lib/</OutDir>
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)/</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'">
<LinkIncremental>false</LinkIncremental>
<CustomBuildBeforeTargets />
<TargetName>$(ProjectName)16</TargetName>
<OutDir>$(SolutionDir)lib/</OutDir>
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)/</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>$(WarningLevel)</WarningLevel>
<MinimalRebuild>false</MinimalRebuild>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<Optimization>Disabled</Optimization>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<IntrinsicFunctions>true</IntrinsicFunctions>
<FunctionLevelLinking>true</FunctionLevelLinking>
<FloatingPointExceptions>false</FloatingPointExceptions>
<TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType>
<PrecompiledHeaderFile>pngpriv.h</PrecompiledHeaderFile>
<BrowseInformation>true</BrowseInformation>
<CompileAs>CompileAsC</CompileAs>
<StringPooling>true</StringPooling>
<DisableSpecificWarnings>$(DisableSpecificWarnings)</DisableSpecificWarnings>
<AdditionalIncludeDirectories>$(ZLibSrcDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<TreatWarningAsError>$(TreatWarningAsError)</TreatWarningAsError>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'">
<ClCompile>
<WarningLevel>$(WarningLevel)</WarningLevel>
<PrecompiledHeader>Use</PrecompiledHeader>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<FloatingPointExceptions>false</FloatingPointExceptions>
<TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType>
<PrecompiledHeaderFile>pngpriv.h</PrecompiledHeaderFile>
<BrowseInformation>true</BrowseInformation>
<CompileAs>CompileAsC</CompileAs>
<StringPooling>true</StringPooling>
<MinimalRebuild>false</MinimalRebuild>
<DisableSpecificWarnings>$(DisableSpecificWarnings)</DisableSpecificWarnings>
<AdditionalIncludeDirectories>$(ZLibSrcDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<TreatWarningAsError>$(TreatWarningAsError)</TreatWarningAsError>
<Optimization>Full</Optimization>
<WholeProgramOptimization>true</WholeProgramOptimization>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
<Lib>
<LinkTimeCodeGeneration>true</LinkTimeCodeGeneration>
</Lib>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include=".\libpng\png.c">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include=".\libpng\pngerror.c" />
<ClCompile Include=".\libpng\pngget.c" />
<ClCompile Include=".\libpng\pngmem.c" />
<ClCompile Include=".\libpng\pngpread.c" />
<ClCompile Include=".\libpng\pngread.c" />
<ClCompile Include=".\libpng\pngrio.c" />
<ClCompile Include=".\libpng\pngrtran.c" />
<ClCompile Include=".\libpng\pngrutil.c" />
<ClCompile Include=".\libpng\pngset.c" />
<ClCompile Include=".\libpng\pngtrans.c" />
<ClCompile Include=".\libpng\pngwio.c" />
<ClCompile Include=".\libpng\pngwrite.c" />
<ClCompile Include=".\libpng\pngwtran.c" />
<ClCompile Include=".\libpng\pngwutil.c" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include=".\libpng\scripts\pngwin.rc">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'">true</ExcludedFromBuild>
</ResourceCompile>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
-1
Submodule 3rdparty/libusb deleted from 7cfa00e9d7
-90
View File
@@ -1,90 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectName>libusb-1.0 (static)</ProjectName>
<ProjectGuid>{349EE8F9-7D25-4909-AAF5-FF3FADE72187}</ProjectGuid>
<RootNamespace>libusb</RootNamespace>
</PropertyGroup>
<Import Project="..\common_default.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<Import Project="..\common_default_macros.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<CharacterSet>Unicode</CharacterSet>
<WholeProgramOptimization Condition="'$(Configuration)'=='Release'">true</WholeProgramOptimization>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
<OutDir>$(SolutionDir)lib/</OutDir>
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)/</IntDir>
<TargetName>libusb-1.0</TargetName>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>libusb\msvc;libusb\libusb;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WINVER=0x0501;_WIN32_WINNT=0x0501;_LIB;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
<WarningLevel>Level4</WarningLevel>
</ClCompile>
<ClCompile Condition="'$(Configuration)'=='Debug'">
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<Optimization>Disabled</Optimization>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<ClCompile Condition="'$(Configuration)'=='Release'">
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Lib>
<OutputFile>$(OutDir)libusb-1.0.lib</OutputFile>
</Lib>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="libusb\libusb\core.c" />
<ClCompile Include="libusb\libusb\descriptor.c" />
<ClCompile Include="libusb\libusb\hotplug.c" />
<ClCompile Include="libusb\libusb\io.c" />
<ClCompile Include="libusb\libusb\os\poll_windows.c" />
<ClCompile Include="libusb\libusb\strerror.c" />
<ClCompile Include="libusb\libusb\sync.c" />
<ClCompile Include="libusb\libusb\os\threads_windows.c" />
<ClCompile Include="libusb\libusb\os\windows_nt_common.c" />
<ClCompile Include="libusb\libusb\os\windows_usbdk.c" />
<ClCompile Include="libusb\libusb\os\windows_winusb.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="libusb\msvc\config.h" />
<ClInclude Include="libusb\libusb\hotplug.h" />
<ClInclude Include="libusb\libusb\libusb.h" />
<ClInclude Include="libusb\libusb\libusbi.h" />
<ClInclude Include="libusb\libusb\os\poll_windows.h" />
<ClInclude Include="libusb\libusb\os\threads_windows.h" />
<ClInclude Include="libusb\libusb\version.h" />
<ClInclude Include="libusb\libusb\version_nano.h" />
<ClInclude Include="libusb\libusb\os\windows_common.h" />
<ClInclude Include="libusb\libusb\os\windows_nt_common.h" />
<ClInclude Include="libusb\libusb\os\windows_nt_shared_types.h" />
<ClInclude Include="libusb\libusb\os\windows_usbdk.h" />
<ClInclude Include="libusb\libusb\os\windows_winusb.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
-65
View File
@@ -1,65 +0,0 @@
if(WITH_LLVM)
if(BUILD_LLVM_SUBMODULE)
message(STATUS "LLVM will be built from the submodule.")
set(LLVM_TARGETS_TO_BUILD "X86" CACHE INTERNAL "")
option(LLVM_BUILD_RUNTIME OFF)
option(LLVM_BUILD_TOOLS OFF)
option(LLVM_INCLUDE_DOCS OFF)
option(LLVM_INCLUDE_EXAMPLES OFF)
option(LLVM_INCLUDE_TESTS OFF)
option(LLVM_INCLUDE_TOOLS OFF)
option(LLVM_INCLUDE_UTILS OFF)
option(WITH_POLLY OFF)
option(LLVM_ENABLE_CXX1Z TRUE)
set(CXX_FLAGS_OLD ${CMAKE_CXX_FLAGS})
if (MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS")
endif()
# LLVM needs to be built out-of-tree
add_subdirectory(${CMAKE_SOURCE_DIR}/llvm ${CMAKE_CURRENT_BINARY_DIR}/llvm_build EXCLUDE_FROM_ALL)
set(LLVM_DIR "${CMAKE_CURRENT_BINARY_DIR}/llvm_build/lib/cmake/llvm/")
set(CMAKE_CXX_FLAGS ${CXX_FLAGS_OLD})
# now tries to find LLVM again
find_package(LLVM 9.0 CONFIG)
if(NOT LLVM_FOUND)
message(FATAL_ERROR "Couldn't build LLVM from the submodule. You might need to run `git submodule update --init`")
endif()
else()
message(STATUS "Using prebuilt LLVM")
if (LLVM_DIR AND NOT IS_ABSOLUTE "${LLVM_DIR}")
# change relative LLVM_DIR to be relative to the source dir
set(LLVM_DIR ${CMAKE_SOURCE_DIR}/${LLVM_DIR})
endif()
find_package(LLVM 9.0 CONFIG)
if (NOT LLVM_FOUND)
if (LLVM_VERSION AND LLVM_VERSION_MAJOR LESS 9)
message(FATAL_ERROR "Found LLVM version ${LLVM_VERSION}. Required version 9.0. \
Enable BUILD_LLVM_SUBMODULE option to build LLVM from included as a git submodule.")
endif()
message(FATAL_ERROR "Can't find LLVM libraries from the CMAKE_PREFIX_PATH path or LLVM_DIR. \
Enable BUILD_LLVM_SUBMODULE option to build LLVM from included as a git submodule.")
endif()
endif()
set(LLVM_LIBS LLVMMCJIT LLVMX86CodeGen LLVMX86AsmParser)
add_library(3rdparty_llvm INTERFACE)
target_link_libraries(3rdparty_llvm INTERFACE ${LLVM_LIBS})
target_include_directories(3rdparty_llvm INTERFACE ${LLVM_INCLUDE_DIRS})
target_compile_definitions(3rdparty_llvm INTERFACE ${LLVM_DEFINITIONS} -DLLVM_AVAILABLE)
add_library(3rdparty::llvm ALIAS 3rdparty_llvm)
else()
add_library(3rdparty::llvm ALIAS 3rdparty_dummy_lib)
endif()
-64
View File
@@ -1,64 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{EB33566E-DA7F-4D28-9077-88C0B7C77E35}</ProjectGuid>
<RootNamespace>pnglibconf</RootNamespace>
</PropertyGroup>
<Import Project="..\common_default.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<Import Project="..\common_default_macros.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(SolutionDir)\3rdparty\zlib.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<CustomBuildBeforeTargets>Build</CustomBuildBeforeTargets>
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>$(WarningLevel)</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
<CustomBuildStep>
<Command>copy .\libpng\scripts\pnglibconf.h.prebuilt .\libpng\pnglibconf.h</Command>
</CustomBuildStep>
<CustomBuildStep>
<Message>Generating pnglibconf.h</Message>
</CustomBuildStep>
<CustomBuildStep>
<Outputs>.\libpng\pnglibconf.h</Outputs>
</CustomBuildStep>
<CustomBuildStep>
<Inputs>.\libpng\scripts\pnglibconf.h.prebuilt</Inputs>
</CustomBuildStep>
</ItemDefinitionGroup>
<ItemGroup>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
-49
View File
@@ -1,49 +0,0 @@
add_library(3rdparty_qt5 INTERFACE)
find_package(Qt5 5.10 CONFIG COMPONENTS Widgets Network Qml)
if(WIN32)
find_package(Qt5 5.10 COMPONENTS WinExtras REQUIRED)
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::WinExtras Qt5::Network Qt5::Qml)
else()
find_package(Qt5 5.10 COMPONENTS DBus Gui)
if(Qt5DBus_FOUND)
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::DBus Qt5::Network Qt5::Qml)
target_compile_definitions(3rdparty_qt5 INTERFACE -DHAVE_QTDBUS)
else()
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::Network Qt5::Qml)
endif()
target_include_directories(3rdparty_qt5 INTERFACE ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
endif()
if(NOT Qt5Widgets_FOUND)
if(Qt5Widgets_VERSION VERSION_LESS 5.10.0)
message("Minimum supported Qt5 version is 5.10.0! You have version ${Qt5Widgets_VERSION} installed, please upgrade!")
if(CMAKE_SYSTEM MATCHES "Linux")
message(FATAL_ERROR "Most distros do not provide an up-to-date version of Qt.
If you're on Ubuntu or Linux Mint, there are PPAs you can use to install one of the latest qt5 versions.
https://launchpad.net/~beineri/+archive/ubuntu/opt-qt-5.11.0-bionic
https://launchpad.net/~beineri/+archive/ubuntu/opt-qt-5.11.0-xenial
https://launchpad.net/~beineri/+archive/ubuntu/opt-qt-5.10.1-trusty
just make sure to run
source /opt/qt511/bin/qt511-env.sh
respective
source /opt/qt510/bin/qt510-env.sh
before re-running cmake")
elseif(WIN32)
message(FATAL_ERROR "You can download the latest version of Qt5 here: https://www.qt.io/download-open-source/")
else()
message(FATAL_ERROR "Look online for instructions on installing an up-to-date Qt5 on ${CMAKE_SYSTEM}.")
endif()
endif()
message("CMake was unable to find Qt5!")
if(WIN32)
message(FATAL_ERROR "Make sure the QTDIR env variable has been set properly. (for example C:\\Qt\\5.11.1\\msvc2017_64\\)")
elseif(CMAKE_SYSTEM MATCHES "Linux")
message(FATAL_ERROR "Make sure to install your distro's qt5 package!")
else()
message(FATAL_ERROR "You need to have Qt5 installed, look online for instructions on installing Qt5 on ${CMAKE_SYSTEM}.")
endif()
endif()
add_library(3rdparty::qt5 ALIAS 3rdparty_qt5)
+4672 -5521
View File
File diff suppressed because it is too large Load Diff
+2316 -3897
View File
File diff suppressed because it is too large Load Diff
-1
Submodule 3rdparty/xxHash deleted from 7cc9639699
-59
View File
@@ -1,59 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{939FE206-1182-ABC3-1234-FEAB88E98404}</ProjectGuid>
</PropertyGroup>
<Import Project="..\common_default.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<Import Project="..\common_default_macros.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\rpcs3_default.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="..\rpcs3_debug.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="..\rpcs3_release.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<ItemDefinitionGroup>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="xxHash/xxhash.c" />
</ItemGroup>
<ItemGroup>
<Text Include="LICENSE" />
<Text Include="CMakeLists.txt" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="xxHash/xxhash.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
Submodule 3rdparty/yaml-cpp deleted from eca9cfd648
-105
View File
@@ -1,105 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClCompile Include="$(ZLibSrcDir)\adler32.c" />
<ClCompile Include="$(ZLibSrcDir)\compress.c" />
<ClCompile Include="$(ZLibSrcDir)\crc32.c" />
<ClCompile Include="$(ZLibSrcDir)\deflate.c" />
<ClCompile Include="$(ZLibSrcDir)\infback.c" />
<ClCompile Include="$(ZLibSrcDir)\inffast.c" />
<ClCompile Include="$(ZLibSrcDir)\inflate.c" />
<ClCompile Include="$(ZLibSrcDir)\inftrees.c" />
<ClCompile Include="$(ZLibSrcDir)\trees.c" />
<ClCompile Include="$(ZLibSrcDir)\uncompr.c" />
<ClCompile Include="$(ZLibSrcDir)\zutil.c" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{60F89955-91C6-3A36-8000-13C592FEC2DF}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>zlib</RootNamespace>
</PropertyGroup>
<Import Project="$(SolutionDir)\3rdparty\zlib.props" />
<Import Project="..\common_default.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<Import Project="..\common_default_macros.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)lib\$(Configuration)-$(Platform)\</OutDir>
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)lib\$(Configuration)-$(Platform)\</OutDir>
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<WarningLevel>$(WarningLevel)</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<Optimization>Disabled</Optimization>
<BrowseInformation>true</BrowseInformation>
<FunctionLevelLinking>true</FunctionLevelLinking>
<DisableSpecificWarnings>$(DisableSpecificWarnings);4127;4131;4242;4244</DisableSpecificWarnings>
<TreatWarningAsError>$(TreatWarningAsError)</TreatWarningAsError>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>$(WarningLevel)</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<Optimization>Full</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<WholeProgramOptimization>true</WholeProgramOptimization>
<BufferSecurityCheck>false</BufferSecurityCheck>
<BrowseInformation>true</BrowseInformation>
<FunctionLevelLinking>true</FunctionLevelLinking>
<DisableSpecificWarnings>$(DisableSpecificWarnings);4127;4131;4242;4244</DisableSpecificWarnings>
<TreatWarningAsError>$(TreatWarningAsError)</TreatWarningAsError>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
<Lib>
<LinkTimeCodeGeneration>true</LinkTimeCodeGeneration>
</Lib>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
-116
View File
@@ -1,116 +0,0 @@
# Building
Only Windows and Linux are officially supported for building. However, various other platforms are capable of building RPCS3.
Other instructions may be found [here](https://wiki.rpcs3.net/index.php?title=Building).
## Setup your environment
### Windows 7 or later
* [CMake 3.8.2+](https://www.cmake.org/download/) (add to PATH)
* [Python 3.3+](https://www.python.org/downloads/) (add to PATH)
* [Qt 5.10+](https://www.qt.io/download-qt-installer) (Avoid 5.11.1, due to a bug)
* [Visual Studio 2017](https://visualstudio.microsoft.com/vs/older-downloads/)
* [Vulkan SDK 1.1.97.0+](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/windows/getting_started.html))
**Either add the** `QTDIR` **environment variable, e.g.** `<QtInstallFolder>\5.10.0\msvc2017_64\` **, or use the [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools-19123)**
### Linux
These are the essentials tools to build RPCS3 on Linux. Some of them can be installed through your favorite package manager.
* Clang 5.0+ or GCC 8.1+
* [CMake 3.8.2+](https://www.cmake.org/download/)
* [Qt 5.10+](https://www.qt.io/download-qt-installer) (Avoid 5.11.1, due to a bug)
* [Vulkan SDK 1.1.97.0+](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/linux/getting_started.html))
**If you have an NVIDIA GPU, you may need to install the libglvnd package.**
#### Arch Linux
sudo pacman -S glew openal cmake vulkan-validation-layers qt5-base qt5-declarative
#### Debian & Ubuntu
sudo apt-get install cmake build-essential libasound2-dev libpulse-dev libopenal-dev libglew-dev zlib1g-dev libedit-dev libvulkan-dev libudev-dev git qt5-default libevdev-dev qtdeclarative5-dev qtbase5-private-dev
##### GCC 8.x installation
If the `gcc-8` package is not available on your system, use the following command
```
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
```
Then
```
sudo apt-get update
sudo apt-get install gcc-8 g++-8
```
You can either use `update-alternatives` to setup `gcc-8`/`g++-8` as your default compilers or prefix any `cmake` command by `CXX=g++-8 CC=gcc-8 ` to use it.
##### Vulkan SDK
For Ubuntu systems, it is strongly recommended to use the PPA from [LunarG](https://packages.lunarg.com/) which will provide a compatible Vulkan SDK to compile RPCS3. If your Vulkan SDK is older, it can lead to compilation errors.
Ubuntu 18.04 (Bionic Beaver)
```
wget -qO - http://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add -
sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-1.1.106-bionic.list http://packages.lunarg.com/vulkan/1.1.106/lunarg-vulkan-1.1.106-bionic.list
sudo apt update
sudo apt install vulkan-sdk
```
Ubuntu 16.04 (Xenial Xerus)
```
wget -qO - http://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add -
sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-1.1.106-xenial.list http://packages.lunarg.com/vulkan/1.1.106/lunarg-vulkan-1.1.106-xenial.list
sudo apt update
sudo apt install vulkan-sdk
```
#### Fedora
sudo dnf install alsa-lib-devel cmake glew glew-devel libatomic libevdev-devel libudev-devel openal-devel qt5-devel vulkan-devel
#### OpenSUSE
sudo zypper install git cmake libasound2 libpulse-devel openal-soft-devel glew-devel zlib-devel libedit-devel vulkan-devel libudev-devel libqt5-qtbase-devel libevdev-devel
## Setup the project
Clone and initialize the repository
```
git clone https://github.com/RPCS3/rpcs3.git
cd rpcs3
git submodule update --init
```
### Windows
#### Configuring the Qt plugin (if used)
1) Go to the Qt5 menu and edit Qt5 options.
2) Add the path to your Qt installation with compiler e.g. `<QtInstallFolder>\5.10.0\msvc2017_64`.
3) While selecting the rpcs3qt project, go to Qt5->Project Setting and select the version you added.
#### Building the projects
Open `rpcs3.sln`. The recommended build configuration is `Release - LLVM` for all purposes.
You may want to download precompiled [LLVM libs](https://github.com/RPCS3/llvm/releases/download/continuous-master/llvmlibs.7z) and extract to root rpcs3 folder (which contains `rpcs3.sln`), as well as download and extract [additional libs](https://drive.google.com/uc?export=download&id=1A2eOMmCO714i0U7J0qI4aEMKnuWl8l_R) to `lib\%CONFIGURATION%-x64\` to speed up compilation time (unoptimised/debug libs are currently not available precompiled).
If you're not using precompiled libs, build the projects in *__BUILD_BEFORE* folder: right-click on every project > *Build*.
`Build > Build Solution`
### Linux
While still in the project root:
1) `cd .. && mkdir rpcs3_build && cd rpcs3_build`
2) `cmake ../rpcs3/ && make` or `CXX=g++-8 CC=gcc-8 cmake ../rpcs3/ && make` to force these compilers
3) Run RPCS3 with `./bin/rpcs3`
When using GDB, configure it to ignore SIGSEGV signal (`handle SIGSEGV nostop noprint`).
If desired, use the various build options in [CMakeLists](https://github.com/RPCS3/rpcs3/blob/master/CMakeLists.txt).
+29 -59
View File
@@ -1,46 +1,19 @@
cmake_minimum_required(VERSION 3.8.2)
project(rpcs3)
if(CMAKE_COMPILER_IS_GNUCXX)
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8)
message(FATAL_ERROR "RPCS3 requires at least gcc-8.")
endif()
elseif(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
message(FATAL_ERROR "RPCS3 requires at least clang-5.0.")
endif()
endif()
cmake_minimum_required(VERSION 3.0.2)
option(WITH_GDB "WITH_GDB" OFF)
option(USE_NATIVE_INSTRUCTIONS "USE_NATIVE_INSTRUCTIONS makes rpcs3 compile with -march=native, which is useful for local builds, but not good for packages." ON)
option(WITH_LLVM "Enable usage of LLVM library" ON)
option(BUILD_LLVM_SUBMODULE "Build LLVM from git submodule" ON)
option(USE_ALSA "ALSA audio backend" ON)
option(USE_PULSE "PulseAudio audio backend" ON)
option(USE_LIBEVDEV "libevdev-based joystick support" ON)
option(USE_DISCORD_RPC "Discord rich presence integration" ON)
option(USE_SYSTEM_ZLIB "Prefer system ZLIB instead of the builtin one" ON)
option(USE_VULKAN "Vulkan render backend" ON)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/rpcs3/cmake_modules")
set(CMAKE_CXX_STANDARD 17)
include(CheckCXXCompilerFlag)
option(WITHOUT_LLVM "WITHOUT_LLVM" OFF)
if (WITH_GDB)
add_definitions(-DWITH_GDB_DEBUGGER)
endif()
set(ASMJIT_STATIC TRUE)
if (NOT CMAKE_BUILD_TYPE)
message(STATUS "No build type selected, default to Release")
set(CMAKE_BUILD_TYPE "Release")
endif()
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
add_definitions(-DNDEBUG)
endif()
if(NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
message( FATAL_ERROR "RPCS3 can only be compiled on 64-bit platforms." )
endif()
@@ -51,39 +24,36 @@ if (CCACHE_FOUND)
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)
endif()
if(WIN32)
add_definitions(-DUNICODE)
add_definitions(-D_WIN32_WINNT=0x0602)
add_definitions(-DCMAKE_BUILD)
# We use libpng's static library and don't need to build the shared library and run the tests
set(PNG_SHARED OFF CACHE BOOL "Build shared lib." FORCE)
set(PNG_TESTS OFF CACHE BOOL "Build tests." FORCE)
# Select the version of libpng to use, default is builtin
if (NOT USE_SYSTEM_LIBPNG)
add_subdirectory( 3rdparty/libpng )
endif()
if(APPLE)
include_directories(/opt/local/include)
link_directories(/opt/local/lib)
endif()
# Warnings are silenced for 3rdparty code
set(CMAKE_CXX_FLAGS -w)
set(CMAKE_C_FLAGS -w)
set(LLVM_ENABLE_WARNINGS OFF CACHE BOOL "")
if(MSVC)
add_compile_options(/wd4530) # C++ exception handler used, but unwind semantics are not enabled
endif()
add_subdirectory(Vulkan EXCLUDE_FROM_ALL)
add_subdirectory(asmjitsrc EXCLUDE_FROM_ALL)
add_subdirectory(3rdparty)
unset(CMAKE_CXX_FLAGS)
unset(CMAKE_C_FLAGS)
if (NOT WIN32)
add_compile_options(-pthread)
endif()
option(VULKAN_PREBUILT "" OFF)
# TODO: do real installation, including copying directory structure
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${PROJECT_BINARY_DIR}/bin")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${PROJECT_BINARY_DIR}/bin")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${PROJECT_BINARY_DIR}/bin")
add_subdirectory(rpcs3)
add_subdirectory( Vulkan )
add_subdirectory( rpcs3 )
include_directories(3rdparty/hidapi/hidapi)
if(APPLE)
add_subdirectory(3rdparty/hidapi/mac)
#list(APPEND LIBS hidapi)
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
add_subdirectory(3rdparty/hidapi/linux)
elseif(MSVC)
add_subdirectory(3rdparty/hidapi/windows)
else()
add_subdirectory(3rdparty/hidapi/libusb)
#list(APPEND LIBS hidapi-libusb)
endif()
+75 -17
View File
@@ -4,36 +4,94 @@ RPCS3
[![Build Status](https://travis-ci.org/RPCS3/rpcs3.svg?branch=master)](https://travis-ci.org/RPCS3/rpcs3)
[![Build status](https://ci.appveyor.com/api/projects/status/411c4clmiohtx7eo/branch/master?svg=true)](https://ci.appveyor.com/project/rpcs3/rpcs3/branch/master)
The world's first free and open-source PlayStation 3 emulator/debugger, written in C++ for Windows and Linux.
The world's first open-source PlayStation 3 emulator/debugger written in C++ for Windows and Linux.
You can find some basic information on our [**website**](https://rpcs3.net/). Game info is being populated on the [**Wiki**](https://wiki.rpcs3.net/).
For discussion about this emulator, PS3 emulation, and game compatibility reports, please visit our [**forums**](https://forums.rpcs3.net) and our [**Discord server**](https://discord.me/RPCS3).
You can find some basic information in our [**website**](https://rpcs3.net/).
For discussion about this emulator and PS3 emulation please visit our [**forums**](https://forums.rpcs3.net) and our [**Discord server**](https://discord.me/RPCS3).
[**Support Lead Developers Nekotekina and kd-11 on Patreon**](https://www.patreon.com/Nekotekina)
## Contributing
If you want to help the project but do not code, the best way to help out is to test games and make bug reports. See:
* [Quickstart](https://rpcs3.net/quickstart)
## Development
If you want to contribute as a developer, please take a look at the following pages:
If you want to contribute please take a look at the [Coding Style](https://github.com/RPCS3/rpcs3/wiki/Coding-Style), [Roadmap](https://github.com/RPCS3/rpcs3/wiki/Roadmap) and [Developer Information](https://github.com/RPCS3/rpcs3/wiki/Developer-Information) pages. You should as well contact any of the developers in the forums or in Discord in order to know more about the current situation of the emulator.
* [Coding Style](https://github.com/RPCS3/rpcs3/wiki/Coding-Style)
* [Developer Information](https://github.com/RPCS3/rpcs3/wiki/Developer-Information)
* [Roadmap](https://rpcs3.net/roadmap)
You should also contact any of the developers in the forums or in the Discord server to learn more about the current state of the emulator.
## Dependencies
## Building
### Windows
* [Visual Studio 2017](https://www.visualstudio.com/en/downloads/)
* [Visual C++ Redistributable Packages for Visual Studio 2017](https://go.microsoft.com/fwlink/?LinkId=746572)
* [Cmake 3.1.0+](https://www.cmake.org/download/) (required; add to PATH)
* [Python 3.3+](https://www.python.org/downloads/) (required; add to PATH)
* [Qt 5.9+](https://www.qt.io/download-open-source/) (required; add QTDIR `<QtInstallFolder>\5.9.1\msvc2015_64\` environment variable if you do not want to use the Visual Studio Qt Plugin)
* [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2015) (optional; see above)
See [BUILDING.md](BUILDING.md) for more information about how to setup an environment to build RPCS3.
### Linux
* [Qt 5.9+](https://www.qt.io/download-open-source/)
* GCC 5.1+ or Clang 3.5.0+ ([not GCC 6.1](https://github.com/RPCS3/rpcs3/issues/1691))
* Debian & Ubuntu: `sudo apt-get install cmake build-essential libasound2-dev libopenal-dev libglew-dev zlib1g-dev libedit-dev libvulkan-dev libudev-dev git qt5-default`
* Arch: `sudo pacman -S glew openal cmake llvm qt5-base`
* Fedora: `sudo dnf install cmake qt5-devel vulkan-devel glew glew-devel`
**If you have a NVIDIA GPU, you may need to install the libglvnd package.**
## Running
### MacOS
MacOS is not supported at this moment because it doesn't meet system requirements (OpenGL 4.3)
* Xcode 6+ (tested with Xcode 6.4)
* Install with Homebrew: `brew install glew llvm qt cmake`
Check our friendly [quickstart](https://rpcs3.net/quickstart) guide to make sure your computer meets the minimum system requirements to run RPCS3.
Don't forget to have your graphics driver up to date and to install the [Visual C++ Redistributable Packages for Visual Studio 2017](https://go.microsoft.com/fwlink/?LinkId=746572) if you are a Windows user.
## Building on Windows:
To initialize the repository don't forget to execute `git submodule update --init` to pull the submodules.
### Configuring Qt
*If you're using Visual Studio 2017 without Qt plugin support (or simply dont want to use it):*
1) Add `QTDIR` environment variable and set it to `<QtInstallFolder>\5.9.1\msvc2017_64\` </br>
*If you wish to use the Visual Studio plugin for Qt:* </br>
1) Go to the Qt5 menu and edit Qt5 options. Add the path to your Qt installation with compiler e.g. `C:\Qt\5.9.1\msvc2015_64`. </br>
2) While selecting the rpcs3qt project, go to Qt5->Project Setting and select the version you added.
### Building the projects
1) Build the projects in *__BUILD_BEFORE* folder: right-click on every project > *Build*. </br>
2) Press *BUILD* > *Build Solution* or *Rebuild Solution*. </br>
## Building on Linux & Mac OS:
1) `git clone https://github.com/RPCS3/rpcs3.git` </br>
2) `cd rpcs3/` </br>
3) `git submodule update --init` </br>
4) `cd ../ && mkdir rpcs3_build && cd rpcs3_build`
4) `cmake ../rpcs3/ && make GitVersion && make` </br>
5) Run RPCS3 with `./bin/rpcs3` </br>
If you are on MacOS and want to build with brew llvm and qt don't forget to add the following environment variables
* `LLVM_DIR=/usr/local/opt/llvm/` (or wherever llvm was installed).
* `Qt5_DIR=/usr/local/opt/qt/lib/cmake/Qt5` (or wherever qt was installed).
When using GDB, configure it to ignore SIGSEGV signal (`handle SIGSEGV nostop noprint`).
## CMake Build Options (Linux & Mac OS)
- ```-DUSE_SYSTEM_LIBPNG=ON/OFF``` (default = *OFF*)
Build against the shared libpng instead of using the builtin one. libpng 1.6+ highly recommended. Try this option if you get version conflict errors or only see black game icons.
- ```-DUSE_SYSTEM_FFMPEG=ON/OFF``` (default = *OFF*)
Build against the shared ffmpeg libraries instead of using the builtin patched version. Try this if the builtin version breaks the OpenGL renderer for you.
- ```-DWITHOUT_LLVM=ON/OFF``` (default = *OFF*)
This forces RPCS3 to build without LLVM, not recommended.
- ```-DWITH_GDB=ON/OFF``` (default = *OFF*)
This Builds RPCS3 with support for debugging PS3 games using gdb.
- ```-DUSE_VULKAN=ON/OFF``` (default = *ON*)
This builds RPCS3 with Vulkan support.
## License
Most files are licensed under the terms of GNU GPLv2 License; see LICENSE file for details. Some files may be licensed differently; check appropriate file headers for details.
Most files are licensed under the terms of GNU GPLv2 License, see LICENSE file for details. Some files may be licensed differently, check appropriate file headers for details.
File diff suppressed because it is too large Load Diff
+1 -2
View File
@@ -1,8 +1,7 @@
#pragma once
#include "util/atomic.hpp"
#include "Atomic.h"
#include <memory>
#include <cstddef>
// Unfinished. Only std::default_delete will work as expected.
template<typename T, typename D>
+49 -30
View File
@@ -1,7 +1,6 @@
#pragma once
#include "types.h"
#include <cstring>
// 128-bit vector type and also se_storage<> storage type
union alignas(16) v128
@@ -308,7 +307,7 @@ struct offset32_array<v128::masked_array_t<T, N, M>>
template <typename Arg>
static inline u32 index32(const Arg& arg)
{
return u32{sizeof(T)} * (static_cast<u32>(arg) ^ static_cast<u32>(M));
return SIZE_32(T) * (static_cast<u32>(arg) ^ static_cast<u32>(M));
}
};
@@ -335,10 +334,7 @@ inline v128 operator~(const v128& other)
template <typename T, std::size_t Align, std::size_t Size>
struct se_storage
{
struct type
{
alignas(Align) std::byte data[Size];
};
using type = std::aligned_storage_t<Size, Align>;
// Unoptimized generic byteswap for unaligned data
static void reverse(u8* dst, const u8* src)
@@ -362,6 +358,16 @@ struct se_storage
reverse(reinterpret_cast<u8*>(&result), reinterpret_cast<const u8*>(&src));
return result;
}
static type copy(const type& src)
{
type result;
for (std::size_t i = 0; i < Size; i++)
{
reinterpret_cast<u8*>(&result)[i] = reinterpret_cast<const u8*>(&src)[i];
}
return result;
}
};
template <typename T>
@@ -380,12 +386,18 @@ struct se_storage<T, 2, 2>
static inline u16 to(const T& src)
{
return swap(std::bit_cast<u16>(src));
return swap(reinterpret_cast<const u16&>(src));
}
static inline T from(u16 src)
{
return std::bit_cast<T, u16>(swap(src));
const u16 result = swap(src);
return reinterpret_cast<const T&>(result);
}
static inline T copy(const T& src)
{
return src;
}
};
@@ -405,12 +417,18 @@ struct se_storage<T, 4, 4>
static inline u32 to(const T& src)
{
return swap(std::bit_cast<u32>(src));
return swap(reinterpret_cast<const u32&>(src));
}
static inline T from(u32 src)
{
return std::bit_cast<T, u32>(swap(src));
const u32 result = swap(src);
return reinterpret_cast<const T&>(result);
}
static inline T copy(const T& src)
{
return src;
}
};
@@ -430,12 +448,18 @@ struct se_storage<T, 8, 8>
static inline u64 to(const T& src)
{
return swap(std::bit_cast<u64>(src));
return swap(reinterpret_cast<const u64&>(src));
}
static inline T from(u64 src)
{
return std::bit_cast<T, u64>(swap(src));
const u64 result = swap(src);
return reinterpret_cast<const T&>(result);
}
static inline T copy(const T& src)
{
return src;
}
};
@@ -446,17 +470,23 @@ struct se_storage<T, 16, 16>
static inline v128 swap(const v128& src)
{
return v128::from64(se_storage<u64>::swap(src._u64[1]), se_storage<u64>::swap(src._u64[0]));
return v128::fromV(_mm_shuffle_epi8(src.vi, _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)));
}
static inline v128 to(const T& src)
{
return swap(std::bit_cast<v128>(src));
return swap(reinterpret_cast<const v128&>(src));
}
static inline T from(const v128& src)
{
return std::bit_cast<T, v128>(swap(src));
const v128 result = swap(src);
return reinterpret_cast<const T&>(result);
}
static inline T copy(const T& src)
{
return src;
}
};
@@ -490,11 +520,6 @@ public:
return storage::from(m_data);
}
stype& raw()
{
return m_data;
}
se_t& operator=(const se_t&) = default;
se_t& operator=(type value)
@@ -529,33 +554,27 @@ public:
se_t() = default;
se_t(type value)
: m_data(std::bit_cast<stype>(value))
: m_data(reinterpret_cast<const stype&>(value))
{
}
type value() const
{
return std::bit_cast<type>(m_data);
}
stype& raw()
{
return m_data;
return storage::copy(reinterpret_cast<const type&>(m_data));
}
se_t& operator=(const se_t& value) = default;
se_t& operator=(type value)
{
m_data = std::bit_cast<stype>(value);
return *this;
return m_data = reinterpret_cast<const stype&>(value), *this;
}
using simple_type = simple_t<T>;
operator type() const
{
return value();
return storage::copy(reinterpret_cast<const type&>(m_data));
}
};
+4 -5
View File
@@ -1,7 +1,6 @@
#pragma once
#include "types.h"
#include <limits>
template<typename T, uint N>
struct bf_base
@@ -16,11 +15,11 @@ struct bf_base
// Field bitsize
static constexpr uint bitsize = N;
// All ones mask
static constexpr utype mask1 = std::numeric_limits<utype>::max();
// Value mask
static constexpr utype vmask = mask1 >> (bitmax - bitsize);
static constexpr utype vmask = static_cast<utype>(~utype{} >> (bitmax - bitsize));
// All ones mask
static constexpr utype mask1 = static_cast<utype>(~utype{});
protected:
type m_data;
-243
View File
@@ -1,243 +0,0 @@
#pragma once
#include <Utilities/types.h>
#ifdef _WIN32
#include "windows.h"
#include "tlhelp32.h"
#else
#include "stdlib.h"
#include "sys/times.h"
#include "sys/types.h"
#include "unistd.h"
#endif
#ifdef __APPLE__
# include <mach/mach_init.h>
# include <mach/task.h>
# include <mach/vm_map.h>
#endif
#ifdef __linux__
# include <dirent.h>
#endif
#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
# include <sys/sysctl.h>
# if defined(__DragonFly__) || defined(__FreeBSD__)
# include <sys/user.h>
# endif
# if defined(__NetBSD__)
# undef KERN_PROC
# define KERN_PROC KERN_PROC2
# define kinfo_proc kinfo_proc2
# endif
# if defined(__DragonFly__)
# define KP_NLWP(kp) (kp.kp_nthreads)
# elif defined(__FreeBSD__)
# define KP_NLWP(kp) (kp.ki_numthreads)
# elif defined(__NetBSD__)
# define KP_NLWP(kp) (kp.p_nlwps)
# endif
#endif
class CPUStats
{
#ifdef _WIN32
HANDLE m_self;
using time_type = ULARGE_INTEGER;
#else
using time_type = clock_t;
#endif
private:
s32 m_num_processors;
time_type m_last_cpu, m_sys_cpu, m_usr_cpu;
public:
CPUStats()
{
#ifdef _WIN32
SYSTEM_INFO sysInfo;
FILETIME ftime, fsys, fuser;
GetSystemInfo(&sysInfo);
m_num_processors = sysInfo.dwNumberOfProcessors;
GetSystemTimeAsFileTime(&ftime);
memcpy(&m_last_cpu, &ftime, sizeof(FILETIME));
m_self = GetCurrentProcess();
GetProcessTimes(m_self, &ftime, &ftime, &fsys, &fuser);
memcpy(&m_sys_cpu, &fsys, sizeof(FILETIME));
memcpy(&m_usr_cpu, &fuser, sizeof(FILETIME));
#else
struct tms timeSample;
m_last_cpu = times(&timeSample);
m_sys_cpu = timeSample.tms_stime;
m_usr_cpu = timeSample.tms_utime;
m_num_processors = sysconf(_SC_NPROCESSORS_ONLN);
#endif
}
double get_usage()
{
#ifdef _WIN32
FILETIME ftime, fsys, fusr;
ULARGE_INTEGER now, sys, usr;
GetSystemTimeAsFileTime(&ftime);
memcpy(&now, &ftime, sizeof(FILETIME));
GetProcessTimes(m_self, &ftime, &ftime, &fsys, &fusr);
memcpy(&sys, &fsys, sizeof(FILETIME));
memcpy(&usr, &fusr, sizeof(FILETIME));
double percent = double(sys.QuadPart - m_sys_cpu.QuadPart) + (usr.QuadPart - m_usr_cpu.QuadPart);
percent /= (now.QuadPart - m_last_cpu.QuadPart);
percent /= m_num_processors;
m_last_cpu = now;
m_usr_cpu = usr;
m_sys_cpu = sys;
return std::clamp(percent * 100, 0.0, 100.0);
#else
struct tms timeSample;
clock_t now;
double percent;
now = times(&timeSample);
if (now <= m_last_cpu || timeSample.tms_stime < m_sys_cpu || timeSample.tms_utime < m_usr_cpu)
{
// Overflow detection. Just skip this value.
percent = -1.0;
}
else
{
percent = (timeSample.tms_stime - m_sys_cpu) + (timeSample.tms_utime - m_usr_cpu);
percent /= (now - m_last_cpu);
percent /= m_num_processors;
percent *= 100;
}
m_last_cpu = now;
m_sys_cpu = timeSample.tms_stime;
m_usr_cpu = timeSample.tms_utime;
return percent;
#endif
}
static u32 get_thread_count()
{
#ifdef _WIN32
// first determine the id of the current process
DWORD const id = GetCurrentProcessId();
// then get a process list snapshot.
HANDLE const snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
// initialize the process entry structure.
PROCESSENTRY32 entry = {0};
entry.dwSize = sizeof(entry);
// get the first process info.
BOOL ret = true;
ret = Process32First(snapshot, &entry);
while (ret && entry.th32ProcessID != id)
{
ret = Process32Next(snapshot, &entry);
}
CloseHandle(snapshot);
return ret ? entry.cntThreads : 0;
#elif defined(__APPLE__)
const task_t task = mach_task_self();
mach_msg_type_number_t thread_count;
thread_act_array_t thread_list;
if (task_threads(task, &thread_list, &thread_count) != KERN_SUCCESS)
{
return 0;
}
vm_deallocate(task, reinterpret_cast<vm_address_t>(thread_list),
sizeof(thread_t) * thread_count);
return static_cast<u32>(thread_count);
#elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__)
int mib[] = {
CTL_KERN,
KERN_PROC,
KERN_PROC_PID,
getpid(),
#if defined(__NetBSD__)
sizeof(struct kinfo_proc),
1,
#endif
};
u_int miblen = std::size(mib);
struct kinfo_proc info;
size_t size = sizeof(info);
if (sysctl(mib, miblen, &info, &size, NULL, 0))
{
return 0;
}
return KP_NLWP(info);
#elif defined(__OpenBSD__)
int mib[] = {
CTL_KERN,
KERN_PROC,
KERN_PROC_PID | KERN_PROC_SHOW_THREADS,
getpid(),
sizeof(struct kinfo_proc),
0,
};
u_int miblen = std::size(mib);
// get number of structs
size_t size;
if (sysctl(mib, miblen, NULL, &size, NULL, 0))
{
return 0;
}
mib[5] = size / mib[4];
// populate array of structs
struct kinfo_proc info[mib[5]];
if (sysctl(mib, miblen, &info, &size, NULL, 0))
{
return 0;
}
// exclude empty members
u32 thread_count{0};
for (int i = 0; i < size / mib[4]; i++)
{
if (info[i].p_tid != -1)
++thread_count;
}
return thread_count;
#elif defined(__linux__)
u32 thread_count{0};
DIR* proc_dir = opendir("/proc/self/task");
if (proc_dir)
{
// proc available, iterate through tasks and count them
struct dirent* entry;
while ((entry = readdir(proc_dir)) != NULL)
{
if (entry->d_name[0] == '.')
continue;
++thread_count;
}
closedir(proc_dir);
}
return thread_count;
#else
// unimplemented
return 0;
#endif
}
};
+11 -12
View File
@@ -7,6 +7,8 @@
namespace cfg
{
logs::channel cfg("CFG");
_base::_base(type _type)
: m_type(_type)
{
@@ -65,19 +67,19 @@ bool cfg::try_to_int64(s64* out, const std::string& value, s64 min, s64 max)
}
catch (const std::exception& e)
{
if (out) LOG_ERROR(GENERAL, "cfg::try_to_int('%s'): exception: %s", value, e.what());
if (out) cfg.error("cfg::try_to_int('%s'): exception: %s", value, e.what());
return false;
}
if (pos != value.size())
{
if (out) LOG_ERROR(GENERAL, "cfg::try_to_int('%s'): unexpected characters (pos=%zu)", value, pos);
if (out) cfg.error("cfg::try_to_int('%s'): unexpected characters (pos=%zu)", value, pos);
return false;
}
if (result < min || result > max)
{
if (out) LOG_ERROR(GENERAL, "cfg::try_to_int('%s'): out of bounds (%lld..%lld)", value, min, max);
if (out) cfg.error("cfg::try_to_int('%s'): out of bounds (%lld..%lld)", value, min, max);
return false;
}
@@ -117,13 +119,13 @@ bool cfg::try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) f
if (pos != value.size())
{
if (out) LOG_ERROR(GENERAL, "cfg::try_to_enum_value('%s'): unexpected characters (pos=%zu)", value, pos);
if (out) cfg.error("cfg::try_to_enum_value('%s'): unexpected characters (pos=%zu)", value, pos);
return false;
}
if (val > max)
{
if (out) LOG_ERROR(GENERAL, "cfg::try_to_enum_value('%s'): out of bounds(0..%u)", value, max);
if (out) cfg.error("cfg::try_to_enum_value('%s'): out of bounds(0..%u)", value, max);
return false;
}
@@ -132,7 +134,7 @@ bool cfg::try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) f
}
catch (const std::exception& e)
{
if (out) LOG_ERROR(GENERAL, "cfg::try_to_enum_value('%s'): invalid enum value: %s", value, e.what());
if (out) cfg.error("cfg::try_to_enum_value('%s'): invalid enum value: %s", value, e.what());
return false;
}
}
@@ -200,12 +202,9 @@ void cfg::encode(YAML::Emitter& out, const cfg::_base& rhs)
out << YAML::EndMap;
return;
}
default:
{
out << rhs.to_string();
return;
}
}
out << rhs.to_string();
}
void cfg::decode(const YAML::Node& data, cfg::_base& rhs)
@@ -298,7 +297,7 @@ bool cfg::node::from_string(const std::string& value) try
}
catch (const std::exception& e)
{
LOG_FATAL(GENERAL, "%s thrown: %s", typeid(e).name(), e.what());
cfg.fatal("%s thrown: %s", typeid(e).name(), e.what());
return false;
}
+7 -37
View File
@@ -1,4 +1,4 @@
#pragma once
#pragma once
#include "Utilities/types.h"
#include "Utilities/StrFmt.h"
@@ -49,10 +49,9 @@ namespace cfg
_base(type _type, class node* owner, const std::string& name);
public:
// Disallow copy/move constructors and assignments
_base(const _base&) = delete;
_base& operator=(const _base&) = delete;
// Get type
type get_type() const { return m_type; }
@@ -119,7 +118,7 @@ namespace cfg
bool m_value;
public:
bool def;
const bool def;
_bool(node* owner, const std::string& name, bool def = false)
: _base(type::_bool, owner, name)
@@ -133,11 +132,6 @@ namespace cfg
return m_value;
}
const bool& get() const
{
return m_value;
}
void from_default() override;
std::string to_string() const override
@@ -156,11 +150,6 @@ namespace cfg
return true;
}
void set(const bool& value)
{
m_value = value;
}
};
// Value node with fixed set of possible values, each maps to an enum value of type T.
@@ -184,11 +173,6 @@ namespace cfg
return m_value;
}
const T& get() const
{
return m_value;
}
void from_default() override
{
m_value = def;
@@ -233,11 +217,7 @@ namespace cfg
int_type m_value;
public:
int_type def;
// Expose range
static const s64 max = Max;
static const s64 min = Min;
const int_type def;
_int(node* owner, const std::string& name, int_type def = std::min<int_type>(Max, std::max<int_type>(Min, 0)))
: _base(type::_int, owner, name)
@@ -251,11 +231,6 @@ namespace cfg
return m_value;
}
const int_type& get() const
{
return m_value;
}
void from_default() override
{
m_value = def;
@@ -278,11 +253,6 @@ namespace cfg
return false;
}
void set(const s64& value)
{
m_value = static_cast<int_type>(value);
}
std::vector<std::string> to_list() const override
{
return make_int_range(Min, Max);
@@ -301,7 +271,7 @@ namespace cfg
std::string m_value;
public:
std::string def;
const std::string def;
string(node* owner, const std::string& name, const std::string& def = {})
: _base(type::string, owner, name)
@@ -351,7 +321,7 @@ namespace cfg
{
}
const std::set<std::string>& get_set() const
std::set<std::string> get_set() const
{
return m_set;
}
@@ -386,7 +356,7 @@ namespace cfg
{
}
const std::map<std::string, logs::level>& get_map() const
std::map<std::string, logs::level> get_map() const
{
return m_map;
}
+126 -424
View File
@@ -9,7 +9,6 @@
#include <cstring>
#include <cerrno>
#include <typeinfo>
#include <map>
using namespace std::literals::string_literals;
@@ -76,25 +75,10 @@ static time_t to_time(const FILETIME& ft)
static FILETIME from_time(s64 _time)
{
const ullong wtime = (_time + 11644473600ULL) * 10000000ULL;
FILETIME result;
if (_time <= -11644473600ll)
{
result.dwLowDateTime = 0;
result.dwHighDateTime = 0;
}
else if (_time > INT64_MAX / 10000000ll - 11644473600ll)
{
result.dwLowDateTime = 0xffffffff;
result.dwHighDateTime = 0x7fffffff;
}
else
{
const ullong wtime = (_time + 11644473600ull) * 10000000ull;
result.dwLowDateTime = static_cast<DWORD>(wtime);
result.dwHighDateTime = static_cast<DWORD>(wtime >> 32);
}
result.dwLowDateTime = static_cast<DWORD>(wtime);
result.dwHighDateTime = static_cast<DWORD>(wtime >> 32);
return result;
}
@@ -112,8 +96,6 @@ static fs::error to_error(DWORD e)
case ERROR_INVALID_NAME: return fs::error::inval;
case ERROR_SHARING_VIOLATION: return fs::error::acces;
case ERROR_DIR_NOT_EMPTY: return fs::error::notempty;
case ERROR_NOT_READY: return fs::error::noent;
//case ERROR_INVALID_PARAMETER: return fs::error::inval;
default: fmt::throw_exception("Unknown Win32 error: %u.", e);
}
}
@@ -124,8 +106,6 @@ static fs::error to_error(DWORD e)
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/statvfs.h>
#include <sys/file.h>
#include <sys/uio.h>
#include <dirent.h>
#include <fcntl.h>
#include <libgen.h>
@@ -136,12 +116,10 @@ static fs::error to_error(DWORD e)
#if defined(__APPLE__)
#include <copyfile.h>
#include <mach-o/dyld.h>
#elif defined(__DragonFly__) || defined(__FreeBSD__)
#include <sys/socket.h> // sendfile
#elif defined(__linux__) || defined(__sun)
#include <sys/sendfile.h>
#include <sys/syscall.h>
#include <linux/fs.h>
#else
#include <fstream>
#endif
static fs::error to_error(int e)
@@ -195,43 +173,6 @@ namespace fs
// Do notning
}
fs::native_handle fs::file_base::get_handle()
{
#ifdef _WIN32
return INVALID_HANDLE_VALUE;
#else
return -1;
#endif
}
u64 file_base::write_gather(const iovec_clone* buffers, u64 buf_count)
{
u64 total = 0;
for (u64 i = 0; i < buf_count; i++)
{
if (!buffers[i].iov_base || buffers[i].iov_len + total < total)
{
g_tls_error = error::inval;
return -1;
}
total += buffers[i].iov_len;
}
const auto buf = std::make_unique<uchar[]>(total);
u64 copied = 0;
for (u64 i = 0; i < buf_count; i++)
{
std::memcpy(buf.get() + copied, buffers[i].iov_base, buffers[i].iov_len);
copied += buffers[i].iov_len;
}
return this->write(buf.get(), total);
}
dir_base::~dir_base()
{
}
@@ -257,7 +198,7 @@ std::shared_ptr<fs::device_base> fs::device_manager::get_device(const std::strin
std::shared_ptr<fs::device_base> fs::device_manager::set_device(const std::string& name, const std::shared_ptr<device_base>& device)
{
std::lock_guard lock(m_mutex);
writer_lock lock(m_mutex);
return m_map[name] = device;
}
@@ -324,7 +265,7 @@ bool fs::stat(const std::string& path, stat_t& info)
info.size = (u64)attrs.nFileSizeLow | ((u64)attrs.nFileSizeHigh << 32);
info.atime = to_time(attrs.ftLastAccessTime);
info.mtime = to_time(attrs.ftLastWriteTime);
info.ctime = info.mtime;
info.ctime = to_time(attrs.ftCreationTime);
#else
struct ::stat file_info;
if (::stat(path.c_str(), &file_info) != 0)
@@ -338,12 +279,9 @@ bool fs::stat(const std::string& path, stat_t& info)
info.size = file_info.st_size;
info.atime = file_info.st_atime;
info.mtime = file_info.st_mtime;
info.ctime = info.mtime;
info.ctime = file_info.st_ctime;
#endif
if (info.atime < info.mtime)
info.atime = info.mtime;
return true;
}
@@ -539,30 +477,18 @@ bool fs::create_dir(const std::string& path)
bool fs::create_path(const std::string& path)
{
const std::string parent = get_parent_dir(path);
const auto& parent = get_parent_dir(path);
if (!parent.empty() && !create_path(parent))
if (!parent.empty() && !is_dir(parent) && !create_path(parent))
{
return false;
}
if (!create_dir(path) && g_tls_error != error::exist)
{
return false;
}
return true;
return create_dir(path);
}
bool fs::remove_dir(const std::string& path)
{
if (path.empty())
{
// Don't allow removing empty path (TODO)
g_tls_error = fs::error::noent;
return false;
}
if (auto device = get_virtual_device(path))
{
return device->remove_dir(path);
@@ -589,13 +515,6 @@ bool fs::remove_dir(const std::string& path)
bool fs::rename(const std::string& from, const std::string& to, bool overwrite)
{
if (from.empty() || to.empty())
{
// Don't allow opening empty path (TODO)
g_tls_error = fs::error::noent;
return false;
}
const auto device = get_virtual_device(from);
if (device != get_virtual_device(to))
@@ -640,21 +559,6 @@ bool fs::rename(const std::string& from, const std::string& to, bool overwrite)
return true;
#else
#ifdef __linux__
if (syscall(SYS_renameat2, AT_FDCWD, from.c_str(), AT_FDCWD, to.c_str(), overwrite ? 0 : 1 /* RENAME_NOREPLACE */) == 0)
{
return true;
}
// If the filesystem doesn't support RENAME_NOREPLACE, it returns EINVAL. Retry with fallback method in that case.
if (errno != EINVAL || overwrite)
{
g_tls_error = to_error(errno);
return false;
}
#endif
if (!overwrite && exists(to))
{
g_tls_error = fs::error::exist;
@@ -688,7 +592,7 @@ bool fs::copy_file(const std::string& from, const std::string& to, bool overwrit
}
return true;
#elif defined(__APPLE__) || defined(__linux__) || defined(__sun)
#else
/* Source: http://stackoverflow.com/questions/2180079/how-can-i-copy-a-file-on-unix-using-c */
const int input = ::open(from.c_str(), O_RDONLY);
@@ -712,13 +616,17 @@ bool fs::copy_file(const std::string& from, const std::string& to, bool overwrit
#if defined(__APPLE__)
// fcopyfile works on OS X 10.5+
if (::fcopyfile(input, output, 0, COPYFILE_ALL))
#elif defined(__DragonFly__) || defined(__FreeBSD__)
if (::sendfile(input, output, 0, 0, NULL, NULL, 0))
#elif defined(__linux__) || defined(__sun)
// sendfile will work with non-socket output (i.e. regular file) on Linux 2.6.33+
off_t bytes_copied = 0;
struct ::stat fileinfo = { 0 };
if (::fstat(input, &fileinfo) || ::sendfile(output, input, &bytes_copied, fileinfo.st_size))
#else
#error "Native file copy implementation is missing"
#else // NetBSD, OpenBSD, etc.
fmt::throw_exception("fs::copy_file() isn't implemented for this platform.\nFrom: %s\nTo: %s", from, to);
errno = 0;
if (true)
#endif
{
const int err = errno;
@@ -731,31 +639,6 @@ bool fs::copy_file(const std::string& from, const std::string& to, bool overwrit
::close(input);
::close(output);
return true;
#else // fallback
{
std::ifstream out{to, std::ios::binary};
if (out.good() && !overwrite)
{
g_tls_error = to_error(EEXIST);
return false;
}
}
std::ifstream in{from, std::ios::binary};
std::ofstream out{to, std::ios::binary};
if (!in.good() || !out.good())
{
g_tls_error = to_error(errno);
return false;
}
std::istreambuf_iterator<char> bin(in);
std::istreambuf_iterator<char> ein;
std::ostreambuf_iterator<char> bout(out);
std::copy(bin, ein, bout);
return true;
#endif
}
@@ -879,13 +762,6 @@ void fs::file::xfail() const
fs::file::file(const std::string& path, bs_t<open_mode> mode)
{
if (path.empty())
{
// Don't allow opening empty path (TODO)
g_tls_error = fs::error::noent;
return;
}
if (auto device = get_virtual_device(path))
{
if (auto&& _file = device->open(path, mode))
@@ -899,34 +775,29 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
#ifdef _WIN32
DWORD access = 0;
if (mode & fs::read) access |= GENERIC_READ;
if (mode & fs::write) access |= DELETE | (mode & fs::append ? FILE_APPEND_DATA : GENERIC_WRITE);
if (test(mode & fs::read)) access |= GENERIC_READ;
if (test(mode & fs::write)) access |= test(mode & fs::append) ? FILE_APPEND_DATA : GENERIC_WRITE;
DWORD disp = 0;
if (mode & fs::create)
if (test(mode & fs::create))
{
disp =
mode & fs::excl ? CREATE_NEW :
mode & fs::trunc ? CREATE_ALWAYS : OPEN_ALWAYS;
test(mode & fs::excl) ? CREATE_NEW :
test(mode & fs::trunc) ? CREATE_ALWAYS : OPEN_ALWAYS;
}
else
{
if (mode & fs::excl)
if (test(mode & fs::excl))
{
g_tls_error = error::inval;
return;
}
disp = mode & fs::trunc ? TRUNCATE_EXISTING : OPEN_EXISTING;
disp = test(mode & fs::trunc) ? TRUNCATE_EXISTING : OPEN_EXISTING;
}
DWORD share = 0;
if (!(mode & fs::unread) || !(mode & fs::write))
{
share |= FILE_SHARE_READ;
}
if (!(mode & (fs::lock + fs::unread)) || !(mode & fs::write))
DWORD share = FILE_SHARE_READ;
if (!test(mode & fs::unshare))
{
share |= FILE_SHARE_WRITE | FILE_SHARE_DELETE;
}
@@ -939,7 +810,7 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
return;
}
class windows_file final : public file_base
class windows_file final : public file_base, public get_native_handle
{
const HANDLE m_handle;
@@ -965,10 +836,7 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
info.size = this->size();
info.atime = to_time(basic_info.LastAccessTime);
info.mtime = to_time(basic_info.ChangeTime);
info.ctime = info.mtime;
if (info.atime < info.mtime)
info.atime = info.mtime;
info.ctime = to_time(basic_info.CreationTime);
return info;
}
@@ -1042,7 +910,7 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
return size.QuadPart;
}
native_handle get_handle() override
native_handle get() override
{
return m_handle;
}
@@ -1052,23 +920,16 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
#else
int flags = 0;
if (mode & fs::read && mode & fs::write) flags |= O_RDWR;
else if (mode & fs::read) flags |= O_RDONLY;
else if (mode & fs::write) flags |= O_WRONLY;
if (test(mode & fs::read) && test(mode & fs::write)) flags |= O_RDWR;
else if (test(mode & fs::read)) flags |= O_RDONLY;
else if (test(mode & fs::write)) flags |= O_WRONLY;
if (mode & fs::append) flags |= O_APPEND;
if (mode & fs::create) flags |= O_CREAT;
if (mode & fs::trunc && !(mode & (fs::lock + fs::unread))) flags |= O_TRUNC;
if (mode & fs::excl) flags |= O_EXCL;
if (test(mode & fs::append)) flags |= O_APPEND;
if (test(mode & fs::create)) flags |= O_CREAT;
if (test(mode & fs::trunc)) flags |= O_TRUNC;
if (test(mode & fs::excl)) flags |= O_EXCL;
int perm = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
if (mode & fs::write && mode & fs::unread)
{
perm = 0;
}
const int fd = ::open(path.c_str(), flags, perm);
const int fd = ::open(path.c_str(), flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (fd == -1)
{
@@ -1076,20 +937,7 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
return;
}
if (mode & fs::write && mode & (fs::lock + fs::unread) && ::flock(fd, LOCK_EX | LOCK_NB) != 0)
{
g_tls_error = errno == EWOULDBLOCK ? fs::error::acces : to_error(errno);
::close(fd);
return;
}
if (mode & fs::trunc && mode & (fs::lock + fs::unread))
{
// Postpone truncation in order to avoid using O_TRUNC on a locked file
::ftruncate(fd, 0);
}
class unix_file final : public file_base
class unix_file final : public file_base, public get_native_handle
{
const int m_fd;
@@ -1115,10 +963,7 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
info.size = file_info.st_size;
info.atime = file_info.st_atime;
info.mtime = file_info.st_mtime;
info.ctime = info.mtime;
if (info.atime < info.mtime)
info.atime = info.mtime;
info.ctime = file_info.st_ctime;
return info;
}
@@ -1164,7 +1009,7 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
(fmt::throw_exception("Invalid whence (0x%x)" HERE, whence), 0);
const auto result = ::lseek(m_fd, offset, mode);
if (result == -1)
{
g_tls_error = to_error(errno);
@@ -1182,21 +1027,10 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
return file_info.st_size;
}
native_handle get_handle() override
native_handle get() override
{
return m_fd;
}
u64 write_gather(const iovec_clone* buffers, u64 buf_count) override
{
static_assert(sizeof(iovec) == sizeof(iovec_clone), "Weird iovec size");
static_assert(offsetof(iovec, iov_len) == offsetof(iovec_clone, iov_len), "Weird iovec::iov_len offset");
const auto result = ::writev(m_fd, (const iovec*)buffers, buf_count);
verify("file::write_gather" HERE), result != -1;
return result;
}
};
m_file = std::make_unique<unix_file>(fd);
@@ -1274,9 +1108,9 @@ fs::file::file(const void* ptr, std::size_t size)
fs::native_handle fs::file::get_handle() const
{
if (m_file)
if (auto getter = dynamic_cast<get_native_handle*>(m_file.get()))
{
return m_file->get_handle();
return getter->get();
}
#ifdef _WIN32
@@ -1286,15 +1120,6 @@ fs::native_handle fs::file::get_handle() const
#endif
}
#ifdef _WIN32
bool fs::file::set_delete(bool autodelete) const
{
FILE_DISPOSITION_INFO disp;
disp.DeleteFileW = autodelete;
return SetFileInformationByHandle(get_handle(), FileDispositionInfo, &disp, sizeof(disp)) != 0;
}
#endif
void fs::dir::xnull() const
{
fmt::throw_exception<std::logic_error>("fs::dir is null");
@@ -1302,13 +1127,6 @@ void fs::dir::xnull() const
bool fs::dir::open(const std::string& path)
{
if (path.empty())
{
// Don't allow opening empty path (TODO)
g_tls_error = fs::error::noent;
return false;
}
if (auto device = get_virtual_device(path))
{
if (auto&& _dir = device->open_dir(path))
@@ -1345,10 +1163,7 @@ bool fs::dir::open(const std::string& path)
info.size = ((u64)found.nFileSizeHigh << 32) | (u64)found.nFileSizeLow;
info.atime = to_time(found.ftLastAccessTime);
info.mtime = to_time(found.ftLastWriteTime);
info.ctime = info.mtime;
if (info.atime < info.mtime)
info.atime = info.mtime;
info.ctime = to_time(found.ftCreationTime);
m_entries.emplace_back(std::move(info));
}
@@ -1418,12 +1233,7 @@ bool fs::dir::open(const std::string& path)
}
struct ::stat file_info;
if (::fstatat(::dirfd(m_dd), found->d_name, &file_info, 0) != 0)
{
//failed metadata (broken symlink?), ignore and skip to next file
return read(info);
}
verify("dir::read" HERE), ::fstatat(::dirfd(m_dd), found->d_name, &file_info, 0) == 0;
info.name = found->d_name;
info.is_directory = S_ISDIR(file_info.st_mode);
@@ -1431,10 +1241,7 @@ bool fs::dir::open(const std::string& path)
info.size = file_info.st_size;
info.atime = file_info.st_atime;
info.mtime = file_info.st_mtime;
info.ctime = info.mtime;
if (info.atime < info.mtime)
info.atime = info.mtime;
info.ctime = file_info.st_ctime;
return true;
}
@@ -1459,10 +1266,8 @@ const std::string& fs::get_config_dir()
std::string dir;
#ifdef _WIN32
wchar_t buf[32768];
constexpr DWORD size = static_cast<DWORD>(std::size(buf));
if (GetEnvironmentVariable(L"RPCS3_CONFIG_DIR", buf, size) - 1 >= size - 1 &&
GetModuleFileName(NULL, buf, size) - 1 >= size - 1)
wchar_t buf[2048];
if (GetModuleFileName(NULL, buf, ::size32(buf)) - 1 >= ::size32(buf) - 1)
{
MessageBoxA(0, fmt::format("GetModuleFileName() failed: error %u.", GetLastError()).c_str(), "fs::get_config_dir()", MB_ICONERROR);
return dir; // empty
@@ -1474,22 +1279,16 @@ const std::string& fs::get_config_dir()
dir.resize(dir.rfind('/') + 1);
#else
#ifdef __APPLE__
if (const char* home = ::getenv("HOME"))
dir = home + "/Library/Application Support"s;
#else
if (const char* conf = ::getenv("XDG_CONFIG_HOME"))
dir = conf;
if (const char* home = ::getenv("XDG_CONFIG_HOME"))
dir = home;
else if (const char* home = ::getenv("HOME"))
dir = home + "/.config"s;
#endif
else // Just in case
dir = "./config";
dir += "/rpcs3/";
if (!create_path(dir))
if (!is_dir(dir) && !create_path(dir))
{
std::printf("Failed to create configuration directory '%s' (%d).\n", dir.c_str(), errno);
}
@@ -1501,83 +1300,102 @@ const std::string& fs::get_config_dir()
return s_dir;
}
const std::string& fs::get_cache_dir()
std::string fs::get_data_dir(const std::string& prefix, const std::string& location, const std::string& suffix)
{
static const std::string s_dir = []
{
std::string dir;
const std::string& dir = get_config_dir() + "data/";
#ifdef _WIN32
dir = get_config_dir();
#else
#ifdef __APPLE__
if (const char* home = ::getenv("HOME"))
dir = home + "/Library/Caches"s;
#else
if (const char* cache = ::getenv("XDG_CACHE_HOME"))
dir = cache;
else if (const char* conf = ::getenv("XDG_CONFIG_HOME"))
dir = conf;
else if (const char* home = ::getenv("HOME"))
dir = home + "/.cache"s;
#endif
else // Just in case
dir = "./cache";
dir += "/rpcs3/";
if (!create_path(dir))
if (!is_dir(dir) && !create_path(dir))
{
std::printf("Failed to create configuration directory '%s' (%d).\n", dir.c_str(), errno);
return get_config_dir();
}
#endif
return dir;
}();
return s_dir;
}
std::vector<u8> buf;
buf.reserve(location.size() + 1);
bool fs::remove_all(const std::string& path, bool remove_root)
{
if (const auto root_dir = dir(path))
// Normalize location
for (char c : location)
{
for (const auto& entry : root_dir)
#ifdef _WIN32
if (c == '/' || c == '\\')
#else
if (c == '/')
#endif
{
if (entry.name == "." || entry.name == "..")
if (buf.empty() || buf.back() != '/')
{
continue;
buf.push_back('/');
}
if (entry.is_directory == false)
{
if (!remove_file(path + '/' + entry.name))
{
return false;
}
}
continue;
}
buf.push_back(c);
}
if (entry.is_directory == true)
{
if (!remove_all(path + '/' + entry.name))
{
return false;
}
}
// Calculate hash
u8 hash[20];
sha1(buf.data(), buf.size(), hash);
// Concatenate
std::string&& result = fmt::format("%s%s/%016llx%08x-%s/", s_dir, prefix, reinterpret_cast<be_t<u64>&>(hash[0]), reinterpret_cast<be_t<u32>&>(hash[8]), suffix);
if (!is_dir(result))
{
// Create dir if necessary
if (create_path(result))
{
// Acknowledge original location
file(result + ".location", rewrite).write(buf);
}
}
else
return result;
}
std::string fs::get_data_dir(const std::string& prefix, const std::string& path)
{
#ifdef _WIN32
const auto& delim = "/\\";
#else
const auto& delim = "/";
#endif
// Extract file name and location
const std::string& location = fs::get_parent_dir(path);
const std::size_t name_pos = path.find_first_not_of(delim, location.size());
return fs::get_data_dir(prefix, location, name_pos == -1 ? std::string{} : path.substr(name_pos));
}
void fs::remove_all(const std::string& path, bool remove_root)
{
for (const auto& entry : dir(path))
{
return false;
}
if (remove_root)
{
return remove_dir(path);
if (entry.name == "." || entry.name == "..")
{
continue;
}
if (entry.is_directory == false)
{
remove_file(path + '/' + entry.name);
}
if (entry.is_directory == true)
{
remove_all(path + '/' + entry.name);
}
}
return true;
if (remove_root)
{
remove_dir(path);
}
}
u64 fs::get_dir_size(const std::string& path)
@@ -1605,122 +1423,6 @@ u64 fs::get_dir_size(const std::string& path)
return result;
}
fs::file fs::make_gather(std::vector<fs::file> files)
{
struct gather_stream : file_base
{
u64 pos = 0;
u64 end = 0;
std::vector<file> files;
std::map<u64, u64> ends; // Fragment End Offset -> Index
gather_stream(std::vector<fs::file> arg)
: files(std::move(arg))
{
// Preprocess files
for (auto&& f : files)
{
end += f.size();
ends.emplace(end, ends.size());
}
}
~gather_stream() override
{
}
fs::stat_t stat() override
{
fs::stat_t result{};
if (!files.empty())
{
result = files[0].stat();
}
result.is_directory = false;
result.is_writable = false;
result.size = end;
return result;
}
bool trunc(u64 length) override
{
return false;
}
u64 read(void* buffer, u64 size) override
{
if (pos < end)
{
// Current pos
const u64 start = pos;
// Get readable size
if (const u64 max = std::min<u64>(size, end - pos))
{
u8* buf_out = static_cast<u8*>(buffer);
u64 buf_max = max;
for (auto it = ends.upper_bound(pos); it != ends.end(); ++it)
{
// Set position for the fragment
files[it->second].seek(pos - it->first, fs::seek_end);
const u64 count = std::min<u64>(it->first - pos, buf_max);
const u64 read = files[it->second].read(buf_out, count);
buf_out += count;
buf_max -= count;
pos += read;
if (read < count || buf_max == 0)
{
break;
}
}
return pos - start;
}
}
return 0;
}
u64 write(const void* buffer, u64 size) override
{
return 0;
}
u64 seek(s64 offset, seek_mode whence) override
{
const s64 new_pos =
whence == fs::seek_set ? offset :
whence == fs::seek_cur ? offset + pos :
whence == fs::seek_end ? offset + end :
(fmt::raw_error("fs::gather_stream::seek(): invalid whence"), 0);
if (new_pos < 0)
{
fs::g_tls_error = fs::error::inval;
return -1;
}
pos = new_pos;
return pos;
}
u64 size() override
{
return end;
}
};
fs::file result;
result.reset(std::make_unique<gather_stream>(std::move(files)));
return result;
}
template<>
void fmt_class_string<fs::seek_mode>::format(std::string& out, u64 arg)
{
+21 -37
View File
@@ -25,8 +25,7 @@ namespace fs
create,
trunc,
excl,
lock,
unread,
unshare,
__bitset_enum_max
};
@@ -37,8 +36,7 @@ namespace fs
constexpr auto create = +open_mode::create; // Create file if it doesn't exist
constexpr auto trunc = +open_mode::trunc; // Clear opened file if it's not empty
constexpr auto excl = +open_mode::excl; // Failure if the file already exists (used with `create`)
constexpr auto lock = +open_mode::lock; // Prevent opening the file more than once
constexpr auto unread = +open_mode::unread; // Aggressively prevent reading the opened file (do not use)
constexpr auto unshare = +open_mode::unshare; // Prevent opening the file twice
constexpr auto rewrite = open_mode::write + open_mode::create + open_mode::trunc;
@@ -65,11 +63,10 @@ namespace fs
s64 ctime;
};
// Helper, layout is equal to iovec struct
struct iovec_clone
// Native handle getter
struct get_native_handle
{
const void* iov_base;
std::size_t iov_len;
virtual native_handle get() = 0;
};
// File handle base
@@ -84,8 +81,6 @@ namespace fs
virtual u64 write(const void* buffer, u64 size) = 0;
virtual u64 seek(s64 offset, seek_mode whence) = 0;
virtual u64 size() = 0;
virtual native_handle get_handle();
virtual u64 write_gather(const iovec_clone* buffers, u64 buf_count);
};
// Directory entry (TODO)
@@ -190,7 +185,7 @@ namespace fs
file() = default;
// Open file with specified mode
explicit file(const std::string& path, bs_t<open_mode> mode = ::fs::read);
explicit file(const std::string& path, bs_t<open_mode> mode = ::fs::read);
// Open memory for read
explicit file(const void* ptr, std::size_t size);
@@ -369,18 +364,6 @@ namespace fs
// Get native handle if available
native_handle get_handle() const;
// Gathered write
u64 write_gather(const iovec_clone* buffers, u64 buf_count) const
{
if (!m_file) xnull();
return m_file->write_gather(buffers, buf_count);
}
#ifdef _WIN32
// Windows-specific function
bool set_delete(bool autodelete = true) const;
#endif
};
class dir final
@@ -417,7 +400,7 @@ namespace fs
{
m_dir = std::move(ptr);
}
std::unique_ptr<dir_base> release()
{
return std::move(m_dir);
@@ -439,7 +422,7 @@ namespace fs
class iterator
{
const dir* m_parent;
dir* m_parent;
dir_entry m_entry;
public:
@@ -449,7 +432,7 @@ namespace fs
from_current
};
iterator(const dir* parent, mode mode_ = mode::from_first)
iterator(dir* parent, mode mode_ = mode::from_first)
: m_parent(parent)
{
if (!m_parent)
@@ -475,7 +458,7 @@ namespace fs
iterator& operator++()
{
*this = {m_parent, mode::from_current};
*this = { m_parent, mode::from_current };
return *this;
}
@@ -485,25 +468,28 @@ namespace fs
}
};
iterator begin() const
iterator begin()
{
return {m_dir ? this : nullptr};
return{ m_dir ? this : nullptr };
}
iterator end() const
iterator end()
{
return {nullptr};
return{ nullptr };
}
};
// Get configuration directory
const std::string& get_config_dir();
// Get common cache directory
const std::string& get_cache_dir();
// Get data/cache directory for specified prefix and suffix
std::string get_data_dir(const std::string& prefix, const std::string& location, const std::string& suffix);
// Get data/cache directory for specified prefix and path (suffix will be filename)
std::string get_data_dir(const std::string& prefix, const std::string& path);
// Delete directory and all its contents recursively
bool remove_all(const std::string& path, bool remove_root = true);
void remove_all(const std::string& path, bool remove_root = true);
// Get size of all files recursively
u64 get_dir_size(const std::string& path);
@@ -631,12 +617,10 @@ namespace fs
if (fs::file f{path, mode})
{
// Write args sequentially
(f.write(args), ...);
int seq[]{ (f.write(args), 0)... };
return true;
}
return false;
}
file make_gather(std::vector<file>);
}
+38 -74
View File
@@ -4,7 +4,7 @@
#include "GDBDebugServer.h"
#include "Log.h"
#include <algorithm>
#include "Emu/Memory/vm.h"
#include "Emu/Memory/Memory.h"
#include "Emu/System.h"
#include "Emu/IdManager.h"
#include "Emu/CPU/CPUThread.h"
@@ -13,13 +13,13 @@
#include "Emu/Cell/SPUThread.h"
#ifndef _WIN32
#include "fcntl.h"
#include"fcntl.h"
#endif
extern void ppu_set_breakpoint(u32 addr);
extern void ppu_remove_breakpoint(u32 addr);
LOG_CHANNEL(gdbDebugServer);
logs::channel gdbDebugServer("gdbDebugServer");
int sock_init(void)
{
@@ -166,10 +166,9 @@ char GDBDebugServer::read_char()
u8 GDBDebugServer::read_hexbyte()
{
std::string s = "";
s += read_char();
s += read_char();
return hex_to_u8(s);
char buf[2];
read(buf, 2);
return static_cast<u8>(strtol(buf, nullptr, 16));
}
void GDBDebugServer::try_read_cmd(gdb_cmd & out_cmd)
@@ -177,7 +176,7 @@ void GDBDebugServer::try_read_cmd(gdb_cmd & out_cmd)
char c = read_char();
//interrupt
if (UNLIKELY(c == 0x03)) {
out_cmd.cmd = '\x03';
out_cmd.cmd = "\0x03";
out_cmd.data = "";
out_cmd.checksum = 0;
return;
@@ -224,7 +223,7 @@ void GDBDebugServer::try_read_cmd(gdb_cmd & out_cmd)
}
out_cmd.checksum = read_hexbyte();
if (out_cmd.checksum != checksum) {
throw wrong_checksum_exception("Wrong checksum for packet" HERE);
throw new wrong_checksum_exception("Wrong checksum for packet" HERE);
}
}
@@ -338,7 +337,6 @@ bool GDBDebugServer::select_thread(u64 id)
selected_thread = ppu.ptr;
return true;
}
gdbDebugServer.warning("Unable to select thread! Is the emulator running?");
return false;
}
@@ -354,7 +352,7 @@ std::string GDBDebugServer::get_reg(std::shared_ptr<ppu_thread> thread, u32 rid)
case 65:
return std::string(16, 'x');
case 66:
return u32_to_padded_hex(thread->cr.pack());
return u32_to_padded_hex(thread->cr_pack());
case 67:
return u64_to_padded_hex(thread->lr);
case 68:
@@ -367,8 +365,8 @@ std::string GDBDebugServer::get_reg(std::shared_ptr<ppu_thread> thread, u32 rid)
return std::string(8, 'x');
default:
if (rid > 70) return "";
return (rid > 31)
? u64_to_padded_hex(std::bit_cast<u64>(thread->fpr[rid - 32])) //fpr
return (rid > 31)
? u64_to_padded_hex(reinterpret_cast<u64&>(thread->fpr[rid - 32])) //fpr
: u64_to_padded_hex(thread->gpr[rid]); //gpr
}
}
@@ -383,7 +381,7 @@ bool GDBDebugServer::set_reg(std::shared_ptr<ppu_thread> thread, u32 rid, std::s
case 65:
return true;
case 66:
thread->cr.unpack(hex_to_u32(value));
thread->cr_unpack(hex_to_u32(value));
return true;
case 67:
thread->lr = hex_to_u64(value);
@@ -401,7 +399,7 @@ bool GDBDebugServer::set_reg(std::shared_ptr<ppu_thread> thread, u32 rid, std::s
if (rid > 70) return false;
if (rid > 31) {
u64 val = hex_to_u64(value);
thread->fpr[rid - 32] = std::bit_cast<f64>(val);
thread->fpr[rid - 32] = reinterpret_cast<f64&>(val);
} else {
thread->gpr[rid] = hex_to_u64(value);
}
@@ -429,25 +427,6 @@ bool GDBDebugServer::send_reason()
return send_cmd_ack("S05");
}
void GDBDebugServer::wait_with_interrupts() {
char c;
while (!paused) {
int result = recv(client_socket, &c, 1, 0);
if (result == SOCKET_ERROR) {
if (check_errno_again()) {
thread_ctrl::wait_for(50);
continue;
}
gdbDebugServer.error("Error during socket read");
fmt::throw_exception("Error during socket read" HERE);
} else if (c == 0x03) {
paused = true;
}
}
}
bool GDBDebugServer::cmd_extended_mode(gdb_cmd & cmd)
{
return send_cmd_ack("OK");
@@ -474,25 +453,24 @@ bool GDBDebugServer::cmd_thread_info(gdb_cmd & cmd)
result += u64_to_padded_hex(static_cast<u64>(cpu.id));
};
idm::select<ppu_thread>(on_select);
//idm::select<RawSPUThread>(on_select);
//idm::select<SPUThread>(on_select);
idm::select<ARMv7Thread>(on_select);
idm::select<RawSPUThread>(on_select);
idm::select<SPUThread>(on_select);
//todo: this may exceed max command length
result = "m" + result + "l";
return send_cmd_ack(result);;
}
bool GDBDebugServer::cmd_current_thread(gdb_cmd & cmd)
{
return send_cmd_ack(selected_thread.expired() ? "" : ("QC" + u64_to_padded_hex(selected_thread.lock()->id)));
return send_cmd_ack(selected_thread.expired() ? "" : u64_to_padded_hex(selected_thread.lock()->id));
}
bool GDBDebugServer::cmd_read_register(gdb_cmd & cmd)
{
if (!select_thread(general_ops_thread_id)) {
return send_cmd_ack("E02");
}
select_thread(general_ops_thread_id);
auto th = selected_thread.lock();
if (th->id_type() == 1) {
auto ppu = std::static_pointer_cast<ppu_thread>(th);
@@ -510,9 +488,7 @@ bool GDBDebugServer::cmd_read_register(gdb_cmd & cmd)
bool GDBDebugServer::cmd_write_register(gdb_cmd & cmd)
{
if (!select_thread(general_ops_thread_id)) {
return send_cmd_ack("E02");
}
select_thread(general_ops_thread_id);
auto th = selected_thread.lock();
if (th->id_type() == 1) {
auto ppu = std::static_pointer_cast<ppu_thread>(th);
@@ -541,7 +517,7 @@ bool GDBDebugServer::cmd_read_memory(gdb_cmd & cmd)
std::string result;
result.reserve(len * 2);
for (u32 i = 0; i < len; ++i) {
if (vm::check_addr(addr)) {
if (vm::check_addr(addr + i)) {
result += to_hexbyte(vm::read8(addr + i));
} else {
break;
@@ -567,7 +543,7 @@ bool GDBDebugServer::cmd_write_memory(gdb_cmd & cmd)
u32 len = hex_to_u32(cmd.data.substr(s + 1, s2 - s - 1));
const char* data_ptr = (cmd.data.c_str()) + s2 + 1;
for (u32 i = 0; i < len; ++i) {
if (vm::check_addr(addr + i, 1, vm::page_writable)) {
if (vm::check_addr(addr + i)) {
u8 val;
int res = sscanf_s(data_ptr, "%02hhX", &val);
if (!res) {
@@ -658,31 +634,30 @@ bool GDBDebugServer::cmd_vcont(gdb_cmd & cmd)
{
//todo: handle multiple actions and thread ids
this->from_breakpoint = false;
if (cmd.data[1] == 'c' || cmd.data[1] == 's') {
if (cmd.data[1] == 'c') {
select_thread(continue_ops_thread_id);
auto ppu = std::static_pointer_cast<ppu_thread>(selected_thread.lock());
paused = false;
if (cmd.data[1] == 's') {
ppu->state += cpu_flag::dbg_step;
}
ppu->state -= cpu_flag::dbg_pause;
//special case if app didn't start yet (only loaded)
if (!Emu.IsPaused() && !Emu.IsRunning()) {
Emu.Run();
if (Emu.IsPaused()) {
Emu.Resume();
}
thread_ctrl::wait();
//we are in all-stop mode
Emu.Pause();
return send_reason();
} else if (cmd.data[1] == 's') {
select_thread(continue_ops_thread_id);
auto ppu = std::static_pointer_cast<ppu_thread>(selected_thread.lock());
ppu->state += cpu_flag::dbg_step;
ppu->state -= cpu_flag::dbg_pause;
if (Emu.IsPaused()) {
Emu.Resume();
} else {
ppu->notify();
}
wait_with_interrupts();
thread_ctrl::wait();
//we are in all-stop mode
Emu.Pause();
select_thread(pausedBy);
// we have to remove dbg_pause from thread that paused execution, otherwise
// it will be paused forever (Emu.Resume only removes dbg_global_pause)
ppu = std::static_pointer_cast<ppu_thread>(selected_thread.lock());
ppu->state -= cpu_flag::dbg_pause;
return send_reason();
}
return send_cmd_ack("");
@@ -754,9 +729,7 @@ void GDBDebugServer::on_task()
return;
}
//stop immediately
if (Emu.IsRunning()) {
Emu.Pause();
}
Emu.Pause();
try {
char hostbuf[32];
@@ -795,7 +768,7 @@ void GDBDebugServer::on_task()
}
}
}
catch (std::runtime_error& e)
catch (std::runtime_error& e)
{
if (client_socket) {
closesocket(client_socket);
@@ -829,16 +802,7 @@ void GDBDebugServer::on_stop()
this->stop = true;
//just in case we are waiting for breakpoint
this->notify();
old_thread::on_stop();
}
void GDBDebugServer::pause_from(cpu_thread* t) {
if (paused) {
return;
}
paused = true;
pausedBy = t->id;
notify();
named_thread::on_stop();
}
u32 g_gdb_debugger_id = 0;
+15 -11
View File
@@ -40,8 +40,8 @@ public:
const u64 ALL_THREADS = 0xffffffffffffffff;
const u64 ANY_THREAD = 0;
class GDBDebugServer
{
class GDBDebugServer : public named_thread {
socket_t server_socket;
socket_t client_socket;
std::weak_ptr<cpu_thread> selected_thread;
@@ -50,7 +50,7 @@ class GDBDebugServer
//initialize server socket and start listening
void start_server();
//read at most cnt bytes to buf, returns number of bytes actually read
//read at most cnt bytes to buf, returns nubmer of bytes actually read
int read(void* buf, int cnt);
//reads one character
char read_char();
@@ -60,8 +60,8 @@ class GDBDebugServer
void try_read_cmd(gdb_cmd& out_cmd);
//reads commands until receiveing one with valid checksum
//in case of other exception (i.e. wrong first char of command)
//it will log exception text and return false
//in that case best for caller would be to stop reading, because
//it will log exception text and return false
//in that case best for caller would be to stop reading, because
//chance of getting correct command is low
bool read_cmd(gdb_cmd& out_cmd);
//send cnt bytes from buf to client
@@ -90,8 +90,6 @@ class GDBDebugServer
//send reason of stop, returns false if sending response failed
bool send_reason();
void wait_with_interrupts();
//commands
bool cmd_extended_mode(gdb_cmd& cmd);
bool cmd_reason(gdb_cmd& cmd);
@@ -112,14 +110,20 @@ class GDBDebugServer
bool cmd_set_breakpoint(gdb_cmd& cmd);
bool cmd_remove_breakpoint(gdb_cmd& cmd);
protected:
void on_task() override final;
void on_exit() override final;
public:
static const u32 id_base = 1;
static const u32 id_step = 1;
static const u32 id_count = 0x100000;
bool from_breakpoint = true;
bool stop = false;
bool paused = false;
u64 pausedBy;
void operator()();
void pause_from(cpu_thread* t);
virtual std::string get_name() const;
virtual void on_stop() override final;
};
extern u32 g_gdb_debugger_id;
+1 -1
View File
@@ -4,7 +4,7 @@
#pragma push_macro("new")
#undef new
#include <gsl/gsl>
#include <gsl.h>
#pragma pop_macro("new")
#undef Expects
#undef Ensures
-2
View File
@@ -1,7 +1,5 @@
#pragma once
#include <type_traits>
template<typename T1, typename T2> struct range_t
{
T1 _min; // first value
+66 -405
View File
@@ -1,232 +1,3 @@
#include "types.h"
#include "JIT.h"
#include "StrFmt.h"
#include "File.h"
#include "Log.h"
#include "mutex.h"
#include "sysinfo.h"
#include "VirtualMemory.h"
#include <immintrin.h>
// Memory manager mutex
shared_mutex s_mutex2;
#ifdef __linux__
#define CAN_OVERCOMMIT
#endif
static u8* get_jit_memory()
{
// Reserve 2G memory (magic static)
static void* const s_memory2 = []() -> void*
{
void* ptr = utils::memory_reserve(0x80000000);
#ifdef CAN_OVERCOMMIT
utils::memory_commit(ptr, 0x80000000);
utils::memory_protect(ptr, 0x40000000, utils::protection::wx);
#endif
return ptr;
}();
return static_cast<u8*>(s_memory2);
}
// Allocation counters (1G code, 1G data subranges)
static atomic_t<u64> s_code_pos{0}, s_data_pos{0};
// Snapshot of code generated before main()
static std::vector<u8> s_code_init, s_data_init;
template <atomic_t<u64>& Ctr, uint Off, utils::protection Prot>
static u8* add_jit_memory(std::size_t size, uint align)
{
// Select subrange
u8* pointer = get_jit_memory() + Off;
if (UNLIKELY(!size && !align))
{
// Return subrange info
return pointer;
}
#ifndef CAN_OVERCOMMIT
std::lock_guard lock(s_mutex2);
#endif
u64 olda, newa;
// Simple allocation by incrementing pointer to the next free data
const u64 pos = Ctr.atomic_op([&](u64& ctr) -> u64
{
const u64 _pos = ::align(ctr, align);
const u64 _new = ::align(_pos + size, align);
if (UNLIKELY(_new > 0x40000000))
{
// Sorry, we failed, and further attempts should fail too.
ctr = 0x40000000;
return -1;
}
// Check the necessity to commit more memory
olda = ::align(ctr, 0x10000);
newa = ::align(_new, 0x10000);
ctr = _new;
return _pos;
});
if (UNLIKELY(pos == -1))
{
LOG_WARNING(GENERAL, "JIT: Out of memory (size=0x%x, align=0x%x, off=0x%x)", size, align, Off);
return nullptr;
}
if (UNLIKELY(olda != newa))
{
#ifdef CAN_OVERCOMMIT
// TODO: possibly madvise
#else
// Commit more memory
utils::memory_commit(pointer + olda, newa - olda, Prot);
#endif
}
return pointer + pos;
}
jit_runtime::jit_runtime()
: HostRuntime()
{
}
jit_runtime::~jit_runtime()
{
}
asmjit::Error jit_runtime::_add(void** dst, asmjit::CodeHolder* code) noexcept
{
std::size_t codeSize = code->getCodeSize();
if (UNLIKELY(!codeSize))
{
*dst = nullptr;
return asmjit::kErrorNoCodeGenerated;
}
void* p = jit_runtime::alloc(codeSize, 16);
if (UNLIKELY(!p))
{
*dst = nullptr;
return asmjit::kErrorNoVirtualMemory;
}
std::size_t relocSize = code->relocate(p);
if (UNLIKELY(!relocSize))
{
*dst = nullptr;
return asmjit::kErrorInvalidState;
}
flush(p, relocSize);
*dst = p;
return asmjit::kErrorOk;
}
asmjit::Error jit_runtime::_release(void* ptr) noexcept
{
return asmjit::kErrorOk;
}
u8* jit_runtime::alloc(std::size_t size, uint align, bool exec) noexcept
{
if (exec)
{
return add_jit_memory<s_code_pos, 0x0, utils::protection::wx>(size, align);
}
else
{
return add_jit_memory<s_data_pos, 0x40000000, utils::protection::rw>(size, align);
}
}
void jit_runtime::initialize()
{
if (!s_code_init.empty() || !s_data_init.empty())
{
return;
}
// Create code/data snapshot
s_code_init.resize(s_code_pos);
std::memcpy(s_code_init.data(), alloc(0, 0, true), s_code_pos);
s_data_init.resize(s_data_pos);
std::memcpy(s_data_init.data(), alloc(0, 0, false), s_data_pos);
}
void jit_runtime::finalize() noexcept
{
// Reset JIT memory
#ifdef CAN_OVERCOMMIT
utils::memory_reset(get_jit_memory(), 0x80000000);
utils::memory_protect(get_jit_memory(), 0x40000000, utils::protection::wx);
#else
utils::memory_decommit(get_jit_memory(), 0x80000000);
#endif
s_code_pos = 0;
s_data_pos = 0;
// Restore code/data snapshot
std::memcpy(alloc(s_code_init.size(), 1, true), s_code_init.data(), s_code_init.size());
std::memcpy(alloc(s_data_init.size(), 1, false), s_data_init.data(), s_data_init.size());
}
asmjit::JitRuntime& asmjit::get_global_runtime()
{
// Magic static
static asmjit::JitRuntime g_rt;
return g_rt;
}
void asmjit::build_transaction_enter(asmjit::X86Assembler& c, asmjit::Label fallback, const asmjit::X86Gp& ctr, uint less_than)
{
Label fall = c.newLabel();
Label begin = c.newLabel();
c.jmp(begin);
c.bind(fall);
if (less_than < 65)
{
c.add(ctr, 1);
c.test(x86::eax, _XABORT_RETRY);
c.jz(fallback);
}
else
{
// Count an attempt without RETRY flag as 65 normal attempts and continue
c.not_(x86::eax);
c.and_(x86::eax, _XABORT_RETRY);
c.shl(x86::eax, 5);
c.add(x86::eax, 1); // eax = RETRY ? 1 : 65
c.add(ctr, x86::rax);
}
c.cmp(ctr, less_than);
c.jae(fallback);
c.align(kAlignCode, 16);
c.bind(begin);
c.xbegin(fall);
}
void asmjit::build_transaction_abort(asmjit::X86Assembler& c, unsigned char code)
{
c.db(0xc6);
c.db(0xf8);
c.db(code);
}
#ifdef LLVM_AVAILABLE
#include <unordered_map>
@@ -236,6 +7,14 @@ void asmjit::build_transaction_abort(asmjit::X86Assembler& c, unsigned char code
#include <array>
#include <deque>
#include "types.h"
#include "StrFmt.h"
#include "File.h"
#include "Log.h"
#include "mutex.h"
#include "sysinfo.h"
#include "VirtualMemory.h"
#ifdef _MSC_VER
#pragma warning(push, 0)
#endif
@@ -251,10 +30,10 @@ void asmjit::build_transaction_abort(asmjit::X86Assembler& c, unsigned char code
#ifdef _WIN32
#include <Windows.h>
#else
#include <sys/mman.h>
#endif
#include "JIT.h"
// Memory manager mutex
shared_mutex s_mutex;
@@ -266,14 +45,8 @@ static void* const s_memory = []() -> void*
{
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();
llvm::InitializeNativeTargetAsmParser();
LLVMLinkInMCJIT();
#ifdef MAP_32BIT
auto ptr = ::mmap(nullptr, s_memory_size, PROT_NONE, MAP_ANON | MAP_PRIVATE | MAP_32BIT, -1, 0);
if (ptr != MAP_FAILED)
return ptr;
#else
for (u64 addr = 0x10000000; addr <= 0x80000000 - s_memory_size; addr += 0x1000000)
{
if (auto ptr = utils::memory_reserve(s_memory_size, (void*)addr))
@@ -281,7 +54,6 @@ static void* const s_memory = []() -> void*
return ptr;
}
}
#endif
return utils::memory_reserve(s_memory_size);
}();
@@ -291,8 +63,6 @@ static void* s_next = s_memory;
#ifdef _WIN32
static std::deque<std::vector<RUNTIME_FUNCTION>> s_unwater;
static std::vector<std::vector<RUNTIME_FUNCTION>> s_unwind; // .pdata
#else
static std::deque<std::pair<u8*, std::size_t>> s_unfire;
#endif
// Reset memory manager
@@ -309,12 +79,7 @@ extern void jit_finalize()
s_unwind.clear();
#else
for (auto&& t : s_unfire)
{
llvm::RTDyldMemoryManager::deregisterEHFramesInProcess(t.first, t.second);
}
s_unfire.clear();
// TODO: unregister EH frames if necessary
#endif
utils::memory_decommit(s_memory, s_memory_size);
@@ -365,7 +130,7 @@ struct MemoryManager : llvm::RTDyldMemoryManager
if ((u64)s_memory > 0x80000000 - s_memory_size ? (u64)addr - (u64)s_memory >= s_memory_size : addr >= 0x80000000)
{
// Lock memory manager
std::lock_guard lock(s_mutex);
writer_lock lock(s_mutex);
// Allocate memory for trampolines
if (!m_tramps)
@@ -398,10 +163,10 @@ struct MemoryManager : llvm::RTDyldMemoryManager
return {addr, llvm::JITSymbolFlags::Exported};
}
u8* allocateCodeSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name) override
virtual u8* allocateCodeSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name) override
{
// Lock memory manager
std::lock_guard lock(s_mutex);
writer_lock lock(s_mutex);
// Simple allocation
const u64 next = ::align((u64)s_next + size, 4096);
@@ -419,10 +184,10 @@ struct MemoryManager : llvm::RTDyldMemoryManager
return (u8*)std::exchange(s_next, (void*)next);
}
u8* allocateDataSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name, bool is_ro) override
virtual u8* allocateDataSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name, bool is_ro) override
{
// Lock memory manager
std::lock_guard lock(s_mutex);
writer_lock lock(s_mutex);
// Simple allocation
const u64 next = ::align((u64)s_next + size, 4096);
@@ -435,6 +200,7 @@ struct MemoryManager : llvm::RTDyldMemoryManager
if (!is_ro)
{
LOG_ERROR(GENERAL, "LLVM: Writeable data section not supported!");
}
utils::memory_commit(s_next, size);
@@ -443,10 +209,10 @@ struct MemoryManager : llvm::RTDyldMemoryManager
return (u8*)std::exchange(s_next, (void*)next);
}
bool finalizeMemory(std::string* = nullptr) override
virtual bool finalizeMemory(std::string* = nullptr) override
{
// Lock memory manager
std::lock_guard lock(s_mutex);
writer_lock lock(s_mutex);
// TODO: make only read-only sections read-only
//#ifdef _WIN32
@@ -460,11 +226,11 @@ struct MemoryManager : llvm::RTDyldMemoryManager
return false;
}
void registerEHFrames(u8* addr, u64 load_addr, std::size_t size) override
virtual void registerEHFrames(u8* addr, u64 load_addr, std::size_t size) override
{
#ifdef _WIN32
// Lock memory manager
std::lock_guard lock(s_mutex);
writer_lock lock(s_mutex);
// Use s_memory as a BASE, compute the difference
const u64 unwind_diff = (u64)addr - (u64)s_memory;
@@ -487,97 +253,16 @@ struct MemoryManager : llvm::RTDyldMemoryManager
{
s_unwind.emplace_back(std::move(pdata));
}
#else
s_unfire.push_front(std::make_pair(addr, size));
#endif
return RTDyldMemoryManager::registerEHFramesInProcess(addr, size);
return RTDyldMemoryManager::registerEHFrames(addr, load_addr, size);
}
void deregisterEHFrames() override
virtual void deregisterEHFrames() override
{
}
};
LOG_ERROR(GENERAL, "deregisterEHFrames() called"); // Not expected
// Simple memory manager
struct MemoryManager2 : llvm::RTDyldMemoryManager
{
MemoryManager2() = default;
~MemoryManager2() override
{
}
u8* allocateCodeSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name) override
{
return jit_runtime::alloc(size, align, true);
}
u8* allocateDataSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name, bool is_ro) override
{
return jit_runtime::alloc(size, align, false);
}
bool finalizeMemory(std::string* = nullptr) override
{
return false;
}
void registerEHFrames(u8* addr, u64 load_addr, std::size_t size) override
{
#ifndef _WIN32
RTDyldMemoryManager::registerEHFramesInProcess(addr, size);
s_unfire.push_front(std::make_pair(addr, size));
#endif
}
void deregisterEHFrames() override
{
}
};
// Simple memory manager. I promise there will be no MemoryManager4.
struct MemoryManager3 : llvm::RTDyldMemoryManager
{
std::vector<std::pair<u8*, std::size_t>> allocs;
MemoryManager3() = default;
~MemoryManager3() override
{
for (auto& a : allocs)
{
utils::memory_release(a.first, a.second);
}
}
u8* allocateCodeSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name) override
{
u8* r = static_cast<u8*>(utils::memory_reserve(size));
utils::memory_commit(r, size, utils::protection::wx);
allocs.emplace_back(r, size);
return r;
}
u8* allocateDataSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name, bool is_ro) override
{
u8* r = static_cast<u8*>(utils::memory_reserve(size));
utils::memory_commit(r, size);
allocs.emplace_back(r, size);
return r;
}
bool finalizeMemory(std::string* = nullptr) override
{
return false;
}
void registerEHFrames(u8* addr, u64 load_addr, std::size_t size) override
{
}
void deregisterEHFrames() override
{
return RTDyldMemoryManager::deregisterEHFrames();
}
};
@@ -591,7 +276,7 @@ struct EventListener : llvm::JITEventListener
{
}
void notifyObjectLoaded(ObjectKey K, const llvm::object::ObjectFile& obj, const llvm::RuntimeDyld::LoadedObjectInfo& inf) override
virtual void NotifyObjectEmitted(const llvm::object::ObjectFile& obj, const llvm::RuntimeDyld::LoadedObjectInfo& inf) override
{
#ifdef _WIN32
for (auto it = obj.section_begin(), end = obj.section_end(); it != end; ++it)
@@ -619,7 +304,7 @@ struct EventListener : llvm::JITEventListener
}
// Lock memory manager
std::lock_guard lock(s_mutex);
writer_lock lock(s_mutex);
// Use s_memory as a BASE, compute the difference
const u64 code_diff = (u64)m_mem.m_code_addr - (u64)s_memory;
@@ -656,15 +341,15 @@ public:
std::string name = m_path;
name.append(module->getName());
fs::file(name, fs::rewrite).write(obj.getBufferStart(), obj.getBufferSize());
LOG_NOTICE(GENERAL, "LLVM: Created module: %s", module->getName().data());
LOG_SUCCESS(GENERAL, "LLVM: Created module: %s", module->getName().data());
}
static std::unique_ptr<llvm::MemoryBuffer> load(const std::string& path)
{
if (fs::file cached{path, fs::read})
{
auto buf = llvm::WritableMemoryBuffer::getNewUninitMemBuffer(cached.size());
cached.read(buf->getBufferStart(), buf->getBufferSize());
auto buf = llvm::MemoryBuffer::getNewUninitMemBuffer(cached.size());
cached.read(const_cast<char*>(buf->getBufferStart()), buf->getBufferSize());
return buf;
}
@@ -678,7 +363,7 @@ public:
if (auto buf = load(path))
{
LOG_NOTICE(GENERAL, "LLVM: Loaded module: %s", module->getName().data());
LOG_SUCCESS(GENERAL, "LLVM: Loaded module: %s", module->getName().data());
return buf;
}
@@ -686,10 +371,10 @@ public:
}
};
std::string jit_compiler::cpu(const std::string& _cpu)
jit_compiler::jit_compiler(const std::unordered_map<std::string, u64>& _link, std::string _cpu)
: m_link(std::move(_link))
, m_cpu(std::move(_cpu))
{
std::string m_cpu = _cpu;
if (m_cpu.empty())
{
m_cpu = llvm::sys::getHostCPUName();
@@ -700,63 +385,24 @@ std::string jit_compiler::cpu(const std::string& _cpu)
m_cpu == "broadwell" ||
m_cpu == "skylake" ||
m_cpu == "skylake-avx512" ||
m_cpu == "cascadelake" ||
m_cpu == "cannonlake" ||
m_cpu == "icelake" ||
m_cpu == "icelake-client" ||
m_cpu == "icelake-server")
m_cpu == "cannonlake")
{
// Downgrade if AVX is not supported by some chips
if (!utils::has_avx())
{
m_cpu = "nehalem";
}
}
if (m_cpu == "skylake-avx512" ||
m_cpu == "cascadelake" ||
m_cpu == "cannonlake" ||
m_cpu == "icelake" ||
m_cpu == "icelake-client" ||
m_cpu == "icelake-server")
{
// Downgrade if AVX-512 is disabled or not supported
if (!utils::has_512())
{
m_cpu = "skylake";
}
}
}
return m_cpu;
}
jit_compiler::jit_compiler(const std::unordered_map<std::string, u64>& _link, const std::string& _cpu, u32 flags)
: m_link(_link)
, m_cpu(cpu(_cpu))
{
std::string result;
if (m_link.empty())
{
std::unique_ptr<llvm::RTDyldMemoryManager> mem;
if (flags & 0x1)
{
mem = std::make_unique<MemoryManager3>();
}
else
{
mem = std::make_unique<MemoryManager2>();
}
// Auxiliary JIT (does not use custom memory manager, only writes the objects)
m_engine.reset(llvm::EngineBuilder(std::make_unique<llvm::Module>("null_", m_context))
.setErrorStr(&result)
.setEngineKind(llvm::EngineKind::JIT)
.setMCJITMemoryManager(std::move(mem))
.setOptLevel(llvm::CodeGenOpt::Aggressive)
.setCodeModel(flags & 0x2 ? llvm::CodeModel::Large : llvm::CodeModel::Small)
.setCodeModel(llvm::CodeModel::Small)
.setMCPU(m_cpu)
.create());
}
@@ -768,10 +414,9 @@ jit_compiler::jit_compiler(const std::unordered_map<std::string, u64>& _link, co
m_engine.reset(llvm::EngineBuilder(std::make_unique<llvm::Module>("null", m_context))
.setErrorStr(&result)
.setEngineKind(llvm::EngineKind::JIT)
.setMCJITMemoryManager(std::move(mem))
.setOptLevel(llvm::CodeGenOpt::Aggressive)
.setCodeModel(flags & 0x2 ? llvm::CodeModel::Large : llvm::CodeModel::Small)
.setCodeModel(llvm::CodeModel::Small)
.setMCPU(m_cpu)
.create());
@@ -808,19 +453,6 @@ void jit_compiler::add(std::unique_ptr<llvm::Module> module, const std::string&
}
}
void jit_compiler::add(std::unique_ptr<llvm::Module> module)
{
const auto ptr = module.get();
m_engine->addModule(std::move(module));
m_engine->generateCodeForModule(ptr);
for (auto& func : ptr->functions())
{
// Delete IR to lower memory consumption
func.deleteBody();
}
}
void jit_compiler::add(const std::string& path)
{
m_engine->addObjectFile(std::move(llvm::object::ObjectFile::createObjectFile(*ObjectCache::load(path)).get()));
@@ -836,4 +468,33 @@ u64 jit_compiler::get(const std::string& name)
return m_engine->getGlobalValueAddress(name);
}
std::unordered_map<std::string, u64> jit_compiler::add(std::unordered_map<std::string, std::string> data)
{
// Lock memory manager
writer_lock lock(s_mutex);
std::unordered_map<std::string, u64> result;
std::size_t size = 0;
for (auto&& pair : data)
{
size += ::align(pair.second.size(), 16);
}
utils::memory_commit(s_next, size, utils::protection::wx);
std::memset(s_next, 0xc3, ::align(size, 4096));
for (auto&& pair : data)
{
std::memcpy(s_next, pair.second.data(), pair.second.size());
result.emplace(pair.first, (u64)s_next);
s_next = (void*)::align((u64)s_next + pair.second.size(), 16);
}
s_next = (void*)::align((u64)s_next, 4096);
return result;
}
#endif
+8 -98
View File
@@ -1,97 +1,9 @@
#pragma once
#define ASMJIT_EMBED
#define ASMJIT_DEBUG
#include <asmjit/asmjit.h>
#include <array>
#include <functional>
enum class jit_class
{
ppu_code,
ppu_data,
spu_code,
spu_data,
};
// ASMJIT runtime for emitting code in a single 2G region
struct jit_runtime final : asmjit::HostRuntime
{
jit_runtime();
~jit_runtime() override;
// Allocate executable memory
asmjit::Error _add(void** dst, asmjit::CodeHolder* code) noexcept override;
// Do nothing (deallocation is delayed)
asmjit::Error _release(void* p) noexcept override;
// Allocate memory
static u8* alloc(std::size_t size, uint align, bool exec = true) noexcept;
// Should be called at least once after global initialization
static void initialize();
// Deallocate all memory
static void finalize() noexcept;
};
namespace asmjit
{
// Should only be used to build global functions
asmjit::JitRuntime& get_global_runtime();
// Emit xbegin and adjacent loop, return label at xbegin
void build_transaction_enter(X86Assembler& c, Label fallback, const X86Gp& ctr, uint less_than);
// Emit xabort
void build_transaction_abort(X86Assembler& c, unsigned char code);
}
// Build runtime function with asmjit::X86Assembler
template <typename FT, typename F>
FT build_function_asm(F&& builder)
{
using namespace asmjit;
auto& rt = get_global_runtime();
CodeHolder code;
code.init(rt.getCodeInfo());
code._globalHints = asmjit::CodeEmitter::kHintOptimizedAlign;
std::array<X86Gp, 4> args;
#ifdef _WIN32
args[0] = x86::rcx;
args[1] = x86::rdx;
args[2] = x86::r8;
args[3] = x86::r9;
#else
args[0] = x86::rdi;
args[1] = x86::rsi;
args[2] = x86::rdx;
args[3] = x86::rcx;
#endif
X86Assembler compiler(&code);
builder(std::ref(compiler), args);
FT result;
if (rt.add(&result, &code))
{
return nullptr;
}
return result;
}
#ifdef LLVM_AVAILABLE
#include <memory>
#include <string>
#include <string_view>
#include <unordered_map>
#include "types.h"
@@ -128,7 +40,7 @@ class jit_compiler final
std::string m_cpu;
public:
jit_compiler(const std::unordered_map<std::string, u64>& _link, const std::string& _cpu, u32 flags = 0);
jit_compiler(const std::unordered_map<std::string, u64>& _link, std::string _cpu);
~jit_compiler();
// Get LLVM context
@@ -137,17 +49,9 @@ public:
return m_context;
}
auto& get_engine() const
{
return *m_engine;
}
// Add module (path to obj cache dir)
void add(std::unique_ptr<llvm::Module> module, const std::string& path);
// Add module (not cached)
void add(std::unique_ptr<llvm::Module> module);
// Add object (path to obj file)
void add(const std::string& path);
@@ -157,8 +61,14 @@ public:
// Get compiled function address
u64 get(const std::string& name);
// Add functions directly to the memory manager (name -> code)
static std::unordered_map<std::string, u64> add(std::unordered_map<std::string, std::string>);
// Get CPU info
static std::string cpu(const std::string& _cpu);
const std::string& cpu() const
{
return m_cpu;
}
// Check JIT purpose
bool is_primary() const
+64 -278
View File
@@ -4,24 +4,16 @@
#include "sema.h"
#include "Utilities/sysinfo.h"
#include "Utilities/Thread.h"
#include "rpcs3_version.h"
#include <cstring>
#include <cstdarg>
#include <string>
#include <unordered_map>
#include <thread>
#include <chrono>
#include <cstring>
using namespace std::literals::chrono_literals;
#ifdef _WIN32
#define NOMINMAX
#include <Windows.h>
#else
#include <chrono>
#include <sys/mman.h>
#include <sys/stat.h>
#endif
#include <zlib.h>
@@ -49,7 +41,6 @@ void fmt_class_string<logs::level>::format(std::string& out, u64 arg)
case logs::level::warning: return "Warning";
case logs::level::notice: return "Notice";
case logs::level::trace: return "Trace";
case logs::level::_uninit: return unknown;
}
return unknown;
@@ -58,32 +49,23 @@ void fmt_class_string<logs::level>::format(std::string& out, u64 arg)
namespace logs
{
// Memory-mapped buffer size
constexpr u64 s_log_size = 32 * 1024 * 1024;
constexpr std::size_t s_log_size = 128 * 1024 * 1024;
constexpr std::size_t s_log_size_s = s_log_size / 2;
constexpr std::size_t s_log_size_t = s_log_size / 4 + s_log_size_s;
constexpr std::size_t s_log_size_e = s_log_size / 8 + s_log_size_t;
constexpr std::size_t s_log_size_f = s_log_size / 16 + s_log_size_e;
class file_writer
{
fs::file m_file;
std::string m_name;
std::thread m_writer;
fs::file m_fout;
fs::file m_fout2;
u64 m_max_size;
#ifdef _WIN32
::HANDLE m_fmap;
#endif
atomic_t<std::size_t> m_pos{0};
std::size_t m_size{0};
uchar* m_fptr{};
z_stream m_zs{};
shared_mutex m_m;
alignas(128) atomic_t<u64> m_buf{0}; // MSB (40 bit): push begin, LSB (24 bis): push size
alignas(128) atomic_t<u64> m_out{0}; // Amount of bytes written to file
uchar m_zout[65536];
// Write buffered logs immediately
bool flush(u64 bufv);
public:
file_writer(const std::string& name);
@@ -137,7 +119,7 @@ namespace logs
static file_listener* get_logger()
{
// Use magic static
static file_listener logger("RPCS3");
static file_listener logger("RPCS3.log");
return &logger;
}
@@ -181,16 +163,17 @@ namespace logs
channel HLE("HLE");
channel PPU("PPU");
channel SPU("SPU");
channel ARMv7("ARMv7");
// Channel registry mutex
shared_mutex g_mutex;
semaphore<> g_mutex;
// Must be set to true in main()
atomic_t<bool> g_init{false};
void reset()
{
std::lock_guard lock(g_mutex);
semaphore_lock lock(g_mutex);
for (auto&& pair : get_logger()->channels)
{
@@ -200,7 +183,7 @@ namespace logs
void set_level(const std::string& ch_name, level value)
{
std::lock_guard lock(g_mutex);
semaphore_lock lock(g_mutex);
get_logger()->channels[ch_name].set_level(value);
}
@@ -210,7 +193,7 @@ namespace logs
{
if (!g_init)
{
std::lock_guard lock(g_mutex);
semaphore_lock lock(g_mutex);
get_logger()->messages.clear();
g_init = true;
}
@@ -226,7 +209,7 @@ void logs::listener::add(logs::listener* _new)
// Get first (main) listener
listener* lis = get_logger();
std::lock_guard lock(g_mutex);
semaphore_lock lock(g_mutex);
// Install new listener at the end of linked list
while (lis->m_next || !lis->m_next.compare_and_swap_test(nullptr, _new))
@@ -241,7 +224,7 @@ void logs::listener::add(logs::listener* _new)
}
}
void logs::message::broadcast(const char* fmt, const fmt_type_info* sup, ...) const
void logs::message::broadcast(const char* fmt, const fmt_type_info* sup, const u64* args)
{
// Get timestamp
const u64 stamp = get_stamp();
@@ -249,7 +232,7 @@ void logs::message::broadcast(const char* fmt, const fmt_type_info* sup, ...) co
// Register channel
if (ch->enabled == level::_uninit)
{
std::lock_guard lock(g_mutex);
semaphore_lock lock(g_mutex);
auto& info = get_logger()->channels[ch->name];
@@ -270,23 +253,9 @@ void logs::message::broadcast(const char* fmt, const fmt_type_info* sup, ...) co
}
}
// Get text, extract va_args
thread_local std::string text;
thread_local std::vector<u64> args;
std::size_t args_count = 0;
for (auto v = sup; v->fmt_string; v++)
args_count++;
text.clear();
args.resize(args_count);
va_list c_args;
va_start(c_args, sup);
for (u64& arg : args)
arg = va_arg(c_args, u64);
va_end(c_args);
fmt::raw_append(text, fmt, sup, args.data());
// Get text
thread_local std::string text; text.clear();
fmt::raw_append(text, fmt, sup, args);
std::string prefix = g_tls_log_prefix();
// Get first (main) listener
@@ -294,7 +263,7 @@ void logs::message::broadcast(const char* fmt, const fmt_type_info* sup, ...) co
if (!g_init)
{
std::lock_guard lock(g_mutex);
semaphore_lock lock(g_mutex);
if (!g_init)
{
@@ -322,45 +291,13 @@ void logs::message::broadcast(const char* fmt, const fmt_type_info* sup, ...) co
logs::file_writer::file_writer(const std::string& name)
: m_name(name)
{
const std::string log_name = fs::get_cache_dir() + name + ".log";
const std::string buf_name = fs::get_cache_dir() + name + ".buf";
try
{
if (!m_file.open(buf_name, fs::read + fs::rewrite + fs::lock))
if (!m_file.open(fs::get_config_dir() + name, fs::read + fs::write + fs::create + fs::trunc + fs::unshare))
{
if (fs::g_tls_error == fs::error::acces)
{
if (fs::exists(buf_name))
{
fmt::throw_exception("Another instance of %s is running. Close it or kill its process, if necessary.", name);
}
else
{
fmt::throw_exception("Cannot create %s.log (access denied)."
#ifdef _WIN32
"\nNote that %s cannot be installed in Program Files or similar directory with limited permissions."
#else
"\nPlease, check %s permissions in '~/.config/'."
#endif
, name, name);
}
}
fmt::throw_exception("Cannot create %s.log (error %s)", name, fs::g_tls_error);
fmt::throw_exception("Can't create file %s (error %s)", name, fs::g_tls_error);
}
// Check free space
fs::device_stat stats{};
if (!fs::statfs(fs::get_cache_dir(), stats) || stats.avail_free < s_log_size * 8)
{
fmt::throw_exception("Not enough free space (%f KB)", stats.avail_free / 1000000.);
}
// Limit log size to ~25% of free space
m_max_size = stats.avail_free / 4;
// Initialize memory mapped file
#ifdef _WIN32
m_fmap = CreateFileMappingW(m_file.get_handle(), 0, PAGE_READWRITE, s_log_size >> 32, s_log_size & 0xffffffff, 0);
m_fptr = m_fmap ? (uchar*)MapViewOfFile(m_fmap, FILE_MAP_WRITE, 0, 0, 0) : nullptr;
@@ -370,215 +307,79 @@ logs::file_writer::file_writer(const std::string& name)
#endif
verify(name.c_str()), m_fptr;
std::memset(m_fptr, '\n', s_log_size);
// Rotate backups (TODO)
fs::remove_file(fs::get_cache_dir() + name + "1.log.gz");
fs::create_dir(fs::get_cache_dir() + "old_logs");
fs::rename(fs::get_cache_dir() + m_name + ".log.gz", fs::get_cache_dir() + "old_logs/" + m_name + ".log.gz", true);
// Actual log file (allowed to fail)
m_fout.open(log_name, fs::rewrite);
// Compressed log, make it inaccessible (foolproof)
if (!m_fout2.open(log_name + ".gz", fs::rewrite + fs::unread) || deflateInit2(&m_zs, 9, Z_DEFLATED, 16 + 15, 9, Z_DEFAULT_STRATEGY) != Z_OK)
{
m_fout2.close();
}
#ifdef _WIN32
// Autodelete compressed log file
m_fout2.set_delete();
#endif
}
catch (const std::exception& e)
{
std::thread([text = std::string{e.what()}]{ report_fatal_error(text); }).detach();
return;
fs::remove_file(fs::get_config_dir() + name + "1.gz");
fs::create_dir(fs::get_config_dir() + "old_logs");
fs::rename(fs::get_config_dir() + m_name + ".gz", fs::get_config_dir() + "old_logs/" + m_name + ".gz", true);
}
catch (...)
{
std::thread([]{ report_fatal_error("Unknown error" HERE); }).detach();
return;
catch_all_exceptions();
}
m_writer = std::thread([this]()
{
thread_ctrl::set_native_priority(-1);
while (true)
{
const u64 bufv = m_buf;
if (bufv & 0xffffff)
{
// Wait if threads are writing logs
std::this_thread::yield();
continue;
}
if (!flush(bufv))
{
if (m_out == -1)
{
break;
}
std::this_thread::sleep_for(10ms);
}
}
});
}
logs::file_writer::~file_writer()
{
if (!m_fptr)
if (m_size == 0)
{
return;
m_size = std::min<std::size_t>(+m_pos, s_log_size);
}
// Stop writer thread
while (m_out << 24 < m_buf)
// Compress
z_stream zs{};
if (deflateInit2(&zs, 9, Z_DEFLATED, 16 + 15, 9, Z_DEFAULT_STRATEGY) == Z_OK)
{
std::this_thread::yield();
}
auto buf = std::make_unique<uchar[]>(s_log_size);
zs.avail_in = ::narrow<u32>(m_size);
zs.avail_out = s_log_size;
zs.next_in = m_fptr;
zs.next_out = buf.get();
m_out = -1;
m_writer.join();
if (m_fout2)
{
m_zs.avail_in = 0;
m_zs.next_in = nullptr;
do
if (deflate(&zs, Z_FINISH) != Z_STREAM_ERROR)
{
m_zs.avail_out = sizeof(m_zout);
m_zs.next_out = m_zout;
if (deflate(&m_zs, Z_FINISH) == Z_STREAM_ERROR || m_fout2.write(m_zout, sizeof(m_zout) - m_zs.avail_out) != sizeof(m_zout) - m_zs.avail_out)
{
break;
}
fs::file(fs::get_config_dir() + m_name + ".gz", fs::rewrite).write(buf.get(), zs.total_out);
}
while (m_zs.avail_out == 0);
deflateEnd(&m_zs);
if (deflateEnd(&zs) != Z_OK)
{
}
}
#ifdef _WIN32
// Cancel compressed log file autodeletion
m_fout2.set_delete(false);
UnmapViewOfFile(m_fptr);
CloseHandle(m_fmap);
#else
// Restore compressed log file permissions
::fchmod(m_fout2.get_handle(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
::munmap(m_fptr, s_log_size);
#endif
}
bool logs::file_writer::flush(u64 bufv)
{
std::lock_guard lock(m_m);
const u64 st = +m_out;
const u64 end = std::min<u64>((st + s_log_size) & ~(s_log_size - 1), bufv >> 24);
if (end > st)
{
// Avoid writing too big fragments
const u64 size = std::min<u64>(end - st, sizeof(m_zout) / 2);
// Write uncompressed
if (m_fout && st < m_max_size && m_fout.write(m_fptr + st % s_log_size, size) != size)
{
m_fout.close();
}
// Write compressed
if (m_fout2 && st < m_max_size)
{
m_zs.avail_in = size;
m_zs.next_in = m_fptr + st % s_log_size;
do
{
m_zs.avail_out = sizeof(m_zout);
m_zs.next_out = m_zout;
if (deflate(&m_zs, Z_NO_FLUSH) == Z_STREAM_ERROR || m_fout2.write(m_zout, sizeof(m_zout) - m_zs.avail_out) != sizeof(m_zout) - m_zs.avail_out)
{
deflateEnd(&m_zs);
m_fout2.close();
break;
}
}
while (m_zs.avail_out == 0);
}
m_out += size;
return true;
}
return false;
m_file.trunc(m_size);
}
void logs::file_writer::log(logs::level sev, const char* text, std::size_t size)
{
if (!m_fptr)
// Adaptive log limit
const auto lim =
sev >= logs::level::success ? s_log_size_s :
sev == logs::level::todo ? s_log_size_t :
sev == logs::level::error ? s_log_size_e : s_log_size_f;
if (m_pos >= lim)
{
return;
}
// TODO: write bigger fragment directly in blocking manner
while (size && size <= 0xffffff)
// Acquire memory
const auto pos = m_pos.fetch_add(size);
// Write if possible
if (pos + size <= s_log_size)
{
u64 bufv;
const auto pos = m_buf.atomic_op([&](u64& v) -> uchar*
{
const u64 v1 = v >> 24;
const u64 v2 = v & 0xffffff;
if (UNLIKELY(v2 + size > 0xffffff || v1 + v2 + size >= m_out + s_log_size))
{
bufv = v;
return nullptr;
}
v += size;
return m_fptr + (v1 + v2) % s_log_size;
});
if (UNLIKELY(!pos))
{
if ((bufv & 0xffffff) + size > 0xffffff || bufv & 0xffffff)
{
// Concurrency limit reached
std::this_thread::yield();
}
else
{
// Queue is full, need to write out
flush(bufv);
}
continue;
}
if (pos + size > m_fptr + s_log_size)
{
const auto frag = m_fptr + s_log_size - pos;
std::memcpy(pos, text, frag);
std::memcpy(m_fptr, text + frag, size - frag);
}
else
{
std::memcpy(pos, text, size);
}
m_buf += (u64{size} << 24) - size;
break;
std::memcpy(m_fptr + pos, text, size);
}
else if (pos <= s_log_size)
{
m_size = pos;
}
}
@@ -589,30 +390,16 @@ logs::file_listener::file_listener(const std::string& name)
// Write UTF-8 BOM
file_writer::log(logs::level::always, "\xEF\xBB\xBF", 3);
const std::string firmware_version = utils::get_firmware_version();
const std::string firmware_string = firmware_version.empty() ? "" : (" | Firmware version: " + firmware_version);
// Write initial message
stored_message ver;
ver.m.ch = nullptr;
ver.m.sev = level::always;
ver.stamp = 0;
ver.text = fmt::format("RPCS3 v%s | %s%s\n%s", rpcs3::version.to_string(), rpcs3::get_branch(), firmware_string, utils::get_system_info());
ver.text = fmt::format("RPCS3 v%s | %s\n%s", rpcs3::version.to_string(), rpcs3::get_branch(), utils::get_system_info());
file_writer::log(logs::level::always, ver.text.data(), ver.text.size());
file_writer::log(logs::level::always, "\n", 1);
messages.emplace_back(std::move(ver));
// Write OS version
stored_message os;
os.m.ch = nullptr;
os.m.sev = level::notice;
os.stamp = 0;
os.text = utils::get_OS_version();
file_writer::log(logs::level::notice, os.text.data(), os.text.size());
file_writer::log(logs::level::notice, "\n", 1);
messages.emplace_back(std::move(os));
}
void logs::file_listener::log(u64 stamp, const logs::message& msg, const std::string& prefix, const std::string& _text)
@@ -630,17 +417,16 @@ void logs::file_listener::log(u64 stamp, const logs::message& msg, const std::st
case level::warning: text = u8"·W "; break;
case level::notice: text = u8"·! "; break;
case level::trace: text = u8"·T "; break;
case level::_uninit: text = u8"· "; break;
}
// Print µs timestamp
// Print miscosecond timestamp
const u64 hours = stamp / 3600'000'000;
const u64 mins = (stamp % 3600'000'000) / 60'000'000;
const u64 secs = (stamp % 60'000'000) / 1'000'000;
const u64 frac = (stamp % 1'000'000);
fmt::append(text, "%u:%02u:%02u.%06u ", hours, mins, secs, frac);
if (!prefix.empty())
if (prefix.size() > 0)
{
text += "{";
text += prefix;
+17 -16
View File
@@ -1,7 +1,7 @@
#pragma once
#include "types.h"
#include "util/atomic.hpp"
#include "Atomic.h"
#include "StrFmt.h"
#include <climits>
@@ -23,17 +23,14 @@ namespace logs
struct channel;
// Message information
// Message information (temporary data)
struct message
{
channel* ch;
level sev;
private:
// Send log message to global logger instance
void broadcast(const char*, const fmt_type_info*, ...) const;
friend struct channel;
void broadcast(const char*, const fmt_type_info*, const u64*);
};
class listener
@@ -70,16 +67,21 @@ namespace logs
{
}
// Formatting function
template<typename... Args>
SAFE_BUFFERS FORCE_INLINE void format(level sev, const char* fmt, const Args&... args)
{
if (UNLIKELY(sev <= enabled))
{
message{this, sev}.broadcast(fmt, fmt::get_type_info<fmt_unveil_t<Args>...>(), fmt_args_t<Args...>{fmt_unveil<Args>::get(args)...});
}
}
#define GEN_LOG_METHOD(_sev)\
const message msg_##_sev{this, level::_sev};\
template <typename... Args>\
void _sev(const char* fmt, const Args&... args)\
template<typename... Args>\
SAFE_BUFFERS void _sev(const char* fmt, const Args&... args)\
{\
if (UNLIKELY(level::_sev <= enabled))\
{\
static constexpr fmt_type_info type_list[sizeof...(Args) + 1]{fmt_type_info::make<fmt_unveil_t<Args>>()...};\
msg_##_sev.broadcast(fmt, type_list, u64{fmt_unveil<Args>::get(args)}...);\
}\
return format<Args...>(level::_sev, fmt, args...);\
}
GEN_LOG_METHOD(fatal)
@@ -102,6 +104,7 @@ namespace logs
extern channel HLE;
extern channel PPU;
extern channel SPU;
extern channel ARMv7;
// Log level control: set all channels to level::notice
void reset();
@@ -110,8 +113,6 @@ namespace logs
void set_level(const std::string&, level);
}
#define LOG_CHANNEL(ch, ...) ::logs::channel ch(#ch, ##__VA_ARGS__)
// Legacy:
#define LOG_SUCCESS(ch, fmt, ...) logs::ch.success("" fmt, ##__VA_ARGS__)
+13 -69
View File
@@ -4,7 +4,6 @@
#include "cfmt.h"
#include <algorithm>
#include <string_view>
#ifdef _WIN32
#include <Windows.h>
@@ -24,81 +23,34 @@ void fmt_class_string<std::pair<const fmt_type_info*, u64>>::format(std::string&
}
}
template <>
void fmt_class_string<fmt::base57>::format(std::string& out, u64 arg)
{
const auto& _arg = get_object(arg);
if (_arg.data && _arg.size)
{
// Precomputed tail sizes if input data is not multiple of 8
static constexpr u8 s_tail[8] = {0, 2, 3, 5, 6, 7, 9, 10};
// Get full output size
const std::size_t out_size = _arg.size / 8 * 11 + s_tail[_arg.size % 8];
out.resize(out.size() + out_size);
const auto ptr = &out.front() + (out.size() - out_size);
// Each 8 bytes of input data produce 11 bytes of base57 output
for (std::size_t i = 0, p = 0; i < _arg.size; i += 8, p += 11)
{
// Load up to 8 bytes
be_t<u64> be_value;
if (_arg.size - i < sizeof(be_value))
{
std::memset(&be_value, 0, sizeof(be_value));
std::memcpy(&be_value, _arg.data + i, _arg.size - i);
}
else
{
std::memcpy(&be_value, _arg.data + i, sizeof(be_value));
}
u64 value = be_value;
for (int j = 10; j >= 0; j--)
{
if (p + j < out_size)
{
ptr[p + j] = "0123456789ACEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"[value % 57];
}
value /= 57;
}
}
}
}
void fmt_class_string<const void*>::format(std::string& out, u64 arg)
{
fmt::append(out, "%p", arg);
if (arg)
{
fmt::append(out, "%p", reinterpret_cast<const void*>(static_cast<std::uintptr_t>(arg)));
}
else
{
out += "(NULL)";
}
}
void fmt_class_string<const char*>::format(std::string& out, u64 arg)
{
if (arg)
{
out += reinterpret_cast<const char*>(arg);
out += reinterpret_cast<const char*>(static_cast<std::uintptr_t>(arg));
}
else
{
out += "(NULLSTR)";
out += "(NULL)";
}
}
template <>
void fmt_class_string<std::string>::format(std::string& out, u64 arg)
{
out += get_object(arg);
}
template <>
void fmt_class_string<std::string_view>::format(std::string& out, u64 arg)
{
out += get_object(arg);
out += get_object(arg).c_str(); // TODO?
}
template <>
@@ -177,13 +129,13 @@ void fmt_class_string<ullong>::format(std::string& out, u64 arg)
template <>
void fmt_class_string<float>::format(std::string& out, u64 arg)
{
fmt::append(out, "%gf", static_cast<float>(std::bit_cast<f64>(arg)));
fmt::append(out, "%gf", static_cast<float>(reinterpret_cast<f64&>(arg)));
}
template <>
void fmt_class_string<double>::format(std::string& out, u64 arg)
{
fmt::append(out, "%g", std::bit_cast<f64>(arg));
fmt::append(out, "%g", reinterpret_cast<f64&>(arg));
}
template <>
@@ -423,14 +375,6 @@ std::string fmt::to_upper(const std::string& string)
return result;
}
std::string fmt::to_lower(const std::string& string)
{
std::string result;
result.resize(string.size());
std::transform(string.begin(), string.end(), result.begin(), ::tolower);
return result;
}
bool fmt::match(const std::string& source, const std::string& mask)
{
std::size_t source_position = 0, mask_position = 0;
+20 -33
View File
@@ -62,7 +62,7 @@ struct fmt_unveil<T, std::enable_if_t<std::is_floating_point<T>::value && sizeof
// Convert FP to f64 and reinterpret as u64
static inline u64 get(const f64& arg)
{
return std::bit_cast<u64>(arg);
return *reinterpret_cast<const u64*>(reinterpret_cast<const u8*>(&arg));
}
};
@@ -91,9 +91,9 @@ struct fmt_unveil<T, std::enable_if_t<std::is_enum<T>::value>>
template <typename T>
struct fmt_unveil<T*, void>
{
using type = std::add_const_t<T>*;
using type = const T*;
static inline u64 get(type arg)
static inline u64 get(const T* arg)
{
return reinterpret_cast<std::uintptr_t>(arg);
}
@@ -102,9 +102,9 @@ struct fmt_unveil<T*, void>
template <typename T, std::size_t N>
struct fmt_unveil<T[N], void>
{
using type = std::add_const_t<T>*;
using type = const T*;
static inline u64 get(type arg)
static inline u64 get(const T* arg)
{
return reinterpret_cast<std::uintptr_t>(arg);
}
@@ -140,9 +140,18 @@ struct fmt_class_string
// Enum -> string function type
using convert_t = const char*(*)(T value);
// Enum -> string function registered
static convert_t convert_enum;
// Helper function (safely converts arg to enum value)
static SAFE_BUFFERS FORCE_INLINE void format_enum(std::string& out, u64 arg, convert_t convert)
{
// Save convert function
if (convert_enum == nullptr)
{
convert_enum = convert;
}
const auto value = static_cast<std::underlying_type_t<T>>(arg);
// Check narrowing
@@ -194,6 +203,9 @@ struct fmt_class_string
static constexpr const char* unknown = nullptr;
};
template <typename T, typename V>
const char*(*fmt_class_string<T, V>::convert_enum)(T) = nullptr;
template <>
struct fmt_class_string<const void*, void>
{
@@ -238,38 +250,15 @@ using fmt_args_t = const u64(&&)[sizeof...(Args) + 1];
namespace fmt
{
// Base-57 format helper
struct base57
{
const uchar* data;
std::size_t size;
template <typename T>
base57(const T& arg)
: data(reinterpret_cast<const uchar*>(&arg))
, size(sizeof(T))
{
}
base57(const uchar* data, std::size_t size)
: data(data)
, size(size)
{
}
};
template <typename... Args>
SAFE_BUFFERS FORCE_INLINE const fmt_type_info* get_type_info()
{
// Constantly initialized null-terminated list of type-specific information
static constexpr fmt_type_info result[sizeof...(Args) + 1]{fmt_type_info::make<fmt_unveil_t<Args>>()...};
static constexpr fmt_type_info result[sizeof...(Args) + 1]{fmt_type_info::make<Args>()...};
return result;
}
template <typename... Args>
constexpr const fmt_type_info type_info_v[sizeof...(Args) + 1]{fmt_type_info::make<fmt_unveil_t<Args>>()...};
// Internal formatting function
void raw_append(std::string& out, const char*, const fmt_type_info*, const u64*) noexcept;
@@ -277,8 +266,7 @@ namespace fmt
template <typename... Args>
SAFE_BUFFERS FORCE_INLINE void append(std::string& out, const char* fmt, const Args&... args)
{
static constexpr fmt_type_info type_list[sizeof...(Args) + 1]{fmt_type_info::make<fmt_unveil_t<Args>>()...};
raw_append(out, fmt, type_list, fmt_args_t<Args...>{fmt_unveil<Args>::get(args)...});
raw_append(out, fmt, fmt::get_type_info<fmt_unveil_t<Args>...>(), fmt_args_t<Args...>{fmt_unveil<Args>::get(args)...});
}
// Formatting function
@@ -298,7 +286,6 @@ namespace fmt
template <typename T = std::runtime_error, typename... Args>
[[noreturn]] SAFE_BUFFERS FORCE_INLINE void throw_exception(const char* fmt, const Args&... args)
{
static constexpr fmt_type_info type_list[sizeof...(Args) + 1]{fmt_type_info::make<fmt_unveil_t<Args>>()...};
raw_throw_exception<T>(fmt, type_list, fmt_args_t<Args...>{fmt_unveil<Args>::get(args)...});
raw_throw_exception<T>(fmt, fmt::get_type_info<fmt_unveil_t<Args>...>(), fmt_args_t<Args...>{fmt_unveil<Args>::get(args)...});
}
}
+2 -10
View File
@@ -5,7 +5,6 @@
#include <string>
#include <vector>
#include <functional>
#include <string_view>
// Copy null-terminated string from std::string to char array with truncation
template <std::size_t N>
@@ -25,12 +24,6 @@ inline void strcpy_trunc(char (&dst)[N], const char (&src)[N2])
dst[count] = '\0';
}
template <std::size_t N>
inline bool ends_with(const std::string& src, const char (&end)[N])
{
return src.size() >= N - 1 && src.compare(src.size() - (N - 1), N - 1, end, N - 1) == 0;
}
namespace fmt
{
std::string replace_first(const std::string& src, const std::string& from, const std::string& to);
@@ -101,10 +94,10 @@ namespace fmt
auto end = source.end();
for (--end; it != end; ++it)
{
result += std::string{*it} + separator;
result += *it + separator;
}
return result + std::string{source.back()};
return result + source.back();
}
template <typename T>
@@ -135,7 +128,6 @@ namespace fmt
}
std::string to_upper(const std::string& string);
std::string to_lower(const std::string& string);
bool match(const std::string& source, const std::string& mask);
}
+314 -588
View File
File diff suppressed because it is too large Load Diff
+205 -312
View File
@@ -1,115 +1,101 @@
#pragma once
#include "types.h"
#include "util/atomic.hpp"
#include "Atomic.h"
#include <exception>
#include <string>
#include <memory>
#include <string_view>
#include "mutex.h"
#include "sema.h"
#include "cond.h"
#include "lockless.h"
// Report error and call std::abort(), defined in main.cpp
[[noreturn]] void report_fatal_error(const std::string&);
// Will report exception and call std::abort() if put in catch(...)
[[noreturn]] void catch_all_exceptions();
// Hardware core layout
enum class native_core_arrangement : u32
// Simple list of void() functors
class task_stack
{
undefined,
generic,
intel_ht,
amd_ccx
};
enum class thread_class : u32
{
general,
rsx,
spu,
ppu
};
enum class thread_state
{
created, // Initial state
detached, // The thread has been detached to destroy its own named_thread object (can be dangerously misused)
aborting, // The thread has been joined in the destructor or explicitly aborted (mutually exclusive with detached)
finished // Final state, always set at the end of thread execution
};
template <class Context>
class named_thread;
template <typename T>
struct result_storage
{
alignas(T) std::byte data[sizeof(T)];
static constexpr bool empty = false;
using type = T;
T* get()
struct task_base
{
std::unique_ptr<task_base> next;
virtual ~task_base();
virtual void invoke()
{
if (next)
{
next->invoke();
}
}
};
template <typename F>
struct task_type final : task_base
{
std::remove_reference_t<F> func;
task_type(F&& func)
: func(std::forward<F>(func))
{
}
void invoke() final override
{
func();
task_base::invoke();
}
};
std::unique_ptr<task_base> m_stack;
public:
task_stack() = default;
template <typename F>
task_stack(F&& func)
: m_stack(new task_type<F>(std::forward<F>(func)))
{
return reinterpret_cast<T*>(&data);
}
const T* get() const
void push(task_stack stack)
{
return reinterpret_cast<const T*>(&data);
auto _top = stack.m_stack.release();
auto _next = m_stack.release();
m_stack.reset(_top);
while (UNLIKELY(_top->next)) _top = _top->next.get();
_top->next.reset(_next);
}
void destroy() noexcept
void reset()
{
get()->~T();
m_stack.reset();
}
void invoke() const
{
if (m_stack)
{
m_stack->invoke();
}
}
};
template <>
struct result_storage<void>
// Thread control class
class thread_ctrl final
{
static constexpr bool empty = true;
// Current thread
static thread_local thread_ctrl* g_tls_this_thread;
using type = void;
};
template <class Context, typename... Args>
using result_storage_t = result_storage<std::invoke_result_t<Context, Args...>>;
// Detect on_abort() method (should return void)
template <typename T, typename = void>
struct thread_on_abort : std::bool_constant<false> {};
template <typename T>
struct thread_on_abort<T, decltype(std::declval<named_thread<T>&>().on_abort())> : std::bool_constant<true> {};
// Detect on_cleanup() static member function (should return void) (in C++20 can use destroying delete instead)
template <typename T, typename = void>
struct thread_on_cleanup : std::bool_constant<false> {};
template <typename T>
struct thread_on_cleanup<T, decltype(named_thread<T>::on_cleanup(std::declval<named_thread<T>*>()))> : std::bool_constant<true> {};
// Thread base class
class thread_base
{
// Native thread entry point function type
#ifdef _WIN32
using native_entry = uint(__stdcall*)(void* arg);
#else
using native_entry = void*(*)(void* arg);
#endif
// Self pointer
std::shared_ptr<thread_ctrl> m_self;
// Thread handle (platform-specific)
atomic_t<std::uintptr_t> m_thread{0};
// Thread mutex
mutable shared_mutex m_mutex;
mutable semaphore<> m_mutex;
// Thread condition variable
cond_variable m_cond;
@@ -118,123 +104,77 @@ class thread_base
atomic_t<u32> m_signal{0};
// Thread joining condition variable
mutable cond_variable m_jcv;
cond_variable m_jcv;
// Thread state
atomic_t<thread_state> m_state = thread_state::created;
// Remotely set or caught exception
std::exception_ptr m_exception;
// Thread name
lf_value<std::string> m_name;
// Thread initial task or atexit task
task_stack m_task;
//
atomic_t<u64> m_cycles = 0;
// Fixed name
std::string m_name;
// Start thread
void start(native_entry);
static void start(const std::shared_ptr<thread_ctrl>&, task_stack);
// Called at the thread start
void initialize();
// Called at the thread end, returns true if needs destruction
bool finalize(int) noexcept;
// Called at the thread end
void finalize(std::exception_ptr) noexcept;
// Cleanup after possibly deleting the thread instance
static void finalize() noexcept;
friend class thread_ctrl;
template <class Context>
friend class named_thread;
protected:
thread_base(std::string_view name);
~thread_base();
public:
// Get CPU cycles since last time this function was called. First call returns 0.
u64 get_cycles();
// Wait for the thread (it does NOT change thread state, and can be called from multiple threads)
void join() const;
// Notify the thread
void notify();
};
// Collection of global function for current thread
class thread_ctrl final
{
// Current thread
static thread_local thread_base* g_tls_this_thread;
// Target cpu core layout
static atomic_t<native_core_arrangement> g_native_core_layout;
// Add task (atexit)
static void _push(task_stack);
// Internal waiting function, may throw. Infinite value is -1.
static bool _wait_for(u64 usec);
friend class thread_base;
// Internal throwing function. Mutex must be locked and will be unlocked.
[[noreturn]] void _throw();
// Internal notification function
void _notify(cond_variable thread_ctrl::*);
public:
// Get current thread name
static std::string_view get_name()
{
return g_tls_this_thread->m_name.get();
}
thread_ctrl(std::string&& name);
thread_ctrl(const thread_ctrl&) = delete;
~thread_ctrl();
// Get thread name
template <typename T>
static std::string_view get_name(const named_thread<T>& thread)
const std::string& get_name() const
{
return static_cast<const thread_base&>(thread).m_name.get();
return m_name;
}
// Set current thread name (not recommended)
static void set_name(std::string_view name)
{
g_tls_this_thread->m_name.assign(name);
}
// Get exception
std::exception_ptr get_exception() const;
// Set thread name (not recommended)
template <typename T>
static void set_name(named_thread<T>& thread, std::string_view name)
{
static_cast<thread_base&>(thread).m_name.assign(name);
}
// Set exception
void set_exception(std::exception_ptr ptr);
template <typename T>
static u64 get_cycles(named_thread<T>& thread)
{
return static_cast<thread_base&>(thread).get_cycles();
}
// Get thread result (may throw, simultaneous joining allowed)
void join();
template <typename T>
static void notify(named_thread<T>& thread)
{
static_cast<thread_base&>(thread).notify();
}
// Notify the thread
void notify();
// Read current state
static inline thread_state state()
{
return g_tls_this_thread->m_state;
}
// Wait once with timeout. May spuriously return false.
// Wait once with timeout. Abortable, may throw. May spuriously return false.
static inline bool wait_for(u64 usec)
{
return _wait_for(usec);
}
// Wait.
// Wait. Abortable, may throw.
static inline void wait()
{
_wait_for(-1);
}
// Wait until pred().
template <typename F, typename RT = std::invoke_result_t<F>>
// Wait until pred(). Abortable, may throw.
template<typename F, typename RT = std::result_of_t<F()>>
static inline RT wait(F&& pred)
{
while (true)
@@ -248,183 +188,136 @@ public:
}
}
// Wait eternally until aborted.
[[noreturn]] static inline void eternalize()
{
while (true)
{
_wait_for(-1);
}
}
// Test exception (may throw).
static void test();
// Get current thread (may be nullptr)
static thread_base* get_current()
static thread_ctrl* get_current()
{
return g_tls_this_thread;
}
// Detect layout
static void detect_cpu_layout();
// Returns a core affinity mask. Set whether to generate the high priority set or not
static u64 get_affinity_mask(thread_class group);
// Sets the native thread priority
static void set_native_priority(int priority);
// Sets the preferred affinity mask for this thread
static void set_thread_affinity_mask(u64 mask);
// Spawn a detached named thread
template <typename F>
static void spawn(std::string_view name, F&& func)
// Register function at thread exit (for the current thread)
template<typename F>
static inline void atexit(F&& func)
{
new named_thread<F>(thread_state::detached, name, std::forward<F>(func));
_push(std::forward<F>(func));
}
// Create detached named thread
template<typename N, typename F>
static inline void spawn(N&& name, F&& func)
{
auto out = std::make_shared<thread_ctrl>(std::forward<N>(name));
thread_ctrl::start(out, std::forward<F>(func));
}
// Named thread factory
template<typename N, typename F>
static inline void spawn(std::shared_ptr<thread_ctrl>& out, N&& name, F&& func)
{
out = std::make_shared<thread_ctrl>(std::forward<N>(name));
thread_ctrl::start(out, std::forward<F>(func));
}
static void set_native_priority(int priority);
static void set_ideal_processor_core(int core);
};
// Derived from the callable object Context, possibly a lambda
template <class Context>
class named_thread final : public Context, result_storage_t<Context>, thread_base
class named_thread
{
using result = result_storage_t<Context>;
using thread = thread_base;
// Type-erased thread entry point
#ifdef _WIN32
static inline uint __stdcall entry_point(void* arg) try
#else
static inline void* entry_point(void* arg) try
#endif
{
const auto _this = static_cast<named_thread*>(static_cast<thread*>(arg));
// Perform self-cleanup if necessary
if (_this->entry_point())
{
// Call on_cleanup() static member function if it's available
if constexpr (thread_on_cleanup<Context>())
{
Context::on_cleanup(_this);
}
else
{
delete _this;
}
}
thread::finalize();
return 0;
}
catch (...)
{
catch_all_exceptions();
}
bool entry_point()
{
thread::initialize();
if constexpr (result::empty)
{
// No result
Context::operator()();
}
else
{
// Construct the result using placement new (copy elision should happen)
new (result::get()) typename result::type(Context::operator()());
}
return thread::finalize(0);
}
// Detached thread constructor
named_thread(thread_state s, std::string_view name, Context&& f)
: Context(std::forward<Context>(f))
, thread(name)
{
thread::m_state.raw() = s;
thread::start(&named_thread::entry_point);
}
friend class thread_ctrl;
// Pointer to managed resource (shared with actual thread)
std::shared_ptr<thread_ctrl> m_thread;
public:
// Normal forwarding constructor
template <typename... Args, typename = std::enable_if_t<std::is_constructible_v<Context, Args&&...>>>
named_thread(std::string_view name, Args&&... args)
: Context(std::forward<Args>(args)...)
, thread(name)
{
thread::start(&named_thread::entry_point);
}
named_thread();
// Lambda constructor, also the implicit deduction guide candidate
named_thread(std::string_view name, Context&& f)
: Context(std::forward<Context>(f))
, thread(name)
{
thread::start(&named_thread::entry_point);
}
virtual ~named_thread();
// Deleted copy/move constructors + copy/move operators
named_thread(const named_thread&) = delete;
named_thread& operator=(const named_thread&) = delete;
// Get thread name
virtual std::string get_name() const;
// Wait for the completion and access result (if not void)
[[nodiscard]] decltype(auto) operator()()
protected:
// Start thread (cannot be called from the constructor: should throw in such case)
void start_thread(const std::shared_ptr<void>& _this);
// Thread task (called in the thread)
virtual void on_task() = 0;
// Thread finalization (called after on_task)
virtual void on_exit() {}
// Called once upon thread spawn within the thread's own context
virtual void on_spawn() {}
public:
// ID initialization
virtual void on_init(const std::shared_ptr<void>& _this)
{
thread::join();
if constexpr (!result::empty)
{
return *result::get();
}
return start_thread(_this);
}
// Wait for the completion and access result (if not void)
[[nodiscard]] decltype(auto) operator()() const
// ID finalization
virtual void on_stop()
{
thread::join();
if constexpr (!result::empty)
{
return *result::get();
}
m_thread->join();
}
// Access thread state
operator thread_state() const
// Access thread_ctrl
thread_ctrl* get() const
{
return thread::m_state.load();
return m_thread.get();
}
// Try to abort/detach
named_thread& operator=(thread_state s)
void join() const
{
if (s != thread_state::aborting && s != thread_state::detached)
{
ASSUME(0);
}
if (thread::m_state.compare_and_swap_test(thread_state::created, s))
{
if (s == thread_state::aborting)
{
// Call on_abort() method if it's available
if constexpr (thread_on_abort<Context>())
{
Context::on_abort();
}
}
thread::notify();
}
return *this;
return m_thread->join();
}
// Context type doesn't need virtual destructor
~named_thread()
void notify() const
{
*this = thread_state::aborting;
thread::join();
if constexpr (!result::empty)
{
result::destroy();
}
return m_thread->notify();
}
};
// Wrapper for named thread, joins automatically in the destructor, can only be used in function scope
class scope_thread final
{
std::shared_ptr<thread_ctrl> m_thread;
public:
template<typename N, typename F>
scope_thread(N&& name, F&& func)
{
thread_ctrl::spawn(m_thread, std::forward<N>(name), std::forward<F>(func));
}
// Deleted copy/move constructors + copy/move operators
scope_thread(const scope_thread&) = delete;
// Destructor with exceptions allowed
~scope_thread() noexcept(false)
{
m_thread->join();
}
// Access thread_ctrl
thread_ctrl* get() const
{
return m_thread.get();
}
};
-9
View File
@@ -1,7 +1,5 @@
#pragma once
#include "types.h"
#include <chrono>
class Timer
@@ -51,11 +49,4 @@ public:
return std::chrono::duration_cast<std::chrono::nanoseconds>(now - m_start).count();
}
u64 GetMsSince(std::chrono::steady_clock::time_point timestamp)
{
std::chrono::steady_clock::time_point now = m_stopped ? m_end : std::chrono::steady_clock::now();
return std::chrono::duration_cast<std::chrono::milliseconds>(now - timestamp).count();
}
};
+4 -194
View File
@@ -7,27 +7,10 @@
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#endif
#ifdef __linux__
#include <sys/syscall.h>
#ifdef __NR_memfd_create
#elif __x86_64__
#define __NR_memfd_create 319
#elif __aarch64__
#define __NR_memfd_create 279
#endif
static int memfd_create_(const char *name, uint flags)
{
return syscall(__NR_memfd_create, name, flags);
}
#endif
namespace utils
{
// Convert memory protection (internal)
@@ -80,7 +63,7 @@ namespace utils
#ifdef _WIN32
verify(HERE), ::VirtualAlloc(pointer, size, MEM_COMMIT, +prot);
#else
verify(HERE), ::mprotect((void*)((u64)pointer & -4096), size + ((u64)pointer & 4095), +prot) != -1;
verify(HERE), ::mprotect((void*)((u64)pointer & -4096), ::align(size, 4096), +prot) != -1;
#endif
}
@@ -93,186 +76,13 @@ namespace utils
#endif
}
void memory_reset(void* pointer, std::size_t size, protection prot)
{
#ifdef _WIN32
memory_decommit(pointer, size);
memory_commit(pointer, size, prot);
#else
verify(HERE), ::mmap(pointer, size, +prot, MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0);
#endif
}
void memory_release(void* pointer, std::size_t size)
{
#ifdef _WIN32
verify(HERE), ::VirtualFree(pointer, 0, MEM_RELEASE);
#else
verify(HERE), ::munmap(pointer, size) != -1;
#endif
}
void memory_protect(void* pointer, std::size_t size, protection prot)
{
#ifdef _WIN32
for (u64 addr = (u64)pointer, end = addr + size; addr < end;)
{
const u64 boundary = (addr + 0x10000) & -0x10000;
const u64 block_size = std::min(boundary, end) - addr;
DWORD old;
if (!::VirtualProtect((LPVOID)addr, block_size, +prot, &old))
{
fmt::throw_exception("VirtualProtect failed (%p, 0x%x, addr=0x%x, error=%#x)", pointer, size, addr, GetLastError());
}
// Next region
addr += block_size;
}
DWORD old;
verify(HERE), ::VirtualProtect(pointer, size, +prot, &old);
#else
verify(HERE), ::mprotect((void*)((u64)pointer & -4096), size + ((u64)pointer & 4095), +prot) != -1;
#endif
}
shm::shm(u32 size)
: m_size(::align(size, 0x10000))
{
#ifdef _WIN32
m_handle = ::CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, 0, m_size, NULL);
verify(HERE), m_handle != INVALID_HANDLE_VALUE;
#elif __linux__
m_file = ::memfd_create_("", 0);
verify(HERE), m_file >= 0;
verify(HERE), ::ftruncate(m_file, m_size) >= 0;
#else
while ((m_file = ::shm_open("/rpcs3-mem1", O_RDWR | O_CREAT | O_EXCL, S_IWUSR | S_IRUSR)) == -1)
{
if (m_file == -1 && errno == EMFILE)
{
fmt::throw_exception("Too many open files. Raise the limit and try again.");
}
verify(HERE), errno == EEXIST;
}
verify(HERE), ::shm_unlink("/rpcs3-mem1") >= 0;
verify(HERE), ::ftruncate(m_file, m_size) >= 0;
#endif
}
shm::~shm()
{
#ifdef _WIN32
::CloseHandle(m_handle);
#else
::close(m_file);
#endif
}
u8* shm::map(void* ptr, protection prot) const
{
#ifdef _WIN32
DWORD access = FILE_MAP_WRITE;
switch (prot)
{
case protection::rw:
case protection::ro:
case protection::no:
break;
case protection::wx:
case protection::rx:
access |= FILE_MAP_EXECUTE;
break;
}
if (auto ret = static_cast<u8*>(::MapViewOfFileEx(m_handle, access, 0, 0, m_size, ptr)))
{
if (prot != protection::rw && prot != protection::wx)
{
DWORD old;
if (!::VirtualProtect(ret, m_size, +prot, &old))
{
::UnmapViewOfFile(ret);
return nullptr;
}
}
return ret;
}
return nullptr;
#else
return static_cast<u8*>(::mmap((void*)((u64)ptr & -0x10000), m_size, +prot, MAP_SHARED | (ptr ? MAP_FIXED : 0), m_file, 0));
#endif
}
u8* shm::map_critical(void* ptr, protection prot)
{
const auto target = (u8*)((u64)ptr & -0x10000);
#ifdef _WIN32
::MEMORY_BASIC_INFORMATION mem;
if (!::VirtualQuery(target, &mem, sizeof(mem)) || mem.State != MEM_RESERVE || !::VirtualFree(mem.AllocationBase, 0, MEM_RELEASE))
{
return nullptr;
}
const auto base = (u8*)mem.AllocationBase;
const auto size = mem.RegionSize + (target - base);
if (base < target && !::VirtualAlloc(base, target - base, MEM_RESERVE, PAGE_NOACCESS))
{
return nullptr;
}
if (target + m_size < base + size && !::VirtualAlloc(target + m_size, base + size - target - m_size, MEM_RESERVE, PAGE_NOACCESS))
{
return nullptr;
}
#endif
return this->map(target, prot);
}
void shm::unmap(void* ptr) const
{
#ifdef _WIN32
::UnmapViewOfFile(ptr);
#else
::munmap(ptr, m_size);
#endif
}
void shm::unmap_critical(void* ptr)
{
const auto target = (u8*)((u64)ptr & -0x10000);
this->unmap(target);
#ifdef _WIN32
::MEMORY_BASIC_INFORMATION mem, mem2;
if (!::VirtualQuery(target - 1, &mem, sizeof(mem)) || !::VirtualQuery(target + m_size, &mem2, sizeof(mem2)))
{
return;
}
if (mem.State == MEM_RESERVE && !::VirtualFree(mem.AllocationBase, 0, MEM_RELEASE))
{
return;
}
if (mem2.State == MEM_RESERVE && !::VirtualFree(mem2.AllocationBase, 0, MEM_RELEASE))
{
return;
}
const auto size1 = mem.State == MEM_RESERVE ? target - (u8*)mem.AllocationBase : 0;
const auto size2 = mem2.State == MEM_RESERVE ? mem2.RegionSize : 0;
if (!::VirtualAlloc(mem.State == MEM_RESERVE ? mem.AllocationBase : target, m_size + size1 + size2, MEM_RESERVE, PAGE_NOACCESS))
{
return;
}
verify(HERE), ::mprotect((void*)((u64)pointer & -4096), ::align(size, 4096), +prot) != -1;
#endif
}
}
+3 -46
View File
@@ -1,7 +1,5 @@
#pragma once
#include "types.h"
namespace utils
{
// Memory protection type
@@ -27,52 +25,11 @@ namespace utils
*/
void memory_commit(void* pointer, std::size_t size, protection prot = protection::rw);
// Decommit all memory committed via commit_page_memory.
/**
* Decommit all memory committed via commit_page_memory.
*/
void memory_decommit(void* pointer, std::size_t size);
// Decommit all memory and commit it again.
void memory_reset(void* pointer, std::size_t size, protection prot = protection::rw);
// Free memory after reserved by memory_reserve, should specify original size
void memory_release(void* pointer, std::size_t size);
// Set memory protection
void memory_protect(void* pointer, std::size_t size, protection prot);
// Shared memory handle
class shm
{
#ifdef _WIN32
void* m_handle;
#else
int m_file;
#endif
u32 m_size;
public:
explicit shm(u32 size);
shm(const shm&) = delete;
shm& operator=(const shm&) = delete;
~shm();
// Map shared memory
u8* map(void* ptr, protection prot = protection::rw) const;
// Map shared memory over reserved memory region, which is unsafe (non-atomic) under Win32
u8* map_critical(void* ptr, protection prot = protection::rw);
// Unmap shared memory
void unmap(void* ptr) const;
// Unmap shared memory, undoing map_critical
void unmap_critical(void* ptr);
u32 size() const
{
return m_size;
}
};
}
-592
View File
@@ -1,592 +0,0 @@
#pragma once
#include "types.h"
#include "StrFmt.h"
#include <vector>
namespace utils
{
class address_range_vector;
/**
* Constexprs
*/
constexpr inline u32 page_start(u32 addr)
{
return addr & ~4095u;
}
constexpr inline u32 next_page(u32 addr)
{
return page_start(addr + 4096u);
}
constexpr inline u32 page_end(u32 addr)
{
return next_page(addr) - 1;
}
constexpr inline u32 is_page_aligned(u32 addr)
{
return page_start(addr) == addr;
}
/**
* Address Range utility class
*/
class address_range
{
public:
u32 start = UINT32_MAX; // First address in range
u32 end = 0; // Last address
private:
// Helper constexprs
static constexpr inline bool range_overlaps(u32 start1, u32 end1, u32 start2, u32 end2)
{
return (start1 <= end2 && start2 <= end1);
}
static constexpr inline bool address_overlaps(u32 address, u32 start, u32 end)
{
return (start <= address && address <= end);
}
static constexpr inline bool range_inside_range(u32 start1, u32 end1, u32 start2, u32 end2)
{
return (start1 >= start2 && end1 <= end2);
}
address_range(u32 _start, u32 _end) : start(_start), end(_end) {}
public:
// Constructors
address_range() = default;
address_range(const address_range &other) : start(other.start), end(other.end) {}
static inline address_range start_length(u32 _start, u32 _length)
{
return address_range(_start, _start + (_length - 1));
}
static inline address_range start_end(u32 _start, u32 _end)
{
return address_range(_start, _end);
}
// Length
u32 length() const
{
AUDIT(valid());
return end - start + 1;
}
void set_length(const u32 new_length)
{
end = start + new_length - 1;
ASSERT(valid());
}
u32 next_address() const
{
return end + 1;
}
u32 prev_address() const
{
return start - 1;
}
// Overlapping checks
bool overlaps(const address_range &other) const
{
AUDIT(valid() && other.valid());
return range_overlaps(start, end, other.start, other.end);
}
bool overlaps(const u32 addr) const
{
AUDIT(valid());
return address_overlaps(addr, start, end);
}
bool inside(const address_range &other) const
{
AUDIT(valid() && other.valid());
return range_inside_range(start, end, other.start, other.end);
}
inline bool inside(const address_range_vector &vec) const;
inline bool overlaps(const address_range_vector &vec) const;
bool touches(const address_range &other) const
{
AUDIT(valid() && other.valid());
// returns true if there is overlap, or if sections are side-by-side
return overlaps(other) || other.start == next_address() || other.end == prev_address();
}
// Utilities
s32 signed_distance(const address_range &other) const
{
if (touches(other))
{
return 0;
}
// other after this
if (other.start > end)
{
return (s32)(other.start - end - 1);
}
// this after other
AUDIT(start > other.end);
return -((s32)(start - other.end - 1));
}
u32 distance(const address_range &other) const
{
if (touches(other))
{
return 0;
}
// other after this
if (other.start > end)
{
return (other.start - end - 1);
}
// this after other
AUDIT(start > other.end);
return (start - other.end - 1);
}
address_range get_min_max(const address_range &other) const
{
return {
std::min(valid() ? start : UINT32_MAX, other.valid() ? other.start : UINT32_MAX),
std::max(valid() ? end : 0, other.valid() ? other.end : 0)
};
}
void set_min_max(const address_range &other)
{
const address_range _range = get_min_max(other);
start = _range.start;
end = _range.end;
}
bool is_page_range() const
{
return (valid() && start % 4096u == 0 && length() % 4096u == 0);
}
address_range to_page_range() const
{
AUDIT(valid());
return { page_start(start), page_end(end) };
}
void page_align()
{
AUDIT(valid());
start = page_start(start);
end = page_end(end);
AUDIT(is_page_range());
}
address_range get_intersect(const address_range &clamp) const
{
if (!valid() || !clamp.valid())
{
return {};
}
return { std::max(start, clamp.start), std::min(end, clamp.end) };
}
void intersect(const address_range &clamp)
{
if (!clamp.valid())
{
invalidate();
}
else
{
start = std::max(start, clamp.start);
end = std::min(end, clamp.end);
}
}
// Validity
bool valid() const
{
return (start <= end);
}
void invalidate()
{
start = UINT32_MAX;
end = 0;
}
// Comparison Operators
bool operator ==(const address_range &other) const
{
return (start == other.start && end == other.end);
}
bool operator !=(const address_range &other) const
{
return (start != other.start || end != other.end);
}
/**
* Debug
*/
std::string str() const
{
return fmt::format("{0x%x->0x%x}", start, end);
}
};
static inline address_range page_for(u32 addr)
{
return address_range::start_end(page_start(addr), page_end(addr));
}
/**
* Address Range Vector utility class
*
* Collection of address_range objects. Allows for merging and removing ranges from the set.
*/
class address_range_vector
{
public:
using vector_type = typename std::vector<address_range>;
using iterator = typename vector_type::iterator;
using const_iterator = typename vector_type::const_iterator;
using size_type = typename vector_type::size_type;
private:
vector_type data;
public:
// Wrapped functions
inline void reserve(size_t nr) { data.reserve(nr); }
inline void clear() { data.clear(); }
inline size_type size() const { return data.size(); }
inline bool empty() const { return data.empty(); }
inline address_range& operator[](size_type n) { return data[n]; }
inline const address_range& operator[](size_type n) const { return data[n]; }
inline iterator begin() { return data.begin(); }
inline const_iterator begin() const { return data.begin(); }
inline iterator end() { return data.end(); }
inline const_iterator end() const { return data.end(); }
// Search for ranges that touch new_range. If found, merge instead of adding new_range.
// When adding a new range, re-use invalid ranges whenever possible
void merge(const address_range &new_range)
{
// Note the case where we have
// AAAA BBBB
// CCCC
// If we have data={A,B}, and new_range=C, we have to merge A with C, then B with A and invalidate B
if (!new_range.valid())
{
return;
}
address_range *found = nullptr;
address_range *invalid = nullptr;
for (auto &existing : data)
{
if (!existing.valid())
{
invalid = &existing;
continue;
}
// range1 overlaps, is immediately before, or is immediately after range2
if (existing.touches(new_range))
{
if (found != nullptr)
{
// Already found a match, merge and invalidate "existing"
found->set_min_max(existing);
existing.invalidate();
}
else
{
// First match, merge "new_range"
existing.set_min_max(new_range);
found = &existing;
}
}
}
if (found != nullptr)
{
return;
}
if (invalid != nullptr)
{
*invalid = new_range;
}
else
{
data.push_back(new_range);
}
AUDIT(check_consistency());
}
void merge(const address_range_vector &other)
{
for (const address_range &new_range : other)
{
merge(new_range);
}
}
// Exclude a given range from data
void exclude(const address_range &exclusion)
{
// Note the case where we have
// AAAAAAA
// EEE
// where data={A} and exclusion=E.
// In this case, we need to reduce A to the head (before E starts), and then create a new address_range B for the tail (after E ends), i.e.
// AA BB
// EEE
if (!exclusion.valid())
{
return;
}
address_range *invalid = nullptr; // try to re-use an invalid range instead of calling push_back
// We use index access because we might have to push_back within the loop, which could invalidate the iterators
size_type _size = data.size();
for (size_type n = 0; n < _size; ++n)
{
address_range &existing = data[n];
if (!existing.valid())
{
// Null
invalid = &existing;
continue;
}
if (!existing.overlaps(exclusion))
{
// No overlap, skip
continue;
}
const bool head_excluded = exclusion.overlaps(existing.start); // This section has its start inside excluded range
const bool tail_excluded = exclusion.overlaps(existing.end); // This section has its end inside excluded range
if (head_excluded && tail_excluded)
{
// Cannot be salvaged, fully excluded
existing.invalidate();
invalid = &existing;
}
else if (head_excluded)
{
// Head overlaps, truncate head
existing.start = exclusion.next_address();
}
else if (tail_excluded)
{
// Tail overlaps, truncate tail
existing.end = exclusion.prev_address();
}
else
{
// Section sits in the middle (see comment above function header)
AUDIT(exclusion.inside(existing));
const auto tail_end = existing.end;
// Head
existing.end = exclusion.prev_address();
// Tail
if (invalid != nullptr)
{
invalid->start = exclusion.next_address();
invalid->end = tail_end;
invalid = nullptr;
}
else
{
// IMPORTANT: adding to data invalidates "existing". This must be done last!
data.push_back(address_range::start_end(exclusion.next_address(), tail_end));
}
}
}
AUDIT(check_consistency());
AUDIT(!overlaps(exclusion));
}
void exclude(const address_range_vector &other)
{
for (const address_range &exclusion : other)
{
exclude(exclusion);
}
}
// Checks the consistency of this vector.
// Will fail if ranges within the vector overlap our touch each-other
bool check_consistency() const
{
size_t _size = data.size();
for (size_t i = 0; i < _size; ++i)
{
const auto &r1 = data[i];
if (!r1.valid())
{
continue;
}
for (size_t j = i + 1; j < _size; ++j)
{
const auto &r2 = data[j];
if (!r2.valid())
{
continue;
}
if (r1.touches(r2))
{
return false;
}
}
}
return true;
}
// Test for overlap with a given range
bool overlaps(const address_range &range) const
{
for (const address_range &current : data)
{
if (!current.valid())
{
continue;
}
if (current.overlaps(range))
{
return true;
}
}
return false;
}
// Test for overlap with a given address_range vector
bool overlaps(const address_range_vector &other) const
{
for (const address_range &rng1 : data)
{
if (!rng1.valid())
{
continue;
}
for (const address_range &rng2 : other.data)
{
if (!rng2.valid())
{
continue;
}
if (rng1.overlaps(rng2))
{
return true;
}
}
}
return false;
}
// Test if a given range is fully contained inside this vector
bool contains(const address_range &range) const
{
for (const address_range &cur : *this)
{
if (!cur.valid())
{
continue;
}
if (range.inside(cur))
{
return true;
}
}
return false;
}
// Test if all ranges in this vector are full contained inside a specific range
bool inside(const address_range &range) const
{
for (const address_range &cur : *this)
{
if (!cur.valid())
{
continue;
}
if (!cur.inside(range))
{
return false;
}
}
return true;
}
};
// These declarations must be done after address_range_vector has been defined
bool address_range::inside(const address_range_vector &vec) const
{
return vec.contains(*this);
}
bool address_range::overlaps(const address_range_vector &vec) const
{
return vec.overlaps(*this);
}
} // namespace utils
namespace std {
static_assert(sizeof(size_t) >= 2 * sizeof(u32), "size_t must be at least twice the size of u32");
template <>
struct hash<utils::address_range>
{
std::size_t operator()(const utils::address_range& k) const
{
// we can guarantee a unique hash since our type is 64 bits and size_t as well
return (size_t{ k.start } << 32) | size_t{ k.end };
}
};
}
-228
View File
@@ -1,228 +0,0 @@
#pragma once
#include "types.h"
namespace utils
{
inline u32 cntlz32(u32 arg, bool nonzero = false)
{
#ifdef _MSC_VER
ulong res;
return _BitScanReverse(&res, arg) || nonzero ? res ^ 31 : 32;
#elif __LZCNT__
return _lzcnt_u32(arg);
#else
return arg || nonzero ? __builtin_clz(arg) : 32;
#endif
}
inline u64 cntlz64(u64 arg, bool nonzero = false)
{
#ifdef _MSC_VER
ulong res;
return _BitScanReverse64(&res, arg) || nonzero ? res ^ 63 : 64;
#elif __LZCNT__
return _lzcnt_u64(arg);
#else
return arg || nonzero ? __builtin_clzll(arg) : 64;
#endif
}
inline u32 cnttz32(u32 arg, bool nonzero = false)
{
#ifdef _MSC_VER
ulong res;
return _BitScanForward(&res, arg) || nonzero ? res : 32;
#elif __BMI__
return _tzcnt_u32(arg);
#else
return arg || nonzero ? __builtin_ctz(arg) : 32;
#endif
}
inline u64 cnttz64(u64 arg, bool nonzero = false)
{
#ifdef _MSC_VER
ulong res;
return _BitScanForward64(&res, arg) || nonzero ? res : 64;
#elif __BMI__
return _tzcnt_u64(arg);
#else
return arg || nonzero ? __builtin_ctzll(arg) : 64;
#endif
}
inline u8 popcnt32(u32 arg)
{
#ifdef _MSC_VER
const u32 a1 = arg & 0x55555555;
const u32 a2 = (arg >> 1) & 0x55555555;
const u32 a3 = a1 + a2;
const u32 b1 = a3 & 0x33333333;
const u32 b2 = (a3 >> 2) & 0x33333333;
const u32 b3 = b1 + b2;
const u32 c3 = (b3 + (b3 >> 4)) & 0x0f0f0f0f;
const u32 d3 = c3 + (c3 >> 8);
return static_cast<u8>(d3 + (d3 >> 16));
#else
return __builtin_popcount(arg);
#endif
}
// Rotate helpers
#if defined(__GNUG__)
inline u8 rol8(u8 x, u8 n)
{
#if __has_builtin(__builtin_rotateleft8)
return __builtin_rotateleft8(x, n);
#else
u8 result = x;
__asm__("rolb %[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
return result;
#endif
}
inline u8 ror8(u8 x, u8 n)
{
#if __has_builtin(__builtin_rotateright8)
return __builtin_rotateright8(x, n);
#else
u8 result = x;
__asm__("rorb %[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
return result;
#endif
}
inline u16 rol16(u16 x, u16 n)
{
#if __has_builtin(__builtin_rotateleft16)
return __builtin_rotateleft16(x, n);
#else
u16 result = x;
__asm__("rolw %b[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
return result;
#endif
}
inline u16 ror16(u16 x, u16 n)
{
#if __has_builtin(__builtin_rotateright16)
return __builtin_rotateright16(x, n);
#else
u16 result = x;
__asm__("rorw %b[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
return result;
#endif
}
inline u32 rol32(u32 x, u32 n)
{
#if __has_builtin(__builtin_rotateleft32)
return __builtin_rotateleft32(x, n);
#else
u32 result = x;
__asm__("roll %b[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
return result;
#endif
}
inline u32 ror32(u32 x, u32 n)
{
#if __has_builtin(__builtin_rotateright32)
return __builtin_rotateright32(x, n);
#else
u32 result = x;
__asm__("rorl %b[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
return result;
#endif
}
inline u64 rol64(u64 x, u64 n)
{
#if __has_builtin(__builtin_rotateleft64)
return __builtin_rotateleft64(x, n);
#else
u64 result = x;
__asm__("rolq %b[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
return result;
#endif
}
inline u64 ror64(u64 x, u64 n)
{
#if __has_builtin(__builtin_rotateright64)
return __builtin_rotateright64(x, n);
#else
u64 result = x;
__asm__("rorq %b[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
return result;
#endif
}
constexpr u64 umulh64(u64 a, u64 b)
{
const __uint128_t x = a;
const __uint128_t y = b;
return (x * y) >> 64;
}
constexpr s64 mulh64(s64 a, s64 b)
{
const __int128_t x = a;
const __int128_t y = b;
return (x * y) >> 64;
}
#elif defined(_MSC_VER)
inline u8 rol8(u8 x, u8 n)
{
return _rotl8(x, n);
}
inline u8 ror8(u8 x, u8 n)
{
return _rotr8(x, n);
}
inline u16 rol16(u16 x, u16 n)
{
return _rotl16(x, (u8)n);
}
inline u16 ror16(u16 x, u16 n)
{
return _rotr16(x, (u8)n);
}
inline u32 rol32(u32 x, u32 n)
{
return _rotl(x, (int)n);
}
inline u32 ror32(u32 x, u32 n)
{
return _rotr(x, (int)n);
}
inline u64 rol64(u64 x, u64 n)
{
return _rotl64(x, (int)n);
}
inline u64 ror64(u64 x, u64 n)
{
return _rotr64(x, (int)n);
}
inline u64 umulh64(u64 x, u64 y)
{
return __umulh(x, y);
}
inline s64 mulh64(s64 x, s64 y)
{
return __mulh(x, y);
}
#endif
} // namespace utils
+6 -16
View File
@@ -1,5 +1,5 @@
#include "bin_patch.h"
#include <yaml-cpp/yaml.h>
#include "yaml-cpp/yaml.h"
#include "File.h"
#include "Config.h"
@@ -32,23 +32,13 @@ void patch_engine::append(const std::string& patch)
{
if (fs::file f{patch})
{
YAML::Node root;
try
{
root = YAML::Load(f.to_string());
}
catch (const std::exception& e)
{
LOG_FATAL(GENERAL, "Failed to load patch file %s\n%s thrown: %s", patch, typeid(e).name(), e.what());
return;
}
auto root = YAML::Load(f.to_string());
for (auto pair : root)
{
auto& name = pair.first.Scalar();
auto& data = m_map[name];
for (auto patch : pair.second)
{
u64 type64 = 0;
@@ -86,13 +76,13 @@ void patch_engine::append(const std::string& patch)
case patch_type::bef32:
case patch_type::lef32:
{
info.value = std::bit_cast<u32>(patch[2].as<f32>());
info.value_as<f32>() = patch[2].as<f32>();
break;
}
case patch_type::bef64:
case patch_type::lef64:
{
info.value = std::bit_cast<u64>(patch[2].as<f64>());
info.value_as<f64>() = patch[2].as<f64>();
break;
}
default:
@@ -101,7 +91,7 @@ void patch_engine::append(const std::string& patch)
break;
}
}
data.emplace_back(info);
}
}
+6
View File
@@ -28,6 +28,12 @@ class patch_engine
patch_type type;
u32 offset;
u64 value;
template <typename T>
T& value_as()
{
return *reinterpret_cast<T*>(reinterpret_cast<char*>(&value));
}
};
// Database
+603 -284
View File
File diff suppressed because it is too large Load Diff
+19 -25
View File
@@ -1,14 +1,11 @@
#pragma once
#include "types.h"
#include "asm.h"
#include <climits>
#include <string>
#include <vector>
#include <algorithm>
static const size_t size_dropped = std::numeric_limits<size_t>::max();
/*
C-style format parser. Appends formatted string to `out`, returns number of characters written.
`out`: mutable reference to std::string, std::vector<char> or other compatible container
@@ -43,7 +40,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
const auto drop_sequence = [&]
{
out.insert(out.end(), fmt - ctx.size, fmt);
ctx.size = size_dropped;
ctx.size = -1;
};
const auto read_decimal = [&](uint result) -> uint
@@ -59,7 +56,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
const auto write_octal = [&](u64 value, u64 min_num)
{
out.resize(out.size() + std::max<u64>(min_num, 66 / 3 - (utils::cntlz64(value | 1, true) + 2) / 3), '0');
out.resize(out.size() + std::max<u64>(min_num, 66 / 3 - (cntlz64(value | 1, true) + 2) / 3), '0');
// Write in reversed order
for (auto i = out.rbegin(); value; i++, value /= 8)
@@ -70,8 +67,8 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
const auto write_hex = [&](u64 value, bool upper, u64 min_num)
{
out.resize(out.size() + std::max<u64>(min_num, 64 / 4 - utils::cntlz64(value | 1, true) / 4), '0');
out.resize(out.size() + std::max<u64>(min_num, 64 / 4 - cntlz64(value | 1, true) / 4), '0');
// Write in reversed order
for (auto i = out.rbegin(); value; i++, value /= 16)
{
@@ -114,7 +111,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
ctx = {0};
out.push_back(ch);
}
else if (ctx.size == size_dropped)
else if (ctx.size == -1)
{
out.push_back(ch);
}
@@ -144,7 +141,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
{
ctx.width = read_decimal(ch - '0');
}
break;
}
@@ -160,7 +157,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
ctx.width = std::abs(warg);
ctx.left |= warg < 0;
}
break;
}
@@ -310,7 +307,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
const std::size_t start = out.size();
const std::size_t size1 = src.fmt_string(out, ctx.args);
if (ctx.dot && size1 > ctx.prec)
{
// Shrink if necessary
@@ -412,10 +409,9 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
}
const u64 mask =
ctx.type == 1 ? 0xff :
ctx.type == 2 ? 0xffff :
ctx.type == 4 ? 0xffff'ffffu :
0xffff'ffff'ffff'ffffu;
ctx.type == 1 ? 0xffull :
ctx.type == 2 ? 0xffffull :
ctx.type == 4 ? 0xffffffffull : 0xffffffffffffffffull;
// Trunc sign-extended signed types
const u64 val = src.template get<u64>(ctx.args) & mask;
@@ -469,10 +465,9 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
}
const u64 mask =
ctx.type == 1 ? 0xff :
ctx.type == 2 ? 0xffff :
ctx.type == 4 ? 0xffff'ffffu :
0xffff'ffff'ffff'ffffu;
ctx.type == 1 ? 0xffull :
ctx.type == 2 ? 0xffffull :
ctx.type == 4 ? 0xffffffffull : 0xffffffffffffffffull;
// Trunc sign-extended signed types
const u64 val = src.template get<u64>(ctx.args) & mask;
@@ -533,10 +528,9 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
}
const u64 mask =
ctx.type == 1 ? 0xff :
ctx.type == 2 ? 0xffff :
ctx.type == 4 ? 0xffff'ffffu :
0xffff'ffff'ffff'ffffu;
ctx.type == 1 ? 0xffull :
ctx.type == 2 ? 0xffffull :
ctx.type == 4 ? 0xffffffffull : 0xffffffffffffffffull;
// Trunc sign-extended signed types
const u64 val = src.template get<u64>(ctx.args) & mask;
@@ -582,7 +576,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
// Add padding if necessary
out.insert(ctx.left ? out.end() : out.begin() + start, ctx.width - size2, ' ');
}
src.skip(ctx.args);
ctx = {0};
break;
@@ -631,7 +625,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
}
// Handle unfinished sequence
if (ctx.size && ctx.size != size_dropped)
if (ctx.size && ctx.size != -1)
{
fmt--, drop_sequence();
}
+77 -447
View File
@@ -1,6 +1,5 @@
#include "cond.h"
#include "sync.h"
#include "lockless.h"
#include <limits.h>
@@ -10,468 +9,99 @@
bool cond_variable::imp_wait(u32 _old, u64 _timeout) noexcept
{
verify("cond_variable overflow" HERE), (_old & 0xffff) != 0xffff; // Very unlikely: it requires 65535 distinct threads to wait simultaneously
return balanced_wait_until(m_value, _timeout, [&](u32& value, auto... ret) -> int
{
if (value >> 16)
{
// Success
value -= 0x10001;
return +1;
}
if constexpr (sizeof...(ret))
{
// Retire
value -= 1;
return -1;
}
return 0;
});
verify(HERE), _old != -1; // Very unlikely: it requires 2^32 distinct threads to wait simultaneously
#ifdef _WIN32
if (_old >= 0x10000 && !OptWaitOnAddress && m_value)
LARGE_INTEGER timeout;
timeout.QuadPart = _timeout * -10;
if (HRESULT rc = NtWaitForKeyedEvent(nullptr, &m_value, false, _timeout == -1 ? nullptr : &timeout))
{
// Workaround possibly stolen signal
imp_wake(1);
verify(HERE), rc == WAIT_TIMEOUT;
// Retire
if (!m_value.fetch_op([](u32& value) { if (value) value--; }))
{
NtWaitForKeyedEvent(nullptr, &m_value, false, nullptr);
return true;
}
return false;
}
return true;
#else
timespec timeout;
timeout.tv_sec = _timeout / 1000000;
timeout.tv_nsec = (_timeout % 1000000) * 1000;
for (u32 value = _old + 1;; value = m_value)
{
const int err = futex((int*)&m_value.raw(), FUTEX_WAIT_PRIVATE, value, _timeout == -1 ? nullptr : &timeout, nullptr, 0) == 0
? 0
: errno;
// Normal or timeout wakeup
if (!err || (_timeout != -1 && err == ETIMEDOUT))
{
// Cleanup (remove waiter)
verify(HERE), m_value--;
return !err;
}
// Not a wakeup
verify(HERE), err == EAGAIN;
}
#endif
}
void cond_variable::imp_wake(u32 _count) noexcept
{
// TODO (notify_one)
balanced_awaken<true>(m_value, m_value.atomic_op([&](u32& value) -> u32
#ifdef _WIN32
// Try to subtract required amount of waiters
const u32 count = m_value.atomic_op([=](u32& value)
{
// Subtract already signaled number from total amount of waiters
const u32 can_sig = (value & 0xffff) - (value >> 16);
const u32 num_sig = std::min<u32>(can_sig, _count);
value += num_sig << 16;
return num_sig;
}));
}
bool notifier::imp_try_lock(u32 count)
{
return m_counter.atomic_op([&](u32& value)
{
if ((value % (max_readers + 1)) + count <= max_readers)
if (value > _count)
{
value += count;
return true;
value -= _count;
return _count;
}
return false;
});
}
void notifier::imp_unlock(u32 count)
{
const u32 counter = m_counter.sub_fetch(count);
if (UNLIKELY(counter % (max_readers + 1)))
{
return;
}
if (counter)
{
const u32 _old = m_counter.atomic_op([](u32& value) -> u32
{
if (value % (max_readers + 1))
{
return 0;
}
return std::exchange(value, 0) / (max_readers + 1);
});
const u32 wc = m_cond.m_value;
if (_old && wc)
{
m_cond.imp_wake(_old > wc ? wc : _old);
}
}
}
u32 notifier::imp_notify(u32 count)
{
return m_counter.atomic_op([&](u32& value) -> u32
{
if (const u32 add = value % (max_readers + 1))
{
// Mutex is locked
const u32 result = add > count ? count : add;
value += result * (max_readers + 1);
return result;
}
else
{
// Mutex is unlocked
value = 0;
return count;
}
});
}
bool notifier::wait(u64 usec_timeout)
{
const u32 _old = m_cond.m_value.fetch_add(1);
if (max_readers < m_counter.fetch_op([](u32& value)
{
if (value > max_readers)
{
value -= max_readers;
}
value -= 1;
}))
{
// Return without waiting
m_cond.imp_wait(_old, 0);
return true;
}
const bool res = m_cond.imp_wait(_old, usec_timeout);
while (!try_lock_shared())
{
// TODO
busy_wait();
}
return res;
}
bool unique_cond::imp_wait(u64 _timeout) noexcept
{
// State transition: c_sig -> c_lock \ c_lock -> c_wait
const u32 _old = m_value.fetch_sub(1);
if (LIKELY(_old == c_sig))
return true;
return balanced_wait_until(m_value, _timeout, [&](u32& value, auto... ret) -> int
{
if (value == c_sig)
{
value = c_lock;
return +1;
}
if constexpr (sizeof...(ret))
{
value = c_lock;
return -1;
}
return 0;
});
}
void unique_cond::imp_notify() noexcept
{
auto [old, ok] = m_value.fetch_op([](u32& v)
{
if (UNLIKELY(v > 0 && v < c_sig))
{
v = c_sig;
return true;
}
return false;
return std::exchange(value, 0);
});
verify(HERE), old <= c_sig;
if (LIKELY(!ok || old == c_lock))
for (u32 i = count; i > 0; i--)
{
return;
NtReleaseKeyedEvent(nullptr, &m_value, false, nullptr);
}
#else
for (u32 i = _count; i > 0; std::this_thread::yield())
{
const u32 value = m_value;
balanced_awaken(m_value, 1);
}
bool shared_cond::imp_wait(u32 slot, u64 _timeout) noexcept
{
if (slot >= 32)
{
// Invalid argument, assume notified
return true;
}
const u64 wait_bit = c_wait << slot;
const u64 lock_bit = c_lock << slot;
// Change state from c_lock to c_wait
const u64 old_ = m_cvx32.fetch_op([=](u64& cvx32)
{
if (cvx32 & wait_bit)
{
// c_lock -> c_wait
cvx32 &= ~(lock_bit & ~wait_bit);
}
else
{
// c_sig -> c_lock
cvx32 |= lock_bit;
}
});
if ((old_ & wait_bit) == 0)
{
// Already signaled, return without waiting
return true;
}
return balanced_wait_until(m_cvx32, _timeout, [&](u64& cvx32, auto... ret) -> int
{
if ((cvx32 & wait_bit) == 0)
{
// c_sig -> c_lock
cvx32 |= lock_bit;
return +1;
}
if constexpr (sizeof...(ret))
{
// Retire
cvx32 |= lock_bit;
return -1;
}
return 0;
});
}
void shared_cond::imp_notify() noexcept
{
auto [old, ok] = m_cvx32.fetch_op([](u64& cvx32)
{
if (const u64 sig_mask = cvx32 & 0xffffffff)
{
cvx32 &= 0xffffffffull << 32;
cvx32 |= sig_mask << 32;
return true;
}
return false;
});
// Determine if some waiters need a syscall notification
const u64 wait_mask = old & (~old >> 32);
if (UNLIKELY(!ok || !wait_mask))
{
return;
}
balanced_awaken<true>(m_cvx32, utils::popcnt32(wait_mask));
}
void shared_cond::wait_all() noexcept
{
// Try to acquire waiting state without locking but only if there are other locks
const auto [old_, result] = m_cvx32.fetch_op([](u64& cvx32) -> u64
{
// Check waiting alone
if ((cvx32 & 0xffffffff) == 0)
{
return 0;
}
// Combine used bits and invert to find least significant bit unused
const u32 slot = utils::cnttz64(~((cvx32 & 0xffffffff) | (cvx32 >> 32)), true);
// Set waiting bit (does nothing if all slots are used)
cvx32 |= (1ull << slot) & 0xffffffff;
return 1ull << slot;
});
if (!result)
{
return;
}
if (result > 0xffffffffu)
{
// All slots are used, fallback to spin wait
while (m_cvx32 & 0xffffffff)
{
busy_wait();
}
return;
}
const u64 wait_bit = result;
const u64 lock_bit = wait_bit | (wait_bit << 32);
balanced_wait_until(m_cvx32, -1, [&](u64& cvx32, auto... ret) -> int
{
if ((cvx32 & wait_bit) == 0)
{
// Remove signal and unlock at once
cvx32 &= ~lock_bit;
return +1;
}
if constexpr (sizeof...(ret))
{
cvx32 &= ~lock_bit;
return -1;
}
return 0;
});
}
bool shared_cond::wait_all(shared_cond::shared_lock& lock) noexcept
{
AUDIT(lock.m_this == this);
if (lock.m_slot >= 32)
{
// Invalid argument, assume notified
return true;
}
const u64 wait_bit = c_wait << lock.m_slot;
const u64 lock_bit = c_lock << lock.m_slot;
// Try to acquire waiting state only if there are other locks
const auto [old_, not_alone] = m_cvx32.fetch_op([&](u64& cvx32)
{
// Check locking alone
if (((cvx32 >> 32) & cvx32) == (lock_bit >> 32))
{
return false;
}
// c_lock -> c_wait, c_sig -> unlock
cvx32 &= ~(lock_bit & ~wait_bit);
return true;
});
if (!not_alone)
{
return false;
}
else
{
// Set invalid slot to acknowledge unlocking
lock.m_slot = 33;
}
if ((old_ & wait_bit) == 0)
{
// Already signaled, return without waiting
return true;
}
balanced_wait_until(m_cvx32, -1, [&](u64& cvx32, auto... ret) -> int
{
if ((cvx32 & wait_bit) == 0)
{
// Remove signal and unlock at once
cvx32 &= ~lock_bit;
return +1;
}
if constexpr (sizeof...(ret))
{
cvx32 &= ~lock_bit;
return -1;
}
return 0;
});
return true;
}
bool shared_cond::notify_all(shared_cond::shared_lock& lock) noexcept
{
AUDIT(lock.m_this == this);
if (lock.m_slot >= 32)
{
// Invalid argument
return false;
}
const u64 slot_mask = c_sig << lock.m_slot;
auto [old, ok] = m_cvx32.fetch_op([&](u64& cvx32)
{
if (((cvx32 << 32) & cvx32) != slot_mask)
{
return false;
}
if (const u64 sig_mask = cvx32 & 0xffffffff)
{
cvx32 &= (0xffffffffull << 32) & ~slot_mask;
cvx32 |= (sig_mask << 32) & ~slot_mask;
return true;
}
return false;
});
if (!ok)
{
// Not an exclusive reader
return false;
}
// Set invalid slot to acknowledge unlocking
lock.m_slot = 34;
// Determine if some waiters need a syscall notification
const u64 wait_mask = old & (~old >> 32);
if (UNLIKELY(!wait_mask))
{
return true;
}
balanced_awaken<true>(m_cvx32, utils::popcnt32(wait_mask));
return true;
}
bool lf_queue_base::wait(u64 _timeout)
{
auto _old = m_head.compare_and_swap(0, 1);
if (_old)
{
verify("lf_queue concurrent wait" HERE), _old != 1;
return true;
}
return balanced_wait_until(m_head, _timeout, [](std::uintptr_t& head, auto... ret) -> int
{
if (head != 1)
{
return +1;
}
if constexpr (sizeof...(ret))
{
head = 0;
return -1;
}
return 0;
});
}
void lf_queue_base::imp_notify()
{
balanced_awaken(m_head, 1);
// Constrain remaining amount with imaginary waiter count
if (i > value)
{
i = value;
}
if (!value || i == 0)
{
// Nothing to do
return;
}
if (const int res = futex((int*)&m_value.raw(), FUTEX_WAKE_PRIVATE, i > INT_MAX ? INT_MAX : i, nullptr, nullptr, 0))
{
verify(HERE), res >= 0 && (u32)res <= i;
i -= res;
}
if (!m_value || i == 0)
{
// Escape
return;
}
}
#endif
}
+3 -237
View File
@@ -1,9 +1,7 @@
#pragma once
#include "types.h"
#include "util/atomic.hpp"
#include <shared_mutex>
#include "asm.h"
#include "Atomic.h"
// Lightweight condition variable
class cond_variable
@@ -11,8 +9,6 @@ class cond_variable
// Internal waiter counter
atomic_t<u32> m_value{0};
friend class notifier;
protected:
// Internal waiting function
bool imp_wait(u32 _old, u64 _timeout) noexcept;
@@ -25,7 +21,7 @@ public:
// Intrusive wait algorithm for lockable objects
template <typename T>
bool wait(T& object, u64 usec_timeout = -1)
explicit_bool_t wait(T& object, u64 usec_timeout = -1)
{
const u32 _old = m_value.fetch_add(1); // Increment waiter counter
object.unlock();
@@ -34,15 +30,6 @@ public:
return res;
}
// Unlock all specified objects but don't lock them again
template <typename... Locks>
bool wait_unlock(u64 usec_timeout, Locks&&... locks)
{
const u32 _old = m_value.fetch_add(1); // Increment waiter counter
(..., std::forward<Locks>(locks).unlock());
return imp_wait(_old, usec_timeout);
}
// Wake one thread
void notify_one() noexcept
{
@@ -57,228 +44,7 @@ public:
{
if (m_value)
{
imp_wake(65535);
imp_wake(-1);
}
}
static constexpr u64 max_timeout = u64{UINT32_MAX} / 1000 * 1000000;
};
// Pair of a fake shared mutex (only limited shared locking) and a condition variable. Obsolete.
class notifier
{
atomic_t<u32> m_counter{0};
cond_variable m_cond;
bool imp_try_lock(u32 count);
void imp_unlock(u32 count);
u32 imp_notify(u32 count);
public:
constexpr notifier() = default;
bool try_lock()
{
return imp_try_lock(max_readers);
}
void unlock()
{
imp_unlock(max_readers);
}
bool try_lock_shared()
{
return imp_try_lock(1);
}
void unlock_shared()
{
imp_unlock(1);
}
bool wait(u64 usec_timeout = -1);
void notify_all()
{
if (m_counter)
{
imp_notify(-1);
}
// Notify after imaginary "exclusive" lock+unlock
m_cond.notify_all();
}
void notify_one()
{
// TODO
if (m_counter)
{
if (imp_notify(1))
{
return;
}
}
m_cond.notify_one();
}
static constexpr u32 max_readers = 0x7f;
};
// Condition variable fused with a pseudo-mutex which is never supposed to be locked concurrently.
class unique_cond
{
enum : u32
{
c_wait = 1,
c_lock = 2,
c_sig = 3,
};
atomic_t<u32> m_value{0};
bool imp_wait(u64 _timeout) noexcept;
void imp_notify() noexcept;
public:
constexpr unique_cond() = default;
void lock() noexcept
{
// Shouldn't be locked by more than one thread concurrently
while (UNLIKELY(!m_value.compare_and_swap_test(0, c_lock)))
;
}
void unlock() noexcept
{
m_value = 0;
}
bool wait(std::unique_lock<unique_cond>& lock, u64 usec_timeout = -1) noexcept
{
AUDIT(lock.owns_lock());
AUDIT(lock.mutex() == this);
return imp_wait(usec_timeout);
}
void notify() noexcept
{
// Early exit if notification is not required
if (LIKELY(!m_value))
return;
imp_notify();
}
};
// Condition variable fused with a pseudo-mutex supporting only reader locks (up to 32 readers).
class shared_cond
{
// For information, shouldn't modify
enum : u64
{
// Wait bit is aligned for compatibility with 32-bit futex.
c_wait = 1,
c_sig = 1ull << 32,
c_lock = 1ull << 32 | 1,
};
// Split in 32-bit parts for convenient bit combining
atomic_t<u64> m_cvx32{0};
class shared_lock
{
shared_cond* m_this;
u32 m_slot;
friend class shared_cond;
public:
shared_lock(shared_cond* _this) noexcept
: m_this(_this)
{
// Lock and remember obtained slot index
m_slot = m_this->m_cvx32.atomic_op([](u64& cvx32)
{
// Combine used bits and invert to find least significant bit unused
const u32 slot = static_cast<u32>(utils::cnttz64(~((cvx32 & 0xffffffff) | (cvx32 >> 32)), true));
// Set lock bits (does nothing if all slots are used)
const u64 bit = (1ull << slot) & 0xffffffff;
cvx32 |= bit | (bit << 32);
return slot;
});
}
shared_lock(const shared_lock&) = delete;
shared_lock(shared_lock&& rhs)
: m_this(rhs.m_this)
, m_slot(rhs.m_slot)
{
rhs.m_slot = 32;
}
shared_lock& operator=(const shared_lock&) = delete;
~shared_lock()
{
// Clear the slot (does nothing if all slots are used)
const u64 bit = (1ull << m_slot) & 0xffffffff;
m_this->m_cvx32 &= ~(bit | (bit << 32));
}
explicit operator bool() const noexcept
{
// Check success
return m_slot < 32;
}
bool wait(u64 usec_timeout = -1) const noexcept
{
return m_this->wait(*this, usec_timeout);
}
};
bool imp_wait(u32 slot, u64 _timeout) noexcept;
void imp_notify() noexcept;
public:
constexpr shared_cond() = default;
shared_lock try_shared_lock() noexcept
{
return shared_lock(this);
}
u32 count() const noexcept
{
const u64 cvx32 = m_cvx32;
return utils::popcnt32(static_cast<u32>(cvx32 | (cvx32 >> 32)));
}
bool wait(shared_lock const& lock, u64 usec_timeout = -1) noexcept
{
AUDIT(lock.m_this == this);
return imp_wait(lock.m_slot, usec_timeout);
}
void wait_all() noexcept;
bool wait_all(shared_lock& lock) noexcept;
void notify_all() noexcept
{
if (LIKELY(!m_cvx32))
return;
imp_notify();
}
bool notify_all(shared_lock& lock) noexcept;
};
-44
View File
@@ -1,44 +0,0 @@
#pragma once
#include <ctime>
#include <string>
namespace date_time
{
static inline tm get_time(time_t* _time)
{
tm buf;
time_t t = time(_time);
#ifdef _MSC_VER
localtime_s(&buf, &t);
#else
buf = *localtime(&t);
#endif
return buf;
}
static inline std::string current_time()
{
char str[80];
tm now = get_time(0);
strftime(str, sizeof(str), "%c", &now);
return str;
}
template<char separator = 0>
static inline std::string current_time_narrow()
{
char str[80];
tm now = get_time(0);
std::string parse_buf;
if constexpr(separator != 0)
parse_buf = std::string("%Y") + separator + "%m" + separator + "%d" + separator + "%H" + separator + "%M" + separator + "%S";
else
parse_buf = "%Y%m%d%H%M%S";
strftime(str, sizeof(str), parse_buf.c_str(), &now);
return str;
}
}
+2 -2
View File
@@ -1,4 +1,4 @@
#include "stdafx.h"
#include "stdafx.h"
#include "dynamic_library.h"
#ifdef _WIN32
@@ -50,7 +50,7 @@ namespace utils
bool dynamic_library::loaded() const
{
return m_handle != nullptr;
return !m_handle;
}
dynamic_library::operator bool() const
+3 -15
View File
@@ -63,30 +63,18 @@ namespace utils
{
}
void init()
// Caller
R operator()(Args... args)
{
if (!ptr)
{
// TODO: atomic
ptr = reinterpret_cast<R(*)(Args...)>(get_proc_address(lib, name));
}
}
operator bool()
{
init();
return ptr;
}
// Caller
R operator()(Args... args)
{
init();
return ptr(args...);
}
};
}
#define DYNAMIC_IMPORT(lib, name, ...) inline utils::dynamic_import<__VA_ARGS__> name(lib, #name);
#define DYNAMIC_IMPORT(lib, name, ...) static utils::dynamic_import<__VA_ARGS__> name(lib, #name);
+5 -7
View File
@@ -4,7 +4,7 @@
#include <deque>
#include <list>
#include "util/atomic.hpp"
#include "Atomic.h"
template <typename T, T Mod = T::__state_enum_max, typename Under = std::underlying_type_t<T>>
T operator ++(T& value, int)
@@ -71,7 +71,7 @@ public:
}
// Conditionally set state (optimized)
bool state_test_and_set(T expected, T state)
explicit_bool_t state_test_and_set(T expected, T state)
{
if (m_value == expected && m_value.compare_and_swap_test(expected, state))
{
@@ -83,7 +83,7 @@ public:
}
// Conditionally set state (list version)
bool state_test_and_set(std::initializer_list<T> expected, T state)
explicit_bool_t state_test_and_set(std::initializer_list<T> expected, T state)
{
T _old;
@@ -111,10 +111,9 @@ public:
// Unconditionally set next state
void state_next()
{
T _old, state = m_value.atomic_op([&](T& value)
T _old, state = m_value.op_fetch([&](T& value)
{
_old = value++;
return value;
});
(static_cast<CRT*>(this)->*transition_get(state))(_old);
@@ -123,10 +122,9 @@ public:
// Unconditionally set previous state
void state_prev()
{
T _old, state = m_value.atomic_op([&](T& value)
T _old, state = m_value.op_fetch([&](T& value)
{
_old = value--;
return value;
});
(static_cast<CRT*>(this)->*transition_get(state))(_old);
+6 -36
View File
@@ -1,4 +1,4 @@
#pragma once
#pragma once
#include <cmath>
@@ -673,16 +673,6 @@ struct area_base
return{ x1, y1, x2 - x1, y2 - y1 };
}
constexpr T width() const
{
return (x1 < x2) ? (x2 - x1) : (x1 - x2);
}
constexpr T height() const
{
return (y1 < y2) ? (y2 - y1) : (y1 - y2);
}
void flip_vertical()
{
T _y = y1; y1 = y2; y2 = _y;
@@ -703,11 +693,6 @@ struct area_base
return{ x2, y1, x1, y2 };
}
constexpr bool is_flipped() const
{
return (x1 > x2 || y1 > y2);
}
constexpr bool operator == (const area_base& rhs) const
{
return x1 == rhs.x1 && x2 == rhs.x2 && y1 == rhs.y1 && y2 == rhs.y2;
@@ -841,7 +826,7 @@ struct color4_base
T xyzw[4];
};
constexpr color4_base()
color4_base()
: x{}
, y{}
, z{}
@@ -849,7 +834,7 @@ struct color4_base
{
}
constexpr color4_base(T x, T y = {}, T z = {}, T w = {})
color4_base(T x, T y = {}, T z = {}, T w = {})
: x(x)
, y(y)
, z(z)
@@ -857,18 +842,18 @@ struct color4_base
{
}
constexpr bool operator == (const color4_base& rhs) const
bool operator == (const color4_base& rhs) const
{
return r == rhs.r && g == rhs.g && b == rhs.b && a == rhs.a;
}
constexpr bool operator != (const color4_base& rhs) const
bool operator != (const color4_base& rhs) const
{
return !(*this == rhs);
}
template<typename NT>
constexpr operator color4_base<NT>() const
operator color4_base<NT>() const
{
return{ (NT)x, (NT)y, (NT)z, (NT)w };
}
@@ -991,77 +976,62 @@ struct color1_base
};
//specializations
using positionu = position_base<unsigned int>;
using positioni = position_base<int>;
using positionf = position_base<float>;
using positiond = position_base<double>;
using coordu = coord_base<unsigned int>;
using coordi = coord_base<int>;
using coordf = coord_base<float>;
using coordd = coord_base<double>;
using areau = area_base<unsigned int>;
using areai = area_base<int>;
using areaf = area_base<float>;
using aread = area_base<double>;
using position1u = position1_base<unsigned int>;
using position1i = position1_base<int>;
using position1f = position1_base<float>;
using position1d = position1_base<double>;
using position2u = position2_base<unsigned int>;
using position2i = position2_base<int>;
using position2f = position2_base<float>;
using position2d = position2_base<double>;
using position3u = position3_base<unsigned int>;
using position3i = position3_base<int>;
using position3f = position3_base<float>;
using position3d = position3_base<double>;
using position4u = position4_base<unsigned int>;
using position4i = position4_base<int>;
using position4f = position4_base<float>;
using position4d = position4_base<double>;
using size2u = size2_base<unsigned int>;
using size2i = size2_base<int>;
using size2f = size2_base<float>;
using size2d = size2_base<double>;
using sizeu = size2u;
using sizei = size2i;
using sizef = size2f;
using sized = size2d;
using size3u = size3_base<unsigned int>;
using size3i = size3_base<int>;
using size3f = size3_base<float>;
using size3d = size3_base<double>;
using coord3u = coord3_base<unsigned int>;
using coord3i = coord3_base<int>;
using coord3f = coord3_base<float>;
using coord3d = coord3_base<double>;
using color4u = color4_base<unsigned int>;
using color4i = color4_base<int>;
using color4f = color4_base<float>;
using color4d = color4_base<double>;
using color3u = color3_base<unsigned int>;
using color3i = color3_base<int>;
using color3f = color3_base<float>;
using color3d = color3_base<double>;
using color2u = color2_base<unsigned int>;
using color2i = color2_base<int>;
using color2f = color2_base<float>;
using color2d = color2_base<double>;
using color1u = color1_base<unsigned int>;
using color1i = color1_base<int>;
using color1f = color1_base<float>;
using color1d = color1_base<double>;
+5 -30
View File
@@ -13,10 +13,9 @@ rem // A copy of the GPL 2.0 should have been included with the program.
rem // If not, see http://www.gnu.org/licenses/
rem // Official git repository and contact information can be found at
rem // https://github.com/RPCS3/rpcs3 and https://rpcs3.net/.
rem // https://github.com/RPCS3/rpcs3 and http://rpcs3.net/.
setlocal ENABLEDELAYEDEXPANSION
setlocal ENABLEEXTENSIONS
set GIT_VERSION_FILE=%~p0..\rpcs3\git-version.h
if not defined GIT (
@@ -55,33 +54,9 @@ if errorlevel 1 (
goto done
)
rem // Get commit count from (unshallowed) HEAD
for /F %%I IN ('call %GIT% rev-list HEAD --count') do set COMMIT_COUNT=%%I
rem // If we're in AppVeyor, building a non-master, pull request artifact
if defined APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH (
if "%APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH%"=="master" (
rem // If pull request comes from a master branch, GIT_BRANCH = username/branch in order to distinguish from upstream/master
for /f "tokens=1* delims=/" %%a in ("%APPVEYOR_PULL_REQUEST_HEAD_REPO_NAME%") do set user=%%a
set "GIT_BRANCH=!user!/%APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH%"
) else (
rem // Otherwise, GIT_BRANCH=branch
set GIT_BRANCH=%APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH%
)
rem // Make GIT_VERSION the last commit (shortened); Don't include commit count on non-master builds
for /F %%I IN ('call %GIT% rev-parse --short^=8 HEAD') do set GIT_VERSION=%%I
) else (
rem // Get last commit (shortened) and concat after commit count in GIT_VERSION
for /F %%I IN ('call %GIT% rev-parse --short^=8 HEAD') do set GIT_VERSION=%COMMIT_COUNT%-%%I
for /F %%I IN ('call %GIT% rev-parse --abbrev-ref HEAD') do set GIT_BRANCH=%%I
)
rem // Echo obtained GIT_VERSION for debug purposes if needed
echo %GIT_VERSION%
for /F %%I IN ('call %GIT% rev-list HEAD --count') do set GIT_VERSION=%%I
for /F %%I IN ('call %GIT% rev-parse --short HEAD') do set GIT_VERSION=%GIT_VERSION%-%%I
for /F %%I IN ('call %GIT% rev-parse --abbrev-ref HEAD') do set GIT_BRANCH=%%I
rem // Don't modify the file if it already has the current version.
if exist "%GIT_VERSION_FILE%" (
@@ -94,7 +69,7 @@ if exist "%GIT_VERSION_FILE%" (
echo // This is a generated file. > "%GIT_VERSION_FILE%"
echo. >> "%GIT_VERSION_FILE%"
echo #define RPCS3_GIT_VERSION "%GIT_VERSION%" >> "%GIT_VERSION_FILE%"
echo #define RPCS3_GIT_BRANCH ^"%GIT_BRANCH%^" >> "%GIT_VERSION_FILE%"
echo #define RPCS3_GIT_BRANCH "%GIT_BRANCH%" >> "%GIT_VERSION_FILE%"
echo. >> "%GIT_VERSION_FILE%"
echo // If you don't want this file to update/recompile, change to 1. >> "%GIT_VERSION_FILE%"
echo #define RPCS3_GIT_VERSION_NO_UPDATE 0 >> "%GIT_VERSION_FILE%"
+7 -31
View File
@@ -1,6 +1,5 @@
#pragma once
#include "Utilities/types.h"
#include <stdint.h>
namespace rpcs3
{
@@ -10,42 +9,19 @@ namespace rpcs3
return static_cast<size_t>(value);
}
template<typename T, typename U>
static size_t hash_struct_base(const T& value)
template<typename T>
static size_t hash_struct(const T& value)
{
// FNV 64-bit
size_t result = 14695981039346656037ull;
const U *bits = reinterpret_cast<const U*>(&value);
const unsigned char *bytes = reinterpret_cast<const unsigned char*>(&value);
for (size_t n = 0; n < (sizeof(T) / sizeof(U)); ++n)
for (size_t n = 0; n < sizeof(T); ++n)
{
result ^= bits[n];
result ^= bytes[n];
result *= 1099511628211ull;
}
return result;
}
template<typename T>
static size_t hash_struct(const T& value)
{
static constexpr auto block_sz = sizeof(T);
if constexpr ((block_sz & 0x7) == 0)
{
return hash_struct_base<T, u64>(value);
}
if constexpr ((block_sz & 0x3) == 0)
{
return hash_struct_base<T, u32>(value);
}
if constexpr ((block_sz & 0x1) == 0)
{
return hash_struct_base<T, u16>(value);
}
return hash_struct_base<T, u8>(value);
}
}
}
+131 -279
View File
@@ -1,14 +1,14 @@
#pragma once
#pragma once
#include "types.h"
#include "util/atomic.hpp"
#include "Atomic.h"
//! Simple sizeless array base for concurrent access. Cannot shrink, only growths automatically.
//! There is no way to know the current size. The smaller index is, the faster it's accessed.
//!
//!
//! T is the type of elements. Currently, default constructor of T shall be constexpr.
//! N is initial element count, available without any memory allocation and only stored contiguously.
template <typename T, std::size_t N>
template<typename T, std::size_t N>
class lf_array
{
// Data (default-initialized)
@@ -68,13 +68,13 @@ public:
// Get current "push" position
u32 size() const
{
return m_ctrl.load().push;
return reinterpret_cast<const atomic_t<u32>&>(m_ctrl).load(); // Hack
}
// Acquire the place for one or more elements.
u32 push_begin(u32 count = 1)
{
return std::bit_cast<atomic_t<u64>*>(&m_ctrl)->fetch_add(count); // Hack
return reinterpret_cast<atomic_t<u32>&>(m_ctrl).fetch_add(count); // Hack
}
// Get current "pop" position
@@ -130,7 +130,7 @@ public:
{
return m_default_key_data;
}
// Calculate hash and array position
for (std::size_t pos = Hash{}(key) % Size;; pos += Size)
{
@@ -146,333 +146,185 @@ public:
}
};
// Helper type, linked list element
template <typename T>
class lf_queue_item final
// Fixed-size single-producer single-consumer queue
template <typename T, std::uint32_t N>
class lf_spsc
{
lf_queue_item* m_link = nullptr;
// If N is a power of 2, m_push/m_pop can safely overflow and the algorithm is simplified
static_assert(N && (1u << 31) % N == 0, "lf_spsc<> error: size must be power of 2");
T m_data;
template <typename U>
friend class lf_queue_iterator;
template <typename U>
friend class lf_queue_slice;
template <typename U>
friend class lf_queue;
constexpr lf_queue_item() = default;
template <typename... Args>
constexpr lf_queue_item(lf_queue_item* link, Args&&... args)
: m_link(link)
, m_data(std::forward<Args>(args)...)
{
}
protected:
volatile std::uint32_t m_push{0};
volatile std::uint32_t m_pop{0};
T m_data[N]{};
public:
lf_queue_item(const lf_queue_item&) = delete;
constexpr lf_spsc() = default;
lf_queue_item& operator=(const lf_queue_item&) = delete;
~lf_queue_item()
// Try to push (producer only)
template <typename T2>
bool try_push(T2&& data)
{
for (lf_queue_item* ptr = m_link; ptr;)
const std::uint32_t pos = m_push;
if (pos - m_pop >= N)
{
delete std::exchange(ptr, std::exchange(ptr->m_link, nullptr));
}
}
};
// Forward iterator: non-owning pointer to the list element in lf_queue_slice<>
template <typename T>
class lf_queue_iterator
{
lf_queue_item<T>* m_ptr = nullptr;
template <typename U>
friend class lf_queue_slice;
public:
constexpr lf_queue_iterator() = default;
bool operator ==(const lf_queue_iterator& rhs) const
{
return m_ptr == rhs.m_ptr;
}
bool operator !=(const lf_queue_iterator& rhs) const
{
return m_ptr != rhs.m_ptr;
}
T& operator *() const
{
return m_ptr->m_data;
}
T* operator ->() const
{
return &m_ptr->m_data;
}
lf_queue_iterator& operator ++()
{
m_ptr = m_ptr->m_link;
return *this;
}
lf_queue_iterator operator ++(int)
{
lf_queue_iterator result;
result.m_ptr = m_ptr;
m_ptr = m_ptr->m_link;
return result;
}
};
// Owning pointer to the linked list taken from the lf_queue<>
template <typename T>
class lf_queue_slice
{
lf_queue_item<T>* m_head = nullptr;
template <typename U>
friend class lf_queue;
public:
constexpr lf_queue_slice() = default;
lf_queue_slice(const lf_queue_slice&) = delete;
lf_queue_slice(lf_queue_slice&& r) noexcept
: m_head(r.m_head)
{
r.m_head = nullptr;
}
lf_queue_slice& operator =(const lf_queue_slice&) = delete;
lf_queue_slice& operator =(lf_queue_slice&& r) noexcept
{
if (this != &r)
{
delete m_head;
m_head = r.m_head;
r.m_head = nullptr;
return false;
}
return *this;
_mm_lfence();
m_data[pos % N] = std::forward<T2>(data);
_mm_sfence();
m_push = pos + 1;
return true;
}
~lf_queue_slice()
// Try to get push pointer (producer only)
operator T*()
{
delete m_head;
const std::uint32_t pos = m_push;
if (pos - m_pop >= N)
{
return nullptr;
}
_mm_lfence();
return m_data + (pos % N);
}
T& operator *() const
// Increment push counter (producer only)
void end_push()
{
return m_head->m_data;
const std::uint32_t pos = m_push;
if (pos - m_pop < N)
{
_mm_sfence();
m_push = pos + 1;
}
}
T* operator ->() const
// Unsafe access
T& get_push(std::size_t i)
{
return &m_head->m_data;
_mm_lfence();
return m_data[(m_push + i) % N];
}
explicit operator bool() const
// Try to pop (consumer only)
template <typename T2>
bool try_pop(T2& out)
{
return m_head != nullptr;
const std::uint32_t pos = m_pop;
if (m_push - pos <= 0)
{
return false;
}
_mm_lfence();
out = std::move(m_data[pos % N]);
_mm_sfence();
m_pop = pos + 1;
return true;
}
T* get() const
// Increment pop counter (consumer only)
void end_pop()
{
return m_head ? &m_head->m_data : nullptr;
const std::uint32_t pos = m_pop;
if (m_push - pos > 0)
{
_mm_sfence();
m_pop = pos + 1;
}
}
lf_queue_iterator<T> begin() const
// Get size (consumer only)
std::uint32_t size() const
{
lf_queue_iterator<T> result;
result.m_ptr = m_head;
return result;
return m_push - m_pop;
}
lf_queue_iterator<T> end() const
// Direct access (consumer only)
T& operator [](std::size_t i)
{
return {};
}
lf_queue_slice& pop_front()
{
delete std::exchange(m_head, std::exchange(m_head->m_link, nullptr));
return *this;
_mm_lfence();
return m_data[(m_pop + i) % N];
}
};
class lf_queue_base
// Fixed-size multi-producer single-consumer queue
template <typename T, std::uint32_t N>
class lf_mpsc : lf_spsc<T, N>
{
protected:
atomic_t<std::uintptr_t> m_head = 0;
using lf_spsc<T, N>::m_push;
using lf_spsc<T, N>::m_pop;
using lf_spsc<T, N>::m_data;
void imp_notify();
public:
// Wait for new elements pushed, no other thread shall call wait() or pop_all() simultaneously
bool wait(u64 usec_timeout = -1);
};
// Linked list-based multi-producer queue (the consumer drains the whole queue at once)
template <typename T>
class lf_queue : public lf_queue_base
{
using lf_queue_base::m_head;
// Extract all elements and reverse element order (FILO to FIFO)
lf_queue_item<T>* reverse() noexcept
enum : std::uint64_t
{
if (auto* head = m_head.load() ? reinterpret_cast<lf_queue_item<T>*>(m_head.exchange(0)) : nullptr)
c_ack = 1ull << 0,
c_rel = 1ull << 32,
};
atomic_t<std::uint64_t> m_lock{0};
void release(std::uint64_t value)
{
// Push all pending elements at once when possible
if (value && value % c_rel == value / c_rel)
{
if (auto* prev = head->m_link)
{
head->m_link = nullptr;
do
{
auto* pprev = prev->m_link;
prev->m_link = head;
head = std::exchange(prev, pprev);
}
while (prev);
}
return head;
_mm_sfence();
m_push += value % c_rel;
m_lock.compare_and_swap_test(value, 0);
}
return nullptr;
}
public:
constexpr lf_queue() = default;
constexpr lf_mpsc() = default;
~lf_queue()
// Try to get push pointer
operator T*()
{
delete reinterpret_cast<lf_queue_item<T>*>(m_head.load());
}
const std::uint64_t old = m_lock.fetch_add(c_ack);
const std::uint32_t pos = m_push;
template <typename... Args>
void push(Args&&... args)
{
auto _old = m_head.load();
auto* item = new lf_queue_item<T>(_old & 1 ? nullptr : reinterpret_cast<lf_queue_item<T>*>(_old), std::forward<Args>(args)...);
while (!m_head.compare_exchange(_old, reinterpret_cast<std::uint64_t>(item)))
if (old % N >= N || pos - m_pop >= N - (old % N))
{
item->m_link = _old & 1 ? nullptr : reinterpret_cast<lf_queue_item<T>*>(_old);
release(m_lock.sub_fetch(c_ack));
return nullptr;
}
if (_old & 1)
{
lf_queue_base::imp_notify();
}
return m_data + ((pos + old) % N);
}
// Withdraw the list, supports range-for loop: for (auto&& x : y.pop_all()) ...
lf_queue_slice<T> pop_all()
// Increment push counter (producer only)
void end_push()
{
lf_queue_slice<T> result;
result.m_head = reverse();
return result;
release(m_lock.add_fetch(c_rel));
}
// Apply func(data) to each element, return the total length
template <typename F>
std::size_t apply(F func)
// Try to push
template <typename T2>
bool try_push(T2&& data)
{
std::size_t count = 0;
for (auto slice = pop_all(); slice; slice.pop_front())
if (T* ptr = *this)
{
std::invoke(func, *slice);
*ptr = std::forward<T2>(data);
end_push();
return true;
}
return count;
}
};
// Assignable lock-free thread-safe value of any type (memory-inefficient)
template <typename T>
class lf_value final
{
atomic_t<lf_value*> m_head;
T m_data;
public:
template <typename... Args>
explicit constexpr lf_value(Args&&... args)
: m_head(this)
, m_data(std::forward<Args>(args)...)
{
}
~lf_value()
{
// All values are kept in the queue until the end
for (lf_value* ptr = m_head.load(); ptr != this;)
{
delete std::exchange(ptr, std::exchange(ptr->m_head.raw(), ptr));
}
}
// Get current head, allows to inspect old values
[[nodiscard]] const lf_value* head() const
{
return m_head.load();
}
// Inspect the initial (oldest) value
[[nodiscard]] const T& first() const
{
return m_data;
}
[[nodiscard]] const T& get() const
{
return m_head.load()->m_data;
}
[[nodiscard]] operator const T&() const
{
return m_head.load()->m_data;
}
// Construct new value in-place
template <typename... Args>
const T& assign(Args&&... args)
{
lf_value* val = new lf_value(std::forward<Args>(args)...);
lf_value* old = m_head.load();
do
{
val->m_head = old;
}
while (!m_head.compare_exchange(old, val));
return val->m_data;
}
// Copy-assign new value
const T& operator =(const T& value)
{
return assign(value);
}
// Move-assign new value
const T& operator =(T&& value)
{
return assign(std::move(value));
return false;
}
// Enable consumer methods
using lf_spsc<T, N>::try_pop;
using lf_spsc<T, N>::end_pop;
using lf_spsc<T, N>::size;
using lf_spsc<T, N>::operator [];
};
+264 -188
View File
@@ -1,258 +1,334 @@
#include "mutex.h"
#include "sync.h"
void shared_mutex::imp_lock_shared(u32 val)
#include <climits>
#include <vector>
#include <algorithm>
// TLS variable for tracking owned mutexes
thread_local std::vector<shared_mutex*> g_tls_locks;
void shared_mutex::imp_lock_shared(s64 _old)
{
verify("shared_mutex underflow" HERE), val < c_err;
verify("shared_mutex overflow" HERE), _old <= c_max;
for (int i = 0; i < 10; i++)
{
busy_wait();
if (try_lock_shared())
const s64 value = m_value.load();
if (value >= c_min && m_value.compare_and_swap_test(value, value - c_min))
{
return;
}
}
// Acquire writer lock and downgrade
const u32 old = m_value.fetch_add(c_one);
#ifdef _WIN32
// Acquire writer lock
imp_wait(m_value.load());
if (old == 0)
// Convert to reader lock
s64 value = m_value.fetch_add(c_one - c_min);
// Proceed exclusively
return;
if (value != 0)
{
lock_downgrade();
return;
imp_unlock(value);
}
verify("shared_mutex overflow" HERE), (old % c_sig) + c_one < c_sig;
imp_wait();
lock_downgrade();
}
void shared_mutex::imp_unlock_shared(u32 old)
{
verify("shared_mutex underflow" HERE), old - 1 < c_err;
// Check reader count, notify the writer if necessary
if ((old - 1) % c_one == 0)
// Wait as a reader if necessary
if (value + c_one - c_min < 0)
{
imp_signal();
NtWaitForKeyedEvent(nullptr, (int*)&m_value + 1, false, nullptr);
}
}
#else
// Acquire writer lock
imp_wait(0);
void shared_mutex::imp_lock_low(u32 val)
{
verify("shared_mutex underflow" HERE), val < c_err;
// Convert to reader lock
m_value += c_one - c_min;
for (int i = 0; i < 10; i++)
// Disabled code
while (false)
{
busy_wait();
if (try_lock_low())
const s64 value0 = m_value.fetch_op([](s64& value)
{
return;
}
}
// Acquire writer lock and downgrade
const u32 old = m_value.fetch_add(c_one);
if (old == 0)
{
lock_downgrade();
return;
}
verify("shared_mutex overflow" HERE), (old % c_sig) + c_one < c_sig;
imp_wait();
lock_downgrade();
}
void shared_mutex::imp_unlock_low(u32 old)
{
verify("shared_mutex underflow" HERE), old - 1 < c_err;
// Check reader count, notify the writer if necessary
if ((old - 1) % c_vip == 0)
{
imp_signal();
}
}
void shared_mutex::imp_lock_vip(u32 val)
{
verify("shared_mutex underflow" HERE), val < c_err;
for (int i = 0; i < 10; i++)
{
busy_wait();
if (try_lock_vip())
{
return;
}
}
// Acquire writer lock and downgrade
const u32 old = m_value.fetch_add(c_one);
if (old == 0)
{
lock_downgrade_to_vip();
return;
}
verify("shared_mutex overflow" HERE), (old % c_sig) + c_one < c_sig;
imp_wait();
lock_downgrade_to_vip();
}
void shared_mutex::imp_unlock_vip(u32 old)
{
verify("shared_mutex underflow" HERE), old - 1 < c_err;
// Check reader count, notify the writer if necessary
if ((old - 1) % c_one / c_vip == 0)
{
imp_signal();
}
}
void shared_mutex::imp_wait()
{
while (true)
{
const auto [old, ok] = m_value.fetch_op([](u32& value)
{
if (value >= c_sig)
if (value >= c_min)
{
value -= c_sig;
return true;
value -= c_min;
}
return false;
});
if (ok)
if (value0 >= c_min)
{
break;
return;
}
m_value.wait(old);
// Acquire writer lock
imp_wait(value0);
// Convert to reader lock
s64 value1 = m_value.fetch_add(c_one - c_min);
if (value1 != 0)
{
imp_unlock(value1);
}
value1 += c_one - c_min;
if (value1 > 0)
{
return;
}
// Wait as a reader if necessary
while (futex((int*)&m_value.raw() + IS_LE_MACHINE, FUTEX_WAIT_BITSET_PRIVATE, int(value1 >> 32), nullptr, nullptr, INT_MIN))
{
value1 = m_value.load();
if (value1 >= 0)
{
return;
}
}
// If blocked by writers, release the reader lock and try again
const s64 value2 = m_value.fetch_op([](s64& value)
{
if (value < 0)
{
value += c_min;
}
});
if (value2 >= 0)
{
return;
}
imp_unlock_shared(value2);
}
#endif
}
void shared_mutex::imp_unlock_shared(s64 _old)
{
verify("shared_mutex overflow" HERE), _old + c_min <= c_max;
// Check reader count, notify the writer if necessary
if ((_old + c_min) % c_one == 0)
{
#ifdef _WIN32
NtReleaseKeyedEvent(nullptr, &m_value, false, nullptr);
#else
m_value -= c_sig;
futex((int*)&m_value.raw() + IS_LE_MACHINE, FUTEX_WAKE_BITSET_PRIVATE, 1, nullptr, nullptr, u32(c_sig >> 32));
#endif
}
}
void shared_mutex::imp_signal()
void shared_mutex::imp_wait(s64)
{
m_value += c_sig;
m_value.notify_one();
#ifdef _WIN32
if (m_value.sub_fetch(c_one))
{
NtWaitForKeyedEvent(nullptr, &m_value, false, nullptr);
}
#else
if (!m_value.sub_fetch(c_one))
{
// Return immediately if locked
return;
}
while (true)
{
// Load new value, try to acquire c_sig
const s64 value = m_value.fetch_op([](s64& value)
{
if (value <= c_one - c_sig)
{
value += c_sig;
}
});
if (value <= c_one - c_sig)
{
return;
}
futex((int*)&m_value.raw() + IS_LE_MACHINE, FUTEX_WAIT_BITSET_PRIVATE, int(value >> 32), nullptr, nullptr, u32(c_sig >> 32));
}
#endif
}
void shared_mutex::imp_lock(u32 val)
void shared_mutex::imp_lock(s64 _old)
{
verify("shared_mutex underflow" HERE), val < c_err;
verify("shared_mutex overflow" HERE), _old <= c_max;
for (int i = 0; i < 10; i++)
{
busy_wait();
if (!m_value && try_lock())
const s64 value = m_value.load();
if (value == c_one && m_value.compare_and_swap_test(c_one, 0))
{
return;
}
}
const u32 old = m_value.fetch_add(c_one);
if (old == 0)
{
return;
}
verify("shared_mutex overflow" HERE), (old % c_sig) + c_one < c_sig;
imp_wait();
imp_wait(m_value.load());
}
void shared_mutex::imp_unlock(u32 old)
void shared_mutex::imp_unlock(s64 _old)
{
verify("shared_mutex underflow" HERE), old - c_one < c_err;
verify("shared_mutex overflow" HERE), _old + c_one <= c_max;
// 1) Notify the next writer if necessary
// 2) Notify all readers otherwise if necessary (currently indistinguishable from writers)
if (old - c_one)
// 2) Notify all readers otherwise if necessary
#ifdef _WIN32
if (_old + c_one <= 0)
{
imp_signal();
NtReleaseKeyedEvent(nullptr, &m_value, false, nullptr);
}
else if (s64 count = -_old / c_min * 0)
{
// Disabled code
while (count--)
{
NtReleaseKeyedEvent(nullptr, (int*)&m_value + 1, false, nullptr);
}
}
#else
if (_old + c_one <= 0)
{
m_value -= c_sig;
futex((int*)&m_value.raw() + IS_LE_MACHINE, FUTEX_WAKE_BITSET_PRIVATE, 1, nullptr, nullptr, u32(c_sig >> 32));
}
else if (false)
{
// Disabled code
futex((int*)&m_value.raw() + IS_LE_MACHINE, FUTEX_WAKE_BITSET_PRIVATE, INT_MAX, nullptr, nullptr, INT_MIN);
}
#endif
}
void shared_mutex::imp_lock_upgrade()
{
for (int i = 0; i < 10; i++)
{
busy_wait();
if (try_lock_upgrade())
{
return;
}
}
// Convert to writer lock
const u32 old = m_value.fetch_add(c_one - 1);
verify("shared_mutex overflow" HERE), (old % c_sig) + c_one - 1 < c_sig;
if (old % c_one == 1)
{
return;
}
imp_wait();
// TODO
unlock_shared();
lock();
}
void shared_mutex::imp_lock_unlock()
void shared_mutex::imp_lock_degrade()
{
u32 _max = 1;
for (int i = 0; i < 30; i++)
{
const u32 val = m_value;
if (val % c_one == 0 && (val / c_one < _max || val >= c_sig))
{
// Return if have cought a state where:
// 1) Mutex is free
// 2) Total number of waiters decreased since last check
// 3) Signal bit is set (if used on the platform)
return;
}
_max = val / c_one;
busy_wait(1500);
}
// Lock and unlock
if (!m_value.fetch_add(c_one))
{
unlock();
return;
}
imp_wait();
// TODO
unlock();
lock_shared();
}
bool shared_mutex::downgrade_unique_vip_lock_to_low_or_unlock()
bool shared_mutex::try_lock_shared()
{
return m_value.atomic_op([](u32& value)
// Conditional decrement
return m_value.fetch_op([](s64& value) { if (value >= c_min) value -= c_min; }) >= c_min;
}
bool shared_mutex::try_lock()
{
// Conditional decrement (TODO: obtain c_sig)
return m_value.compare_and_swap_test(c_one, 0);
}
bool shared_mutex::try_lock_upgrade()
{
// TODO
return m_value.compare_and_swap_test(c_one - c_min, 0);
}
bool shared_mutex::try_lock_degrade()
{
// TODO
return m_value.compare_and_swap_test(0, c_one - c_min);
}
safe_reader_lock::safe_reader_lock(shared_mutex& mutex)
: m_mutex(mutex)
, m_is_owned(false)
{
if (std::count(g_tls_locks.cbegin(), g_tls_locks.cend(), &m_mutex) == 0)
{
if (value % c_one / c_vip == 1)
m_is_owned = true;
if (m_is_owned)
{
value -= c_vip - 1;
return true;
m_mutex.lock_shared();
g_tls_locks.emplace_back(&m_mutex);
return;
}
value -= c_vip;
return false;
});
// TODO: order locks
}
}
safe_reader_lock::~safe_reader_lock()
{
if (m_is_owned)
{
m_mutex.unlock_shared();
g_tls_locks.erase(std::remove(g_tls_locks.begin(), g_tls_locks.end(), &m_mutex), g_tls_locks.cend());
return;
}
// TODO: order locks
}
safe_writer_lock::safe_writer_lock(shared_mutex& mutex)
: m_mutex(mutex)
, m_is_owned(false)
, m_is_upgraded(false)
{
if (std::count(g_tls_locks.cbegin(), g_tls_locks.cend(), &m_mutex) == 0)
{
m_is_owned = true;
if (m_is_owned)
{
m_mutex.lock_shared();
g_tls_locks.emplace_back(&m_mutex);
return;
}
// TODO: order locks
}
if (m_mutex.is_reading())
{
m_is_upgraded = true;
m_mutex.lock_upgrade();
}
}
safe_writer_lock::~safe_writer_lock()
{
if (m_is_upgraded)
{
m_mutex.lock_degrade();
return;
}
if (m_is_owned)
{
m_mutex.unlock();
g_tls_locks.erase(std::remove(g_tls_locks.begin(), g_tls_locks.end(), &m_mutex), g_tls_locks.cend());
return;
}
// TODO: order locks
}
+120 -218
View File
@@ -1,171 +1,65 @@
#pragma once
#include <mutex>
#include "types.h"
#include "util/atomic.hpp"
#include "Atomic.h"
// Shared mutex with small size (u32).
// Shared mutex.
class shared_mutex final
{
enum : u32
enum : s64
{
c_one = 1u << 14, // Fixed-point 1.0 value (one writer, max_readers = c_one - 1)
c_sig = 1u << 30,
c_err = 1u << 31,
c_vip = 1u << 7,
c_one = 1ull << 31, // Fixed-point 1.0 value (one writer)
c_min = 0x00000001, // Fixed-point 1.0/max_readers value
c_sig = 1ull << 62,
c_max = c_one
};
atomic_t<u32> m_value{};
atomic_t<s64> m_value{c_one}; // Semaphore-alike counter
void imp_lock_shared(s64 _old);
void imp_unlock_shared(s64 _old);
void imp_wait(s64 _old);
void imp_lock(s64 _old);
void imp_unlock(s64 _old);
void imp_lock_shared(u32 val);
void imp_unlock_shared(u32 old);
void imp_lock_low(u32 val);
void imp_unlock_low(u32 old);
void imp_lock_vip(u32 val);
void imp_unlock_vip(u32 old);
void imp_wait();
void imp_signal();
void imp_lock(u32 val);
void imp_unlock(u32 old);
void imp_lock_upgrade();
void imp_lock_unlock();
void imp_lock_degrade();
public:
constexpr shared_mutex() = default;
bool try_lock_shared()
{
const u32 value = m_value.load();
// Conditional increment
return value < c_one - 1 && m_value.compare_and_swap_test(value, value + 1);
}
bool try_lock_shared();
void lock_shared()
{
const u32 value = m_value.load();
const s64 value = m_value.load();
if (UNLIKELY(value >= c_one - 1 || !m_value.compare_and_swap_test(value, value + 1)))
// Fast path: decrement if positive
if (UNLIKELY(value < c_min || value > c_one || !m_value.compare_and_swap_test(value, value - c_min)))
{
imp_lock_shared(value);
}
}
void lock_shared_hle()
{
const u32 value = m_value.load();
if (LIKELY(value < c_one - 1))
{
u32 old = value;
if (LIKELY(atomic_storage<u32>::compare_exchange_hle_acq(m_value.raw(), old, value + 1)))
{
return;
}
}
imp_lock_shared(value);
}
void unlock_shared()
{
// Unconditional decrement (can result in broken state)
const u32 value = m_value.fetch_sub(1);
// Unconditional increment
const s64 value = m_value.fetch_add(c_min);
if (UNLIKELY(value >= c_one))
if (value < 0 || value > c_one - c_min)
{
imp_unlock_shared(value);
}
}
void unlock_shared_hle()
{
const u32 value = atomic_storage<u32>::fetch_add_hle_rel(m_value.raw(), -1);
if (UNLIKELY(value >= c_one))
{
imp_unlock_shared(value);
}
}
bool try_lock_low()
{
const u32 value = m_value.load();
// Conditional increment
return value < c_vip - 1 && m_value.compare_and_swap_test(value, value + 1);
}
void lock_low()
{
const u32 value = m_value.load();
if (UNLIKELY(value >= c_vip - 1 || !m_value.compare_and_swap_test(value, value + 1)))
{
imp_lock_low(value);
}
}
void unlock_low()
{
// Unconditional decrement (can result in broken state)
const u32 value = m_value.fetch_sub(1);
if (UNLIKELY(value >= c_one))
{
imp_unlock_low(value);
}
}
bool try_lock_vip()
{
const u32 value = m_value.load();
// Conditional increment
return value < c_one - 1 && (value % c_vip) == 0 && m_value.compare_and_swap_test(value, value + c_vip);
}
void lock_vip()
{
const u32 value = m_value.load();
if (UNLIKELY(value >= c_one - 1 || (value % c_vip) || !m_value.compare_and_swap_test(value, value + c_vip)))
{
imp_lock_vip(value);
}
}
void unlock_vip()
{
// Unconditional decrement (can result in broken state)
const u32 value = m_value.fetch_sub(c_vip);
if (UNLIKELY(value >= c_one))
{
imp_unlock_vip(value);
}
}
bool try_lock()
{
return m_value.compare_and_swap_test(0, c_one);
}
bool try_lock();
void lock()
{
const u32 value = m_value.compare_and_swap(0, c_one);
// Try to lock
const s64 value = m_value.compare_and_swap(c_one, 0);
if (UNLIKELY(value))
{
imp_lock(value);
}
}
void lock_hle()
{
u32 value = 0;
if (UNLIKELY(!atomic_storage<u32>::compare_exchange_hle_acq(m_value.raw(), value, c_one)))
if (value != c_one)
{
imp_lock(value);
}
@@ -173,96 +67,69 @@ public:
void unlock()
{
// Unconditional decrement (can result in broken state)
const u32 value = m_value.fetch_sub(c_one);
// Unconditional increment
const s64 value = m_value.fetch_add(c_one);
if (UNLIKELY(value != c_one))
if (value != 0)
{
imp_unlock(value);
}
}
void unlock_hle()
{
const u32 value = atomic_storage<u32>::fetch_add_hle_rel(m_value.raw(), 0u - c_one);
if (UNLIKELY(value != c_one))
{
imp_unlock(value);
}
}
bool try_lock_upgrade()
{
const u32 value = m_value.load();
// Conditional increment, try to convert a single reader into a writer, ignoring other writers
return (value + c_one - 1) % c_one == 0 && m_value.compare_and_swap_test(value, value + c_one - 1);
}
bool try_lock_upgrade();
void lock_upgrade()
{
if (UNLIKELY(!try_lock_upgrade()))
if (!m_value.compare_and_swap_test(c_one - c_min, 0))
{
imp_lock_upgrade();
}
}
void lock_downgrade()
{
// Convert to reader lock (can result in broken state)
m_value -= c_one - 1;
}
bool try_lock_degrade();
void lock_downgrade_to_vip()
void lock_degrade()
{
// Convert to vip lock (can result in broken state)
m_value -= c_one - c_vip;
}
// Optimized wait for lockability without locking, relaxed
void lock_unlock()
{
if (UNLIKELY(m_value != 0))
if (!m_value.compare_and_swap_test(0, c_one - c_min))
{
imp_lock_unlock();
imp_lock_degrade();
}
}
// Check whether can immediately obtain an exclusive (writer) lock
bool is_free() const
bool is_reading() const
{
return m_value.load() == 0;
return (m_value.load() % c_one) != 0;
}
// Check whether can immediately obtain a shared (reader) lock
bool is_lockable() const
{
return m_value.load() < c_one - 1;
}
// Special purpose logic
bool downgrade_unique_vip_lock_to_low_or_unlock();
};
// Simplified shared (reader) lock implementation. Mutually incompatible with low_lock and vip_lock.
// Simplified shared (reader) lock implementation.
class reader_lock final
{
shared_mutex& m_mutex;
bool m_upgraded = false;
void lock()
{
m_upgraded ? m_mutex.lock() : m_mutex.lock_shared();
}
void unlock()
{
m_upgraded ? m_mutex.unlock() : m_mutex.unlock_shared();
}
friend class cond_variable;
public:
reader_lock(const reader_lock&) = delete;
reader_lock& operator=(const reader_lock&) = delete;
explicit reader_lock(shared_mutex& mutex)
: m_mutex(mutex)
{
m_mutex.lock_shared();
lock();
}
// One-way lock upgrade; note that the observed state could have been changed
// One-way lock upgrade
void upgrade()
{
if (!m_upgraded)
@@ -272,58 +139,93 @@ public:
}
}
// Try to upgrade; if it succeeds, the observed state has NOT been changed
bool try_upgrade()
{
return m_upgraded || (m_upgraded = m_mutex.try_lock_upgrade());
}
~reader_lock()
{
m_upgraded ? m_mutex.unlock() : m_mutex.unlock_shared();
unlock();
}
};
// Special shared (reader) lock, mutually exclusive with vip locks. Mutually incompatible with normal shared (reader) lock.
class low_lock final
// Simplified exclusive (writer) lock implementation.
class writer_lock final
{
shared_mutex& m_mutex;
public:
low_lock(const low_lock&) = delete;
low_lock& operator=(const low_lock&) = delete;
explicit low_lock(shared_mutex& mutex)
: m_mutex(mutex)
void lock()
{
m_mutex.lock_low();
m_mutex.lock();
}
~low_lock()
void unlock()
{
m_mutex.unlock_low();
m_mutex.unlock();
}
friend class cond_variable;
public:
writer_lock(const writer_lock&) = delete;
explicit writer_lock(shared_mutex& mutex)
: m_mutex(mutex)
{
lock();
}
~writer_lock()
{
unlock();
}
};
// Special shared (reader) lock, mutually exclusive with low locks. Mutually incompatible with normal shared (reader) lock.
class vip_lock final
// Safe reader lock. Can be recursive above other safe locks (reader or writer).
class safe_reader_lock final
{
shared_mutex& m_mutex;
bool m_is_owned;
void lock()
{
m_mutex.lock_shared();
}
void unlock()
{
m_mutex.unlock_shared();
}
friend class cond_variable;
public:
vip_lock(const vip_lock&) = delete;
safe_reader_lock(const safe_reader_lock&) = delete;
vip_lock& operator=(const vip_lock&) = delete;
explicit safe_reader_lock(shared_mutex& mutex);
explicit vip_lock(shared_mutex& mutex)
: m_mutex(mutex)
{
m_mutex.lock_vip();
}
~vip_lock()
{
m_mutex.unlock_vip();
}
~safe_reader_lock();
};
// Safe writer lock. Can be recursive above other safe locks. Performs upgrade and degrade operations above existing reader lock if necessary.
class safe_writer_lock final
{
shared_mutex& m_mutex;
bool m_is_owned;
bool m_is_upgraded;
void lock()
{
m_mutex.lock();
}
void unlock()
{
m_mutex.unlock();
}
friend class cond_variable;
public:
safe_writer_lock(const safe_writer_lock&) = delete;
explicit safe_writer_lock(shared_mutex& mutex);
~safe_writer_lock();
};
+5 -1
View File
@@ -1,6 +1,10 @@
#pragma once
#include <pugixml.hpp>
#ifndef PUGIXML_HEADER_ONLY
#define PUGIXML_HEADER_ONLY 1
#endif // !PUGIXML_HEADER_ONLY
#include "pugixml.hpp"
#undef PUGIXML_HEADER_ONLY
struct rXmlNode
{
+124
View File
@@ -0,0 +1,124 @@
#ifndef MAPBOX_UTIL_RECURSIVE_WRAPPER_HPP
#define MAPBOX_UTIL_RECURSIVE_WRAPPER_HPP
// Based on variant/recursive_wrapper.hpp from boost.
//
// Original license:
//
// Copyright (c) 2002-2003
// Eric Friedman, Itay Maman
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <cassert>
#include <utility>
//namespace mapbox {
//namespace util {
namespace std {
template <typename T>
class recursive_wrapper
{
T* p_;
void assign(T const& rhs)
{
this->get() = rhs;
}
public:
using type = T;
/**
* Default constructor default initializes the internally stored value.
* For POD types this means nothing is done and the storage is
* uninitialized.
*
* @throws std::bad_alloc if there is insufficient memory for an object
* of type T.
* @throws any exception thrown by the default constructur of T.
*/
recursive_wrapper()
: p_(new T){}
~recursive_wrapper() noexcept { delete p_; }
recursive_wrapper(recursive_wrapper const& operand)
: p_(new T(operand.get())) {}
recursive_wrapper(T const& operand)
: p_(new T(operand)) {}
recursive_wrapper(recursive_wrapper&& operand)
: p_(new T(std::move(operand.get()))) {}
recursive_wrapper(T&& operand)
: p_(new T(std::move(operand))) {}
inline recursive_wrapper& operator=(recursive_wrapper const& rhs)
{
assign(rhs.get());
return *this;
}
inline recursive_wrapper& operator=(T const& rhs)
{
assign(rhs);
return *this;
}
inline void swap(recursive_wrapper& operand) noexcept
{
T* temp = operand.p_;
operand.p_ = p_;
p_ = temp;
}
recursive_wrapper& operator=(recursive_wrapper&& rhs) noexcept
{
swap(rhs);
return *this;
}
recursive_wrapper& operator=(T&& rhs)
{
get() = std::move(rhs);
return *this;
}
T& get()
{
assert(p_);
return *get_pointer();
}
T const& get() const
{
assert(p_);
return *get_pointer();
}
T* get_pointer() { return p_; }
const T* get_pointer() const { return p_; }
operator T const&() const { return this->get(); }
operator T&() { return this->get(); }
}; // class recursive_wrapper
template <typename T>
inline void swap(recursive_wrapper<T>& lhs, recursive_wrapper<T>& rhs) noexcept
{
lhs.swap(rhs);
}
}
//} // namespace util
//} // namespace mapbox
#endif // MAPBOX_UTIL_RECURSIVE_WRAPPER_HPP
+32 -6
View File
@@ -1,4 +1,5 @@
#include "sema.h"
#include "sync.h"
void semaphore_base::imp_wait()
{
@@ -14,10 +15,18 @@ void semaphore_base::imp_wait()
}
}
#ifdef _WIN32
const s32 value = m_value.fetch_sub(1);
if (value <= 0)
{
NtWaitForKeyedEvent(nullptr, &m_value, false, nullptr);
}
#else
while (true)
{
// Try hard way
const s32 value = m_value.atomic_op([](s32& value)
const s32 value = m_value.op_fetch([](s32& value)
{
// Use sign bit to acknowledge waiter presence
if (value && value > INT32_MIN)
@@ -35,26 +44,43 @@ void semaphore_base::imp_wait()
// Set sign bit
value = INT32_MIN;
}
return value;
});
if (value >= 0)
{
// Signal other waiter to wake up or to restore sign bit
m_value.notify_one();
futex(&m_value.raw(), FUTEX_WAKE_PRIVATE, 1, nullptr, nullptr, 0);
return;
}
m_value.wait(value);
futex(&m_value.raw(), FUTEX_WAIT_PRIVATE, value, nullptr, nullptr, 0);
}
#endif
}
void semaphore_base::imp_post(s32 _old)
{
verify("semaphore_base: overflow" HERE), _old < 0;
m_value.notify_one();
#ifdef _WIN32
NtReleaseKeyedEvent(nullptr, &m_value, false, nullptr);
#else
futex(&m_value.raw(), FUTEX_WAKE_PRIVATE, 1, nullptr, nullptr, 0);
#endif
}
bool semaphore_base::try_wait()
{
// Conditional decrement
const s32 value = m_value.fetch_op([](s32& value)
{
if (value > 0)
{
value -= 1;
}
});
return value > 0;
}
bool semaphore_base::try_post(s32 _max)
+41 -12
View File
@@ -1,8 +1,7 @@
#pragma once
#include <mutex>
#include "types.h"
#include "util/atomic.hpp"
#include "Atomic.h"
// Lightweight semaphore helper class
class semaphore_base
@@ -14,6 +13,8 @@ class semaphore_base
void imp_post(s32 _old);
friend class semaphore_lock;
protected:
explicit constexpr semaphore_base(s32 value)
: m_value{value}
@@ -32,10 +33,7 @@ protected:
}
}
bool try_wait()
{
return m_value.try_dec(0);
}
bool try_wait();
void post(s32 _max)
{
@@ -75,36 +73,36 @@ class semaphore final : public semaphore_base
public:
// Default constructor (recommended)
constexpr semaphore()
: base(Def)
: base{Def}
{
}
// Explicit value constructor (not recommended)
explicit constexpr semaphore(s32 value)
: base(value)
: base{value}
{
}
// Obtain a semaphore
void lock()
void wait()
{
return base::wait();
}
// Try to obtain a semaphore
bool try_lock()
explicit_bool_t try_wait()
{
return base::try_wait();
}
// Return a semaphore
void unlock()
void post()
{
return base::post(Max);
}
// Try to return a semaphore
bool try_unlock()
explicit_bool_t try_post()
{
return base::try_post(Max);
}
@@ -115,3 +113,34 @@ public:
return Max;
}
};
class semaphore_lock
{
semaphore_base& m_base;
void lock()
{
m_base.wait();
}
void unlock()
{
m_base.post(INT32_MAX);
}
friend class cond_variable;
public:
explicit semaphore_lock(const semaphore_lock&) = delete;
semaphore_lock(semaphore_base& sema)
: m_base(sema)
{
lock();
}
~semaphore_lock()
{
unlock();
}
};
+23 -183
View File
@@ -3,7 +3,7 @@
/* For internal use. Don't include. */
#include "types.h"
#include "util/atomic.hpp"
#include "Atomic.h"
#include "dynamic_library.h"
#ifdef _WIN32
@@ -17,7 +17,6 @@
#include <unistd.h>
#else
#endif
#include <algorithm>
#include <ctime>
#include <chrono>
#include <mutex>
@@ -25,12 +24,10 @@
#include <unordered_map>
#ifdef _WIN32
DYNAMIC_IMPORT("ntdll.dll", NtSetTimerResolution, NTSTATUS(ULONG DesiredResolution, BOOLEAN SetResolution, PULONG CurrentResolution));
DYNAMIC_IMPORT("ntdll.dll", NtWaitForKeyedEvent, NTSTATUS(HANDLE Handle, PVOID Key, BOOLEAN Alertable, PLARGE_INTEGER Timeout));
DYNAMIC_IMPORT("ntdll.dll", NtReleaseKeyedEvent, NTSTATUS(HANDLE Handle, PVOID Key, BOOLEAN Alertable, PLARGE_INTEGER Timeout));
DYNAMIC_IMPORT("ntdll.dll", NtDelayExecution, NTSTATUS(BOOLEAN Alertable, PLARGE_INTEGER DelayInterval));
inline utils::dynamic_import<BOOL(volatile VOID* Address, PVOID CompareAddress, SIZE_T AddressSize, DWORD dwMilliseconds)> OptWaitOnAddress("kernel32.dll", "WaitOnAddress");
inline utils::dynamic_import<VOID(PVOID Address)> OptWakeByAddressSingle("kernel32.dll", "WakeByAddressSingle");
inline utils::dynamic_import<VOID(PVOID Address)> OptWakeByAddressAll("kernel32.dll", "WakeByAddressAll");
#endif
#ifndef __linux__
@@ -49,37 +46,37 @@ enum
};
#endif
inline int futex(volatile void* uaddr, int futex_op, uint val, const timespec* timeout = nullptr, uint mask = 0)
inline int futex(int* uaddr, int futex_op, int val, const timespec* timeout, int* uaddr2, int val3)
{
#ifdef __linux__
return syscall(SYS_futex, uaddr, futex_op, static_cast<int>(val), timeout, nullptr, static_cast<int>(mask));
return syscall(SYS_futex, uaddr, futex_op, val, timeout, uaddr, val3);
#else
static struct futex_manager
static struct futex_map
{
struct waiter
{
uint val;
int val;
uint mask;
std::condition_variable cv;
};
std::mutex mutex;
std::unordered_multimap<volatile void*, waiter*, pointer_hash<volatile void, alignof(int)>> map;
std::unordered_multimap<int*, waiter*, pointer_hash<int>> map;
int operator()(volatile void* uaddr, int futex_op, uint val, const timespec* timeout, uint mask)
int operator()(int* uaddr, int futex_op, int val, const timespec* timeout, int*, uint val3)
{
std::unique_lock lock(mutex);
std::unique_lock<std::mutex> lock(mutex);
switch (futex_op)
{
case FUTEX_WAIT_PRIVATE:
case FUTEX_WAIT:
{
mask = -1;
[[fallthrough]];
val3 = -1;
// Fallthrough
}
case FUTEX_WAIT_BITSET_PRIVATE:
case FUTEX_WAIT_BITSET:
{
if (*reinterpret_cast<volatile uint*>(uaddr) != val)
if (*(volatile int*)uaddr != val)
{
errno = EAGAIN;
return -1;
@@ -87,7 +84,7 @@ inline int futex(volatile void* uaddr, int futex_op, uint val, const timespec* t
waiter rec;
rec.val = val;
rec.mask = mask;
rec.mask = val3;
const auto& ref = *map.emplace(uaddr, &rec);
int res = 0;
@@ -110,25 +107,25 @@ inline int futex(volatile void* uaddr, int futex_op, uint val, const timespec* t
{
// TODO
}
map.erase(std::find(map.find(uaddr), map.end(), ref));
return res;
}
case FUTEX_WAKE_PRIVATE:
case FUTEX_WAKE:
{
mask = -1;
[[fallthrough]];
val3 = -1;
// Fallthrough
}
case FUTEX_WAKE_BITSET_PRIVATE:
case FUTEX_WAKE_BITSET:
{
int res = 0;
for (auto range = map.equal_range(uaddr); val && range.first != range.second; range.first++)
{
auto& entry = *range.first->second;
if (entry.mask & mask)
if (entry.mask & val3)
{
entry.cv.notify_one();
entry.mask = 0;
@@ -146,163 +143,6 @@ inline int futex(volatile void* uaddr, int futex_op, uint val, const timespec* t
}
} g_futex;
return g_futex(uaddr, futex_op, val, timeout, mask);
#endif
}
template <typename T, typename Pred>
bool balanced_wait_until(atomic_t<T>& var, u64 usec_timeout, Pred&& pred)
{
static_assert(sizeof(T) == 4 || sizeof(T) == 8);
const bool is_inf = usec_timeout > u64{UINT32_MAX / 1000} * 1000000;
// Optional second argument indicates that the predicate should try to retire
auto test_pred = [&](T& _new, auto... args)
{
T old = var.load();
while (true)
{
_new = old;
// Zero indicates failure without modifying the value
// Negative indicates failure but modifies the value
auto ret = std::invoke(std::forward<Pred>(pred), _new, args...);
if (LIKELY(!ret || var.compare_exchange(old, _new)))
{
return ret > 0;
}
}
};
T value;
#ifdef _WIN32
if (OptWaitOnAddress)
{
while (!test_pred(value))
{
if (OptWaitOnAddress(&var, &value, sizeof(T), is_inf ? INFINITE : usec_timeout / 1000))
{
if (!test_pred(value, nullptr))
{
return false;
}
break;
}
if (GetLastError() == ERROR_TIMEOUT)
{
// Retire
return test_pred(value, nullptr);
}
}
return true;
}
LARGE_INTEGER timeout;
timeout.QuadPart = usec_timeout * -10;
if (!usec_timeout || NtWaitForKeyedEvent(nullptr, &var, false, is_inf ? nullptr : &timeout))
{
// Timed out: retire
if (!test_pred(value, nullptr))
{
return false;
}
// Signaled in the last moment: restore balance
NtWaitForKeyedEvent(nullptr, &var, false, nullptr);
return true;
}
if (!test_pred(value, nullptr))
{
// Stolen notification: restore balance
NtReleaseKeyedEvent(nullptr, &var, false, nullptr);
return false;
}
return true;
#else
struct timespec timeout;
timeout.tv_sec = usec_timeout / 1000000;
timeout.tv_nsec = (usec_timeout % 1000000) * 1000;
char* ptr = reinterpret_cast<char*>(&var);
if constexpr (sizeof(T) == 8)
{
ptr += 4 * IS_BE_MACHINE;
}
while (!test_pred(value))
{
if (futex(ptr, FUTEX_WAIT_PRIVATE, static_cast<u32>(value), is_inf ? nullptr : &timeout) == 0)
{
if (!test_pred(value, nullptr))
{
return false;
}
break;
}
switch (errno)
{
case EAGAIN: break;
case ETIMEDOUT: return test_pred(value, nullptr);
default: verify("Unknown futex error" HERE), 0;
}
}
return true;
#endif
}
template <bool All = false, typename T>
void balanced_awaken(atomic_t<T>& var, u32 weight)
{
static_assert(sizeof(T) == 4 || sizeof(T) == 8);
#ifdef _WIN32
if (OptWaitOnAddress)
{
if (All || weight > 3)
{
OptWakeByAddressAll(&var);
return;
}
for (u32 i = 0; i < weight; i++)
{
OptWakeByAddressSingle(&var);
}
return;
}
for (u32 i = 0; i < weight; i++)
{
NtReleaseKeyedEvent(nullptr, &var, false, nullptr);
}
#else
char* ptr = reinterpret_cast<char*>(&var);
if constexpr (sizeof(T) == 8)
{
ptr += 4 * IS_BE_MACHINE;
}
if (All || weight)
{
futex(ptr, FUTEX_WAKE_PRIVATE, All ? INT_MAX : std::min<u32>(INT_MAX, weight));
}
return;
return g_futex(uaddr, futex_op, val, timeout, uaddr2, val3);
#endif
}
Executable → Regular
+2 -179
View File
@@ -1,74 +1,12 @@
#include "sysinfo.h"
#include "sysinfo.h"
#include "StrFmt.h"
#include "File.h"
#include "Emu/System.h"
#ifdef _WIN32
#include "windows.h"
#include "sysinfoapi.h"
#include "subauth.h"
#include "stringapiset.h"
#else
#include <unistd.h>
#include <sys/utsname.h>
#include <errno.h>
#endif
bool utils::has_ssse3()
{
static const bool g_value = get_cpuid(0, 0)[0] >= 0x1 && get_cpuid(1, 0)[2] & 0x200;
return g_value;
}
bool utils::has_sse41()
{
static const bool g_value = get_cpuid(0, 0)[0] >= 0x1 && get_cpuid(1, 0)[2] & 0x80000;
return g_value;
}
bool utils::has_avx()
{
static const bool g_value = get_cpuid(0, 0)[0] >= 0x1 && get_cpuid(1, 0)[2] & 0x10000000 && (get_cpuid(1, 0)[2] & 0x0C000000) == 0x0C000000 && (get_xgetbv(0) & 0x6) == 0x6;
return g_value;
}
bool utils::has_avx2()
{
static const bool g_value = get_cpuid(0, 0)[0] >= 0x7 && get_cpuid(7, 0)[1] & 0x20 && (get_cpuid(1, 0)[2] & 0x0C000000) == 0x0C000000 && (get_xgetbv(0) & 0x6) == 0x6;
return g_value;
}
bool utils::has_rtm()
{
static const bool g_value = get_cpuid(0, 0)[0] >= 0x7 && (get_cpuid(7, 0)[1] & 0x800) == 0x800;
return g_value;
}
bool utils::has_tsx_force_abort()
{
static const bool g_value = get_cpuid(0, 0)[0] >= 0x7 && (get_cpuid(7, 0)[3] & 0x2000) == 0x2000;
return g_value;
}
bool utils::has_mpx()
{
static const bool g_value = get_cpuid(0, 0)[0] >= 0x7 && (get_cpuid(7, 0)[1] & 0x4000) == 0x4000;
return g_value;
}
bool utils::has_512()
{
// Check AVX512F, AVX512CD, AVX512DQ, AVX512BW, AVX512VL extensions (Skylake-X level support)
static const bool g_value = get_cpuid(0, 0)[0] >= 0x7 && (get_cpuid(7, 0)[1] & 0xd0030000) == 0xd0030000 && (get_cpuid(1,0)[2] & 0x0C000000) == 0x0C000000 && (get_xgetbv(0) & 0xe6) == 0xe6;
return g_value;
}
bool utils::has_xop()
{
static const bool g_value = has_avx() && get_cpuid(0x80000001, 0)[2] & 0x800;
return g_value;
}
std::string utils::get_system_info()
{
std::string result;
@@ -86,9 +24,8 @@ std::string utils::get_system_info()
brand = "Unknown CPU";
}
brand.erase(brand.find_last_not_of('\0') + 1);
brand.erase(brand.find_last_not_of(' ') + 1);
brand.erase(0, brand.find_first_not_of(' '));
brand.erase(brand.find_last_not_of(' ') + 1);
while (auto found = brand.find(" ") + 1)
{
@@ -110,129 +47,15 @@ std::string utils::get_system_info()
fmt::append(result, "%s | %d Threads | %.2f GiB RAM", brand, num_proc, mem_total / (1024.0f * 1024 * 1024));
if (const ullong tsc_freq = get_tsc_freq())
{
fmt::append(result, " | TSC: %.02fGHz", tsc_freq / 1000000000.);
}
else
{
fmt::append(result, " | TSC: Bad");
}
if (has_avx())
{
result += " | AVX";
if (has_avx2())
{
result += '+';
}
if (has_512())
{
result += '+';
}
if (has_xop())
{
result += 'x';
}
}
if (has_rtm())
{
result += " | TSX";
if (has_tsx_force_abort())
{
result += "-FA";
}
if (!has_mpx())
{
result += " disabled by default";
}
}
return result;
}
std::string utils::get_firmware_version()
{
const std::string file_path = g_cfg.vfs.get_dev_flash() + "vsh/etc/version.txt";
if (fs::is_file(file_path))
{
const fs::file version_file = fs::file(file_path);
std::string version = version_file.to_string();
// Extract version
const size_t start = version.find_first_of(':') + 1;
const size_t end = version.find_first_of(':', start);
version = version.substr(start, end - start);
// Trim version
const size_t trim_start = version.find_first_not_of('0');
const size_t trim_end = version.find_last_not_of('0');
version = version.substr(trim_start, trim_end);
return version;
}
return "";
}
std::string utils::get_OS_version()
{
std::string output = "";
#ifdef _WIN32
// GetVersionEx is deprecated, RtlGetVersion is kernel-mode only and AnalyticsInfo is UWP only.
// So we're forced to read PEB instead to get Windows version info. It's ugly but works.
const DWORD peb_offset = 0x60;
const INT_PTR peb = __readgsqword(peb_offset);
const DWORD version_major = *reinterpret_cast<const DWORD*>(peb + 0x118);
const DWORD version_minor = *reinterpret_cast<const DWORD*>(peb + 0x11c);
const WORD build = *reinterpret_cast<const WORD*>(peb + 0x120);
const UNICODE_STRING service_pack = *reinterpret_cast<const UNICODE_STRING*>(peb + 0x02E8);
const u64 compatibility_mode = *reinterpret_cast<const u64*>(peb + 0x02C8); // Two DWORDs, major & minor version
const bool has_sp = service_pack.Length > 0;
std::vector<char> holder(service_pack.Length + 1, '\0');
if (has_sp)
{
WideCharToMultiByte(CP_UTF8, NULL, service_pack.Buffer, service_pack.Length,
(LPSTR) holder.data(), static_cast<int>(holder.size()), NULL, NULL);
}
fmt::append(output,
"Operating system: Windows, Major: %lu, Minor: %lu, Build: %u, Service Pack: %s, Compatibility mode: %llu",
version_major, version_minor, build, has_sp ? holder.data() : "none", compatibility_mode);
#else
struct utsname details = {};
if (!uname(&details))
{
fmt::append(output, "Operating system: POSIX, Name: %s, Release: %s, Version: %s",
details.sysname, details.release, details.version);
}
else
{
fmt::append(output, "Operating system: POSIX, Unknown version! (Error: %d)", errno);
}
#endif
return output;
}
ullong utils::get_tsc_freq()
{
#ifdef _WIN32
LARGE_INTEGER freq;
if (!QueryPerformanceFrequency(&freq) || freq.QuadPart > 9'999'999)
return 0;
return freq.QuadPart * 1024;
#else
// TODO
return 0;
#endif
}
Executable → Regular
+30 -31
View File
@@ -1,4 +1,4 @@
#pragma once
#pragma once
#include "types.h"
#include <string>
@@ -8,48 +8,47 @@ namespace utils
inline std::array<u32, 4> get_cpuid(u32 func, u32 subfunc)
{
int regs[4];
#ifdef _MSC_VER
#ifdef _MSC_VER
__cpuidex(regs, func, subfunc);
#else
#else
__asm__ volatile("cpuid" : "=a" (regs[0]), "=b" (regs[1]), "=c" (regs[2]), "=d" (regs[3]) : "a" (func), "c" (subfunc));
#endif
#endif
return {0u+regs[0], 0u+regs[1], 0u+regs[2], 0u+regs[3]};
}
inline u64 get_xgetbv(u32 xcr)
inline bool has_ssse3()
{
#ifdef _MSC_VER
return _xgetbv(xcr);
#else
u32 eax, edx;
__asm__ volatile( "xgetbv" : "=a"(eax), "=d"(edx) : "c"(xcr));
return eax | (u64(edx) << 32);
#endif
return get_cpuid(0, 0)[0] >= 0x1 && get_cpuid(1, 0)[2] & 0x200;
}
bool has_ssse3();
inline bool has_avx()
{
return get_cpuid(0, 0)[0] >= 0x1 && get_cpuid(1, 0)[2] & 0x10000000;
}
bool has_sse41();
inline bool has_rtm()
{
// Check RTM and MPX extensions in order to filter out TSX on Haswell CPUs
return get_cpuid(0, 0)[0] >= 0x7 && (get_cpuid(7, 0)[1] & 0x4800) == 0x4800;
}
bool has_avx();
inline bool transaction_enter()
{
while (true)
{
const auto status = _xbegin();
bool has_avx2();
if (status == _XBEGIN_STARTED)
{
return true;
}
bool has_rtm();
bool has_tsx_force_abort();
bool has_mpx();
bool has_512();
bool has_xop();
if (!(status & _XABORT_RETRY))
{
return false;
}
}
}
std::string get_system_info();
std::string get_firmware_version();
std::string get_OS_version();
ullong get_tsc_freq();
}
-1058
View File
File diff suppressed because it is too large Load Diff
+290 -154
View File
@@ -1,4 +1,4 @@
#pragma once
#pragma once
#ifdef _MSC_VER
#include <intrin.h>
@@ -9,100 +9,58 @@
#include <emmintrin.h>
#include <cstdint>
#include <cstddef>
#include <cstring>
#include <type_traits>
#include <utility>
#include <chrono>
#include <limits>
#include <array>
// Assume little-endian
#define IS_LE_MACHINE 1
#define IS_BE_MACHINE 0
#ifndef __has_builtin
#define __has_builtin(x) 0
#endif
#ifdef _MSC_VER
#define ASSUME(...) __assume(__VA_ARGS__) // MSVC __assume ignores side-effects
#define ASSUME(cond) __assume(cond)
#define LIKELY
#define UNLIKELY
#define SAFE_BUFFERS __declspec(safebuffers)
#define NEVER_INLINE __declspec(noinline)
#define FORCE_INLINE __forceinline
#define RESTRICT __restrict
#else // not _MSC_VER
#ifdef __clang__
#if defined(__has_builtin) && __has_builtin(__builtin_assume)
#pragma clang diagnostic ignored "-Wassume" // ignore the clang "side-effects ignored" warning
#define ASSUME(...) __builtin_assume(!!(__VA_ARGS__)) // __builtin_assume (supported by modern clang) ignores side-effects
#endif
#endif
#ifndef ASSUME // gcc and old clang
#define ASSUME(...) do { if (!(__VA_ARGS__)) __builtin_unreachable(); } while (0) // note: the compiler will generate code to evaluate "cond" if the expression is opaque
#endif
#define LIKELY(...) __builtin_expect(!!(__VA_ARGS__), 1)
#define UNLIKELY(...) __builtin_expect(!!(__VA_ARGS__), 0)
#else
#define ASSUME(cond) do { if (!(cond)) __builtin_unreachable(); } while (0)
#define LIKELY(cond) __builtin_expect(!!(cond), 1)
#define UNLIKELY(cond) __builtin_expect(!!(cond), 0)
#define SAFE_BUFFERS
#define NEVER_INLINE __attribute__((noinline))
#define FORCE_INLINE __attribute__((always_inline)) inline
#define RESTRICT __restrict__
#endif // _MSC_VER
#endif
#define CHECK_SIZE(type, size) static_assert(sizeof(type) == size, "Invalid " #type " type size")
#define CHECK_ALIGN(type, align) static_assert(alignof(type) == align, "Invalid " #type " type alignment")
#define CHECK_MAX_SIZE(type, size) static_assert(sizeof(type) <= size, #type " type size is too big")
#define CHECK_SIZE_ALIGN(type, size, align) CHECK_SIZE(type, size); CHECK_ALIGN(type, align)
// Variant pattern matching helper
#define MATCH(arg, ...) constexpr(std::is_same_v<std::decay_t<decltype(arg)>, __VA_ARGS__>)
// Return 32 bit sizeof() to avoid widening/narrowing conversions with size_t
#define SIZE_32(...) static_cast<u32>(sizeof(__VA_ARGS__))
// Return 32 bit alignof() to avoid widening/narrowing conversions with size_t
#define ALIGN_32(...) static_cast<u32>(alignof(__VA_ARGS__))
#define CONCATENATE_DETAIL(x, y) x ## y
#define CONCATENATE(x, y) CONCATENATE_DETAIL(x, y)
#define STRINGIZE_DETAIL(x) #x ""
#define STRINGIZE_DETAIL(x) #x
#define STRINGIZE(x) STRINGIZE_DETAIL(x)
#define HERE "\n(in file " __FILE__ ":" STRINGIZE(__LINE__) ")"
// Ensure that the expression evaluates to true. Obsolete.
//#define EXPECTS(...) do { if (!(__VA_ARGS__)) fmt::raw_error("Precondition failed: " #__VA_ARGS__ HERE); } while (0)
//#define ENSURES(...) do { if (!(__VA_ARGS__)) fmt::raw_error("Postcondition failed: " #__VA_ARGS__ HERE); } while (0)
#define DECLARE(...) decltype(__VA_ARGS__) __VA_ARGS__
#define STR_CASE(...) case __VA_ARGS__: return #__VA_ARGS__
#define ASSERT(...) do { if(!(__VA_ARGS__)) fmt::raw_error("Assertion failed: " STRINGIZE(__VA_ARGS__) HERE); } while(0)
#if defined(_DEBUG) || defined(_AUDIT)
#define AUDIT(...) ASSERT(__VA_ARGS__)
#else
#define AUDIT(...) ((void)0)
#endif
#if defined(__cpp_lib_bit_cast) && (__cpp_lib_bit_cast >= 201806L)
#include <bit>
#else
namespace std
{
template <class To, class From, typename = std::enable_if_t<sizeof(To) == sizeof(From)>>
constexpr To bit_cast(const From& from) noexcept
{
static_assert(sizeof(To) == sizeof(From), "std::bit_cast<>: incompatible type size");
To result;
std::memcpy(&result, &from, sizeof(From));
return result;
}
}
#endif
using schar = signed char;
using uchar = unsigned char;
using ushort = unsigned short;
@@ -111,11 +69,7 @@ using ulong = unsigned long;
using ullong = unsigned long long;
using llong = long long;
#if __APPLE__
using uptr = std::uint64_t;
#else
using uptr = std::uintptr_t;
#endif
using u8 = std::uint8_t;
using u16 = std::uint16_t;
@@ -131,49 +85,9 @@ using steady_clock = std::conditional<
std::chrono::high_resolution_clock::is_steady,
std::chrono::high_resolution_clock, std::chrono::steady_clock>::type;
// Get integral type from type size
template <std::size_t N>
struct get_int_impl
{
};
template <>
struct get_int_impl<sizeof(u8)>
{
using utype = u8;
using stype = s8;
};
template <>
struct get_int_impl<sizeof(u16)>
{
using utype = u16;
using stype = s16;
};
template <>
struct get_int_impl<sizeof(u32)>
{
using utype = u32;
using stype = s32;
};
template <>
struct get_int_impl<sizeof(u64)>
{
using utype = u64;
using stype = s64;
};
template <std::size_t N>
using get_uint_t = typename get_int_impl<N>::utype;
template <std::size_t N>
using get_sint_t = typename get_int_impl<N>::stype;
namespace gsl
{
using std::byte;
enum class byte : u8;
}
// Formatting helper, type-specific preprocessing for improving safety and functionality
@@ -197,9 +111,64 @@ struct se_storage;
template <typename T, bool Se = true, std::size_t Align = alignof(T)>
class se_t;
template <typename T, std::size_t Size = sizeof(T)>
struct atomic_storage;
template <typename T1, typename T2, typename = void>
struct atomic_add;
template <typename T1, typename T2, typename = void>
struct atomic_sub;
template <typename T1, typename T2, typename = void>
struct atomic_and;
template <typename T1, typename T2, typename = void>
struct atomic_or;
template <typename T1, typename T2, typename = void>
struct atomic_xor;
template <typename T, typename = void>
struct atomic_pre_inc;
template <typename T, typename = void>
struct atomic_post_inc;
template <typename T, typename = void>
struct atomic_pre_dec;
template <typename T, typename = void>
struct atomic_post_dec;
template <typename T1, typename T2, typename = void>
struct atomic_test_and_set;
template <typename T1, typename T2, typename = void>
struct atomic_test_and_reset;
template <typename T1, typename T2, typename = void>
struct atomic_test_and_complement;
template <typename T>
class atomic_t;
#ifdef _MSC_VER
using std::void_t;
#else
namespace void_details
{
template <typename...>
struct make_void
{
using type = void;
};
}
template <typename... T>
using void_t = typename void_details::make_void<T...>::type;
#endif
// Extract T::simple_type if available, remove cv qualifiers
template <typename T, typename = void>
struct simple_type_helper
@@ -208,7 +177,7 @@ struct simple_type_helper
};
template <typename T>
struct simple_type_helper<T, std::void_t<typename T::simple_type>>
struct simple_type_helper<T, void_t<typename T::simple_type>>
{
using type = typename T::simple_type;
};
@@ -235,6 +204,22 @@ public:
}
};
// Bool wrapper for restricting bool result conversions
struct explicit_bool_t
{
const bool value;
constexpr explicit_bool_t(bool value)
: value(value)
{
}
explicit constexpr operator bool() const
{
return value;
}
};
#ifndef _MSC_VER
using u128 = __uint128_t;
using s128 = __int128_t;
@@ -424,9 +409,6 @@ struct alignas(16) s128
CHECK_SIZE_ALIGN(u128, 16, 16);
CHECK_SIZE_ALIGN(s128, 16, 16);
using f32 = float;
using f64 = double;
union alignas(2) f16
{
u16 _u16;
@@ -437,20 +419,30 @@ union alignas(2) f16
_u16 = raw;
}
explicit operator f32() const
explicit operator float() const
{
// See http://stackoverflow.com/a/26779139
// The conversion doesn't handle NaN/Inf
u32 raw = ((_u16 & 0x8000) << 16) | // Sign (just moved)
(((_u16 & 0x7c00) + 0x1C000) << 13) | // Exponent ( exp - 15 + 127)
((_u16 & 0x03FF) << 13); // Mantissa
return std::bit_cast<f32>(raw);
return (float&)raw;
}
};
CHECK_SIZE_ALIGN(f16, 2, 2);
using f32 = float;
using f64 = double;
struct ignore
{
template <typename T>
ignore(T)
{
}
};
template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
constexpr T align(const T& value, ullong align)
{
@@ -461,11 +453,13 @@ template <typename T, typename T2>
inline u32 offset32(T T2::*const mptr)
{
#ifdef _MSC_VER
return std::bit_cast<u32>(mptr);
static_assert(sizeof(mptr) == sizeof(u32), "Invalid pointer-to-member size");
return reinterpret_cast<const u32&>(mptr);
#elif __GNUG__
return std::bit_cast<std::size_t>(mptr);
static_assert(sizeof(mptr) == sizeof(std::size_t), "Invalid pointer-to-member size");
return static_cast<u32>(reinterpret_cast<const std::size_t&>(mptr));
#else
static_assert(sizeof(mptr) == 0, "Unsupported pointer-to-member size");
static_assert(sizeof(mptr) == 0, "Invalid pointer-to-member size");
#endif
}
@@ -477,7 +471,7 @@ struct offset32_array
template <typename Arg>
static inline u32 index32(const Arg& arg)
{
return u32{sizeof(std::remove_extent_t<T>)} * static_cast<u32>(arg);
return SIZE_32(std::remove_extent_t<T>) * static_cast<u32>(arg);
}
};
@@ -487,7 +481,7 @@ struct offset32_array<std::array<T, N>>
template <typename Arg>
static inline u32 index32(const Arg& arg)
{
return u32{sizeof(T)} * static_cast<u32>(arg);
return SIZE_32(T) * static_cast<u32>(arg);
}
};
@@ -520,6 +514,26 @@ struct offset32_detail<T3 T4::*>
}
};
inline u32 cntlz32(u32 arg, bool nonzero = false)
{
#ifdef _MSC_VER
ulong res;
return _BitScanReverse(&res, arg) || nonzero ? res ^ 31 : 32;
#else
return arg || nonzero ? __builtin_clzll(arg) - 32 : 32;
#endif
}
inline u64 cntlz64(u64 arg, bool nonzero = false)
{
#ifdef _MSC_VER
ulong res;
return _BitScanReverse64(&res, arg) || nonzero ? res ^ 63 : 64;
#else
return arg || nonzero ? __builtin_clzll(arg) : 64;
#endif
}
// Helper function, used by ""_u16, ""_u32, ""_u64
constexpr u8 to_u8(char c)
{
@@ -691,7 +705,7 @@ struct narrow_impl<From, To, std::enable_if_t<std::is_signed<From>::value && std
// Simple type enabled (TODO: allow for To as well)
template <typename From, typename To>
struct narrow_impl<From, To, std::void_t<typename From::simple_type>>
struct narrow_impl<From, To, void_t<typename From::simple_type>>
: narrow_impl<simple_t<From>, To>
{
};
@@ -723,6 +737,42 @@ constexpr u32 size32(const T (&)[Size], const char* msg = nullptr)
return static_cast<u32>(Size);
}
template <typename T1, typename = std::enable_if_t<std::is_integral<T1>::value>>
constexpr bool test(const T1& value)
{
return value != 0;
}
template <typename T1, typename T2, typename = std::enable_if_t<std::is_integral<T1>::value && std::is_integral<T2>::value>>
constexpr bool test(const T1& lhs, const T2& rhs)
{
return (lhs & rhs) != 0;
}
template <typename T, typename T2, typename = std::enable_if_t<std::is_integral<T>::value && std::is_integral<T2>::value>>
inline bool test_and_set(T& lhs, const T2& rhs)
{
const bool result = (lhs & rhs) != 0;
lhs |= rhs;
return result;
}
template <typename T, typename T2, typename = std::enable_if_t<std::is_integral<T>::value && std::is_integral<T2>::value>>
inline bool test_and_reset(T& lhs, const T2& rhs)
{
const bool result = (lhs & rhs) != 0;
lhs &= ~rhs;
return result;
}
template <typename T, typename T2, typename = std::enable_if_t<std::is_integral<T>::value && std::is_integral<T2>::value>>
inline bool test_and_complement(T& lhs, const T2& rhs)
{
const bool result = (lhs & rhs) != 0;
lhs ^= rhs;
return result;
}
// Simplified hash algorithm for pointers. May be used in std::unordered_(map|set).
template <typename T, std::size_t Align = alignof(T)>
struct pointer_hash
@@ -747,7 +797,7 @@ struct value_hash
template <template <typename> class TT, std::size_t S, std::size_t A = S>
struct alignas(A) any_pod
{
alignas(A) std::byte data[S];
std::aligned_storage_t<S, A> data;
any_pod() = default;
@@ -830,6 +880,35 @@ struct cmd64 : any64
static_assert(sizeof(cmd64) == 8 && std::is_pod<cmd64>::value, "Incorrect cmd64 type");
// Allows to define integer convertible to multiple types
template <typename T, T Value, typename T1 = void, typename... Ts>
struct multicast : multicast<T, Value, Ts...>
{
constexpr multicast()
: multicast<T, Value, Ts...>()
{
}
// Implicit conversion to desired type
constexpr operator T1() const
{
return static_cast<T1>(Value);
}
};
// Recursion terminator
template <typename T, T Value>
struct multicast<T, Value, void>
{
constexpr multicast() = default;
// Explicit conversion to base type
explicit constexpr operator T() const
{
return Value;
}
};
// Error code type (return type), implements error reporting. Could be a template.
struct error_code
{
@@ -854,23 +933,26 @@ struct error_code
};
template<typename ET>
struct is_error<ET, std::enable_if_t<sizeof(ET::__not_an_error) != 0>> : std::false_type
struct is_error<ET, void_t<decltype(ET::__not_an_error)>> : std::false_type
{
};
// Common constructor
template<typename ET>
error_code(const ET& value)
// Not an error constructor
template<typename ET, typename = decltype(ET::__not_an_error)>
error_code(const ET& value, std::nullptr_t = nullptr)
: value(static_cast<s32>(value))
{
if constexpr(is_error<ET>::value)
{
this->value = error_report(fmt::get_type_info<fmt_unveil_t<ET>>(), fmt_unveil<ET>::get(value), nullptr, 0);
}
}
// Error constructor
template<typename ET, typename = std::enable_if_t<is_error<ET>::value>>
error_code(const ET& value)
: value(error_report(fmt::get_type_info<fmt_unveil_t<ET>>(), fmt_unveil<ET>::get(value), nullptr, 0))
{
}
// Error constructor (2 args)
template<typename ET, typename T2>
template<typename ET, typename T2, typename = std::enable_if_t<is_error<ET>::value>>
error_code(const ET& value, const T2& value2)
: value(error_report(fmt::get_type_info<fmt_unveil_t<ET>>(), fmt_unveil<ET>::get(value), fmt::get_type_info<fmt_unveil_t<T2>>(), fmt_unveil<T2>::get(value2)))
{
@@ -890,39 +972,93 @@ constexpr FORCE_INLINE error_code::not_an_error not_an_error(const T& value)
}
// Synchronization helper (cache-friendly busy waiting)
inline void busy_wait(std::size_t cycles = 3000)
inline void busy_wait(std::size_t count = 100)
{
const u64 s = __rdtsc();
do _mm_pause(); while (__rdtsc() - s < cycles);
while (count--) _mm_pause();
}
// TODO: Remove when moving to c++20
template <typename T>
inline constexpr uintmax_t floor2(T value)
{
value >>= 1;
// Rotate helpers
#if defined(__GNUG__)
for (uintmax_t i = 0;; i++, value >>= 1)
{
if (value == 0)
{
return i;
}
}
inline u8 rol8(u8 x, u8 n)
{
u8 result = x;
__asm__("rolb %[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
return result;
}
template <typename T>
inline constexpr uintmax_t ceil2(T value)
inline u8 ror8(u8 x, u8 n)
{
const uintmax_t ispow2 = value & (value - 1); // if power of 2 the result is 0
u8 result = x;
__asm__("rorb %[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
return result;
}
value >>= 1;
inline u16 rol16(u16 x, u16 n)
{
u16 result = x;
__asm__("rolw %b[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
return result;
}
for (uintmax_t i = 0;; i++, value >>= 1)
{
if (value == 0)
{
return i + std::min<uintmax_t>(ispow2, 1);
}
}
}
inline u16 ror16(u16 x, u16 n)
{
u16 result = x;
__asm__("rorw %b[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
return result;
}
inline u32 rol32(u32 x, u32 n)
{
u32 result = x;
__asm__("roll %b[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
return result;
}
inline u32 ror32(u32 x, u32 n)
{
u32 result = x;
__asm__("rorl %b[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
return result;
}
inline u64 rol64(u64 x, u64 n)
{
u64 result = x;
__asm__("rolq %b[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
return result;
}
inline u64 ror64(u64 x, u64 n)
{
u64 result = x;
__asm__("rorq %b[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
return result;
}
inline u64 umulh64(u64 a, u64 b)
{
u64 result;
__asm__("mulq %[b]" : "=d" (result) : [a] "a" (a), [b] "rm" (b));
return result;
}
inline s64 mulh64(s64 a, s64 b)
{
s64 result;
__asm__("imulq %[b]" : "=d" (result) : [a] "a" (a), [b] "rm" (b));
return result;
}
#elif defined(_MSC_VER)
inline u8 rol8(u8 x, u8 n) { return _rotl8(x, n); }
inline u8 ror8(u8 x, u8 n) { return _rotr8(x, n); }
inline u16 rol16(u16 x, u16 n) { return _rotl16(x, (u8)n); }
inline u16 ror16(u16 x, u16 n) { return _rotr16(x, (u8)n); }
inline u32 rol32(u32 x, u32 n) { return _rotl(x, (int)n); }
inline u32 ror32(u32 x, u32 n) { return _rotr(x, (int)n); }
inline u64 rol64(u64 x, u64 n) { return _rotl64(x, (int)n); }
inline u64 ror64(u64 x, u64 n) { return _rotr64(x, (int)n); }
inline u64 umulh64(u64 x, u64 y) { return __umulh(x, y); }
inline s64 mulh64(s64 x, s64 y) { return __mulh(x, y); }
#endif
+963
View File
@@ -0,0 +1,963 @@
#ifndef MAPBOX_UTIL_VARIANT_HPP
#define MAPBOX_UTIL_VARIANT_HPP
#include <cassert>
#include <cstddef> // size_t
#include <new> // operator new
#include <stdexcept> // runtime_error
#include <string>
#include <tuple>
#include <type_traits>
#include <typeinfo>
#include <utility>
#include "recursive_wrapper.hpp"
// clang-format off
// [[deprecated]] is only available in C++14, use this for the time being
#if __cplusplus <= 201103L
# ifdef __GNUC__
# define MAPBOX_VARIANT_DEPRECATED __attribute__((deprecated))
# elif defined(_MSC_VER)
# define MAPBOX_VARIANT_DEPRECATED __declspec(deprecated)
# else
# define MAPBOX_VARIANT_DEPRECATED
# endif
#else
# define MAPBOX_VARIANT_DEPRECATED [[deprecated]]
#endif
#ifdef _MSC_VER
// https://msdn.microsoft.com/en-us/library/bw1hbe6y.aspx
# ifdef NDEBUG
# define VARIANT_INLINE __forceinline
# else
# define VARIANT_INLINE //__declspec(noinline)
# endif
#else
# ifdef NDEBUG
# define VARIANT_INLINE //inline __attribute__((always_inline))
# else
# define VARIANT_INLINE __attribute__((noinline))
# endif
#endif
// clang-format on
// Exceptions
#if defined( __EXCEPTIONS) || defined( _MSC_VER)
#define HAS_EXCEPTIONS
#endif
#define VARIANT_MAJOR_VERSION 1
#define VARIANT_MINOR_VERSION 1
#define VARIANT_PATCH_VERSION 0
#define VARIANT_VERSION (VARIANT_MAJOR_VERSION * 100000) + (VARIANT_MINOR_VERSION * 100) + (VARIANT_PATCH_VERSION)
//namespace mapbox {
// namespace util {
namespace std {
// XXX This should derive from std::logic_error instead of std::runtime_error.
// See https://github.com/mapbox/variant/issues/48 for details.
class bad_variant_access : public std::runtime_error
{
public:
explicit bad_variant_access(const std::string& what_arg)
: runtime_error(what_arg) {}
explicit bad_variant_access(const char* what_arg)
: runtime_error(what_arg) {}
}; // class bad_variant_access
namespace detail {
static constexpr std::size_t invalid_value = std::size_t(-1);
template <typename T, typename... Types>
struct direct_type;
template <typename T, typename First, typename... Types>
struct direct_type<T, First, Types...>
{
static constexpr std::size_t index = std::is_same<T, First>::value
? sizeof...(Types)
: direct_type<T, Types...>::index;
};
template <typename T>
struct direct_type<T>
{
static constexpr std::size_t index = invalid_value;
};
#if __cpp_lib_logical_traits >= 201510L
using std::disjunction;
#else
template <typename...>
struct disjunction : std::false_type {};
template <typename B1>
struct disjunction<B1> : B1 {};
template <typename B1, typename B2>
struct disjunction<B1, B2> : std::conditional<B1::value, B1, B2>::type {};
template <typename B1, typename... Bs>
struct disjunction<B1, Bs...> : std::conditional<B1::value, B1, disjunction<Bs...>>::type {};
#endif
template <typename T, typename... Types>
struct convertible_type;
template <typename T, typename First, typename... Types>
struct convertible_type<T, First, Types...>
{
static constexpr std::size_t index = std::is_convertible<T, First>::value
? disjunction<std::is_convertible<T, Types>...>::value ? invalid_value : sizeof...(Types)
: convertible_type<T, Types...>::index;
};
template <typename T>
struct convertible_type<T>
{
static constexpr std::size_t index = invalid_value;
};
template <typename T, typename... Types>
struct value_traits
{
using value_type = typename std::remove_const<typename std::remove_reference<T>::type>::type;
static constexpr std::size_t direct_index = direct_type<value_type, Types...>::index;
static constexpr bool is_direct = direct_index != invalid_value;
static constexpr std::size_t index = is_direct ? direct_index : convertible_type<value_type, Types...>::index;
static constexpr bool is_valid = index != invalid_value;
static constexpr std::size_t tindex = is_valid ? sizeof...(Types)-index : 0;
using target_type = typename std::tuple_element<tindex, std::tuple<void, Types...>>::type;
};
template <typename T, typename R = void>
struct enable_if_type
{
using type = R;
};
template <typename F, typename V, typename Enable = void>
struct result_of_unary_visit
{
using type = typename std::result_of<F(V&)>::type;
};
template <typename F, typename V>
struct result_of_unary_visit<F, V, typename enable_if_type<typename F::result_type>::type>
{
using type = typename F::result_type;
};
template <typename F, typename V, typename Enable = void>
struct result_of_binary_visit
{
using type = typename std::result_of<F(V&, V&)>::type;
};
template <typename F, typename V>
struct result_of_binary_visit<F, V, typename enable_if_type<typename F::result_type>::type>
{
using type = typename F::result_type;
};
template <std::size_t arg1, std::size_t... others>
struct static_max;
template <std::size_t arg>
struct static_max<arg>
{
static const std::size_t value = arg;
};
template <std::size_t arg1, std::size_t arg2, std::size_t... others>
struct static_max<arg1, arg2, others...>
{
static const std::size_t value = arg1 >= arg2 ? static_max<arg1, others...>::value : static_max<arg2, others...>::value;
};
template <typename... Types>
struct variant_helper;
template <typename T, typename... Types>
struct variant_helper<T, Types...>
{
VARIANT_INLINE static void destroy(const std::size_t type_index, void* data)
{
if (type_index == sizeof...(Types))
{
reinterpret_cast<T*>(data)->~T();
}
else
{
variant_helper<Types...>::destroy(type_index, data);
}
}
VARIANT_INLINE static void move(const std::size_t old_type_index, void* old_value, void* new_value)
{
if (old_type_index == sizeof...(Types))
{
new (new_value) T(std::move(*reinterpret_cast<T*>(old_value)));
}
else
{
variant_helper<Types...>::move(old_type_index, old_value, new_value);
}
}
VARIANT_INLINE static void copy(const std::size_t old_type_index, const void* old_value, void* new_value)
{
if (old_type_index == sizeof...(Types))
{
new (new_value) T(*reinterpret_cast<const T*>(old_value));
}
else
{
variant_helper<Types...>::copy(old_type_index, old_value, new_value);
}
}
};
template <>
struct variant_helper<>
{
VARIANT_INLINE static void destroy(const std::size_t, void*) {}
VARIANT_INLINE static void move(const std::size_t, void*, void*) {}
VARIANT_INLINE static void copy(const std::size_t, const void*, void*) {}
};
template <typename T>
struct unwrapper
{
static T const& apply_const(T const& obj) { return obj; }
static T& apply(T& obj) { return obj; }
};
template <typename T>
struct unwrapper<recursive_wrapper<T>>
{
static auto apply_const(recursive_wrapper<T> const& obj)
-> typename recursive_wrapper<T>::type const&
{
return obj.get();
}
static auto apply(recursive_wrapper<T>& obj)
-> typename recursive_wrapper<T>::type&
{
return obj.get();
}
};
template <typename T>
struct unwrapper<std::reference_wrapper<T>>
{
static auto apply_const(std::reference_wrapper<T> const& obj)
-> typename std::reference_wrapper<T>::type const&
{
return obj.get();
}
static auto apply(std::reference_wrapper<T>& obj)
-> typename std::reference_wrapper<T>::type&
{
return obj.get();
}
};
template <typename F, typename V, typename R, typename... Types>
struct dispatcher;
template <typename F, typename V, typename R, typename T, typename... Types>
struct dispatcher<F, V, R, T, Types...>
{
VARIANT_INLINE static R apply_const(V const& v, F&& f)
{
if (v.template is<T>())
{
return f(unwrapper<T>::apply_const(v.template get_unchecked<T>()));
}
else
{
return dispatcher<F, V, R, Types...>::apply_const(v, std::forward<F>(f));
}
}
VARIANT_INLINE static R apply(V& v, F&& f)
{
if (v.template is<T>())
{
return f(unwrapper<T>::apply(v.template get_unchecked<T>()));
}
else
{
return dispatcher<F, V, R, Types...>::apply(v, std::forward<F>(f));
}
}
};
template <typename F, typename V, typename R, typename T>
struct dispatcher<F, V, R, T>
{
VARIANT_INLINE static R apply_const(V const& v, F&& f)
{
return f(unwrapper<T>::apply_const(v.template get_unchecked<T>()));
}
VARIANT_INLINE static R apply(V& v, F&& f)
{
return f(unwrapper<T>::apply(v.template get_unchecked<T>()));
}
};
template <typename F, typename V, typename R, typename T, typename... Types>
struct binary_dispatcher_rhs;
template <typename F, typename V, typename R, typename T0, typename T1, typename... Types>
struct binary_dispatcher_rhs<F, V, R, T0, T1, Types...>
{
VARIANT_INLINE static R apply_const(V const& lhs, V const& rhs, F&& f)
{
if (rhs.template is<T1>()) // call binary functor
{
return f(unwrapper<T0>::apply_const(lhs.template get_unchecked<T0>()),
unwrapper<T1>::apply_const(rhs.template get_unchecked<T1>()));
}
else
{
return binary_dispatcher_rhs<F, V, R, T0, Types...>::apply_const(lhs, rhs, std::forward<F>(f));
}
}
VARIANT_INLINE static R apply(V& lhs, V& rhs, F&& f)
{
if (rhs.template is<T1>()) // call binary functor
{
return f(unwrapper<T0>::apply(lhs.template get_unchecked<T0>()),
unwrapper<T1>::apply(rhs.template get_unchecked<T1>()));
}
else
{
return binary_dispatcher_rhs<F, V, R, T0, Types...>::apply(lhs, rhs, std::forward<F>(f));
}
}
};
template <typename F, typename V, typename R, typename T0, typename T1>
struct binary_dispatcher_rhs<F, V, R, T0, T1>
{
VARIANT_INLINE static R apply_const(V const& lhs, V const& rhs, F&& f)
{
return f(unwrapper<T0>::apply_const(lhs.template get_unchecked<T0>()),
unwrapper<T1>::apply_const(rhs.template get_unchecked<T1>()));
}
VARIANT_INLINE static R apply(V& lhs, V& rhs, F&& f)
{
return f(unwrapper<T0>::apply(lhs.template get_unchecked<T0>()),
unwrapper<T1>::apply(rhs.template get_unchecked<T1>()));
}
};
template <typename F, typename V, typename R, typename T, typename... Types>
struct binary_dispatcher_lhs;
template <typename F, typename V, typename R, typename T0, typename T1, typename... Types>
struct binary_dispatcher_lhs<F, V, R, T0, T1, Types...>
{
VARIANT_INLINE static R apply_const(V const& lhs, V const& rhs, F&& f)
{
if (lhs.template is<T1>()) // call binary functor
{
return f(unwrapper<T1>::apply_const(lhs.template get_unchecked<T1>()),
unwrapper<T0>::apply_const(rhs.template get_unchecked<T0>()));
}
else
{
return binary_dispatcher_lhs<F, V, R, T0, Types...>::apply_const(lhs, rhs, std::forward<F>(f));
}
}
VARIANT_INLINE static R apply(V& lhs, V& rhs, F&& f)
{
if (lhs.template is<T1>()) // call binary functor
{
return f(unwrapper<T1>::apply(lhs.template get_unchecked<T1>()),
unwrapper<T0>::apply(rhs.template get_unchecked<T0>()));
}
else
{
return binary_dispatcher_lhs<F, V, R, T0, Types...>::apply(lhs, rhs, std::forward<F>(f));
}
}
};
template <typename F, typename V, typename R, typename T0, typename T1>
struct binary_dispatcher_lhs<F, V, R, T0, T1>
{
VARIANT_INLINE static R apply_const(V const& lhs, V const& rhs, F&& f)
{
return f(unwrapper<T1>::apply_const(lhs.template get_unchecked<T1>()),
unwrapper<T0>::apply_const(rhs.template get_unchecked<T0>()));
}
VARIANT_INLINE static R apply(V& lhs, V& rhs, F&& f)
{
return f(unwrapper<T1>::apply(lhs.template get_unchecked<T1>()),
unwrapper<T0>::apply(rhs.template get_unchecked<T0>()));
}
};
template <typename F, typename V, typename R, typename... Types>
struct binary_dispatcher;
template <typename F, typename V, typename R, typename T, typename... Types>
struct binary_dispatcher<F, V, R, T, Types...>
{
VARIANT_INLINE static R apply_const(V const& v0, V const& v1, F&& f)
{
if (v0.template is<T>())
{
if (v1.template is<T>())
{
return f(unwrapper<T>::apply_const(v0.template get_unchecked<T>()),
unwrapper<T>::apply_const(v1.template get_unchecked<T>())); // call binary functor
}
else
{
return binary_dispatcher_rhs<F, V, R, T, Types...>::apply_const(v0, v1, std::forward<F>(f));
}
}
else if (v1.template is<T>())
{
return binary_dispatcher_lhs<F, V, R, T, Types...>::apply_const(v0, v1, std::forward<F>(f));
}
return binary_dispatcher<F, V, R, Types...>::apply_const(v0, v1, std::forward<F>(f));
}
VARIANT_INLINE static R apply(V& v0, V& v1, F&& f)
{
if (v0.template is<T>())
{
if (v1.template is<T>())
{
return f(unwrapper<T>::apply(v0.template get_unchecked<T>()),
unwrapper<T>::apply(v1.template get_unchecked<T>())); // call binary functor
}
else
{
return binary_dispatcher_rhs<F, V, R, T, Types...>::apply(v0, v1, std::forward<F>(f));
}
}
else if (v1.template is<T>())
{
return binary_dispatcher_lhs<F, V, R, T, Types...>::apply(v0, v1, std::forward<F>(f));
}
return binary_dispatcher<F, V, R, Types...>::apply(v0, v1, std::forward<F>(f));
}
};
template <typename F, typename V, typename R, typename T>
struct binary_dispatcher<F, V, R, T>
{
VARIANT_INLINE static R apply_const(V const& v0, V const& v1, F&& f)
{
return f(unwrapper<T>::apply_const(v0.template get_unchecked<T>()),
unwrapper<T>::apply_const(v1.template get_unchecked<T>())); // call binary functor
}
VARIANT_INLINE static R apply(V& v0, V& v1, F&& f)
{
return f(unwrapper<T>::apply(v0.template get_unchecked<T>()),
unwrapper<T>::apply(v1.template get_unchecked<T>())); // call binary functor
}
};
// comparator functors
struct equal_comp
{
template <typename T>
bool operator()(T const& lhs, T const& rhs) const
{
return lhs == rhs;
}
};
struct less_comp
{
template <typename T>
bool operator()(T const& lhs, T const& rhs) const
{
return lhs < rhs;
}
};
template <typename Variant, typename Comp>
class comparer
{
public:
explicit comparer(Variant const& lhs) noexcept
: lhs_(lhs) {}
comparer& operator=(comparer const&) = delete;
// visitor
template <typename T>
bool operator()(T const& rhs_content) const
{
T const& lhs_content = lhs_.template get_unchecked<T>();
return Comp()(lhs_content, rhs_content);
}
private:
Variant const& lhs_;
};
} // namespace detail
struct no_init
{
};
template <typename... Types>
class variant
{
static_assert(sizeof...(Types) > 0, "Template parameter type list of variant can not be empty");
static_assert(!detail::disjunction<std::is_reference<Types>...>::value, "Variant can not hold reference types. Maybe use std::reference_wrapper?");
private:
static const std::size_t data_size = detail::static_max<sizeof(Types)...>::value;
static const std::size_t data_align = detail::static_max<alignof(Types)...>::value;
using first_type = typename std::tuple_element<0, std::tuple<Types...>>::type;
using data_type = typename std::aligned_storage<data_size, data_align>::type;
using helper_type = detail::variant_helper<Types...>;
std::size_t type_index;
data_type data;
public:
VARIANT_INLINE variant() noexcept(std::is_nothrow_default_constructible<first_type>::value)
: type_index(sizeof...(Types)-1)
{
static_assert(std::is_default_constructible<first_type>::value, "First type in variant must be default constructible to allow default construction of variant");
new (&data) first_type();
}
VARIANT_INLINE variant(no_init) noexcept
: type_index(detail::invalid_value) {}
// http://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers
template <typename T, typename Traits = detail::value_traits<T, Types...>,
typename Enable = typename std::enable_if<Traits::is_valid>::type>
VARIANT_INLINE variant(T&& val) noexcept(std::is_nothrow_constructible<typename Traits::target_type, T&&>::value)
: type_index(Traits::index)
{
new (&data) typename Traits::target_type(std::forward<T>(val));
}
VARIANT_INLINE variant(variant<Types...> const& old)
: type_index(old.type_index)
{
helper_type::copy(old.type_index, &old.data, &data);
}
VARIANT_INLINE variant(variant<Types...>&& old) noexcept(std::is_nothrow_move_constructible<std::tuple<Types...>>::value)
: type_index(old.type_index)
{
helper_type::move(old.type_index, &old.data, &data);
}
private:
VARIANT_INLINE void copy_assign(variant<Types...> const& rhs)
{
helper_type::destroy(type_index, &data);
type_index = detail::invalid_value;
helper_type::copy(rhs.type_index, &rhs.data, &data);
type_index = rhs.type_index;
}
VARIANT_INLINE void move_assign(variant<Types...>&& rhs)
{
helper_type::destroy(type_index, &data);
type_index = detail::invalid_value;
helper_type::move(rhs.type_index, &rhs.data, &data);
type_index = rhs.type_index;
}
public:
VARIANT_INLINE variant<Types...>& operator=(variant<Types...>&& other)
{
move_assign(std::move(other));
return *this;
}
VARIANT_INLINE variant<Types...>& operator=(variant<Types...> const& other)
{
copy_assign(other);
return *this;
}
// conversions
// move-assign
template <typename T>
VARIANT_INLINE variant<Types...>& operator=(T&& rhs) noexcept
{
variant<Types...> temp(std::forward<T>(rhs));
move_assign(std::move(temp));
return *this;
}
// copy-assign
template <typename T>
VARIANT_INLINE variant<Types...>& operator=(T const& rhs)
{
variant<Types...> temp(rhs);
copy_assign(temp);
return *this;
}
template <typename T, typename std::enable_if<
(detail::direct_type<T, Types...>::index != detail::invalid_value)>::type* = nullptr>
VARIANT_INLINE bool is() const
{
return type_index == detail::direct_type<T, Types...>::index;
}
template <typename T, typename std::enable_if<
(detail::direct_type<recursive_wrapper<T>, Types...>::index != detail::invalid_value)>::type* = nullptr>
VARIANT_INLINE bool is() const
{
return type_index == detail::direct_type<recursive_wrapper<T>, Types...>::index;
}
VARIANT_INLINE bool valid() const
{
return type_index != detail::invalid_value;
}
template <typename T, typename... Args>
VARIANT_INLINE void set(Args&&... args)
{
helper_type::destroy(type_index, &data);
type_index = detail::invalid_value;
new (&data) T(std::forward<Args>(args)...);
type_index = detail::direct_type<T, Types...>::index;
}
// get_unchecked<T>()
template <typename T, typename std::enable_if<
(detail::direct_type<T, Types...>::index != detail::invalid_value)>::type* = nullptr>
VARIANT_INLINE T& get_unchecked()
{
return *reinterpret_cast<T*>(&data);
}
#ifdef HAS_EXCEPTIONS
// get<T>()
template <typename T, typename std::enable_if<
(detail::direct_type<T, Types...>::index != detail::invalid_value)>::type* = nullptr>
VARIANT_INLINE T& get()
{
if (type_index == detail::direct_type<T, Types...>::index)
{
return *reinterpret_cast<T*>(&data);
}
else
{
throw bad_variant_access("in get<T>()");
}
}
#endif
template <typename T, typename std::enable_if<
(detail::direct_type<T, Types...>::index != detail::invalid_value)>::type* = nullptr>
VARIANT_INLINE T const& get_unchecked() const
{
return *reinterpret_cast<T const*>(&data);
}
#ifdef HAS_EXCEPTIONS
template <typename T, typename std::enable_if<
(detail::direct_type<T, Types...>::index != detail::invalid_value)>::type* = nullptr>
VARIANT_INLINE T const& get() const
{
if (type_index == detail::direct_type<T, Types...>::index)
{
return *reinterpret_cast<T const*>(&data);
}
else
{
throw bad_variant_access("in get<T>()");
}
}
#endif
// get_unchecked<T>() - T stored as recursive_wrapper<T>
template <typename T, typename std::enable_if<
(detail::direct_type<recursive_wrapper<T>, Types...>::index != detail::invalid_value)>::type* = nullptr>
VARIANT_INLINE T& get_unchecked()
{
return (*reinterpret_cast<recursive_wrapper<T>*>(&data)).get();
}
#ifdef HAS_EXCEPTIONS
// get<T>() - T stored as recursive_wrapper<T>
template <typename T, typename std::enable_if<
(detail::direct_type<recursive_wrapper<T>, Types...>::index != detail::invalid_value)>::type* = nullptr>
VARIANT_INLINE T& get()
{
if (type_index == detail::direct_type<recursive_wrapper<T>, Types...>::index)
{
return (*reinterpret_cast<recursive_wrapper<T>*>(&data)).get();
}
else
{
throw bad_variant_access("in get<T>()");
}
}
#endif
template <typename T, typename std::enable_if<
(detail::direct_type<recursive_wrapper<T>, Types...>::index != detail::invalid_value)>::type* = nullptr>
VARIANT_INLINE T const& get_unchecked() const
{
return (*reinterpret_cast<recursive_wrapper<T> const*>(&data)).get();
}
#ifdef HAS_EXCEPTIONS
template <typename T, typename std::enable_if<
(detail::direct_type<recursive_wrapper<T>, Types...>::index != detail::invalid_value)>::type* = nullptr>
VARIANT_INLINE T const& get() const
{
if (type_index == detail::direct_type<recursive_wrapper<T>, Types...>::index)
{
return (*reinterpret_cast<recursive_wrapper<T> const*>(&data)).get();
}
else
{
throw bad_variant_access("in get<T>()");
}
}
#endif
// get_unchecked<T>() - T stored as std::reference_wrapper<T>
template <typename T, typename std::enable_if<
(detail::direct_type<std::reference_wrapper<T>, Types...>::index != detail::invalid_value)>::type* = nullptr>
VARIANT_INLINE T& get_unchecked()
{
return (*reinterpret_cast<std::reference_wrapper<T>*>(&data)).get();
}
#ifdef HAS_EXCEPTIONS
// get<T>() - T stored as std::reference_wrapper<T>
template <typename T, typename std::enable_if<
(detail::direct_type<std::reference_wrapper<T>, Types...>::index != detail::invalid_value)>::type* = nullptr>
VARIANT_INLINE T& get()
{
if (type_index == detail::direct_type<std::reference_wrapper<T>, Types...>::index)
{
return (*reinterpret_cast<std::reference_wrapper<T>*>(&data)).get();
}
else
{
throw bad_variant_access("in get<T>()");
}
}
#endif
template <typename T, typename std::enable_if<
(detail::direct_type<std::reference_wrapper<T const>, Types...>::index != detail::invalid_value)>::type* = nullptr>
VARIANT_INLINE T const& get_unchecked() const
{
return (*reinterpret_cast<std::reference_wrapper<T const> const*>(&data)).get();
}
#ifdef HAS_EXCEPTIONS
template <typename T, typename std::enable_if<
(detail::direct_type<std::reference_wrapper<T const>, Types...>::index != detail::invalid_value)>::type* = nullptr>
VARIANT_INLINE T const& get() const
{
if (type_index == detail::direct_type<std::reference_wrapper<T const>, Types...>::index)
{
return (*reinterpret_cast<std::reference_wrapper<T const> const*>(&data)).get();
}
else
{
throw bad_variant_access("in get<T>()");
}
}
#endif
// This function is deprecated because it returns an internal index field.
// Use which() instead.
MAPBOX_VARIANT_DEPRECATED VARIANT_INLINE std::size_t get_type_index() const
{
return type_index;
}
VARIANT_INLINE int which() const noexcept
{
return static_cast<int>(sizeof...(Types)-type_index - 1);
}
template <typename T, typename std::enable_if<
(detail::direct_type<T, Types...>::index != detail::invalid_value)>::type* = nullptr>
VARIANT_INLINE static constexpr int which() noexcept
{
return static_cast<int>(sizeof...(Types)-detail::direct_type<T, Types...>::index - 1);
}
// visitor
// unary
template <typename F, typename V, typename R = typename detail::result_of_unary_visit<F, first_type>::type>
auto VARIANT_INLINE static visit(V const& v, F&& f)
-> decltype(detail::dispatcher<F, V, R, Types...>::apply_const(v, std::forward<F>(f)))
{
return detail::dispatcher<F, V, R, Types...>::apply_const(v, std::forward<F>(f));
}
// non-const
template <typename F, typename V, typename R = typename detail::result_of_unary_visit<F, first_type>::type>
auto VARIANT_INLINE static visit(V& v, F&& f)
-> decltype(detail::dispatcher<F, V, R, Types...>::apply(v, std::forward<F>(f)))
{
return detail::dispatcher<F, V, R, Types...>::apply(v, std::forward<F>(f));
}
// binary
// const
template <typename F, typename V, typename R = typename detail::result_of_binary_visit<F, first_type>::type>
auto VARIANT_INLINE static binary_visit(V const& v0, V const& v1, F&& f)
-> decltype(detail::binary_dispatcher<F, V, R, Types...>::apply_const(v0, v1, std::forward<F>(f)))
{
return detail::binary_dispatcher<F, V, R, Types...>::apply_const(v0, v1, std::forward<F>(f));
}
// non-const
template <typename F, typename V, typename R = typename detail::result_of_binary_visit<F, first_type>::type>
auto VARIANT_INLINE static binary_visit(V& v0, V& v1, F&& f)
-> decltype(detail::binary_dispatcher<F, V, R, Types...>::apply(v0, v1, std::forward<F>(f)))
{
return detail::binary_dispatcher<F, V, R, Types...>::apply(v0, v1, std::forward<F>(f));
}
~variant() noexcept // no-throw destructor
{
helper_type::destroy(type_index, &data);
}
// comparison operators
// equality
VARIANT_INLINE bool operator==(variant const& rhs) const
{
assert(valid() && rhs.valid());
if (this->which() != rhs.which())
{
return false;
}
detail::comparer<variant, detail::equal_comp> visitor(*this);
return visit(rhs, visitor);
}
VARIANT_INLINE bool operator!=(variant const& rhs) const
{
return !(*this == rhs);
}
// less than
VARIANT_INLINE bool operator<(variant const& rhs) const
{
assert(valid() && rhs.valid());
if (this->which() != rhs.which())
{
return this->which() < rhs.which();
}
detail::comparer<variant, detail::less_comp> visitor(*this);
return visit(rhs, visitor);
}
VARIANT_INLINE bool operator>(variant const& rhs) const
{
return rhs < *this;
}
VARIANT_INLINE bool operator<=(variant const& rhs) const
{
return !(*this > rhs);
}
VARIANT_INLINE bool operator>=(variant const& rhs) const
{
return !(*this < rhs);
}
};
// unary visitor interface
// const
template <typename F, typename V>
auto VARIANT_INLINE apply_visitor(F&& f, V const& v) -> decltype(V::visit(v, std::forward<F>(f)))
{
return V::visit(v, std::forward<F>(f));
}
// non-const
template <typename F, typename V>
auto VARIANT_INLINE apply_visitor(F&& f, V& v) -> decltype(V::visit(v, std::forward<F>(f)))
{
return V::visit(v, std::forward<F>(f));
}
// binary visitor interface
// const
template <typename F, typename V>
auto VARIANT_INLINE apply_visitor(F&& f, V const& v0, V const& v1) -> decltype(V::binary_visit(v0, v1, std::forward<F>(f)))
{
return V::binary_visit(v0, v1, std::forward<F>(f));
}
// non-const
template <typename F, typename V>
auto VARIANT_INLINE apply_visitor(F&& f, V& v0, V& v1) -> decltype(V::binary_visit(v0, v1, std::forward<F>(f)))
{
return V::binary_visit(v0, v1, std::forward<F>(f));
}
// getter interface
#ifdef HAS_EXCEPTIONS
template <typename ResultType, typename T>
auto get(T& var)->decltype(var.template get<ResultType>())
{
return var.template get<ResultType>();
}
#endif
template <typename ResultType, typename T>
ResultType& get_unchecked(T& var)
{
return var.template get_unchecked<ResultType>();
}
#ifdef HAS_EXCEPTIONS
template <typename ResultType, typename T>
auto get(T const& var)->decltype(var.template get<ResultType>())
{
return var.template get<ResultType>();
}
#endif
template <typename ResultType, typename T>
ResultType const& get_unchecked(T const& var)
{
return var.template get_unchecked<ResultType>();
}
}
// } // namespace util
//} // namespace mapbox
#endif // MAPBOX_UTIL_VARIANT_HPP
Submodule Utilities/yaml-cpp added at 017626a6ce

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