Compare commits

..

No commits in common. "master" and "v0.0.37" have entirely different histories.

152 changed files with 2302 additions and 3238 deletions

View file

@ -1,10 +1,9 @@
#!/bin/sh -ex #!/bin/sh -ex
# Pull all the submodules except some # Pull all the submodules except llvm, opencv, libpng, sdl and curl
# Note: Tried to use git submodule status, but it takes over 20 seconds # Note: Tried to use git submodule status, but it takes over 20 seconds
# shellcheck disable=SC2046 # shellcheck disable=SC2046
git config --global --add safe.directory . git submodule -q update --init --depth 1 $(awk '/path/ && !/llvm/ && !/opencv/ && !/libpng/ && !/libsdl-org/ && !/curl/ { print $3 }' .gitmodules)
git submodule -q update --init --depth 1 $(awk '/path/ && !/llvm/ && !/opencv/ && !/libpng/ && !/libsdl-org/ && !/curl/ && !/zlib/ && !/libusb/ { print $3 }' .gitmodules)
CONFIGURE_ARGS=" CONFIGURE_ARGS="
-DWITH_LLVM=ON -DWITH_LLVM=ON
@ -14,7 +13,6 @@ CONFIGURE_ARGS="
-DUSE_SYSTEM_FFMPEG=ON -DUSE_SYSTEM_FFMPEG=ON
-DUSE_SYSTEM_CURL=ON -DUSE_SYSTEM_CURL=ON
-DUSE_SYSTEM_LIBPNG=ON -DUSE_SYSTEM_LIBPNG=ON
-DUSE_SYSTEM_LIBUSB=ON
-DUSE_SYSTEM_OPENCV=ON -DUSE_SYSTEM_OPENCV=ON
" "

View file

@ -1,14 +1,16 @@
#!/bin/sh -ex #!/bin/sh -ex
cd rpcs3 || exit 1 if [ -z "$CIRRUS_CI" ]; then
cd rpcs3 || exit 1
fi
shellcheck .ci/*.sh shellcheck .ci/*.sh
git config --global --add safe.directory '*' git config --global --add safe.directory '*'
# Pull all the submodules except some # Pull all the submodules except llvm, opencv, sdl and curl
# shellcheck disable=SC2046 # shellcheck disable=SC2046
git submodule -q update --init $(awk '/path/ && !/llvm/ && !/opencv/ && !/libsdl-org/ && !/curl/ && !/zlib/ { print $3 }' .gitmodules) git submodule -q update --init $(awk '/path/ && !/llvm/ && !/opencv/ && !/libsdl-org/ && !/curl/ { print $3 }' .gitmodules)
mkdir build && cd build || exit 1 mkdir build && cd build || exit 1
@ -50,6 +52,10 @@ ninja; build_status=$?;
cd .. cd ..
# If it compiled succesfully let's deploy. # If it compiled succesfully let's deploy.
if [ "$build_status" -eq 0 ]; then # Azure and Cirrus publish PRs as artifacts only.
{ [ "$CI_HAS_ARTIFACTS" = "true" ];
} && SHOULD_DEPLOY="true" || SHOULD_DEPLOY="false"
if [ "$build_status" -eq 0 ] && [ "$SHOULD_DEPLOY" = "true" ]; then
.ci/deploy-linux.sh "aarch64" .ci/deploy-linux.sh "aarch64"
fi fi

View file

@ -1,15 +1,17 @@
#!/bin/sh -ex #!/bin/sh -ex
cd rpcs3 || exit 1 if [ -z "$CIRRUS_CI" ]; then
cd rpcs3 || exit 1
fi
shellcheck .ci/*.sh shellcheck .ci/*.sh
git config --global --add safe.directory '*' git config --global --add safe.directory '*'
# Pull all the submodules except some # Pull all the submodules except llvm, opencv, sdl and curl
# Note: Tried to use git submodule status, but it takes over 20 seconds # Note: Tried to use git submodule status, but it takes over 20 seconds
# shellcheck disable=SC2046 # shellcheck disable=SC2046
git submodule -q update --init $(awk '/path/ && !/llvm/ && !/opencv/ && !/libsdl-org/ && !/curl/ && !/zlib/ { print $3 }' .gitmodules) git submodule -q update --init $(awk '/path/ && !/llvm/ && !/opencv/ && !/libsdl-org/ && !/curl/ { print $3 }' .gitmodules)
mkdir build && cd build || exit 1 mkdir build && cd build || exit 1
@ -61,6 +63,10 @@ ninja; build_status=$?;
cd .. cd ..
# If it compiled succesfully let's deploy. # If it compiled succesfully let's deploy.
if [ "$build_status" -eq 0 ]; then # Azure and Cirrus publish PRs as artifacts only.
{ [ "$CI_HAS_ARTIFACTS" = "true" ];
} && SHOULD_DEPLOY="true" || SHOULD_DEPLOY="false"
if [ "$build_status" -eq 0 ] && [ "$SHOULD_DEPLOY" = "true" ]; then
.ci/deploy-linux.sh "x86_64" .ci/deploy-linux.sh "x86_64"
fi fi

View file

@ -6,7 +6,7 @@ export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1
export HOMEBREW_NO_ENV_HINTS=1 export HOMEBREW_NO_ENV_HINTS=1
export HOMEBREW_NO_INSTALL_CLEANUP=1 export HOMEBREW_NO_INSTALL_CLEANUP=1
/opt/homebrew/bin/brew install -f --overwrite --quiet nasm ninja p7zip ccache pipenv gnutls freetype googletest #create-dmg /opt/homebrew/bin/brew install -f --overwrite --quiet nasm ninja p7zip ccache pipenv gnutls freetype #create-dmg
/opt/homebrew/bin/brew install -f --quiet ffmpeg@5 /opt/homebrew/bin/brew install -f --quiet ffmpeg@5
/opt/homebrew/bin/brew install --quiet "llvm@$LLVM_COMPILER_VER" glew cmake sdl3 vulkan-headers coreutils /opt/homebrew/bin/brew install --quiet "llvm@$LLVM_COMPILER_VER" glew cmake sdl3 vulkan-headers coreutils
/opt/homebrew/bin/brew link -f --quiet "llvm@$LLVM_COMPILER_VER" ffmpeg@5 /opt/homebrew/bin/brew link -f --quiet "llvm@$LLVM_COMPILER_VER" ffmpeg@5
@ -32,7 +32,7 @@ if [ ! -d "/tmp/Qt/$QT_VER" ]; then
git clone https://github.com/engnr/qt-downloader.git git clone https://github.com/engnr/qt-downloader.git
cd qt-downloader cd qt-downloader
git checkout f52efee0f18668c6d6de2dec0234b8c4bc54c597 git checkout f52efee0f18668c6d6de2dec0234b8c4bc54c597
# nested Qt 6.9.1 URL workaround # nested Qt 6.9.0 URL workaround
# sed -i '' "s/'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/'qt{0}_{0}{1}{2}'.format(major, minor, patch), 'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/g" qt-downloader # sed -i '' "s/'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/'qt{0}_{0}{1}{2}'.format(major, minor, patch), 'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/g" qt-downloader
# sed -i '' "s/'{}\/{}\/qt{}_{}\/'/'{0}\/{1}\/qt{2}_{3}\/qt{2}_{3}\/'/g" qt-downloader # sed -i '' "s/'{}\/{}\/qt{}_{}\/'/'{0}\/{1}\/qt{2}_{3}\/qt{2}_{3}\/'/g" qt-downloader
# archived Qt 6.7.3 URL workaround # archived Qt 6.7.3 URL workaround
@ -41,7 +41,7 @@ if [ ! -d "/tmp/Qt/$QT_VER" ]; then
arch -arm64 "$BREW_PATH/bin/pipenv" run pip3 uninstall py7zr requests semantic_version lxml arch -arm64 "$BREW_PATH/bin/pipenv" run pip3 uninstall py7zr requests semantic_version lxml
arch -arm64 "$BREW_PATH/bin/pipenv" run pip3 install py7zr requests semantic_version lxml --no-cache arch -arm64 "$BREW_PATH/bin/pipenv" run pip3 install py7zr requests semantic_version lxml --no-cache
mkdir -p "$QT_VER/macos" ; ln -s "macos" "$QT_VER/clang_64" mkdir -p "$QT_VER/macos" ; ln -s "macos" "$QT_VER/clang_64"
# sed -i '' 's/args\.version \/ derive_toolchain_dir(args) \/ //g' "$WORKDIR/qt-downloader/qt-downloader" # Qt 6.9.1 workaround # sed -i '' 's/args\.version \/ derive_toolchain_dir(args) \/ //g' "$WORKDIR/qt-downloader/qt-downloader" # Qt 6.9.0 workaround
arch -arm64 "$BREW_PATH/bin/pipenv" run "$WORKDIR/qt-downloader/qt-downloader" macos desktop "$QT_VER" clang_64 --opensource --addons qtmultimedia qtimageformats # -o "$QT_VER/clang_64" arch -arm64 "$BREW_PATH/bin/pipenv" run "$WORKDIR/qt-downloader/qt-downloader" macos desktop "$QT_VER" clang_64 --opensource --addons qtmultimedia qtimageformats # -o "$QT_VER/clang_64"
fi fi
@ -77,8 +77,8 @@ mkdir build && cd build || exit 1
export MACOSX_DEPLOYMENT_TARGET=14.0 export MACOSX_DEPLOYMENT_TARGET=14.0
"$BREW_PATH/bin/cmake" .. \ "$BREW_PATH/bin/cmake" .. \
-DBUILD_RPCS3_TESTS="${RUN_UNIT_TESTS}" \ -DBUILD_RPCS3_TESTS=OFF \
-DRUN_RPCS3_TESTS="${RUN_UNIT_TESTS}" \ -DRUN_RPCS3_TESTS=OFF \
-DUSE_SDL=ON \ -DUSE_SDL=ON \
-DUSE_DISCORD_RPC=ON \ -DUSE_DISCORD_RPC=ON \
-DUSE_VULKAN=ON \ -DUSE_VULKAN=ON \
@ -115,7 +115,9 @@ export MACOSX_DEPLOYMENT_TARGET=14.0
cd .. cd ..
# If it compiled succesfully let's deploy. { [ "$CI_HAS_ARTIFACTS" = "true" ];
if [ "$build_status" -eq 0 ]; then } && SHOULD_DEPLOY="true" || SHOULD_DEPLOY="false"
if [ "$build_status" -eq 0 ] && [ "$SHOULD_DEPLOY" = "true" ]; then
.ci/deploy-mac-arm64.sh .ci/deploy-mac-arm64.sh
fi fi

View file

@ -36,7 +36,7 @@ if [ ! -d "/tmp/Qt/$QT_VER" ]; then
git clone https://github.com/engnr/qt-downloader.git git clone https://github.com/engnr/qt-downloader.git
cd qt-downloader cd qt-downloader
git checkout f52efee0f18668c6d6de2dec0234b8c4bc54c597 git checkout f52efee0f18668c6d6de2dec0234b8c4bc54c597
# nested Qt 6.9.1 URL workaround # nested Qt 6.9.0 URL workaround
# sed -i '' "s/'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/'qt{0}_{0}{1}{2}'.format(major, minor, patch), 'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/g" qt-downloader # sed -i '' "s/'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/'qt{0}_{0}{1}{2}'.format(major, minor, patch), 'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/g" qt-downloader
# sed -i '' "s/'{}\/{}\/qt{}_{}\/'/'{0}\/{1}\/qt{2}_{3}\/qt{2}_{3}\/'/g" qt-downloader # sed -i '' "s/'{}\/{}\/qt{}_{}\/'/'{0}\/{1}\/qt{2}_{3}\/qt{2}_{3}\/'/g" qt-downloader
# archived Qt 6.7.3 URL workaround # archived Qt 6.7.3 URL workaround
@ -44,7 +44,7 @@ if [ ! -d "/tmp/Qt/$QT_VER" ]; then
cd "/tmp/Qt" cd "/tmp/Qt"
arch -x86_64 "$BREW_X64_PATH/bin/pipenv" --python "$BREW_X64_PATH/bin/python3" run pip3 install py7zr requests semantic_version lxml arch -x86_64 "$BREW_X64_PATH/bin/pipenv" --python "$BREW_X64_PATH/bin/python3" run pip3 install py7zr requests semantic_version lxml
mkdir -p "$QT_VER/macos" ; ln -s "macos" "$QT_VER/clang_64" mkdir -p "$QT_VER/macos" ; ln -s "macos" "$QT_VER/clang_64"
# sed -i '' 's/args\.version \/ derive_toolchain_dir(args) \/ //g' "$WORKDIR/qt-downloader/qt-downloader" # Qt 6.9.1 workaround # sed -i '' 's/args\.version \/ derive_toolchain_dir(args) \/ //g' "$WORKDIR/qt-downloader/qt-downloader" # Qt 6.9.0 workaround
arch -x86_64 "$BREW_X64_PATH/bin/pipenv" --python "$BREW_X64_PATH/bin/python3" run "$WORKDIR/qt-downloader/qt-downloader" macos desktop "$QT_VER" clang_64 --opensource --addons qtmultimedia qtimageformats # -o "$QT_VER/clang_64" arch -x86_64 "$BREW_X64_PATH/bin/pipenv" --python "$BREW_X64_PATH/bin/python3" run "$WORKDIR/qt-downloader/qt-downloader" macos desktop "$QT_VER" clang_64 --opensource --addons qtmultimedia qtimageformats # -o "$QT_VER/clang_64"
fi fi
@ -118,7 +118,9 @@ export MACOSX_DEPLOYMENT_TARGET=14.0
cd .. cd ..
# If it compiled succesfully let's deploy. { [ "$CI_HAS_ARTIFACTS" = "true" ];
if [ "$build_status" -eq 0 ]; then } && SHOULD_DEPLOY="true" || SHOULD_DEPLOY="false"
if [ "$build_status" -eq 0 ] && [ "$SHOULD_DEPLOY" = "true" ]; then
.ci/deploy-mac.sh .ci/deploy-mac.sh
fi fi

View file

@ -1,61 +0,0 @@
#!/bin/sh -ex
git config --global --add safe.directory '*'
# Pull all the submodules except some
# Note: Tried to use git submodule status, but it takes over 20 seconds
# shellcheck disable=SC2046
git submodule -q update --init $(awk '/path/ && !/llvm/ && !/opencv/ && !/ffmpeg/ && !/curl/ && !/FAudio/ && !/zlib/ { print $3 }' .gitmodules)
mkdir build && cd build || exit 1
export CC="clang"
export CXX="clang++"
export LINKER=lld
export LINKER_FLAG="-fuse-ld=${LINKER}"
if [ -n "$LLVMVER" ]; then
export AR="llvm-ar-$LLVMVER"
export RANLIB="llvm-ranlib-$LLVMVER"
else
export AR="llvm-ar"
export RANLIB="llvm-ranlib"
fi
cmake .. \
-DCMAKE_PREFIX_PATH=/clang64 \
-DCMAKE_INSTALL_PREFIX=/usr \
-DUSE_NATIVE_INSTRUCTIONS=OFF \
-DUSE_PRECOMPILED_HEADERS=OFF \
-DCMAKE_C_FLAGS="$CFLAGS" \
-DCMAKE_CXX_FLAGS="$CFLAGS" \
-DCMAKE_EXE_LINKER_FLAGS="${LINKER_FLAG}" \
-DCMAKE_MODULE_LINKER_FLAGS="${LINKER_FLAG}" \
-DCMAKE_SHARED_LINKER_FLAGS="${LINKER_FLAG}" \
-DCMAKE_AR="$AR" \
-DCMAKE_RANLIB="$RANLIB" \
-DUSE_SYSTEM_CURL=ON \
-DUSE_FAUDIO=OFF \
-DUSE_SDL=ON \
-DUSE_SYSTEM_SDL=OFF \
-DUSE_SYSTEM_FFMPEG=ON \
-DUSE_SYSTEM_OPENCV=ON \
-DUSE_SYSTEM_OPENAL=OFF \
-DUSE_DISCORD_RPC=ON \
-DOpenGL_GL_PREFERENCE=LEGACY \
-DWITH_LLVM=ON \
-DLLVM_DIR=/clang64/lib/cmake/llvm \
-DVulkan_LIBRARY=/clang64/lib/libvulkan-1.dll.a \
-DSTATIC_LINK_LLVM=ON \
-DBUILD_RPCS3_TESTS=OFF \
-DRUN_RPCS3_TESTS=OFF \
-G Ninja
ninja; build_status=$?;
cd ..
# If it compiled succesfully let's deploy.
if [ "$build_status" -eq 0 ]; then
.ci/deploy-windows-clang.sh "x86_64"
fi

View file

@ -26,6 +26,9 @@ if [ "$DEPLOY_APPIMAGE" = "true" ]; then
rm -f ./AppDir/usr/lib/libvulkan.so* rm -f ./AppDir/usr/lib/libvulkan.so*
# Remove unused Qt6 libraries # Remove unused Qt6 libraries
rm -f ./AppDir/usr/lib/libQt6OpenGL.so*
rm -f ./AppDir/usr/lib/libQt6Qml*.so*
rm -f ./AppDir/usr/lib/libQt6Quick.so*
rm -f ./AppDir/usr/lib/libQt6VirtualKeyboard.so* rm -f ./AppDir/usr/lib/libQt6VirtualKeyboard.so*
rm -f ./AppDir/usr/plugins/platforminputcontexts/libqtvirtualkeyboardplugin.so* rm -f ./AppDir/usr/plugins/platforminputcontexts/libqtvirtualkeyboardplugin.so*

View file

@ -1,20 +0,0 @@
#!/bin/sh -ex
# First let's print some info about our caches
"$(cygpath -u "$CCACHE_BIN_DIR")"/ccache.exe --show-stats -v
# BUILD_blablabla is Azure specific, so we wrap it for portability
ARTIFACT_DIR="$BUILD_ARTIFACTSTAGINGDIRECTORY"
BUILD="llvmlibs_mt.7z"
# Package artifacts
7z a -m0=LZMA2 -mx9 "$BUILD" ./build/lib/Release-x64/llvm_build
# Generate sha256 hashes
# Write to file for GitHub releases
sha256sum "$BUILD" | awk '{ print $1 }' | tee "$BUILD.sha256"
echo "$(cat "$BUILD.sha256");$(stat -c %s "$BUILD")B" > GitHubReleaseMessage.txt
# Move files to publishing directory
cp -- "$BUILD" "$ARTIFACT_DIR"
cp -- "$BUILD.sha256" "$ARTIFACT_DIR"

View file

@ -1,38 +0,0 @@
#!/bin/sh -ex
# source ci-vars.env
# shellcheck disable=SC1091
. .ci/ci-vars.env
cd build || exit 1
CPU_ARCH="${1:-x86_64}"
echo "Deploying rpcs3 windows clang $CPU_ARCH"
# BUILD_blablabla is CI specific, so we wrap it for portability
ARTIFACT_DIR=$(cygpath -u "$BUILD_ARTIFACTSTAGINGDIRECTORY")
MSYS2_CLANG_BIN=$(cygpath -w /clang64/bin)
MSYS2_USR_BIN=$(cygpath -w /usr/bin)
echo "Installing dependencies of: ./bin/rpcs3.exe (MSYS2 dir is '$MSYS2_CLANG_BIN', usr dir is '$MSYS2_USR_BIN')"
cmake -DMSYS2_CLANG_BIN="$MSYS2_CLANG_BIN" -DMSYS2_USR_BIN="$MSYS2_USR_BIN" -Dexe=./bin/rpcs3.exe -P ../buildfiles/cmake/CopyRuntimeDependencies.cmake
# Prepare compatibility and SDL database for packaging
mkdir ./bin/config
mkdir ./bin/config/input_configs
curl -fsSL 'https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt' 1> ./bin/config/input_configs/gamecontrollerdb.txt
curl -fsSL 'https://rpcs3.net/compatibility?api=v1&export' | iconv -t UTF-8 1> ./bin/GuiConfigs/compat_database.dat
# Package artifacts
7z a -m0=LZMA2 -mx9 "$BUILD" ./bin/*
# Generate sha256 hashes
# Write to file for GitHub releases
sha256sum "$BUILD" | awk '{ print $1 }' | tee "$BUILD.sha256"
echo "$(cat "$BUILD.sha256");$(stat -c %s "$BUILD")B" > GitHubReleaseMessage.txt
# Move files to publishing directory
mkdir -p "$ARTIFACT_DIR"
cp -- "$BUILD" "$ARTIFACT_DIR"
cp -- "$BUILD.sha256" "$ARTIFACT_DIR"

View file

@ -1,9 +1,9 @@
#!/bin/sh -ex #!/bin/sh -ex
# First let's print some info about our caches # First let's see print some info about our caches
"$(cygpath -u "$CCACHE_BIN_DIR")"/ccache.exe --show-stats -v "$(cygpath -u "$CCACHE_BIN_DIR")"/ccache.exe --show-stats -v
# BUILD_blablabla is CI specific, so we wrap it for portability # BUILD_blablabla is Azure specific, so we wrap it for portability
ARTIFACT_DIR="$BUILD_ARTIFACTSTAGINGDIRECTORY" ARTIFACT_DIR="$BUILD_ARTIFACTSTAGINGDIRECTORY"
# Remove unecessary files # Remove unecessary files

View file

@ -1,4 +1,5 @@
# Variables set by CI # Variables set by Azure Pipelines
CI_HAS_ARTIFACTS
BUILD_REASON BUILD_REASON
BUILD_SOURCEVERSION BUILD_SOURCEVERSION
BUILD_ARTIFACTSTAGINGDIRECTORY BUILD_ARTIFACTSTAGINGDIRECTORY

13
.ci/export-azure-vars.sh Executable file
View file

@ -0,0 +1,13 @@
#!/bin/sh -e
# Export variables for later stages of the Azure pipeline
# Values done in this manner will appear as environment variables
# in later stages.
# From pure-sh-bible
# Setting 'IFS' tells 'read' where to split the string.
while IFS='=' read -r key val; do
# Skip over lines containing comments.
[ "${key##\#*}" ] || continue
echo "##vso[task.setvariable variable=$key]$val"
done < ".ci/ci-vars.env"

13
.ci/export-cirrus-vars.sh Executable file
View file

@ -0,0 +1,13 @@
#!/bin/sh -e
# Export variables for later stages of the Cirrus pipeline
# Values done in this manner will appear as environment variables
# in later stages.
# From pure-sh-bible
# Setting 'IFS' tells 'read' where to split the string.
while IFS='=' read -r key val; do
# Skip over lines containing comments.
[ "${key##\#*}" ] || continue
export "$key"="$val"
done < ".ci/ci-vars.env"

View file

@ -15,4 +15,4 @@ pkg install "llvm$LLVM_COMPILER_VER"
pkg install git ccache cmake ninja "qt$QT_VER_MAIN-multimedia" "qt$QT_VER_MAIN-svg" glew openal-soft ffmpeg pkg install git ccache cmake ninja "qt$QT_VER_MAIN-multimedia" "qt$QT_VER_MAIN-svg" glew openal-soft ffmpeg
# Optional dependencies (libevdev is pulled by qtX-base) # Optional dependencies (libevdev is pulled by qtX-base)
pkg install pkgconf alsa-lib pulseaudio sdl3 evdev-proto vulkan-headers vulkan-loader opencv pkg install pkgconf alsa-lib pulseaudio sdl3 evdev-proto vulkan-headers vulkan-loader

View file

@ -1,63 +0,0 @@
#!/bin/sh -ex
# Resource/dependency URLs
CCACHE_URL="https://github.com/ccache/ccache/releases/download/v4.11.2/ccache-4.11.2-windows-x86_64.zip"
DEP_URLS=" \
$CCACHE_URL"
# CI doesn't make a cache dir if it doesn't exist, so we do it manually
[ -d "$DEPS_CACHE_DIR" ] || mkdir "$DEPS_CACHE_DIR"
# Pull the llvm submodule
# shellcheck disable=SC2046
git submodule -q update --init --depth=1 -- 3rdparty/llvm
# Git bash doesn't have rev, so here it is
rev()
{
echo "$1" | awk '{ for(i = length($0); i != 0; --i) { a = a substr($0, i, 1); } } END { print a }'
}
# Usage: download_and_verify url checksum algo file
# Check to see if a file is already cached, and the checksum matches. If not, download it.
# Tries up to 3 times
download_and_verify()
{
url="$1"
correctChecksum="$2"
algo="$3"
fileName="$4"
for _ in 1 2 3; do
[ -e "$DEPS_CACHE_DIR/$fileName" ] || curl -fLo "$DEPS_CACHE_DIR/$fileName" "$url"
fileChecksum=$("${algo}sum" "$DEPS_CACHE_DIR/$fileName" | awk '{ print $1 }')
[ "$fileChecksum" = "$correctChecksum" ] && return 0
done
return 1;
}
# Some dependencies install here
[ -d "./build/lib_ext/Release-x64" ] || mkdir -p "./build/lib_ext/Release-x64"
for url in $DEP_URLS; do
# Get the filename from the URL and remove query strings (?arg=something).
fileName="$(rev "$(rev "$url" | cut -d'/' -f1)" | cut -d'?' -f1)"
[ -z "$fileName" ] && echo "Unable to parse url: $url" && exit 1
# shellcheck disable=SC1003
case "$url" in
*ccache*) checksum=$CCACHE_SHA; algo="sha256"; outDir="$CCACHE_BIN_DIR" ;;
*) echo "Unknown url resource: $url"; exit 1 ;;
esac
download_and_verify "$url" "$checksum" "$algo" "$fileName"
7z x -y "$DEPS_CACHE_DIR/$fileName" -aos -o"$outDir"
done
# Setup ccache tool
[ -d "$CCACHE_DIR" ] || mkdir -p "$(cygpath -u "$CCACHE_DIR")"
CCACHE_SH_DIR=$(cygpath -u "$CCACHE_BIN_DIR")
mv "$CCACHE_SH_DIR"/ccache-*/* "$CCACHE_SH_DIR"
cp "$CCACHE_SH_DIR"/ccache.exe "$CCACHE_SH_DIR"/cl.exe

View file

@ -1,39 +0,0 @@
#!/bin/sh -ex
CPU_ARCH="${1:-win64}"
COMPILER="${2:-msvc}"
# These are CI specific, so we wrap them for portability
REPO_NAME="$BUILD_REPOSITORY_NAME"
REPO_BRANCH="$BUILD_SOURCEBRANCHNAME"
PR_NUMBER="$BUILD_PR_NUMBER"
# Gather explicit version number and number of commits
COMM_TAG=$(awk '/version{.*}/ { printf("%d.%d.%d", $5, $6, $7) }' ./rpcs3/rpcs3_version.cpp)
COMM_COUNT=$(git rev-list --count HEAD)
COMM_HASH=$(git rev-parse --short=8 HEAD)
# Format the above into filenames
if [ -n "$PR_NUMBER" ]; then
AVVER="${COMM_TAG}-${COMM_HASH}"
BUILD_RAW="rpcs3-v${AVVER}_${CPU_ARCH}_${COMPILER}"
BUILD="${BUILD_RAW}.7z"
else
AVVER="${COMM_TAG}-${COMM_COUNT}"
BUILD_RAW="rpcs3-v${AVVER}-${COMM_HASH}_${CPU_ARCH}_${COMPILER}"
BUILD="${BUILD_RAW}.7z"
fi
# BRANCH is used for experimental build warnings for pr builds, used in main_window.cpp.
# BUILD is the name of the release artifact
# BUILD_RAW is just filename
# AVVER is used for GitHub releases, it is the version number.
BRANCH="${REPO_NAME}/${REPO_BRANCH}"
# SC2129
{
echo "BRANCH=$BRANCH"
echo "BUILD=$BUILD"
echo "BUILD_RAW=$BUILD_RAW"
echo "AVVER=$AVVER"
} >> .ci/ci-vars.env

View file

@ -1,5 +1,10 @@
#!/bin/sh -ex #!/bin/sh -ex
# These are Azure specific, so we wrap them for portability
REPO_NAME="$BUILD_REPOSITORY_NAME"
REPO_BRANCH="$SYSTEM_PULLREQUEST_SOURCEBRANCH"
PR_NUMBER="$SYSTEM_PULLREQUEST_PULLREQUESTID"
# Resource/dependency URLs # Resource/dependency URLs
# Qt mirrors can be volatile and slow, so we list 2 # Qt mirrors can be volatile and slow, so we list 2
#QT_HOST="http://mirrors.ocf.berkeley.edu/qt/" #QT_HOST="http://mirrors.ocf.berkeley.edu/qt/"
@ -28,7 +33,7 @@ DEP_URLS=" \
$VULKAN_SDK_URL\ $VULKAN_SDK_URL\
$CCACHE_URL" $CCACHE_URL"
# CI doesn't make a cache dir if it doesn't exist, so we do it manually # Azure pipelines doesn't make a cache dir if it doesn't exist, so we do it manually
[ -d "$DEPS_CACHE_DIR" ] || mkdir "$DEPS_CACHE_DIR" [ -d "$DEPS_CACHE_DIR" ] || mkdir "$DEPS_CACHE_DIR"
# Pull all the submodules except llvm, since it is built separately and we just download that build # Pull all the submodules except llvm, since it is built separately and we just download that build
@ -94,3 +99,33 @@ done
CCACHE_SH_DIR=$(cygpath -u "$CCACHE_BIN_DIR") CCACHE_SH_DIR=$(cygpath -u "$CCACHE_BIN_DIR")
mv "$CCACHE_SH_DIR"/ccache-*/* "$CCACHE_SH_DIR" mv "$CCACHE_SH_DIR"/ccache-*/* "$CCACHE_SH_DIR"
cp "$CCACHE_SH_DIR"/ccache.exe "$CCACHE_SH_DIR"/cl.exe cp "$CCACHE_SH_DIR"/ccache.exe "$CCACHE_SH_DIR"/cl.exe
# Gather explicit version number and number of commits
COMM_TAG=$(awk '/version{.*}/ { printf("%d.%d.%d", $5, $6, $7) }' ./rpcs3/rpcs3_version.cpp)
COMM_COUNT=$(git rev-list --count HEAD)
COMM_HASH=$(git rev-parse --short=8 HEAD)
# Format the above into filenames
if [ -n "$PR_NUMBER" ]; then
AVVER="${COMM_TAG}-${COMM_HASH}"
BUILD_RAW="rpcs3-v${AVVER}_win64"
BUILD="${BUILD_RAW}.7z"
else
AVVER="${COMM_TAG}-${COMM_COUNT}"
BUILD_RAW="rpcs3-v${AVVER}-${COMM_HASH}_win64"
BUILD="${BUILD_RAW}.7z"
fi
# BRANCH is used for experimental build warnings for pr builds, used in main_window.cpp.
# BUILD is the name of the release artifact
# BUILD_RAW is just filename
# AVVER is used for GitHub releases, it is the version number.
BRANCH="${REPO_NAME}/${REPO_BRANCH}"
# SC2129
{
echo "BRANCH=$BRANCH"
echo "BUILD=$BUILD"
echo "BUILD_RAW=$BUILD_RAW"
echo "AVVER=$AVVER"
} >> .ci/ci-vars.env

153
.cirrus.yml Normal file
View file

@ -0,0 +1,153 @@
env:
CIRRUS_CLONE_DEPTH: 0 # Unshallow clone to obtain proper GIT_VERSION
BUILD_REPOSITORY_NAME: $CIRRUS_REPO_FULL_NAME
SYSTEM_PULLREQUEST_SOURCEBRANCH: $CIRRUS_BRANCH
SYSTEM_PULLREQUEST_PULLREQUESTID: $CIRRUS_PR
BUILD_SOURCEVERSION: $CIRRUS_CHANGE_IN_REPO
BUILD_SOURCEBRANCHNAME: $CIRRUS_BRANCH
RPCS3_TOKEN: ENCRYPTED[100ebb8e3552bf2021d0ef55dccda3e58d27be5b6cab0b0b92843ef490195d3c4edaefa087e4a3b425caa6392300b9b1]
QT_VER_MAIN: '6'
QT_VER: '6.9.0'
LLVM_COMPILER_VER: '19'
LLVM_VER: '19.1.7'
# windows_task:
# matrix:
# - name: Cirrus Windows
# windows_container:
# image: cirrusci/windowsservercore:visualstudio2019
# cpu: 8
# memory: 16G
# env:
# CIRRUS_SHELL: "bash"
# COMPILER: msvc
# BUILD_ARTIFACTSTAGINGDIRECTORY: ${CIRRUS_WORKING_DIR}\artifacts\
# QT_VER_MSVC: 'msvc2022'
# QT_DATE: '202503301022'
# QTDIR: C:\Qt\${QT_VER}\${QT_VER_MSVC}_64
# VULKAN_VER: '1.3.268.0'
# VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5'
# VULKAN_SDK: C:\VulkanSDK\${VULKAN_VER}
# CACHE_DIR: "./cache"
# UPLOAD_COMMIT_HASH: 7d09e3be30805911226241afbb14f8cdc2eb054e
# UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-win"
# deps_cache:
# folder: "./cache"
# #obj_cache:
# # folder: "./tmp"
# #obj2_cache:
# # folder: "./rpcs3/x64"
# setup_script:
# - './.ci/get_keys-windows.sh'
# - './.ci/setup-windows.sh'
# rpcs3_script:
# - export PATH=${PATH}:"C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\MSBuild\Current\Bin"
# - msbuild.exe rpcs3.sln //p:Configuration=Release //m
# deploy_script:
# - mkdir artifacts
# - source './.ci/export-cirrus-vars.sh'
# - './.ci/deploy-windows.sh'
# artifacts:
# name: Artifact
# path: "*.7z*"
# push_script: |
# if [ "$CIRRUS_REPO_OWNER" = "RPCS3" ] && [ -z "$CIRRUS_PR" ] && [ "$CIRRUS_BRANCH" = "master" ]; then
# source './.ci/export-cirrus-vars.sh'
# './.ci/github-upload.sh'
# fi;
# linux_task:
# container:
# image: rpcs3/rpcs3-ci-jammy:1.6
# cpu: 4
# memory: 16G
# env:
# BUILD_ARTIFACTSTAGINGDIRECTORY: ${CIRRUS_WORKING_DIR}/artifacts
# ARTDIR: ${CIRRUS_WORKING_DIR}/artifacts/
# CCACHE_DIR: "/tmp/ccache_dir"
# CCACHE_MAXSIZE: 300M
# CI_HAS_ARTIFACTS: true
# UPLOAD_COMMIT_HASH: d812f1254a1157c80fd402f94446310560f54e5f
# UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-linux"
# DEPLOY_APPIMAGE: true
# APPDIR: "./appdir"
# RELEASE_MESSAGE: "../GitHubReleaseMessage.txt"
# ccache_cache:
# folder: "/tmp/ccache_dir"
# matrix:
# - name: Cirrus Linux GCC
# env:
# COMPILER: gcc
# gcc_script:
# - mkdir artifacts
# - ".ci/build-linux.sh"
# - name: Cirrus Linux Clang
# env:
# COMPILER: clang
# clang_script:
# - mkdir artifacts
# - ".ci/build-linux.sh"
# artifacts:
# name: Artifact
# path: "artifacts/*"
# push_script: |
# if [ "$CIRRUS_REPO_OWNER" = "RPCS3" ] && [ -z "$CIRRUS_PR" ] && [ "$CIRRUS_BRANCH" = "master" ] && [ "$COMPILER" = "gcc" ]; then
# COMM_TAG=$(awk '/version{.*}/ { printf("%d.%d.%d", $5, $6, $7) }' ./rpcs3/rpcs3_version.cpp)
# COMM_COUNT=$(git rev-list --count HEAD)
# COMM_HASH=$(git rev-parse --short=8 HEAD)
# export AVVER="${COMM_TAG}-${COMM_COUNT}"
# .ci/github-upload.sh
# fi;
freebsd_task:
matrix:
- name: Cirrus FreeBSD
freebsd_instance:
image_family: freebsd-13-5
cpu: 8
memory: 8G
env:
CCACHE_MAXSIZE: 300M # 3x clean build, rounded
CCACHE_DIR: /tmp/ccache_dir
ccache_cache:
folder: /tmp/ccache_dir
install_script: "sh -ex ./.ci/install-freebsd.sh"
script: "./.ci/build-freebsd.sh"
# linux_aarch64_task:
# env:
# BUILD_ARTIFACTSTAGINGDIRECTORY: ${CIRRUS_WORKING_DIR}/artifacts
# ARTDIR: ${CIRRUS_WORKING_DIR}/artifacts/
# CCACHE_DIR: "/tmp/ccache_dir"
# CCACHE_MAXSIZE: 300M
# CI_HAS_ARTIFACTS: true
# UPLOAD_COMMIT_HASH: a1d35836e8d45bfc6f63c26f0a3e5d46ef622fe1
# UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-linux-arm64"
# DEPLOY_APPIMAGE: true
# APPDIR: "./appdir"
# RELEASE_MESSAGE: "../GitHubReleaseMessage.txt"
# COMPILER: clang
# ccache_cache:
# folder: "/tmp/ccache_dir"
# matrix:
# - name: Cirrus Linux AArch64 Clang
# arm_container:
# image: 'docker.io/rpcs3/rpcs3-ci-jammy-aarch64:1.6'
# cpu: 8
# memory: 8G
# clang_script:
# - mkdir artifacts
# - "sh -ex ./.ci/build-linux-aarch64.sh"
# artifacts:
# name: Artifact
# path: "artifacts/*"
# push_script: |
# if [ "$CIRRUS_REPO_OWNER" = "RPCS3" ] && [ -z "$CIRRUS_PR" ] && [ "$CIRRUS_BRANCH" = "master" ]; then
# COMM_TAG=$(awk '/version{.*}/ { printf("%d.%d.%d", $5, $6, $7) }' ./rpcs3/rpcs3_version.cpp)
# COMM_COUNT=$(git rev-list --count HEAD)
# COMM_HASH=$(git rev-parse --short=8 HEAD)
# export AVVER="${COMM_TAG}-${COMM_COUNT}"
# .ci/github-upload.sh
# fi;

View file

