diff --git a/CMakeModules/DuckStationUtils.cmake b/CMakeModules/DuckStationUtils.cmake index 1ab52e4123..9d7c5ad465 100644 --- a/CMakeModules/DuckStationUtils.cmake +++ b/CMakeModules/DuckStationUtils.cmake @@ -92,17 +92,22 @@ function(detect_architecture) elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "riscv64") message(STATUS "Building RISC-V 64 binaries.") set(CPU_ARCH_RISCV64 TRUE PARENT_SCOPE) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -finline-atomics" PARENT_SCOPE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -finline-atomics" PARENT_SCOPE) - # Still need this, apparently. - link_libraries("-latomic") + # Don't want function calls for atomics. + if(COMPILER_GCC) + set(EXTRA_CFLAGS "${EXTRA_CFLAGS} -finline-atomics") + + # Still need this, apparently. + link_libraries("-latomic") + endif() if(NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Debug") # Frame pointers generate an annoying amount of code on leaf functions. - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fomit-frame-pointer" PARENT_SCOPE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fomit-frame-pointer" PARENT_SCOPE) + set(EXTRA_CFLAGS "${EXTRA_CFLAGS} -fomit-frame-pointer") endif() + + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}" PARENT_SCOPE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_CFLAGS}" PARENT_SCOPE) else() message(FATAL_ERROR "Unknown system processor: ${CMAKE_SYSTEM_PROCESSOR}") endif() @@ -121,8 +126,8 @@ function(detect_page_size) return() endif() - if(HOST_MIN_PAGE_SIZE OR HOST_MAX_PAGE_SIZE) - if(NOT HOST_MIN_PAGE_SIZE OR NOT HOST_MAX_PAGE_SIZE) + if(DEFINED HOST_MIN_PAGE_SIZE OR DEFINED HOST_MAX_PAGE_SIZE) + if(NOT DEFINED HOST_MIN_PAGE_SIZE OR NOT DEFINED HOST_MAX_PAGE_SIZE) message(FATAL_ERROR "Both HOST_MIN_PAGE_SIZE and HOST_MAX_PAGE_SIZE must be defined.") endif() return() @@ -172,7 +177,7 @@ function(detect_cache_line_size) endif() if(CMAKE_CROSSCOMPILING) - message(WARNING "Cross-compiling and can't determine page size, assuming default.") + message(WARNING "Cross-compiling and can't determine cache line size, assuming default.") return() endif() diff --git a/data/resources/gamedb.yaml b/data/resources/gamedb.yaml index 83e917d3a4..6dd2b6965f 100644 --- a/data/resources/gamedb.yaml +++ b/data/resources/gamedb.yaml @@ -66894,6 +66894,9 @@ SLPM-86031: - DigitalController codes: - HASH-4B6CD9BD936C3CCE + traits: + - DisablePGXP # 2D, PGXP is not beneficial. + - DisableWidescreen # Apparently issues with sprites. metadata: publisher: "Konami" developer: "Konami Computer Entertainment Tokyo, Inc" diff --git a/scripts/deps/build-dependencies-linux-cross.sh b/scripts/deps/build-dependencies-linux-cross.sh new file mode 100755 index 0000000000..bd6d9dc3c3 --- /dev/null +++ b/scripts/deps/build-dependencies-linux-cross.sh @@ -0,0 +1,519 @@ +#!/usr/bin/env bash + +# SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin +# SPDX-License-Identifier: CC-BY-NC-ND-4.0 + +set -e + +if [ "$#" -lt 4 ]; then + echo "Syntax: $0 [-system-freetype] [-system-harfbuzz] [-system-libjpeg] [-system-libpng] [-system-libwebp] [-system-libzip] [-system-zstd] [-system-qt] [-skip-download] [-skip-cleanup] [-only-download] " + exit 1 +fi + +for arg in "$@"; do + if [ "$arg" == "-system-freetype" ]; then + echo "Skipping building FreeType." + SKIP_FREETYPE=true + shift + elif [ "$arg" == "-system-harfbuzz" ]; then + echo "Skipping building HarfBuzz." + SKIP_HARFBUZZ=true + shift + elif [ "$arg" == "-system-libjpeg" ]; then + echo "Skipping building libjpeg." + SKIP_LIBJPEG=true + shift + elif [ "$arg" == "-system-libpng" ]; then + echo "Skipping building libpng." + SKIP_LIBPNG=true + shift + elif [ "$arg" == "-system-libwebp" ]; then + echo "Skipping building libwebp." + SKIP_LIBWEBP=true + shift + elif [ "$arg" == "-system-libzip" ]; then + echo "Skipping building libzip." + SKIP_LIBZIP=true + shift + elif [ "$arg" == "-system-zstd" ]; then + echo "Skipping building zstd." + SKIP_ZSTD=true + shift + elif [ "$arg" == "-system-qt" ]; then + echo "Skipping building Qt." + SKIP_QT=true + shift + elif [ "$arg" == "-skip-download" ]; then + echo "Not downloading sources." + SKIP_DOWNLOAD=true + shift + elif [ "$arg" == "-skip-cleanup" ]; then + echo "Not removing build directory." + SKIP_CLEANUP=true + shift + elif [ "$arg" == "-only-download" ]; then + echo "Only downloading sources." + ONLY_DOWNLOAD=true + shift + fi +done + +SCRIPTDIR=$(realpath $(dirname "${BASH_SOURCE[0]}")) +NPROCS="$(getconf _NPROCESSORS_ONLN)" +HOSTDIR="$1" +if [ "${HOSTDIR:0:1}" != "/" ]; then + HOSTDIR="$PWD/$HOSTDIR" +fi +CROSSARCH="$2" +SYSROOTDIR="$3" +if [ "${SYSROOTDIR:0:1}" != "/" ]; then + SYSROOTDIR="$PWD/$SYSROOTDIR" +fi +INSTALLDIR="$4" +if [ "${INSTALLDIR:0:1}" != "/" ]; then + INSTALLDIR="$PWD/$INSTALLDIR" +fi +TOOLCHAINFILE="$INSTALLDIR/toolchain.cmake" +CMAKE_COMMON=( + -DCMAKE_BUILD_TYPE=Release + -DCMAKE_TOOLCHAIN_FILE="$TOOLCHAINFILE" + -DCMAKE_PREFIX_PATH="$INSTALLDIR" + -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" +) + +# TODO: Pull all of this from the main file. + +FREETYPE=2.13.3 +HARFBUZZ=10.1.0 +LIBBACKTRACE=86885d14049fab06ef8a33aac51664230ca09200 +LIBJPEGTURBO=3.0.4 +LIBPNG=1.6.44 +LIBWEBP=1.4.0 +LIBZIP=1.11.2 +SDL2=2.30.9 +QT=6.8.1 +ZSTD=1.5.6 + +CPUINFO=7524ad504fdcfcf75a18a133da6abd75c5d48053 +DISCORD_RPC=144f3a3f1209994d8d9e8a87964a989cb9911c1e +LUNASVG=9af1ac7b90658a279b372add52d6f77a4ebb482c +SHADERC=1c0d3d18819aa75ec74f1fbd9ff0461e1b69a4d6 +SOUNDTOUCH=463ade388f3a51da078dc9ed062bf28e4ba29da7 +SPIRV_CROSS=vulkan-sdk-1.3.290.0 + +mkdir -p "${INSTALLDIR}" +mkdir -p deps-build +cd deps-build + +if [ "$SKIP_DOWNLOAD" != true ]; then + curl -C - -L \ + -O "https://github.com/ianlancetaylor/libbacktrace/archive/$LIBBACKTRACE.tar.gz" \ + -O "https://github.com/libsdl-org/SDL/releases/download/release-$SDL2/SDL2-$SDL2.tar.gz" \ + -o "cpuinfo-$CPUINFO.tar.gz" "https://github.com/stenzek/cpuinfo/archive/$CPUINFO.tar.gz" \ + -o "discord-rpc-$DISCORD_RPC.tar.gz" "https://github.com/stenzek/discord-rpc/archive/$DISCORD_RPC.tar.gz" \ + -o "lunasvg-$LUNASVG.tar.gz" "https://github.com/stenzek/lunasvg/archive/$LUNASVG.tar.gz" \ + -o "shaderc-$SHADERC.tar.gz" "https://github.com/stenzek/shaderc/archive/$SHADERC.tar.gz" \ + -o "soundtouch-$SOUNDTOUCH.tar.gz" "https://github.com/stenzek/soundtouch/archive/$SOUNDTOUCH.tar.gz" +fi + +cat > SHASUMS <> SHASUMS <> SHASUMS <> SHASUMS <> SHASUMS <> SHASUMS <> SHASUMS <> SHASUMS <> SHASUMS < "$TOOLCHAINFILE" << EOF +set(CMAKE_CROSSCOMPILING TRUE) +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR ${CROSSARCH}) + +set(CMAKE_C_COMPILER "/usr/bin/${CROSSARCH}-linux-gnu-gcc") +set(CMAKE_C_COMPILER_TARGET "${CROSSARCH}-linux-gnu") +set(CMAKE_C_COMPILER_AR "/usr/bin/${CROSSARCH}-linux-gnu-ar") +set(CMAKE_C_COMPILER_RANLIB "/usr/bin/${CROSSARCH}-linux-gnu-ranlib") + +set(CMAKE_CXX_COMPILER "/usr/bin/${CROSSARCH}-linux-gnu-g++") +set(CMAKE_CXX_COMPILER_TARGET "${CROSSARCH}-linux-gnu") +set(CMAKE_CXX_COMPILER_AR "/usr/bin/${CROSSARCH}-linux-gnu-ar") +set(CMAKE_CXX_COMPILER_RANLIB "/usr/bin/${CROSSARCH}-linux-gnu-ranlib") + +set(CMAKE_FIND_ROOT_PATH "${INSTALLDIR};${SYSROOTDIR}") +set(CMAKE_SYSROOT "${SYSROOTDIR}") + +set(CMAKE_PKG_CONFIG_PC_PATH "${PKG_CONFIG_PATH}") +set(CMAKE_PKG_CONFIG_SYSROOT_DIR "${PKG_CONFIG_SYSROOT_DIR}") + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) +EOF + +# NOTE: Must be a shared library because otherwise aarch64 libgcc symbols are missing when building with clang. +echo "Building libbacktrace..." +rm -fr "libbacktrace-$LIBBACKTRACE" +tar xf "$LIBBACKTRACE.tar.gz" +cd "libbacktrace-$LIBBACKTRACE" +./configure --prefix="$INSTALLDIR" --build=x86_64-linux-gnu --host="${CROSSARCH}-linux-gnu" --with-pic --enable-shared --disable-static +make +make install +cd .. + +if [ "$SKIP_LIBPNG" != true ]; then + echo "Building libpng..." + rm -fr "libpng-$LIBPNG" + tar xf "libpng-$LIBPNG.tar.xz" + cd "libpng-$LIBPNG" + cmake "${CMAKE_COMMON[@]}" -DBUILD_SHARED_LIBS=ON -DPNG_TESTS=OFF -DPNG_STATIC=OFF -DPNG_SHARED=ON -DPNG_TOOLS=OFF -B build -G Ninja + cmake --build build --parallel + ninja -C build install + cd .. +fi + +if [ "$SKIP_LIBJPEG" != true ]; then + echo "Building libjpeg..." + rm -fr "libjpeg-turbo-$LIBJPEGTURBO" + tar xf "libjpeg-turbo-$LIBJPEGTURBO.tar.gz" + cd "libjpeg-turbo-$LIBJPEGTURBO" + cmake "${CMAKE_COMMON[@]}" -DENABLE_STATIC=OFF -DENABLE_SHARED=ON -B build -G Ninja + cmake --build build --parallel + ninja -C build install + cd .. +fi + +if [ "$SKIP_ZSTD" != true ]; then + echo "Building Zstandard..." + rm -fr "zstd-$ZSTD" + tar xf "zstd-$ZSTD.tar.gz" + cd "zstd-$ZSTD" + cmake "${CMAKE_COMMON[@]}" -DBUILD_SHARED_LIBS=ON -DZSTD_BUILD_SHARED=ON -DZSTD_BUILD_STATIC=OFF -DZSTD_BUILD_PROGRAMS=OFF -B build -G Ninja build/cmake + cmake --build build --parallel + ninja -C build install + cd .. +fi + +if [ "$SKIP_LIBWEBP" != true ]; then + echo "Building WebP..." + rm -fr "libwebp-$LIBWEBP" + tar xf "libwebp-$LIBWEBP.tar.gz" + cd "libwebp-$LIBWEBP" + cmake "${CMAKE_COMMON[@]}" -B build -G Ninja \ + -DWEBP_BUILD_ANIM_UTILS=OFF -DWEBP_BUILD_CWEBP=OFF -DWEBP_BUILD_DWEBP=OFF -DWEBP_BUILD_GIF2WEBP=OFF -DWEBP_BUILD_IMG2WEBP=OFF \ + -DWEBP_BUILD_VWEBP=OFF -DWEBP_BUILD_WEBPINFO=OFF -DWEBP_BUILD_WEBPMUX=OFF -DWEBP_BUILD_EXTRAS=OFF -DBUILD_SHARED_LIBS=ON + cmake --build build --parallel + ninja -C build install + cd .. +fi + +if [ "$SKIP_LIBZIP" != true ]; then + echo "Building libzip..." + rm -fr "libzip-$LIBZIP" + tar xf "libzip-$LIBZIP.tar.xz" + cd "libzip-$LIBZIP" + cmake "${CMAKE_COMMON[@]}" -B build -G Ninja \ + -DENABLE_COMMONCRYPTO=OFF -DENABLE_GNUTLS=OFF -DENABLE_MBEDTLS=OFF -DENABLE_OPENSSL=OFF -DENABLE_WINDOWS_CRYPTO=OFF \ + -DENABLE_BZIP2=OFF -DENABLE_LZMA=OFF -DENABLE_ZSTD=ON -DBUILD_SHARED_LIBS=ON -DLIBZIP_DO_INSTALL=ON \ + -DBUILD_TOOLS=OFF -DBUILD_REGRESS=OFF -DBUILD_OSSFUZZ=OFF -DBUILD_EXAMPLES=OFF -DBUILD_DOC=OFF + cmake --build build --parallel + ninja -C build install + cd .. +fi + +if [ "$SKIP_FREETYPE" != true ]; then + if [ "$SKIP_HARFBUZZ" != true ]; then + echo "Building FreeType without HarfBuzz..." + rm -fr "freetype-$FREETYPE" + tar xf "freetype-$FREETYPE.tar.xz" + cd "freetype-$FREETYPE" + cmake "${CMAKE_COMMON[@]}" -DBUILD_SHARED_LIBS=ON -DFT_REQUIRE_ZLIB=ON -DFT_REQUIRE_PNG=ON -DFT_DISABLE_BZIP2=TRUE -DFT_DISABLE_BROTLI=TRUE -DFT_DISABLE_HARFBUZZ=TRUE -B build -G Ninja + cmake --build build --parallel + ninja -C build install + cd .. + + echo "Building HarfBuzz..." + rm -fr "harfbuzz-$HARFBUZZ" + tar xf "harfbuzz-$HARFBUZZ.tar.gz" + cd "harfbuzz-$HARFBUZZ" + cmake "${CMAKE_COMMON[@]}" -DBUILD_SHARED_LIBS=ON -DHB_BUILD_UTILS=OFF -B build -G Ninja + cmake --build build --parallel + ninja -C build install + cd .. + fi + + echo "Building FreeType with HarfBuzz..." + rm -fr "freetype-$FREETYPE" + tar xf "freetype-$FREETYPE.tar.xz" + cd "freetype-$FREETYPE" + cmake "${CMAKE_COMMON[@]}" -DBUILD_SHARED_LIBS=ON -DFT_REQUIRE_ZLIB=ON -DFT_REQUIRE_PNG=ON -DFT_DISABLE_BZIP2=TRUE -DFT_DISABLE_BROTLI=TRUE -DFT_REQUIRE_HARFBUZZ=TRUE -B build -G Ninja + cmake --build build --parallel + ninja -C build install + cd .. +fi + +echo "Building SDL2..." +rm -fr "SDL2-$SDL2" +tar xf "SDL2-$SDL2.tar.gz" +cd "SDL2-$SDL2" +# needed because -Isystem with chroot/usr/include breaks +patch -p1 < "$SCRIPTDIR/sdl2-disable-isystem.patch" +cmake -B build "${CMAKE_COMMON[@]}" -DBUILD_SHARED_LIBS=ON -DSDL_SHARED=ON -DSDL_STATIC=OFF -G Ninja +cmake --build build --parallel +ninja -C build install +cd .. + +if [ "$SKIP_QT" != true ]; then + # Couple notes: + # -fontconfig is needed otherwise Qt Widgets render only boxes. + # -qt-doubleconversion avoids a dependency on libdouble-conversion. + # ICU avoids pulling in a bunch of large libraries, and hopefully we can get away without it. + # OpenGL is needed to render window decorations in Wayland, apparently. + # dbus-runtime and linked off to avoid a relocation error (different to host.. probably should change that). + echo "Building Qt Base..." + rm -fr "qtbase-everywhere-src-$QT" + tar xf "qtbase-everywhere-src-$QT.tar.xz" + cd "qtbase-everywhere-src-$QT" + patch -p1 < "$SCRIPTDIR/qtbase-disable-pcre2-jit.patch" + mkdir build + cd build + ../configure -prefix "$INSTALLDIR" -extprefix "$INSTALLDIR" -qt-host-path "$HOSTDIR" -release -dbus runtime -gui -widgets -fontconfig -qt-doubleconversion -ssl -openssl-runtime -opengl desktop -qpa xcb,wayland -xkbcommon -xcb -gtk -- -DCMAKE_TOOLCHAIN_FILE="$TOOLCHAINFILE" -DFEATURE_cups=OFF -DFEATURE_dbus=ON -DFEATURE_dbus_linked=OFF -DFEATURE_icu=OFF -DFEATURE_sql=OFF -DFEATURE_system_png=ON -DFEATURE_system_jpeg=ON -DFEATURE_system_zlib=ON -DFEATURE_system_freetype=ON -DFEATURE_system_harfbuzz=ON + cmake --build . --parallel + ninja install + cd ../../ + + echo "Building Qt SVG..." + rm -fr "qtsvg-everywhere-src-$QT" + tar xf "qtsvg-everywhere-src-$QT.tar.xz" + cd "qtsvg-everywhere-src-$QT" + mkdir build + cd build + "$INSTALLDIR/bin/qt-configure-module" .. -- -DCMAKE_PREFIX_PATH="$INSTALLDIR" + cmake --build . --parallel + ninja install + cd ../../ + + echo "Building Qt Image Formats..." + rm -fr "qtimageformats-everywhere-src-$QT" + tar xf "qtimageformats-everywhere-src-$QT.tar.xz" + cd "qtimageformats-everywhere-src-$QT" + mkdir build + cd build + "$INSTALLDIR/bin/qt-configure-module" .. -- -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DFEATURE_system_webp=ON + cmake --build . --parallel + ninja install + cd ../../ + + echo "Building Qt Wayland..." + rm -fr "qtwayland-everywhere-src-$QT" + tar xf "qtwayland-everywhere-src-$QT.tar.xz" + cd "qtwayland-everywhere-src-$QT" + mkdir build + cd build + "$INSTALLDIR/bin/qt-configure-module" .. -- -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DFEATURE_wayland_server=OFF + cmake --build . --parallel + ninja install + cd ../../ + + echo "Installing Qt Tools..." + rm -fr "qttools-everywhere-src-$QT" + tar xf "qttools-everywhere-src-$QT.tar.xz" + cd "qttools-everywhere-src-$QT" + + # Force disable clang scanning, it gets very confused. + patch -u configure.cmake < +# SPDX-License-Identifier: CC-BY-NC-ND-4.0 + +SCRIPTDIR=$(dirname "${BASH_SOURCE[0]}") + +function retry_command { + # Package servers tend to be unreliable at times.. + # Retry a bunch of times. + local RETRIES=10 + + for i in $(seq 1 "$RETRIES"); do + "$@" && break + if [ "$i" == "$RETRIES" ]; then + echo "Command \"$@\" failed after ${RETRIES} retries." + exit 1 + fi + done +} + +if [ "$1" == "-inject-libc" ]; then + echo "Injecting libc/libstdc++" + INJECT_LIBC=true + shift +fi + +if [ "$#" -ne 5 ]; then + echo "Syntax: $0 [-inject-libc] " + exit 1 +fi + +ARCH=$1 +BUILDDIR=$2 +DEPSDIR=$3 +CHROOTDIR=$4 +NAME=$5 + +BINARY=duckstation-qt +APPDIRNAME=DuckStation.AppDir +STRIP=llvm-strip +TRIPLE="${ARCH}-linux-gnu" + +declare -a SYSLIBS=( + "libatk-1.0.so.0" + "libatk-bridge-2.0.so.0" + "libatspi.so.0" + "libblkid.so.1" + "libbrotlicommon.so.1" + "libbrotlidec.so.1" + "libbsd.so.0" + "libcairo-gobject.so.2" + "libcairo.so.2" + "libcap.so.2" + "libcrypto.so.3" + "libcurl.so.4" + "libdatrie.so.1" + "libdbus-1.so.3" + "libdeflate.so.0" + "libepoxy.so.0" + "libffi.so.8" + "libgcrypt.so.20" + "libgdk-3.so.0" + "libgdk_pixbuf-2.0.so.0" + "libgio-2.0.so.0" + "libglib-2.0.so.0" + "libgmodule-2.0.so.0" + "libgnutls.so.30" + "libgobject-2.0.so.0" + "libgraphite2.so.3" + "libgssapi_krb5.so.2" + "libgtk-3.so.0" + "libhogweed.so.6" + "libidn2.so.0" + "libjbig.so.0" + "libk5crypto.so.3" + "libkeyutils.so.1" + "libkrb5.so.3" + "libkrb5support.so.0" + "liblber-2.5.so.0" + "libldap-2.5.so.0" + "liblz4.so.1" + "liblzma.so.5" + "libmd.so.0" + "libmount.so.1" + "libnettle.so.8" + "libnghttp2.so.14" + "libp11-kit.so.0" + "libpango-1.0.so.0" + "libpangocairo-1.0.so.0" + "libpangoft2-1.0.so.0" + "libpcre2-16.so.0" + "libpcre2-8.so.0" + "libpcre.so.3" + "libpixman-1.so.0" + "libpsl.so.5" + "librtmp.so.1" + "libsasl2.so.2" + "libselinux.so.1" + "libssh.so.4" + "libssl.so.3" + "libsystemd.so.0" + "libtasn1.so.6" + "libtiff.so.5" + "libudev.so.1" + "libunistring.so.2" + "libXau.so.6" + "libxcb-cursor.so.0" + "libxcb-glx.so.0" + "libxcb-icccm.so.4" + "libxcb-image.so.0" + "libxcb-keysyms.so.1" + "libxcb-randr.so.0" + "libxcb-render.so.0" + "libxcb-render-util.so.0" + "libxcb-shape.so.0" + "libxcb-shm.so.0" + "libxcb-sync.so.1" + "libxcb-util.so.1" + "libxcb-xfixes.so.0" + "libxcb-xkb.so.1" + "libXcomposite.so.1" + "libXcursor.so.1" + "libXdamage.so.1" + "libXdmcp.so.6" + "libXext.so.6" + "libXfixes.so.3" + "libXinerama.so.1" + "libXi.so.6" + "libxkbcommon.so.0" + "libxkbcommon-x11.so.0" + "libXrandr.so.2" + "libXrender.so.1" +) + +declare -a DEPLIBS=( + "libbacktrace.so.0" + "libfreetype.so.6" + "libharfbuzz.so" + "libjpeg.so.62" + "libpng16.so.16" + "libSDL2-2.0.so.0" + "libsharpyuv.so.0" + "libwebpdemux.so.2" + "libwebpmux.so.3" + "libwebp.so.7" + "libzip.so.5" + "libzstd.so.1" + + "libcpuinfo.so" + "libdiscord-rpc.so" + "liblunasvg.so" + "libshaderc_ds.so" + "libsoundtouch.so.2" + "libspirv-cross-c-shared.so.0" + + #"libavcodec.so.61" + #"libavformat.so.61" + #"libavutil.so.59" + #"libswscale.so.8" + #"libswresample.so.5" + #"libva-drm.so.2" + #"libva.so.2" +) + +declare -a QTLIBS=( + "libQt6Core.so.6" + "libQt6DBus.so.6" + "libQt6Gui.so.6" + "libQt6OpenGL.so.6" + "libQt6Svg.so.6" + "libQt6WaylandClient.so.6" + "libQt6WaylandEglClientHwIntegration.so.6" + "libQt6Widgets.so.6" + "libQt6XcbQpa.so.6" +) + +declare -a QTPLUGINS=( + "plugins/iconengines" + "plugins/imageformats" + "plugins/platforminputcontexts" + "plugins/platforms" + "plugins/platformthemes" + "plugins/wayland-decoration-client" + "plugins/wayland-graphics-integration-client" + "plugins/wayland-shell-integration" + "plugins/xcbglintegrations" +) + +set -e +IFS=" +" + +APPIMAGETOOL=./appimagetool-x86_64 +APPIMAGERUNTIME=./runtime-${ARCH} +PATCHELF=patchelf + +if [ ! -f "$APPIMAGETOOL" ]; then + retry_command wget -O "$APPIMAGETOOL" https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage + chmod +x "$APPIMAGETOOL" +fi + +if [ ! -f "$APPIMAGERUNTIME" ]; then + retry_command wget -O "$APPIMAGERUNTIME" https://github.com/stenzek/type2-runtime/releases/download/continuous/runtime-${ARCH} +fi + +OUTDIR=$(realpath "./$APPDIRNAME") +rm -fr "$OUTDIR" +mkdir "$OUTDIR" +mkdir -p "$OUTDIR/usr/bin" "$OUTDIR/usr/lib" + +echo "Copying binary and resources..." +cp -a "$BUILDDIR/bin/$BINARY" "$BUILDDIR/bin/resources" "$BUILDDIR/bin/translations" "$OUTDIR/usr/bin" + +# Currently we leave the main binary unstripped, uncomment if this is not desired. +# NOTE: Strip must come before patchelf, otherwise shit breaks. +$STRIP "$OUTDIR/usr/bin/$BINARY" + +# Patch RPATH so the binary goes hunting for shared libraries in the AppDir instead of system. +echo "Patching RPATH in ${BINARY}..." +patchelf --set-rpath '$ORIGIN/../lib' "$OUTDIR/usr/bin/$BINARY" + +# Libraries we pull in from the system. +echo "Copying system libraries..." +for lib in "${SYSLIBS[@]}"; do + blib=$(basename "$lib") + echo "$CHROOTDIR/lib/$TRIPLE/$lib" + if [ -f "$CHROOTDIR/lib/$TRIPLE/$lib" ]; then + cp "$CHROOTDIR/lib/$TRIPLE/$lib" "$OUTDIR/usr/lib/$blib" + elif [ -f "$CHROOTDIR/usr/lib/$TRIPLE/$lib" ]; then + cp "$CHROOTDIR/usr/lib/$TRIPLE/$lib" "$OUTDIR/usr/lib/$blib" + elif [ -f "$CHROOTDIR/lib/$lib" ]; then + cp "$CHROOTDIR/lib/$lib" "$OUTDIR/usr/lib/$blib" + elif [ -f "$CHROOTDIR/usr/lib/$lib" ]; then + cp "$CHROOTDIR/usr/lib/$lib" "$OUTDIR/usr/lib/$blib" + else + echo "*** Failed to find '$blib'" + exit 1 + fi + + $STRIP $OUTDIR/usr/lib/$blib +done + +echo "Copying local libraries..." +for lib in "${DEPLIBS[@]}"; do + blib=$(basename "$lib") + echo "$DEPSDIR/lib/$lib" + if [ -f "$DEPSDIR/lib/$lib" ]; then + cp "$DEPSDIR/lib/$lib" "$OUTDIR/usr/lib/$blib" + else + echo "*** Failed to find '$blib'" + exit 1 + fi + + $STRIP "$OUTDIR/usr/lib/$blib" +done + +echo "Copying Qt libraries..." +for lib in "${QTLIBS[@]}"; do + cp -avL "$DEPSDIR/lib/$lib" "$OUTDIR/usr/lib" + $STRIP "$OUTDIR/usr/lib/$lib" +done + +echo "Copying Qt plugins..." +mkdir -p $OUTDIR/usr/lib/plugins +for plugin in "${QTPLUGINS[@]}"; do + mkdir -p "$OUTDIR/usr/lib/$plugin" + cp -avL "$DEPSDIR/$plugin/"*.so "$OUTDIR/usr/lib/$plugin/" +done + +for so in $(find $OUTDIR/usr/lib/plugins -iname '*.so'); do + # This is ../../ because it's usually plugins/group/name.so + echo "Patching RPATH in ${so}..." + patchelf --set-rpath '$ORIGIN/../..' "$so" + $STRIP "$so" +done + +for so in $(find $OUTDIR/usr/lib -maxdepth 1); do + if [ -f "$so" ]; then + echo "Patching RPATH in ${so}" + patchelf --set-rpath '$ORIGIN' "$so" + fi +done + +echo "Creating qt.conf..." +cat > "$OUTDIR/usr/bin/qt.conf" << EOF +[Paths] +Plugins = ../lib/plugins +EOF + +# Copy desktop/icon +echo "Copying desktop/icon..." +mkdir -p "$OUTDIR/usr/share/applications" +mkdir -p "$OUTDIR/usr/share/icons/hicolor/512x512/apps" +cp -v "$SCRIPTDIR/../org.duckstation.DuckStation.desktop" "$OUTDIR/usr/share/applications" +cp -v "$SCRIPTDIR/../org.duckstation.DuckStation.png" "$OUTDIR/usr/share/icons/hicolor/512x512/apps" +ln -s "usr/share/applications/org.duckstation.DuckStation.desktop" "$OUTDIR" +ln -s "usr/share/icons/hicolor/512x512/apps/org.duckstation.DuckStation.png" "$OUTDIR" + +# Generate AppStream meta-info. +echo "Generating AppStream metainfo..." +mkdir -p "$OUTDIR/usr/share/metainfo" +"$SCRIPTDIR/../generate-metainfo.sh" "$OUTDIR/usr/share/metainfo" + +# Copy AppRun +cp "$SCRIPTDIR/apprun-cross.sh" "$OUTDIR/AppRun" +chmod +x "$OUTDIR/AppRun" +ln -s "usr/bin/$BINARY" "$OUTDIR/AppRun.wrapped" + +# Copy in AppRun hooks. +echo "Copying AppRun hooks..." +mkdir -p "$OUTDIR/apprun-hooks" +for hookpath in "$SCRIPTDIR/apprun-hooks"/*; do + hookname=$(basename "$hookpath") + cp -v "$hookpath" "$OUTDIR/apprun-hooks/$hookname" + sed -i -e 's/exec /source "$this_dir"\/apprun-hooks\/"'"$hookname"'"\nexec /' "$OUTDIR/AppRun" +done + +# Optionally inject libc +if [ "$INJECT_LIBC" == true ]; then + echo "Injecting libc/libc++..." + if [ "$ARCH" == "aarch64" ]; then + DEBARCH="arm64" + else + echo "Unknown arch for libc injection." + exit 1 + fi + + "$SCRIPTDIR/inject-libc.sh" "$OUTDIR" "$DEBARCH" "$TRIPLE" "https://ports.ubuntu.com" "$BINARY" +fi + +echo "Generating AppImage..." +rm -f "$NAME.AppImage" +"$APPIMAGETOOL" -v --runtime-file "$APPIMAGERUNTIME" "$OUTDIR" "$NAME.AppImage" diff --git a/src/common/fastjmp.cpp b/src/common/fastjmp.cpp index 5ffca2a424..5daf72380a 100644 --- a/src/common/fastjmp.cpp +++ b/src/common/fastjmp.cpp @@ -12,13 +12,14 @@ #define PREFIX "" #endif +// clang-format off + #if defined(__x86_64__) asm("\t.global " PREFIX "fastjmp_set\n" - "\t.global " PREFIX "fastjmp_jmp\n" - "\t.text\n" - "\t" PREFIX "fastjmp_set:" - R"( + "\t.global " PREFIX "fastjmp_jmp\n" + "\t.text\n" + "\t" PREFIX "fastjmp_set:" R"( movq 0(%rsp), %rax movq %rsp, %rdx # fixup stack pointer, so it doesn't include the call to fastjmp_set addq $8, %rdx @@ -33,8 +34,7 @@ asm("\t.global " PREFIX "fastjmp_set\n" xorl %eax, %eax ret )" - "\t" PREFIX "fastjmp_jmp:" - R"( + "\t" PREFIX "fastjmp_jmp:" R"( movl %esi, %eax movq 0(%rdi), %rdx # actually rip movq 8(%rdi), %rbx @@ -95,20 +95,20 @@ asm( "\t.global " PREFIX "fastjmp_jmp\n" "\t.text\n" "\t" PREFIX "fastjmp_set:" R"( - vstmia r0!, {d8-d15} - stmia r0!, {r4-r14} - fmrx r1, fpscr - str r1, [r0] - mov r0, #0 - bx lr + vstmia r0!, {d8-d15} + stmia r0!, {r4-r14} + fmrx r1, fpscr + str r1, [r0] + mov r0, #0 + bx lr )" "\t" PREFIX "fastjmp_jmp:" R"( - vldmia r0!, {d8-d15} - ldmia r0!, {r4-r14} + vldmia r0!, {d8-d15} + ldmia r0!, {r4-r14} ldr r0, [r0] fmxr fpscr, r0 - mov r0, r1 + mov r0, r1 bx lr )"); @@ -117,68 +117,69 @@ asm( asm( "\t.global " PREFIX "fastjmp_set\n" "\t.global " PREFIX "fastjmp_jmp\n" + "\t.attribute arch, \"rv64gc\"\n" "\t.text\n" "\t.align 16\n" "\t" PREFIX "fastjmp_set:" R"( - sd sp, 0(a0) - sd s0, 8(a0) - sd s1, 16(a0) - sd s2, 24(a0) - sd s3, 32(a0) - sd s4, 40(a0) - sd s5, 48(a0) - sd s6, 56(a0) - sd s7, 64(a0) - sd s8, 72(a0) - sd s9, 80(a0) - sd s10, 88(a0) - sd s11, 96(a0) - fsd fs0, 104(a0) - fsd fs1, 112(a0) - fsd fs2, 120(a0) - fsd fs3, 128(a0) - fsd fs4, 136(a0) - fsd fs5, 144(a0) - fsd fs6, 152(a0) - fsd fs7, 160(a0) - fsd fs8, 168(a0) - fsd fs9, 176(a0) - fsd fs10, 184(a0) - fsd fs11, 192(a0) - sd ra, 208(a0) - li a0, 0 - jr ra + sd sp, 0(a0) + sd s0, 8(a0) + sd s1, 16(a0) + sd s2, 24(a0) + sd s3, 32(a0) + sd s4, 40(a0) + sd s5, 48(a0) + sd s6, 56(a0) + sd s7, 64(a0) + sd s8, 72(a0) + sd s9, 80(a0) + sd s10, 88(a0) + sd s11, 96(a0) + fsd fs0, 104(a0) + fsd fs1, 112(a0) + fsd fs2, 120(a0) + fsd fs3, 128(a0) + fsd fs4, 136(a0) + fsd fs5, 144(a0) + fsd fs6, 152(a0) + fsd fs7, 160(a0) + fsd fs8, 168(a0) + fsd fs9, 176(a0) + fsd fs10, 184(a0) + fsd fs11, 192(a0) + sd ra, 208(a0) + li a0, 0 + jr ra )" ".align 16\n" "\t" PREFIX "fastjmp_jmp:" R"( - ld ra, 208(a0) - fld fs11, 192(a0) - fld fs10, 184(a0) - fld fs9, 176(a0) - fld fs8, 168(a0) - fld fs7, 160(a0) - fld fs6, 152(a0) - fld fs5, 144(a0) - fld fs4, 136(a0) - fld fs3, 128(a0) - fld fs2, 120(a0) - fld fs1, 112(a0) - fld fs0, 104(a0) - ld s11, 96(a0) - ld s10, 88(a0) - ld s9, 80(a0) - ld s8, 72(a0) - ld s7, 64(a0) - ld s6, 56(a0) - ld s5, 48(a0) - ld s4, 40(a0) - ld s3, 32(a0) - ld s2, 24(a0) - ld s1, 16(a0) - ld s0, 8(a0) - ld sp, 0(a0) - mv a0, a1 - jr ra + ld ra, 208(a0) + fld fs11, 192(a0) + fld fs10, 184(a0) + fld fs9, 176(a0) + fld fs8, 168(a0) + fld fs7, 160(a0) + fld fs6, 152(a0) + fld fs5, 144(a0) + fld fs4, 136(a0) + fld fs3, 128(a0) + fld fs2, 120(a0) + fld fs1, 112(a0) + fld fs0, 104(a0) + ld s11, 96(a0) + ld s10, 88(a0) + ld s9, 80(a0) + ld s8, 72(a0) + ld s7, 64(a0) + ld s6, 56(a0) + ld s5, 48(a0) + ld s4, 40(a0) + ld s3, 32(a0) + ld s2, 24(a0) + ld s1, 16(a0) + ld s0, 8(a0) + ld sp, 0(a0) + mv a0, a1 + jr ra )"); @@ -188,4 +189,6 @@ asm( #endif +// clang-format on + #endif // __WIN32 diff --git a/src/common/fastjmp.h b/src/common/fastjmp.h index c7e84284e7..e815d7b85e 100644 --- a/src/common/fastjmp.h +++ b/src/common/fastjmp.h @@ -21,7 +21,7 @@ struct fastjmp_buf #elif defined(_M_IX86) || defined(__i386__) static constexpr std::size_t BUF_SIZE = 24; #elif defined(__riscv) && __riscv_xlen == 64 - static constexpr std::size_t BUF_SIZE = 208; + static constexpr std::size_t BUF_SIZE = 216; #else #error Unknown architecture. #endif diff --git a/src/core/bus.cpp b/src/core/bus.cpp index 9686ba82df..2ffad56225 100644 --- a/src/core/bus.cpp +++ b/src/core/bus.cpp @@ -794,6 +794,8 @@ const TickCount* Bus::GetMemoryAccessTimePtr(PhysicalMemoryAddress address, Memo // Currently only BIOS, but could be EXP1 as well. if (address >= BIOS_BASE && address < (BIOS_BASE + BIOS_MIRROR_SIZE)) return &g_bios_access_time[static_cast(size)]; + else if (address >= EXP1_BASE && address < (EXP1_BASE + EXP1_SIZE)) + return &g_exp1_access_time[static_cast(size)]; return nullptr; } @@ -1638,8 +1640,7 @@ template u32 Bus::SIO2ReadHandler(PhysicalMemoryAddress address) { // Stub for using PS2 BIOS. - if (const BIOS::ImageInfo* ii = System::GetBIOSImageInfo(); - !ii || ii->fastboot_patch != BIOS::ImageInfo::FastBootPatch::Type2) [[unlikely]] + if (!System::IsUsingPS2BIOS()) [[unlikely]] { // Throw exception when not using PS2 BIOS. return UnmappedReadHandler(address); @@ -1653,8 +1654,7 @@ template void Bus::SIO2WriteHandler(PhysicalMemoryAddress address, u32 value) { // Stub for using PS2 BIOS. - if (const BIOS::ImageInfo* ii = System::GetBIOSImageInfo(); - !ii || ii->fastboot_patch != BIOS::ImageInfo::FastBootPatch::Type2) [[unlikely]] + if (!System::IsUsingPS2BIOS()) [[unlikely]] { // Throw exception when not using PS2 BIOS. UnmappedWriteHandler(address, value); diff --git a/src/core/cpu_recompiler_riscv64.cpp b/src/core/cpu_recompiler_riscv64.cpp index 2366bd591b..b5e38819f9 100644 --- a/src/core/cpu_recompiler_riscv64.cpp +++ b/src/core/cpu_recompiler_riscv64.cpp @@ -282,8 +282,8 @@ u32 CPU::CodeCache::EmitASMFunctions(void* code, u32 code_size) rvAsm->SLLI(RARG2, RARG2, 3); rvAsm->ADD(RARG2, RARG2, RARG3); rvAsm->LD(RARG2, 0, RARG2); - rvAsm->SLLI(RARG1, RARG1, 48); // idx = (pc & 0xFFFF) >> 2 - rvAsm->SRLI(RARG1, RARG1, 50); + rvAsm->SLLI64(RARG1, RARG1, 48); // idx = (pc & 0xFFFF) >> 2 + rvAsm->SRLI64(RARG1, RARG1, 50); rvAsm->SLLI(RARG1, RARG1, 3); // blr(x9[pc * 2]) (fast_map[idx]) @@ -560,7 +560,7 @@ void CPU::RISCV64Recompiler::GenerateICacheCheckAndUpdate() if (m_block->HasFlag(CodeCache::BlockFlags::NeedsDynamicFetchTicks)) { rvEmitFarLoad(rvAsm, RARG2, GetFetchMemoryAccessTimePtr()); - rvAsm->LW(RARG1, PTR(&g_state.pending_ticks)); + rvAsm->LWU(RARG1, PTR(&g_state.pending_ticks)); rvEmitMov(rvAsm, RARG3, m_block->size); rvAsm->MULW(RARG2, RARG2, RARG3); rvAsm->ADD(RARG1, RARG1, RARG2); @@ -568,7 +568,7 @@ void CPU::RISCV64Recompiler::GenerateICacheCheckAndUpdate() } else { - rvAsm->LW(RARG1, PTR(&g_state.pending_ticks)); + rvAsm->LWU(RARG1, PTR(&g_state.pending_ticks)); SafeADDIW(RARG1, RARG1, static_cast(m_block->uncached_fetch_ticks)); rvAsm->SW(RARG1, PTR(&g_state.pending_ticks)); } @@ -579,8 +579,12 @@ void CPU::RISCV64Recompiler::GenerateICacheCheckAndUpdate() const auto& current_tag_reg = RARG2; const auto& existing_tag_reg = RARG3; + // start of block, nothing should be using this + const auto& maddr_reg = biscuit::t0; + DebugAssert(!IsHostRegAllocated(maddr_reg.Index())); + VirtualMemoryAddress current_pc = m_block->pc & ICACHE_TAG_ADDRESS_MASK; - rvAsm->LW(ticks_reg, PTR(&g_state.pending_ticks)); + rvAsm->LWU(ticks_reg, PTR(&g_state.pending_ticks)); rvEmitMov(rvAsm, current_tag_reg, current_pc); for (u32 i = 0; i < m_block->icache_line_count; i++, current_pc += ICACHE_LINE_SIZE) @@ -592,12 +596,22 @@ void CPU::RISCV64Recompiler::GenerateICacheCheckAndUpdate() const u32 line = GetICacheLine(current_pc); const u32 offset = OFFSETOF(State, icache_tags) + (line * sizeof(u32)); - // TODO: Verify sign extension here... + // Offsets must fit in signed 12 bits. Label cache_hit; - rvAsm->LW(existing_tag_reg, offset, RSTATE); - rvAsm->BEQ(existing_tag_reg, current_tag_reg, &cache_hit); + if (offset >= 2048) + { + SafeADDI(maddr_reg, RSTATE, offset); + rvAsm->LW(existing_tag_reg, 0, maddr_reg); + rvAsm->BEQ(existing_tag_reg, current_tag_reg, &cache_hit); + rvAsm->SW(current_tag_reg, 0, maddr_reg); + } + else + { + rvAsm->LW(existing_tag_reg, offset, RSTATE); + rvAsm->BEQ(existing_tag_reg, current_tag_reg, &cache_hit); + rvAsm->SW(current_tag_reg, offset, RSTATE); + } - rvAsm->SW(current_tag_reg, offset, RSTATE); SafeADDIW(ticks_reg, ticks_reg, static_cast(fill_ticks)); rvAsm->Bind(&cache_hit); diff --git a/src/core/fullscreen_ui.cpp b/src/core/fullscreen_ui.cpp index 4387abe762..954bc80a92 100644 --- a/src/core/fullscreen_ui.cpp +++ b/src/core/fullscreen_ui.cpp @@ -4384,9 +4384,9 @@ void FullscreenUI::DrawGraphicsSettingsPage() bsi, FSUI_ICONSTR(ICON_FA_GRIP_LINES, "Deinterlacing Mode"), FSUI_CSTR( "Determines which algorithm is used to convert interlaced frames to progressive for display on your system."), - "Display", "DeinterlacingMode", Settings::DEFAULT_DISPLAY_DEINTERLACING_MODE, - &Settings::ParseDisplayDeinterlacingMode, &Settings::GetDisplayDeinterlacingModeName, - &Settings::GetDisplayDeinterlacingModeDisplayName, DisplayDeinterlacingMode::Count); + "GPU", "DeinterlacingMode", Settings::DEFAULT_DISPLAY_DEINTERLACING_MODE, &Settings::ParseDisplayDeinterlacingMode, + &Settings::GetDisplayDeinterlacingModeName, &Settings::GetDisplayDeinterlacingModeDisplayName, + DisplayDeinterlacingMode::Count); DrawEnumSetting(bsi, FSUI_ICONSTR(ICON_FA_CROP_ALT, "Crop Mode"), FSUI_CSTR("Determines how much of the area typically not visible on a consumer TV set to crop/hide."), diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index e3fb627c81..356bc099af 100644 --- a/src/core/gpu.cpp +++ b/src/core/gpu.cpp @@ -462,7 +462,7 @@ void GPU::UpdateDMARequest() case BlitterState::ReadingVRAM: m_GPUSTAT.ready_to_send_vram = true; - m_GPUSTAT.ready_to_recieve_dma = m_fifo.IsEmpty(); + m_GPUSTAT.ready_to_recieve_dma = false; break; case BlitterState::DrawingPolyLine: @@ -1883,19 +1883,22 @@ void GPU::ClearDisplay() void GPU::UpdateDisplay(bool submit_frame) { + const bool interlaced = IsInterlacedDisplayEnabled(); + const u8 interlaced_field = GetInterlacedDisplayField(); + const bool line_skip = (interlaced && m_GPUSTAT.vertical_resolution); GPUBackendUpdateDisplayCommand* cmd = GPUBackend::NewUpdateDisplayCommand(); cmd->display_width = m_crtc_state.display_width; cmd->display_height = m_crtc_state.display_height; cmd->display_origin_left = m_crtc_state.display_origin_left; cmd->display_origin_top = m_crtc_state.display_origin_top; cmd->display_vram_left = m_crtc_state.display_vram_left; - cmd->display_vram_top = m_crtc_state.display_vram_top; + cmd->display_vram_top = m_crtc_state.display_vram_top + (interlaced_field & BoolToUInt8(line_skip)); cmd->display_vram_width = m_crtc_state.display_vram_width; - cmd->display_vram_height = m_crtc_state.display_vram_height; + cmd->display_vram_height = m_crtc_state.display_vram_height >> BoolToUInt8(interlaced); cmd->X = m_crtc_state.regs.X; - cmd->interlaced_display_enabled = IsInterlacedDisplayEnabled(); - cmd->interlaced_display_field = ConvertToBoolUnchecked(GetInterlacedDisplayField()); - cmd->interlaced_display_interleaved = cmd->interlaced_display_enabled && m_GPUSTAT.vertical_resolution; + cmd->interlaced_display_enabled = interlaced; + cmd->interlaced_display_field = ConvertToBoolUnchecked(interlaced_field); + cmd->interlaced_display_interleaved = line_skip; cmd->display_24bit = m_GPUSTAT.display_area_color_depth_24; cmd->display_disabled = IsDisplayDisabled(); cmd->display_pixel_aspect_ratio = ComputePixelAspectRatio(); diff --git a/src/core/gpu.h b/src/core/gpu.h index d177a44545..595d9012a0 100644 --- a/src/core/gpu.h +++ b/src/core/gpu.h @@ -378,7 +378,12 @@ class GPU final ALWAYS_INLINE_RELEASE void AddDrawLineTicks(const GSVector4i rect, bool shaded) { const GSVector4i clamped_rect = rect.rintersect(m_clamped_drawing_area); - u32 drawn_width = clamped_rect.width(); + + // Needed because we're not multiplying either dimension. + if (clamped_rect.rempty()) + return; + + const u32 drawn_width = clamped_rect.width(); u32 drawn_height = clamped_rect.height(); if (m_GPUSTAT.SkipDrawingToActiveField()) diff --git a/src/core/gpu_backend.cpp b/src/core/gpu_backend.cpp index 1b1c9c4d37..d2c2e9b340 100644 --- a/src/core/gpu_backend.cpp +++ b/src/core/gpu_backend.cpp @@ -578,30 +578,15 @@ bool GPUBackend::CompileDisplayPipelines(bool display, bool deinterlace, bool ch if (deinterlace) { - plconfig.SetTargetFormats(GPUTexture::Format::RGBA8); - std::unique_ptr vso = g_gpu_device->CreateShader(GPUShaderStage::Vertex, shadergen.GetLanguage(), shadergen.GenerateScreenQuadVertexShader(), error); if (!vso) return false; GL_OBJECT_NAME(vso, "Deinterlace Vertex Shader"); - std::unique_ptr fso; - if (!(fso = g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GetLanguage(), - shadergen.GenerateInterleavedFieldExtractFragmentShader(), error))) - { - return false; - } - - GL_OBJECT_NAME(fso, "Deinterlace Field Extract Fragment Shader"); - plconfig.layout = GPUPipeline::Layout::SingleTextureAndPushConstants; plconfig.vertex_shader = vso.get(); - plconfig.fragment_shader = fso.get(); - if (!(m_deinterlace_extract_pipeline = g_gpu_device->CreatePipeline(plconfig, error))) - return false; - - GL_OBJECT_NAME(m_deinterlace_extract_pipeline, "Deinterlace Field Extract Pipeline"); + plconfig.SetTargetFormats(GPUTexture::Format::RGBA8); switch (g_gpu_settings.display_deinterlacing_mode) { @@ -611,11 +596,10 @@ bool GPUBackend::CompileDisplayPipelines(bool display, bool deinterlace, bool ch case DisplayDeinterlacingMode::Weave: { - if (!(fso = g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GetLanguage(), - shadergen.GenerateDeinterlaceWeaveFragmentShader(), error))) - { + std::unique_ptr fso = g_gpu_device->CreateShader( + GPUShaderStage::Fragment, shadergen.GetLanguage(), shadergen.GenerateDeinterlaceWeaveFragmentShader(), error); + if (!fso) return false; - } GL_OBJECT_NAME(fso, "Weave Deinterlace Fragment Shader"); @@ -631,11 +615,10 @@ bool GPUBackend::CompileDisplayPipelines(bool display, bool deinterlace, bool ch case DisplayDeinterlacingMode::Blend: { - if (!(fso = g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GetLanguage(), - shadergen.GenerateDeinterlaceBlendFragmentShader(), error))) - { + std::unique_ptr fso = g_gpu_device->CreateShader( + GPUShaderStage::Fragment, shadergen.GetLanguage(), shadergen.GenerateDeinterlaceBlendFragmentShader(), error); + if (!fso) return false; - } GL_OBJECT_NAME(fso, "Blend Deinterlace Fragment Shader"); @@ -651,8 +634,9 @@ bool GPUBackend::CompileDisplayPipelines(bool display, bool deinterlace, bool ch case DisplayDeinterlacingMode::Adaptive: { - fso = g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GetLanguage(), - shadergen.GenerateFastMADReconstructFragmentShader(), error); + std::unique_ptr fso = + g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GetLanguage(), + shadergen.GenerateFastMADReconstructFragmentShader(), error); if (!fso) return false; @@ -704,13 +688,14 @@ bool GPUBackend::CompileDisplayPipelines(bool display, bool deinterlace, bool ch void GPUBackend::HandleUpdateDisplayCommand(const GPUBackendUpdateDisplayCommand* cmd) { + // Height has to be doubled because we halved it on the GPU side. const GPUBackendUpdateDisplayCommand* ccmd = static_cast(cmd); m_display_width = ccmd->display_width; m_display_height = ccmd->display_height; m_display_origin_left = ccmd->display_origin_left; m_display_origin_top = ccmd->display_origin_top; m_display_vram_width = ccmd->display_vram_width; - m_display_vram_height = ccmd->display_vram_height; + m_display_vram_height = (ccmd->display_vram_height << BoolToUInt32(ccmd->interlaced_display_enabled)); m_display_pixel_aspect_ratio = ccmd->display_pixel_aspect_ratio; UpdateDisplay(ccmd); @@ -1022,7 +1007,7 @@ void GPUBackend::DestroyDeinterlaceTextures() m_current_deinterlace_buffer = 0; } -bool GPUBackend::Deinterlace(u32 field, u32 line_skip) +bool GPUBackend::Deinterlace(u32 field) { GPUTexture* src = m_display_texture; const u32 x = m_display_texture_view_x; @@ -1030,24 +1015,39 @@ bool GPUBackend::Deinterlace(u32 field, u32 line_skip) const u32 width = m_display_texture_view_width; const u32 height = m_display_texture_view_height; + const auto copy_to_field_buffer = [&](u32 buffer) { + if (!m_deinterlace_buffers[buffer] || m_deinterlace_buffers[buffer]->GetWidth() != width || + m_deinterlace_buffers[buffer]->GetHeight() != height || + m_deinterlace_buffers[buffer]->GetFormat() != src->GetFormat()) + { + if (!g_gpu_device->ResizeTexture(&m_deinterlace_buffers[buffer], width, height, GPUTexture::Type::Texture, + src->GetFormat(), GPUTexture::Flags::None, false)) [[unlikely]] + { + return false; + } + + GL_OBJECT_NAME_FMT(m_deinterlace_buffers[buffer], "Blend Deinterlace Buffer {}", buffer); + } + + GL_INS_FMT("Copy {}x{} from {},{} to field buffer {}", width, height, x, y, buffer); + g_gpu_device->CopyTextureRegion(m_deinterlace_buffers[buffer].get(), 0, 0, 0, 0, m_display_texture, x, y, 0, 0, + width, height); + return true; + }; + + src->MakeReadyForSampling(); + switch (g_gpu_settings.display_deinterlacing_mode) { case DisplayDeinterlacingMode::Disabled: { - if (line_skip == 0) - return true; - - // Still have to extract the field. - if (!DeinterlaceExtractField(0, src, x, y, width, height, line_skip)) [[unlikely]] - return false; - - SetDisplayTexture(m_deinterlace_buffers[0].get(), m_display_depth_buffer, 0, 0, width, height); + GL_INS("Deinterlacing disabled, displaying field texture"); return true; } case DisplayDeinterlacingMode::Weave: { - GL_SCOPE_FMT("DeinterlaceWeave({{{},{}}}, {}x{}, field={}, line_skip={})", x, y, width, height, field, line_skip); + GL_SCOPE_FMT("DeinterlaceWeave({{{},{}}}, {}x{}, field={})", x, y, width, height, field); const u32 full_height = height * 2; if (!DeinterlaceSetTargetSize(width, full_height, true)) [[unlikely]] @@ -1061,7 +1061,7 @@ bool GPUBackend::Deinterlace(u32 field, u32 line_skip) g_gpu_device->SetRenderTarget(m_deinterlace_texture.get()); g_gpu_device->SetPipeline(m_deinterlace_pipeline.get()); g_gpu_device->SetTextureSampler(0, src, g_gpu_device->GetNearestSampler()); - const u32 uniforms[] = {x, y, field, line_skip}; + const u32 uniforms[4] = {x, y, field, 0}; g_gpu_device->PushUniformBuffer(uniforms, sizeof(uniforms)); g_gpu_device->SetViewportAndScissor(0, 0, width, full_height); g_gpu_device->Draw(3, 0); @@ -1075,20 +1075,20 @@ bool GPUBackend::Deinterlace(u32 field, u32 line_skip) { constexpr u32 NUM_BLEND_BUFFERS = 2; - GL_SCOPE_FMT("DeinterlaceBlend({{{},{}}}, {}x{}, field={}, line_skip={})", x, y, width, height, field, line_skip); + GL_SCOPE_FMT("DeinterlaceBlend({{{},{}}}, {}x{}, field={})", x, y, width, height, field); const u32 this_buffer = m_current_deinterlace_buffer; m_current_deinterlace_buffer = (m_current_deinterlace_buffer + 1u) % NUM_BLEND_BUFFERS; GL_INS_FMT("Current buffer: {}", this_buffer); - if (!DeinterlaceExtractField(this_buffer, src, x, y, width, height, line_skip) || - !DeinterlaceSetTargetSize(width, height, false)) [[unlikely]] + if (!DeinterlaceSetTargetSize(width, height, false) || !copy_to_field_buffer(this_buffer)) [[unlikely]] { ClearDisplayTexture(); return false; } - // TODO: could be implemented with alpha blending instead.. + copy_to_field_buffer(this_buffer); + // TODO: could be implemented with alpha blending instead.. g_gpu_device->InvalidateRenderTarget(m_deinterlace_texture.get()); g_gpu_device->SetRenderTarget(m_deinterlace_texture.get()); g_gpu_device->SetPipeline(m_deinterlace_pipeline.get()); @@ -1105,15 +1105,13 @@ bool GPUBackend::Deinterlace(u32 field, u32 line_skip) case DisplayDeinterlacingMode::Adaptive: { - GL_SCOPE_FMT("DeinterlaceAdaptive({{{},{}}}, {}x{}, field={}, line_skip={})", x, y, width, height, field, - line_skip); + GL_SCOPE_FMT("DeinterlaceAdaptive({{{},{}}}, {}x{}, field={})", x, y, width, height, field); - const u32 full_height = height * 2; const u32 this_buffer = m_current_deinterlace_buffer; + const u32 full_height = height * 2; m_current_deinterlace_buffer = (m_current_deinterlace_buffer + 1u) % DEINTERLACE_BUFFER_COUNT; GL_INS_FMT("Current buffer: {}", this_buffer); - if (!DeinterlaceExtractField(this_buffer, src, x, y, width, height, line_skip) || - !DeinterlaceSetTargetSize(width, full_height, false)) [[unlikely]] + if (!DeinterlaceSetTargetSize(width, full_height, false) || !copy_to_field_buffer(this_buffer)) [[unlikely]] { ClearDisplayTexture(); return false; @@ -1143,50 +1141,6 @@ bool GPUBackend::Deinterlace(u32 field, u32 line_skip) } } -bool GPUBackend::DeinterlaceExtractField(u32 dst_bufidx, GPUTexture* src, u32 x, u32 y, u32 width, u32 height, - u32 line_skip) -{ - if (!m_deinterlace_buffers[dst_bufidx] || m_deinterlace_buffers[dst_bufidx]->GetWidth() != width || - m_deinterlace_buffers[dst_bufidx]->GetHeight() != height) - { - if (!g_gpu_device->ResizeTexture(&m_deinterlace_buffers[dst_bufidx], width, height, GPUTexture::Type::RenderTarget, - GPUTexture::Format::RGBA8, GPUTexture::Flags::None, false)) [[unlikely]] - { - return false; - } - - GL_OBJECT_NAME_FMT(m_deinterlace_buffers[dst_bufidx], "Blend Deinterlace Buffer {}", dst_bufidx); - } - - GPUTexture* dst = m_deinterlace_buffers[dst_bufidx].get(); - g_gpu_device->InvalidateRenderTarget(dst); - - // If we're not skipping lines, then we can simply copy the texture. - if (line_skip == 0 && src->GetFormat() == dst->GetFormat()) - { - GL_INS_FMT("DeinterlaceExtractField({{{},{}}} {}x{} line_skip={}) => copy direct", x, y, width, height, line_skip); - g_gpu_device->CopyTextureRegion(dst, 0, 0, 0, 0, src, x, y, 0, 0, width, height); - } - else - { - GL_SCOPE_FMT("DeinterlaceExtractField({{{},{}}} {}x{} line_skip={}) => shader copy", x, y, width, height, - line_skip); - - // Otherwise, we need to extract every other line from the texture. - src->MakeReadyForSampling(); - g_gpu_device->SetRenderTarget(dst); - g_gpu_device->SetPipeline(m_deinterlace_extract_pipeline.get()); - g_gpu_device->SetTextureSampler(0, src, g_gpu_device->GetNearestSampler()); - const u32 uniforms[] = {x, y, line_skip}; - g_gpu_device->PushUniformBuffer(uniforms, sizeof(uniforms)); - g_gpu_device->SetViewportAndScissor(0, 0, width, height); - g_gpu_device->Draw(3, 0); - } - - dst->MakeReadyForSampling(); - return true; -} - bool GPUBackend::DeinterlaceSetTargetSize(u32 width, u32 height, bool preserve) { if (!m_deinterlace_texture || m_deinterlace_texture->GetWidth() != width || diff --git a/src/core/gpu_backend.h b/src/core/gpu_backend.h index 1314ffa9ac..223a44da16 100644 --- a/src/core/gpu_backend.h +++ b/src/core/gpu_backend.h @@ -167,8 +167,7 @@ class GPUBackend /// Sends the current frame to media capture. void SendDisplayToMediaCapture(MediaCapture* cap); - bool Deinterlace(u32 field, u32 line_skip); - bool DeinterlaceExtractField(u32 dst_bufidx, GPUTexture* src, u32 x, u32 y, u32 width, u32 height, u32 line_skip); + bool Deinterlace(u32 field); bool DeinterlaceSetTargetSize(u32 width, u32 height, bool preserve); void DestroyDeinterlaceTextures(); bool ApplyChromaSmoothing(); @@ -185,7 +184,6 @@ class GPUBackend u32 m_current_deinterlace_buffer = 0; std::unique_ptr m_deinterlace_pipeline; - std::unique_ptr m_deinterlace_extract_pipeline; std::array, DEINTERLACE_BUFFER_COUNT> m_deinterlace_buffers; std::unique_ptr m_deinterlace_texture; diff --git a/src/core/gpu_commands.cpp b/src/core/gpu_commands.cpp index 09a50bbd7f..20e8c2aa91 100644 --- a/src/core/gpu_commands.cpp +++ b/src/core/gpu_commands.cpp @@ -724,21 +724,17 @@ bool GPU::HandleRenderLineCommand() cmd->vertices[i].w = 1.0f; } - const GSVector2 v0f = GSVector2::load(&cmd->vertices[0].x); - const GSVector2 v1f = GSVector2::load(&cmd->vertices[1].x); - const GSVector4i rect = - GSVector4i(GSVector4(v0f.min(v1f)).upld(GSVector4(v0f.max(v1f)))).add32(GSVector4i::cxpr(0, 0, 1, 1)); - const GSVector4i clamped_rect = rect.rintersect(m_clamped_drawing_area); - - if (rect.width() > MAX_PRIMITIVE_WIDTH || rect.height() > MAX_PRIMITIVE_HEIGHT || clamped_rect.rempty()) + const GSVector2i v0 = GSVector2i::load(&cmd->vertices[0].native_x); + const GSVector2i v1 = GSVector2i::load(&cmd->vertices[1].native_x); + const GSVector4i rect = GSVector4i::xyxy(v0.min_s32(v1), v0.max_s32(v1)).add32(GSVector4i::cxpr(0, 0, 1, 1)); + if (rect.width() > MAX_PRIMITIVE_WIDTH || rect.height() > MAX_PRIMITIVE_HEIGHT) { - DEBUG_LOG("Culling too-large/off-screen line: {},{} - {},{}", cmd->vertices[0].y, cmd->vertices[0].y, - cmd->vertices[1].x, cmd->vertices[1].y); + DEBUG_LOG("Culling too-large line: {} - {}", v0, v1); EndCommand(); return true; } - AddDrawLineTicks(clamped_rect, rc.shading_enable); + AddDrawLineTicks(rect, rc.shading_enable); GPUBackend::PushCommand(cmd); } else @@ -776,17 +772,14 @@ bool GPU::HandleRenderLineCommand() const GSVector2i v0 = GSVector2i::load(&cmd->vertices[0].x); const GSVector2i v1 = GSVector2i::load(&cmd->vertices[1].x); const GSVector4i rect = GSVector4i::xyxy(v0.min_s32(v1), v0.max_s32(v1)).add32(GSVector4i::cxpr(0, 0, 1, 1)); - const GSVector4i clamped_rect = rect.rintersect(m_clamped_drawing_area); - - if (rect.width() > MAX_PRIMITIVE_WIDTH || rect.height() > MAX_PRIMITIVE_HEIGHT || clamped_rect.rempty()) + if (rect.width() > MAX_PRIMITIVE_WIDTH || rect.height() > MAX_PRIMITIVE_HEIGHT) { - DEBUG_LOG("Culling too-large/off-screen line: {},{} - {},{}", cmd->vertices[0].y, cmd->vertices[0].y, - cmd->vertices[1].x, cmd->vertices[1].y); + DEBUG_LOG("Culling too-large line: {} - {}", v0, v1); EndCommand(); return true; } - AddDrawLineTicks(clamped_rect, rc.shading_enable); + AddDrawLineTicks(rect, rc.shading_enable); GPUBackend::PushCommand(cmd); } @@ -864,13 +857,13 @@ void GPU::FinishPolyline() (shaded ? Truncate32(m_polyline_buffer[buffer_pos++]) : m_render_command.bits) & UINT32_C(0x00FFFFFF); read_vertex(end, color); - const GSVector2 start_pos = GSVector2::load(&start.x); - const GSVector2 end_pos = GSVector2::load(&end.x); + const GSVector2i start_pos = GSVector2i::load(&start.native_x); + const GSVector2i end_pos = GSVector2i::load(&end.native_x); const GSVector4i rect = - GSVector4i(GSVector4::xyxy(start_pos.min(end_pos), start_pos.max(end_pos))).add32(GSVector4i::cxpr(0, 0, 1, 1)); + GSVector4i::xyxy(start_pos.min_s32(end_pos), start_pos.max_s32(end_pos)).add32(GSVector4i::cxpr(0, 0, 1, 1)); if (rect.width() > MAX_PRIMITIVE_WIDTH || rect.height() > MAX_PRIMITIVE_HEIGHT) { - DEBUG_LOG("Culling too-large/off-screen line: {},{} - {},{}", start_pos.x, start_pos.y, end_pos.x, end_pos.y); + DEBUG_LOG("Culling too-large line: {} - {}", start_pos, end_pos); } else { diff --git a/src/core/gpu_hw.cpp b/src/core/gpu_hw.cpp index a6ed0a5679..9e600cb108 100644 --- a/src/core/gpu_hw.cpp +++ b/src/core/gpu_hw.cpp @@ -3843,14 +3843,12 @@ void GPU_HW::UpdateDisplay(const GPUBackendUpdateDisplayCommand* cmd) const bool interlaced = cmd->interlaced_display_enabled; const u32 interlaced_field = BoolToUInt32(cmd->interlaced_display_field); + const u32 line_skip = BoolToUInt32(cmd->interlaced_display_interleaved); const u32 resolution_scale = cmd->display_24bit ? 1 : m_resolution_scale; const u32 scaled_vram_offset_x = cmd->display_vram_left * resolution_scale; - const u32 scaled_vram_offset_y = (cmd->display_vram_top * resolution_scale) + - ((interlaced && cmd->interlaced_display_interleaved) ? interlaced_field : 0); + const u32 scaled_vram_offset_y = cmd->display_vram_top * resolution_scale; const u32 scaled_display_width = cmd->display_vram_width * resolution_scale; const u32 scaled_display_height = cmd->display_vram_height * resolution_scale; - const u32 read_height = interlaced ? (scaled_display_height / 2u) : scaled_display_height; - const u32 line_skip = cmd->interlaced_display_interleaved; bool drew_anything = false; // Don't bother grabbing depth if postfx doesn't need it. @@ -3864,20 +3862,20 @@ void GPU_HW::UpdateDisplay(const GPUBackendUpdateDisplayCommand* cmd) ClearDisplayTexture(); return; } - else if (!cmd->display_24bit && !IsUsingMultisampling() && + else if (!cmd->display_24bit && line_skip == 0 && !IsUsingMultisampling() && (scaled_vram_offset_x + scaled_display_width) <= m_vram_texture->GetWidth() && (scaled_vram_offset_y + scaled_display_height) <= m_vram_texture->GetHeight() && !PostProcessing::InternalChain.IsActive()) { SetDisplayTexture(m_vram_texture.get(), depth_source, scaled_vram_offset_x, scaled_vram_offset_y, - scaled_display_width, read_height); + scaled_display_width, scaled_display_height); // Fast path if no copies are needed. if (interlaced) { GL_INS("Deinterlace fast path"); drew_anything = true; - Deinterlace(interlaced_field, line_skip); + Deinterlace(interlaced_field); } else { @@ -3887,9 +3885,9 @@ void GPU_HW::UpdateDisplay(const GPUBackendUpdateDisplayCommand* cmd) else { if (!m_vram_extract_texture || m_vram_extract_texture->GetWidth() != scaled_display_width || - m_vram_extract_texture->GetHeight() != read_height) + m_vram_extract_texture->GetHeight() != scaled_display_height) { - if (!g_gpu_device->ResizeTexture(&m_vram_extract_texture, scaled_display_width, read_height, + if (!g_gpu_device->ResizeTexture(&m_vram_extract_texture, scaled_display_width, scaled_display_height, GPUTexture::Type::RenderTarget, GPUTexture::Format::RGBA8, GPUTexture::Flags::None)) [[unlikely]] { @@ -3929,8 +3927,8 @@ void GPU_HW::UpdateDisplay(const GPUBackendUpdateDisplayCommand* cmd) GL_INS_FMT("VRAM extract, depth = {}, 24bpp = {}, skip_x = {}, line_skip = {}", depth_source ? "yes" : "no", cmd->display_24bit, skip_x, line_skip); GL_INS_FMT("Source: {},{} => {},{} ({}x{})", reinterpret_start_x, scaled_vram_offset_y, - reinterpret_start_x + scaled_display_width, scaled_vram_offset_y + read_height, scaled_display_width, - read_height); + reinterpret_start_x + scaled_display_width, (scaled_vram_offset_y + scaled_display_height) << line_skip, + scaled_display_width, scaled_display_height); struct ExtractUniforms { @@ -3943,7 +3941,7 @@ void GPU_HW::UpdateDisplay(const GPUBackendUpdateDisplayCommand* cmd) static_cast(line_skip ? 2 : 1)}; g_gpu_device->PushUniformBuffer(&uniforms, sizeof(uniforms)); - g_gpu_device->SetViewportAndScissor(0, 0, scaled_display_width, read_height); + g_gpu_device->SetViewportAndScissor(0, 0, scaled_display_width, scaled_display_height); g_gpu_device->Draw(3, 0); m_vram_extract_texture->MakeReadyForSampling(); @@ -3957,19 +3955,19 @@ void GPU_HW::UpdateDisplay(const GPUBackendUpdateDisplayCommand* cmd) drew_anything = true; SetDisplayTexture(m_vram_extract_texture.get(), depth_source ? m_vram_extract_depth_texture.get() : nullptr, 0, 0, - scaled_display_width, read_height); + scaled_display_width, scaled_display_height); if (g_settings.display_24bit_chroma_smoothing) { if (ApplyChromaSmoothing()) { if (interlaced) - Deinterlace(interlaced_field, 0); + Deinterlace(interlaced_field); } } else { if (interlaced) - Deinterlace(interlaced_field, 0); + Deinterlace(interlaced_field); } } diff --git a/src/core/gpu_hw_shadergen.cpp b/src/core/gpu_hw_shadergen.cpp index b3fd5fdd75..83fe2dd10c 100644 --- a/src/core/gpu_hw_shadergen.cpp +++ b/src/core/gpu_hw_shadergen.cpp @@ -1266,7 +1266,9 @@ float3 SampleVRAM24(uint2 icoords) DeclareFragmentEntryPoint(ss, 0, 1, {}, true, depth_buffer ? 2 : 1); ss << R"( { - uint2 icoords = uint2(v_pos.x + u_skip_x, v_pos.y * u_line_skip); + // Have to floor because SV_Position is at the pixel center. + float2 v_pos_floored = floor(v_pos.xy); + uint2 icoords = uint2(v_pos_floored.x + u_skip_x, v_pos_floored.y * u_line_skip); int2 wrapped_coords = int2((icoords + u_vram_offset) % VRAM_SIZE); #if COLOR_24BIT diff --git a/src/core/gpu_shadergen.cpp b/src/core/gpu_shadergen.cpp index 6f43c3ee45..4e275d4ab0 100644 --- a/src/core/gpu_shadergen.cpp +++ b/src/core/gpu_shadergen.cpp @@ -102,29 +102,11 @@ std::string GPUShaderGen::GenerateDisplaySharpBilinearFragmentShader() const return ss.str(); } -std::string GPUShaderGen::GenerateInterleavedFieldExtractFragmentShader() const -{ - std::stringstream ss; - WriteHeader(ss); - DeclareUniformBuffer(ss, {"uint2 u_src_offset", "uint u_line_skip"}, true); - DeclareTexture(ss, "samp0", 0, false); - - DeclareFragmentEntryPoint(ss, 0, 1, {}, true); - ss << R"( -{ - uint2 tcoord = u_src_offset + uint2(uint(v_pos.x), uint(v_pos.y) << u_line_skip); - o_col0 = LOAD_TEXTURE(samp0, int2(tcoord), 0); -} -)"; - - return ss.str(); -} - std::string GPUShaderGen::GenerateDeinterlaceWeaveFragmentShader() const { std::stringstream ss; WriteHeader(ss); - DeclareUniformBuffer(ss, {"uint2 u_src_offset", "uint u_render_field", "uint u_line_skip"}, true); + DeclareUniformBuffer(ss, {"uint2 u_src_offset", "uint u_render_field"}, true); DeclareTexture(ss, "samp0", 0, false); DeclareFragmentEntryPoint(ss, 0, 1, {}, true); @@ -134,7 +116,7 @@ std::string GPUShaderGen::GenerateDeinterlaceWeaveFragmentShader() const if ((fcoord.y & 1) != u_render_field) discard; - uint2 tcoord = u_src_offset + uint2(fcoord.x, (fcoord.y / 2u) << u_line_skip); + uint2 tcoord = u_src_offset + uint2(fcoord.x, (fcoord.y / 2u)); o_col0 = LOAD_TEXTURE(samp0, int2(tcoord), 0); })"; diff --git a/src/core/gpu_shadergen.h b/src/core/gpu_shadergen.h index 5a912ed688..cb742f75f6 100644 --- a/src/core/gpu_shadergen.h +++ b/src/core/gpu_shadergen.h @@ -15,7 +15,6 @@ class GPUShaderGen : public ShaderGen std::string GenerateDisplayFragmentShader(bool clamp_uv, bool nearest) const; std::string GenerateDisplaySharpBilinearFragmentShader() const; - std::string GenerateInterleavedFieldExtractFragmentShader() const; std::string GenerateDeinterlaceWeaveFragmentShader() const; std::string GenerateDeinterlaceBlendFragmentShader() const; std::string GenerateFastMADReconstructFragmentShader() const; diff --git a/src/core/gpu_sw.cpp b/src/core/gpu_sw.cpp index 67ea20b67a..86e7840eb5 100644 --- a/src/core/gpu_sw.cpp +++ b/src/core/gpu_sw.cpp @@ -393,36 +393,37 @@ void GPU_SW::UpdateDisplay(const GPUBackendUpdateDisplayCommand* cmd) } const bool is_24bit = cmd->display_24bit; - const bool interlaced = cmd->interlaced_display_enabled; const u32 field = BoolToUInt32(cmd->interlaced_display_field); - const u32 vram_offset_x = is_24bit ? cmd->X : cmd->display_vram_left; - const u32 vram_offset_y = cmd->display_vram_top + ((interlaced && cmd->interlaced_display_interleaved) ? field : 0); + const u32 line_skip = BoolToUInt32(cmd->interlaced_display_interleaved); + const u32 src_x = is_24bit ? cmd->X : cmd->display_vram_left; const u32 skip_x = is_24bit ? (cmd->display_vram_left - cmd->X) : 0; - const u32 read_width = cmd->display_vram_width; - const u32 read_height = interlaced ? (cmd->display_vram_height / 2) : cmd->display_vram_height; + const u32 src_y = cmd->display_vram_top; + const u32 width = cmd->display_vram_width; + const u32 height = cmd->display_vram_height; + + GL_INS_FMT("Software scanout {}x{} from {},{} line_skip={}", width, height, src_x, src_y, line_skip); if (cmd->interlaced_display_enabled) { - const u32 line_skip = cmd->interlaced_display_interleaved; - if (CopyOut(vram_offset_x, vram_offset_y, skip_x, read_width, read_height, line_skip, is_24bit)) + if (CopyOut(src_x, src_y, skip_x, width, height, line_skip, is_24bit)) { - SetDisplayTexture(m_upload_texture.get(), nullptr, 0, 0, read_width, read_height); + SetDisplayTexture(m_upload_texture.get(), nullptr, 0, 0, width, height); if (is_24bit && g_settings.display_24bit_chroma_smoothing) { if (ApplyChromaSmoothing()) - Deinterlace(field, 0); + Deinterlace(field); } else { - Deinterlace(field, 0); + Deinterlace(field); } } } else { - if (CopyOut(vram_offset_x, vram_offset_y, skip_x, read_width, read_height, 0, is_24bit)) + if (CopyOut(src_x, src_y, skip_x, width, height, 0, is_24bit)) { - SetDisplayTexture(m_upload_texture.get(), nullptr, 0, 0, read_width, read_height); + SetDisplayTexture(m_upload_texture.get(), nullptr, 0, 0, width, height); if (is_24bit && g_settings.display_24bit_chroma_smoothing) ApplyChromaSmoothing(); } diff --git a/src/core/gpu_sw_rasterizer.inl b/src/core/gpu_sw_rasterizer.inl index 21017d9572..9ad5ff02cb 100644 --- a/src/core/gpu_sw_rasterizer.inl +++ b/src/core/gpu_sw_rasterizer.inl @@ -1399,13 +1399,8 @@ static void DrawTriangle(const GPUBackendDrawCommand* RESTRICT cmd, tl = tl >> 1; // Invalid size early culling. - if (static_cast(std::abs(v2->x - v0->x)) >= MAX_PRIMITIVE_WIDTH || - static_cast(std::abs(v2->x - v1->x)) >= MAX_PRIMITIVE_WIDTH || - static_cast(std::abs(v1->x - v0->x)) >= MAX_PRIMITIVE_WIDTH || - static_cast(v2->y - v0->y) >= MAX_PRIMITIVE_HEIGHT || v0->y == v2->y) - { + if (v0->y == v2->y) return; - } // Same as line rasterization, use higher precision for position. static constexpr auto makefp_xy = [](s32 x) { return (static_cast(x) << 32) + ((1LL << 32) - (1 << 11)); }; diff --git a/src/core/shader_cache_version.h b/src/core/shader_cache_version.h index dd969ce797..1719cb0470 100644 --- a/src/core/shader_cache_version.h +++ b/src/core/shader_cache_version.h @@ -5,4 +5,4 @@ #include "common/types.h" -static constexpr u32 SHADER_CACHE_VERSION = 24; +static constexpr u32 SHADER_CACHE_VERSION = 25; diff --git a/src/core/system.cpp b/src/core/system.cpp index 0047f6f0e4..5a4dc733d3 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -382,7 +382,6 @@ bool System::PerformEarlyHardwareChecks(Error* error) Error::SetStringFmt( error, "Page size mismatch. This build was compiled with {} byte pages, but the system has {} byte pages.", HOST_PAGE_SIZE, runtime_host_page_size); - CPUThreadShutdown(); return false; } #else @@ -390,7 +389,6 @@ bool System::PerformEarlyHardwareChecks(Error* error) { Error::SetStringFmt(error, "Page size of {} bytes is out of the range supported by this build: {}-{}.", HOST_PAGE_SIZE, MIN_HOST_PAGE_SIZE, MAX_HOST_PAGE_SIZE); - CPUThreadShutdown(); return false; } #endif @@ -451,7 +449,7 @@ void System::LogStartupInformation() INFO_LOG("Version: {} [{}]", g_scm_tag_str, g_scm_branch_str); INFO_LOG("SCM Timestamp: {}", g_scm_date_str); INFO_LOG("Build Timestamp: {} {}", __DATE__, __TIME__); - if (const cpuinfo_package* package = cpuinfo_get_package(0)) [[likely]] + if (const cpuinfo_package* package = cpuinfo_initialize() ? cpuinfo_get_package(0) : nullptr) [[likely]] { INFO_LOG("Host CPU: {}", package->name); INFO_LOG("CPU has {} logical processor(s) and {} core(s) across {} cluster(s).", package->processor_count, @@ -777,9 +775,9 @@ System::BootMode System::GetBootMode() return s_state.boot_mode; } -const BIOS::ImageInfo* System::GetBIOSImageInfo() +bool System::IsUsingPS2BIOS() { - return s_state.bios_image_info; + return (s_state.bios_image_info && s_state.bios_image_info->fastboot_patch == BIOS::ImageInfo::FastBootPatch::Type2); } bool System::IsExePath(std::string_view path) @@ -2730,7 +2728,7 @@ void System::InternalReset() CPU::Reset(); CPU::CodeCache::Reset(); if (g_settings.gpu_pgxp_enable) - CPU::PGXP::Initialize(); + CPU::PGXP::Reset(); Bus::Reset(); PIO::Reset(); diff --git a/src/core/system.h b/src/core/system.h index e41a78f82f..33d7b02a53 100644 --- a/src/core/system.h +++ b/src/core/system.h @@ -31,10 +31,6 @@ class GPUTexture; class INISettingsInterface; class MediaCapture; -namespace BIOS { -struct ImageInfo; -} // namespace BIOS - namespace GameDatabase { struct Entry; } @@ -225,13 +221,12 @@ const std::string& GetExeOverride(); const GameDatabase::Entry* GetGameDatabaseEntry(); GameHash GetGameHash(); bool IsRunningUnknownGame(); +bool IsUsingPS2BIOS(); BootMode GetBootMode(); /// Returns the time elapsed in the current play session. u64 GetSessionPlayedTime(); -const BIOS::ImageInfo* GetBIOSImageInfo(); - void FormatLatencyStats(SmallStringBase& str); /// Loads global settings (i.e. EmuConfig). diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index e3cefb0205..640b701a1f 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -264,7 +264,6 @@ elseif(NOT ANDROID) find_package(PkgConfig REQUIRED) pkg_check_modules(DBUS REQUIRED dbus-1) target_include_directories(util PRIVATE ${DBUS_INCLUDE_DIRS}) - target_link_libraries(util PRIVATE ${DBUS_LINK_LIBRARIES}) if(LINUX) target_link_libraries(util PRIVATE UDEV::UDEV) diff --git a/src/util/platform_misc_unix.cpp b/src/util/platform_misc_unix.cpp index e2350989bd..0e64f0d82d 100644 --- a/src/util/platform_misc_unix.cpp +++ b/src/util/platform_misc_unix.cpp @@ -4,6 +4,7 @@ #include "input_manager.h" #include "platform_misc.h" +#include "common/dynamic_library.h" #include "common/error.h" #include "common/log.h" #include "common/path.h" @@ -12,6 +13,7 @@ #include #include +#include #include #include #include @@ -32,75 +34,135 @@ bool PlatformMisc::InitializeSocketSupport(Error* error) static bool SetScreensaverInhibitDBus(const bool inhibit_requested, const char* program_name, const char* reason) { +#define DBUS_FUNCS(X) \ + X(dbus_error_is_set) \ + X(dbus_error_free) \ + X(dbus_message_unref) \ + X(dbus_error_init) \ + X(dbus_bus_get) \ + X(dbus_connection_set_exit_on_disconnect) \ + X(dbus_message_new_method_call) \ + X(dbus_message_iter_init_append) \ + X(dbus_message_iter_append_basic) \ + X(dbus_connection_send_with_reply_and_block) \ + X(dbus_message_get_args) + + static std::mutex s_screensaver_inhibit_dbus_mutex; + static DynamicLibrary s_dbus_library; + static bool s_dbus_library_loaded; static dbus_uint32_t s_cookie; + static DBusConnection* s_comparison_connection; + +#define DEFINE_FUNC(F) static decltype(&::F) x##F; + DBUS_FUNCS(DEFINE_FUNC) +#undef DEFINE_FUNC + const char* bus_method = (inhibit_requested) ? "Inhibit" : "UnInhibit"; DBusError error; DBusConnection* connection = nullptr; - static DBusConnection* s_comparison_connection; DBusMessage* message = nullptr; DBusMessage* response = nullptr; DBusMessageIter message_itr; + std::unique_lock lock(s_screensaver_inhibit_dbus_mutex); + if (!s_dbus_library_loaded) + { + Error lerror; + s_dbus_library_loaded = true; + + if (!s_dbus_library.Open(DynamicLibrary::GetVersionedFilename("dbus", 1).c_str(), &lerror)) + { + ERROR_LOG("Failed to open libdbus: {}", lerror.GetDescription()); + return false; + } + +#define LOAD_FUNC(F) \ + if (!s_dbus_library.GetSymbol(#F, &x##F)) \ + { \ + ERROR_LOG("Failed to find function {}", #F); \ + s_dbus_library.Close(); \ + return false; \ + } + DBUS_FUNCS(LOAD_FUNC) +#undef LOAD_FUNC + } + + if (!s_dbus_library.IsOpen()) + return false; + ScopedGuard cleanup = [&]() { - if (dbus_error_is_set(&error)) + if (xdbus_error_is_set(&error)) { ERROR_LOG("SetScreensaverInhibitDBus error: {}", error.message); - dbus_error_free(&error); + xdbus_error_free(&error); } if (message) - dbus_message_unref(message); + xdbus_message_unref(message); if (response) - dbus_message_unref(response); + xdbus_message_unref(response); }; - dbus_error_init(&error); + xdbus_error_init(&error); + // Calling dbus_bus_get() after the first time returns a pointer to the existing connection. - connection = dbus_bus_get(DBUS_BUS_SESSION, &error); - if (!connection || (dbus_error_is_set(&error))) + connection = xdbus_bus_get(DBUS_BUS_SESSION, &error); + if (!connection || (xdbus_error_is_set(&error))) return false; + if (s_comparison_connection != connection) { - dbus_connection_set_exit_on_disconnect(connection, false); + xdbus_connection_set_exit_on_disconnect(connection, false); s_cookie = 0; s_comparison_connection = connection; } - message = dbus_message_new_method_call("org.freedesktop.ScreenSaver", "/org/freedesktop/ScreenSaver", - "org.freedesktop.ScreenSaver", bus_method); + + message = xdbus_message_new_method_call("org.freedesktop.ScreenSaver", "/org/freedesktop/ScreenSaver", + "org.freedesktop.ScreenSaver", bus_method); if (!message) return false; + // Initialize an append iterator for the message, gets freed with the message. - dbus_message_iter_init_append(message, &message_itr); + xdbus_message_iter_init_append(message, &message_itr); if (inhibit_requested) { // Guard against repeat inhibitions which would add extra inhibitors each generating a different cookie. if (s_cookie) return false; + // Append process/window name. - if (!dbus_message_iter_append_basic(&message_itr, DBUS_TYPE_STRING, &program_name)) + if (!xdbus_message_iter_append_basic(&message_itr, DBUS_TYPE_STRING, &program_name)) return false; + // Append reason for inhibiting the screensaver. - if (!dbus_message_iter_append_basic(&message_itr, DBUS_TYPE_STRING, &reason)) + if (!xdbus_message_iter_append_basic(&message_itr, DBUS_TYPE_STRING, &reason)) return false; } else { // Only Append the cookie. - if (!dbus_message_iter_append_basic(&message_itr, DBUS_TYPE_UINT32, &s_cookie)) + if (!xdbus_message_iter_append_basic(&message_itr, DBUS_TYPE_UINT32, &s_cookie)) return false; } + // Send message and get response. - response = dbus_connection_send_with_reply_and_block(connection, message, DBUS_TIMEOUT_USE_DEFAULT, &error); - if (!response || dbus_error_is_set(&error)) + response = xdbus_connection_send_with_reply_and_block(connection, message, DBUS_TIMEOUT_USE_DEFAULT, &error); + if (!response || xdbus_error_is_set(&error)) return false; + s_cookie = 0; if (inhibit_requested) { // Get the cookie from the response message. - if (!dbus_message_get_args(response, &error, DBUS_TYPE_UINT32, &s_cookie, DBUS_TYPE_INVALID) || - dbus_error_is_set(&error)) + if (!xdbus_message_get_args(response, &error, DBUS_TYPE_UINT32, &s_cookie, DBUS_TYPE_INVALID) || + xdbus_error_is_set(&error)) + { return false; + } } + return true; + +#undef DBUS_FUNCS } static bool SetScreensaverInhibit(bool inhibit) diff --git a/src/util/vulkan_device.cpp b/src/util/vulkan_device.cpp index b10eaaab80..a462cf4968 100644 --- a/src/util/vulkan_device.cpp +++ b/src/util/vulkan_device.cpp @@ -1995,7 +1995,10 @@ bool VulkanDevice::CreateDeviceAndMainSwapChain(std::string_view adapter, Featur swap_chain = std::make_unique(wi, vsync_mode, allow_present_throttle, exclusive_fullscreen_control); if (!swap_chain->CreateSurface(m_instance, m_physical_device, error)) + { + swap_chain->Destroy(*this, false); return false; + } } // Attempt to create the device. diff --git a/src/util/vulkan_loader.cpp b/src/util/vulkan_loader.cpp index a59c90ce1e..fa5e0f2236 100644 --- a/src/util/vulkan_loader.cpp +++ b/src/util/vulkan_loader.cpp @@ -8,6 +8,7 @@ #include "common/assert.h" #include "common/dynamic_library.h" +#include "common/error.h" #include "common/log.h" #include @@ -73,7 +74,7 @@ bool Vulkan::LoadVulkanLibrary(Error* error) bool required_functions_missing = false; #define VULKAN_MODULE_ENTRY_POINT(name, required) \ - if (!s_vulkan_library.GetSymbol(#name, &name)) \ + if (!s_vulkan_library.GetSymbol(#name, &name) && required) \ { \ ERROR_LOG("Vulkan: Failed to load required module function {}", #name); \ required_functions_missing = true; \ @@ -83,6 +84,7 @@ bool Vulkan::LoadVulkanLibrary(Error* error) if (required_functions_missing) { + Error::SetStringView(error, "One or more required functions are missing. The log contains more information."); ResetVulkanLibraryFunctionPointers(); s_vulkan_library.Close(); return false;