@ -1,72 +0,0 @@
name: Build LLVM
defaults:
run:
shell: bash
on:
workflow_dispatch:
concurrency:
group: ${{ github.ref }}-${{ github.event_name }}
cancel-in-progress: true
env:
BUILD_ARTIFACTSTAGINGDIRECTORY: ${{ github.workspace }}/artifacts/
jobs:
Windows_Build:
if: github.event_name == 'workflow_dispatch'
name: LLVM Windows (MSVC)
runs-on: windows-2025
env:
COMPILER: msvc
CCACHE_SHA: '1f39f3ad5aae3fe915e99ad1302633bc8f6718e58fa7c0de2b0ba7e080f0f08c'
CCACHE_BIN_DIR: 'C:\ccache_bin'
CCACHE_DIR: 'C:\ccache'
CCACHE_INODECACHE: 'true'
CCACHE_SLOPPINESS: 'time_macros'
DEPS_CACHE_DIR: ./dependency_cache
steps:
- name: Checkout repository
uses: actions/checkout@main
with:
fetch-depth: 0
- name: Restore Dependencies Cache
uses: actions/cache/restore@main
id: restore-dependencies-cache
with:
path: ${{ env.DEPS_CACHE_DIR }}
key: "${{ runner.os }}-${{ env.COMPILER }}-llvm-${{ env.CCACHE_SHA }}"
restore-keys: ${{ runner.os }}-${{ env.COMPILER }}-llvm
- name: Download and unpack dependencies
run: .ci/setup-llvm.sh
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@main
- name: Compile LLVM
shell: pwsh
run: msbuild 3rdparty\llvm\llvm_build.vcxproj /p:SolutionDir="$(pwd)/" /p:Configuration=Release /v:minimal /p:Platform=x64 /p:PreferredToolArchitecture=x64 /p:CLToolPath=${{ env.CCACHE_BIN_DIR }} /p:UseMultiToolTask=true /p:CustomAfterMicrosoftCommonTargets="${{ github.workspace }}\buildfiles\msvc\ci_only.targets"
- name: Pack up build artifacts
run: |
mkdir -p "${{ env.BUILD_ARTIFACTSTAGINGDIRECTORY }}"
.ci/deploy-llvm.sh
- name: Upload artifacts (7z)
uses: actions/upload-artifact@main
with:
name: LLVM for Windows (MSVC)
path: ${{ env.BUILD_ARTIFACTSTAGINGDIRECTORY }}
compression-level: 0
if-no-files-found: error
- name: Save Dependencies Cache
if: github.ref == 'refs/heads/master'
uses: actions/cache/save@main
with:
path: ${{ env.DEPS_CACHE_DIR }}
key: ${{ steps.restore-dependencies-cache.outputs.cache-primary-key }}

View file

@ -17,7 +17,6 @@ concurrency:
env: env:
BUILD_REPOSITORY_NAME: ${{ github.repository }} BUILD_REPOSITORY_NAME: ${{ github.repository }}
BUILD_SOURCEBRANCHNAME: ${{ github.ref_name }} BUILD_SOURCEBRANCHNAME: ${{ github.ref_name }}
BUILD_PR_NUMBER: ${{ github.event.pull_request.number }}
BUILD_SOURCEVERSION: ${{ github.sha }} BUILD_SOURCEVERSION: ${{ github.sha }}
BUILD_ARTIFACTSTAGINGDIRECTORY: ${{ github.workspace }}/artifacts/ BUILD_ARTIFACTSTAGINGDIRECTORY: ${{ github.workspace }}/artifacts/
@ -45,14 +44,11 @@ jobs:
compiler: clang compiler: clang
UPLOAD_COMMIT_HASH: a1d35836e8d45bfc6f63c26f0a3e5d46ef622fe1 UPLOAD_COMMIT_HASH: a1d35836e8d45bfc6f63c26f0a3e5d46ef622fe1
UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-linux-arm64" UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-linux-arm64"
- os: ubuntu-24.04-arm
docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.6"
build_sh: "/rpcs3/.ci/build-linux-aarch64.sh"
compiler: gcc
name: RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }} name: RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }}
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
env: env:
CCACHE_DIR: ${{ github.workspace }}/ccache CCACHE_DIR: ${{ github.workspace }}/ccache
CI_HAS_ARTIFACTS: true
DEPLOY_APPIMAGE: true DEPLOY_APPIMAGE: true
APPDIR: "/rpcs3/build/appdir" APPDIR: "/rpcs3/build/appdir"
ARTDIR: "/root/artifacts" ARTDIR: "/root/artifacts"
@ -67,19 +63,19 @@ jobs:
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Restore build Ccache - name: Setup Cache
uses: actions/cache/restore@main uses: actions/cache@main
id: restore-build-ccache
with: with:
path: ${{ env.CCACHE_DIR }} path: ${{ env.CCACHE_DIR }}
key: ${{ runner.os }}-ccache-${{ matrix.compiler }}-${{ runner.arch }}-${{github.run_id}} key: ${{ runner.os }}-ccache-${{ matrix.compiler }}-${{ runner.arch }}-${{github.run_id}}
restore-keys: ${{ runner.os }}-ccache-${{ matrix.compiler }}-${{ runner.arch }}- restore-keys: |
${{ runner.os }}-ccache-${{ matrix.compiler }}-${{ runner.arch }}-
- name: Docker setup and build - name: Docker setup and build
run: | run: |
docker pull --quiet ${{ matrix.docker_img }} docker pull --quiet ${{ matrix.docker_img }}
docker run \ docker run \
-v $PWD:/rpcs3 \ -v $PWD:/rpcs3 \
--env-file .ci/docker.env \ --env-file .ci/docker.env \
-v ${{ env.CCACHE_DIR }}:/root/.ccache \ -v ${{ env.CCACHE_DIR }}:/root/.ccache \
-v ${{ env.BUILD_ARTIFACTSTAGINGDIRECTORY }}:${{ env.ARTDIR }} \ -v ${{ env.BUILD_ARTIFACTSTAGINGDIRECTORY }}:${{ env.ARTDIR }} \
@ -108,13 +104,6 @@ jobs:
export AVVER="${COMM_TAG}-${COMM_COUNT}" export AVVER="${COMM_TAG}-${COMM_COUNT}"
.ci/github-upload.sh .ci/github-upload.sh
- name: Save build Ccache
if: github.ref == 'refs/heads/master'
uses: actions/cache/save@main
with:
path: ${{ env.CCACHE_DIR }}
key: ${{ steps.restore-build-ccache.outputs.cache-primary-key }}
Mac_Build: Mac_Build:
# Only run push event on master branch of main repo, but run all PRs # Only run push event on master branch of main repo, but run all PRs
if: github.event_name != 'push' || (github.repository == 'RPCS3/rpcs3' && github.ref_name == 'master') if: github.event_name != 'push' || (github.repository == 'RPCS3/rpcs3' && github.ref_name == 'master')
@ -134,34 +123,34 @@ jobs:
runs-on: macos-14 runs-on: macos-14
env: env:
CCACHE_DIR: /tmp/ccache_dir CCACHE_DIR: /tmp/ccache_dir
CI_HAS_ARTIFACTS: true
QT_VER: '6.7.3' QT_VER: '6.7.3'
QT_VER_MAIN: '6' QT_VER_MAIN: '6'
LLVM_COMPILER_VER: '19' LLVM_COMPILER_VER: '19'
RELEASE_MESSAGE: ../GitHubReleaseMessage.txt RELEASE_MESSAGE: ../GitHubReleaseMessage.txt
UPLOAD_COMMIT_HASH: ${{ matrix.UPLOAD_COMMIT_HASH }} UPLOAD_COMMIT_HASH: ${{ matrix.UPLOAD_COMMIT_HASH }}
UPLOAD_REPO_FULL_NAME: ${{ matrix.UPLOAD_REPO_FULL_NAME }} UPLOAD_REPO_FULL_NAME: ${{ matrix.UPLOAD_REPO_FULL_NAME }}
RUN_UNIT_TESTS: github.event_name == 'pull_request' && 'ON' || 'OFF'
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@main uses: actions/checkout@main
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Restore Build Ccache - name: Setup Cache
uses: actions/cache/restore@main uses: actions/cache@main
id: restore-build-ccache
with: with:
path: ${{ env.CCACHE_DIR }} path: ${{ env.CCACHE_DIR }}
key: ${{ runner.os }}-ccache-${{ matrix.name }}-${{github.run_id}} key: ${{ runner.os }}-ccache-${{ matrix.name }}-${{github.run_id}}
restore-keys: ${{ runner.os }}-ccache-${{ matrix.name }}- restore-keys: |
${{ runner.os }}-ccache-${{ matrix.name }}-
- name: Restore Qt Cache - name: Setup Qt Cache
uses: actions/cache/restore@main uses: actions/cache@main
id: restore-qt-cache
with: with:
path: /tmp/Qt path: /tmp/Qt
key: ${{ runner.os }}-qt-${{ matrix.name }}-${{ env.QT_VER }} key: ${{ runner.os }}-qt-${{ matrix.name }}-${{ env.QT_VER }}
restore-keys: ${{ runner.os }}-qt-${{ matrix.name }}-${{ env.QT_VER }} restore-keys: |
${{ runner.os }}-qt-${{ matrix.name }}-${{ env.QT_VER }}
- name: Build - name: Build
run: ${{ matrix.build_sh }} run: ${{ matrix.build_sh }}
@ -190,20 +179,6 @@ jobs:
RPCS3_TOKEN: ${{ secrets.RPCS3_TOKEN }} RPCS3_TOKEN: ${{ secrets.RPCS3_TOKEN }}
run: .ci/github-upload.sh run: .ci/github-upload.sh
- name: Save Build Ccache
if: github.ref == 'refs/heads/master'
uses: actions/cache/save@main
with:
path: ${{ env.CCACHE_DIR }}
key: ${{ steps.restore-build-ccache.outputs.cache-primary-key }}
- name: Save Qt Cache
if: github.ref == 'refs/heads/master'
uses: actions/cache/save@main
with:
path: /tmp/Qt
key: ${{ steps.restore-qt-cache.outputs.cache-primary-key }}
Windows_Build: Windows_Build:
# Only run push event on master branch of main repo, but run all PRs # Only run push event on master branch of main repo, but run all PRs
if: github.event_name != 'push' || (github.repository == 'RPCS3/rpcs3' && github.ref_name == 'master') if: github.event_name != 'push' || (github.repository == 'RPCS3/rpcs3' && github.ref_name == 'master')
@ -212,9 +187,9 @@ jobs:
env: env:
COMPILER: msvc COMPILER: msvc
QT_VER_MAIN: '6' QT_VER_MAIN: '6'
QT_VER: '6.9.1' QT_VER: '6.9.0'
QT_VER_MSVC: 'msvc2022' QT_VER_MSVC: 'msvc2022'
QT_DATE: '202505291653' QT_DATE: '202503301022'
LLVM_VER: '19.1.7' LLVM_VER: '19.1.7'
VULKAN_VER: '1.3.268.0' VULKAN_VER: '1.3.268.0'
VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5' VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5'
@ -248,26 +223,22 @@ jobs:
- name: Get Cache Keys - name: Get Cache Keys
run: .ci/get_keys-windows.sh run: .ci/get_keys-windows.sh
- name: Restore Build Ccache - name: Setup Build Ccache
uses: actions/cache/restore@main uses: actions/cache@main
id: restore-build-ccache
with: with:
path: ${{ env.CCACHE_DIR }} path: ${{ env.CCACHE_DIR }}
key: "${{ runner.os }}-ccache-${{ env.COMPILER }}-${{github.run_id}}" key: "${{ runner.os }}-ccache-${{ env.COMPILER }}-${{github.run_id}}"
restore-keys: ${{ runner.os }}-ccache-${{ env.COMPILER }}- restore-keys: ${{ runner.os }}-ccache-${{ env.COMPILER }}-
- name: Restore Dependencies Cache - name: Setup Dependencies Cache
uses: actions/cache/restore@main uses: actions/cache@main
id: restore-dependencies-cache
with: with:
path: ${{ env.DEPS_CACHE_DIR }} path: ${{ env.DEPS_CACHE_DIR }}
key: "${{ runner.os }}-${{ env.COMPILER }}-${{ env.QT_VER }}-${{ env.VULKAN_SDK_SHA }}-${{ env.CCACHE_SHA }}-${{ hashFiles('llvm.lock') }}" key: "${{ runner.os }}-${{ env.COMPILER }}-${{ env.QT_VER }}-${{ env.VULKAN_SDK_SHA }}-${{ env.CCACHE_SHA }}-${{ hashFiles('llvm.lock') }}"
restore-keys: ${{ runner.os }}-${{ env.COMPILER }}- restore-keys: ${{ runner.os }}-${{ env.COMPILER }}-
- name: Download and unpack dependencies - name: Download and unpack dependencies
run: | run: .ci/setup-windows.sh
.ci/setup-windows.sh
.ci/setup-windows-ci-vars.sh win64 msvc
- name: Export Variables - name: Export Variables
run: | run: |
@ -310,135 +281,3 @@ jobs:
env: env:
RPCS3_TOKEN: ${{ secrets.RPCS3_TOKEN }} RPCS3_TOKEN: ${{ secrets.RPCS3_TOKEN }}
run: .ci/github-upload.sh run: .ci/github-upload.sh
- name: Save Build Ccache
if: github.ref == 'refs/heads/master'
uses: actions/cache/save@main
with:
path: ${{ env.CCACHE_DIR }}
key: ${{ steps.restore-build-ccache.outputs.cache-primary-key }}
- name: Save Dependencies Cache
if: github.ref == 'refs/heads/master'
uses: actions/cache/save@main
with:
path: ${{ env.DEPS_CACHE_DIR }}
key: ${{ steps.restore-dependencies-cache.outputs.cache-primary-key }}
Windows_Build_Clang:
# Only run push event on master branch of main repo, but run all PRs
if: github.event_name != 'push' || (github.repository == 'RPCS3/rpcs3' && github.ref_name == 'master')
name: RPCS3 Windows Clang
runs-on: windows-2025
strategy:
matrix:
include:
- msys2: clang64
compiler: clang
arch: win64
env:
CCACHE_DIR: 'C:\ccache'
steps:
- name: Checkout repository
uses: actions/checkout@main
with:
fetch-depth: 0
- name: Setup msys2
uses: msys2/setup-msys2@v2
with:
msystem: ${{ matrix.msys2 }}
update: true
cache: true
install: |
mingw-w64-clang-x86_64-clang
mingw-w64-clang-x86_64-ccache
mingw-w64-clang-x86_64-cmake
mingw-w64-clang-x86_64-lld
mingw-w64-clang-x86_64-ninja
mingw-w64-clang-x86_64-llvm
mingw-w64-clang-x86_64-ffmpeg
mingw-w64-clang-x86_64-opencv
mingw-w64-clang-x86_64-glew
mingw-w64-clang-x86_64-vulkan
mingw-w64-clang-x86_64-vulkan-headers
mingw-w64-clang-x86_64-vulkan-loader
mingw-w64-clang-x86_64-gtest
mingw-w64-clang-x86_64-qt6-base
mingw-w64-clang-x86_64-qt6-declarative
mingw-w64-clang-x86_64-qt6-multimedia
mingw-w64-clang-x86_64-qt6-svg
base-devel
curl
git
p7zip
- name: Restore build Ccache
uses: actions/cache/restore@main
id: restore-build-ccache
with:
path: ${{ env.CCACHE_DIR }}
key: ${{ runner.os }}-ccache-${{ matrix.compiler }}-${{ runner.arch }}-${{ github.run_id }}
restore-keys: ${{ runner.os }}-ccache-${{ matrix.compiler }}-${{ runner.arch }}-
- name: Build RPCS3
shell: msys2 {0}
run: |
export CCACHE_DIR=$(cygpath -u "$CCACHE_DIR")
echo "CCACHE_DIR=$CCACHE_DIR"
.ci/setup-windows-ci-vars.sh ${{ matrix.arch }} ${{ matrix.compiler }}
.ci/build-windows-clang.sh
- name: Save build Ccache
if: github.ref == 'refs/heads/master'
uses: actions/cache/save@main
with:
path: ${{ env.CCACHE_DIR }}
key: ${{ steps.restore-build-ccache.outputs.cache-primary-key }}
- name: Upload artifacts
uses: actions/upload-artifact@main
with:
name: RPCS3 for Windows (${{ runner.arch }}, ${{ matrix.compiler }})
path: ${{ env.BUILD_ARTIFACTSTAGINGDIRECTORY }}
compression-level: 0
if-no-files-found: error
FreeBSD_Build:
# Only run push event on master branch of main repo, but run all PRs
if: github.event_name != 'push' || (github.repository == 'RPCS3/rpcs3' && github.ref_name == 'master')
name: RPCS3 FreeBSD
runs-on: ubuntu-latest
timeout-minutes: 60
env:
CCACHE_DIR: ${{ github.workspace }}/ccache
QT_VER_MAIN: '6'
LLVM_COMPILER_VER: '19'
steps:
- name: Checkout repository
uses: actions/checkout@main
with:
fetch-depth: 0
- name: Restore Build Ccache
uses: actions/cache/restore@main
id: restore-build-ccache
with:
path: ${{ env.CCACHE_DIR }}
key: FreeBSD-ccache-${{github.run_id}}
restore-keys: FreeBSD-ccache-
- name: FreeBSD build
id: root
uses: vmactions/freebsd-vm@v1
with:
envs: 'QT_VER_MAIN LLVM_COMPILER_VER CCACHE_DIR'
usesh: true
run: .ci/install-freebsd.sh && .ci/build-freebsd.sh
- name: Save Build Ccache
if: github.ref == 'refs/heads/master'
uses: actions/cache/save@main
with:
path: ${{ env.CCACHE_DIR }}
key: ${{ steps.restore-build-ccache.outputs.cache-primary-key }}

2
3rdparty/FAudio vendored

@ -1 +1 @@
Subproject commit e6ddfabab2efbc8765750039634fe5e24ac31205 Subproject commit 6077ea740a7114a54f76ed9b7abe08cffc0034b6

View file

@ -11,7 +11,6 @@ if(USE_SYSTEM_OPENAL)
else() else()
option(ALSOFT_UTILS "Build utility programs" OFF) option(ALSOFT_UTILS "Build utility programs" OFF)
option(ALSOFT_EXAMPLES "Build example programs" OFF) option(ALSOFT_EXAMPLES "Build example programs" OFF)
set(LIBTYPE "STATIC")
add_subdirectory(openal-soft EXCLUDE_FROM_ALL) add_subdirectory(openal-soft EXCLUDE_FROM_ALL)
add_library(3rdparty_openal INTERFACE) add_library(3rdparty_openal INTERFACE)
target_link_libraries(3rdparty_openal INTERFACE OpenAL::OpenAL) target_link_libraries(3rdparty_openal INTERFACE OpenAL::OpenAL)

2
3rdparty/curl/curl vendored

@ -1 +1 @@
Subproject commit fdb8a789d2b446b77bd7cdd2eff95f6cbc814cf4 Subproject commit 4dacb79fcdd9364c1083e06f6a011d797a344f47

@ -1 +1 @@
Subproject commit 2b978915d82377df13fcbb1fb56660195ded868a Subproject commit ea127968204cc5d10f3fc9250c306b9e8cbd9b80

@ -1 +1 @@
Subproject commit c9a6709bd21750f1ad9597be21abace78c6378c9 Subproject commit 8d604353a53853fa56d1bdce0363535605ca868f

@ -1 +1 @@
Subproject commit 15a7ebb4d426c5ce196684347d2b7cafad862626 Subproject commit a61afe5f75d969c4561a1d0ad753aa23cee6329a

View file

@ -66,10 +66,12 @@ if(WITH_LLVM)
find_package(LLVM CONFIG) find_package(LLVM CONFIG)
if (NOT LLVM_FOUND) if (NOT LLVM_FOUND)
message(FATAL_ERROR "Can't find LLVM libraries from the CMAKE_PREFIX_PATH path or LLVM_DIR. Enable BUILD_LLVM option to build LLVM from included as a git submodule.") message(FATAL_ERROR "Can't find LLVM libraries from the CMAKE_PREFIX_PATH path or LLVM_DIR. \
Enable BUILD_LLVM option to build LLVM from included as a git submodule.")
endif() endif()
if (LLVM_VERSION VERSION_LESS 18) if (LLVM_VERSION VERSION_LESS 18)
message(FATAL_ERROR "Found LLVM version ${LLVM_VERSION}. Required version 18 or above. Enable BUILD_LLVM option to build LLVM from included as a git submodule.") message(FATAL_ERROR "Found LLVM version ${LLVM_VERSION}. Required version 18 or above. \
Enable BUILD_LLVM option to build LLVM from included as a git submodule.")
endif() endif()
endif() endif()

@ -1 +1 @@
Subproject commit 50fb5e701d8b0d3fe8262ed84668a94cc8cbf0b1 Subproject commit f76628fb5b25746fcb75a7ce85be0d8c6439fc57

View file

@ -39,14 +39,13 @@ check_tags()
echo "Checking $path" echo "Checking $path"
git_call fetch --prune --all # git_call fetch --prune --all
# Get the latest tag (by commit date, not tag name) # Get the latest tag (by commit date, not tag name)
local tag_list=$(git_call rev-list --tags --max-count=1) local tag_list=$(git_call rev-list --tags --max-count=1)
local latest_tag=$(git_call describe --tags "$tag_list") local latest_tag=$(git_call describe --tags "$tag_list")
local highest_tag=$(git_call tag -l | sort -V | tail -n1)
if [ -n "$latest_tag" ] || [ -n "$highest_tag" ]; then if [ -n "$latest_tag" ]; then
# Get the current tag # Get the current tag
local current_tag=$(git_call describe --tags --abbrev=0) local current_tag=$(git_call describe --tags --abbrev=0)
@ -54,29 +53,28 @@ check_tags()
if [ -n "$current_tag" ]; then if [ -n "$current_tag" ]; then
if [ "$verbose" -eq 1 ]; then if [ "$verbose" -eq 1 ]; then
echo "$path -> latest: $latest_tag, highest: $highest_tag, current: $current_tag" echo "$path -> latest: $latest_tag, current: $current_tag"
fi fi
local ts0=$(git_call log -1 --format=%ct $highest_tag)
local ts1=$(git_call log -1 --format=%ct $latest_tag) local ts1=$(git_call log -1 --format=%ct $latest_tag)
local ts2=$(git_call log -1 --format=%ct $current_tag) local ts2=$(git_call log -1 --format=%ct $current_tag)
if (( ts0 > ts2 )) || (( ts1 > ts2 )); then if (( ts1 > ts2 )); then
if [ "$verbose" -eq 1 ]; then if [ "$verbose" -eq 1 ]; then
echo -e "\t $path: latest is newer" echo -e "\t $path: latest is newer"
elif [ "$verbose" -eq 0 ]; then elif [ "$verbose" -eq 0 ]; then
echo "$path -> latest: $latest_tag, highest: $highest_tag, current: $current_tag" echo "$path -> latest: $latest_tag, current: $current_tag"
fi fi
# Critical section guarded by flock # Critical section guarded by flock
( (
flock 200 flock 200
echo "$path -> latest: $latest_tag, highest: $highest_tag, current: $current_tag" >> "$resultfile" echo "$path -> latest: $latest_tag, current: $current_tag" >> "$resultfile"
) 200>"$lockfile" ) 200>"$lockfile"
fi fi
elif [ "$verbose" -eq 1 ]; then elif [ "$verbose" -eq 1 ]; then
echo "$path -> latest: $latest_tag, highest: $highest_tag" echo "$path -> latest: $latest_tag"
fi fi
elif [ "$verbose" -eq 1 ]; then elif [ "$verbose" -eq 1 ]; then

View file

@ -19,26 +19,26 @@ The following tools are required to build RPCS3 on Windows 10 or later:
with standalone **CMake** tool. with standalone **CMake** tool.
- [Python 3.6+](https://www.python.org/downloads/) (add to PATH) - [Python 3.6+](https://www.python.org/downloads/) (add to PATH)
- [Qt 6.9.1](https://www.qt.io/download-qt-installer) In case you can't download from the official installer, you can use [Another Qt installer](https://github.com/miurahr/aqtinstall) (In that case you will need to manually add the "qtmultimedia" module when installing Qt) - [Qt 6.9.0](https://www.qt.io/download-qt-installer) In case you can't download from the official installer, you can use [Another Qt installer](https://github.com/miurahr/aqtinstall) (In that case you will need to manually add the "qtmultimedia" module when installing Qt)
- [Vulkan SDK 1.3.268.0](https://vulkan.lunarg.com/sdk/home) (see "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/windows/getting_started.html)) for now future SDKs don't work. You need precisely 1.3.268.0. - [Vulkan SDK 1.3.268.0](https://vulkan.lunarg.com/sdk/home) (see "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/windows/getting_started.html)) for now future SDKs don't work. You need precisely 1.3.268.0.
The `sln` solution available only on **Visual Studio** is the preferred building solution. It easily allows to build the **RPCS3** application in `Release` and `Debug` mode. The `sln` solution available only on **Visual Studio** is the preferred building solution. It easily allows to build the **RPCS3** application in `Release` and `Debug` mode.
In order to build **RPCS3** with the `sln` solution (with **Visual Studio**), **Qt** libs need to be detected. To detect the libs: In order to build **RPCS3** with the `sln` solution (with **Visual Studio**), **Qt** libs need to be detected. To detect the libs:
- add and set the `QTDIR` environment variable, e.g. `<QtInstallFolder>\6.9.1\msvc2022_64\` - add and set the `QTDIR` environment variable, e.g. `<QtInstallFolder>\6.9.0\msvc2022_64\`
- or use the [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2022) - or use the [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2022)
**NOTE:** If you have issues with the **Visual Studio Qt Plugin**, you may want to uninstall it and install the [Legacy Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.LEGACYQtVisualStudioTools2022) instead. **NOTE:** If you have issues with the **Visual Studio Qt Plugin**, you may want to uninstall it and install the [Legacy Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.LEGACYQtVisualStudioTools2022) instead.
In order to build **RPCS3** with the `CMake` solution (with both **Visual Studio** and standalone **CMake** tool): In order to build **RPCS3** with the `CMake` solution (with both **Visual Studio** and standalone **CMake** tool):
- add and set the `Qt6_ROOT` environment variable to the **Qt** libs path, e.g. `<QtInstallFolder>\6.9.1\msvc2022_64\` - add and set the `Qt6_ROOT` environment variable to the **Qt** libs path, e.g. `<QtInstallFolder>\6.9.0\msvc2022_64\`
### Linux ### Linux
These are the essentials tools to build RPCS3 on Linux. Some of them can be installed through your favorite package manager: These are the essentials tools to build RPCS3 on Linux. Some of them can be installed through your favorite package manager:
- Clang 17+ or GCC 13+ - Clang 17+ or GCC 13+
- [CMake 3.28.0+](https://www.cmake.org/download/) - [CMake 3.28.0+](https://www.cmake.org/download/)
- [Qt 6.9.1](https://www.qt.io/download-qt-installer) - [Qt 6.9.0](https://www.qt.io/download-qt-installer)
- [Vulkan SDK 1.3.268.0](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/linux/getting_started.html)) for now future SDKs don't work. You need precisely 1.3.268.0. - [Vulkan SDK 1.3.268.0](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/linux/getting_started.html)) for now future SDKs don't work. You need precisely 1.3.268.0.
- [SDL3](https://github.com/libsdl-org/SDL/releases) (for the FAudio backend) - [SDL3](https://github.com/libsdl-org/SDL/releases) (for the FAudio backend)
@ -121,7 +121,7 @@ Start **Visual Studio**, click on `Open a project or solution` and select the `r
##### Configuring the Qt Plugin (if used) ##### Configuring the Qt Plugin (if used)
1) go to `Extensions->Qt VS Tools->Qt Versions` 1) go to `Extensions->Qt VS Tools->Qt Versions`
2) add the path to your Qt installation with compiler e.g. `<QtInstallFolder>\6.9.1\msvc2022_64`, version will fill in automatically 2) add the path to your Qt installation with compiler e.g. `<QtInstallFolder>\6.9.0\msvc2022_64`, version will fill in automatically
3) go to `Extensions->Qt VS Tools->Options->Legacy Project Format`. (Only available in the **Legacy Qt Plugin**) 3) go to `Extensions->Qt VS Tools->Options->Legacy Project Format`. (Only available in the **Legacy Qt Plugin**)
4) set `Build: Run pre-build setup` to `true`. (Only available in the **Legacy Qt Plugin**) 4) set `Build: Run pre-build setup` to `true`. (Only available in the **Legacy Qt Plugin**)

View file

@ -1,12 +1,5 @@
cmake_minimum_required(VERSION 3.28) cmake_minimum_required(VERSION 3.28)
find_program(CCACHE_PATH ccache HINTS ENV PATH)
if(CCACHE_PATH)
message(STATUS "Using ccache: ${CCACHE_PATH}")
set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_PATH}")
set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PATH}")
endif()
project(rpcs3 LANGUAGES C CXX) project(rpcs3 LANGUAGES C CXX)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
@ -29,23 +22,16 @@ option(STATIC_LINK_LLVM "Link against LLVM statically. This will get set to ON i
option(USE_FAUDIO "FAudio audio backend" ON) option(USE_FAUDIO "FAudio audio backend" ON)
option(USE_LIBEVDEV "libevdev-based joystick support" ON) option(USE_LIBEVDEV "libevdev-based joystick support" ON)
option(USE_DISCORD_RPC "Discord rich presence integration" OFF) option(USE_DISCORD_RPC "Discord rich presence integration" OFF)
option(USE_SYSTEM_ZLIB "Prefer system ZLIB instead of the builtin one" ON)
option(USE_VULKAN "Vulkan render backend" ON) option(USE_VULKAN "Vulkan render backend" ON)
option(USE_PRECOMPILED_HEADERS "Use precompiled headers" OFF) option(USE_PRECOMPILED_HEADERS "Use precompiled headers" OFF)
option(USE_SDL "Enables SDL input handler" OFF) option(USE_SDL "Enables SDL input handler" OFF)
option(USE_SYSTEM_CUBEB "Prefer system cubeb instead of the builtin one" OFF)
option(USE_SYSTEM_CURL "Prefer system Curl instead of the prebuild one" ON)
option(USE_SYSTEM_FAUDIO "Prefer system FAudio instead of the builtin one" OFF)
option(USE_SYSTEM_FFMPEG "Prefer system ffmpeg instead of the prebuild one" OFF)
option(USE_SYSTEM_FLATBUFFERS "Prefer system flatbuffers instead of the builtin one" OFF)
option(USE_SYSTEM_LIBPNG "Prefer system libpng instead of the builtin one" OFF)
option(USE_SYSTEM_LIBUSB "Prefer system libusb instead of the builtin one" OFF)
option(USE_SYSTEM_MVK "Prefer system MoltenVK instead of the builtin one" OFF)
option(USE_SYSTEM_OPENAL "Prefer system OpenAL instead of the prebuild one" ON)
option(USE_SYSTEM_OPENCV "Prefer system OpenCV instead of the builtin one" ON)
option(USE_SYSTEM_PUGIXML "Prefer system pugixml instead of the builtin one" OFF)
option(USE_SYSTEM_SDL "Prefer system SDL instead of the builtin one" ON) option(USE_SYSTEM_SDL "Prefer system SDL instead of the builtin one" ON)
option(USE_SYSTEM_WOLFSSL "Prefer system MoltenVK instead of the builtin one" OFF) option(USE_SYSTEM_FFMPEG "Prefer system ffmpeg instead of the prebuild one" OFF)
option(USE_SYSTEM_ZLIB "Prefer system ZLIB instead of the builtin one" ON) option(USE_SYSTEM_OPENAL "Prefer system OpenAL instead of the prebuild one" ON)
option(USE_SYSTEM_CURL "Prefer system Curl instead of the prebuild one" ON)
option(USE_SYSTEM_OPENCV "Prefer system OpenCV instead of the builtin one" ON)
option(USE_SYSTEM_CUBEB "Prefer system cubeb instead of the builtin one" OFF)
option(HAS_MEMORY_BREAKPOINTS "Add support for memory breakpoints to the interpreter" OFF) option(HAS_MEMORY_BREAKPOINTS "Add support for memory breakpoints to the interpreter" OFF)
option(USE_LTO "Use LTO for building" ON) option(USE_LTO "Use LTO for building" ON)
option(BUILD_RPCS3_TESTS "Build RPCS3 unit tests." OFF) option(BUILD_RPCS3_TESTS "Build RPCS3 unit tests." OFF)
@ -123,6 +109,11 @@ if(NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
message( FATAL_ERROR "RPCS3 can only be compiled on 64-bit platforms." ) message( FATAL_ERROR "RPCS3 can only be compiled on 64-bit platforms." )
endif() endif()
find_program(CCACHE_FOUND ccache)
if(CCACHE_FOUND)
set(CMAKE_CXX_COMPILER_LAUNCHER ccache)
endif()
if(APPLE AND CMAKE_OSX_ARCHITECTURES STREQUAL "arm64") if(APPLE AND CMAKE_OSX_ARCHITECTURES STREQUAL "arm64")
include_directories(/opt/homebrew/include) include_directories(/opt/homebrew/include)
link_directories(/opt/homebrew/lib) link_directories(/opt/homebrew/lib)

View file

@ -1,6 +1,7 @@
RPCS3 RPCS3
===== =====
[![Cirrus CI - Base Branch Build Status](https://img.shields.io/cirrus/github/RPCS3/rpcs3/master?label=Cirrus%20CI&logo=cirrus-ci)](https://cirrus-ci.com/github/RPCS3/rpcs3)
[![GitHub Actions](https://img.shields.io/github/actions/workflow/status/RPCS3/rpcs3/rpcs3.yml?branch=master&logo=github&label=Actions)](https://github.com/RPCS3/rpcs3/actions/workflows/rpcs3.yml) [![GitHub Actions](https://img.shields.io/github/actions/workflow/status/RPCS3/rpcs3/rpcs3.yml?branch=master&logo=github&label=Actions)](https://github.com/RPCS3/rpcs3/actions/workflows/rpcs3.yml)
[![RPCS3 Discord Server](https://img.shields.io/discord/272035812277878785?color=5865F2&label=RPCS3%20Discord&logo=discord&logoColor=white)](https://discord.gg/rpcs3) [![RPCS3 Discord Server](https://img.shields.io/discord/272035812277878785?color=5865F2&label=RPCS3%20Discord&logo=discord&logoColor=white)](https://discord.gg/rpcs3)

View file

@ -393,7 +393,7 @@ namespace fmt
raw_throw_exception(src_loc, reinterpret_cast<const char*>(fmt), type_info_v<Args...>, fmt_args_t<Args...>{fmt_unveil<Args>::get(args)...}); raw_throw_exception(src_loc, reinterpret_cast<const char*>(fmt), type_info_v<Args...>, fmt_args_t<Args...>{fmt_unveil<Args>::get(args)...});
} }
#if !defined(_MSC_VER) || defined(__clang__) #ifndef _MSC_VER
[[noreturn]] ~throw_exception(); [[noreturn]] ~throw_exception();
#endif #endif
}; };

View file

@ -96,7 +96,7 @@ class thread_future
thread_future* prev{}; thread_future* prev{};
protected: protected:
atomic_t<void(*)(const thread_base*, thread_future*)> exec{}; atomic_t<void(*)(thread_base*, thread_future*)> exec{};
atomic_t<u32> done{0}; atomic_t<u32> done{0};
@ -374,23 +374,13 @@ private:
static const u64 process_affinity_mask; static const u64 process_affinity_mask;
}; };
#if defined(__has_cpp_attribute)
#if __has_cpp_attribute(no_unique_address)
#define NO_UNIQUE_ADDRESS [[no_unique_address]]
#else
#define NO_UNIQUE_ADDRESS
#endif
#else
#define NO_UNIQUE_ADDRESS
#endif
// Used internally // Used internally
template <bool Discard, typename Ctx, typename... Args> template <bool Discard, typename Ctx, typename... Args>
class thread_future_t : public thread_future, result_storage<Ctx, std::conditional_t<Discard, int, void>, Args...> class thread_future_t : public thread_future, result_storage<Ctx, std::conditional_t<Discard, int, void>, Args...>
{ {
NO_UNIQUE_ADDRESS decltype(std::make_tuple(std::forward<Args>(std::declval<Args>())...)) m_args; [[no_unique_address]] decltype(std::make_tuple(std::forward<Args>(std::declval<Args>())...)) m_args;
NO_UNIQUE_ADDRESS Ctx m_func; [[no_unique_address]] Ctx m_func;
using future = thread_future_t; using future = thread_future_t;
@ -399,7 +389,7 @@ public:
: m_args(std::forward<Args>(args)...) : m_args(std::forward<Args>(args)...)
, m_func(std::forward<Ctx>(func)) , m_func(std::forward<Ctx>(func))
{ {
thread_future::exec.raw() = +[](const thread_base* tb, thread_future* tf) thread_future::exec.raw() = +[](thread_base* tb, thread_future* tf)
{ {
const auto _this = static_cast<future*>(tf); const auto _this = static_cast<future*>(tf);

View file

@ -94,6 +94,8 @@ if defined BUILD_SOURCEBRANCHNAME (
rem // This must be a CI build rem // This must be a CI build
echo SYSTEM_PULLREQUEST_SOURCEBRANCH: %SYSTEM_PULLREQUEST_SOURCEBRANCH%
if defined BUILD_REPOSITORY_NAME ( if defined BUILD_REPOSITORY_NAME (
echo BUILD_REPOSITORY_NAME: %BUILD_REPOSITORY_NAME% echo BUILD_REPOSITORY_NAME: %BUILD_REPOSITORY_NAME%
) else ( ) else (
@ -108,7 +110,9 @@ if defined BUILD_SOURCEBRANCHNAME (
rem // These environment variables are defined by CI rem // These environment variables are defined by CI
rem // BUILD_REPOSITORY_NAME will look like "RPCS3/rpcs3" rem // BUILD_REPOSITORY_NAME will look like "RPCS3/rpcs3"
rem // SYSTEM_PULLREQUEST_SOURCEBRANCH will look like "master"
rem // BUILD_SOURCEBRANCHNAME will look like "master" rem // BUILD_SOURCEBRANCHNAME will look like "master"
rem // See https://docs.microsoft.com/en-us/azure/devops/pipelines/build/variables
set GIT_FULL_BRANCH=%BUILD_REPOSITORY_NAME%/%BUILD_SOURCEBRANCHNAME% set GIT_FULL_BRANCH=%BUILD_REPOSITORY_NAME%/%BUILD_SOURCEBRANCHNAME%
echo GIT_FULL_BRANCH: !GIT_FULL_BRANCH! echo GIT_FULL_BRANCH: !GIT_FULL_BRANCH!
@ -125,12 +129,12 @@ if defined BUILD_SOURCEBRANCHNAME (
rem // This must be a pull request or a build from a fork. rem // This must be a pull request or a build from a fork.
echo Assuming pull request build echo Assuming pull request build
if "%BUILD_SOURCEBRANCHNAME%"=="master" ( if "%SYSTEM_PULLREQUEST_SOURCEBRANCH%"=="master" (
rem // If pull request comes from a master branch, GIT_BRANCH = username/branch in order to distinguish from upstream/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 ("%BUILD_REPOSITORY_NAME%") do set user=%%a for /f "tokens=1* delims=/" %%a in ("%BUILD_REPOSITORY_NAME%") do set user=%%a
set "GIT_BRANCH=!user!/%BUILD_SOURCEBRANCHNAME%" set "GIT_BRANCH=!user!/%SYSTEM_PULLREQUEST_SOURCEBRANCH%"
) else ( ) else (
set GIT_BRANCH=%BUILD_SOURCEBRANCHNAME% set GIT_BRANCH=%SYSTEM_PULLREQUEST_SOURCEBRANCH%
) )
rem // Make GIT_VERSION the last commit (shortened); Don't include commit count on non-release builds rem // Make GIT_VERSION the last commit (shortened); Don't include commit count on non-release builds

View file

@ -84,8 +84,9 @@ public:
transactional_storage& operator=(const transactional_storage&) = delete; transactional_storage& operator=(const transactional_storage&) = delete;
transactional_storage(transactional_storage&& other) transactional_storage(transactional_storage&& other)
: pool(std::move(other.pool))
{ {
pool = std::move(other.pool);
std::unique_lock lock_other{other.current_mutex}; std::unique_lock lock_other{other.current_mutex};
const std::shared_ptr<T> other_current = other.current; const std::shared_ptr<T> other_current = other.current;
other.current = nullptr; other.current = nullptr;

View file

@ -1,51 +0,0 @@
# Copy remaining DLLS
cmake_path(GET exe PARENT_PATH exe_dir)
cmake_path(CONVERT "${MSYS2_CLANG_BIN}" TO_CMAKE_PATH_LIST msys2_clang_bin)
cmake_path(CONVERT "${MSYS2_USR_BIN}" TO_CMAKE_PATH_LIST msys2_usr_bin)
message(STATUS "Getting runtime dependencies for '${exe}' in '${exe_dir}'")
message(STATUS "Dependency dirs: '${msys2_clang_bin}', '${msys2_usr_bin}'")
file(GET_RUNTIME_DEPENDENCIES EXECUTABLES ${exe}
RESOLVED_DEPENDENCIES_VAR resolved_deps
UNRESOLVED_DEPENDENCIES_VAR unresolved_deps
DIRECTORIES ${msys2_clang_bin} ${msys2_usr_bin})
foreach(dep IN LISTS resolved_deps)
cmake_path(GET dep FILENAME dep_filename)
cmake_path(GET dep PARENT_PATH dep_dir_raw)
cmake_path(CONVERT "${dep_dir_raw}" TO_CMAKE_PATH_LIST dep_dir)
string(COMPARE EQUAL "${dep_dir}" "${msys2_clang_bin}" is_clang_path)
string(COMPARE EQUAL "${dep_dir}" "${msys2_usr_bin}" is_usr_path)
set(same_path FALSE)
if(is_clang_path OR is_usr_path)
set(same_path TRUE)
endif()
if(same_path)
set(dest "${exe_dir}/${dep_filename}")
if(NOT EXISTS "${dest}")
file(COPY "${dep}" DESTINATION "${exe_dir}")
message(STATUS "Copied '${dep_filename}' to '${exe_dir}'")
else()
message(STATUS "Already exists: '${dest}', skipping.")
endif()
else()
message(STATUS "Found and ignored '${dep}' in '${dep_dir}'. Is clang path: ${is_clang_path}, is usr path: ${is_usr_path}")
endif()
endforeach()
# Warn about unresolved dependencies
if(unresolved_deps)
message(WARNING "Unresolved dependencies:")
foreach(dep IN LISTS unresolved_deps)
cmake_path(GET dep PARENT_PATH dep_dir)
string(TOLOWER "${dep_dir}" dep_dir_lower)
if(NOT dep_dir_lower MATCHES ".*(windows[/\\]system32|windows[/\\]winsxs|program files).*")
message(STATUS " - ${dep_dir}/${dep}")
endif()
endforeach()
endif()

View file

@ -154,11 +154,11 @@ if (NOT ANDROID)
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/GuiConfigs $<TARGET_FILE_DIR:rpcs3>/GuiConfigs) COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/GuiConfigs $<TARGET_FILE_DIR:rpcs3>/GuiConfigs)
elseif(WIN32) elseif(WIN32)
add_custom_command(TARGET rpcs3 POST_BUILD add_custom_command(TARGET rpcs3 POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:OpenAL::OpenAL> $<TARGET_FILE_DIR:rpcs3>
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/Icons $<TARGET_FILE_DIR:rpcs3>/Icons COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/Icons $<TARGET_FILE_DIR:rpcs3>/Icons
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/GuiConfigs $<TARGET_FILE_DIR:rpcs3>/GuiConfigs COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/GuiConfigs $<TARGET_FILE_DIR:rpcs3>/GuiConfigs
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/test $<TARGET_FILE_DIR:rpcs3>/test
COMMAND "${WINDEPLOYQT_EXECUTABLE}" --no-compiler-runtime --no-opengl-sw --no-patchqt COMMAND "${WINDEPLOYQT_EXECUTABLE}" --no-compiler-runtime --no-opengl-sw --no-patchqt
--no-translations --no-system-d3d-compiler --no-system-dxc-compiler --no-quick-import --no-translations --no-system-d3d-compiler --no-system-dxc-compiler --no-ffmpeg --no-quick-import
--plugindir "$<IF:$<CXX_COMPILER_ID:MSVC>,$<TARGET_FILE_DIR:rpcs3>/plugins,$<TARGET_FILE_DIR:rpcs3>/share/qt6/plugins>" --plugindir "$<IF:$<CXX_COMPILER_ID:MSVC>,$<TARGET_FILE_DIR:rpcs3>/plugins,$<TARGET_FILE_DIR:rpcs3>/share/qt6/plugins>"
--verbose 0 "$<TARGET_FILE:rpcs3>") --verbose 0 "$<TARGET_FILE:rpcs3>")
endif() endif()

View file

@ -337,7 +337,7 @@ f64 XAudio2Backend::GetCallbackFrameLen()
return std::max<f64>(min_latency, _10ms); // 10ms is the minimum for XAudio return std::max<f64>(min_latency, _10ms); // 10ms is the minimum for XAudio
} }
void XAudio2Backend::OnVoiceProcessingPassStart(UINT32 BytesRequired) noexcept void XAudio2Backend::OnVoiceProcessingPassStart(UINT32 BytesRequired)
{ {
std::unique_lock lock(m_cb_mutex, std::defer_lock); std::unique_lock lock(m_cb_mutex, std::defer_lock);
if (BytesRequired && !m_reset_req.observe() && lock.try_lock_for(std::chrono::microseconds{50}) && m_write_callback && m_playing) if (BytesRequired && !m_reset_req.observe() && lock.try_lock_for(std::chrono::microseconds{50}) && m_write_callback && m_playing)
@ -366,7 +366,7 @@ void XAudio2Backend::OnVoiceProcessingPassStart(UINT32 BytesRequired) noexcept
} }
} }
void XAudio2Backend::OnCriticalError(HRESULT Error) noexcept void XAudio2Backend::OnCriticalError(HRESULT Error)
{ {
XAudio.error("OnCriticalError() called: %s (0x%08x)", std::system_category().message(Error), static_cast<u32>(Error)); XAudio.error("OnCriticalError() called: %s (0x%08x)", std::system_category().message(Error), static_cast<u32>(Error));

View file

@ -54,18 +54,18 @@ private:
atomic_t<bool> m_reset_req = false; atomic_t<bool> m_reset_req = false;
// XAudio voice callbacks // XAudio voice callbacks
void OnVoiceProcessingPassStart(UINT32 BytesRequired) noexcept override; void OnVoiceProcessingPassStart(UINT32 BytesRequired) override;
void OnVoiceProcessingPassEnd() noexcept override {} void OnVoiceProcessingPassEnd() override {}
void OnStreamEnd() noexcept override {} void OnStreamEnd() override {}
void OnBufferStart(void* /* pBufferContext */) noexcept override {} void OnBufferStart(void* /* pBufferContext */) override {}
void OnBufferEnd(void* /* pBufferContext*/) noexcept override {} void OnBufferEnd(void* /* pBufferContext*/) override {}
void OnLoopEnd(void* /* pBufferContext */) noexcept override {} void OnLoopEnd(void* /* pBufferContext */) override {}
void OnVoiceError(void* /* pBufferContext */, HRESULT /* Error */) noexcept override {} void OnVoiceError(void* /* pBufferContext */, HRESULT /* Error */) override {}
// XAudio engine callbacks // XAudio engine callbacks
void OnProcessingPassStart() noexcept override {}; void OnProcessingPassStart() override {};
void OnProcessingPassEnd() noexcept override {}; void OnProcessingPassEnd() override {};
void OnCriticalError(HRESULT Error) noexcept override; void OnCriticalError(HRESULT Error) override;
// IMMNotificationClient callbacks // IMMNotificationClient callbacks
IFACEMETHODIMP_(ULONG) AddRef() override { return 1; }; IFACEMETHODIMP_(ULONG) AddRef() override { return 1; };

View file

@ -29,10 +29,6 @@ if(USE_ASAN)
set_source_files_properties(../../Utilities/Thread.cpp PROPERTIES COMPILE_DEFINITIONS USE_ASAN) set_source_files_properties(../../Utilities/Thread.cpp PROPERTIES COMPILE_DEFINITIONS USE_ASAN)
endif() endif()
if(NOT USE_SYSTEM_OPENAL)
target_compile_definitions(rpcs3_emu PUBLIC AL_LIBTYPE_STATIC)
endif()
if(HAS_MEMORY_BREAKPOINTS) if(HAS_MEMORY_BREAKPOINTS)
target_compile_definitions(rpcs3_emu PRIVATE RPCS3_HAS_MEMORY_BREAKPOINTS) target_compile_definitions(rpcs3_emu PRIVATE RPCS3_HAS_MEMORY_BREAKPOINTS)
endif() endif()

View file

@ -434,7 +434,7 @@ struct llvm_value_t<T*> : llvm_value_t<T>
static llvm::Type* get_type(llvm::LLVMContext& context) static llvm::Type* get_type(llvm::LLVMContext& context)
{ {
return llvm::PointerType::getUnqual(context); return llvm_value_t<T>::get_type(context)->getPointerTo();
} }
}; };

View file

@ -351,7 +351,7 @@ public:
for (u32 i = 0; i < CELL_GEM_MAX_NUM; i++) for (u32 i = 0; i < CELL_GEM_MAX_NUM; i++)
{ {
const auto& pad = ::at32(handler->GetPads(), pad_num(i)); const auto& pad = ::at32(handler->GetPads(), pad_num(i));
const bool connected = pad && pad->is_connected() && !pad->is_copilot() && i < attribute.max_connect; const bool connected = pad && pad->is_connected() && i < attribute.max_connect;
const bool is_real_move = g_cfg.io.move != move_handler::real || pad->m_pad_handler == pad_handler::move; const bool is_real_move = g_cfg.io.move != move_handler::real || pad->m_pad_handler == pad_handler::move;
update_connection(i, connected && is_real_move); update_connection(i, connected && is_real_move);
@ -407,7 +407,7 @@ public:
std::lock_guard pad_lock(pad::g_pad_mutex); std::lock_guard pad_lock(pad::g_pad_mutex);
const auto handler = pad::get_pad_thread(); const auto handler = pad::get_pad_thread();
const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num)); const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num));
if (pad && pad->m_pad_handler == pad_handler::move && !pad->is_copilot()) if (pad && pad->m_pad_handler == pad_handler::move)
{ {
if (!pad->move_data.calibration_requested || !pad->move_data.calibration_succeeded) if (!pad->move_data.calibration_requested || !pad->move_data.calibration_succeeded)
{ {
@ -437,7 +437,7 @@ public:
std::lock_guard pad_lock(pad::g_pad_mutex); std::lock_guard pad_lock(pad::g_pad_mutex);
const auto handler = pad::get_pad_thread(); const auto handler = pad::get_pad_thread();
const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num)); const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num));
if (pad && pad->m_pad_handler == pad_handler::move && !pad->is_copilot()) if (pad && pad->m_pad_handler == pad_handler::move)
{ {
pad->move_data.calibration_requested = false; pad->move_data.calibration_requested = false;
pad->move_data.calibration_succeeded = false; pad->move_data.calibration_succeeded = false;
@ -469,7 +469,7 @@ public:
for (u32 i = 0; i < std::min<u32>(attribute.max_connect, CELL_GEM_MAX_NUM); i++) for (u32 i = 0; i < std::min<u32>(attribute.max_connect, CELL_GEM_MAX_NUM); i++)
{ {
const auto& pad = ::at32(handler->GetPads(), pad_num(i)); const auto& pad = ::at32(handler->GetPads(), pad_num(i));
if (pad && pad->m_pad_handler == pad_handler::move && pad->is_connected() && !pad->is_copilot()) if (pad && pad->m_pad_handler == pad_handler::move && pad->is_connected())
{ {
connected_controllers++; connected_controllers++;
@ -490,7 +490,7 @@ public:
for (u32 i = 0; i < std::min<u32>(attribute.max_connect, CELL_GEM_MAX_NUM); i++) for (u32 i = 0; i < std::min<u32>(attribute.max_connect, CELL_GEM_MAX_NUM); i++)
{ {
const auto& pad = ::at32(handler->GetPads(), pad_num(i)); const auto& pad = ::at32(handler->GetPads(), pad_num(i));
if (pad && pad->is_connected() && !pad->is_copilot()) if (pad && pad->is_connected())
{ {
connected_controllers++; connected_controllers++;
@ -1776,7 +1776,7 @@ static void ds3_input_to_pad(const u32 gem_num, be_t<u16>& digital_buttons, be_t
const auto handler = pad::get_pad_thread(); const auto handler = pad::get_pad_thread();
const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num)); const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num));
if (!pad->is_connected() || pad->is_copilot()) if (!pad->is_connected())
{ {
return; return;
} }
@ -1864,7 +1864,7 @@ static void ds3_pos_to_gem_state(u32 gem_num, gem_config::gem_controller& contro
const auto handler = pad::get_pad_thread(); const auto handler = pad::get_pad_thread();
const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num)); const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num));
if (!pad->is_connected() || pad->is_copilot()) if (!pad->is_connected())
{ {
return; return;
} }
@ -1895,7 +1895,7 @@ static void ps_move_pos_to_gem_state(u32 gem_num, gem_config::gem_controller& co
const auto handler = pad::get_pad_thread(); const auto handler = pad::get_pad_thread();
const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num)); const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num));
if (pad->m_pad_handler != pad_handler::move || !pad->is_connected() || pad->is_copilot()) if (pad->m_pad_handler != pad_handler::move || !pad->is_connected())
{ {
return; return;
} }
@ -1940,7 +1940,7 @@ static void ds3_input_to_ext(u32 gem_num, gem_config::gem_controller& controller
const auto handler = pad::get_pad_thread(); const auto handler = pad::get_pad_thread();
const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num)); const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num));
if (!pad->is_connected() || pad->is_copilot()) if (!pad->is_connected())
{ {
return; return;
} }
@ -1952,7 +1952,7 @@ static void ds3_input_to_ext(u32 gem_num, gem_config::gem_controller& controller
ext.status = controller.ext_status; ext.status = controller.ext_status;
for (const AnalogStick& stick : pad->m_sticks_external) for (const AnalogStick& stick : pad->m_sticks)
{ {
switch (stick.m_offset) switch (stick.m_offset)
{ {
@ -1964,7 +1964,7 @@ static void ds3_input_to_ext(u32 gem_num, gem_config::gem_controller& controller
} }
} }
for (const Button& button : pad->m_buttons_external) for (const Button& button : pad->m_buttons)
{ {
if (!button.m_pressed) if (!button.m_pressed)
continue; continue;
@ -2400,7 +2400,7 @@ error_code cellGemEnableMagnetometer(u32 gem_num, u32 enable)
const auto handler = pad::get_pad_thread(); const auto handler = pad::get_pad_thread();
const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num)); const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num));
if (pad && pad->m_pad_handler == pad_handler::move && !pad->is_copilot()) if (pad && pad->m_pad_handler == pad_handler::move)
{ {
pad->move_data.magnetometer_enabled = controller.enabled_magnetometer; pad->move_data.magnetometer_enabled = controller.enabled_magnetometer;
} }
@ -2448,7 +2448,7 @@ error_code cellGemEnableMagnetometer2(u32 gem_num, u32 enable)
const auto handler = pad::get_pad_thread(); const auto handler = pad::get_pad_thread();
const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num)); const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num));
if (pad && pad->m_pad_handler == pad_handler::move && !pad->is_copilot()) if (pad && pad->m_pad_handler == pad_handler::move)
{ {
pad->move_data.magnetometer_enabled = controller.enabled_magnetometer; pad->move_data.magnetometer_enabled = controller.enabled_magnetometer;
} }
@ -2777,7 +2777,7 @@ error_code cellGemGetInertialState(u32 gem_num, u32 state_flag, u64 timestamp, v
const auto handler = pad::get_pad_thread(); const auto handler = pad::get_pad_thread();
const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num)); const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num));
if (pad && pad->is_connected() && !pad->is_copilot()) if (pad && pad->is_connected())
{ {
inertial_state->temperature = pad->move_data.temperature; inertial_state->temperature = pad->move_data.temperature;
inertial_state->accelerometer[0] = pad->move_data.accelerometer_x; inertial_state->accelerometer[0] = pad->move_data.accelerometer_x;
@ -3392,7 +3392,7 @@ error_code cellGemReadExternalPortDeviceInfo(u32 gem_num, vm::ptr<u32> ext_id, v
const auto handler = pad::get_pad_thread(); const auto handler = pad::get_pad_thread();
const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num)); const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num));
if (pad->m_pad_handler != pad_handler::move || !pad->is_connected() || pad->is_copilot()) if (pad->m_pad_handler != pad_handler::move || !pad->is_connected())
{ {
return CELL_GEM_NOT_CONNECTED; return CELL_GEM_NOT_CONNECTED;
} }
@ -3706,7 +3706,7 @@ error_code cellGemWriteExternalPort(u32 gem_num, vm::ptr<u8[CELL_GEM_EXTERNAL_PO
const auto handler = pad::get_pad_thread(); const auto handler = pad::get_pad_thread();
const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num)); const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num));
if (pad->m_pad_handler != pad_handler::move || !pad->is_connected() || pad->is_copilot()) if (pad->m_pad_handler != pad_handler::move || !pad->is_connected())
{ {
return CELL_GEM_NOT_CONNECTED; return CELL_GEM_NOT_CONNECTED;
} }

View file

@ -411,27 +411,60 @@ void pad_get_data(u32 port_no, CellPadData* data, bool get_periph_data = false)
} }
}; };
for (const Button& button : pad->m_buttons_external) for (const Button& button : pad->m_buttons)
{ {
// here we check btns, and set pad accordingly, // here we check btns, and set pad accordingly,
// if something changed, set btnChanged // if something changed, set btnChanged
bool pressed = button.m_pressed;
u16 value = button.m_value;
// Merge copilots
if (!pad->copilots.empty())
{
for (const auto& copilot : pad->copilots)
{
if (!copilot || !copilot->is_connected())
{
continue;
}
for (const Button& other : copilot->m_buttons)
{
if (button.m_offset == other.m_offset && button.m_outKeyCode == other.m_outKeyCode)
{
if (other.m_pressed)
{
pressed = true;
if (value < other.m_value)
{
value = other.m_value;
}
}
break;
}
}
}
}
switch (button.m_offset) switch (button.m_offset)
{ {
case CELL_PAD_BTN_OFFSET_DIGITAL1: case CELL_PAD_BTN_OFFSET_DIGITAL1:
{ {
if (button.m_pressed) if (pressed)
pad->m_digital_1 |= button.m_outKeyCode; pad->m_digital_1 |= button.m_outKeyCode;
else else
pad->m_digital_1 &= ~button.m_outKeyCode; pad->m_digital_1 &= ~button.m_outKeyCode;
switch (button.m_outKeyCode) switch (button.m_outKeyCode)
{ {
case CELL_PAD_CTRL_LEFT: set_value(pad->m_press_left, button.m_value); break; case CELL_PAD_CTRL_LEFT: set_value(pad->m_press_left, value); break;
case CELL_PAD_CTRL_DOWN: set_value(pad->m_press_down, button.m_value); break; case CELL_PAD_CTRL_DOWN: set_value(pad->m_press_down, value); break;
case CELL_PAD_CTRL_RIGHT: set_value(pad->m_press_right, button.m_value); break; case CELL_PAD_CTRL_RIGHT: set_value(pad->m_press_right, value); break;
case CELL_PAD_CTRL_UP: set_value(pad->m_press_up, button.m_value); break; case CELL_PAD_CTRL_UP: set_value(pad->m_press_up, value); break;
// These aren't pressure btns // These arent pressure btns
case CELL_PAD_CTRL_R3: case CELL_PAD_CTRL_R3:
case CELL_PAD_CTRL_L3: case CELL_PAD_CTRL_L3:
case CELL_PAD_CTRL_START: case CELL_PAD_CTRL_START:
@ -442,21 +475,21 @@ void pad_get_data(u32 port_no, CellPadData* data, bool get_periph_data = false)
} }
case CELL_PAD_BTN_OFFSET_DIGITAL2: case CELL_PAD_BTN_OFFSET_DIGITAL2:
{ {
if (button.m_pressed) if (pressed)
pad->m_digital_2 |= button.m_outKeyCode; pad->m_digital_2 |= button.m_outKeyCode;
else else
pad->m_digital_2 &= ~button.m_outKeyCode; pad->m_digital_2 &= ~button.m_outKeyCode;
switch (button.m_outKeyCode) switch (button.m_outKeyCode)
{ {
case CELL_PAD_CTRL_SQUARE: set_value(pad->m_press_square, button.m_value); break; case CELL_PAD_CTRL_SQUARE: set_value(pad->m_press_square, value); break;
case CELL_PAD_CTRL_CROSS: set_value(pad->m_press_cross, button.m_value); break; case CELL_PAD_CTRL_CROSS: set_value(pad->m_press_cross, value); break;
case CELL_PAD_CTRL_CIRCLE: set_value(pad->m_press_circle, button.m_value); break; case CELL_PAD_CTRL_CIRCLE: set_value(pad->m_press_circle, value); break;
case CELL_PAD_CTRL_TRIANGLE: set_value(pad->m_press_triangle, button.m_value); break; case CELL_PAD_CTRL_TRIANGLE: set_value(pad->m_press_triangle, value); break;
case CELL_PAD_CTRL_R1: set_value(pad->m_press_R1, button.m_value); break; case CELL_PAD_CTRL_R1: set_value(pad->m_press_R1, value); break;
case CELL_PAD_CTRL_L1: set_value(pad->m_press_L1, button.m_value); break; case CELL_PAD_CTRL_L1: set_value(pad->m_press_L1, value); break;
case CELL_PAD_CTRL_R2: set_value(pad->m_press_R2, button.m_value); break; case CELL_PAD_CTRL_R2: set_value(pad->m_press_R2, value); break;
case CELL_PAD_CTRL_L2: set_value(pad->m_press_L2, button.m_value); break; case CELL_PAD_CTRL_L2: set_value(pad->m_press_L2, value); break;
default: break; default: break;
} }
break; break;
@ -465,18 +498,18 @@ void pad_get_data(u32 port_no, CellPadData* data, bool get_periph_data = false)
{ {
switch (button.m_outKeyCode) switch (button.m_outKeyCode)
{ {
case CELL_PAD_CTRL_PRESS_RIGHT: set_value(pad->m_press_right, button.m_value, true); break; case CELL_PAD_CTRL_PRESS_RIGHT: set_value(pad->m_press_right, value, true); break;
case CELL_PAD_CTRL_PRESS_LEFT: set_value(pad->m_press_left, button.m_value, true); break; case CELL_PAD_CTRL_PRESS_LEFT: set_value(pad->m_press_left, value, true); break;
case CELL_PAD_CTRL_PRESS_UP: set_value(pad->m_press_up, button.m_value, true); break; case CELL_PAD_CTRL_PRESS_UP: set_value(pad->m_press_up, value, true); break;
case CELL_PAD_CTRL_PRESS_DOWN: set_value(pad->m_press_down, button.m_value, true); break; case CELL_PAD_CTRL_PRESS_DOWN: set_value(pad->m_press_down, value, true); break;
case CELL_PAD_CTRL_PRESS_TRIANGLE: set_value(pad->m_press_triangle, button.m_value, true, 255, 63); break; // Infrared on RIDE Skateboard case CELL_PAD_CTRL_PRESS_TRIANGLE: set_value(pad->m_press_triangle, value, true, 255, 63); break; // Infrared on RIDE Skateboard
case CELL_PAD_CTRL_PRESS_CIRCLE: set_value(pad->m_press_circle, button.m_value, true, 255, 63); break; // Infrared on RIDE Skateboard case CELL_PAD_CTRL_PRESS_CIRCLE: set_value(pad->m_press_circle, value, true, 255, 63); break; // Infrared on RIDE Skateboard
case CELL_PAD_CTRL_PRESS_CROSS: set_value(pad->m_press_cross, button.m_value, true, 255, 63); break; // Infrared on RIDE Skateboard case CELL_PAD_CTRL_PRESS_CROSS: set_value(pad->m_press_cross, value, true, 255, 63); break; // Infrared on RIDE Skateboard
case CELL_PAD_CTRL_PRESS_SQUARE: set_value(pad->m_press_square, button.m_value, true, 255, 63); break; // Infrared on RIDE Skateboard case CELL_PAD_CTRL_PRESS_SQUARE: set_value(pad->m_press_square, value, true, 255, 63); break; // Infrared on RIDE Skateboard
case CELL_PAD_CTRL_PRESS_L1: set_value(pad->m_press_L1, button.m_value, true); break; case CELL_PAD_CTRL_PRESS_L1: set_value(pad->m_press_L1, value, true); break;
case CELL_PAD_CTRL_PRESS_R1: set_value(pad->m_press_R1, button.m_value, true); break; case CELL_PAD_CTRL_PRESS_R1: set_value(pad->m_press_R1, value, true); break;
case CELL_PAD_CTRL_PRESS_L2: set_value(pad->m_press_L2, button.m_value, true); break; case CELL_PAD_CTRL_PRESS_L2: set_value(pad->m_press_L2, value, true); break;
case CELL_PAD_CTRL_PRESS_R2: set_value(pad->m_press_R2, button.m_value, true); break; case CELL_PAD_CTRL_PRESS_R2: set_value(pad->m_press_R2, value, true); break;
default: break; default: break;
} }
break; break;
@ -486,14 +519,46 @@ void pad_get_data(u32 port_no, CellPadData* data, bool get_periph_data = false)
} }
} }
for (const AnalogStick& stick : pad->m_sticks_external) for (const AnalogStick& stick : pad->m_sticks)
{ {
u16 value = stick.m_value;
// Merge copilots
if (!pad->copilots.empty())
{
const auto normalize = [](s32 value)
{
return (value - 128) / 127.0f;
};
f32 accumulated_value = normalize(value);
for (const auto& copilot : pad->copilots)
{
if (!copilot || !copilot->is_connected())
{
continue;
}
for (const AnalogStick& other : copilot->m_sticks)
{
if (stick.m_offset == other.m_offset)
{
accumulated_value += normalize(other.m_value);
break;
}
}
}
value = static_cast<u16>(std::round(std::clamp(accumulated_value * 127.0f + 128.0f, 0.0f, 255.0f)));
}
switch (stick.m_offset) switch (stick.m_offset)
{ {
case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X: set_value(pad->m_analog_left_x, stick.m_value); break; case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X: set_value(pad->m_analog_left_x, value); break;
case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y: set_value(pad->m_analog_left_y, stick.m_value); break; case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y: set_value(pad->m_analog_left_y, value); break;
case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X: set_value(pad->m_analog_right_x, stick.m_value); break; case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X: set_value(pad->m_analog_right_x, value); break;
case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y: set_value(pad->m_analog_right_y, stick.m_value); break; case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y: set_value(pad->m_analog_right_y, value); break;
default: break; default: break;
} }
} }
@ -1240,7 +1305,7 @@ error_code cellPadLddGetPortNo(s32 handle)
return CELL_PAD_ERROR_UNINITIALIZED; return CELL_PAD_ERROR_UNINITIALIZED;
const auto handler = pad::get_pad_thread(); const auto handler = pad::get_pad_thread();
const auto& pads = handler->GetPads(); auto& pads = handler->GetPads();
if (handle < 0 || static_cast<u32>(handle) >= pads.size()) if (handle < 0 || static_cast<u32>(handle) >= pads.size())
return CELL_PAD_ERROR_INVALID_PARAMETER; return CELL_PAD_ERROR_INVALID_PARAMETER;

View file

@ -3,6 +3,7 @@
#include "Emu/Io/pad_types.h" #include "Emu/Io/pad_types.h"
#include <array> #include <array>
#include "util/types.hpp"
enum CellPadError : u32 enum CellPadError : u32
{ {

View file

@ -700,32 +700,11 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
u32 errDialog, PSetList setList, PSetBuf setBuf, PFuncList funcList, PFuncFixed funcFixed, PFuncStat funcStat, u32 errDialog, PSetList setList, PSetBuf setBuf, PFuncList funcList, PFuncFixed funcFixed, PFuncStat funcStat,
PFuncFile funcFile, u32 container, u32 unk_op_flags /*TODO*/, vm::ptr<void> userdata, u32 userId, PFuncDone funcDone) PFuncFile funcFile, u32 container, u32 unk_op_flags /*TODO*/, vm::ptr<void> userdata, u32 userId, PFuncDone funcDone)
{ {
if (setList) if (const auto& [ok, list] = setList.try_read(); ok)
{ cellSaveData.notice("savedata_op(): setList = { .sortType=%d, .sortOrder=%d, .dirNamePrefix='%s' }", list.sortType, list.sortOrder, list.dirNamePrefix);
if (const auto& [ok, list] = setList.try_read(); ok)
{
cellSaveData.notice("savedata_op(): setList = { .sortType=%d, .sortOrder=%d, .dirNamePrefix='%s' }", list.sortType, list.sortOrder, list.dirNamePrefix);
}
else
{
cellSaveData.error("savedata_op(): Failed to read setList!");
}
}
if (setBuf) if (const auto& [ok, buf] = setBuf.try_read(); ok)
{ cellSaveData.notice("savedata_op(): setBuf = { .dirListMax=%d, .fileListMax=%d, .bufSize=%d }", buf.dirListMax, buf.fileListMax, buf.bufSize);
if (const auto& [ok, buf] = setBuf.try_read(); ok)
{
cellSaveData.notice("savedata_op(): setBuf = { .dirListMax=%d, .fileListMax=%d, .bufSize=%d }", buf.dirListMax, buf.fileListMax, buf.bufSize);
}
else
{
cellSaveData.error("savedata_op(): Failed to read setBuf!");
}
}
// There is a lot going on in this function, ensure function log and past log commands have completed for ease of debugging
logs::listener::sync_all();
if (const auto ecode = savedata_check_args(operation, version, dirName, errDialog, setList, setBuf, funcList, funcFixed, funcStat, if (const auto ecode = savedata_check_args(operation, version, dirName, errDialog, setList, setBuf, funcList, funcFixed, funcStat,
funcFile, container, unk_op_flags, userdata, userId, funcDone)) funcFile, container, unk_op_flags, userdata, userId, funcDone))
@ -879,34 +858,25 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
const u32 order = setList->sortOrder; const u32 order = setList->sortOrder;
const u32 type = setList->sortType; const u32 type = setList->sortType;
std::sort(save_entries.begin(), save_entries.end(), [order, type](const SaveDataEntry& entry1, const SaveDataEntry& entry2) -> bool std::sort(save_entries.begin(), save_entries.end(), [=](const SaveDataEntry& entry1, const SaveDataEntry& entry2)
{ {
const bool mtime_lower = entry1.mtime < entry2.mtime; if (order == CELL_SAVEDATA_SORTORDER_DESCENT && type == CELL_SAVEDATA_SORTTYPE_MODIFIEDTIME)
const bool mtime_equal = entry1.mtime == entry2.mtime;
const bool subtitle_lower = entry1.subtitle < entry2.subtitle;
const bool subtitle_equal = entry1.subtitle == entry2.subtitle;
const bool revert_order = order == CELL_SAVEDATA_SORTORDER_DESCENT;
if (type == CELL_SAVEDATA_SORTTYPE_MODIFIEDTIME)
{ {
if (mtime_equal) return entry1.mtime >= entry2.mtime;
{
return subtitle_lower != revert_order;
}
return mtime_lower != revert_order;
} }
else if (type == CELL_SAVEDATA_SORTTYPE_SUBTITLE) if (order == CELL_SAVEDATA_SORTORDER_DESCENT && type == CELL_SAVEDATA_SORTTYPE_SUBTITLE)
{ {
if (subtitle_equal) return entry1.subtitle >= entry2.subtitle;
{ }
return mtime_lower != revert_order; if (order == CELL_SAVEDATA_SORTORDER_ASCENT && type == CELL_SAVEDATA_SORTTYPE_MODIFIEDTIME)
} {
return entry1.mtime < entry2.mtime;
return subtitle_lower != revert_order; }
if (order == CELL_SAVEDATA_SORTORDER_ASCENT && type == CELL_SAVEDATA_SORTTYPE_SUBTITLE)
{
return entry1.subtitle < entry2.subtitle;
} }
ensure(false);
return true; return true;
}); });
} }

View file

@ -1220,7 +1220,7 @@ error_code cellVdecGetPictureExt(ppu_thread& ppu, u32 handle, vm::cptr<CellVdecP
if (arg4 || format->unk0 || format->unk1) if (arg4 || format->unk0 || format->unk1)
{ {
cellVdec.todo("cellVdecGetPictureExt: Unknown arguments (arg4=*0x%x, unk0=0x%x, unk1=0x%x)", arg4, format->unk0, format->unk1); fmt::throw_exception("cellVdecGetPictureExt: Unknown arguments (arg4=*0x%x, unk0=0x%x, unk1=0x%x)", arg4, format->unk0, format->unk1);
} }
vdec_frame frame; vdec_frame frame;

View file

@ -4265,7 +4265,7 @@ error_code sceNpManagerGetEntitlementById(vm::cptr<char> entId, vm::ptr<SceNpEnt
return SCE_NP_ERROR_INVALID_ARGUMENT; return SCE_NP_ERROR_INVALID_ARGUMENT;
} }
return SCE_NP_ERROR_ID_NOT_FOUND; return CELL_OK;
} }
error_code sceNpManagerGetSigninId(vm::ptr<void> signInId) error_code sceNpManagerGetSigninId(vm::ptr<void> signInId)

View file

@ -5760,7 +5760,7 @@ static void ppu_initialize2(jit_compiler& jit, const ppu_module<lv2_obj>& module
// Define some types // Define some types
const auto _func = FunctionType::get(translator.get_type<void>(), { const auto _func = FunctionType::get(translator.get_type<void>(), {
translator.get_type<u8*>(), // Exec base translator.get_type<u8*>(), // Exec base
translator.get_type<u8*>(), // PPU context translator.GetContextType()->getPointerTo(), // PPU context
translator.get_type<u64>(), // Segment address (for PRX) translator.get_type<u64>(), // Segment address (for PRX)
translator.get_type<u8*>(), // Memory base translator.get_type<u8*>(), // Memory base
translator.get_type<u64>(), // r0 translator.get_type<u64>(), // r0

View file

@ -340,7 +340,7 @@ Function* PPUTranslator::GetSymbolResolver(const ppu_module<lv2_obj>& info)
const auto ftype = FunctionType::get(get_type<void>(), { const auto ftype = FunctionType::get(get_type<void>(), {
get_type<u8*>(), // Exec base get_type<u8*>(), // Exec base
m_ir->getPtrTy(), // PPU context GetContextType()->getPointerTo(), // PPU context
get_type<u64>(), // Segment address (for PRX) get_type<u64>(), // Segment address (for PRX)
get_type<u8*>(), // Memory base get_type<u8*>(), // Memory base
get_type<u64>(), // r0 get_type<u64>(), // r0
@ -380,7 +380,7 @@ Function* PPUTranslator::GetSymbolResolver(const ppu_module<lv2_obj>& info)
const auto addr_array = new GlobalVariable(*m_module, addr_array_type, false, GlobalValue::PrivateLinkage, ConstantDataArray::get(m_context, vec_addrs)); const auto addr_array = new GlobalVariable(*m_module, addr_array_type, false, GlobalValue::PrivateLinkage, ConstantDataArray::get(m_context, vec_addrs));
// Create an array of function pointers // Create an array of function pointers
const auto func_table_type = ArrayType::get(m_ir->getPtrTy(), functions.size()); const auto func_table_type = ArrayType::get(ftype->getPointerTo(), functions.size());
const auto init_func_table = ConstantArray::get(func_table_type, functions); const auto init_func_table = ConstantArray::get(func_table_type, functions);
const auto func_table = new GlobalVariable(*m_module, func_table_type, false, GlobalVariable::PrivateLinkage, init_func_table); const auto func_table = new GlobalVariable(*m_module, func_table_type, false, GlobalVariable::PrivateLinkage, init_func_table);
@ -407,7 +407,7 @@ Function* PPUTranslator::GetSymbolResolver(const ppu_module<lv2_obj>& info)
const auto func_pc = ZExt(m_ir->CreateLoad(ptr_inst->getResultElementType(), ptr_inst), get_type<u64>()); const auto func_pc = ZExt(m_ir->CreateLoad(ptr_inst->getResultElementType(), ptr_inst), get_type<u64>());
ptr_inst = dyn_cast<GetElementPtrInst>(m_ir->CreateGEP(func_table->getValueType(), func_table, {m_ir->getInt64(0), index_value})); ptr_inst = dyn_cast<GetElementPtrInst>(m_ir->CreateGEP(func_table->getValueType(), func_table, {m_ir->getInt64(0), index_value}));
assert(ptr_inst->getResultElementType() == m_ir->getPtrTy()); assert(ptr_inst->getResultElementType() == ftype->getPointerTo());
const auto faddr = m_ir->CreateLoad(ptr_inst->getResultElementType(), ptr_inst); const auto faddr = m_ir->CreateLoad(ptr_inst->getResultElementType(), ptr_inst);
const auto faddr_int = m_ir->CreatePtrToInt(faddr, get_type<uptr>()); const auto faddr_int = m_ir->CreatePtrToInt(faddr, get_type<uptr>());
@ -612,7 +612,7 @@ void PPUTranslator::CallFunction(u64 target, Value* indirect)
const auto pos = m_ir->CreateShl(indirect, 1); const auto pos = m_ir->CreateShl(indirect, 1);
const auto ptr = dyn_cast<GetElementPtrInst>(m_ir->CreateGEP(get_type<u8>(), m_exec, pos)); const auto ptr = dyn_cast<GetElementPtrInst>(m_ir->CreateGEP(get_type<u8>(), m_exec, pos));
const auto val = m_ir->CreateLoad(get_type<u64>(), ptr); const auto val = m_ir->CreateLoad(get_type<u64>(), ptr);
callee = FunctionCallee(type, m_ir->CreateIntToPtr(val, m_ir->getPtrTy())); callee = FunctionCallee(type, m_ir->CreateIntToPtr(val, type->getPointerTo()));
// Load new segment address // Load new segment address
const auto seg_base_ptr = m_ir->CreateIntToPtr(m_ir->CreateAdd( const auto seg_base_ptr = m_ir->CreateIntToPtr(m_ir->CreateAdd(
@ -2794,8 +2794,8 @@ void PPUTranslator::MFOCRF(ppu_opcode_t op)
else if (std::none_of(m_cr + 0, m_cr + 32, [](auto* p) { return p; })) else if (std::none_of(m_cr + 0, m_cr + 32, [](auto* p) { return p; }))
{ {
// MFCR (optimized) // MFCR (optimized)
Value* ln0 = m_ir->CreateIntToPtr(m_ir->CreatePtrToInt(m_ir->CreateStructGEP(m_thread_type, m_thread, 99), GetType<uptr>()), m_ir->getPtrTy()); Value* ln0 = m_ir->CreateIntToPtr(m_ir->CreatePtrToInt(m_ir->CreateStructGEP(m_thread_type, m_thread, 99), GetType<uptr>()), GetType<u8[16]>()->getPointerTo());
Value* ln1 = m_ir->CreateIntToPtr(m_ir->CreatePtrToInt(m_ir->CreateStructGEP(m_thread_type, m_thread, 115), GetType<uptr>()), m_ir->getPtrTy()); Value* ln1 = m_ir->CreateIntToPtr(m_ir->CreatePtrToInt(m_ir->CreateStructGEP(m_thread_type, m_thread, 115), GetType<uptr>()), GetType<u8[16]>()->getPointerTo());
ln0 = m_ir->CreateLoad(GetType<u8[16]>(), ln0); ln0 = m_ir->CreateLoad(GetType<u8[16]>(), ln0);
ln1 = m_ir->CreateLoad(GetType<u8[16]>(), ln1); ln1 = m_ir->CreateLoad(GetType<u8[16]>(), ln1);
@ -5384,7 +5384,7 @@ MDNode* PPUTranslator::CheckBranchProbability(u32 bo)
void PPUTranslator::build_interpreter() void PPUTranslator::build_interpreter()
{ {
#define BUILD_VEC_INST(i) { \ #define BUILD_VEC_INST(i) { \
m_function = llvm::cast<llvm::Function>(m_module->getOrInsertFunction("op_" #i, get_type<void>(), m_ir->getPtrTy()).getCallee()); \ m_function = llvm::cast<llvm::Function>(m_module->getOrInsertFunction("op_" #i, get_type<void>(), m_thread_type->getPointerTo()).getCallee()); \
std::fill(std::begin(m_globals), std::end(m_globals), nullptr); \ std::fill(std::begin(m_globals), std::end(m_globals), nullptr); \
std::fill(std::begin(m_locals), std::end(m_locals), nullptr); \ std::fill(std::begin(m_locals), std::end(m_locals), nullptr); \
IRBuilder<> irb(BasicBlock::Create(m_context, "__entry", m_function)); \ IRBuilder<> irb(BasicBlock::Create(m_context, "__entry", m_function)); \

View file

@ -130,18 +130,6 @@ bool spu_thread::read_reg(const u32 addr, u32& value)
return true; return true;
} }
case Prxy_QueryMask_offs:
{
value = mfc_prxy_mask;
return true;
}
case Prxy_QueryType_offs:
{
value = 0;
return true;
}
case SPU_Out_MBox_offs: case SPU_Out_MBox_offs:
{ {
value = ch_out_mbox.pop(); value = ch_out_mbox.pop();
@ -150,29 +138,7 @@ bool spu_thread::read_reg(const u32 addr, u32& value)
case SPU_MBox_Status_offs: case SPU_MBox_Status_offs:
{ {
// Load channel counts atomically value = (ch_out_mbox.get_count() & 0xff) | ((4 - ch_in_mbox.get_count()) << 8 & 0xff00) | (ch_out_intr_mbox.get_count() << 16 & 0xff0000);
auto counts = std::make_tuple(ch_out_mbox.get_count(), ch_in_mbox.get_count(), ch_out_intr_mbox.get_count());
while (true)
{
atomic_fence_acquire();
const auto counts_check = std::make_tuple(ch_out_mbox.get_count(), ch_in_mbox.get_count(), ch_out_intr_mbox.get_count());
if (counts_check == counts)
{
break;
}
// Update and reload
counts = counts_check;
}
const u32 out_mbox = std::get<0>(counts);
const u32 in_mbox = 4 - std::get<1>(counts);
const u32 intr_mbox = std::get<2>(counts);
value = (out_mbox & 0xff) | ((in_mbox << 8) & 0xff00) | ((intr_mbox << 16) & 0xff0000);
return true; return true;
} }
@ -379,8 +345,6 @@ bool spu_thread::test_is_problem_state_register_offset(u32 offset, bool for_read
case MFC_QStatus_offs: case MFC_QStatus_offs:
case SPU_Out_MBox_offs: case SPU_Out_MBox_offs:
case SPU_MBox_Status_offs: case SPU_MBox_Status_offs:
case Prxy_QueryType_offs:
case Prxy_QueryMask_offs:
case SPU_Status_offs: case SPU_Status_offs:
case Prxy_TagStatus_offs: case Prxy_TagStatus_offs:
case SPU_NPC_offs: case SPU_NPC_offs:

View file

@ -2886,7 +2886,7 @@ public:
// Create interpreter table // Create interpreter table
const auto if_type = get_ftype<void, u8*, u8*, u32, u32, u8*, u32, u8*>(); const auto if_type = get_ftype<void, u8*, u8*, u32, u32, u8*, u32, u8*>();
m_function_table = new GlobalVariable(*m_module, ArrayType::get(m_ir->getPtrTy(), 1ull << m_interp_magn), true, GlobalValue::InternalLinkage, nullptr); m_function_table = new GlobalVariable(*m_module, ArrayType::get(if_type->getPointerTo(), 1ull << m_interp_magn), true, GlobalValue::InternalLinkage, nullptr);
init_luts(); init_luts();
@ -2930,7 +2930,7 @@ public:
m_ir->CreateStore(m_ir->CreateCall(get_intrinsic<u64>(Intrinsic::read_register), {rsp_name}), native_sp); m_ir->CreateStore(m_ir->CreateCall(get_intrinsic<u64>(Intrinsic::read_register), {rsp_name}), native_sp);
// Decode (shift) and load function pointer // Decode (shift) and load function pointer
const auto first = m_ir->CreateLoad(m_ir->getPtrTy(), m_ir->CreateGEP(m_ir->getPtrTy(), m_interp_table, m_ir->CreateLShr(m_interp_op, 32u - m_interp_magn))); const auto first = m_ir->CreateLoad(if_type->getPointerTo(), m_ir->CreateGEP(if_type->getPointerTo(), m_interp_table, m_ir->CreateLShr(m_interp_op, 32u - m_interp_magn)));
const auto call0 = m_ir->CreateCall(if_type, first, {m_lsptr, m_thread, m_interp_pc, m_interp_op, m_interp_table, m_interp_7f0, m_interp_regs}); const auto call0 = m_ir->CreateCall(if_type, first, {m_lsptr, m_thread, m_interp_pc, m_interp_op, m_interp_table, m_interp_7f0, m_interp_regs});
call0->setCallingConv(CallingConv::GHC); call0->setCallingConv(CallingConv::GHC);
m_ir->CreateRetVoid(); m_ir->CreateRetVoid();
@ -3074,7 +3074,7 @@ public:
const auto next_pc = itype & spu_itype::branch ? m_interp_pc : m_interp_pc_next; const auto next_pc = itype & spu_itype::branch ? m_interp_pc : m_interp_pc_next;
const auto be32_op = m_ir->CreateLoad(get_type<u32>(), m_ir->CreateGEP(get_type<u8>(), m_lsptr, m_ir->CreateZExt(next_pc, get_type<u64>()))); const auto be32_op = m_ir->CreateLoad(get_type<u32>(), m_ir->CreateGEP(get_type<u8>(), m_lsptr, m_ir->CreateZExt(next_pc, get_type<u64>())));
const auto next_op = m_ir->CreateCall(get_intrinsic<u32>(Intrinsic::bswap), {be32_op}); const auto next_op = m_ir->CreateCall(get_intrinsic<u32>(Intrinsic::bswap), {be32_op});
const auto next_if = m_ir->CreateLoad(m_ir->getPtrTy(), m_ir->CreateGEP(m_ir->getPtrTy(), m_interp_table, m_ir->CreateLShr(next_op, 32u - m_interp_magn))); const auto next_if = m_ir->CreateLoad(if_type->getPointerTo(), m_ir->CreateGEP(if_type->getPointerTo(), m_interp_table, m_ir->CreateLShr(next_op, 32u - m_interp_magn)));
llvm::cast<LoadInst>(next_if)->setVolatile(true); llvm::cast<LoadInst>(next_if)->setVolatile(true);
if (!(itype & spu_itype::branch)) if (!(itype & spu_itype::branch))
@ -3199,7 +3199,7 @@ public:
} }
} }
m_function_table->setInitializer(ConstantArray::get(ArrayType::get(m_ir->getPtrTy(), 1ull << m_interp_magn), iptrs)); m_function_table->setInitializer(ConstantArray::get(ArrayType::get(if_type->getPointerTo(), 1ull << m_interp_magn), iptrs));
m_function_table = nullptr; m_function_table = nullptr;
for (auto& f : *_module) for (auto& f : *_module)
@ -7808,7 +7808,7 @@ public:
m_ir->CreateStore(splat<u64[2]>(-1).eval(m_ir), m_ir->CreateGEP(get_type<u8>(), m_thread, stack0.value)); m_ir->CreateStore(splat<u64[2]>(-1).eval(m_ir), m_ir->CreateGEP(get_type<u8>(), m_thread, stack0.value));
const auto targ = m_ir->CreateAdd(m_ir->CreateLShr(_ret, 32), get_segment_base()); const auto targ = m_ir->CreateAdd(m_ir->CreateLShr(_ret, 32), get_segment_base());
const auto type = m_finfo->chunk->getFunctionType(); const auto type = m_finfo->chunk->getFunctionType();
const auto fval = m_ir->CreateIntToPtr(targ, m_ir->getPtrTy()); const auto fval = m_ir->CreateIntToPtr(targ, type->getPointerTo());
tail_chunk({type, fval}, m_ir->CreateTrunc(m_ir->CreateLShr(link, 32), get_type<u32>())); tail_chunk({type, fval}, m_ir->CreateTrunc(m_ir->CreateLShr(link, 32), get_type<u32>()));
m_ir->SetInsertPoint(fail); m_ir->SetInsertPoint(fail);
} }

View file

@ -4047,22 +4047,6 @@ void do_cell_atomic_128_store(u32 addr, const void* to_write)
auto& sdata = *vm::get_super_ptr<spu_rdata_t>(addr); auto& sdata = *vm::get_super_ptr<spu_rdata_t>(addr);
auto& res = *utils::bless<atomic_t<u128>>(vm::g_reservations + (addr & 0xff80) / 2); auto& res = *utils::bless<atomic_t<u128>>(vm::g_reservations + (addr & 0xff80) / 2);
if (std::memcmp(static_cast<const u8*>(to_write), &sdata, 16) == 0 && std::memcmp(static_cast<const u8*>(to_write) + 64, &sdata[64], 16) == 0)
{
const auto& write_data = *static_cast<const spu_rdata_t*>(to_write);
const u64 at_read_time = vm::reservation_acquire(addr);
if (!(at_read_time & 127))
{
if (cmp_rdata(sdata, write_data) && at_read_time == vm::reservation_acquire(addr) && cmp_rdata(sdata, write_data))
{
// Write of the same data (verified atomically)
vm::try_reservation_update(addr);
return;
}
}
}
for (u64 j = 0;; j++) for (u64 j = 0;; j++)
{ {
auto [_oldd, _ok] = res.fetch_op([&](u128& r) auto [_oldd, _ok] = res.fetch_op([&](u128& r)

View file

@ -285,10 +285,8 @@ private:
libusb_context* ctx = nullptr; libusb_context* ctx = nullptr;
#ifndef _WIN32
#if LIBUSB_API_VERSION >= 0x01000102 #if LIBUSB_API_VERSION >= 0x01000102
libusb_hotplug_callback_handle callback_handle {}; libusb_hotplug_callback_handle callback_handle {};
#endif
#endif #endif
bool hotplug_supported = false; bool hotplug_supported = false;
@ -304,7 +302,6 @@ void LIBUSB_CALL callback_transfer(struct libusb_transfer* transfer)
usbh.transfer_complete(transfer); usbh.transfer_complete(transfer);
} }
#ifndef _WIN32
#if LIBUSB_API_VERSION >= 0x01000102 #if LIBUSB_API_VERSION >= 0x01000102
static int LIBUSB_CALL hotplug_callback(libusb_context* /*ctx*/, libusb_device * /*dev*/, libusb_hotplug_event event, void * /*user_data*/) static int LIBUSB_CALL hotplug_callback(libusb_context* /*ctx*/, libusb_device * /*dev*/, libusb_hotplug_event event, void * /*user_data*/)
{ {
@ -312,7 +309,6 @@ static int LIBUSB_CALL hotplug_callback(libusb_context* /*ctx*/, libusb_device *
return 0; return 0;
} }
#endif #endif
#endif
#if LIBUSB_API_VERSION >= 0x0100010A #if LIBUSB_API_VERSION >= 0x0100010A
static void LIBUSB_CALL log_cb(libusb_context* /*ctx*/, enum libusb_log_level level, const char* str) static void LIBUSB_CALL log_cb(libusb_context* /*ctx*/, enum libusb_log_level level, const char* str)
@ -601,11 +597,9 @@ usb_handler_thread::~usb_handler_thread()
libusb_free_transfer(transfers[index].transfer); libusb_free_transfer(transfers[index].transfer);
} }
#ifndef _WIN32
#if LIBUSB_API_VERSION >= 0x01000102 #if LIBUSB_API_VERSION >= 0x01000102
if (ctx && hotplug_supported) if (ctx && hotplug_supported)
libusb_hotplug_deregister_callback(ctx, callback_handle); libusb_hotplug_deregister_callback(ctx, callback_handle);
#endif
#endif #endif
if (ctx) if (ctx)

View file

@ -173,7 +173,7 @@ void usb_device_buzz::interrupt_transfer(u32 buf_size, u8* buf, u32 /*endpoint*/
{ {
const auto& pad = pads[i]; const auto& pad = pads[i];
if (!pad->is_connected() || pad->is_copilot()) if (!pad->is_connected())
{ {
continue; continue;
} }

View file

@ -152,7 +152,7 @@ void usb_device_ghltar::interrupt_transfer(u32 buf_size, u8* buf, u32 /*endpoint
const auto handler = pad::get_pad_thread(); const auto handler = pad::get_pad_thread();
const auto& pad = ::at32(handler->GetPads(), m_controller_index); const auto& pad = ::at32(handler->GetPads(), m_controller_index);
if (!pad->is_connected() || pad->is_copilot()) if (!pad->is_connected())
{ {
return; return;
} }

View file

@ -198,9 +198,9 @@ void usb_device_gametablet::interrupt_transfer(u32 buf_size, u8* buf, u32 /*endp
const auto gamepad_handler = pad::get_pad_thread(); const auto gamepad_handler = pad::get_pad_thread();
const auto& pads = gamepad_handler->GetPads(); const auto& pads = gamepad_handler->GetPads();
const auto& pad = ::at32(pads, m_controller_index); const auto& pad = ::at32(pads, m_controller_index);
if (pad->is_connected() && !pad->is_copilot()) if (pad->is_connected())
{ {
for (Button& button : pad->m_buttons_external) for (Button& button : pad->m_buttons)
{ {
if (!button.m_pressed) if (!button.m_pressed)
{ {

View file

@ -258,7 +258,7 @@ void usb_device_guncon3::interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint,
const auto gamepad_handler = pad::get_pad_thread(); const auto gamepad_handler = pad::get_pad_thread();
const auto& pads = gamepad_handler->GetPads(); const auto& pads = gamepad_handler->GetPads();
const auto& pad = ::at32(pads, m_controller_index); const auto& pad = ::at32(pads, m_controller_index);
if (pad->is_connected() && !pad->is_copilot()) if (pad->is_connected())
{ {
cfg->handle_input(pad, true, input_callback); cfg->handle_input(pad, true, input_callback);
} }

View file

@ -318,7 +318,7 @@ void usb_device_topshotelite::interrupt_transfer(u32 buf_size, u8* buf, u32 /*en
const auto gamepad_handler = pad::get_pad_thread(); const auto gamepad_handler = pad::get_pad_thread();
const auto& pads = gamepad_handler->GetPads(); const auto& pads = gamepad_handler->GetPads();
const auto& pad = ::at32(pads, m_controller_index); const auto& pad = ::at32(pads, m_controller_index);
if (pad->is_connected() && !pad->is_copilot()) if (pad->is_connected())
{ {
cfg->handle_input(pad, true, input_callback); cfg->handle_input(pad, true, input_callback);
} }

View file

@ -342,7 +342,7 @@ void usb_device_topshotfearmaster::interrupt_transfer(u32 buf_size, u8* buf, u32
const auto gamepad_handler = pad::get_pad_thread(); const auto gamepad_handler = pad::get_pad_thread();
const auto& pads = gamepad_handler->GetPads(); const auto& pads = gamepad_handler->GetPads();
const auto& pad = ::at32(pads, m_controller_index); const auto& pad = ::at32(pads, m_controller_index);
if (pad->is_connected() && !pad->is_copilot()) if (pad->is_connected())
{ {
cfg->handle_input(pad, true, input_callback); cfg->handle_input(pad, true, input_callback);
} }

View file

@ -166,7 +166,7 @@ void usb_device_turntable::interrupt_transfer(u32 buf_size, u8* buf, u32 /*endpo
const auto& pads = handler->GetPads(); const auto& pads = handler->GetPads();
const auto& pad = ::at32(pads, m_controller_index); const auto& pad = ::at32(pads, m_controller_index);
if (!pad->is_connected() || pad->is_copilot()) if (!pad->is_connected())
return; return;
const auto& cfg = ::at32(g_cfg_turntable.players, m_controller_index); const auto& cfg = ::at32(g_cfg_turntable.players, m_controller_index);

View file

@ -90,10 +90,10 @@ public:
void handle_input(std::shared_ptr<Pad> pad, bool press_only, const std::function<void(T, pad_button, u16, bool, bool&)>& func) const void handle_input(std::shared_ptr<Pad> pad, bool press_only, const std::function<void(T, pad_button, u16, bool, bool&)>& func) const
{ {
if (!pad || pad->is_copilot()) if (!pad)
return; return;
for (const Button& button : pad->m_buttons_external) for (const Button& button : pad->m_buttons)
{ {
if (button.m_pressed || !press_only) if (button.m_pressed || !press_only)
{ {
@ -104,7 +104,7 @@ public:
} }
} }
for (const AnalogStick& stick : pad->m_sticks_external) for (const AnalogStick& stick : pad->m_sticks)
{ {
if (handle_input(func, stick.m_offset, get_axis_keycode(stick.m_offset, stick.m_value), stick.m_value, true, true)) if (handle_input(func, stick.m_offset, get_axis_keycode(stick.m_offset, stick.m_value), stick.m_value, true, true))
{ {

View file

@ -56,7 +56,7 @@ bool event_is_number(const char* s)
} }
// compare /dev/input/eventX and /dev/input/eventY where X and Y are numbers // compare /dev/input/eventX and /dev/input/eventY where X and Y are numbers
bool event_cmp_less_than(const char* x, const char* y) int event_strcmp_events(const char* x, const char* y)
{ {
// find a common string // find a common string
int n = 0; int n = 0;
@ -71,10 +71,14 @@ bool event_cmp_less_than(const char* x, const char* y)
const int a = atoi(x + n); const int a = atoi(x + n);
const int b = atoi(y + n); const int b = atoi(y + n);
return a < b; if (a == b)
return 0;
if (a < b)
return -1;
return 1;
} }
return strcmp(x, y) < 0; return strcmp(x, y);
} }
evdev_gun_handler::evdev_gun_handler() evdev_gun_handler::evdev_gun_handler()
@ -239,7 +243,7 @@ bool evdev_gun_handler::init()
// Sort the udev entries by devnode name so that they are created in the proper order // Sort the udev entries by devnode name so that they are created in the proper order
std::sort(sorted_devices.begin(), sorted_devices.end(), [](const event_udev_entry& a, const event_udev_entry& b) std::sort(sorted_devices.begin(), sorted_devices.end(), [](const event_udev_entry& a, const event_udev_entry& b)
{ {
return event_cmp_less_than(a.devnode, b.devnode); return event_strcmp_events(a.devnode, b.devnode);
}); });
for (const event_udev_entry& entry : sorted_devices) for (const event_udev_entry& entry : sorted_devices)

View file

@ -379,22 +379,20 @@ enum special_button_value
struct Button struct Button
{ {
u32 m_offset = 0; u32 m_offset = 0;
std::set<u32> m_key_codes{};
u32 m_outKeyCode = 0; u32 m_outKeyCode = 0;
u16 m_value = 0; u16 m_value = 0;
bool m_pressed = false; bool m_pressed = false;
std::set<u32> m_key_codes{};
u16 m_actual_value = 0; // only used in keyboard_pad_handler u16 m_actual_value = 0; // only used in keyboard_pad_handler
bool m_analog = false; // only used in keyboard_pad_handler bool m_analog = false; // only used in keyboard_pad_handler
bool m_trigger = false; // only used in keyboard_pad_handler bool m_trigger = false; // only used in keyboard_pad_handler
std::map<u32, u16> m_pressed_keys{}; // only used in keyboard_pad_handler std::map<u32, u16> m_pressed_keys{}; // only used in keyboard_pad_handler
Button(){}
Button(u32 offset, std::set<u32> key_codes, u32 outKeyCode) Button(u32 offset, std::set<u32> key_codes, u32 outKeyCode)
: m_offset(offset) : m_offset(offset)
, m_outKeyCode(outKeyCode)
, m_key_codes(std::move(key_codes)) , m_key_codes(std::move(key_codes))
, m_outKeyCode(outKeyCode)
{ {
if (offset == CELL_PAD_BTN_OFFSET_DIGITAL1) if (offset == CELL_PAD_BTN_OFFSET_DIGITAL1)
{ {
@ -421,10 +419,9 @@ struct Button
struct AnalogStick struct AnalogStick
{ {
u32 m_offset = 0; u32 m_offset = 0;
u16 m_value = 128;
std::set<u32> m_key_codes_min{}; std::set<u32> m_key_codes_min{};
std::set<u32> m_key_codes_max{}; std::set<u32> m_key_codes_max{};
u16 m_value = 128;
std::map<u32, u16> m_pressed_keys_min{}; // only used in keyboard_pad_handler std::map<u32, u16> m_pressed_keys_min{}; // only used in keyboard_pad_handler
std::map<u32, u16> m_pressed_keys_max{}; // only used in keyboard_pad_handler std::map<u32, u16> m_pressed_keys_max{}; // only used in keyboard_pad_handler
@ -546,9 +543,6 @@ struct Pad
std::array<AnalogSensor, 4> m_sensors{}; std::array<AnalogSensor, 4> m_sensors{};
std::array<VibrateMotor, 2> m_vibrateMotors{}; std::array<VibrateMotor, 2> m_vibrateMotors{};
std::vector<Button> m_buttons_external;
std::array<AnalogStick, 4> m_sticks_external{};
std::vector<std::shared_ptr<Pad>> copilots; std::vector<std::shared_ptr<Pad>> copilots;
// These hold bits for their respective buttons // These hold bits for their respective buttons
@ -618,12 +612,6 @@ struct Pad
return umax; return umax;
} }
bool is_copilot() const
{
const u32 copilot_player_id = copilot_player();
return copilot_player_id != umax && copilot_player_id != m_player_id;
}
bool is_connected() const bool is_connected() const
{ {
return !!(m_port_status & CELL_PAD_STATUS_CONNECTED); return !!(m_port_status & CELL_PAD_STATUS_CONNECTED);

View file

@ -210,7 +210,7 @@ void usb_device_usio::translate_input_taiko()
const usz offset = player * 8ULL; const usz offset = player * 8ULL;
auto& status = m_io_status[0]; auto& status = m_io_status[0];
if (const auto& pad = ::at32(handler->GetPads(), pad_number); pad->is_connected() && !pad->is_copilot() && is_input_allowed()) if (const auto& pad = ::at32(handler->GetPads(), pad_number); pad->is_connected() && is_input_allowed())
{ {
const auto& cfg = ::at32(g_cfg_usio.players, pad_number); const auto& cfg = ::at32(g_cfg_usio.players, pad_number);
cfg->handle_input(pad, false, [&](usio_btn btn, pad_button /*pad_btn*/, u16 /*value*/, bool pressed, bool& /*abort*/) cfg->handle_input(pad, false, [&](usio_btn btn, pad_button /*pad_btn*/, u16 /*value*/, bool pressed, bool& /*abort*/)
@ -295,7 +295,7 @@ void usb_device_usio::translate_input_tekken()
auto& status = m_io_status[player / 2]; auto& status = m_io_status[player / 2];
auto& input = digital_input[player / 2]; auto& input = digital_input[player / 2];
if (const auto& pad = ::at32(handler->GetPads(), pad_number); pad->is_connected() && !pad->is_copilot() && is_input_allowed()) if (const auto& pad = ::at32(handler->GetPads(), pad_number); pad->is_connected() && is_input_allowed())
{ {
const auto& cfg = ::at32(g_cfg_usio.players, pad_number); const auto& cfg = ::at32(g_cfg_usio.players, pad_number);
cfg->handle_input(pad, false, [&](usio_btn btn, pad_button /*pad_btn*/, u16 /*value*/, bool pressed, bool& /*abort*/) cfg->handle_input(pad, false, [&](usio_btn btn, pad_button /*pad_btn*/, u16 /*value*/, bool pressed, bool& /*abort*/)

View file

@ -35,7 +35,6 @@ namespace vm
// Update reservation status // Update reservation status
void reservation_update(u32 addr); void reservation_update(u32 addr);
std::pair<bool, u64> try_reservation_update(u32 addr);
struct reservation_waiter_t struct reservation_waiter_t
{ {
@ -82,8 +81,7 @@ namespace vm
static inline atomic_t<reservation_waiter_t>* reservation_notifier_begin_wait(u32 raddr, u64 rtime) static inline atomic_t<reservation_waiter_t>* reservation_notifier_begin_wait(u32 raddr, u64 rtime)
{ {
const auto notifiers = reservation_notifier(raddr); atomic_t<reservation_waiter_t>& waiter = *reservation_notifier(raddr).first;
atomic_t<reservation_waiter_t>& waiter = *notifiers.first;
waiter.atomic_op([](reservation_waiter_t& value) waiter.atomic_op([](reservation_waiter_t& value)
{ {

View file

@ -59,7 +59,7 @@ namespace np
u32 last_free = 0; u32 last_free = 0;
bool found_space = false; bool found_space = false;
for (const auto& a : m_allocs) for (auto& a : m_allocs)
{ {
if ((a.first - last_free) >= alloc_size) if ((a.first - last_free) >= alloc_size)
{ {

View file

@ -23,8 +23,8 @@
#include <iphlpapi.h> #include <iphlpapi.h>
#else #else
#ifdef __clang__ #ifdef __clang__
#pragma clang diagnostic push #pragma GCC diagnostic push
#pragma clang diagnostic ignored "-Wold-style-cast" #pragma GCC diagnostic ignored "-Wold-style-cast"
#endif #endif
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
@ -34,7 +34,7 @@
#include <netdb.h> #include <netdb.h>
#include <unistd.h> #include <unistd.h>
#ifdef __clang__ #ifdef __clang__
#pragma clang diagnostic pop #pragma GCC diagnostic pop
#endif #endif
#endif #endif

View file

@ -23,8 +23,8 @@
#include <WS2tcpip.h> #include <WS2tcpip.h>
#else #else
#ifdef __clang__ #ifdef __clang__
#pragma clang diagnostic push #pragma GCC diagnostic push
#pragma clang diagnostic ignored "-Wold-style-cast" #pragma GCC diagnostic ignored "-Wold-style-cast"
#endif #endif
#include <sys/time.h> #include <sys/time.h>
#include <sys/types.h> #include <sys/types.h>
@ -37,7 +37,7 @@
#include <poll.h> #include <poll.h>
#include <netdb.h> #include <netdb.h>
#ifdef __clang__ #ifdef __clang__
#pragma clang diagnostic pop #pragma GCC diagnostic pop
#endif #endif
#endif #endif

View file

@ -9,17 +9,16 @@
#ifdef _WIN32 #ifdef _WIN32
#include <winsock2.h> #include <winsock2.h>
#include <ws2tcpip.h>
#else #else
#ifdef __clang__ #ifdef __clang__
#pragma clang diagnostic push #pragma GCC diagnostic push
#pragma clang diagnostic ignored "-Wold-style-cast" #pragma GCC diagnostic ignored "-Wold-style-cast"
#endif #endif
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#ifdef __clang__ #ifdef __clang__
#pragma clang diagnostic pop #pragma GCC diagnostic pop
#endif #endif
#endif #endif
@ -29,13 +28,13 @@
#include <flatbuffers/flatbuffers.h> #include <flatbuffers/flatbuffers.h>
#ifdef __clang__ #ifdef __clang__
#pragma clang diagnostic push #pragma GCC diagnostic push
#pragma clang diagnostic ignored "-Wold-style-cast" #pragma GCC diagnostic ignored "-Wold-style-cast"
#pragma clang diagnostic ignored "-Wextern-c-compat" #pragma GCC diagnostic ignored "-Wextern-c-compat"
#endif #endif
#include <wolfssl/ssl.h> #include <wolfssl/ssl.h>
#ifdef __clang__ #ifdef __clang__
#pragma clang diagnostic pop #pragma GCC diagnostic pop
#endif #endif
#include "rpcn_types.h" #include "rpcn_types.h"

View file

@ -6,11 +6,6 @@
#include "util/v128.hpp" #include "util/v128.hpp"
#include "util/simd.hpp" #include "util/simd.hpp"
#if !defined(_MSC_VER)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
#endif
#if defined(ARCH_ARM64) #if defined(ARCH_ARM64)
#if !defined(_MSC_VER) #if !defined(_MSC_VER)
#pragma GCC diagnostic ignored "-Wstrict-aliasing" #pragma GCC diagnostic ignored "-Wstrict-aliasing"
@ -19,6 +14,11 @@
#include "Emu/CPU/sse2neon.h" #include "Emu/CPU/sse2neon.h"
#endif #endif
#if !defined(_MSC_VER)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
#endif
#if defined(_MSC_VER) || !defined(__SSE2__) #if defined(_MSC_VER) || !defined(__SSE2__)
#define SSE4_1_FUNC #define SSE4_1_FUNC
#define AVX2_FUNC #define AVX2_FUNC

View file

@ -285,13 +285,6 @@ namespace rsx
return pos; return pos;
} }
void operator += (const rsx::simple_array<Ty>& that)
{
const auto old_size = _size;
resize(_size + that._size);
std::memcpy(data() + old_size, that.data(), that.size_bytes());
}
void clear() void clear()
{ {
_size = 0; _size = 0;
@ -312,11 +305,6 @@ namespace rsx
return _size * sizeof(Ty); return _size * sizeof(Ty);
} }
u32 size_bytes32() const
{
return _size * sizeof(Ty);
}
u32 capacity() const u32 capacity() const
{ {
return _capacity; return _capacity;

View file

@ -678,7 +678,6 @@ namespace rsx
} }
m_ranges.insert(pos, this_range); m_ranges.insert(pos, this_range);
inserted = true;
break; break;
} }
} }

View file

@ -225,7 +225,7 @@ namespace rsx
continue; continue;
} }
if (!pad->is_connected() || pad->is_copilot()) if (!pad->is_connected())
{ {
continue; continue;
} }
@ -314,7 +314,7 @@ namespace rsx
continue; continue;
} }
for (const Button& button : pad->m_buttons_external) for (const Button& button : pad->m_buttons)
{ {
pad_button button_id = pad_button::pad_button_max_enum; pad_button button_id = pad_button::pad_button_max_enum;
if (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL1) if (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL1)
@ -391,7 +391,7 @@ namespace rsx
break; break;
} }
for (const AnalogStick& stick : pad->m_sticks_external) for (const AnalogStick& stick : pad->m_sticks)
{ {
pad_button button_id = pad_button::pad_button_max_enum; pad_button button_id = pad_button::pad_button_max_enum;
pad_button release_id = pad_button::pad_button_max_enum; pad_button release_id = pad_button::pad_button_max_enum;

View file

@ -14,8 +14,8 @@ R"(
#define SAMPLER_MODE_TEXTURE2D 3 #define SAMPLER_MODE_TEXTURE2D 3
#ifdef VULKAN #ifdef VULKAN
layout(set=0, binding=0) uniform sampler2D fs0; layout(set=0, binding=1) uniform sampler2D fs0;
layout(set=0, binding=1) uniform sampler2DArray fs1; layout(set=0, binding=2) uniform sampler2DArray fs1;
#else #else
layout(binding=31) uniform sampler2D fs0; layout(binding=31) uniform sampler2D fs0;
layout(binding=30) uniform sampler2DArray fs1; layout(binding=30) uniform sampler2DArray fs1;

View file

@ -20,9 +20,8 @@ namespace program_common
COMPILER_OPT_ENABLE_KIL = (1 << 11), COMPILER_OPT_ENABLE_KIL = (1 << 11),
COMPILER_OPT_ENABLE_STIPPLING = (1 << 12), COMPILER_OPT_ENABLE_STIPPLING = (1 << 12),
COMPILER_OPT_ENABLE_INSTANCING = (1 << 13), COMPILER_OPT_ENABLE_INSTANCING = (1 << 13),
COMPILER_OPT_ENABLE_VTX_TEXTURES = (1 << 14),
COMPILER_OPT_MAX = COMPILER_OPT_ENABLE_VTX_TEXTURES COMPILER_OPT_MAX = COMPILER_OPT_ENABLE_INSTANCING
}; };
static std::string get_vertex_interpreter() static std::string get_vertex_interpreter()

View file

@ -6,7 +6,7 @@ namespace vk
{ {
struct fence; struct fence;
enum rctrl_command : u32 // callback commands enum // callback commands
{ {
rctrl_queue_submit = 0x80000000, rctrl_queue_submit = 0x80000000,
rctrl_run_gc = 0x80000001, rctrl_run_gc = 0x80000001,

View file

@ -35,31 +35,4 @@ namespace vk
fmt::throw_exception("Unknown register name: %s", varying_register_name); fmt::throw_exception("Unknown register name: %s", varying_register_name);
} }
int get_texture_index(std::string_view name)
{
if (name.length() < 2)
{
fmt::throw_exception("Invalid texture name: '%s'", name);
}
constexpr int max_index_length = 2;
const int name_length = static_cast<int>(name.length());
std::string index;
for (int char_idx = name_length - max_index_length; char_idx < name_length; ++char_idx)
{
if (std::isdigit(name[char_idx]))
{
index += name[char_idx];
}
}
if (index.empty())
{
fmt::throw_exception("Invalid texture name: '%s'", name);
}
return std::atoi(index.c_str());
}
} }

View file

@ -6,6 +6,4 @@ namespace vk
using namespace ::glsl; using namespace ::glsl;
int get_varying_register_location(std::string_view varying_register_name); int get_varying_register_location(std::string_view varying_register_name);
int get_texture_index(std::string_view name);
} }

View file

@ -8,6 +8,157 @@
namespace vk namespace vk
{ {
rsx::simple_array<VkDescriptorSetLayoutBinding> get_common_binding_table()
{
const auto& binding_table = vk::get_current_renderer()->get_pipeline_binding_table();
rsx::simple_array<VkDescriptorSetLayoutBinding> bindings(binding_table.instancing_constants_buffer_slot + 1);
u32 idx = 0;
// Vertex stream, one stream for cacheable data, one stream for transient data
for (int i = 0; i < 3; i++)
{
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[idx].binding = binding_table.vertex_buffers_first_bind_slot + i;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
}
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.fragment_constant_buffers_bind_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.fragment_state_bind_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.fragment_texture_params_bind_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[idx].binding = binding_table.vertex_constant_buffers_bind_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS;
bindings[idx].binding = binding_table.vertex_params_bind_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[idx].binding = binding_table.conditional_render_predicate_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.rasterizer_env_bind_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[idx].binding = binding_table.instancing_lookup_table_bind_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[idx].binding = binding_table.instancing_constants_buffer_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
return bindings;
}
std::tuple<VkPipelineLayout, VkDescriptorSetLayout, rsx::simple_array<VkDescriptorSetLayoutBinding>>
get_common_pipeline_layout(VkDevice dev)
{
const auto& binding_table = vk::get_current_renderer()->get_pipeline_binding_table();
auto bindings = get_common_binding_table();
u32 idx = ::size32(bindings);
bindings.resize(binding_table.total_descriptor_bindings);
for (auto binding = binding_table.textures_first_bind_slot;
binding < binding_table.vertex_textures_first_bind_slot;
binding++)
{
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
}
for (int i = 0; i < rsx::limits::vertex_textures_count; i++)
{
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[idx].binding = binding_table.vertex_textures_first_bind_slot + i;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
}
ensure(idx == binding_table.total_descriptor_bindings);
std::array<VkPushConstantRange, 1> push_constants;
push_constants[0].offset = 0;
push_constants[0].size = 20;
push_constants[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
if (vk::emulate_conditional_rendering())
{
// Conditional render toggle
push_constants[0].size = 24;
}
const auto set_layout = vk::descriptors::create_layout(bindings);
VkPipelineLayoutCreateInfo layout_info = {};
layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
layout_info.setLayoutCount = 1;
layout_info.pSetLayouts = &set_layout;
layout_info.pushConstantRangeCount = 1;
layout_info.pPushConstantRanges = push_constants.data();
VkPipelineLayout result;
CHECK_RESULT(vkCreatePipelineLayout(dev, &layout_info, nullptr, &result));
return std::make_tuple(result, set_layout, bindings);
}
rsx::simple_array<VkDescriptorPoolSize> get_descriptor_pool_sizes(const rsx::simple_array<VkDescriptorSetLayoutBinding>& bindings) rsx::simple_array<VkDescriptorPoolSize> get_descriptor_pool_sizes(const rsx::simple_array<VkDescriptorSetLayoutBinding>& bindings)
{ {
// Compile descriptor pool sizes // Compile descriptor pool sizes

View file

@ -5,6 +5,13 @@
namespace vk namespace vk
{ {
// Grab standard layout for decompiled RSX programs. Also used by the interpreter.
// FIXME: This generates a bloated monstrosity that needs to die.
std::tuple<VkPipelineLayout, VkDescriptorSetLayout, rsx::simple_array<VkDescriptorSetLayoutBinding>> get_common_pipeline_layout(VkDevice dev);
// Returns the standard binding layout without texture slots. Those have special handling depending on the consumer.
rsx::simple_array<VkDescriptorSetLayoutBinding> get_common_binding_table();
// Returns an array of pool sizes that can be used to generate a proper descriptor pool // Returns an array of pool sizes that can be used to generate a proper descriptor pool
rsx::simple_array<VkDescriptorPoolSize> get_descriptor_pool_sizes(const rsx::simple_array<VkDescriptorSetLayoutBinding>& bindings); rsx::simple_array<VkDescriptorPoolSize> get_descriptor_pool_sizes(const rsx::simple_array<VkDescriptorSetLayoutBinding>& bindings);
} }

View file

@ -8,43 +8,64 @@
namespace vk namespace vk
{ {
std::vector<glsl::program_input> compute_task::get_inputs() std::vector<std::pair<VkDescriptorType, u8>> compute_task::get_descriptor_layout()
{ {
std::vector<glsl::program_input> result; std::vector<std::pair<VkDescriptorType, u8>> result;
for (unsigned i = 0; i < ssbo_count; ++i) result.emplace_back(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, ssbo_count);
{
const auto input = glsl::program_input::make
(
::glsl::glsl_compute_program,
"ssbo" + std::to_string(i),
glsl::program_input_type::input_type_storage_buffer,
0,
i
);
result.push_back(input);
}
if (use_push_constants && push_constants_size > 0)
{
const auto input = glsl::program_input::make
(
::glsl::glsl_compute_program,
"push_constants",
glsl::program_input_type::input_type_push_constant,
0,
0,
glsl::push_constant_ref{ .offset = 0, .size = push_constants_size }
);
result.push_back(input);
}
return result; return result;
} }
void compute_task::init_descriptors()
{
rsx::simple_array<VkDescriptorPoolSize> descriptor_pool_sizes;
rsx::simple_array<VkDescriptorSetLayoutBinding> bindings;
const auto layout = get_descriptor_layout();
for (const auto &e : layout)
{
descriptor_pool_sizes.push_back({e.first, e.second});
for (unsigned n = 0; n < e.second; ++n)
{
bindings.push_back
({
u32(bindings.size()),
e.first,
1,
VK_SHADER_STAGE_COMPUTE_BIT,
nullptr
});
}
}
// Reserve descriptor pools
m_descriptor_pool.create(*g_render_device, descriptor_pool_sizes);
m_descriptor_layout = vk::descriptors::create_layout(bindings);
VkPipelineLayoutCreateInfo layout_info = {};
layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
layout_info.setLayoutCount = 1;
layout_info.pSetLayouts = &m_descriptor_layout;
VkPushConstantRange push_constants{};
if (use_push_constants)
{
push_constants.size = push_constants_size;
push_constants.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
layout_info.pushConstantRangeCount = 1;
layout_info.pPushConstantRanges = &push_constants;
}
CHECK_RESULT(vkCreatePipelineLayout(*g_render_device, &layout_info, nullptr, &m_pipeline_layout));
}
void compute_task::create() void compute_task::create()
{ {
if (!initialized) if (!initialized)
{ {
init_descriptors();
switch (vk::get_driver_vendor()) switch (vk::get_driver_vendor())
{ {
case vk::driver_vendor::unknown: case vk::driver_vendor::unknown:
@ -100,6 +121,10 @@ namespace vk
m_program.reset(); m_program.reset();
m_param_buffer.reset(); m_param_buffer.reset();
vkDestroyDescriptorSetLayout(*g_render_device, m_descriptor_layout, nullptr);
vkDestroyPipelineLayout(*g_render_device, m_pipeline_layout, nullptr);
m_descriptor_pool.destroy();
initialized = false; initialized = false;
} }
} }
@ -117,23 +142,26 @@ namespace vk
shader_stage.module = handle; shader_stage.module = handle;
shader_stage.pName = "main"; shader_stage.pName = "main";
VkComputePipelineCreateInfo create_info VkComputePipelineCreateInfo info{};
{ info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, info.stage = shader_stage;
.stage = { info.layout = m_pipeline_layout;
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, info.basePipelineIndex = -1;
.stage = VK_SHADER_STAGE_COMPUTE_BIT, info.basePipelineHandle = VK_NULL_HANDLE;
.module = handle,
.pName = "main"
},
};
auto compiler = vk::get_pipe_compiler(); auto compiler = vk::get_pipe_compiler();
m_program = compiler->compile(create_info, vk::pipe_compiler::COMPILE_INLINE, {}, get_inputs()); m_program = compiler->compile(info, m_pipeline_layout, vk::pipe_compiler::COMPILE_INLINE);
declare_inputs();
} }
bind_resources(cmd); ensure(m_used_descriptors < VK_MAX_COMPUTE_TASKS);
m_program->bind(cmd, VK_PIPELINE_BIND_POINT_COMPUTE);
m_descriptor_set = m_descriptor_pool.allocate(m_descriptor_layout, VK_TRUE);
bind_resources();
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, m_program->pipeline);
m_descriptor_set.bind(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, m_pipeline_layout);
} }
void compute_task::run(const vk::command_buffer& cmd, u32 invocations_x, u32 invocations_y, u32 invocations_z) void compute_task::run(const vk::command_buffer& cmd, u32 invocations_x, u32 invocations_y, u32 invocations_z)
@ -243,19 +271,15 @@ namespace vk
m_src += suffix; m_src += suffix;
} }
void cs_shuffle_base::bind_resources(const vk::command_buffer& cmd) void cs_shuffle_base::bind_resources()
{ {
set_parameters(cmd); m_program->bind_buffer({ m_data->value, m_data_offset, m_data_length }, 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, m_descriptor_set);
m_program->bind_uniform({ m_data->value, m_data_offset, m_data_length }, 0, 0);
} }
void cs_shuffle_base::set_parameters(const vk::command_buffer& cmd) void cs_shuffle_base::set_parameters(const vk::command_buffer& cmd, const u32* params, u8 count)
{ {
if (!m_params.empty()) ensure(use_push_constants);
{ vkCmdPushConstants(cmd, m_pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, count * 4, params);
ensure(use_push_constants);
vkCmdPushConstants(cmd, m_program->layout(), VK_SHADER_STAGE_COMPUTE_BIT, 0, m_params.size_bytes32(), m_params.data());
}
} }
void cs_shuffle_base::run(const vk::command_buffer& cmd, const vk::buffer* data, u32 data_length, u32 data_offset) void cs_shuffle_base::run(const vk::command_buffer& cmd, const vk::buffer* data, u32 data_length, u32 data_offset)
@ -293,15 +317,15 @@ namespace vk
" uint stencil_offset;\n"; " uint stencil_offset;\n";
} }
void cs_interleave_task::bind_resources(const vk::command_buffer& cmd) void cs_interleave_task::bind_resources()
{ {
set_parameters(cmd); m_program->bind_buffer({ m_data->value, m_data_offset, m_ssbo_length }, 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, m_descriptor_set);
m_program->bind_uniform({ m_data->value, m_data_offset, m_ssbo_length }, 0, 0);
} }
void cs_interleave_task::run(const vk::command_buffer& cmd, const vk::buffer* data, u32 data_offset, u32 data_length, u32 zeta_offset, u32 stencil_offset) void cs_interleave_task::run(const vk::command_buffer& cmd, const vk::buffer* data, u32 data_offset, u32 data_length, u32 zeta_offset, u32 stencil_offset)
{ {
m_params = { data_length, zeta_offset - data_offset, stencil_offset - data_offset, 0 }; u32 parameters[4] = { data_length, zeta_offset - data_offset, stencil_offset - data_offset, 0 };
set_parameters(cmd, parameters, 4);
ensure(stencil_offset > data_offset); ensure(stencil_offset > data_offset);
m_ssbo_length = stencil_offset + (data_length / 4) - data_offset; m_ssbo_length = stencil_offset + (data_length / 4) - data_offset;
@ -353,10 +377,10 @@ namespace vk
m_src = fmt::replace_all(m_src, syntax_replace); m_src = fmt::replace_all(m_src, syntax_replace);
} }
void cs_aggregator::bind_resources(const vk::command_buffer& /*cmd*/) void cs_aggregator::bind_resources()
{ {
m_program->bind_uniform({ src->value, 0, block_length }, 0, 0); m_program->bind_buffer({ src->value, 0, block_length }, 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, m_descriptor_set);
m_program->bind_uniform({ dst->value, 0, 4 }, 0, 1); m_program->bind_buffer({ dst->value, 0, 4 }, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, m_descriptor_set);
} }
void cs_aggregator::run(const vk::command_buffer& cmd, const vk::buffer* dst, const vk::buffer* src, u32 num_words) void cs_aggregator::run(const vk::command_buffer& cmd, const vk::buffer* dst, const vk::buffer* src, u32 num_words)

View file

@ -19,6 +19,12 @@ namespace vk
std::unique_ptr<vk::glsl::program> m_program; std::unique_ptr<vk::glsl::program> m_program;
std::unique_ptr<vk::buffer> m_param_buffer; std::unique_ptr<vk::buffer> m_param_buffer;
vk::descriptor_pool m_descriptor_pool;
descriptor_set m_descriptor_set;
VkDescriptorSetLayout m_descriptor_layout = nullptr;
VkPipelineLayout m_pipeline_layout = nullptr;
u32 m_used_descriptors = 0;
bool initialized = false; bool initialized = false;
bool unroll_loops = true; bool unroll_loops = true;
bool use_push_constants = false; bool use_push_constants = false;
@ -31,11 +37,15 @@ namespace vk
compute_task() = default; compute_task() = default;
virtual ~compute_task() { destroy(); } virtual ~compute_task() { destroy(); }
virtual std::vector<std::pair<VkDescriptorType, u8>> get_descriptor_layout();
void init_descriptors();
void create(); void create();
void destroy(); void destroy();
virtual std::vector<glsl::program_input> get_inputs(); virtual void bind_resources() {}
virtual void bind_resources(const vk::command_buffer& /*cmd*/) {} virtual void declare_inputs() {}
void load_program(const vk::command_buffer& cmd); void load_program(const vk::command_buffer& cmd);
@ -50,8 +60,6 @@ namespace vk
u32 m_data_length = 0; u32 m_data_length = 0;
u32 kernel_size = 1; u32 kernel_size = 1;
rsx::simple_array<u32> m_params;
std::string variables, work_kernel, loop_advance, suffix; std::string variables, work_kernel, loop_advance, suffix;
std::string method_declarations; std::string method_declarations;
@ -59,9 +67,9 @@ namespace vk
void build(const char* function_name, u32 _kernel_size = 0); void build(const char* function_name, u32 _kernel_size = 0);
void bind_resources(const vk::command_buffer& cmd) override; void bind_resources() override;
void set_parameters(const vk::command_buffer& cmd); void set_parameters(const vk::command_buffer& cmd, const u32* params, u8 count);
void run(const vk::command_buffer& cmd, const vk::buffer* data, u32 data_length, u32 data_offset = 0); void run(const vk::command_buffer& cmd, const vk::buffer* data, u32 data_length, u32 data_offset = 0);
}; };
@ -127,7 +135,7 @@ namespace vk
cs_interleave_task(); cs_interleave_task();
void bind_resources(const vk::command_buffer& cmd) override; void bind_resources() override;
void run(const vk::command_buffer& cmd, const vk::buffer* data, u32 data_offset, u32 data_length, u32 zeta_offset, u32 stencil_offset); void run(const vk::command_buffer& cmd, const vk::buffer* data, u32 data_offset, u32 data_length, u32 zeta_offset, u32 stencil_offset);
}; };
@ -344,10 +352,9 @@ namespace vk
cs_shuffle_base::build(""); cs_shuffle_base::build("");
} }
void bind_resources(const vk::command_buffer& cmd) override void bind_resources() override
{ {
set_parameters(cmd); m_program->bind_buffer({ m_data->value, m_data_offset, m_ssbo_length }, 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, m_descriptor_set);
m_program->bind_uniform({ m_data->value, m_data_offset, m_ssbo_length }, 0, 0);
} }
void run(const vk::command_buffer& cmd, const vk::buffer* data, u32 src_offset, u32 src_length, u32 dst_offset) void run(const vk::command_buffer& cmd, const vk::buffer* data, u32 src_offset, u32 src_length, u32 dst_offset)
@ -364,7 +371,8 @@ namespace vk
data_offset = src_offset; data_offset = src_offset;
} }
m_params = { src_length, src_offset - data_offset, dst_offset - data_offset, 0 }; u32 parameters[4] = { src_length, src_offset - data_offset, dst_offset - data_offset, 0 };
set_parameters(cmd, parameters, 4);
cs_shuffle_base::run(cmd, data, src_length, data_offset); cs_shuffle_base::run(cmd, data, src_length, data_offset);
} }
}; };
@ -445,17 +453,15 @@ namespace vk
m_src = fmt::replace_all(m_src, syntax_replace); m_src = fmt::replace_all(m_src, syntax_replace);
} }
void bind_resources(const vk::command_buffer& cmd) override void bind_resources() override
{ {
set_parameters(cmd); m_program->bind_buffer({ src_buffer->value, in_offset, block_length }, 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, m_descriptor_set);
m_program->bind_buffer({ dst_buffer->value, out_offset, block_length }, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, m_descriptor_set);
m_program->bind_uniform({ src_buffer->value, in_offset, block_length }, 0, 0);
m_program->bind_uniform({ dst_buffer->value, out_offset, block_length }, 0, 1);
} }
void set_parameters(const vk::command_buffer& cmd) void set_parameters(const vk::command_buffer& cmd)
{ {
vkCmdPushConstants(cmd, m_program->layout(), VK_SHADER_STAGE_COMPUTE_BIT, 0, push_constants_size, params.data); vkCmdPushConstants(cmd, m_pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, push_constants_size, params.data);
} }
void run(const vk::command_buffer& cmd, const vk::buffer* dst, u32 out_offset, const vk::buffer* src, u32 in_offset, u32 data_length, u32 width, u32 height, u32 depth, u32 mipmaps) override void run(const vk::command_buffer& cmd, const vk::buffer* dst, u32 out_offset, const vk::buffer* src, u32 in_offset, u32 data_length, u32 width, u32 height, u32 depth, u32 mipmaps) override
@ -474,6 +480,7 @@ namespace vk
params.logw = rsx::ceil_log2(width); params.logw = rsx::ceil_log2(width);
params.logh = rsx::ceil_log2(height); params.logh = rsx::ceil_log2(height);
params.logd = rsx::ceil_log2(depth); params.logd = rsx::ceil_log2(depth);
set_parameters(cmd);
const u32 num_bytes_per_invocation = (sizeof(_BlockType) * optimal_group_size); const u32 num_bytes_per_invocation = (sizeof(_BlockType) * optimal_group_size);
const u32 linear_invocations = utils::aligned_div(data_length, num_bytes_per_invocation); const u32 linear_invocations = utils::aligned_div(data_length, num_bytes_per_invocation);
@ -490,7 +497,7 @@ namespace vk
cs_aggregator(); cs_aggregator();
void bind_resources(const vk::command_buffer& cmd) override; void bind_resources() override;
void run(const vk::command_buffer& cmd, const vk::buffer* dst, const vk::buffer* src, u32 num_words); void run(const vk::command_buffer& cmd, const vk::buffer* dst, const vk::buffer* src, u32 num_words);
}; };
@ -574,18 +581,16 @@ namespace vk
m_src = fmt::replace_all(m_src, syntax_replace); m_src = fmt::replace_all(m_src, syntax_replace);
} }
void bind_resources(const vk::command_buffer& cmd) override void bind_resources() override
{ {
set_parameters(cmd); const auto op = static_cast<int>(Op);
m_program->bind_buffer({ src_buffer->value, in_offset, in_block_length }, 0 ^ op, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, m_descriptor_set);
const auto op = static_cast<u32>(Op); m_program->bind_buffer({ dst_buffer->value, out_offset, out_block_length }, 1 ^ op, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, m_descriptor_set);
m_program->bind_uniform({ src_buffer->value, in_offset, in_block_length }, 0u, 0u ^ op);
m_program->bind_uniform({ dst_buffer->value, out_offset, out_block_length }, 0u, 1u ^ op);
} }
void set_parameters(const vk::command_buffer& cmd) void set_parameters(const vk::command_buffer& cmd)
{ {
vkCmdPushConstants(cmd, m_program->layout(), VK_SHADER_STAGE_COMPUTE_BIT, 0, push_constants_size, &params); vkCmdPushConstants(cmd, m_pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, push_constants_size, &params);
} }
void run(const vk::command_buffer& cmd, const RSX_detiler_config& config) void run(const vk::command_buffer& cmd, const RSX_detiler_config& config)
@ -648,6 +653,7 @@ namespace vk
params.image_height = (Op == RSX_detiler_op::decode) ? tile_aligned_height : config.image_height; params.image_height = (Op == RSX_detiler_op::decode) ? tile_aligned_height : config.image_height;
params.image_pitch = config.image_pitch; params.image_pitch = config.image_pitch;
params.image_bpp = config.image_bpp; params.image_bpp = config.image_bpp;
set_parameters(cmd);
const u32 subtexels_per_invocation = (config.image_bpp < 4) ? (4 / config.image_bpp) : 1; const u32 subtexels_per_invocation = (config.image_bpp < 4) ? (4 / config.image_bpp) : 1;
const u32 virtual_width = config.image_width / subtexels_per_invocation; const u32 virtual_width = config.image_width / subtexels_per_invocation;

View file

@ -554,8 +554,9 @@ bool VKGSRender::bind_texture_env()
if (view) [[likely]] if (view) [[likely]]
{ {
m_program->bind_uniform({ fs_sampler_handles[i]->value, view->value, view->image()->current_layout }, m_program->bind_uniform({ fs_sampler_handles[i]->value, view->value, view->image()->current_layout },
vk::glsl::binding_set_index_fragment, i,
m_fs_binding_table->ftex_location[i]); ::glsl::program_domain::glsl_fragment_program,
m_current_frame->descriptor_set);
if (current_fragment_program.texture_state.redirected_textures & (1 << i)) if (current_fragment_program.texture_state.redirected_textures & (1 << i))
{ {
@ -575,22 +576,27 @@ bool VKGSRender::bind_texture_env()
} }
m_program->bind_uniform({ m_stencil_mirror_sampler->value, stencil_view->value, stencil_view->image()->current_layout }, m_program->bind_uniform({ m_stencil_mirror_sampler->value, stencil_view->value, stencil_view->image()->current_layout },
vk::glsl::binding_set_index_fragment, i,
m_fs_binding_table->ftex_stencil_location[i]); ::glsl::program_domain::glsl_fragment_program,
m_current_frame->descriptor_set,
true);
} }
} }
else else
{ {
const VkImageViewType view_type = vk::get_view_type(current_fragment_program.get_texture_dimension(i)); const VkImageViewType view_type = vk::get_view_type(current_fragment_program.get_texture_dimension(i));
m_program->bind_uniform({ vk::null_sampler(), vk::null_image_view(*m_current_command_buffer, view_type)->value, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }, m_program->bind_uniform({ vk::null_sampler(), vk::null_image_view(*m_current_command_buffer, view_type)->value, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL },
vk::glsl::binding_set_index_fragment, i,
m_fs_binding_table->ftex_location[i]); ::glsl::program_domain::glsl_fragment_program,
m_current_frame->descriptor_set);
if (current_fragment_program.texture_state.redirected_textures & (1 << i)) if (current_fragment_program.texture_state.redirected_textures & (1 << i))
{ {
m_program->bind_uniform({ vk::null_sampler(), vk::null_image_view(*m_current_command_buffer, view_type)->value, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }, m_program->bind_uniform({ vk::null_sampler(), vk::null_image_view(*m_current_command_buffer, view_type)->value, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL },
vk::glsl::binding_set_index_fragment, i,
m_fs_binding_table->ftex_stencil_location[i]); ::glsl::program_domain::glsl_fragment_program,
m_current_frame->descriptor_set,
true);
} }
} }
} }
@ -604,8 +610,9 @@ bool VKGSRender::bind_texture_env()
{ {
const auto view_type = vk::get_view_type(current_vertex_program.get_texture_dimension(i)); const auto view_type = vk::get_view_type(current_vertex_program.get_texture_dimension(i));
m_program->bind_uniform({ vk::null_sampler(), vk::null_image_view(*m_current_command_buffer, view_type)->value, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }, m_program->bind_uniform({ vk::null_sampler(), vk::null_image_view(*m_current_command_buffer, view_type)->value, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL },
vk::glsl::binding_set_index_vertex, i,
m_vs_binding_table->vtex_location[i]); ::glsl::program_domain::glsl_vertex_program,
m_current_frame->descriptor_set);
continue; continue;
} }
@ -627,8 +634,9 @@ bool VKGSRender::bind_texture_env()
const auto view_type = vk::get_view_type(current_vertex_program.get_texture_dimension(i)); const auto view_type = vk::get_view_type(current_vertex_program.get_texture_dimension(i));
m_program->bind_uniform({ vk::null_sampler(), vk::null_image_view(*m_current_command_buffer, view_type)->value, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }, m_program->bind_uniform({ vk::null_sampler(), vk::null_image_view(*m_current_command_buffer, view_type)->value, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL },
vk::glsl::binding_set_index_vertex, i,
m_vs_binding_table->vtex_location[i]); ::glsl::program_domain::glsl_vertex_program,
m_current_frame->descriptor_set);
continue; continue;
} }
@ -636,8 +644,9 @@ bool VKGSRender::bind_texture_env()
validate_image_layout_for_read_access(*m_current_command_buffer, image_ptr, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, sampler_state); validate_image_layout_for_read_access(*m_current_command_buffer, image_ptr, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, sampler_state);
m_program->bind_uniform({ vs_sampler_handles[i]->value, image_ptr->value, image_ptr->image()->current_layout }, m_program->bind_uniform({ vs_sampler_handles[i]->value, image_ptr->value, image_ptr->image()->current_layout },
vk::glsl::binding_set_index_vertex, i,
m_vs_binding_table->vtex_location[i]); ::glsl::program_domain::glsl_vertex_program,
m_current_frame->descriptor_set);
} }
return out_of_memory; return out_of_memory;
@ -712,7 +721,7 @@ bool VKGSRender::bind_interpreter_texture_env()
} }
} }
m_shader_interpreter.update_fragment_textures(texture_env); m_shader_interpreter.update_fragment_textures(texture_env, m_current_frame->descriptor_set);
return out_of_memory; return out_of_memory;
} }
@ -761,6 +770,9 @@ void VKGSRender::emit_geometry(u32 sub_index)
return; return;
} }
const auto old_persistent_buffer = m_persistent_attribute_storage ? m_persistent_attribute_storage->value : null_buffer_view->value;
const auto old_volatile_buffer = m_volatile_attribute_storage ? m_volatile_attribute_storage->value : null_buffer_view->value;
// Programs data is dependent on vertex state // Programs data is dependent on vertex state
auto upload_info = upload_vertex_data(); auto upload_info = upload_vertex_data();
if (!upload_info.vertex_draw_count) if (!upload_info.vertex_draw_count)
@ -815,6 +827,8 @@ void VKGSRender::emit_geometry(u32 sub_index)
auto volatile_buffer = m_volatile_attribute_storage ? m_volatile_attribute_storage->value : null_buffer_view->value; auto volatile_buffer = m_volatile_attribute_storage ? m_volatile_attribute_storage->value : null_buffer_view->value;
bool update_descriptors = false; bool update_descriptors = false;
const auto& binding_table = m_device->get_pipeline_binding_table();
if (m_current_draw.subdraw_id == 0) if (m_current_draw.subdraw_id == 0)
{ {
update_descriptors = true; update_descriptors = true;
@ -834,6 +848,33 @@ void VKGSRender::emit_geometry(u32 sub_index)
vk::clear_status_interrupt(vk::heap_changed); vk::clear_status_interrupt(vk::heap_changed);
} }
} }
else if (persistent_buffer != old_persistent_buffer || volatile_buffer != old_volatile_buffer)
{
// Need to update descriptors; make a copy for the next draw
VkDescriptorSet previous_set = m_current_frame->descriptor_set.value();
m_current_frame->descriptor_set.flush();
m_current_frame->descriptor_set = allocate_descriptor_set();
rsx::simple_array<VkCopyDescriptorSet> copy_cmds(binding_table.total_descriptor_bindings);
for (u32 n = 0; n < binding_table.total_descriptor_bindings; ++n)
{
copy_cmds[n] =
{
VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET, // sType
nullptr, // pNext
previous_set, // srcSet
n, // srcBinding
0u, // srcArrayElement
m_current_frame->descriptor_set.value(), // dstSet
n, // dstBinding
0u, // dstArrayElement
1u // descriptorCount
};
}
m_current_frame->descriptor_set.push(copy_cmds);
update_descriptors = true;
}
// Update vertex fetch parameters // Update vertex fetch parameters
update_vertex_env(sub_index, upload_info); update_vertex_env(sub_index, upload_info);
@ -841,9 +882,9 @@ void VKGSRender::emit_geometry(u32 sub_index)
ensure(m_vertex_layout_storage); ensure(m_vertex_layout_storage);
if (update_descriptors) if (update_descriptors)
{ {
m_program->bind_uniform(persistent_buffer, vk::glsl::binding_set_index_vertex, m_vs_binding_table->vertex_buffers_location); m_program->bind_uniform(persistent_buffer, binding_table.vertex_buffers_first_bind_slot, m_current_frame->descriptor_set);
m_program->bind_uniform(volatile_buffer, vk::glsl::binding_set_index_vertex, m_vs_binding_table->vertex_buffers_location + 1); m_program->bind_uniform(volatile_buffer, binding_table.vertex_buffers_first_bind_slot + 1, m_current_frame->descriptor_set);
m_program->bind_uniform(m_vertex_layout_storage->value, vk::glsl::binding_set_index_vertex, m_vs_binding_table->vertex_buffers_location + 2); m_program->bind_uniform(m_vertex_layout_storage->value, binding_table.vertex_buffers_first_bind_slot + 2, m_current_frame->descriptor_set);
} }
bool reload_state = (!m_current_draw.subdraw_id++); bool reload_state = (!m_current_draw.subdraw_id++);
@ -867,12 +908,10 @@ void VKGSRender::emit_geometry(u32 sub_index)
reload_state = true; reload_state = true;
}); });
// Bind both pipe and descriptors in one go
// FIXME: We only need to rebind the pipeline when reload state is set. Flags?
m_program->bind(*m_current_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS);
if (reload_state) if (reload_state)
{ {
vkCmdBindPipeline(*m_current_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_program->pipeline);
update_draw_state(); update_draw_state();
begin_render_pass(); begin_render_pass();
@ -890,6 +929,7 @@ void VKGSRender::emit_geometry(u32 sub_index)
} }
// Bind the new set of descriptors for use with this draw call // Bind the new set of descriptors for use with this draw call
m_current_frame->descriptor_set.bind(*m_current_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_program->pipeline_layout);
m_frame_stats.setup_time += m_profiler.duration(); m_frame_stats.setup_time += m_profiler.duration();
if (!upload_info.index_info) if (!upload_info.index_info)
@ -1043,6 +1083,9 @@ void VKGSRender::end()
return; return;
} }
// Allocate descriptor set
m_current_frame->descriptor_set = allocate_descriptor_set();
// Load program execution environment // Load program execution environment
load_program_env(); load_program_env();
m_frame_stats.setup_time += m_profiler.duration(); m_frame_stats.setup_time += m_profiler.duration();

View file

@ -26,85 +26,8 @@ std::string VKFragmentDecompilerThread::compareFunction(COMPARE f, const std::st
return glsl::compareFunctionImpl(f, Op0, Op1); return glsl::compareFunctionImpl(f, Op0, Op1);
} }
void VKFragmentDecompilerThread::prepareBindingTable()
{
// First check if we have constants and textures as those need extra work
bool has_constants = false, has_textures = false;
for (const ParamType& PT : m_parr.params[PF_PARAM_UNIFORM])
{
if (has_constants && has_textures)
{
break;
}
if (PT.type.starts_with("sampler"))
{
has_textures = true;
continue;
}
ensure(PT.type.starts_with("vec"));
has_constants = true;
}
unsigned location = 0; // All bindings must be set from this var
vk_prog->binding_table.context_buffer_location = location++;
if (has_constants)
{
vk_prog->binding_table.cbuf_location = location++;
}
vk_prog->binding_table.tex_param_location = location++;
vk_prog->binding_table.polygon_stipple_params_location = location++;
std::memset(vk_prog->binding_table.ftex_location, 0xff, sizeof(vk_prog->binding_table.ftex_location));
std::memset(vk_prog->binding_table.ftex_stencil_location, 0xff, sizeof(vk_prog->binding_table.ftex_stencil_location));
if (has_textures) [[ likely ]]
{
for (const ParamType& PT : m_parr.params[PF_PARAM_UNIFORM])
{
if (!PT.type.starts_with("sampler"))
{
continue;
}
for (const ParamItem& PI : PT.items)
{
const auto texture_id = vk::get_texture_index(PI.name);
const auto mask = 1u << texture_id;
// Allocate real binding
vk_prog->binding_table.ftex_location[texture_id] = location++;
// Tag the stencil mirror if required
if (properties.redirected_sampler_mask & mask) [[ unlikely ]]
{
vk_prog->binding_table.ftex_stencil_location[texture_id] = 0;
}
}
// Normalize stencil offsets
if (properties.redirected_sampler_mask != 0) [[ unlikely ]]
{
for (auto& stencil_location : vk_prog->binding_table.ftex_stencil_location)
{
if (stencil_location != 0)
{
continue;
}
stencil_location = location++;
}
}
}
}
}
void VKFragmentDecompilerThread::insertHeader(std::stringstream & OS) void VKFragmentDecompilerThread::insertHeader(std::stringstream & OS)
{ {
prepareBindingTable();
std::vector<const char*> required_extensions; std::vector<const char*> required_extensions;
if (device_props.has_native_half_support) if (device_props.has_native_half_support)
@ -174,18 +97,21 @@ void VKFragmentDecompilerThread::insertOutputs(std::stringstream & OS)
void VKFragmentDecompilerThread::insertConstants(std::stringstream & OS) void VKFragmentDecompilerThread::insertConstants(std::stringstream & OS)
{ {
u32 location = m_binding_table.textures_first_bind_slot;
for (const ParamType& PT : m_parr.params[PF_PARAM_UNIFORM]) for (const ParamType& PT : m_parr.params[PF_PARAM_UNIFORM])
{ {
if (!PT.type.starts_with("sampler")) if (PT.type != "sampler1D" &&
{ PT.type != "sampler2D" &&
PT.type != "sampler3D" &&
PT.type != "samplerCube")
continue; continue;
}
for (const ParamItem& PI : PT.items) for (const ParamItem& PI : PT.items)
{ {
std::string samplerType = PT.type; std::string samplerType = PT.type;
const int index = vk::get_texture_index(PI.name); ensure(PI.name.length() > 3);
int index = atoi(&PI.name[3]);
const auto mask = (1 << index); const auto mask = (1 << index);
if (properties.multisampled_sampler_mask & mask) if (properties.multisampled_sampler_mask & mask)
@ -209,37 +135,39 @@ void VKFragmentDecompilerThread::insertConstants(std::stringstream & OS)
} }
} }
const int id = vk::get_texture_index(PI.name); vk::glsl::program_input in;
auto in = vk::glsl::program_input::make( in.location = location;
glsl::glsl_fragment_program, in.domain = glsl::glsl_fragment_program;
PI.name, in.name = PI.name;
vk::glsl::input_type_texture, in.type = vk::glsl::input_type_texture;
vk::glsl::binding_set_index_fragment,
vk_prog->binding_table.ftex_location[id]
);
inputs.push_back(in); inputs.push_back(in);
OS << "layout(set=1, binding=" << in.location << ") uniform " << samplerType << " " << PI.name << ";\n"; OS << "layout(set=0, binding=" << location++ << ") uniform " << samplerType << " " << PI.name << ";\n";
if (properties.redirected_sampler_mask & mask) if (properties.redirected_sampler_mask & mask)
{ {
// Insert stencil mirror declaration // Insert stencil mirror declaration
in.name += "_stencil"; in.name += "_stencil";
in.location = vk_prog->binding_table.ftex_stencil_location[id]; in.location = location;
inputs.push_back(in); inputs.push_back(in);
OS << "layout(set=1, binding=" << in.location << ") uniform u" << samplerType << " " << in.name << ";\n"; OS << "layout(set=0, binding=" << location++ << ") uniform u" << samplerType << " " << in.name << ";\n";
} }
} }
} }
ensure(location <= m_binding_table.vertex_textures_first_bind_slot); // "Too many sampler descriptors!"
std::string constants_block; std::string constants_block;
for (const ParamType& PT : m_parr.params[PF_PARAM_UNIFORM]) for (const ParamType& PT : m_parr.params[PF_PARAM_UNIFORM])
{ {
if (PT.type.starts_with("sampler")) if (PT.type == "sampler1D" ||
{ PT.type == "sampler2D" ||
PT.type == "sampler3D" ||
PT.type == "samplerCube")
continue; continue;
}
for (const ParamItem& PI : PT.items) for (const ParamItem& PI : PT.items)
{ {
@ -249,13 +177,13 @@ void VKFragmentDecompilerThread::insertConstants(std::stringstream & OS)
if (!constants_block.empty()) if (!constants_block.empty())
{ {
OS << "layout(std140, set=1, binding=" << vk_prog->binding_table.cbuf_location << ") uniform FragmentConstantsBuffer\n"; OS << "layout(std140, set = 0, binding = 2) uniform FragmentConstantsBuffer\n";
OS << "{\n"; OS << "{\n";
OS << constants_block; OS << constants_block;
OS << "};\n\n"; OS << "};\n\n";
} }
OS << "layout(std140, set=1, binding=" << vk_prog->binding_table.context_buffer_location << ") uniform FragmentStateBuffer\n"; OS << "layout(std140, set = 0, binding = 3) uniform FragmentStateBuffer\n";
OS << "{\n"; OS << "{\n";
OS << " float fog_param0;\n"; OS << " float fog_param0;\n";
OS << " float fog_param1;\n"; OS << " float fog_param1;\n";
@ -267,39 +195,32 @@ void VKFragmentDecompilerThread::insertConstants(std::stringstream & OS)
OS << " float wpos_bias;\n"; OS << " float wpos_bias;\n";
OS << "};\n\n"; OS << "};\n\n";
OS << "layout(std140, set=1, binding=" << vk_prog->binding_table.tex_param_location << ") uniform TextureParametersBuffer\n"; OS << "layout(std140, set = 0, binding = 4) uniform TextureParametersBuffer\n";
OS << "{\n"; OS << "{\n";
OS << " sampler_info texture_parameters[16];\n"; OS << " sampler_info texture_parameters[16];\n";
OS << "};\n\n"; OS << "};\n\n";
OS << "layout(std140, set=1, binding=" << vk_prog->binding_table.polygon_stipple_params_location << ") uniform RasterizerHeap\n"; OS << "layout(std140, set = 0, binding = " << std::to_string(m_binding_table.rasterizer_env_bind_slot) << ") uniform RasterizerHeap\n";
OS << "{\n"; OS << "{\n";
OS << " uvec4 stipple_pattern[8];\n"; OS << " uvec4 stipple_pattern[8];\n";
OS << "};\n\n"; OS << "};\n\n";
vk::glsl::program_input in vk::glsl::program_input in;
{ in.location = m_binding_table.fragment_constant_buffers_bind_slot;
.domain = glsl::glsl_fragment_program, in.domain = glsl::glsl_fragment_program;
.type = vk::glsl::input_type_uniform_buffer, in.name = "FragmentConstantsBuffer";
.set = vk::glsl::binding_set_index_fragment in.type = vk::glsl::input_type_uniform_buffer;
}; inputs.push_back(in);
if (!constants_block.empty()) in.location = m_binding_table.fragment_state_bind_slot;
{
in.location = vk_prog->binding_table.cbuf_location;
in.name = "FragmentConstantsBuffer";
inputs.push_back(in);
}
in.location = vk_prog->binding_table.context_buffer_location;
in.name = "FragmentStateBuffer"; in.name = "FragmentStateBuffer";
inputs.push_back(in); inputs.push_back(in);
in.location = vk_prog->binding_table.tex_param_location; in.location = m_binding_table.fragment_texture_params_bind_slot;
in.name = "TextureParametersBuffer"; in.name = "TextureParametersBuffer";
inputs.push_back(in); inputs.push_back(in);
in.location = vk_prog->binding_table.polygon_stipple_params_location; in.location = m_binding_table.rasterizer_env_bind_slot;
in.name = "RasterizerHeap"; in.name = "RasterizerHeap";
inputs.push_back(in); inputs.push_back(in);
} }
@ -451,6 +372,7 @@ void VKFragmentDecompilerThread::insertMainEnd(std::stringstream & OS)
void VKFragmentDecompilerThread::Task() void VKFragmentDecompilerThread::Task()
{ {
m_binding_table = vk::g_render_device->get_pipeline_binding_table();
m_shader = Decompile(); m_shader = Decompile();
vk_prog->SetInputs(inputs); vk_prog->SetInputs(inputs);
} }
@ -484,7 +406,10 @@ void VKFragmentProgram::Decompile(const RSXFragmentProgram& prog)
{ {
for (const ParamItem& PI : PT.items) for (const ParamItem& PI : PT.items)
{ {
if (PT.type.starts_with("sampler")) if (PT.type == "sampler1D" ||
PT.type == "sampler2D" ||
PT.type == "sampler3D" ||
PT.type == "samplerCube")
continue; continue;
usz offset = atoi(PI.name.c_str() + 2); usz offset = atoi(PI.name.c_str() + 2);

View file

@ -10,7 +10,7 @@ namespace vk
class shader_interpreter; class shader_interpreter;
} }
class VKFragmentDecompilerThread : public FragmentProgramDecompiler struct VKFragmentDecompilerThread : public FragmentProgramDecompiler
{ {
friend class vk::shader_interpreter; friend class vk::shader_interpreter;
@ -19,8 +19,7 @@ class VKFragmentDecompilerThread : public FragmentProgramDecompiler
std::vector<vk::glsl::program_input> inputs; std::vector<vk::glsl::program_input> inputs;
class VKFragmentProgram *vk_prog; class VKFragmentProgram *vk_prog;
glsl::shader_properties m_shader_props{}; glsl::shader_properties m_shader_props{};
vk::pipeline_binding_table m_binding_table{};
void prepareBindingTable();
public: public:
VKFragmentDecompilerThread(std::string& shader, ParamArray& parr, const RSXFragmentProgram &prog, u32& size, class VKFragmentProgram& dst) VKFragmentDecompilerThread(std::string& shader, ParamArray& parr, const RSXFragmentProgram &prog, u32& size, class VKFragmentProgram& dst)
@ -33,7 +32,6 @@ public:
void Task(); void Task();
const std::vector<vk::glsl::program_input>& get_inputs() { return inputs; } const std::vector<vk::glsl::program_input>& get_inputs() { return inputs; }
protected: protected:
std::string getFloatTypeName(usz elementCount) override; std::string getFloatTypeName(usz elementCount) override;
std::string getHalfTypeName(usz elementCount) override; std::string getHalfTypeName(usz elementCount) override;
@ -65,19 +63,8 @@ public:
std::vector<usz> FragmentConstantOffsetCache; std::vector<usz> FragmentConstantOffsetCache;
std::array<u32, 4> output_color_masks{ {} }; std::array<u32, 4> output_color_masks{ {} };
std::vector<vk::glsl::program_input> uniforms; std::vector<vk::glsl::program_input> uniforms;
struct
{
u32 context_buffer_location = umax; // Rasterizer context
u32 cbuf_location = umax; // Constants register file
u32 tex_param_location = umax; // Texture configuration data
u32 polygon_stipple_params_location = umax; // Polygon stipple settings
u32 ftex_location[16]; // Texture locations array
u32 ftex_stencil_location[16]; // Texture stencil mirror array
} binding_table;
void SetInputs(std::vector<vk::glsl::program_input>& inputs); void SetInputs(std::vector<vk::glsl::program_input>& inputs);
/** /**
* Decompile a fragment shader located in the PS3's Memory. This function operates synchronously. * Decompile a fragment shader located in the PS3's Memory. This function operates synchronously.

View file

@ -1,4 +1,3 @@
#include "Emu/RSX/VK/vkutils/descriptors.h"
#include "stdafx.h" #include "stdafx.h"
#include "../Overlays/overlay_compile_notification.h" #include "../Overlays/overlay_compile_notification.h"
#include "../Overlays/Shaders/shader_loading_dialog_native.h" #include "../Overlays/Shaders/shader_loading_dialog_native.h"
@ -424,8 +423,8 @@ VKGSRender::VKGSRender(utils::serial* ar) noexcept : GSRender(ar)
std::vector<vk::physical_device>& gpus = m_instance.enumerate_devices(); std::vector<vk::physical_device>& gpus = m_instance.enumerate_devices();
// Actually confirm that the loader found at least one compatible device //Actually confirm that the loader found at least one compatible device
// This should not happen unless something is wrong with the driver setup on the target system //This should not happen unless something is wrong with the driver setup on the target system
if (gpus.empty()) if (gpus.empty())
{ {
//We can't throw in Emulator::Load, so we show error and return //We can't throw in Emulator::Load, so we show error and return
@ -483,16 +482,20 @@ VKGSRender::VKGSRender(utils::serial* ar) noexcept : GSRender(ar)
swapchain_unavailable = true; swapchain_unavailable = true;
} }
// create command buffer... //create command buffer...
m_command_buffer_pool.create((*m_device), m_device->get_graphics_queue_family()); m_command_buffer_pool.create((*m_device), m_device->get_graphics_queue_family());
m_primary_cb_list.create(m_command_buffer_pool, vk::command_buffer::access_type_hint::flush_only); m_primary_cb_list.create(m_command_buffer_pool, vk::command_buffer::access_type_hint::flush_only);
m_current_command_buffer = m_primary_cb_list.get(); m_current_command_buffer = m_primary_cb_list.get();
m_current_command_buffer->begin(); m_current_command_buffer->begin();
// Create secondary command_buffer for parallel operations //Create secondary command_buffer for parallel operations
m_secondary_command_buffer_pool.create((*m_device), m_device->get_graphics_queue_family()); m_secondary_command_buffer_pool.create((*m_device), m_device->get_graphics_queue_family());
m_secondary_cb_list.create(m_secondary_command_buffer_pool, vk::command_buffer::access_type_hint::all); m_secondary_cb_list.create(m_secondary_command_buffer_pool, vk::command_buffer::access_type_hint::all);
//Precalculated stuff
rsx::simple_array<VkDescriptorSetLayoutBinding> binding_layout;
std::tie(m_pipeline_layout, m_descriptor_layouts, binding_layout) = vk::get_common_pipeline_layout(*m_device);
//Occlusion //Occlusion
m_occlusion_query_manager = std::make_unique<vk::query_pool_manager>(*m_device, VK_QUERY_TYPE_OCCLUSION, OCCLUSION_MAX_POOL_SIZE); m_occlusion_query_manager = std::make_unique<vk::query_pool_manager>(*m_device, VK_QUERY_TYPE_OCCLUSION, OCCLUSION_MAX_POOL_SIZE);
m_occlusion_map.resize(rsx::reports::occlusion_query_count); m_occlusion_map.resize(rsx::reports::occlusion_query_count);
@ -505,6 +508,11 @@ VKGSRender::VKGSRender(utils::serial* ar) noexcept : GSRender(ar)
m_occlusion_query_manager->set_control_flags(VK_QUERY_CONTROL_PRECISE_BIT, 0); m_occlusion_query_manager->set_control_flags(VK_QUERY_CONTROL_PRECISE_BIT, 0);
} }
// Generate frame contexts
const u32 max_draw_calls = m_device->get_descriptor_max_draw_calls();
const auto descriptor_type_sizes = vk::get_descriptor_pool_sizes(binding_layout);
m_descriptor_pool.create(*m_device, descriptor_type_sizes, max_draw_calls);
VkSemaphoreCreateInfo semaphore_info = {}; VkSemaphoreCreateInfo semaphore_info = {};
semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
@ -844,6 +852,12 @@ VKGSRender::~VKGSRender()
m_stencil_mirror_sampler.reset(); m_stencil_mirror_sampler.reset();
// Pipeline descriptors
m_descriptor_pool.destroy();
vkDestroyPipelineLayout(*m_device, m_pipeline_layout, nullptr);
vkDestroyDescriptorSetLayout(*m_device, m_descriptor_layouts, nullptr);
// Queries // Queries
m_occlusion_query_manager.reset(); m_occlusion_query_manager.reset();
m_cond_render_buffer.reset(); m_cond_render_buffer.reset();
@ -855,9 +869,6 @@ VKGSRender::~VKGSRender()
m_command_buffer_pool.destroy(); m_command_buffer_pool.destroy();
m_secondary_command_buffer_pool.destroy(); m_secondary_command_buffer_pool.destroy();
// Descriptors
vk::descriptors::flush();
// Global resources // Global resources
vk::destroy_global_resources(); vk::destroy_global_resources();
@ -1146,6 +1157,18 @@ void VKGSRender::check_present_status()
} }
} }
VkDescriptorSet VKGSRender::allocate_descriptor_set()
{
if (!m_shader_interpreter.is_interpreter(m_program)) [[likely]]
{
return m_descriptor_pool.allocate(m_descriptor_layouts, VK_TRUE);
}
else
{
return m_shader_interpreter.allocate_descriptor_set();
}
}
void VKGSRender::set_viewport() void VKGSRender::set_viewport()
{ {
const auto [clip_width, clip_height] = rsx::apply_resolution_scale<true>( const auto [clip_width, clip_height] = rsx::apply_resolution_scale<true>(
@ -1219,7 +1242,7 @@ void VKGSRender::on_init_thread()
if (!m_overlay_manager) if (!m_overlay_manager)
{ {
m_frame->hide(); m_frame->hide();
m_shaders_cache->load(nullptr); m_shaders_cache->load(nullptr, m_pipeline_layout);
m_frame->show(); m_frame->show();
} }
else else
@ -1227,7 +1250,7 @@ void VKGSRender::on_init_thread()
rsx::shader_loading_dialog_native dlg(this); rsx::shader_loading_dialog_native dlg(this);
// TODO: Handle window resize messages during loading on GPUs without OUT_OF_DATE_KHR support // TODO: Handle window resize messages during loading on GPUs without OUT_OF_DATE_KHR support
m_shaders_cache->load(&dlg); m_shaders_cache->load(&dlg, m_pipeline_layout);
} }
} }
@ -1780,11 +1803,8 @@ bool VKGSRender::load_program()
m_program = m_shader_interpreter.get( m_program = m_shader_interpreter.get(
m_pipeline_properties, m_pipeline_properties,
current_fp_metadata, current_fp_metadata,
current_vp_metadata,
current_vertex_program.ctrl, current_vertex_program.ctrl,
current_fragment_program.ctrl); current_fragment_program.ctrl);
std::tie(m_vs_binding_table, m_fs_binding_table) = get_binding_table();
return true; return true;
} }
} }
@ -1850,7 +1870,7 @@ bool VKGSRender::load_program()
vertex_program, vertex_program,
fragment_program, fragment_program,
m_pipeline_properties, m_pipeline_properties,
shadermode != shader_mode::recompiler, true); shadermode != shader_mode::recompiler, true, m_pipeline_layout);
vk::leave_uninterruptible(); vk::leave_uninterruptible();
@ -1882,7 +1902,6 @@ bool VKGSRender::load_program()
m_program = m_shader_interpreter.get( m_program = m_shader_interpreter.get(
m_pipeline_properties, m_pipeline_properties,
current_fp_metadata, current_fp_metadata,
current_vp_metadata,
current_vertex_program.ctrl, current_vertex_program.ctrl,
current_fragment_program.ctrl); current_fragment_program.ctrl);
@ -1904,16 +1923,6 @@ bool VKGSRender::load_program()
} }
} }
if (m_program)
{
std::tie(m_vs_binding_table, m_fs_binding_table) = get_binding_table();
}
else
{
m_vs_binding_table = nullptr;
m_fs_binding_table = nullptr;
}
return m_program != nullptr; return m_program != nullptr;
} }
@ -1925,14 +1934,13 @@ void VKGSRender::load_program_env()
} }
const u32 fragment_constants_size = current_fp_metadata.program_constants_buffer_length; const u32 fragment_constants_size = current_fp_metadata.program_constants_buffer_length;
const bool is_interpreter = m_shader_interpreter.is_interpreter(m_program);
const bool update_transform_constants = !!(m_graphics_state & rsx::pipeline_state::transform_constants_dirty); const bool update_transform_constants = !!(m_graphics_state & rsx::pipeline_state::transform_constants_dirty);
const bool update_fragment_constants = !!(m_graphics_state & rsx::pipeline_state::fragment_constants_dirty); const bool update_fragment_constants = !!(m_graphics_state & rsx::pipeline_state::fragment_constants_dirty);
const bool update_vertex_env = !!(m_graphics_state & rsx::pipeline_state::vertex_state_dirty); const bool update_vertex_env = !!(m_graphics_state & rsx::pipeline_state::vertex_state_dirty);
const bool update_fragment_env = !!(m_graphics_state & rsx::pipeline_state::fragment_state_dirty); const bool update_fragment_env = !!(m_graphics_state & rsx::pipeline_state::fragment_state_dirty);
const bool update_fragment_texture_env = !!(m_graphics_state & rsx::pipeline_state::fragment_texture_state_dirty); const bool update_fragment_texture_env = !!(m_graphics_state & rsx::pipeline_state::fragment_texture_state_dirty);
const bool update_instruction_buffers = (!!m_interpreter_state && is_interpreter); const bool update_instruction_buffers = (!!m_interpreter_state && m_shader_interpreter.is_interpreter(m_program));
const bool update_raster_env = (rsx::method_registers.polygon_stipple_enabled() && !!(m_graphics_state & rsx::pipeline_state::polygon_stipple_pattern_dirty)); const bool update_raster_env = (rsx::method_registers.polygon_stipple_enabled() && !!(m_graphics_state & rsx::pipeline_state::polygon_stipple_pattern_dirty));
const bool update_instancing_data = rsx::method_registers.current_draw_clause.is_trivial_instanced_draw; const bool update_instancing_data = rsx::method_registers.current_draw_clause.is_trivial_instanced_draw;
@ -2093,36 +2101,34 @@ void VKGSRender::load_program_env()
} }
} }
m_program->bind_uniform(m_vertex_env_buffer_info, vk::glsl::binding_set_index_vertex, m_vs_binding_table->context_buffer_location); const auto& binding_table = m_device->get_pipeline_binding_table();
m_program->bind_uniform(m_fragment_env_buffer_info, vk::glsl::binding_set_index_fragment, m_fs_binding_table->context_buffer_location);
m_program->bind_uniform(m_fragment_texture_params_buffer_info, vk::glsl::binding_set_index_fragment, m_fs_binding_table->tex_param_location);
m_program->bind_uniform(m_raster_env_buffer_info, vk::glsl::binding_set_index_fragment, m_fs_binding_table->polygon_stipple_params_location);
if (m_vs_binding_table->cbuf_location != umax) m_program->bind_uniform(m_vertex_env_buffer_info, binding_table.vertex_params_bind_slot, m_current_frame->descriptor_set);
{ m_program->bind_buffer(m_vertex_constants_buffer_info, binding_table.vertex_constant_buffers_bind_slot, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, m_current_frame->descriptor_set);
m_program->bind_uniform(m_vertex_constants_buffer_info, vk::glsl::binding_set_index_vertex, m_vs_binding_table->cbuf_location); m_program->bind_uniform(m_fragment_env_buffer_info, binding_table.fragment_state_bind_slot, m_current_frame->descriptor_set);
} m_program->bind_uniform(m_fragment_texture_params_buffer_info, binding_table.fragment_texture_params_bind_slot, m_current_frame->descriptor_set);
m_program->bind_uniform(m_raster_env_buffer_info, binding_table.rasterizer_env_bind_slot, m_current_frame->descriptor_set);
if (m_shader_interpreter.is_interpreter(m_program)) if (!m_shader_interpreter.is_interpreter(m_program))
{ {
m_program->bind_uniform(m_vertex_instructions_buffer_info, vk::glsl::binding_set_index_vertex, m_shader_interpreter.get_vertex_instruction_location()); m_program->bind_uniform(m_fragment_constants_buffer_info, binding_table.fragment_constant_buffers_bind_slot, m_current_frame->descriptor_set);
m_program->bind_uniform(m_fragment_instructions_buffer_info, vk::glsl::binding_set_index_fragment, m_shader_interpreter.get_fragment_instruction_location());
} }
else if (m_fs_binding_table->cbuf_location != umax) else
{ {
m_program->bind_uniform(m_fragment_constants_buffer_info, vk::glsl::binding_set_index_fragment, m_fs_binding_table->cbuf_location); m_program->bind_buffer(m_vertex_instructions_buffer_info, m_shader_interpreter.get_vertex_instruction_location(), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, m_current_frame->descriptor_set);
m_program->bind_buffer(m_fragment_instructions_buffer_info, m_shader_interpreter.get_fragment_instruction_location(), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, m_current_frame->descriptor_set);
} }
if (vk::emulate_conditional_rendering()) if (vk::emulate_conditional_rendering())
{ {
auto predicate = m_cond_render_buffer ? m_cond_render_buffer->value : vk::get_scratch_buffer(*m_current_command_buffer, 4)->value; auto predicate = m_cond_render_buffer ? m_cond_render_buffer->value : vk::get_scratch_buffer(*m_current_command_buffer, 4)->value;
m_program->bind_uniform({ predicate, 0, 4 }, vk::glsl::binding_set_index_vertex, m_vs_binding_table->cr_pred_buffer_location); m_program->bind_buffer({ predicate, 0, 4 }, binding_table.conditional_render_predicate_slot, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, m_current_frame->descriptor_set);
} }
if (current_vertex_program.ctrl & RSX_SHADER_CONTROL_INSTANCED_CONSTANTS) if (current_vertex_program.ctrl & RSX_SHADER_CONTROL_INSTANCED_CONSTANTS)
{ {
m_program->bind_uniform(m_instancing_indirection_buffer_info, vk::glsl::binding_set_index_vertex, m_vs_binding_table->instanced_lut_buffer_location); m_program->bind_buffer(m_instancing_indirection_buffer_info, binding_table.instancing_lookup_table_bind_slot, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, m_current_frame->descriptor_set);
m_program->bind_uniform(m_instancing_constants_array_buffer_info, vk::glsl::binding_set_index_vertex, m_vs_binding_table->instanced_cbuf_location); m_program->bind_buffer(m_instancing_constants_array_buffer_info, binding_table.instancing_constants_buffer_slot, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, m_current_frame->descriptor_set);
} }
// Clear flags // Clear flags
@ -2149,19 +2155,6 @@ void VKGSRender::load_program_env()
m_graphics_state.clear(handled_flags); m_graphics_state.clear(handled_flags);
} }
std::pair<const vs_binding_table_t*, const fs_binding_table_t*> VKGSRender::get_binding_table() const
{
ensure(m_program);
if (!m_shader_interpreter.is_interpreter(m_program))
{
return { &m_vertex_prog->binding_table, &m_fragment_prog->binding_table };
}
const auto& [vs, fs] = m_shader_interpreter.get_shaders();
return { &vs->binding_table, &fs->binding_table };
}
bool VKGSRender::is_current_program_interpreted() const bool VKGSRender::is_current_program_interpreted() const
{ {
return m_program && m_shader_interpreter.is_interpreter(m_program); return m_program && m_shader_interpreter.is_interpreter(m_program);
@ -2222,7 +2215,7 @@ void VKGSRender::update_vertex_env(u32 id, const vk::vertex_upload_info& vertex_
vkCmdPushConstants( vkCmdPushConstants(
*m_current_command_buffer, *m_current_command_buffer,
m_program->layout(), m_pipeline_layout,
VK_SHADER_STAGE_VERTEX_BIT, VK_SHADER_STAGE_VERTEX_BIT,
0, 0,
data_length, data_length,

View file

@ -26,9 +26,6 @@
using namespace vk::vmm_allocation_pool_; // clang workaround. using namespace vk::vmm_allocation_pool_; // clang workaround.
using namespace vk::upscaling_flags_; // ditto using namespace vk::upscaling_flags_; // ditto
using vs_binding_table_t = decltype(VKVertexProgram::binding_table);
using fs_binding_table_t = decltype(VKFragmentProgram::binding_table);
namespace vk namespace vk
{ {
using host_data_t = rsx::host_gpu_context_t; using host_data_t = rsx::host_gpu_context_t;
@ -56,9 +53,6 @@ private:
vk::glsl::program *m_prev_program = nullptr; vk::glsl::program *m_prev_program = nullptr;
vk::pipeline_props m_pipeline_properties; vk::pipeline_props m_pipeline_properties;
const vs_binding_table_t* m_vs_binding_table = nullptr;
const fs_binding_table_t* m_fs_binding_table = nullptr;
vk::texture_cache m_texture_cache; vk::texture_cache m_texture_cache;
vk::surface_cache m_rtts; vk::surface_cache m_rtts;
@ -84,8 +78,6 @@ private:
VkDependencyInfoKHR m_async_compute_dependency_info {}; VkDependencyInfoKHR m_async_compute_dependency_info {};
VkMemoryBarrier2KHR m_async_compute_memory_barrier {}; VkMemoryBarrier2KHR m_async_compute_memory_barrier {};
std::pair<const vs_binding_table_t*, const fs_binding_table_t*> get_binding_table() const;
public: public:
//vk::fbo draw_fbo; //vk::fbo draw_fbo;
std::unique_ptr<vk::vertex_cache> m_vertex_cache; std::unique_ptr<vk::vertex_cache> m_vertex_cache;
@ -113,6 +105,11 @@ private:
vk::command_buffer_chunk* m_current_command_buffer = nullptr; vk::command_buffer_chunk* m_current_command_buffer = nullptr;
std::unique_ptr<vk::buffer> m_host_object_data; std::unique_ptr<vk::buffer> m_host_object_data;
vk::descriptor_pool m_descriptor_pool;
VkDescriptorSetLayout m_descriptor_layouts = VK_NULL_HANDLE;
VkPipelineLayout m_pipeline_layout = VK_NULL_HANDLE;
vk::framebuffer_holder* m_draw_fbo = nullptr; vk::framebuffer_holder* m_draw_fbo = nullptr;
sizeu m_swapchain_dims{}; sizeu m_swapchain_dims{};
@ -223,6 +220,8 @@ private:
void update_draw_state(); void update_draw_state();
void check_present_status(); void check_present_status();
VkDescriptorSet allocate_descriptor_set();
vk::vertex_upload_info upload_vertex_data(); vk::vertex_upload_info upload_vertex_data();
rsx::simple_array<u8> m_scratch_mem; rsx::simple_array<u8> m_scratch_mem;

View file

@ -178,6 +178,8 @@ namespace vk
VkSemaphore acquire_signal_semaphore = VK_NULL_HANDLE; VkSemaphore acquire_signal_semaphore = VK_NULL_HANDLE;
VkSemaphore present_wait_semaphore = VK_NULL_HANDLE; VkSemaphore present_wait_semaphore = VK_NULL_HANDLE;
vk::descriptor_set descriptor_set;
rsx::flags32_t flags = 0; rsx::flags32_t flags = 0;
u32 present_image = -1; u32 present_image = -1;
@ -191,6 +193,7 @@ namespace vk
{ {
present_wait_semaphore = other.present_wait_semaphore; present_wait_semaphore = other.present_wait_semaphore;
acquire_signal_semaphore = other.acquire_signal_semaphore; acquire_signal_semaphore = other.acquire_signal_semaphore;
descriptor_set.swap(other.descriptor_set);
flags = other.flags; flags = other.flags;
heap_snapshot = other.heap_snapshot; heap_snapshot = other.heap_snapshot;
} }

View file

@ -1,5 +1,5 @@
#define VMA_IMPLEMENTATION #define VMA_IMPLEMENTATION
#define VMA_VULKAN_VERSION 1002000 #define VMA_VULKAN_VERSION 1000000
#include "util/atomic.hpp" #include "util/atomic.hpp"
#include "Utilities/mutex.h" #include "Utilities/mutex.h"
@ -36,7 +36,7 @@ private:
#define VMA_RW_MUTEX VmaRWMutex #define VMA_RW_MUTEX VmaRWMutex
#define VMA_MUTEX VmaRWMutex #define VMA_MUTEX VmaRWMutex
#if defined(_MSC_VER) && !defined(__clang__) #ifdef _MSC_VER
#pragma warning(push, 0) #pragma warning(push, 0)
#else #else
#pragma GCC diagnostic push #pragma GCC diagnostic push

View file

@ -43,46 +43,106 @@ namespace vk
if (!m_vao.heap) if (!m_vao.heap)
{ {
m_vao.create(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, 1 * 0x100000, "overlays VAO", 128); m_vao.create(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, 1 * 0x100000, "overlays VAO", 128);
}
if (!m_ubo.heap && m_num_uniform_buffers > 0)
{
m_ubo.create(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, 8 * 0x100000, "overlays UBO", 128); m_ubo.create(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, 8 * 0x100000, "overlays UBO", 128);
} }
} }
void overlay_pass::init_descriptors()
{
rsx::simple_array<VkDescriptorPoolSize> descriptor_pool_sizes = {};
if (m_num_uniform_buffers)
{
descriptor_pool_sizes.push_back({ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, m_num_uniform_buffers });
};
if (m_num_usable_samplers)
{
descriptor_pool_sizes.push_back({ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, m_num_usable_samplers });
}
if (m_num_input_attachments)
{
descriptor_pool_sizes.push_back({ VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, m_num_input_attachments });
}
// Reserve descriptor pools
m_descriptor_pool.create(*m_device, descriptor_pool_sizes);
const auto num_bindings = m_num_uniform_buffers + m_num_usable_samplers + m_num_input_attachments;
rsx::simple_array<VkDescriptorSetLayoutBinding> bindings(num_bindings);
u32 binding_slot = 0;
for (u32 n = 0; n < m_num_uniform_buffers; ++n, ++binding_slot)
{
bindings[binding_slot].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[binding_slot].descriptorCount = 1;
bindings[binding_slot].stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[binding_slot].binding = binding_slot;
bindings[binding_slot].pImmutableSamplers = nullptr;
}
for (u32 n = 0; n < m_num_usable_samplers; ++n, ++binding_slot)
{
bindings[binding_slot].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[binding_slot].descriptorCount = 1;
bindings[binding_slot].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[binding_slot].binding = binding_slot;
bindings[binding_slot].pImmutableSamplers = nullptr;
}
for (u32 n = 0; n < m_num_input_attachments; ++n, ++binding_slot)
{
bindings[binding_slot].descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT;
bindings[binding_slot].descriptorCount = 1;
bindings[binding_slot].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[binding_slot].binding = binding_slot;
bindings[binding_slot].pImmutableSamplers = nullptr;
}
ensure(binding_slot == num_bindings);
m_descriptor_layout = vk::descriptors::create_layout(bindings);
VkPipelineLayoutCreateInfo layout_info = {};
layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
layout_info.setLayoutCount = 1;
layout_info.pSetLayouts = &m_descriptor_layout;
std::vector<VkPushConstantRange> push_constants = get_push_constants();
if (!push_constants.empty())
{
layout_info.pushConstantRangeCount = u32(push_constants.size());
layout_info.pPushConstantRanges = push_constants.data();
}
CHECK_RESULT(vkCreatePipelineLayout(*m_device, &layout_info, nullptr, &m_pipeline_layout));
}
std::vector<vk::glsl::program_input> overlay_pass::get_vertex_inputs() std::vector<vk::glsl::program_input> overlay_pass::get_vertex_inputs()
{ {
check_heap(); check_heap();
return {}; return{};
} }
std::vector<vk::glsl::program_input> overlay_pass::get_fragment_inputs() std::vector<vk::glsl::program_input> overlay_pass::get_fragment_inputs()
{ {
using namespace vk::glsl; std::vector<vk::glsl::program_input> fs_inputs;
std::vector<program_input> fs_inputs;
u32 binding = 0; u32 binding = 0;
for (u32 n = 0; n < m_num_uniform_buffers; ++n, ++binding) for (u32 n = 0; n < m_num_uniform_buffers; ++n, ++binding)
{ {
const std::string name = std::string("static_data") + (n > 0 ? std::to_string(n) : ""); const std::string name = std::string("static_data") + (n > 0 ? std::to_string(n) : "");
const auto input = program_input::make(::glsl::program_domain::glsl_fragment_program, name, program_input_type::input_type_uniform_buffer, 0, 0); fs_inputs.push_back({ ::glsl::program_domain::glsl_fragment_program, vk::glsl::program_input_type::input_type_uniform_buffer,{},{}, 0, name });
fs_inputs.push_back(input);
} }
for (u32 n = 0; n < m_num_usable_samplers; ++n, ++binding) for (u32 n = 0; n < m_num_usable_samplers; ++n, ++binding)
{ {
const std::string name = "fs" + std::to_string(n); fs_inputs.push_back({ ::glsl::program_domain::glsl_fragment_program, vk::glsl::program_input_type::input_type_texture,{},{}, binding, "fs" + std::to_string(n) });
const auto input = program_input::make(::glsl::program_domain::glsl_fragment_program, name, program_input_type::input_type_texture, 0, binding);
fs_inputs.push_back(input);
} }
for (u32 n = 0; n < m_num_input_attachments; ++n, ++binding) for (u32 n = 0; n < m_num_input_attachments; ++n, ++binding)
{ {
const std::string name = "sp" + std::to_string(n); fs_inputs.push_back({ ::glsl::program_domain::glsl_fragment_program, vk::glsl::program_input_type::input_type_texture,{},{}, binding, "sp" + std::to_string(n) });
const auto input = program_input::make(::glsl::program_domain::glsl_fragment_program, name, program_input_type::input_type_texture, 0, binding);
fs_inputs.push_back(input);
} }
return fs_inputs; return fs_inputs;
@ -148,20 +208,20 @@ namespace vk
info.stageCount = 2; info.stageCount = 2;
info.pStages = shader_stages; info.pStages = shader_stages;
info.pDynamicState = &dynamic_state_info; info.pDynamicState = &dynamic_state_info;
info.layout = VK_NULL_HANDLE; info.layout = m_pipeline_layout;
info.basePipelineIndex = -1; info.basePipelineIndex = -1;
info.basePipelineHandle = VK_NULL_HANDLE; info.basePipelineHandle = VK_NULL_HANDLE;
info.renderPass = render_pass; info.renderPass = render_pass;
auto compiler = vk::get_pipe_compiler(); auto compiler = vk::get_pipe_compiler();
auto program = compiler->compile(info, vk::pipe_compiler::COMPILE_INLINE, {}, get_vertex_inputs(), get_fragment_inputs()); auto program = compiler->compile(info, m_pipeline_layout, vk::pipe_compiler::COMPILE_INLINE, {}, get_vertex_inputs(), get_fragment_inputs());
auto result = program.get(); auto result = program.get();
m_program_cache[storage_key] = std::move(program); m_program_cache[storage_key] = std::move(program);
return result; return result;
} }
vk::glsl::program* overlay_pass::load_program(vk::command_buffer& cmd, VkRenderPass pass, const std::vector<vk::image_view*>& src) void overlay_pass::load_program(vk::command_buffer& cmd, VkRenderPass pass, const std::vector<vk::image_view*>& src)
{ {
vk::glsl::program *program = nullptr; vk::glsl::program *program = nullptr;
const auto key = get_pipeline_key(pass); const auto key = get_pipeline_key(pass);
@ -172,6 +232,8 @@ namespace vk
else else
program = build_pipeline(key, pass); program = build_pipeline(key, pass);
m_descriptor_set = m_descriptor_pool.allocate(m_descriptor_layout);
if (!m_sampler && !src.empty()) if (!m_sampler && !src.empty())
{ {
m_sampler = std::make_unique<vk::sampler>(*m_device, m_sampler = std::make_unique<vk::sampler>(*m_device,
@ -183,23 +245,21 @@ namespace vk
if (m_num_uniform_buffers > 0) if (m_num_uniform_buffers > 0)
{ {
program->bind_uniform({ m_ubo.heap->value, m_ubo_offset, std::max(m_ubo_length, 4u) }, 0, 0); program->bind_uniform({ m_ubo.heap->value, m_ubo_offset, std::max(m_ubo_length, 4u) }, 0, m_descriptor_set);
} }
for (uint n = 0; n < src.size(); ++n) for (uint n = 0; n < src.size(); ++n)
{ {
VkDescriptorImageInfo info = { m_sampler->value, src[n]->value, src[n]->image()->current_layout }; VkDescriptorImageInfo info = { m_sampler->value, src[n]->value, src[n]->image()->current_layout };
const auto [set, location] = program->get_uniform_location(::glsl::glsl_fragment_program, glsl::input_type_texture, "fs" + std::to_string(n)); program->bind_uniform(info, "fs" + std::to_string(n), VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, m_descriptor_set);
program->bind_uniform(info, set, location);
} }
program->bind(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS); vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, program->pipeline);
m_descriptor_set.bind(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline_layout);
VkBuffer buffers = m_vao.heap->value; VkBuffer buffers = m_vao.heap->value;
VkDeviceSize offsets = m_vao_offset; VkDeviceSize offsets = m_vao_offset;
vkCmdBindVertexBuffers(cmd, 0, 1, &buffers, &offsets); vkCmdBindVertexBuffers(cmd, 0, 1, &buffers, &offsets);
return program;
} }
void overlay_pass::create(const vk::render_device& dev) void overlay_pass::create(const vk::render_device& dev)
@ -207,6 +267,8 @@ namespace vk
if (!initialized) if (!initialized)
{ {
m_device = &dev; m_device = &dev;
init_descriptors();
initialized = true; initialized = true;
} }
} }
@ -220,6 +282,10 @@ namespace vk
m_program_cache.clear(); m_program_cache.clear();
m_sampler.reset(); m_sampler.reset();
vkDestroyDescriptorSetLayout(*m_device, m_descriptor_layout, nullptr);
vkDestroyPipelineLayout(*m_device, m_pipeline_layout, nullptr);
m_descriptor_pool.destroy();
initialized = false; initialized = false;
} }
} }
@ -237,7 +303,7 @@ namespace vk
return vk::get_framebuffer(dev, target->width(), target->height(), m_num_input_attachments > 0, render_pass, { target }); return vk::get_framebuffer(dev, target->width(), target->height(), m_num_input_attachments > 0, render_pass, { target });
} }
void overlay_pass::emit_geometry(vk::command_buffer& cmd, glsl::program* /*program*/) void overlay_pass::emit_geometry(vk::command_buffer& cmd)
{ {
vkCmdDraw(cmd, num_drawable_elements, 1, first_vertex, 0); vkCmdDraw(cmd, num_drawable_elements, 1, first_vertex, 0);
} }
@ -262,11 +328,11 @@ namespace vk
// This call clobbers dynamic state // This call clobbers dynamic state
cmd.flags |= vk::command_buffer::cb_reload_dynamic_state; cmd.flags |= vk::command_buffer::cb_reload_dynamic_state;
auto program = load_program(cmd, render_pass, src); load_program(cmd, render_pass, src);
set_up_viewport(cmd, viewport.x1, viewport.y1, viewport.width(), viewport.height()); set_up_viewport(cmd, viewport.x1, viewport.y1, viewport.width(), viewport.height());
vk::begin_renderpass(cmd, render_pass, fbo->value, { positionu{0u, 0u}, sizeu{fbo->width(), fbo->height()} }); vk::begin_renderpass(cmd, render_pass, fbo->value, { positionu{0u, 0u}, sizeu{fbo->width(), fbo->height()} });
emit_geometry(cmd, program); emit_geometry(cmd);
} }
void overlay_pass::run(vk::command_buffer& cmd, const areau& viewport, vk::image* target, const std::vector<vk::image_view*>& src, VkRenderPass render_pass) void overlay_pass::run(vk::command_buffer& cmd, const areau& viewport, vk::image* target, const std::vector<vk::image_view*>& src, VkRenderPass render_pass)
@ -310,7 +376,6 @@ namespace vk
// 2 input textures // 2 input textures
m_num_usable_samplers = 2; m_num_usable_samplers = 2;
m_num_uniform_buffers = 0;
renderpass_config.set_attachment_count(1); renderpass_config.set_attachment_count(1);
renderpass_config.set_color_mask(0, true, true, true, true); renderpass_config.set_color_mask(0, true, true, true, true);
@ -485,39 +550,24 @@ namespace vk
false, true, desc->get_data(), owner_uid); false, true, desc->get_data(), owner_uid);
} }
std::vector<vk::glsl::program_input> ui_overlay_renderer::get_vertex_inputs() std::vector<VkPushConstantRange> ui_overlay_renderer::get_push_constants()
{ {
auto result = overlay_pass::get_vertex_inputs(); return
result.push_back( {
glsl::program_input::make( {
::glsl::glsl_vertex_program, .stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
"push_constants", .offset = 0,
glsl::input_type_push_constant, .size = 68
0, },
0, {
glsl::push_constant_ref { .size = 68 } .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
) .offset = 68,
); .size = 12
return result; }
};
} }
std::vector<vk::glsl::program_input> ui_overlay_renderer::get_fragment_inputs() void ui_overlay_renderer::update_uniforms(vk::command_buffer& cmd, vk::glsl::program* /*program*/)
{
auto result = overlay_pass::get_fragment_inputs();
result.push_back(
glsl::program_input::make(
::glsl::glsl_fragment_program,
"push_constants",
glsl::input_type_push_constant,
0,
0,
glsl::push_constant_ref {.offset = 68, .size = 12 }
)
);
return result;
}
void ui_overlay_renderer::update_uniforms(vk::command_buffer& cmd, vk::glsl::program* program)
{ {
// Byte Layout // Byte Layout
// 00: vec4 ui_scale; // 00: vec4 ui_scale;
@ -550,7 +600,7 @@ namespace vk
.get(); .get();
push_buf[16] = std::bit_cast<f32>(vert_config); push_buf[16] = std::bit_cast<f32>(vert_config);
vkCmdPushConstants(cmd, program->layout(), VK_SHADER_STAGE_VERTEX_BIT, 0, 68, push_buf); vkCmdPushConstants(cmd, m_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, 0, 68, push_buf);
// 2. Fragment stuff // 2. Fragment stuff
rsx::overlays::fragment_options frag_opts; rsx::overlays::fragment_options frag_opts;
@ -564,7 +614,7 @@ namespace vk
push_buf[1] = m_time; push_buf[1] = m_time;
push_buf[2] = m_blur_strength; push_buf[2] = m_blur_strength;
vkCmdPushConstants(cmd, program->layout(), VK_SHADER_STAGE_FRAGMENT_BIT, 68, 12, push_buf); vkCmdPushConstants(cmd, m_pipeline_layout, VK_SHADER_STAGE_FRAGMENT_BIT, 68, 12, push_buf);
} }
void ui_overlay_renderer::set_primitive_type(rsx::overlays::primitive_type type) void ui_overlay_renderer::set_primitive_type(rsx::overlays::primitive_type type)
@ -591,7 +641,7 @@ namespace vk
} }
} }
void ui_overlay_renderer::emit_geometry(vk::command_buffer& cmd, glsl::program* program) void ui_overlay_renderer::emit_geometry(vk::command_buffer& cmd)
{ {
if (m_current_primitive_type == rsx::overlays::primitive_type::quad_list) if (m_current_primitive_type == rsx::overlays::primitive_type::quad_list)
{ {
@ -607,7 +657,7 @@ namespace vk
} }
else else
{ {
overlay_pass::emit_geometry(cmd, program); overlay_pass::emit_geometry(cmd);
} }
} }
@ -709,30 +759,22 @@ namespace vk
// Disable samplers // Disable samplers
m_num_usable_samplers = 0; m_num_usable_samplers = 0;
// Disable UBOs
m_num_uniform_buffers = 0;
renderpass_config.set_depth_mask(false); renderpass_config.set_depth_mask(false);
renderpass_config.set_color_mask(0, true, true, true, true); renderpass_config.set_color_mask(0, true, true, true, true);
renderpass_config.set_attachment_count(1); renderpass_config.set_attachment_count(1);
} }
std::vector<vk::glsl::program_input> attachment_clear_pass::get_vertex_inputs() std::vector<VkPushConstantRange> attachment_clear_pass::get_push_constants()
{ {
check_heap(); VkPushConstantRange constant;
return constant.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
{ constant.offset = 0;
vk::glsl::program_input::make( constant.size = 32;
::glsl::glsl_vertex_program,
"push_constants", return { constant };
vk::glsl::input_type_push_constant,
0,
0,
glsl::push_constant_ref{ .size = 32 })
};
} }
void attachment_clear_pass::update_uniforms(vk::command_buffer& cmd, vk::glsl::program* program) void attachment_clear_pass::update_uniforms(vk::command_buffer& cmd, vk::glsl::program* /*program*/)
{ {
f32 data[8]; f32 data[8];
data[0] = clear_color.r; data[0] = clear_color.r;
@ -744,7 +786,7 @@ namespace vk
data[6] = colormask.b; data[6] = colormask.b;
data[7] = colormask.a; data[7] = colormask.a;
vkCmdPushConstants(cmd, program->layout(), VK_SHADER_STAGE_VERTEX_BIT, 0, 32, data); vkCmdPushConstants(cmd, m_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, 0, 32, data);
} }
void attachment_clear_pass::set_up_viewport(vk::command_buffer& cmd, u32 x, u32 y, u32 w, u32 h) void attachment_clear_pass::set_up_viewport(vk::command_buffer& cmd, u32 x, u32 y, u32 w, u32 h)
@ -808,9 +850,6 @@ namespace vk
"{\n" "{\n"
" out_color = vec4(0.);\n" " out_color = vec4(0.);\n"
"}\n"; "}\n";
m_num_uniform_buffers = 0;
m_num_usable_samplers = 0;
} }
void stencil_clear_pass::set_up_viewport(vk::command_buffer& cmd, u32 x, u32 y, u32 w, u32 h) void stencil_clear_pass::set_up_viewport(vk::command_buffer& cmd, u32 x, u32 y, u32 w, u32 h)
@ -859,7 +898,7 @@ namespace vk
std::pair<std::string_view, std::string> repl_list[] = std::pair<std::string_view, std::string> repl_list[] =
{ {
{ "%sampler_binding", "x" }, { "%sampler_binding", fmt::format("(%d + x)", sampler_location(0)) },
{ "%set_decorator", "set=0" }, { "%set_decorator", "set=0" },
}; };
fs_src = fmt::replace_all(fs_src, repl_list); fs_src = fmt::replace_all(fs_src, repl_list);
@ -869,28 +908,21 @@ namespace vk
renderpass_config.set_attachment_count(1); renderpass_config.set_attachment_count(1);
m_num_usable_samplers = 2; m_num_usable_samplers = 2;
m_num_uniform_buffers = 0;
} }
std::vector<vk::glsl::program_input> video_out_calibration_pass::get_fragment_inputs() std::vector<VkPushConstantRange> video_out_calibration_pass::get_push_constants()
{ {
auto result = overlay_pass::get_fragment_inputs(); VkPushConstantRange constant;
result.push_back( constant.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
vk::glsl::program_input::make( constant.offset = 0;
::glsl::glsl_fragment_program, constant.size = 16;
"push_constants",
vk::glsl::input_type_push_constant, return { constant };
0,
0,
glsl::push_constant_ref{ .size = 16 }
)
);
return result;
} }
void video_out_calibration_pass::update_uniforms(vk::command_buffer& cmd, vk::glsl::program* program) void video_out_calibration_pass::update_uniforms(vk::command_buffer& cmd, vk::glsl::program* /*program*/)
{ {
vkCmdPushConstants(cmd, program->layout(), VK_SHADER_STAGE_FRAGMENT_BIT, 0, 16, config.data); vkCmdPushConstants(cmd, m_pipeline_layout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, 16, config.data);
} }
void video_out_calibration_pass::run(vk::command_buffer& cmd, const areau& viewport, vk::framebuffer* target, void video_out_calibration_pass::run(vk::command_buffer& cmd, const areau& viewport, vk::framebuffer* target,

View file

@ -44,6 +44,11 @@ namespace vk
vk::glsl::shader m_vertex_shader; vk::glsl::shader m_vertex_shader;
vk::glsl::shader m_fragment_shader; vk::glsl::shader m_fragment_shader;
vk::descriptor_pool m_descriptor_pool;
descriptor_set m_descriptor_set;
VkDescriptorSetLayout m_descriptor_layout = nullptr;
VkPipelineLayout m_pipeline_layout = nullptr;
VkFilter m_sampler_filter = VK_FILTER_LINEAR; VkFilter m_sampler_filter = VK_FILTER_LINEAR;
u32 m_num_usable_samplers = 1; u32 m_num_usable_samplers = 1;
u32 m_num_input_attachments = 0; u32 m_num_input_attachments = 0;
@ -78,6 +83,8 @@ namespace vk
void check_heap(); void check_heap();
void init_descriptors();
virtual void update_uniforms(vk::command_buffer& /*cmd*/, vk::glsl::program* /*program*/) {} virtual void update_uniforms(vk::command_buffer& /*cmd*/, vk::glsl::program* /*program*/) {}
virtual std::vector<vk::glsl::program_input> get_vertex_inputs(); virtual std::vector<vk::glsl::program_input> get_vertex_inputs();
@ -85,6 +92,11 @@ namespace vk
virtual void get_dynamic_state_entries(std::vector<VkDynamicState>& /*state_descriptors*/) {} virtual void get_dynamic_state_entries(std::vector<VkDynamicState>& /*state_descriptors*/) {}
virtual std::vector<VkPushConstantRange> get_push_constants()
{
return {};
}
int sampler_location(int index) const { return 1 + index; } int sampler_location(int index) const { return 1 + index; }
int input_attachment_location(int index) const { return 1 + m_num_usable_samplers + index; } int input_attachment_location(int index) const { return 1 + m_num_usable_samplers + index; }
@ -101,7 +113,8 @@ namespace vk
} }
vk::glsl::program* build_pipeline(u64 storage_key, VkRenderPass render_pass); vk::glsl::program* build_pipeline(u64 storage_key, VkRenderPass render_pass);
vk::glsl::program* load_program(vk::command_buffer& cmd, VkRenderPass pass, const std::vector<vk::image_view*>& src);
void load_program(vk::command_buffer& cmd, VkRenderPass pass, const std::vector<vk::image_view*>& src);
virtual void create(const vk::render_device& dev); virtual void create(const vk::render_device& dev);
virtual void destroy(); virtual void destroy();
@ -110,7 +123,7 @@ namespace vk
vk::framebuffer* get_framebuffer(vk::image* target, VkRenderPass render_pass); vk::framebuffer* get_framebuffer(vk::image* target, VkRenderPass render_pass);
virtual void emit_geometry(vk::command_buffer& cmd, glsl::program* program); virtual void emit_geometry(vk::command_buffer& cmd);
virtual void set_up_viewport(vk::command_buffer& cmd, u32 x, u32 y, u32 w, u32 h); virtual void set_up_viewport(vk::command_buffer& cmd, u32 x, u32 y, u32 w, u32 h);
@ -156,14 +169,13 @@ namespace vk
vk::image_view* find_font(rsx::overlays::font* font, vk::command_buffer& cmd, vk::data_heap& upload_heap); vk::image_view* find_font(rsx::overlays::font* font, vk::command_buffer& cmd, vk::data_heap& upload_heap);
vk::image_view* find_temp_image(rsx::overlays::image_info_base* desc, vk::command_buffer& cmd, vk::data_heap& upload_heap, u32 owner_uid); vk::image_view* find_temp_image(rsx::overlays::image_info_base* desc, vk::command_buffer& cmd, vk::data_heap& upload_heap, u32 owner_uid);
std::vector<vk::glsl::program_input> get_vertex_inputs() override; std::vector<VkPushConstantRange> get_push_constants() override;
std::vector<vk::glsl::program_input> get_fragment_inputs() override;
void update_uniforms(vk::command_buffer& cmd, vk::glsl::program* program) override; void update_uniforms(vk::command_buffer& cmd, vk::glsl::program* program) override;
void set_primitive_type(rsx::overlays::primitive_type type); void set_primitive_type(rsx::overlays::primitive_type type);
void emit_geometry(vk::command_buffer& cmd, glsl::program* program) override; void emit_geometry(vk::command_buffer& cmd) override;
void run(vk::command_buffer& cmd, const areau& viewport, vk::framebuffer* target, VkRenderPass render_pass, void run(vk::command_buffer& cmd, const areau& viewport, vk::framebuffer* target, VkRenderPass render_pass,
vk::data_heap& upload_heap, rsx::overlays::overlay& ui); vk::data_heap& upload_heap, rsx::overlays::overlay& ui);
@ -177,7 +189,7 @@ namespace vk
attachment_clear_pass(); attachment_clear_pass();
std::vector<vk::glsl::program_input> get_vertex_inputs() override; std::vector<VkPushConstantRange> get_push_constants() override;
void update_uniforms(vk::command_buffer& cmd, vk::glsl::program* program) override; void update_uniforms(vk::command_buffer& cmd, vk::glsl::program* program) override;
@ -215,7 +227,7 @@ namespace vk
video_out_calibration_pass(); video_out_calibration_pass();
std::vector<vk::glsl::program_input> get_fragment_inputs() override; std::vector<VkPushConstantRange> get_push_constants() override;
void update_uniforms(vk::command_buffer& cmd, vk::glsl::program* /*program*/) override; void update_uniforms(vk::command_buffer& cmd, vk::glsl::program* /*program*/) override;

View file

@ -36,12 +36,12 @@ namespace vk
{ {
if (job.is_graphics_job) if (job.is_graphics_job)
{ {
auto compiled = int_compile_graphics_pipe(job.graphics_data, job.graphics_modules, job.inputs, {}, job.flags); auto compiled = int_compile_graphics_pipe(job.graphics_data, job.graphics_modules, job.pipe_layout, job.inputs, {});
job.callback_func(compiled); job.callback_func(compiled);
} }
else else
{ {
auto compiled = int_compile_compute_pipe(job.compute_data, job.inputs, job.flags); auto compiled = int_compile_compute_pipe(job.compute_data, job.pipe_layout);
job.callback_func(compiled); job.callback_func(compiled);
} }
} }
@ -50,33 +50,25 @@ namespace vk
} }
} }
std::unique_ptr<glsl::program> pipe_compiler::int_compile_compute_pipe( std::unique_ptr<glsl::program> pipe_compiler::int_compile_compute_pipe(const VkComputePipelineCreateInfo& create_info, VkPipelineLayout pipe_layout)
const VkComputePipelineCreateInfo& create_info,
const std::vector<glsl::program_input>& cs_inputs,
op_flags flags)
{ {
auto program = std::make_unique<glsl::program>(*m_device, create_info, cs_inputs); VkPipeline pipeline;
program->link(flags & SEPARATE_SHADER_OBJECTS); vkCreateComputePipelines(*g_render_device, nullptr, 1, &create_info, nullptr, &pipeline);
return program; return std::make_unique<vk::glsl::program>(*m_device, pipeline, pipe_layout);
} }
std::unique_ptr<glsl::program> pipe_compiler::int_compile_graphics_pipe( std::unique_ptr<glsl::program> pipe_compiler::int_compile_graphics_pipe(const VkGraphicsPipelineCreateInfo& create_info, VkPipelineLayout pipe_layout,
const VkGraphicsPipelineCreateInfo& create_info, const std::vector<glsl::program_input>& vs_inputs, const std::vector<glsl::program_input>& fs_inputs)
const std::vector<glsl::program_input>& vs_inputs,
const std::vector<glsl::program_input>& fs_inputs,
op_flags flags)
{ {
auto program = std::make_unique<glsl::program>(*m_device, create_info, vs_inputs, fs_inputs); VkPipeline pipeline;
program->link(flags & SEPARATE_SHADER_OBJECTS); CHECK_RESULT(vkCreateGraphicsPipelines(*m_device, VK_NULL_HANDLE, 1, &create_info, nullptr, &pipeline));
return program; auto result = std::make_unique<vk::glsl::program>(*m_device, pipeline, pipe_layout, vs_inputs, fs_inputs);
result->link();
return result;
} }
std::unique_ptr<glsl::program> pipe_compiler::int_compile_graphics_pipe( std::unique_ptr<glsl::program> pipe_compiler::int_compile_graphics_pipe(const vk::pipeline_props &create_info, VkShaderModule modules[2], VkPipelineLayout pipe_layout,
const vk::pipeline_props &create_info, const std::vector<glsl::program_input>& vs_inputs, const std::vector<glsl::program_input>& fs_inputs)
VkShaderModule modules[2],
const std::vector<glsl::program_input>& vs_inputs,
const std::vector<glsl::program_input>& fs_inputs,
op_flags flags)
{ {
VkPipelineShaderStageCreateInfo shader_stages[2] = {}; VkPipelineShaderStageCreateInfo shader_stages[2] = {};
shader_stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; shader_stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
@ -165,54 +157,52 @@ namespace vk
info.stageCount = 2; info.stageCount = 2;
info.pStages = shader_stages; info.pStages = shader_stages;
info.pDynamicState = &dynamic_state_info; info.pDynamicState = &dynamic_state_info;
info.layout = VK_NULL_HANDLE; info.layout = pipe_layout;
info.basePipelineIndex = -1; info.basePipelineIndex = -1;
info.basePipelineHandle = VK_NULL_HANDLE; info.basePipelineHandle = VK_NULL_HANDLE;
info.renderPass = vk::get_renderpass(*m_device, create_info.renderpass_key); info.renderPass = vk::get_renderpass(*m_device, create_info.renderpass_key);
return int_compile_graphics_pipe(info, vs_inputs, fs_inputs, flags); return int_compile_graphics_pipe(info, pipe_layout, vs_inputs, fs_inputs);
} }
std::unique_ptr<glsl::program> pipe_compiler::compile( std::unique_ptr<glsl::program> pipe_compiler::compile(
const VkComputePipelineCreateInfo& create_info, const VkComputePipelineCreateInfo& create_info,
op_flags flags, callback_t callback, VkPipelineLayout pipe_layout,
const std::vector<glsl::program_input>& cs_inputs) op_flags flags, callback_t callback)
{ {
if (flags & COMPILE_INLINE) if (flags == COMPILE_INLINE)
{ {
return int_compile_compute_pipe(create_info, cs_inputs, flags); return int_compile_compute_pipe(create_info, pipe_layout);
} }
m_work_queue.push(create_info, cs_inputs, flags, callback); m_work_queue.push(create_info, pipe_layout, callback);
return {}; return {};
} }
std::unique_ptr<glsl::program> pipe_compiler::compile( std::unique_ptr<glsl::program> pipe_compiler::compile(
const VkGraphicsPipelineCreateInfo& create_info, const VkGraphicsPipelineCreateInfo& create_info,
VkPipelineLayout pipe_layout,
op_flags flags, callback_t /*callback*/, op_flags flags, callback_t /*callback*/,
const std::vector<glsl::program_input>& vs_inputs, const std::vector<glsl::program_input>& vs_inputs, const std::vector<glsl::program_input>& fs_inputs)
const std::vector<glsl::program_input>& fs_inputs)
{ {
// It is very inefficient to defer this as all pointers need to be saved // It is very inefficient to defer this as all pointers need to be saved
ensure(flags & COMPILE_INLINE); ensure(flags == COMPILE_INLINE);
return int_compile_graphics_pipe(create_info, vs_inputs, fs_inputs, flags); return int_compile_graphics_pipe(create_info, pipe_layout, vs_inputs, fs_inputs);
} }
std::unique_ptr<glsl::program> pipe_compiler::compile( std::unique_ptr<glsl::program> pipe_compiler::compile(
const vk::pipeline_props &create_info, const vk::pipeline_props& create_info,
VkShaderModule vs, VkShaderModule module_handles[2],
VkShaderModule fs, VkPipelineLayout pipe_layout,
op_flags flags, callback_t callback, op_flags flags, callback_t callback,
const std::vector<glsl::program_input>& vs_inputs, const std::vector<glsl::program_input>& vs_inputs, const std::vector<glsl::program_input>& fs_inputs)
const std::vector<glsl::program_input>& fs_inputs)
{ {
VkShaderModule modules[] = { vs, fs }; if (flags == COMPILE_INLINE)
if (flags & COMPILE_INLINE)
{ {
return int_compile_graphics_pipe(create_info, modules, vs_inputs, fs_inputs, flags); return int_compile_graphics_pipe(create_info, module_handles, pipe_layout, vs_inputs, fs_inputs);
} }
m_work_queue.push(create_info, modules, vs_inputs, fs_inputs, flags, callback); m_work_queue.push(create_info, pipe_layout, module_handles, vs_inputs, fs_inputs, callback);
return {}; return {};
} }

View file

@ -53,16 +53,13 @@ namespace vk
class pipe_compiler class pipe_compiler
{ {
public: public:
enum op_flag_bits enum op_flags
{ {
COMPILE_DEFAULT = 0, COMPILE_DEFAULT = 0,
COMPILE_INLINE = 1, COMPILE_INLINE = 1,
COMPILE_DEFERRED = 2, COMPILE_DEFERRED = 2
SEPARATE_SHADER_OBJECTS = 4
}; };
using op_flags = rsx::flags32_t;
using callback_t = std::function<void(std::unique_ptr<glsl::program>&)>; using callback_t = std::function<void(std::unique_ptr<glsl::program>&)>;
pipe_compiler(); pipe_compiler();
@ -71,20 +68,21 @@ namespace vk
void initialize(const vk::render_device* pdev); void initialize(const vk::render_device* pdev);
std::unique_ptr<glsl::program> compile( std::unique_ptr<glsl::program> compile(
const VkComputePipelineCreateInfo& cs, const VkComputePipelineCreateInfo& create_info,
op_flags flags, callback_t callback = {}, VkPipelineLayout pipe_layout,
const std::vector<glsl::program_input>& cs_inputs = {}); op_flags flags, callback_t callback = {});
std::unique_ptr<glsl::program> compile( std::unique_ptr<glsl::program> compile(
const VkGraphicsPipelineCreateInfo& create_info, const VkGraphicsPipelineCreateInfo& create_info,
VkPipelineLayout pipe_layout,
op_flags flags, callback_t callback = {}, op_flags flags, callback_t callback = {},
const std::vector<glsl::program_input>& vs_inputs = {}, const std::vector<glsl::program_input>& vs_inputs = {},
const std::vector<glsl::program_input>& fs_inputs = {}); const std::vector<glsl::program_input>& fs_inputs = {});
std::unique_ptr<glsl::program> compile( std::unique_ptr<glsl::program> compile(
const vk::pipeline_props &create_info, const vk::pipeline_props &create_info,
VkShaderModule vs, VkShaderModule module_handles[2],
VkShaderModule fs, VkPipelineLayout pipe_layout,
op_flags flags, callback_t callback = {}, op_flags flags, callback_t callback = {},
const std::vector<glsl::program_input>& vs_inputs = {}, const std::vector<glsl::program_input>& vs_inputs = {},
const std::vector<glsl::program_input>& fs_inputs = {}); const std::vector<glsl::program_input>& fs_inputs = {});
@ -114,25 +112,24 @@ namespace vk
vk::pipeline_props graphics_data; vk::pipeline_props graphics_data;
compute_pipeline_props compute_data; compute_pipeline_props compute_data;
VkPipelineLayout pipe_layout;
VkShaderModule graphics_modules[2]; VkShaderModule graphics_modules[2];
std::vector<glsl::program_input> inputs; std::vector<glsl::program_input> inputs;
op_flags flags;
pipe_compiler_job( pipe_compiler_job(
const vk::pipeline_props& props, const vk::pipeline_props& props,
VkPipelineLayout layout,
VkShaderModule modules[2], VkShaderModule modules[2],
const std::vector<glsl::program_input>& vs_in, const std::vector<glsl::program_input>& vs_in,
const std::vector<glsl::program_input>& fs_in, const std::vector<glsl::program_input>& fs_in,
op_flags flags_,
callback_t func) callback_t func)
{ {
callback_func = func; callback_func = func;
graphics_data = props; graphics_data = props;
pipe_layout = layout;
graphics_modules[0] = modules[0]; graphics_modules[0] = modules[0];
graphics_modules[1] = modules[1]; graphics_modules[1] = modules[1];
is_graphics_job = true; is_graphics_job = true;
flags = flags_;
inputs.reserve(vs_in.size() + fs_in.size()); inputs.reserve(vs_in.size() + fs_in.size());
inputs.insert(inputs.end(), vs_in.begin(), vs_in.end()); inputs.insert(inputs.end(), vs_in.begin(), vs_in.end());
@ -141,42 +138,24 @@ namespace vk
pipe_compiler_job( pipe_compiler_job(
const VkComputePipelineCreateInfo& props, const VkComputePipelineCreateInfo& props,
const std::vector<glsl::program_input>& cs_in, VkPipelineLayout layout,
op_flags flags_,
callback_t func) callback_t func)
{ {
callback_func = func; callback_func = func;
compute_data = props; compute_data = props;
pipe_layout = layout;
is_graphics_job = false; is_graphics_job = false;
flags = flags_;
graphics_modules[0] = VK_NULL_HANDLE;
graphics_modules[1] = VK_NULL_HANDLE;
inputs = cs_in;
} }
}; };
const vk::render_device* m_device = nullptr; const vk::render_device* m_device = nullptr;
lf_queue<pipe_compiler_job> m_work_queue; lf_queue<pipe_compiler_job> m_work_queue;
std::unique_ptr<glsl::program> int_compile_compute_pipe( std::unique_ptr<glsl::program> int_compile_compute_pipe(const VkComputePipelineCreateInfo& create_info, VkPipelineLayout pipe_layout);
const VkComputePipelineCreateInfo& create_info, std::unique_ptr<glsl::program> int_compile_graphics_pipe(const VkGraphicsPipelineCreateInfo& create_info, VkPipelineLayout pipe_layout,
const std::vector<glsl::program_input>& cs_inputs, const std::vector<glsl::program_input>& vs_inputs, const std::vector<glsl::program_input>& fs_inputs);
op_flags flags); std::unique_ptr<glsl::program> int_compile_graphics_pipe(const vk::pipeline_props &create_info, VkShaderModule modules[2], VkPipelineLayout pipe_layout,
const std::vector<glsl::program_input>& vs_inputs, const std::vector<glsl::program_input>& fs_inputs);
std::unique_ptr<glsl::program> int_compile_graphics_pipe(
const VkGraphicsPipelineCreateInfo& create_info,
const std::vector<glsl::program_input>& vs_inputs,
const std::vector<glsl::program_input>& fs_inputs,
op_flags flags);
std::unique_ptr<glsl::program> int_compile_graphics_pipe(
const vk::pipeline_props &create_info,
VkShaderModule modules[2],
const std::vector<glsl::program_input>& vs_inputs,
const std::vector<glsl::program_input>& fs_inputs,
op_flags flags);
}; };
void initialize_pipe_compiler(int num_worker_threads = -1); void initialize_pipe_compiler(int num_worker_threads = -1);

View file

@ -46,16 +46,15 @@ namespace vk
const fragment_program_type& fragmentProgramData, const fragment_program_type& fragmentProgramData,
const vk::pipeline_props& pipelineProperties, const vk::pipeline_props& pipelineProperties,
bool compile_async, bool compile_async,
std::function<pipeline_type*(pipeline_storage_type&)> callback) std::function<pipeline_type*(pipeline_storage_type&)> callback,
VkPipelineLayout common_pipeline_layout)
{ {
vk::pipe_compiler::op_flags compiler_flags = compile_async ? vk::pipe_compiler::COMPILE_DEFERRED : vk::pipe_compiler::COMPILE_INLINE; const auto compiler_flags = compile_async ? vk::pipe_compiler::COMPILE_DEFERRED : vk::pipe_compiler::COMPILE_INLINE;
compiler_flags |= vk::pipe_compiler::SEPARATE_SHADER_OBJECTS; VkShaderModule modules[2] = { vertexProgramData.handle, fragmentProgramData.handle };
auto compiler = vk::get_pipe_compiler(); auto compiler = vk::get_pipe_compiler();
auto result = compiler->compile( auto result = compiler->compile(
pipelineProperties, pipelineProperties, modules, common_pipeline_layout,
vertexProgramData.handle,
fragmentProgramData.handle,
compiler_flags, callback, compiler_flags, callback,
vertexProgramData.uniforms, vertexProgramData.uniforms,
fragmentProgramData.uniforms); fragmentProgramData.uniforms);

View file

@ -1,6 +1,5 @@
#include "stdafx.h" #include "stdafx.h"
#include "VKProgramPipeline.h" #include "VKProgramPipeline.h"
#include "VKResourceManager.h"
#include "vkutils/descriptors.h" #include "vkutils/descriptors.h"
#include "vkutils/device.h" #include "vkutils/device.h"
@ -8,85 +7,10 @@
namespace vk namespace vk
{ {
extern const vk::render_device* get_current_renderer();
namespace glsl namespace glsl
{ {
using namespace ::glsl; using namespace ::glsl;
bool operator == (const descriptor_slot_t& a, const VkDescriptorImageInfo& b)
{
const auto ptr = std::get_if<VkDescriptorImageInfo>(&a);
return !!ptr &&
ptr->imageView == b.imageView &&
ptr->sampler == b.sampler &&
ptr->imageLayout == b.imageLayout;
}
bool operator == (const descriptor_slot_t& a, const VkDescriptorBufferInfo& b)
{
const auto ptr = std::get_if<VkDescriptorBufferInfo>(&a);
return !!ptr &&
ptr->buffer == b.buffer &&
ptr->offset == b.offset &&
ptr->range == b.range;
}
bool operator == (const descriptor_slot_t& a, const VkBufferView& b)
{
const auto ptr = std::get_if<VkBufferView>(&a);
return !!ptr && *ptr == b;
}
VkDescriptorType to_descriptor_type(program_input_type type)
{
switch (type)
{
case input_type_uniform_buffer:
return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
case input_type_texel_buffer:
return VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
case input_type_texture:
return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
case input_type_storage_buffer:
return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
case input_type_storage_texture:
return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
default:
fmt::throw_exception("Unexpected program input type %d", static_cast<int>(type));
}
}
VkShaderStageFlags to_shader_stage_flags(::glsl::program_domain domain)
{
switch (domain)
{
case glsl_vertex_program:
return VK_SHADER_STAGE_VERTEX_BIT;
case glsl_fragment_program:
return VK_SHADER_STAGE_FRAGMENT_BIT;
case glsl_compute_program:
return VK_SHADER_STAGE_COMPUTE_BIT;
default:
fmt::throw_exception("Unexpected domain %d", static_cast<int>(domain));
}
}
const char* to_string(::glsl::program_domain domain)
{
switch (domain)
{
case glsl_vertex_program:
return "vertex";
case glsl_fragment_program:
return "fragment";
case glsl_compute_program:
return "compute";
default:
fmt::throw_exception("Unexpected domain %d", static_cast<int>(domain));
}
}
void shader::create(::glsl::program_domain domain, const std::string& source) void shader::create(::glsl::program_domain domain, const std::string& source)
{ {
type = domain; type = domain;
@ -99,8 +23,11 @@ namespace vk
if (!spirv::compile_glsl_to_spv(m_compiled, m_source, type, ::glsl::glsl_rules_vulkan)) if (!spirv::compile_glsl_to_spv(m_compiled, m_source, type, ::glsl::glsl_rules_vulkan))
{ {
const std::string shader_type = type == ::glsl::program_domain::glsl_vertex_program ? "vertex" :
type == ::glsl::program_domain::glsl_fragment_program ? "fragment" : "compute";
rsx_log.notice("%s", m_source); rsx_log.notice("%s", m_source);
fmt::throw_exception("Failed to compile %s shader", to_string(type)); fmt::throw_exception("Failed to compile %s shader", shader_type);
} }
VkShaderModuleCreateInfo vs_info; VkShaderModuleCreateInfo vs_info;
@ -142,505 +69,165 @@ namespace vk
return m_handle; return m_handle;
} }
void program::init() void program::create_impl()
{ {
m_linked = false; linked = false;
attribute_location_mask = 0;
vertex_attributes_mask = 0;
fs_texture_bindings.fill(~0u);
fs_texture_mirror_bindings.fill(~0u);
vs_texture_bindings.fill(~0u);
} }
program::program(VkDevice dev, const VkGraphicsPipelineCreateInfo& create_info, const std::vector<program_input> &vertex_inputs, const std::vector<program_input>& fragment_inputs) program::program(VkDevice dev, VkPipeline p, VkPipelineLayout layout, const std::vector<program_input> &vertex_input, const std::vector<program_input>& fragment_inputs)
: m_device(dev), m_info(create_info) : m_device(dev), pipeline(p), pipeline_layout(layout)
{ {
init(); create_impl();
load_uniforms(vertex_input);
load_uniforms(vertex_inputs);
load_uniforms(fragment_inputs); load_uniforms(fragment_inputs);
} }
program::program(VkDevice dev, const VkComputePipelineCreateInfo& create_info, const std::vector<program_input>& compute_inputs) program::program(VkDevice dev, VkPipeline p, VkPipelineLayout layout)
: m_device(dev), m_info(create_info) : m_device(dev), pipeline(p), pipeline_layout(layout)
{ {
init(); create_impl();
load_uniforms(compute_inputs);
} }
program::~program() program::~program()
{ {
vkDestroyPipeline(m_device, m_pipeline, nullptr); vkDestroyPipeline(m_device, pipeline, nullptr);
if (m_pipeline_layout)
{
vkDestroyPipelineLayout(m_device, m_pipeline_layout, nullptr);
for (auto& set : m_sets)
{
set.destroy();
}
}
} }
program& program::load_uniforms(const std::vector<program_input>& inputs) program& program::load_uniforms(const std::vector<program_input>& inputs)
{ {
ensure(!m_linked); // "Cannot change uniforms in already linked program!" ensure(!linked); // "Cannot change uniforms in already linked program!"
for (auto &item : inputs) for (auto &item : inputs)
{ {
ensure(item.set < binding_set_index_max_enum); // Ensure we have a valid set id uniforms[item.type].push_back(item);
ensure(item.location < 128u || item.type == input_type_push_constant); // Arbitrary limit but useful to catch possibly uninitialized values
m_sets[item.set].m_inputs[item.type].push_back(item);
} }
return *this; return *this;
} }
program& program::link(bool separate_objects) program& program::link()
{ {
auto p_graphics_info = std::get_if<VkGraphicsPipelineCreateInfo>(&m_info); // Preprocess texture bindings
auto p_compute_info = !p_graphics_info ? std::get_if<VkComputePipelineCreateInfo>(&m_info) : nullptr; // Link step is only useful for rasterizer programs, compute programs do not need this
const bool is_graphics_pipe = p_graphics_info != nullptr; for (const auto &uniform : uniforms[program_input_type::input_type_texture])
if (!is_graphics_pipe) [[ likely ]]
{ {
// We only support compute and graphics, so disable this for compute if (const auto name_start = uniform.name.find("tex"); name_start != umax)
separate_objects = false;
}
if (!separate_objects)
{
// Collapse all sets into set 0 if validation passed
auto& sink = m_sets[0];
for (auto& set : m_sets)
{ {
if (&set == &sink) const auto name_end = uniform.name.find("_stencil");
{ const auto index_start = name_start + 3; // Skip 'tex' part
continue; const auto index_length = (name_end != umax) ? name_end - index_start : name_end;
} const auto index_part = uniform.name.substr(index_start, index_length);
const auto index = std::stoi(index_part);
for (auto& type_arr : set.m_inputs) if (name_start == 0)
{ {
if (type_arr.empty()) // Fragment texture (tex...)
if (name_end == umax)
{ {
continue; // Normal texture
fs_texture_bindings[index] = uniform.location;
} }
else
auto type = type_arr.front().type;
auto& dst = sink.m_inputs[type];
dst.insert(dst.end(), type_arr.begin(), type_arr.end());
// Clear
type_arr.clear();
}
}
sink.validate();
sink.init(m_device);
}
else
{
for (auto& set : m_sets)
{
for (auto& type_arr : set.m_inputs)
{
if (type_arr.empty())
{ {
continue; // Stencil mirror
fs_texture_mirror_bindings[index] = uniform.location;
} }
}
// Real set else
set.validate(); {
set.init(m_device); // Vertex texture (vtex...)
break; vs_texture_bindings[index] = uniform.location;
} }
} }
} }
create_pipeline_layout(); linked = true;
ensure(m_pipeline_layout);
if (is_graphics_pipe)
{
VkGraphicsPipelineCreateInfo create_info = *p_graphics_info;
create_info.layout = m_pipeline_layout;
CHECK_RESULT(vkCreateGraphicsPipelines(m_device, nullptr, 1, &create_info, nullptr, &m_pipeline));
}
else
{
VkComputePipelineCreateInfo create_info = *p_compute_info;
create_info.layout = m_pipeline_layout;
CHECK_RESULT(vkCreateComputePipelines(m_device, nullptr, 1, &create_info, nullptr, &m_pipeline));
}
m_linked = true;
return *this; return *this;
} }
bool program::has_uniform(program_input_type type, const std::string& uniform_name) bool program::has_uniform(program_input_type type, const std::string& uniform_name)
{ {
for (auto& set : m_sets) const auto& uniform = uniforms[type];
return std::any_of(uniform.cbegin(), uniform.cend(), [&uniform_name](const auto& u)
{ {
const auto& uniform = set.m_inputs[type]; return u.name == uniform_name;
return std::any_of(uniform.cbegin(), uniform.cend(), [&uniform_name](const auto& u) });
{
return u.name == uniform_name;
});
}
return false;
} }
std::pair<u32, u32> program::get_uniform_location(::glsl::program_domain domain, program_input_type type, const std::string& uniform_name) void program::bind_uniform(const VkDescriptorImageInfo &image_descriptor, const std::string& uniform_name, VkDescriptorType type, vk::descriptor_set &set)
{ {
for (unsigned i = 0; i < ::size32(m_sets); ++i) for (const auto &uniform : uniforms[program_input_type::input_type_texture])
{ {
const auto& type_arr = m_sets[i].m_inputs[type]; if (uniform.name == uniform_name)
const auto result = std::find_if(type_arr.cbegin(), type_arr.cend(), [&](const auto& u)
{ {
return u.domain == domain && u.name == uniform_name; set.push(image_descriptor, type, uniform.location);
}); attribute_location_mask |= (1ull << uniform.location);
return;
if (result != type_arr.end())
{
return { i, result->location };
} }
} }
return { umax, umax }; rsx_log.notice("texture not found in program: %s", uniform_name.c_str());
} }
void program::bind_uniform(const VkDescriptorImageInfo& image_descriptor, u32 set_id, u32 binding_point) void program::bind_uniform(const VkDescriptorImageInfo & image_descriptor, int texture_unit, ::glsl::program_domain domain, vk::descriptor_set &set, bool is_stencil_mirror)
{ {
if (m_sets[set_id].m_descriptor_slots[binding_point] == image_descriptor) ensure(domain != ::glsl::program_domain::glsl_compute_program);
u32 binding;
if (domain == ::glsl::program_domain::glsl_fragment_program)
{ {
binding = (is_stencil_mirror) ? fs_texture_mirror_bindings[texture_unit] : fs_texture_bindings[texture_unit];
}
else
{
binding = vs_texture_bindings[texture_unit];
}
if (binding != ~0u)
{
set.push(image_descriptor, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, binding);
attribute_location_mask |= (1ull << binding);
return; return;
} }
m_sets[set_id].notify_descriptor_slot_updated(binding_point, image_descriptor); rsx_log.notice("texture not found in program: %stex%u", (domain == ::glsl::program_domain::glsl_vertex_program)? "v" : "", texture_unit);
} }
void program::bind_uniform(const VkDescriptorBufferInfo &buffer_descriptor, u32 set_id, u32 binding_point) void program::bind_uniform(const VkDescriptorBufferInfo &buffer_descriptor, u32 binding_point, vk::descriptor_set &set)
{ {
if (m_sets[set_id].m_descriptor_slots[binding_point] == buffer_descriptor) bind_buffer(buffer_descriptor, binding_point, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, set);
{
return;
}
m_sets[set_id].notify_descriptor_slot_updated(binding_point, buffer_descriptor);
} }
void program::bind_uniform(const VkBufferView &buffer_view, u32 set_id, u32 binding_point) void program::bind_uniform(const VkBufferView &buffer_view, u32 binding_point, vk::descriptor_set &set)
{ {
if (m_sets[set_id].m_descriptor_slots[binding_point] == buffer_view) set.push(buffer_view, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, binding_point);
{ attribute_location_mask |= (1ull << binding_point);
return;
}
m_sets[set_id].notify_descriptor_slot_updated(binding_point, buffer_view);
} }
void program::bind_uniform_array(const VkDescriptorImageInfo* image_descriptors, int count, u32 set_id, u32 binding_point) void program::bind_uniform(const VkBufferView &buffer_view, program_input_type type, const std::string &binding_name, vk::descriptor_set &set)
{ {
// Non-caching write for (const auto &uniform : uniforms[type])
auto& set = m_sets[set_id];
auto& arr = set.m_scratch_images_array;
descriptor_array_ref_t data
{ {
.first = arr.size(), if (uniform.name == binding_name)
.count = static_cast<u32>(count)
};
arr.reserve(arr.size() + static_cast<u32>(count));
for (int i = 0; i < count; ++i)
{
arr.push_back(image_descriptors[i]);
}
set.notify_descriptor_slot_updated(binding_point, data);
}
void program::create_pipeline_layout()
{
ensure(!m_linked);
ensure(m_pipeline_layout == VK_NULL_HANDLE);
rsx::simple_array<VkPushConstantRange> push_constants{};
rsx::simple_array<VkDescriptorSetLayout> set_layouts{};
for (auto& set : m_sets)
{
if (!set.m_device)
{ {
continue; bind_uniform(buffer_view, uniform.location, set);
}
set.create_descriptor_set_layout();
set_layouts.push_back(set.m_descriptor_set_layout);
for (const auto& input : set.m_inputs[input_type_push_constant])
{
const auto& range = input.as_push_constant();
push_constants.push_back({
.stageFlags = to_shader_stage_flags(input.domain),
.offset = range.offset,
.size = range.size
});
}
}
VkPipelineLayoutCreateInfo create_info
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.flags = 0,
.setLayoutCount = set_layouts.size(),
.pSetLayouts = set_layouts.data(),
.pushConstantRangeCount = push_constants.size(),
.pPushConstantRanges = push_constants.data()
};
CHECK_RESULT(vkCreatePipelineLayout(m_device, &create_info, nullptr, &m_pipeline_layout));
}
program& program::bind(const vk::command_buffer& cmd, VkPipelineBindPoint bind_point)
{
VkDescriptorSet bind_sets[binding_set_index_max_enum];
unsigned count = 0;
for (auto& set : m_sets)
{
if (!set.m_device)
{
continue;
}
bind_sets[count++] = set.commit(); // Commit variable changes and return handle to the new set
}
vkCmdBindPipeline(cmd, bind_point, m_pipeline);
vkCmdBindDescriptorSets(cmd, bind_point, m_pipeline_layout, 0, count, bind_sets, 0, nullptr);
return *this;
}
void descriptor_table_t::destroy()
{
if (!m_device)
{
return;
}
if (m_descriptor_set_layout)
{
vkDestroyDescriptorSetLayout(m_device, m_descriptor_set_layout, nullptr);
}
if (m_descriptor_pool)
{
m_descriptor_pool->destroy();
m_descriptor_pool.reset();
}
m_device = VK_NULL_HANDLE;
}
void descriptor_table_t::init(VkDevice dev)
{
m_device = dev;
size_t bind_slots_count = 0;
for (auto& type_arr : m_inputs)
{
if (type_arr.empty() || type_arr.front().type == input_type_push_constant)
{
continue;
}
bind_slots_count += type_arr.size();
}
m_descriptor_slots.resize(bind_slots_count);
std::memset(m_descriptor_slots.data(), 0, sizeof(descriptor_slot_t) * bind_slots_count);
m_descriptors_dirty.resize(bind_slots_count);
std::fill(m_descriptors_dirty.begin(), m_descriptors_dirty.end(), false);
}
VkDescriptorSet descriptor_table_t::allocate_descriptor_set()
{
if (!m_descriptor_pool)
{
create_descriptor_pool();
}
return m_descriptor_pool->allocate(m_descriptor_set_layout);
}
VkDescriptorSet descriptor_table_t::commit()
{
if (!m_descriptor_set)
{
m_any_descriptors_dirty = true;
std::fill(m_descriptors_dirty.begin(), m_descriptors_dirty.end(), false);
}
// Check if we need to actually open a new set
if (!m_any_descriptors_dirty)
{
return m_descriptor_set.value();
}
auto push_descriptor_slot = [this](unsigned idx)
{
const auto& slot = m_descriptor_slots[idx];
const VkDescriptorType type = m_descriptor_types[idx];
if (auto ptr = std::get_if<VkDescriptorImageInfo>(&slot))
{
m_descriptor_set.push(*ptr, type, idx);
return; return;
} }
if (auto ptr = std::get_if<VkDescriptorBufferInfo>(&slot))
{
m_descriptor_set.push(*ptr, type, idx);
return;
}
if (auto ptr = std::get_if<VkBufferView>(&slot))
{
m_descriptor_set.push(*ptr, type, idx);
return;
}
if (auto ptr = std::get_if<descriptor_array_ref_t>(&slot))
{
ensure(type == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); // Only type supported at the moment
ensure((ptr->first + ptr->count) <= m_scratch_images_array.size());
m_descriptor_set.push(m_scratch_images_array.data() + ptr->first, ptr->count, type, idx);
return;
}
fmt::throw_exception("Unexpected descriptor structure at index %u", idx);
};
m_descriptor_set = allocate_descriptor_set();
for (unsigned i = 0; i < m_descriptor_slots.size(); ++i)
{
if (m_descriptors_dirty[i])
{
// Push
push_descriptor_slot(i);
m_descriptors_dirty[i] = false;
continue;
}
// We should copy here if possible.
// Without descriptor_buffer, the most efficient option is to just use the normal bind logic due to the pointer-based nature of the descriptor inputs and no stride.
push_descriptor_slot(i);
} }
m_descriptor_set.on_bind(); rsx_log.notice("vertex buffer not found in program: %s", binding_name.c_str());
m_any_descriptors_dirty = false;
m_scratch_images_array.clear();
return m_descriptor_set.value();
} }
void descriptor_table_t::create_descriptor_set_layout() void program::bind_buffer(const VkDescriptorBufferInfo &buffer_descriptor, u32 binding_point, VkDescriptorType type, vk::descriptor_set &set)
{ {
ensure(m_descriptor_set_layout == VK_NULL_HANDLE); set.push(buffer_descriptor, type, binding_point);
attribute_location_mask |= (1ull << binding_point);
rsx::simple_array<VkDescriptorSetLayoutBinding> bindings;
bindings.reserve(16);
m_descriptor_pool_sizes.clear();
m_descriptor_pool_sizes.reserve(input_type_max_enum);
std::unordered_map<u32, VkDescriptorType> descriptor_type_map;
auto descriptor_count = [](const std::string& name) -> u32
{
const auto start = name.find_last_of("[");
if (start == std::string::npos)
{
return 1;
}
const auto end = name.find_last_of("]");
ensure(end != std::string::npos && start < end, "Invalid variable name");
const std::string array_size = name.substr(start + 1, end - start - 1);
if (const auto count = std::atoi(array_size.c_str());
count > 0)
{
return count;
}
return 1;
};
for (const auto& type_arr : m_inputs)
{
if (type_arr.empty() || type_arr.front().type == input_type_push_constant)
{
continue;
}
VkDescriptorType type = to_descriptor_type(type_arr.front().type);
m_descriptor_pool_sizes.push_back({ .type = type });
for (const auto& input : type_arr)
{
VkDescriptorSetLayoutBinding binding
{
.binding = input.location,
.descriptorType = type,
.descriptorCount = descriptor_count(input.name),
.stageFlags = to_shader_stage_flags(input.domain)
};
bindings.push_back(binding);
descriptor_type_map[input.location] = type;
m_descriptor_pool_sizes.back().descriptorCount += binding.descriptorCount;
}
}
m_descriptor_types.resize(::size32(m_descriptors_dirty));
for (u32 i = 0; i < ::size32(m_descriptors_dirty); ++i)
{
if (descriptor_type_map.find(i) == descriptor_type_map.end())
{
fmt::throw_exception("Invalid input structure. Some input bindings were not declared!");
}
m_descriptor_types[i] = descriptor_type_map[i];
}
m_descriptor_set_layout = vk::descriptors::create_layout(bindings);
}
void descriptor_table_t::create_descriptor_pool()
{
m_descriptor_pool = std::make_unique<descriptor_pool>();
m_descriptor_pool->create(*vk::get_current_renderer(), m_descriptor_pool_sizes);
}
void descriptor_table_t::validate() const
{
// Check for overlapping locations
std::set<u32> taken_locations;
for (auto& type_arr : m_inputs)
{
if (type_arr.empty() ||
type_arr.front().type == input_type_push_constant)
{
continue;
}
for (const auto& input : type_arr)
{
ensure(taken_locations.find(input.location) == taken_locations.end(), "Overlapping input locations found.");
taken_locations.insert(input.location);
}
}
} }
} }
} }

View file

@ -7,7 +7,6 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <variant>
namespace vk namespace vk
{ {
@ -16,20 +15,18 @@ namespace vk
enum program_input_type : u32 enum program_input_type : u32
{ {
input_type_uniform_buffer = 0, input_type_uniform_buffer = 0,
input_type_texel_buffer, input_type_texel_buffer = 1,
input_type_texture, input_type_texture = 2,
input_type_storage_buffer, input_type_storage_buffer = 3,
input_type_storage_texture,
input_type_push_constant,
input_type_max_enum input_type_max_enum = 4
}; };
struct bound_sampler struct bound_sampler
{ {
VkFormat format = VK_FORMAT_UNDEFINED; VkFormat format;
VkImage image = VK_NULL_HANDLE; VkImage image;
VkComponentMapping mapping{}; VkComponentMapping mapping;
}; };
struct bound_buffer struct bound_buffer
@ -40,50 +37,16 @@ namespace vk
u64 size = 0; u64 size = 0;
}; };
struct push_constant_ref
{
u32 offset = 0;
u32 size = 0;
};
struct program_input struct program_input
{ {
::glsl::program_domain domain; ::glsl::program_domain domain;
program_input_type type; program_input_type type;
using bound_data_t = std::variant<bound_buffer, bound_sampler, push_constant_ref>; bound_buffer as_buffer;
bound_data_t bound_data; bound_sampler as_sampler;
u32 set = 0; u32 location;
u32 location = umax;
std::string name; std::string name;
inline bound_buffer& as_buffer() { return *std::get_if<bound_buffer>(&bound_data); }
inline bound_sampler& as_sampler() { return *std::get_if<bound_sampler>(&bound_data); }
inline push_constant_ref& as_push_constant() { return *std::get_if<push_constant_ref>(&bound_data); }
inline const bound_buffer& as_buffer() const { return *std::get_if<bound_buffer>(&bound_data); }
inline const bound_sampler& as_sampler() const { return *std::get_if<bound_sampler>(&bound_data); }
inline const push_constant_ref& as_push_constant() const { return *std::get_if<push_constant_ref>(&bound_data); }
static program_input make(
::glsl::program_domain domain,
const std::string& name,
program_input_type type,
u32 set,
u32 location,
const bound_data_t& data = bound_buffer{})
{
return program_input
{
.domain = domain,
.type = type,
.bound_data = data,
.set = set,
.location = location,
.name = name
};
}
}; };
class shader class shader
@ -109,103 +72,40 @@ namespace vk
VkShaderModule get_handle() const; VkShaderModule get_handle() const;
}; };
struct descriptor_array_ref_t
{
u32 first = 0;
u32 count = 0;
};
using descriptor_slot_t = std::variant<VkDescriptorImageInfo, VkDescriptorBufferInfo, VkBufferView, descriptor_array_ref_t>;
struct descriptor_table_t
{
VkDevice m_device = VK_NULL_HANDLE;
std::array<std::vector<program_input>, input_type_max_enum> m_inputs;
std::unique_ptr<vk::descriptor_pool> m_descriptor_pool;
VkDescriptorSetLayout m_descriptor_set_layout = VK_NULL_HANDLE;
vk::descriptor_set m_descriptor_set{};
rsx::simple_array<VkDescriptorPoolSize> m_descriptor_pool_sizes;
rsx::simple_array<VkDescriptorType> m_descriptor_types;
std::vector<descriptor_slot_t> m_descriptor_slots;
std::vector<bool> m_descriptors_dirty;
bool m_any_descriptors_dirty = false;
rsx::simple_array< VkDescriptorImageInfo> m_scratch_images_array;
void init(VkDevice dev);
void destroy();
void validate() const;
void create_descriptor_set_layout();
void create_descriptor_pool();
VkDescriptorSet allocate_descriptor_set();
VkDescriptorSet commit();
template <typename T>
inline void notify_descriptor_slot_updated(u32 slot, const T& data)
{
m_descriptors_dirty[slot] = true;
m_descriptor_slots[slot] = data;
m_any_descriptors_dirty = true;
}
};
enum binding_set_index : u32
{
// For separate shader objects
binding_set_index_vertex = 0,
binding_set_index_fragment = 1,
// Aliases
binding_set_index_compute = 0,
binding_set_index_unified = 0,
// Meta
binding_set_index_max_enum = 2,
};
class program class program
{ {
VkDevice m_device = VK_NULL_HANDLE; std::array<std::vector<program_input>, input_type_max_enum> uniforms;
VkPipeline m_pipeline = VK_NULL_HANDLE; VkDevice m_device;
VkPipelineLayout m_pipeline_layout = VK_NULL_HANDLE;
std::variant<VkGraphicsPipelineCreateInfo, VkComputePipelineCreateInfo> m_info; std::array<u32, 16> fs_texture_bindings;
std::array<descriptor_table_t, binding_set_index_max_enum> m_sets; std::array<u32, 16> fs_texture_mirror_bindings;
bool m_linked = false; std::array<u32, 4> vs_texture_bindings;
bool linked;
void init(); void create_impl();
void create_pipeline_layout();
program& load_uniforms(const std::vector<program_input>& inputs);
public: public:
VkPipeline pipeline;
VkPipelineLayout pipeline_layout;
u64 attribute_location_mask;
u64 vertex_attributes_mask;
program(VkDevice dev, const VkGraphicsPipelineCreateInfo& create_info, const std::vector<program_input> &vertex_inputs, const std::vector<program_input>& fragment_inputs); program(VkDevice dev, VkPipeline p, VkPipelineLayout layout, const std::vector<program_input> &vertex_input, const std::vector<program_input>& fragment_inputs);
program(VkDevice dev, const VkComputePipelineCreateInfo& create_info, const std::vector<program_input>& compute_inputs); program(VkDevice dev, VkPipeline p, VkPipelineLayout layout);
program(const program&) = delete; program(const program&) = delete;
program(program&& other) = delete; program(program&& other) = delete;
~program(); ~program();
program& link(bool separate_stages); program& load_uniforms(const std::vector<program_input>& inputs);
program& bind(const vk::command_buffer& cmd, VkPipelineBindPoint bind_point); program& link();
bool has_uniform(program_input_type type, const std::string &uniform_name); bool has_uniform(program_input_type type, const std::string &uniform_name);
std::pair<u32, u32> get_uniform_location(::glsl::program_domain domain, program_input_type type, const std::string& uniform_name); void bind_uniform(const VkDescriptorImageInfo &image_descriptor, const std::string &uniform_name, VkDescriptorType type, vk::descriptor_set &set);
void bind_uniform(const VkDescriptorImageInfo &image_descriptor, int texture_unit, ::glsl::program_domain domain, vk::descriptor_set &set, bool is_stencil_mirror = false);
void bind_uniform(const VkDescriptorImageInfo &image_descriptor, u32 set_id, u32 binding_point); void bind_uniform(const VkDescriptorBufferInfo &buffer_descriptor, u32 binding_point, vk::descriptor_set &set);
void bind_uniform(const VkDescriptorBufferInfo &buffer_descriptor, u32 set_id, u32 binding_point); void bind_uniform(const VkBufferView &buffer_view, u32 binding_point, vk::descriptor_set &set);
void bind_uniform(const VkBufferView &buffer_view, u32 set_id, u32 binding_point); void bind_uniform(const VkBufferView &buffer_view, program_input_type type, const std::string &binding_name, vk::descriptor_set &set);
void bind_uniform(const VkBufferView &buffer_view, ::glsl::program_domain domain, program_input_type type, const std::string &binding_name); void bind_buffer(const VkDescriptorBufferInfo &buffer_descriptor, u32 binding_point, VkDescriptorType type, vk::descriptor_set &set);
void bind_uniform_array(const VkDescriptorImageInfo* image_descriptors, int count, u32 set_id, u32 binding_point);
inline VkPipelineLayout layout() const { return m_pipeline_layout; }
inline VkPipeline value() const { return m_pipeline; }
}; };
} }
} }

View file

@ -154,7 +154,7 @@ namespace vk
// If we have driver support for FBO loops, set the usage flag for it. // If we have driver support for FBO loops, set the usage flag for it.
if (vk::get_current_renderer()->get_framebuffer_loops_support()) if (vk::get_current_renderer()->get_framebuffer_loops_support())
{ {
return { VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT, VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT }; return { VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT, 0 };
} }
// Workarounds to force transition to GENERAL to decompress. // Workarounds to force transition to GENERAL to decompress.

View file

@ -16,47 +16,50 @@ namespace vk
u32 cs_wave_y = 1; u32 cs_wave_y = 1;
cs_resolve_base() cs_resolve_base()
{ {}
ssbo_count = 0;
}
virtual ~cs_resolve_base() virtual ~cs_resolve_base()
{} {}
void build(const std::string& format_prefix, bool unresolve, bool bgra_swap); void build(const std::string& format_prefix, bool unresolve, bool bgra_swap);
std::vector<glsl::program_input> get_inputs() override std::vector<std::pair<VkDescriptorType, u8>> get_descriptor_layout() override
{
return
{
{ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 2 }
};
}
void declare_inputs() override
{ {
std::vector<vk::glsl::program_input> inputs = std::vector<vk::glsl::program_input> inputs =
{ {
glsl::program_input::make( {
::glsl::program_domain::glsl_compute_program, ::glsl::program_domain::glsl_compute_program,
"multisampled", vk::glsl::program_input_type::input_type_texture,
glsl::input_type_storage_texture, {}, {},
0, 0,
0 "multisampled"
), },
{
glsl::program_input::make(
::glsl::program_domain::glsl_compute_program, ::glsl::program_domain::glsl_compute_program,
"resolve", vk::glsl::program_input_type::input_type_texture,
glsl::input_type_storage_texture, {}, {},
0, 1,
1 "resolve"
), }
}; };
auto result = compute_task::get_inputs(); m_program->load_uniforms(inputs);
result.insert(result.end(), inputs.begin(), inputs.end());
return result;
} }
void bind_resources(const vk::command_buffer& /*cmd*/) override void bind_resources() override
{ {
auto msaa_view = multisampled->get_view(rsx::default_remap_vector.with_encoding(VK_REMAP_VIEW_MULTISAMPLED)); auto msaa_view = multisampled->get_view(rsx::default_remap_vector.with_encoding(VK_REMAP_VIEW_MULTISAMPLED));
auto resolved_view = resolve->get_view(rsx::default_remap_vector.with_encoding(VK_REMAP_IDENTITY)); auto resolved_view = resolve->get_view(rsx::default_remap_vector.with_encoding(VK_REMAP_IDENTITY));
m_program->bind_uniform({ VK_NULL_HANDLE, msaa_view->value, multisampled->current_layout }, 0, 0); m_program->bind_uniform({ VK_NULL_HANDLE, msaa_view->value, multisampled->current_layout }, "multisampled", VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, m_descriptor_set);
m_program->bind_uniform({ VK_NULL_HANDLE, resolved_view->value, resolve->current_layout }, 0, 1); m_program->bind_uniform({ VK_NULL_HANDLE, resolved_view->value, resolve->current_layout }, "resolve", VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, m_descriptor_set);
} }
void run(const vk::command_buffer& cmd, vk::viewable_image* msaa_image, vk::viewable_image* resolve_image) void run(const vk::command_buffer& cmd, vk::viewable_image* msaa_image, vk::viewable_image* resolve_image)
@ -113,23 +116,19 @@ namespace vk
void build(bool resolve_depth, bool resolve_stencil, bool unresolve); void build(bool resolve_depth, bool resolve_stencil, bool unresolve);
std::vector<glsl::program_input> get_fragment_inputs() override std::vector<VkPushConstantRange> get_push_constants() override
{ {
auto result = overlay_pass::get_fragment_inputs(); VkPushConstantRange constant;
result.push_back(glsl::program_input::make( constant.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
::glsl::glsl_fragment_program, constant.offset = 0;
"push_constants", constant.size = 16;
glsl::input_type_push_constant,
0, return { constant };
umax,
glsl::push_constant_ref{ .size = 16 }
));
return result;
} }
void update_uniforms(vk::command_buffer& cmd, vk::glsl::program* program) override void update_uniforms(vk::command_buffer& cmd, vk::glsl::program* /*program*/) override
{ {
vkCmdPushConstants(cmd, program->layout(), VK_SHADER_STAGE_FRAGMENT_BIT, 0, static_parameters_width * 4, static_parameters); vkCmdPushConstants(cmd, m_pipeline_layout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, static_parameters_width * 4, static_parameters);
} }
void update_sample_configuration(vk::image* msaa_image) void update_sample_configuration(vk::image* msaa_image)
@ -227,16 +226,16 @@ namespace vk
state_descriptors.push_back(VK_DYNAMIC_STATE_STENCIL_WRITE_MASK); state_descriptors.push_back(VK_DYNAMIC_STATE_STENCIL_WRITE_MASK);
} }
void emit_geometry(vk::command_buffer& cmd, glsl::program* program) override void emit_geometry(vk::command_buffer& cmd) override
{ {
vkCmdClearAttachments(cmd, 1, &clear_info, 1, &region); vkCmdClearAttachments(cmd, 1, &clear_info, 1, &region);
for (s32 write_mask = 0x1; write_mask <= 0x80; write_mask <<= 1) for (s32 write_mask = 0x1; write_mask <= 0x80; write_mask <<= 1)
{ {
vkCmdSetStencilWriteMask(cmd, VK_STENCIL_FRONT_AND_BACK, write_mask); vkCmdSetStencilWriteMask(cmd, VK_STENCIL_FRONT_AND_BACK, write_mask);
vkCmdPushConstants(cmd, program->layout(), VK_SHADER_STAGE_FRAGMENT_BIT, 8, 4, &write_mask); vkCmdPushConstants(cmd, m_pipeline_layout, VK_SHADER_STAGE_FRAGMENT_BIT, 8, 4, &write_mask);
overlay_pass::emit_geometry(cmd, program); overlay_pass::emit_geometry(cmd);
} }
} }
@ -286,16 +285,16 @@ namespace vk
state_descriptors.push_back(VK_DYNAMIC_STATE_STENCIL_WRITE_MASK); state_descriptors.push_back(VK_DYNAMIC_STATE_STENCIL_WRITE_MASK);
} }
void emit_geometry(vk::command_buffer& cmd, glsl::program* program) override void emit_geometry(vk::command_buffer& cmd) override
{ {
vkCmdClearAttachments(cmd, 1, &clear_info, 1, &clear_region); vkCmdClearAttachments(cmd, 1, &clear_info, 1, &clear_region);
for (s32 write_mask = 0x1; write_mask <= 0x80; write_mask <<= 1) for (s32 write_mask = 0x1; write_mask <= 0x80; write_mask <<= 1)
{ {
vkCmdSetStencilWriteMask(cmd, VK_STENCIL_FRONT_AND_BACK, write_mask); vkCmdSetStencilWriteMask(cmd, VK_STENCIL_FRONT_AND_BACK, write_mask);
vkCmdPushConstants(cmd, program->layout(), VK_SHADER_STAGE_FRAGMENT_BIT, 8, 4, &write_mask); vkCmdPushConstants(cmd, m_pipeline_layout, VK_SHADER_STAGE_FRAGMENT_BIT, 8, 4, &write_mask);
overlay_pass::emit_geometry(cmd, program); overlay_pass::emit_geometry(cmd);
} }
} }

View file

@ -12,70 +12,23 @@
namespace vk namespace vk
{ {
u32 shader_interpreter::init(VKVertexProgram* vk_prog, u64 compiler_options) const glsl::shader* shader_interpreter::build_vs(u64 compiler_options)
{
std::memset(&vk_prog->binding_table, 0xff, sizeof(vk_prog->binding_table));
u32 location = 0;
vk_prog->binding_table.vertex_buffers_location = location;
location += 3;
vk_prog->binding_table.context_buffer_location = location++;
if (vk::emulate_conditional_rendering())
{
vk_prog->binding_table.cr_pred_buffer_location = location++;
}
if (compiler_options & program_common::interpreter::COMPILER_OPT_ENABLE_INSTANCING)
{
vk_prog->binding_table.instanced_lut_buffer_location = location++;
vk_prog->binding_table.instanced_cbuf_location = location++;
}
else
{
vk_prog->binding_table.cbuf_location = location++;
}
if (vk::emulate_conditional_rendering())
{
vk_prog->binding_table.cr_pred_buffer_location = location++;
}
// Return next index
return location;
}
u32 shader_interpreter::init(VKFragmentProgram* vk_prog, u64 /*compiler_opt*/) const
{
std::memset(&vk_prog->binding_table, 0xff, sizeof(vk_prog->binding_table));
vk_prog->binding_table.context_buffer_location = 0;
vk_prog->binding_table.tex_param_location = 1;
vk_prog->binding_table.polygon_stipple_params_location = 2;
// Return next index
return 3;
}
VKVertexProgram* shader_interpreter::build_vs(u64 compiler_options)
{ {
::glsl::shader_properties properties{}; ::glsl::shader_properties properties{};
properties.domain = ::glsl::program_domain::glsl_vertex_program; properties.domain = ::glsl::program_domain::glsl_vertex_program;
properties.require_lit_emulation = true; properties.require_lit_emulation = true;
// TODO: Extend decompiler thread
// TODO: Rename decompiler thread, it no longer spawns a thread
RSXVertexProgram null_prog; RSXVertexProgram null_prog;
std::string shader_str; std::string shader_str;
ParamArray arr; ParamArray arr;
VKVertexProgram vk_prog;
// Initialize binding layout
auto vk_prog = std::make_unique<VKVertexProgram>();
m_vertex_instruction_start = init(vk_prog.get(), compiler_options);
null_prog.ctrl = (compiler_options & program_common::interpreter::COMPILER_OPT_ENABLE_INSTANCING) null_prog.ctrl = (compiler_options & program_common::interpreter::COMPILER_OPT_ENABLE_INSTANCING)
? RSX_SHADER_CONTROL_INSTANCED_CONSTANTS ? RSX_SHADER_CONTROL_INSTANCED_CONSTANTS
: 0; : 0;
VKVertexDecompilerThread comp(null_prog, shader_str, arr, *vk_prog); VKVertexDecompilerThread comp(null_prog, shader_str, arr, vk_prog);
// Initialize compiler properties // Initialize compiler properties
comp.properties.has_indexed_constants = true; comp.properties.has_indexed_constants = true;
@ -99,12 +52,6 @@ namespace vk
" uvec4 vp_instructions[];\n" " uvec4 vp_instructions[];\n"
"};\n\n"; "};\n\n";
if (compiler_options & program_common::interpreter::COMPILER_OPT_ENABLE_VTX_TEXTURES)
{
// FIXME: Unimplemented
rsx_log.todo("Vertex textures are currently not implemented for the shader interpreter.");
}
if (compiler_options & program_common::interpreter::COMPILER_OPT_ENABLE_INSTANCING) if (compiler_options & program_common::interpreter::COMPILER_OPT_ENABLE_INSTANCING)
{ {
builder << "#define _ENABLE_INSTANCED_CONSTANTS\n"; builder << "#define _ENABLE_INSTANCED_CONSTANTS\n";
@ -121,29 +68,48 @@ namespace vk
builder << program_common::interpreter::get_vertex_interpreter(); builder << program_common::interpreter::get_vertex_interpreter();
const std::string s = builder.str(); const std::string s = builder.str();
auto vs = &vk_prog->shader; auto vs = std::make_unique<glsl::shader>();
vs->create(::glsl::program_domain::glsl_vertex_program, s); vs->create(::glsl::program_domain::glsl_vertex_program, s);
vs->compile(); vs->compile();
// Declare local inputs // Prepare input table
auto vs_inputs = comp.get_inputs(); const auto& binding_table = vk::get_current_renderer()->get_pipeline_binding_table();
vk::glsl::program_input in; vk::glsl::program_input in;
in.set = 0;
in.location = binding_table.vertex_params_bind_slot;
in.domain = ::glsl::glsl_vertex_program; in.domain = ::glsl::glsl_vertex_program;
in.location = m_vertex_instruction_start; in.name = "VertexContextBuffer";
in.type = glsl::input_type_storage_buffer; in.type = vk::glsl::input_type_uniform_buffer;
in.name = "VertexInstructionBlock"; m_vs_inputs.push_back(in);
vs_inputs.push_back(in);
vk_prog->SetInputs(vs_inputs); in.location = binding_table.vertex_buffers_first_bind_slot;
in.name = "persistent_input_stream";
in.type = vk::glsl::input_type_texel_buffer;
m_vs_inputs.push_back(in);
auto ret = vk_prog.get(); in.location = binding_table.vertex_buffers_first_bind_slot + 1;
m_shader_cache[compiler_options].m_vs = std::move(vk_prog); in.name = "volatile_input_stream";
in.type = vk::glsl::input_type_texel_buffer;
m_vs_inputs.push_back(in);
in.location = binding_table.vertex_buffers_first_bind_slot + 2;
in.name = "vertex_layout_stream";
in.type = vk::glsl::input_type_texel_buffer;
m_vs_inputs.push_back(in);
in.location = binding_table.vertex_constant_buffers_bind_slot;
in.name = "VertexConstantsBuffer";
in.type = vk::glsl::input_type_uniform_buffer;
m_vs_inputs.push_back(in);
// TODO: Bind textures if needed
auto ret = vs.get();
m_shader_cache[compiler_options].m_vs = std::move(vs);
return ret; return ret;
} }
VKFragmentProgram* shader_interpreter::build_fs(u64 compiler_options) glsl::shader* shader_interpreter::build_fs(u64 compiler_options)
{ {
[[maybe_unused]] ::glsl::shader_properties properties{}; [[maybe_unused]] ::glsl::shader_properties properties{};
properties.domain = ::glsl::program_domain::glsl_fragment_program; properties.domain = ::glsl::program_domain::glsl_fragment_program;
@ -154,13 +120,10 @@ namespace vk
ParamArray arr; ParamArray arr;
std::string shader_str; std::string shader_str;
RSXFragmentProgram frag; RSXFragmentProgram frag;
VKFragmentProgram vk_prog;
VKFragmentDecompilerThread comp(shader_str, arr, frag, len, vk_prog);
auto vk_prog = std::make_unique<VKFragmentProgram>(); const auto& binding_table = vk::get_current_renderer()->get_pipeline_binding_table();
m_fragment_instruction_start = init(vk_prog.get(), compiler_options);
m_fragment_textures_start = m_fragment_instruction_start + 1;
VKFragmentDecompilerThread comp(shader_str, arr, frag, len, *vk_prog);
std::stringstream builder; std::stringstream builder;
builder << builder <<
"#version 450\n" "#version 450\n"
@ -236,7 +199,7 @@ namespace vk
for (int i = 0, bind_location = m_fragment_textures_start; i < 4; ++i) for (int i = 0, bind_location = m_fragment_textures_start; i < 4; ++i)
{ {
builder << "layout(set=1, binding=" << bind_location++ << ") " << "uniform " << type_names[i] << " " << type_names[i] << "_array[16];\n"; builder << "layout(set=0, binding=" << bind_location++ << ") " << "uniform " << type_names[i] << " " << type_names[i] << "_array[16];\n";
} }
builder << "\n" builder << "\n"
@ -248,7 +211,7 @@ namespace vk
} }
builder << builder <<
"layout(std430, set=1, binding=" << m_fragment_instruction_start << ") readonly restrict buffer FragmentInstructionBlock\n" "layout(std430, binding=" << m_fragment_instruction_start << ") readonly restrict buffer FragmentInstructionBlock\n"
"{\n" "{\n"
" uint shader_control;\n" " uint shader_control;\n"
" uint texture_control;\n" " uint texture_control;\n"
@ -260,55 +223,182 @@ namespace vk
builder << program_common::interpreter::get_fragment_interpreter(); builder << program_common::interpreter::get_fragment_interpreter();
const std::string s = builder.str(); const std::string s = builder.str();
auto fs = &vk_prog->shader; auto fs = std::make_unique<glsl::shader>();
fs->create(::glsl::program_domain::glsl_fragment_program, s); fs->create(::glsl::program_domain::glsl_fragment_program, s);
fs->compile(); fs->compile();
// Declare local inputs // Prepare input table
auto inputs = comp.get_inputs();
vk::glsl::program_input in; vk::glsl::program_input in;
in.set = 1; in.location = binding_table.fragment_constant_buffers_bind_slot;
in.domain = ::glsl::glsl_fragment_program; in.domain = ::glsl::glsl_fragment_program;
in.location = m_fragment_instruction_start; in.name = "FragmentConstantsBuffer";
in.type = glsl::input_type_storage_buffer; in.type = vk::glsl::input_type_uniform_buffer;
in.name = "FragmentInstructionBlock"; m_fs_inputs.push_back(in);
inputs.push_back(in);
if (compiler_options & program_common::interpreter::COMPILER_OPT_ENABLE_TEXTURES) in.location = binding_table.fragment_state_bind_slot;
in.name = "FragmentStateBuffer";
m_fs_inputs.push_back(in);
in.location = binding_table.fragment_texture_params_bind_slot;
in.name = "TextureParametersBuffer";
m_fs_inputs.push_back(in);
for (int i = 0, location = m_fragment_textures_start; i < 4; ++i, ++location)
{ {
for (int i = 0, location = m_fragment_textures_start; i < 4; ++i, ++location) in.location = location;
{ in.name = std::string(type_names[i]) + "_array[16]";
in.location = location; m_fs_inputs.push_back(in);
in.name = std::string(type_names[i]) + "_array[16]";
in.type = glsl::input_type_texture;
inputs.push_back(in);
}
} }
vk_prog->SetInputs(inputs); auto ret = fs.get();
m_shader_cache[compiler_options].m_fs = std::move(fs);
auto ret = vk_prog.get();
m_shader_cache[compiler_options].m_fs = std::move(vk_prog);
return ret; return ret;
} }
std::pair<VkDescriptorSetLayout, VkPipelineLayout> shader_interpreter::create_layout(VkDevice dev)
{
const auto& binding_table = vk::get_current_renderer()->get_pipeline_binding_table();
auto bindings = get_common_binding_table();
u32 idx = ::size32(bindings);
bindings.resize(binding_table.total_descriptor_bindings);
// Texture 1D array
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[idx].descriptorCount = 16;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.textures_first_bind_slot;
bindings[idx].pImmutableSamplers = nullptr;
m_fragment_textures_start = bindings[idx].binding;
idx++;
// Texture 2D array
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[idx].descriptorCount = 16;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.textures_first_bind_slot + 1;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
// Texture 3D array
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[idx].descriptorCount = 16;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.textures_first_bind_slot + 2;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
// Texture CUBE array
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[idx].descriptorCount = 16;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.textures_first_bind_slot + 3;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
// Vertex texture array (2D only)
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[idx].descriptorCount = 4;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[idx].binding = binding_table.textures_first_bind_slot + 4;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
// Vertex program ucode block
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[idx].binding = binding_table.textures_first_bind_slot + 5;
bindings[idx].pImmutableSamplers = nullptr;
m_vertex_instruction_start = bindings[idx].binding;
idx++;
// Fragment program ucode block
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.textures_first_bind_slot + 6;
bindings[idx].pImmutableSamplers = nullptr;
m_fragment_instruction_start = bindings[idx].binding;
idx++;
bindings.resize(idx);
m_descriptor_pool_sizes = get_descriptor_pool_sizes(bindings);
std::array<VkPushConstantRange, 1> push_constants;
push_constants[0].offset = 0;
push_constants[0].size = 16;
push_constants[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
if (vk::emulate_conditional_rendering())
{
// Conditional render toggle
push_constants[0].size = 20;
}
const auto set_layout = vk::descriptors::create_layout(bindings);
VkPipelineLayoutCreateInfo layout_info = {};
layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
layout_info.setLayoutCount = 1;
layout_info.pSetLayouts = &set_layout;
layout_info.pushConstantRangeCount = 1;
layout_info.pPushConstantRanges = push_constants.data();
VkPipelineLayout result;
CHECK_RESULT(vkCreatePipelineLayout(dev, &layout_info, nullptr, &result));
return { set_layout, result };
}
void shader_interpreter::create_descriptor_pools(const vk::render_device& dev)
{
const auto max_draw_calls = dev.get_descriptor_max_draw_calls();
m_descriptor_pool.create(dev, m_descriptor_pool_sizes, max_draw_calls);
}
void shader_interpreter::init(const vk::render_device& dev) void shader_interpreter::init(const vk::render_device& dev)
{ {
m_device = dev; m_device = dev;
std::tie(m_shared_descriptor_layout, m_shared_pipeline_layout) = create_layout(dev);
create_descriptor_pools(dev);
} }
void shader_interpreter::destroy() void shader_interpreter::destroy()
{ {
m_program_cache.clear(); m_program_cache.clear();
m_descriptor_pool.destroy();
for (auto &fs : m_shader_cache)
{
fs.second.m_vs->destroy();
fs.second.m_fs->destroy();
}
m_shader_cache.clear(); m_shader_cache.clear();
if (m_shared_pipeline_layout)
{
vkDestroyPipelineLayout(m_device, m_shared_pipeline_layout, nullptr);
m_shared_pipeline_layout = VK_NULL_HANDLE;
}
if (m_shared_descriptor_layout)
{
vkDestroyDescriptorSetLayout(m_device, m_shared_descriptor_layout, nullptr);
m_shared_descriptor_layout = VK_NULL_HANDLE;
}
} }
glsl::program* shader_interpreter::link(const vk::pipeline_props& properties, u64 compiler_opt) glsl::program* shader_interpreter::link(const vk::pipeline_props& properties, u64 compiler_opt)
{ {
VKVertexProgram* vs; glsl::shader *fs, *vs;
VKFragmentProgram* fs;
if (auto found = m_shader_cache.find(compiler_opt); found != m_shader_cache.end()) if (auto found = m_shader_cache.find(compiler_opt); found != m_shader_cache.end())
{ {
fs = found->second.m_fs.get(); fs = found->second.m_fs.get();
@ -323,12 +413,12 @@ namespace vk
VkPipelineShaderStageCreateInfo shader_stages[2] = {}; VkPipelineShaderStageCreateInfo shader_stages[2] = {};
shader_stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; shader_stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shader_stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; shader_stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
shader_stages[0].module = vs->shader.get_handle(); shader_stages[0].module = vs->get_handle();
shader_stages[0].pName = "main"; shader_stages[0].pName = "main";
shader_stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; shader_stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shader_stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; shader_stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
shader_stages[1].module = fs->shader.get_handle(); shader_stages[1].module = fs->get_handle();
shader_stages[1].pName = "main"; shader_stages[1].pName = "main";
std::vector<VkDynamicState> dynamic_state_descriptors = std::vector<VkDynamicState> dynamic_state_descriptors =
@ -388,42 +478,33 @@ namespace vk
info.stageCount = 2; info.stageCount = 2;
info.pStages = shader_stages; info.pStages = shader_stages;
info.pDynamicState = &dynamic_state_info; info.pDynamicState = &dynamic_state_info;
info.layout = VK_NULL_HANDLE; info.layout = m_shared_pipeline_layout;
info.basePipelineIndex = -1; info.basePipelineIndex = -1;
info.basePipelineHandle = VK_NULL_HANDLE; info.basePipelineHandle = VK_NULL_HANDLE;
info.renderPass = vk::get_renderpass(m_device, properties.renderpass_key); info.renderPass = vk::get_renderpass(m_device, properties.renderpass_key);
auto compiler = vk::get_pipe_compiler(); auto compiler = vk::get_pipe_compiler();
auto program = compiler->compile( auto program = compiler->compile(info, m_shared_pipeline_layout, vk::pipe_compiler::COMPILE_INLINE, {}, m_vs_inputs, m_fs_inputs);
info,
vk::pipe_compiler::COMPILE_INLINE | vk::pipe_compiler::SEPARATE_SHADER_OBJECTS,
{},
vs->uniforms,
fs->uniforms);
return program.release(); return program.release();
} }
void shader_interpreter::update_fragment_textures(const std::array<VkDescriptorImageInfo, 68>& sampled_images) void shader_interpreter::update_fragment_textures(const std::array<VkDescriptorImageInfo, 68>& sampled_images, vk::descriptor_set &set)
{ {
// FIXME: Cannot use m_fragment_textures.start now since each interpreter has its own binding layout
auto [set, binding] = m_current_interpreter->get_uniform_location(::glsl::glsl_fragment_program, glsl::input_type_texture, "sampler1D_array[16]");
if (binding == umax)
{
return;
}
const VkDescriptorImageInfo* texture_ptr = sampled_images.data(); const VkDescriptorImageInfo* texture_ptr = sampled_images.data();
for (u32 i = 0; i < 4; ++i, ++binding, texture_ptr += 16) for (u32 i = 0, binding = m_fragment_textures_start; i < 4; ++i, ++binding, texture_ptr += 16)
{ {
m_current_interpreter->bind_uniform_array(texture_ptr, 16, set, binding); set.push(texture_ptr, 16, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, binding);
} }
} }
VkDescriptorSet shader_interpreter::allocate_descriptor_set()
{
return m_descriptor_pool.allocate(m_shared_descriptor_layout);
}
glsl::program* shader_interpreter::get( glsl::program* shader_interpreter::get(
const vk::pipeline_props& properties, const vk::pipeline_props& properties,
const program_hash_util::fragment_program_utils::fragment_program_metadata& fp_metadata, const program_hash_util::fragment_program_utils::fragment_program_metadata& metadata,
const program_hash_util::vertex_program_utils::vertex_program_metadata& vp_metadata,
u32 vp_ctrl, u32 vp_ctrl,
u32 fp_ctrl) u32 fp_ctrl)
{ {
@ -463,12 +544,11 @@ namespace vk
if (fp_ctrl & CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT) key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_DEPTH_EXPORT; if (fp_ctrl & CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT) key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_DEPTH_EXPORT;
if (fp_ctrl & CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS) key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_F32_EXPORT; if (fp_ctrl & CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS) key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_F32_EXPORT;
if (fp_ctrl & RSX_SHADER_CONTROL_USES_KIL) key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_KIL; if (fp_ctrl & RSX_SHADER_CONTROL_USES_KIL) key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_KIL;
if (fp_metadata.referenced_textures_mask) key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_TEXTURES; if (metadata.referenced_textures_mask) key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_TEXTURES;
if (fp_metadata.has_branch_instructions) key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_FLOW_CTRL; if (metadata.has_branch_instructions) key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_FLOW_CTRL;
if (fp_metadata.has_pack_instructions) key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_PACKING; if (metadata.has_pack_instructions) key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_PACKING;
if (rsx::method_registers.polygon_stipple_enabled()) key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_STIPPLING; if (rsx::method_registers.polygon_stipple_enabled()) key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_STIPPLING;
if (vp_ctrl & RSX_SHADER_CONTROL_INSTANCED_CONSTANTS) key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_INSTANCING; if (vp_ctrl & RSX_SHADER_CONTROL_INSTANCED_CONSTANTS) key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_INSTANCING;
if (vp_metadata.referenced_textures_mask) key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_VTX_TEXTURES;
if (m_current_key == key) [[likely]] if (m_current_key == key) [[likely]]
{ {
@ -505,16 +585,4 @@ namespace vk
{ {
return m_fragment_instruction_start; return m_fragment_instruction_start;
} }
std::pair<VKVertexProgram*, VKFragmentProgram*> shader_interpreter::get_shaders() const
{
if (auto found = m_shader_cache.find(m_current_key.compiler_opt); found != m_shader_cache.end())
{
auto fs = found->second.m_fs.get();
auto vs = found->second.m_vs.get();
return { vs, fs };
}
return { nullptr, nullptr };
}
}; };

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