mirror of
https://github.com/flyinghead/flycast.git
synced 2026-11-08 01:48:31 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8108e63907 | ||
|
|
b0403ed103 | ||
|
|
c86ed291cf | ||
|
|
d981c48a75 | ||
|
|
82925c4e81 | ||
|
|
54e8589f63 | ||
|
|
4750ddb646 | ||
|
|
894ac54b80 | ||
|
|
d4a223656f | ||
|
|
b698cc3d10 | ||
|
|
75af269bb8 | ||
|
|
fd475bdd8e | ||
|
|
f71dd6d4d8 | ||
|
|
5aec6d05b0 | ||
|
|
b4937ef271 | ||
|
|
a50164a91b | ||
|
|
595842f606 | ||
|
|
8f74f7883a | ||
|
|
71dc4c3e39 | ||
|
|
6edad8eaba | ||
|
|
ea1cd9d31b | ||
|
|
bb7ad07701 | ||
|
|
9793e54646 | ||
|
|
6d2ed417be | ||
|
|
d3916f13d7 | ||
|
|
b2c6159b10 | ||
|
|
4d73cc8e13 | ||
|
|
5b343562b9 | ||
|
|
b20db6a8bb | ||
|
|
a8bc1c12f4 | ||
|
|
b94acb70a6 | ||
|
|
0c3f5c7a76 | ||
|
|
03d20f9d5a | ||
|
|
01e5cd882c | ||
|
|
556e2ead56 | ||
|
|
ff6a3119b0 | ||
|
|
86657c0c12 | ||
|
|
d7c168f406 | ||
|
|
f37e43b26f | ||
|
|
e427c4d5c4 | ||
|
|
56fe3b9c9d | ||
|
|
30bb864b37 | ||
|
|
d689c50e21 | ||
|
|
efd71b3a44 | ||
|
|
5450646b5c | ||
|
|
55e0211a57 | ||
|
|
5d719e0b49 | ||
|
|
e10a9929da | ||
|
|
3d41e4ea3e | ||
|
|
2dd4cbe33d | ||
|
|
2653c5ed73 | ||
|
|
343021a8c5 | ||
|
|
7907100b9c | ||
|
|
f179c00bdd | ||
|
|
c83b0e325f | ||
|
|
210e8ca6e3 | ||
|
|
a46dc71156 | ||
|
|
4baf529ff7 | ||
|
|
63ef3aaab4 | ||
|
|
abe71409b5 | ||
|
|
f8c12d2835 | ||
|
|
92555c68c6 | ||
|
|
915532f946 | ||
|
|
079180faab | ||
|
|
38de2868a0 | ||
|
|
0e97f97bb7 | ||
|
|
64d7199107 | ||
|
|
0077b8ecec | ||
|
|
5393235c40 | ||
|
|
a81a02f14b | ||
|
|
480f93e50d | ||
|
|
7174ef1194 | ||
|
|
f039a809da | ||
|
|
1223154665 | ||
|
|
8cb847940b | ||
|
|
9691fe9622 | ||
|
|
c575eac89b | ||
|
|
b468c9bdae | ||
|
|
368bd17ae0 | ||
|
|
757732d0f8 | ||
|
|
0ff9caf656 | ||
|
|
a93bd9e64e | ||
|
|
308d9fc1ac | ||
|
|
7a27a43ba4 | ||
|
|
244bb724c9 | ||
|
|
349083030d | ||
|
|
b99b3450c8 | ||
|
|
6061d402bd | ||
|
|
4d06743d9a | ||
|
|
af7353225b | ||
|
|
cb0b667129 | ||
|
|
00f60a3d62 | ||
|
|
5565b79b9e | ||
|
|
ecb468a723 | ||
|
|
fbe7cc8fe3 | ||
|
|
45bf218df4 | ||
|
|
6b3e86619e | ||
|
|
56b8bb1245 | ||
|
|
cea80560db | ||
|
|
088c1e6b0d | ||
|
|
bb048dea8f | ||
|
|
588dca2bfd | ||
|
|
3afa4322f1 | ||
|
|
0b6b8b3753 | ||
|
|
53d092c326 | ||
|
|
bfba850ee7 | ||
|
|
db5a4d1247 | ||
|
|
3ad00596a1 | ||
|
|
39e04b07b8 | ||
|
|
b9cbf3e47f | ||
|
|
2a4356e72c | ||
|
|
136ce267dc | ||
|
|
c6f50fad9d | ||
|
|
916afeb5ef | ||
|
|
81842ccc23 | ||
|
|
4fdd28fab7 |
No files matched your search
@@ -12,7 +12,7 @@ assignees: ''
|
||||
|
||||
Platform / OS / Hardware:
|
||||
|
||||
Github hash:
|
||||
Flycast version:
|
||||
|
||||
Hardware:
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '17'
|
||||
@@ -28,6 +28,10 @@ jobs:
|
||||
path: ~/.ccache
|
||||
key: android-ccache-${{ github.sha }}
|
||||
restore-keys: android-ccache-
|
||||
|
||||
- name: Disable release signing
|
||||
run: sed -i 's/signingConfig signingConfigs.release/signingConfig signingConfigs.debug/g' shell/android-studio/flycast/build.gradle
|
||||
if: github.repository != 'flyinghead/flycast' || github.event_name != 'push'
|
||||
|
||||
- name: Bump version code
|
||||
uses: chkfung/[email protected]
|
||||
@@ -37,19 +41,15 @@ jobs:
|
||||
|
||||
- name: Gradle
|
||||
working-directory: shell/android-studio
|
||||
run: ./gradlew bundleRelease --parallel
|
||||
run: ./gradlew assembleRelease bundleRelease --parallel
|
||||
env:
|
||||
SENTRY_UPLOAD_URL: ${{ secrets.SENTRY_UPLOAD_URL }}
|
||||
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
|
||||
|
||||
- name: Upload to Google Play
|
||||
uses: r0adkll/[email protected]
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
serviceAccountJsonPlainText: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }}
|
||||
packageName: com.flycast.emulator
|
||||
releaseFiles: shell/android-studio/flycast/build/outputs/bundle/release/flycast-release.aab
|
||||
track: internal
|
||||
status: draft
|
||||
name: flycast-release.apk
|
||||
path: shell/android-studio/flycast/build/outputs/apk/release/flycast-release.apk
|
||||
|
||||
- name: Build dump_syms
|
||||
working-directory: core/deps/breakpad
|
||||
@@ -69,6 +69,21 @@ jobs:
|
||||
mv libflycast.so.sym symbols/libflycast.so/$BUILD_ID
|
||||
done
|
||||
|
||||
- name: Configure AWS Credentials
|
||||
id: aws-credentials
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
with:
|
||||
aws-access-key-id: AKIAJOZQS4H2PHQWYFCA
|
||||
aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
|
||||
aws-region: us-east-2
|
||||
if: github.repository == 'flyinghead/flycast' && github.event_name == 'push'
|
||||
|
||||
- name: Upload to S3
|
||||
run: |
|
||||
cp shell/android-studio/flycast/build/outputs/bundle/release/*.aab shell/android-studio/flycast/build/outputs/apk/release/
|
||||
aws s3 sync shell/android-studio/flycast/build/outputs/apk/release s3://flycast-builds/android/${GITHUB_REF#refs/}-$GITHUB_SHA --acl public-read --exclude='*.json' --follow-symlinks
|
||||
if: ${{ steps.aws-credentials.outputs.aws-account-id != '' }}
|
||||
|
||||
- name: Setup Sentry CLI
|
||||
uses: mathieu-bour/setup-sentry-cli@v2
|
||||
env:
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
name: BSD CI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
env:
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
operating_system: [ freebsd, netbsd, openbsd ]
|
||||
architecture: [ arm64, x86-64 ]
|
||||
include:
|
||||
- operating_system: freebsd
|
||||
version: '14.1'
|
||||
pkginstall: sudo pkg install -y alsa-lib ccache cmake evdev-proto git libao libevdev libudev-devd libzip lua54 miniupnpc ninja pkgconf pulseaudio sdl2
|
||||
- operating_system: netbsd
|
||||
version: '10.0'
|
||||
pkginstall: sudo pkgin update && sudo pkgin -y install alsa-lib ccache cmake gcc12 git libao libzip lua54 miniupnpc ninja-build pkgconf pulseaudio SDL2 && export PATH=/usr/pkg/gcc12/bin:$PATH
|
||||
- operating_system: openbsd
|
||||
version: '7.5'
|
||||
pkginstall: sudo pkg_add ccache cmake git libao libzip miniupnpc ninja pkgconf pulseaudio sdl2
|
||||
exclude:
|
||||
- architecture: arm64
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: true
|
||||
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.CCACHE_DIR }}
|
||||
key: ccache-${{ matrix.operating_system }}-${{ matrix.architecture }}-${{ github.sha }}
|
||||
restore-keys: ccache-${{ matrix.operating_system }}-${{ matrix.architecture }}-
|
||||
|
||||
- uses: cross-platform-actions/[email protected]
|
||||
with:
|
||||
operating_system: ${{ matrix.operating_system }}
|
||||
architecture: ${{ matrix.architecture }}
|
||||
version: ${{ matrix.version }}
|
||||
environment_variables: CCACHE_DIR
|
||||
run: |
|
||||
${{ matrix.pkginstall }}
|
||||
cmake -B build -DCMAKE_BUILD_TYPE=Release -G Ninja
|
||||
cmake --build build --config Release
|
||||
@@ -0,0 +1,182 @@
|
||||
name: C/C++ CI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: ${{ matrix.config.name }}
|
||||
runs-on: ${{ matrix.config.os }}
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: ${{ matrix.config.shell }}
|
||||
|
||||
env:
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- {name: i686-pc-windows-msvc, os: windows-latest, shell: cmd, arch: x86, cmakeArgs: -G Ninja, buildType: Release}
|
||||
- {name: apple-darwin, os: macos-latest, shell: sh, cmakeArgs: -G Xcode -DUSE_DISCORD=ON, destDir: osx, buildType: RelWithDebInfo}
|
||||
- {name: apple-ios, os: macos-latest, shell: sh, cmakeArgs: -DCMAKE_SYSTEM_NAME=iOS -G Xcode, destDir: ios, buildType: Release}
|
||||
- {name: x86_64-pc-linux-gnu, os: ubuntu-20.04, shell: sh, cmakeArgs: -G Ninja -DUSE_DISCORD=ON, destDir: linux, buildType: RelWithDebInfo}
|
||||
- {name: x86_64-pc-windows-msvc, os: windows-latest, shell: cmd, arch: x64, cmakeArgs: -G Ninja -DUSE_DISCORD=ON, buildType: Release}
|
||||
- {name: x86_64-w64-mingw32, os: windows-latest, shell: 'msys2 {0}', cmakeArgs: -G Ninja -DUSE_DISCORD=ON, destDir: win, buildType: RelWithDebInfo}
|
||||
- {name: libretro-x86_64-pc-linux-gnu, os: ubuntu-latest, shell: sh, cmakeArgs: -DLIBRETRO=ON -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE -G Ninja, buildType: Release}
|
||||
- {name: libretro-x86_64-w64-mingw32, os: windows-latest, shell: 'msys2 {0}', cmakeArgs: -DLIBRETRO=ON -G Ninja, buildType: Release}
|
||||
|
||||
steps:
|
||||
- name: Set up build environment (macOS)
|
||||
run: |
|
||||
# Unlink and re-link to prevent errors when github mac runner images install python outside of brew. See https://github.com/actions/setup-python/issues/577
|
||||
brew list -1 | grep python | while read formula; do brew unlink $formula; brew link --overwrite $formula; done
|
||||
brew update || :
|
||||
brew install libao ldid ninja pulseaudio
|
||||
brew uninstall --ignore-dependencies zstd
|
||||
VULKAN_VER=1.3.261.1 && aria2c https://sdk.lunarg.com/sdk/download/$VULKAN_VER/mac/vulkansdk-macos-$VULKAN_VER.dmg
|
||||
hdiutil attach ./vulkansdk-macos-*.dmg -mountpoint /Volumes/VulkanSDK
|
||||
sudo /Volumes/VulkanSDK/InstallVulkan.app/Contents/MacOS/InstallVulkan --root $HOME/VulkanSDK --accept-licenses --default-answer --confirm-command install
|
||||
hdiutil detach /Volumes/VulkanSDK
|
||||
echo "VULKAN_SDK=$HOME/VulkanSDK/macOS" >> $GITHUB_ENV
|
||||
if: runner.os == 'macOS'
|
||||
|
||||
- name: Set up build environment (Linux)
|
||||
run: |
|
||||
sudo add-apt-repository ppa:christianrauch/libdecoration
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install ccache libao-dev libasound2-dev libevdev-dev libgl1-mesa-dev liblua5.3-dev libminiupnpc-dev libpulse-dev libsdl2-dev libudev-dev libzip-dev ninja-build libcurl4-openssl-dev
|
||||
sudo apt-get -y install libwayland-dev libdecor-0-dev libaudio-dev libjack-dev libsndio-dev libsamplerate0-dev libx11-dev libxext-dev libxrandr-dev libxcursor-dev libxfixes-dev libxi-dev libxss-dev libxkbcommon-dev libdrm-dev libgbm-dev libgles2-mesa-dev libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev fcitx-libs-dev
|
||||
if: runner.os == 'Linux'
|
||||
|
||||
- name: Set up build environment (Windows, MinGW)
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: MINGW64
|
||||
install: git make mingw-w64-x86_64-ccache mingw-w64-x86_64-cmake mingw-w64-x86_64-lua mingw-w64-x86_64-ninja mingw-w64-x86_64-SDL2 mingw-w64-x86_64-toolchain
|
||||
if: matrix.config.shell == 'msys2 {0}'
|
||||
|
||||
- name: Set up build environment (Windows, Visual Studio)
|
||||
run: |
|
||||
choco install ccache directx-sdk ninja
|
||||
echo DXSDK_DIR=C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)>> %GITHUB_ENV%
|
||||
if: matrix.config.shell == 'cmd'
|
||||
|
||||
- uses: ilammy/msvc-dev-cmd@v1
|
||||
with:
|
||||
arch: ${{ matrix.config.arch }}
|
||||
if: matrix.config.shell == 'cmd'
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
- name: Compile a universal OpenMP (macOS)
|
||||
run: HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew reinstall --build-from-source --formula ./shell/apple/libomp.rb
|
||||
if: matrix.config.name == 'apple-darwin'
|
||||
|
||||
- name: Compile OpenMP.xcframework (iOS)
|
||||
run: ./shell/apple/emulator-ios/OpenMP/build_ios_openmp.sh --disableSimulator
|
||||
if: matrix.config.name == 'apple-ios'
|
||||
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.CCACHE_DIR }}
|
||||
key: ccache-${{ matrix.config.name }}-${{ github.sha }}
|
||||
restore-keys: ccache-${{ matrix.config.name }}-
|
||||
if: runner.os != 'macOS'
|
||||
|
||||
- name: CMake
|
||||
run: |
|
||||
cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.config.buildType }} -DCMAKE_INSTALL_PREFIX=artifact ${{ matrix.config.cmakeArgs }} -DSENTRY_UPLOAD_URL=${{ env.SENTRY_UPLOAD_URL }}
|
||||
cmake --build build --config ${{ matrix.config.buildType }} --target install
|
||||
env:
|
||||
SENTRY_UPLOAD_URL: ${{ secrets.SENTRY_UPLOAD_URL }}
|
||||
|
||||
- name: Unit Tests
|
||||
run: |
|
||||
cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.config.buildType }} -DCMAKE_INSTALL_PREFIX=artifact -DENABLE_CTEST=ON ${{ matrix.config.cmakeArgs }}
|
||||
cmake --build build --config ${{ matrix.config.buildType }}
|
||||
./build/flycast
|
||||
if: matrix.config.name == 'x86_64-pc-linux-gnu'
|
||||
|
||||
- name: Dump symbols
|
||||
run: |
|
||||
mkdir -p symbols
|
||||
core/deps/breakpad/bin/dump_syms artifact/bin/flycast.exe > symbols/flycast.sym 2>/dev/null
|
||||
strip artifact/bin/flycast.exe
|
||||
if: matrix.config.name == 'x86_64-w64-mingw32'
|
||||
|
||||
- name: Build breakpad (linux)
|
||||
working-directory: core/deps/breakpad
|
||||
run: |
|
||||
./configure
|
||||
make
|
||||
sudo make install
|
||||
if: matrix.config.name == 'x86_64-pc-linux-gnu'
|
||||
|
||||
- name: Dump symbols and make AppImage (linux)
|
||||
run: |
|
||||
mkdir -p symbols
|
||||
dump_syms artifact/bin/flycast > symbols/flycast.sym 2>/dev/null
|
||||
strip artifact/bin/flycast
|
||||
shell/linux/make-appimage.sh . artifact/bin appimage
|
||||
mv flycast-x86_64.AppImage artifact/bin
|
||||
rm artifact/bin/flycast
|
||||
if: matrix.config.name == 'x86_64-pc-linux-gnu'
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: flycast-${{ matrix.config.name }}
|
||||
path: artifact/bin
|
||||
|
||||
- name: Package app (macos)
|
||||
run: |
|
||||
cd artifact/bin
|
||||
zip -rm flycast.app.zip Flycast.app
|
||||
if: matrix.config.name == 'apple-darwin'
|
||||
|
||||
- name: Package app (windows)
|
||||
run: |
|
||||
powershell Compress-Archive artifact/bin/flycast.exe artifact/bin/flycast.zip
|
||||
rm artifact/bin/flycast.exe
|
||||
if: matrix.config.name == 'x86_64-w64-mingw32'
|
||||
|
||||
- name: Configure AWS Credentials
|
||||
id: aws-credentials
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
with:
|
||||
aws-access-key-id: AKIAJOZQS4H2PHQWYFCA
|
||||
aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
|
||||
aws-region: us-east-2
|
||||
if: github.repository == 'flyinghead/flycast' && github.event_name == 'push' && matrix.config.destDir != ''
|
||||
|
||||
- name: Upload to S3
|
||||
run: aws s3 sync artifact/bin s3://flycast-builds/${{ matrix.config.destDir }}/${GITHUB_REF#refs/}-$GITHUB_SHA --acl public-read --follow-symlinks
|
||||
shell: bash
|
||||
if: ${{ steps.aws-credentials.outputs.aws-account-id != '' }}
|
||||
|
||||
- name: Setup Sentry CLI
|
||||
uses: mathieu-bour/setup-sentry-cli@v2
|
||||
env:
|
||||
SENTRY_TOKEN: ${{ secrets.SENTRY_TOKEN }}
|
||||
with:
|
||||
url: https://sentry.io
|
||||
token: ${{ env.SENTRY_TOKEN }}
|
||||
organization: flycast
|
||||
project: minidump
|
||||
version: 2.21.2
|
||||
if: ${{ env.SENTRY_TOKEN != '' }}
|
||||
|
||||
- name: Upload symbols to Sentry (Windows, macOS, Linux)
|
||||
run: |
|
||||
VERSION=$(git describe --tags --always)
|
||||
sentry-cli releases new "$VERSION"
|
||||
sentry-cli releases set-commits "$VERSION" --auto
|
||||
sentry-cli debug-files upload symbols
|
||||
shell: bash
|
||||
env:
|
||||
SENTRY_TOKEN: ${{ secrets.SENTRY_TOKEN }}
|
||||
if: ${{ env.SENTRY_TOKEN != '' && (matrix.config.name == 'x86_64-w64-mingw32' || matrix.config.name == 'apple-darwin' || matrix.config.name == 'x86_64-pc-linux-gnu') }}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
# Prepare source texts & upload them to Crowdin
|
||||
|
||||
name: Crowdin Source Texts Upload
|
||||
|
||||
# on change to the English texts
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- 'shell/libretro/libretro_core_options.h'
|
||||
|
||||
jobs:
|
||||
upload_source_file:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Setup Java JDK
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 18
|
||||
distribution: zulu
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.10'
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Upload Source
|
||||
shell: bash
|
||||
env:
|
||||
CROWDIN_API_KEY: ${{ secrets.CROWDIN_API_KEY }}
|
||||
run: |
|
||||
python3 intl/upload_workflow.py $CROWDIN_API_KEY "flyinghead-flycast" "shell/libretro/libretro_core_options.h"
|
||||
@@ -0,0 +1,49 @@
|
||||
# Download translations form Crowdin & Recreate libretro_core_options_intl.h
|
||||
|
||||
name: Crowdin Translation Integration
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# please choose a random time & weekday to avoid all repos synching at the same time
|
||||
- cron: '15 22 * * 5' # Fridays at 10:15 PM, UTC
|
||||
|
||||
jobs:
|
||||
create_intl_file:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Setup Java JDK
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 18
|
||||
distribution: zulu
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.10'
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false # otherwise, the token used is the GITHUB_TOKEN, instead of your personal access token.
|
||||
fetch-depth: 0 # otherwise, there would be errors pushing refs to the destination repository.
|
||||
|
||||
- name: Create intl file
|
||||
shell: bash
|
||||
env:
|
||||
CROWDIN_API_KEY: ${{ secrets.CROWDIN_API_KEY }}
|
||||
run: |
|
||||
python3 intl/download_workflow.py $CROWDIN_API_KEY "flyinghead-flycast" "shell/libretro/libretro_core_options_intl.h"
|
||||
|
||||
- name: Commit files
|
||||
run: |
|
||||
git config --local user.email "[email protected]"
|
||||
git config --local user.name "github-actions[bot]"
|
||||
git add intl/*_workflow.py "shell/libretro/libretro_core_options_intl.h"
|
||||
git commit -m "Fetch translations & Recreate libretro_core_options_intl.h"
|
||||
|
||||
- name: GitHub Push
|
||||
uses: ad-m/[email protected]
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
branch: ${{ github.ref }}
|
||||
@@ -0,0 +1,62 @@
|
||||
name: Nintendo Switch CI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: ${{ matrix.config.name }}
|
||||
runs-on: ubuntu-latest
|
||||
container: devkitpro/devkita64:latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- {name: standalone, destDir: switch}
|
||||
- {name: libretro, cmakeArgs: -DLIBRETRO=ON}
|
||||
|
||||
steps:
|
||||
- name: Set up build environment
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install awscli ccache
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: true
|
||||
|
||||
- name: Mark git repository as safe
|
||||
run: git config --global --add safe.directory $PWD
|
||||
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.ccache
|
||||
key: ccache-switch-${{ matrix.config.name }}-${{ github.sha }}
|
||||
restore-keys: ccache-switch-${{ matrix.config.name }}-
|
||||
|
||||
- name: CMake
|
||||
run: |
|
||||
$DEVKITPRO/portlibs/switch/bin/aarch64-none-elf-cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=artifact -G Ninja ${{ matrix.config.cmakeArgs }}
|
||||
cmake --build build --config Release --target install
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: flycast-switch-${{ matrix.config.name }}
|
||||
path: artifact/bin
|
||||
|
||||
- name: Package app
|
||||
run: zip -m artifact/bin/flycast.nro.zip artifact/bin/flycast.nro
|
||||
if: hashFiles('artifact/bin/flycast.nro') != ''
|
||||
|
||||
- name: Configure AWS Credentials
|
||||
id: aws-credentials
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
with:
|
||||
aws-access-key-id: AKIAJOZQS4H2PHQWYFCA
|
||||
aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
|
||||
aws-region: us-east-2
|
||||
if: github.repository == 'flyinghead/flycast' && github.event_name == 'push' && matrix.config.destDir != ''
|
||||
|
||||
- name: Upload to S3
|
||||
run: aws s3 sync artifact/bin s3://flycast-builds/${{ matrix.config.destDir }}/${GITHUB_REF#refs/}-$GITHUB_SHA --acl public-read --follow-symlinks
|
||||
if: ${{ steps.aws-credentials.outputs.aws-account-id != '' }}
|
||||
@@ -0,0 +1,54 @@
|
||||
name: Windows UWP CI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: true
|
||||
|
||||
- name: CMake
|
||||
run: |
|
||||
cmake -B build -DCMAKE_BUILD_TYPE=Release -G "Visual Studio 17 2022" -A x64 -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10.0.22000.0
|
||||
cmake --build build --config Release -- /m
|
||||
shell: cmd
|
||||
|
||||
- uses: ilammy/msvc-dev-cmd@v1
|
||||
|
||||
- name: Package app
|
||||
run: |
|
||||
mkdir build\artifact
|
||||
cd build\AppPackages\flycast\flycast_*_x64_Test
|
||||
mkdir tmp
|
||||
ren *.msix flycast.msix
|
||||
makeappx.exe unpack /p .\flycast.msix /d tmp
|
||||
copy ..\..\..\Release\*.dll tmp
|
||||
makeappx pack /d tmp /p ..\..\..\artifact\flycast.appx
|
||||
|
||||
- name: Sign app
|
||||
run: signtool sign /f shell\uwp\sign_cert.pfx /p '${{ secrets.SIGN_CERT_PWD }}' /v /fd SHA256 build\artifact\flycast.appx
|
||||
if: github.repository == 'flyinghead/flycast' && github.event_name == 'push'
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: flycast-uwp
|
||||
path: build/artifact
|
||||
|
||||
- name: Configure AWS Credentials
|
||||
id: aws-credentials
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
with:
|
||||
aws-access-key-id: AKIAJOZQS4H2PHQWYFCA
|
||||
aws-secret-access-key: ${{ secrets.S3_SECRET_KEY }}
|
||||
aws-region: us-east-2
|
||||
if: github.repository == 'flyinghead/flycast' && github.event_name == 'push'
|
||||
|
||||
- name: Upload to S3
|
||||
run: aws s3 sync build/artifact s3://flycast-builds/xbox/${GITHUB_REF#refs/}-$GITHUB_SHA --acl public-read --follow-symlinks
|
||||
shell: bash
|
||||
if: ${{ steps.aws-credentials.outputs.aws-account-id != '' }}
|
||||
+1
-1
@@ -31,7 +31,7 @@
|
||||
url = https://github.com/vkedwardli/Spout2.git
|
||||
[submodule "core/deps/discord-rpc"]
|
||||
path = core/deps/discord-rpc
|
||||
url = https://github.com/flyinghead/discord-rpc
|
||||
url = https://github.com/harmonytf/discord-rpc
|
||||
[submodule "core/deps/libadrenotools"]
|
||||
path = core/deps/libadrenotools
|
||||
url = https://github.com/bylaws/libadrenotools
|
||||
|
||||
+21
-35
@@ -18,7 +18,7 @@ if(APPLE)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "12.0" CACHE STRING "Minimum tvOS deployment version")
|
||||
set(CMAKE_OSX_ARCHITECTURES "arm64" CACHE STRING "")
|
||||
else()
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.11" CACHE STRING "Minimum macOS deployment version")
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13" CACHE STRING "Minimum macOS deployment version")
|
||||
set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64" CACHE STRING "")
|
||||
endif()
|
||||
endif()
|
||||
@@ -65,7 +65,7 @@ option(USE_DX11 "Build with Direct3D 11 support" ON)
|
||||
option(LIBRETRO "Build libretro core" OFF)
|
||||
option(USE_OPENGL "Use OpenGL API" ON)
|
||||
option(USE_VIDEOCORE "RPI: use the legacy Broadcom GLES libraries" OFF)
|
||||
option(APPLE_BREAKPAD "macOS: Build breakpad client and dump symbols" OFF)
|
||||
option(USE_BREAKPAD "Build and link with breakpad library" ON)
|
||||
option(ENABLE_GDB_SERVER "Build with GDB debugging support" OFF)
|
||||
option(ENABLE_DC_PROFILER "Build with support for target machine (SH4) profiler" OFF)
|
||||
option(ENABLE_FC_PROFILER "Build with support for host app (Flycast) profiler" OFF)
|
||||
@@ -266,7 +266,7 @@ if(NINTENDO_SWITCH)
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE shell/switch)
|
||||
endif()
|
||||
|
||||
if(NOT LIBRETRO)
|
||||
if(USE_BREAKPAD AND NOT LIBRETRO)
|
||||
find_program(SH_EXECUTABLE sh)
|
||||
if((ANDROID AND NOT CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") OR (MINGW AND SH_EXECUTABLE) OR (CMAKE_SYSTEM_NAME STREQUAL "Linux"))
|
||||
add_subdirectory(core/deps/breakpad)
|
||||
@@ -275,7 +275,7 @@ if(NOT LIBRETRO)
|
||||
endif()
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC breakpad_client)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE USE_BREAKPAD)
|
||||
elseif(APPLE AND NOT IOS AND APPLE_BREAKPAD)
|
||||
elseif(APPLE AND NOT IOS)
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
core/deps/breakpad/src/client/minidump_file_writer-inl.h
|
||||
core/deps/breakpad/src/client/minidump_file_writer.cc
|
||||
@@ -327,26 +327,26 @@ if(NOT LIBRETRO)
|
||||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build/breakpad/dump_syms
|
||||
-a x86_64
|
||||
-g ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/Flycast.app.dSYM
|
||||
${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/Flycast.app/Contents/MacOS/Flycast > ../symbols/Flycast-x64.sym
|
||||
$<TARGET_FILE:flycast> > ../symbols/Flycast-x64.sym
|
||||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build/breakpad/dump_syms
|
||||
-a arm64
|
||||
-g ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/Flycast.app.dSYM
|
||||
${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/Flycast.app/Contents/MacOS/Flycast > ../symbols/Flycast-arm64.sym
|
||||
$<TARGET_FILE:flycast> > ../symbols/Flycast-arm64.sym
|
||||
)
|
||||
else()
|
||||
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
|
||||
COMMAND sleep 20
|
||||
COMMAND mkdir -p ../symbols
|
||||
COMMAND dsymutil ${CMAKE_CURRENT_BINARY_DIR}/Flycast.app/Contents/MacOS/Flycast
|
||||
COMMAND dsymutil $<TARGET_FILE:flycast>
|
||||
-o ${CMAKE_CURRENT_BINARY_DIR}/Flycast.app.dSYM
|
||||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build/breakpad/dump_syms
|
||||
-a x86_64
|
||||
-g ${CMAKE_CURRENT_BINARY_DIR}/Flycast.app.dSYM
|
||||
${CMAKE_CURRENT_BINARY_DIR}/Flycast.app/Contents/MacOS/Flycast > ../symbols/Flycast-x64.sym
|
||||
$<TARGET_FILE:flycast> > ../symbols/Flycast-x64.sym
|
||||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build/breakpad/dump_syms
|
||||
-a arm64
|
||||
-g ${CMAKE_CURRENT_BINARY_DIR}/Flycast.app.dSYM
|
||||
${CMAKE_CURRENT_BINARY_DIR}/Flycast.app/Contents/MacOS/Flycast > ../symbols/Flycast-arm64.sym
|
||||
$<TARGET_FILE:flycast> > ../symbols/Flycast-arm64.sym
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
@@ -376,7 +376,7 @@ if(USE_OPENMP)
|
||||
message("-- Found OpenMP.xcframework: ${OPENMP_FRAMEWORK} (Linking static library before CMake support linking xcframework)")
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE ${OPENMP_FRAMEWORK})
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE ${OPENMP_XCFRAMEWORK}/ios-arm64_arm64e/OpenMP.framework/Versions/A/Headers)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Xclang -fopenmp")
|
||||
target_compile_options(${PROJECT_NAME} PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-Xclang -fopenmp>)
|
||||
endif()
|
||||
else()
|
||||
message("-- Could NOT find OpenMP.xcframework, compile it using shell/apple/emulator-ios/OpenMP/build_ios_openmp.sh")
|
||||
@@ -645,7 +645,7 @@ if(NOT LIBZIP_FOUND OR NINTENDO_SWITCH)
|
||||
option(BUILD_DOC "Build documentation" OFF)
|
||||
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
|
||||
option(LIBZIP_DO_INSTALL "Install libzip and the related files" OFF)
|
||||
add_subdirectory(core/deps/libzip)
|
||||
add_subdirectory(core/deps/libzip EXCLUDE_FROM_ALL)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE libzip::zip)
|
||||
endif()
|
||||
|
||||
@@ -742,7 +742,7 @@ target_sources(${PROJECT_NAME} PRIVATE core/deps/md5/md5.cpp)
|
||||
|
||||
if(USE_DISCORD AND NOT LIBRETRO)
|
||||
option(BUILD_EXAMPLES "Build example apps" OFF)
|
||||
add_subdirectory(core/deps/discord-rpc)
|
||||
add_subdirectory(core/deps/discord-rpc EXCLUDE_FROM_ALL)
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE core/deps/discord-rpc/include)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE discord-rpc)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE USE_DISCORD)
|
||||
@@ -1327,7 +1327,7 @@ if(NOT LIBRETRO)
|
||||
endif()
|
||||
|
||||
if(USE_VULKAN)
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC VK_NO_PROTOTYPES)
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC VK_ENABLE_BETA_EXTENSIONS VK_NO_PROTOTYPES)
|
||||
if(ANDROID)
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC VK_USE_PLATFORM_ANDROID_KHR)
|
||||
elseif(X11_FOUND)
|
||||
@@ -1335,7 +1335,6 @@ if(USE_VULKAN)
|
||||
elseif(WIN32)
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC VK_USE_PLATFORM_WIN32_KHR)
|
||||
elseif(APPLE)
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC VK_USE_PLATFORM_MACOS_MVK)
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC VK_USE_PLATFORM_METAL_EXT)
|
||||
endif()
|
||||
|
||||
@@ -1343,7 +1342,6 @@ if(USE_VULKAN)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE Vulkan::Headers)
|
||||
|
||||
add_subdirectory(core/deps/VulkanMemoryAllocator)
|
||||
target_compile_definitions(VulkanMemoryAllocator INTERFACE $<$<BOOL:${APPLE}>:VMA_USE_STL_SHARED_MUTEX=0>) # Only available on macOS 10.12+
|
||||
target_compile_options(VulkanMemoryAllocator INTERFACE $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:AppleClang,Clang>>:-Wno-nullability-completeness>)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE GPUOpen::VulkanMemoryAllocator)
|
||||
|
||||
@@ -1649,7 +1647,7 @@ if(NOT LIBRETRO)
|
||||
shell/apple/emulator-ios/emulator/iCade-iOS/iCadeReaderView.h
|
||||
shell/apple/emulator-ios/emulator/iCade-iOS/iCadeReaderView.m
|
||||
shell/apple/emulator-ios/emulator/iCade-iOS/iCadeState.h)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fobjc-arc -fmodules")
|
||||
target_compile_options(${PROJECT_NAME} PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-fobjc-arc -fmodules>)
|
||||
set_source_files_properties(shell/apple/emulator-ios/emulator/FlycastViewController.mm PROPERTIES COMPILE_OPTIONS -fcxx-modules)
|
||||
set_source_files_properties(shell/apple/emulator-ios/emulator/iCade-iOS/iCadeReaderView.m PROPERTIES COMPILE_OPTIONS -fno-objc-arc)
|
||||
|
||||
@@ -1715,15 +1713,9 @@ if(NOT LIBRETRO)
|
||||
|
||||
add_subdirectory(core/deps/Syphon)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE Syphon)
|
||||
if(${CMAKE_GENERATOR} MATCHES "^Xcode.*")
|
||||
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${SYPHON_METALLIB}
|
||||
${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/Flycast.app/Contents/Resources/)
|
||||
else()
|
||||
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${SYPHON_METALLIB}
|
||||
${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/Flycast.app/Contents/Resources/)
|
||||
endif()
|
||||
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${SYPHON_METALLIB}
|
||||
$<TARGET_FILE_DIR:flycast>/../Resources/)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE VIDEO_ROUTING)
|
||||
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
@@ -1774,15 +1766,9 @@ if(NOT LIBRETRO)
|
||||
${IOSURFACE_LIBRARY}
|
||||
${MULTITOUCH_SUPPORT_LIBRARY})
|
||||
|
||||
if(${CMAKE_GENERATOR} MATCHES "^Xcode.*")
|
||||
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "$ENV{VULKAN_SDK}/lib/libMoltenVK.dylib"
|
||||
${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/Flycast.app/Contents/Frameworks/libvulkan.dylib)
|
||||
else()
|
||||
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "$ENV{VULKAN_SDK}/lib/libMoltenVK.dylib"
|
||||
${CMAKE_CURRENT_BINARY_DIR}/Flycast.app/Contents/Frameworks/libvulkan.dylib)
|
||||
endif()
|
||||
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "$ENV{VULKAN_SDK}/lib/libMoltenVK.dylib"
|
||||
$<TARGET_FILE_DIR:flycast>/../Frameworks/libvulkan.dylib)
|
||||
endif()
|
||||
elseif(UNIX)
|
||||
if(NOT BUILD_TESTING)
|
||||
@@ -1843,7 +1829,7 @@ if(BUILD_TESTING)
|
||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||
endif()
|
||||
add_subdirectory(core/deps/googletest EXCLUDE_FROM_ALL)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE gtest_main)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE GTest::gtest_main)
|
||||
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
tests/src/CheatManagerTest.cpp
|
||||
|
||||
@@ -16,6 +16,8 @@ Join us on our [**Discord server**](https://discord.gg/X8YWP8w) for a chat.
|
||||
|
||||
## Install
|
||||
|
||||
### Android 
|
||||
Install Flycast from [**Google Play**](https://play.google.com/store/apps/details?id=com.flycast.emulator).
|
||||
### Flatpak (Linux )
|
||||
|
||||
1. [Set up Flatpak](https://www.flatpak.org/setup/).
|
||||
|
||||
@@ -365,16 +365,16 @@ void Achievements::term()
|
||||
void Achievements::authenticationSuccess(const rc_client_user_t *user)
|
||||
{
|
||||
NOTICE_LOG(COMMON, "RA Login successful");
|
||||
asyncTask([this, user]() {
|
||||
char url[512];
|
||||
int rc = rc_client_user_get_image_url(user, url, sizeof(url));
|
||||
if (rc == RC_OK)
|
||||
{
|
||||
std::string image = getOrDownloadImage(url);
|
||||
std::string url(512, '\0');
|
||||
int rc = rc_client_user_get_image_url(user, url.data(), url.size());
|
||||
if (rc == RC_OK)
|
||||
{
|
||||
asyncTask([this, url]() {
|
||||
std::string image = getOrDownloadImage(url.c_str());
|
||||
std::string text = "User " + config::AchievementsUserName.get() + " authenticated";
|
||||
notifier.notify(Notification::Login, image, text);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
loggedOn = true;
|
||||
if (!settings.content.fileName.empty()) // TODO better test?
|
||||
loadGame();
|
||||
@@ -600,46 +600,48 @@ void Achievements::handleUnlockEvent(const rc_client_event_t *event)
|
||||
assert(cheevo != nullptr);
|
||||
|
||||
INFO_LOG(COMMON, "RA: Achievement %s (%u) for game %s unlocked", cheevo->title, cheevo->id, settings.content.title.c_str());
|
||||
asyncTask([this, cheevo]() {
|
||||
char url[512];
|
||||
int rc = rc_client_achievement_get_image_url(cheevo, cheevo->state, url, sizeof(url));
|
||||
if (rc == RC_OK)
|
||||
{
|
||||
std::string image = getOrDownloadImage(url);
|
||||
std::string text = "Achievement " + std::string(cheevo->title) + " unlocked!";
|
||||
notifier.notify(Notification::Login, image, text, cheevo->description);
|
||||
}
|
||||
});
|
||||
std::string title(cheevo->title);
|
||||
std::string description(cheevo->description);
|
||||
std::string url(512, '\0');
|
||||
int rc = rc_client_achievement_get_image_url(cheevo, cheevo->state, url.data(), url.size());
|
||||
if (rc == RC_OK)
|
||||
{
|
||||
asyncTask([this, url, title, description]() {
|
||||
std::string image = getOrDownloadImage(url.c_str());
|
||||
std::string text = "Achievement " + title + " unlocked!";
|
||||
notifier.notify(Notification::Login, image, text, description);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Achievements::handleAchievementChallengeIndicatorShowEvent(const rc_client_event_t *event)
|
||||
{
|
||||
const rc_client_achievement_t* cheevo = event->achievement;
|
||||
INFO_LOG(COMMON, "RA: Challenge: %s", cheevo->title);
|
||||
asyncTask([this, cheevo]() {
|
||||
char url[128];
|
||||
int rc = rc_client_achievement_get_image_url(cheevo, RC_CLIENT_ACHIEVEMENT_STATE_UNLOCKED, url, sizeof(url));
|
||||
if (rc == RC_OK)
|
||||
{
|
||||
std::string image = getOrDownloadImage(url);
|
||||
std::string url(512, '\0');
|
||||
int rc = rc_client_achievement_get_image_url(cheevo, RC_CLIENT_ACHIEVEMENT_STATE_UNLOCKED, url.data(), url.size());
|
||||
if (rc == RC_OK)
|
||||
{
|
||||
asyncTask([this, url]() {
|
||||
std::string image = getOrDownloadImage(url.c_str());
|
||||
notifier.showChallenge(image);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Achievements::handleAchievementChallengeIndicatorHideEvent(const rc_client_event_t *event)
|
||||
{
|
||||
const rc_client_achievement_t* cheevo = event->achievement;
|
||||
INFO_LOG(COMMON, "RA: Challenge hidden: %s", cheevo->title);
|
||||
asyncTask([this, cheevo]() {
|
||||
char url[128];
|
||||
int rc = rc_client_achievement_get_image_url(cheevo, RC_CLIENT_ACHIEVEMENT_STATE_UNLOCKED, url, sizeof(url));
|
||||
if (rc == RC_OK)
|
||||
{
|
||||
std::string image = getOrDownloadImage(url);
|
||||
std::string url(512, '\0');
|
||||
int rc = rc_client_achievement_get_image_url(cheevo, RC_CLIENT_ACHIEVEMENT_STATE_UNLOCKED, url.data(), url.size());
|
||||
if (rc == RC_OK)
|
||||
{
|
||||
asyncTask([this, url]() {
|
||||
std::string image = getOrDownloadImage(url.c_str());
|
||||
notifier.hideChallenge(image);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Achievements::handleLeaderboardStarted(const rc_client_event_t *event)
|
||||
@@ -685,18 +687,21 @@ void Achievements::handleUpdateLeaderboardTracker(const rc_client_event_t *event
|
||||
void Achievements::handleGameCompleted(const rc_client_event_t *event)
|
||||
{
|
||||
const rc_client_game_t* game = rc_client_get_game_info(rc_client);
|
||||
asyncTask([this, game]() {
|
||||
std::string text1 = (rc_client_get_hardcore_enabled(rc_client) ? "Mastered " : "Completed ") + std::string(game->title);
|
||||
rc_client_user_game_summary_t summary;
|
||||
rc_client_get_user_game_summary(rc_client, &summary);
|
||||
std::stringstream ss;
|
||||
ss << summary.num_unlocked_achievements << " achievements, " << summary.points_unlocked << " points";
|
||||
std::string text2(ss.str());
|
||||
std::string text3 = rc_client_get_user_info(rc_client)->display_name;
|
||||
std::string url(512, '\0');
|
||||
if (rc_client_game_get_image_url(game, url.data(), url.size()) != RC_OK)
|
||||
url.clear();
|
||||
asyncTask([this, url, text1, text2, text3]() {
|
||||
std::string image;
|
||||
char url[128];
|
||||
if (rc_client_game_get_image_url(game, url, sizeof(url)) == RC_OK)
|
||||
image = getOrDownloadImage(url);
|
||||
std::string text1 = (rc_client_get_hardcore_enabled(rc_client) ? "Mastered " : "Completed ") + std::string(game->title);
|
||||
rc_client_user_game_summary_t summary;
|
||||
rc_client_get_user_game_summary(rc_client, &summary);
|
||||
std::stringstream ss;
|
||||
ss << summary.num_unlocked_achievements << " achievements, " << summary.points_unlocked << " points";
|
||||
std::string text3 = rc_client_get_user_info(rc_client)->display_name;
|
||||
notifier.notify(Notification::Mastery, image, text1, ss.str(), text3);
|
||||
if (!url.empty())
|
||||
image = getOrDownloadImage(url.c_str());
|
||||
notifier.notify(Notification::Mastery, image, text1, text2, text3);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -711,12 +716,15 @@ void Achievements::handleHideAchievementProgress(const rc_client_event_t *event)
|
||||
void Achievements::handleUpdateAchievementProgress(const rc_client_event_t *event)
|
||||
{
|
||||
const rc_client_achievement_t* cheevo = event->achievement;
|
||||
asyncTask([this, cheevo]() {
|
||||
char url[128];
|
||||
std::string url(512, '\0');
|
||||
if (rc_client_achievement_get_image_url(cheevo, RC_CLIENT_ACHIEVEMENT_STATE_ACTIVE, url.data(), url.size()) != RC_OK)
|
||||
url.clear();
|
||||
std::string progress(cheevo->measured_progress);
|
||||
asyncTask([this, url, progress]() {
|
||||
std::string image;
|
||||
if (rc_client_achievement_get_image_url(cheevo, RC_CLIENT_ACHIEVEMENT_STATE_ACTIVE, url, sizeof(url)) == RC_OK)
|
||||
image = getOrDownloadImage(url);
|
||||
notifier.notify(Notification::Progress, image, cheevo->measured_progress);
|
||||
if (!url.empty())
|
||||
image = getOrDownloadImage(url.c_str());
|
||||
notifier.notify(Notification::Progress, image, progress);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -919,21 +927,24 @@ void Achievements::gameLoaded(int result, const char *errorMessage)
|
||||
settings.raHardcoreMode = false;
|
||||
else
|
||||
settings.raHardcoreMode = (bool)rc_client_get_hardcore_enabled(rc_client);
|
||||
asyncTask([this, info]() {
|
||||
std::string url(512, '\0');
|
||||
if (rc_client_game_get_image_url(info, url.data(), url.size()) != RC_OK)
|
||||
url.clear();
|
||||
std::string text1(info->title);
|
||||
rc_client_user_game_summary_t summary;
|
||||
rc_client_get_user_game_summary(rc_client, &summary);
|
||||
std::string text2;
|
||||
if (summary.num_core_achievements > 0)
|
||||
text2 = "You have " + std::to_string(summary.num_unlocked_achievements)
|
||||
+ " of " + std::to_string(summary.num_core_achievements) + " achievements unlocked.";
|
||||
else
|
||||
text2 = "This game has no achievements.";
|
||||
asyncTask([this, url, text1, text2]() {
|
||||
std::string image;
|
||||
char url[512];
|
||||
if (rc_client_game_get_image_url(info, url, sizeof(url)) == RC_OK)
|
||||
image = getOrDownloadImage(url);
|
||||
rc_client_user_game_summary_t summary;
|
||||
rc_client_get_user_game_summary(rc_client, &summary);
|
||||
std::string text;
|
||||
if (summary.num_core_achievements > 0)
|
||||
text = "You have " + std::to_string(summary.num_unlocked_achievements)
|
||||
+ " of " + std::to_string(summary.num_core_achievements) + " achievements unlocked.";
|
||||
else
|
||||
text = "This game has no achievements.";
|
||||
std::string text2 = settings.raHardcoreMode ? "Hardcore Mode" : "";
|
||||
notifier.notify(Notification::Login, image, info->title, text, text2);
|
||||
if (!url.empty())
|
||||
image = getOrDownloadImage(url.c_str());
|
||||
std::string text3 = settings.raHardcoreMode ? "Hardcore Mode" : "";
|
||||
notifier.notify(Notification::Login, image, text1, text2, text3);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+5
-1
@@ -132,6 +132,9 @@ Option<std::vector<std::string>, false> ContentPath("Dreamcast.ContentPath");
|
||||
Option<bool, false> HideLegacyNaomiRoms("Dreamcast.HideLegacyNaomiRoms", true);
|
||||
Option<bool, false> UploadCrashLogs("UploadCrashLogs", true);
|
||||
Option<bool, false> DiscordPresence("DiscordPresence", true);
|
||||
#if defined(__ANDROID__) && !defined(LIBRETRO)
|
||||
Option<bool, false> UseSafFilePicker("UseSafFilePicker", true);
|
||||
#endif
|
||||
|
||||
// Profiler
|
||||
Option<bool> ProfilerEnabled("Profiler.Enabled");
|
||||
@@ -143,7 +146,7 @@ Option<float> ProfilerFrameWarningTime("Profiler.FrameWarningTime", 1.0f / 55.0f
|
||||
|
||||
Option<bool> NetworkEnable("Enable", false, "network");
|
||||
Option<bool> ActAsServer("ActAsServer", false, "network");
|
||||
OptionString DNS("DNS", "46.101.91.123", "network");
|
||||
OptionString DNS("DNS", "dns.flyca.st", "network");
|
||||
OptionString NetworkServer("server", "", "network");
|
||||
Option<int> LocalPort("LocalPort", NaomiNetwork::SERVER_PORT, "network");
|
||||
Option<bool> EmulateBBA("EmulateBBA", false, "network");
|
||||
@@ -168,6 +171,7 @@ Option<bool> OmxAudioHdmi("audio_hdmi", true, "omx");
|
||||
|
||||
Option<int> MouseSensitivity("MouseSensitivity", 100, "input");
|
||||
Option<int> VirtualGamepadVibration("VirtualGamepadVibration", 20, "input");
|
||||
Option<int> VirtualGamepadTransparency("VirtualGamepadTransparency", 37, "input");
|
||||
|
||||
std::array<Option<MapleDeviceType>, 4> MapleMainDevices {
|
||||
Option<MapleDeviceType>("device1", MDT_SegaController, "input"),
|
||||
|
||||
@@ -498,6 +498,9 @@ extern Option<std::vector<std::string>, false> ContentPath;
|
||||
extern Option<bool, false> HideLegacyNaomiRoms;
|
||||
extern Option<bool, false> UploadCrashLogs;
|
||||
extern Option<bool, false> DiscordPresence;
|
||||
#if defined(__ANDROID__) && !defined(LIBRETRO)
|
||||
extern Option<bool, false> UseSafFilePicker;
|
||||
#endif
|
||||
|
||||
// Profiling
|
||||
extern Option<bool> ProfilerEnabled;
|
||||
@@ -534,6 +537,7 @@ extern Option<bool> OmxAudioHdmi;
|
||||
|
||||
extern Option<int> MouseSensitivity;
|
||||
extern Option<int> VirtualGamepadVibration;
|
||||
extern Option<int> VirtualGamepadTransparency;
|
||||
extern std::array<Option<MapleDeviceType>, 4> MapleMainDevices;
|
||||
extern std::array<std::array<Option<MapleDeviceType>, 2>, 4> MapleExpansionDevices;
|
||||
extern Option<bool> PerGameVmu;
|
||||
|
||||
+13
-26
@@ -377,6 +377,17 @@ void CheatManager::setActive(bool active)
|
||||
void CheatManager::loadCheatFile(const std::string& filename)
|
||||
{
|
||||
#ifndef LIBRETRO
|
||||
try {
|
||||
hostfs::FileInfo fileInfo = hostfs::storage().getFileInfo(filename);
|
||||
if (fileInfo.size >= 1_MB) {
|
||||
WARN_LOG(COMMON, "Cheat file '%s' is too big", filename.c_str());
|
||||
return;
|
||||
}
|
||||
} catch (const hostfs::StorageException& e) {
|
||||
WARN_LOG(COMMON, "Cannot find cheat file '%s': %s", filename.c_str(), e.what());
|
||||
return;
|
||||
}
|
||||
|
||||
FILE* cheatfile = hostfs::storage().openFile(filename, "r");
|
||||
if (cheatfile == nullptr)
|
||||
{
|
||||
@@ -421,7 +432,8 @@ void CheatManager::loadCheatFile(const std::string& filename)
|
||||
}
|
||||
setActive(!cheats.empty());
|
||||
INFO_LOG(COMMON, "%d cheats loaded", (int)cheats.size());
|
||||
cfgSaveStr("cheats", gameId, filename);
|
||||
if (!cheats.empty())
|
||||
cfgSaveStr("cheats", gameId, filename);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -454,31 +466,6 @@ void CheatManager::reset(const std::string& gameId)
|
||||
else if (gameId == "THE KING OF ROUTE66") {
|
||||
cheats.emplace_back(Cheat::Type::setValue, "ignore drive error", true, 32, 0x00023ee0, 0x0009000B, true); // rts, nop
|
||||
}
|
||||
else if (gameId.substr(0, 8) == "MKG TKOB")
|
||||
{
|
||||
const auto& setMushikingCheats = [this](u32 addr) {
|
||||
cheats.emplace_back(Cheat::Type::setValue, "ignore rfid1 error", true, 32, addr, 0, true); // rfid[0].error = 0
|
||||
cheats.emplace_back(Cheat::Type::setValue, "ignore rfid2 error", true, 32, addr + 0x48, 0, true); // rfid[1].error = 0
|
||||
cheats.emplace_back(Cheat::Type::setValue, "ignore rfid1 status", true, 32, addr + 8, 0, true); // rfid[0].data18 = 0
|
||||
cheats.emplace_back(Cheat::Type::setValue, "ignore rfid2 status", true, 32, addr + 0x50, 0, true); // rfid[1].data18 = 0
|
||||
};
|
||||
if (gameId == "MKG TKOB 2 EXP VER1.001-") // mushi2eo
|
||||
setMushikingCheats(0x6fe1bc);
|
||||
else if (gameId == "MKG TKOB 2 JPN VER2.001-") // mushik2e
|
||||
setMushikingCheats(0x6ffe54);
|
||||
else if (gameId == "MKG TKOB 2K3 2ND VER1.003-")// mushike
|
||||
setMushikingCheats(0x4ad7ec);
|
||||
else if (gameId == "MKG TKOB 4 JPN VER2.000-") // mushik4e
|
||||
setMushikingCheats(0xb0e538);
|
||||
else if (gameId == "MKG TKOB 2K3 2ND VER1.002-")// mushikeo
|
||||
setMushikingCheats(0x4ad56c);
|
||||
else if (gameId == "MKG TKOB 2K3 2ND KOR VER1.000-") // mushikk
|
||||
setMushikingCheats(0x4ac9b8);
|
||||
else if (gameId == "MKG TKOB 2K3 2ND CHN VER1.000-") // mushikc
|
||||
setMushikingCheats(0x4aa9b8);
|
||||
else if (gameId == "MKG TKOB 2 KOR VER1.000-") // mushik2k
|
||||
setMushikingCheats(0x706084);
|
||||
}
|
||||
else if (gameId == "T-8120N") { // Dave Mirra BMX (US)
|
||||
cheats.emplace_back(Cheat::Type::setValue, "fix main loop time", true, 32, 0x0030b8cc, 0x42040000, true); // 33.0 ms
|
||||
}
|
||||
|
||||
+1
-1
Submodule core/deps/SDL updated: 92fe3b19c8...79ec168f3c.
+26
-14
@@ -38,7 +38,7 @@ unsigned long track_mode;
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
void CDI_read_track (FILE *fsource, image_s *image, track_s *track)
|
||||
bool CDI_read_track (FILE *fsource, image_s *image, track_s *track)
|
||||
{
|
||||
|
||||
unsigned char TRACK_START_MARK[10] = { 0, 0, 0x01, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF };
|
||||
@@ -49,10 +49,16 @@ void CDI_read_track (FILE *fsource, image_s *image, track_s *track)
|
||||
fseek(fsource, 8, SEEK_CUR); // extra data (DJ 3.00.780 and up)
|
||||
|
||||
fread(¤t_start_mark, 10, 1, fsource);
|
||||
if (memcmp(TRACK_START_MARK, current_start_mark, 10)) printf( "Unsupported format: Could not find the track start mark");
|
||||
if (memcmp(TRACK_START_MARK, current_start_mark, 10)) {
|
||||
printf("CDI_read_track: Unsupported format: Could not find the track start mark\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
fread(¤t_start_mark, 10, 1, fsource);
|
||||
if (memcmp(TRACK_START_MARK, current_start_mark, 10)) printf( "Unsupported format: Could not find the track start mark");
|
||||
if (memcmp(TRACK_START_MARK, current_start_mark, 10)) {
|
||||
printf("CDI_read_track: Unsupported format: Could not find the track start mark\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
fseek(fsource, 4, SEEK_CUR);
|
||||
fread(&track->filename_length, 1, 1, fsource);
|
||||
@@ -80,20 +86,25 @@ void CDI_read_track (FILE *fsource, image_s *image, track_s *track)
|
||||
case 1 : track->sector_size = 2336; break;
|
||||
case 2 : track->sector_size = 2352; break;
|
||||
case 4 : track->sector_size = 2448; break;
|
||||
default: printf("Unsupported sector size. value %ld\n", track->sector_size_value);
|
||||
break;
|
||||
default:
|
||||
printf("CDI_read_track: Unsupported sector size. value %ld\n", track->sector_size_value);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (track->mode > 2) printf( "Unsupported format: Track mode not supported");
|
||||
if (track->mode > 2) {
|
||||
printf("CDI_read_track: Unsupported format: Track mode not supported\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
fseek(fsource, 29, SEEK_CUR);
|
||||
if (image->version != CDI_V2)
|
||||
{
|
||||
{
|
||||
fseek(fsource, 5, SEEK_CUR);
|
||||
fread(&temp_value, 4, 1, fsource);
|
||||
if (temp_value == 0xffffffff)
|
||||
fseek(fsource, 78, SEEK_CUR); // extra data (DJ 3.00.780 and up)
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -104,9 +115,9 @@ void CDI_skip_next_session (FILE *fsource, image_s *image)
|
||||
if (image->version != CDI_V2) fseek(fsource, 1, SEEK_CUR);
|
||||
}
|
||||
|
||||
void CDI_get_tracks (FILE *fsource, image_s *image)
|
||||
bool CDI_get_tracks (FILE *fsource, image_s *image)
|
||||
{
|
||||
fread(&image->tracks, 2, 1, fsource);
|
||||
return fread(&image->tracks, 2, 1, fsource) == 1;
|
||||
}
|
||||
|
||||
bool CDI_init (FILE *fsource, image_s *image, const char *fsourcename)
|
||||
@@ -121,8 +132,9 @@ bool CDI_init (FILE *fsource, image_s *image, const char *fsourcename)
|
||||
}
|
||||
|
||||
fseek(fsource, image->length-8, SEEK_SET);
|
||||
fread(&image->version, 4, 1, fsource);
|
||||
fread(&image->header_offset, 4, 1, fsource);
|
||||
if (fread(&image->version, 4, 1, fsource) != 1
|
||||
|| fread(&image->header_offset, 4, 1, fsource) != 1)
|
||||
return false;
|
||||
|
||||
if ((image->version != CDI_V2 && image->version != CDI_V3 && image->version != CDI_V35)
|
||||
|| image->header_offset == 0)
|
||||
@@ -133,7 +145,7 @@ bool CDI_init (FILE *fsource, image_s *image, const char *fsourcename)
|
||||
return true;
|
||||
}
|
||||
|
||||
void CDI_get_sessions (FILE *fsource, image_s *image)
|
||||
bool CDI_get_sessions (FILE *fsource, image_s *image)
|
||||
{
|
||||
#ifndef DEBUG_CDI
|
||||
if (image->version == CDI_V35)
|
||||
@@ -144,6 +156,6 @@ void CDI_get_sessions (FILE *fsource, image_s *image)
|
||||
#else
|
||||
fseek(fsource, 0L, SEEK_SET);
|
||||
#endif
|
||||
fread(&image->sessions, 2, 1, fsource);
|
||||
return fread(&image->sessions, 2, 1, fsource) == 1;
|
||||
}
|
||||
|
||||
@@ -38,9 +38,9 @@ typedef struct track_s
|
||||
|
||||
unsigned long ask_type(FILE *fsource, long header_position);
|
||||
bool CDI_init(FILE *fsource, image_s *image, const char *fsourcename);
|
||||
void CDI_get_sessions(FILE *fsource, image_s *image);
|
||||
void CDI_get_tracks(FILE *fsource, image_s *image);
|
||||
void CDI_read_track(FILE *fsource, image_s *image, track_s *track);
|
||||
bool CDI_get_sessions(FILE *fsource, image_s *image);
|
||||
bool CDI_get_tracks(FILE *fsource, image_s *image);
|
||||
bool CDI_read_track(FILE *fsource, image_s *image, track_s *track);
|
||||
void CDI_skip_next_session(FILE *fsource, image_s *image);
|
||||
|
||||
#endif
|
||||
+1
-1
Submodule core/deps/discord-rpc updated: 6cae2bd07a...c62c34d37c.
+1
-1
Submodule core/deps/googletest updated: f8d7d77c06...e397860881.
@@ -11661,7 +11661,7 @@ static bool ImGui::NavScoreItem(ImGuiNavItemData* result)
|
||||
// Compute distance between boxes
|
||||
// FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
|
||||
float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);
|
||||
float dby = cand.Min.y == curr.Max.y || cand.Max.y == curr.Min.y
|
||||
float dby = ImFabs(cand.Min.y - curr.Max.y) < 1 || ImFabs(cand.Max.y - curr.Min.y) < 1
|
||||
// Scale down on Y to keep using box-distance for vertically touching items
|
||||
? NavScoreItemDistInterval(ImLerp(cand.Min.y, cand.Max.y, 0.2f), ImLerp(cand.Min.y, cand.Max.y, 0.8f), ImLerp(curr.Min.y, curr.Max.y, 0.2f), ImLerp(curr.Min.y, curr.Max.y, 0.8f))
|
||||
: NavScoreItemDistInterval(cand.Min.y, cand.Max.y, curr.Min.y, curr.Max.y);
|
||||
|
||||
@@ -2773,6 +2773,9 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
|
||||
dst_tmp_array.resize(atlas->Fonts.Size);
|
||||
memset(src_tmp_array.Data, 0, (size_t)src_tmp_array.size_in_bytes());
|
||||
memset(dst_tmp_array.Data, 0, (size_t)dst_tmp_array.size_in_bytes());
|
||||
ImVector<bool> src_tmp_array_skip;
|
||||
src_tmp_array_skip.resize(atlas->ConfigData.Size);
|
||||
memset(src_tmp_array_skip.Data, 0, (size_t)src_tmp_array_skip.size_in_bytes());
|
||||
|
||||
// 1. Initialize font loading structure, check font data validity
|
||||
for (int src_i = 0; src_i < atlas->ConfigData.Size; src_i++)
|
||||
@@ -2797,7 +2800,8 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
|
||||
if (!stbtt_InitFont(&src_tmp.FontInfo, (unsigned char*)cfg.FontData, font_offset))
|
||||
{
|
||||
IM_ASSERT(0 && "stbtt_InitFont(): failed to parse FontData. It is correct and complete? Check FontDataSize.");
|
||||
return false;
|
||||
src_tmp_array_skip[src_i] = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Measure highest codepoints
|
||||
@@ -2818,6 +2822,8 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
|
||||
int total_glyphs_count = 0;
|
||||
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
|
||||
{
|
||||
if (src_tmp_array_skip[src_i])
|
||||
continue;
|
||||
ImFontBuildSrcData& src_tmp = src_tmp_array[src_i];
|
||||
ImFontBuildDstData& dst_tmp = dst_tmp_array[src_tmp.DstIndex];
|
||||
src_tmp.GlyphsSet.Create(src_tmp.GlyphsHighest + 1);
|
||||
@@ -2844,6 +2850,8 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
|
||||
// 3. Unpack our bit map into a flat list (we now have all the Unicode points that we know are requested _and_ available _and_ not overlapping another)
|
||||
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
|
||||
{
|
||||
if (src_tmp_array_skip[src_i])
|
||||
continue;
|
||||
ImFontBuildSrcData& src_tmp = src_tmp_array[src_i];
|
||||
src_tmp.GlyphsList.reserve(src_tmp.GlyphsCount);
|
||||
UnpackBitVectorToFlatIndexList(&src_tmp.GlyphsSet, &src_tmp.GlyphsList);
|
||||
@@ -2974,6 +2982,8 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
|
||||
// 9. Setup ImFont and glyphs for runtime
|
||||
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
|
||||
{
|
||||
if (src_tmp_array_skip[src_i])
|
||||
continue;
|
||||
// When merging fonts with MergeMode=true:
|
||||
// - We can have multiple input fonts writing into a same destination font.
|
||||
// - dst_font->ConfigData is != from cfg which is our source configuration.
|
||||
|
||||
+1
-1
Submodule core/deps/libchdr updated: 86b272076d...9b6ff6c3c2.
@@ -76,6 +76,9 @@ int _zip_win32_error_to_errno(DWORD win32err);
|
||||
#ifdef __clang__
|
||||
#define DONT_WARN_INCOMPATIBLE_FN_PTR_BEGIN _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wincompatible-function-pointer-types\"")
|
||||
#define DONT_WARN_INCOMPATIBLE_FN_PTR_END _Pragma("GCC diagnostic pop")
|
||||
#elif defined(__GNUC__) && __GNUC__ > 13
|
||||
#define DONT_WARN_INCOMPATIBLE_FN_PTR_BEGIN _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wincompatible-pointer-types\"")
|
||||
#define DONT_WARN_INCOMPATIBLE_FN_PTR_END _Pragma("GCC diagnostic pop")
|
||||
#else
|
||||
#define DONT_WARN_INCOMPATIBLE_FN_PTR_BEGIN
|
||||
#define DONT_WARN_INCOMPATIBLE_FN_PTR_END
|
||||
|
||||
+1
-1
Submodule core/deps/oboe updated: 8740d0fc32...03242e9ef9.
+1
-1
Submodule core/deps/rcheevos updated: e88f749925...563230b1c2.
+106
-20
@@ -136,43 +136,52 @@ static void loadSpecialSettings()
|
||||
// Aikagi
|
||||
|| prod_id == "T20130M"
|
||||
// AIR
|
||||
|| prod_id == "T20112M")
|
||||
|| prod_id == "T20112M"
|
||||
// Cool Boarders Burrrn (JP)
|
||||
|| prod_id == "T36901M"
|
||||
// Castle Fantasia - Seima Taisen (JP)
|
||||
|| prod_id == "T46901M"
|
||||
// Silent Scope (US)
|
||||
|| prod_id == "T9507N"
|
||||
// Silent Scope (EU)
|
||||
|| prod_id == "T9505D")
|
||||
{
|
||||
INFO_LOG(BOOT, "Enabling RTT Copy to VRAM for game %s", prod_id.c_str());
|
||||
config::RenderToTextureBuffer.override(true);
|
||||
}
|
||||
// Cosmic Smash
|
||||
if (prod_id == "HDR-0176" || prod_id == "RDC-0057")
|
||||
{
|
||||
INFO_LOG(BOOT, "Enabling translucent depth multipass for game %s", prod_id.c_str());
|
||||
// Cosmic Smash
|
||||
config::TranslucentPolygonDepthMask.override(true);
|
||||
}
|
||||
// NHL 2K2
|
||||
if (prod_id == "MK-51182")
|
||||
// Extra Depth Scaling
|
||||
if (prod_id == "MK-51182") // NHL 2K2
|
||||
{
|
||||
INFO_LOG(BOOT, "Enabling Extra depth scaling for game %s", prod_id.c_str());
|
||||
config::ExtraDepthScale.override(1e8f);
|
||||
}
|
||||
// Re-Volt (US, EU, JP)
|
||||
else if (prod_id == "T-8109N" || prod_id == "T8107D 50" || prod_id == "T-8101M")
|
||||
else if (prod_id == "T-8109N" // Re-Volt (US, EU, JP)
|
||||
|| prod_id == "T8107D 50"
|
||||
|| prod_id == "T-8101M"
|
||||
|| prod_id == "DR001") // Sturmwind
|
||||
{
|
||||
INFO_LOG(BOOT, "Enabling Extra depth scaling for game %s", prod_id.c_str());
|
||||
config::ExtraDepthScale.override(100.f);
|
||||
}
|
||||
// Test Drive V-Rally
|
||||
else if (prod_id == "T15110N" || prod_id == "T15105D 50")
|
||||
else if (prod_id == "T15110N" // Test Drive V-Rally
|
||||
|| prod_id == "T15105D 50")
|
||||
{
|
||||
INFO_LOG(BOOT, "Enabling Extra depth scaling for game %s", prod_id.c_str());
|
||||
config::ExtraDepthScale.override(0.1f);
|
||||
}
|
||||
// South Park Rally
|
||||
else if (prod_id == "T-8116N" || prod_id == "T-8112D-50")
|
||||
else if (prod_id == "T-8116N" // South Park Rally
|
||||
|| prod_id == "T-8112D-50")
|
||||
{
|
||||
INFO_LOG(BOOT, "Enabling Extra depth scaling for game %s", prod_id.c_str());
|
||||
config::ExtraDepthScale.override(1000.f);
|
||||
}
|
||||
// Capcom vs. SNK - Millennium Fight 2000 Pro
|
||||
else if (prod_id == "T1247M")
|
||||
else if (prod_id == "T1247M") // Capcom vs. SNK - Millennium Fight 2000 Pro
|
||||
{
|
||||
INFO_LOG(BOOT, "Enabling Extra depth scaling for game %s", prod_id.c_str());
|
||||
config::ExtraDepthScale.override(10000.f);
|
||||
@@ -255,7 +264,8 @@ static void loadSpecialSettings()
|
||||
}
|
||||
else if (prod_id == "T17708N" // Stupid Invaders (US)
|
||||
|| prod_id == "T17711D" // Stupid Invaders (EU)
|
||||
|| prod_id == "T46509M") // Suika (JP)
|
||||
|| prod_id == "T46509M" // Suika (JP)
|
||||
|| prod_id == "T36901M") // Cool Boarders Burrrn (JP)
|
||||
{
|
||||
NOTICE_LOG(BOOT, "Forcing HLE BIOS");
|
||||
config::UseReios.override(true);
|
||||
@@ -273,7 +283,8 @@ static void loadSpecialSettings()
|
||||
else if (prod_id == "T-9709D-50" // San Francisco Rush 2049 (EU)
|
||||
|| prod_id == "T-8112D-50" // South Park Rally (EU)
|
||||
|| prod_id == "T7014D 50" // Super Runabout (EU)
|
||||
|| prod_id == "T10001D 50") // MTV Sport - Skateboarding (PAL)
|
||||
|| prod_id == "T10001D 50" // MTV Sport - Skateboarding (PAL)
|
||||
|| prod_id == "MK-5101050") // Snow Surfers
|
||||
{
|
||||
NOTICE_LOG(BOOT, "Forcing PAL broadcasting");
|
||||
config::Broadcast.override(1);
|
||||
@@ -299,6 +310,63 @@ static void loadSpecialSettings()
|
||||
NOTICE_LOG(BOOT, "Disabling Native Depth Interpolation");
|
||||
config::NativeDepthInterpolation.override(false);
|
||||
}
|
||||
// Per-pixel transparent layers
|
||||
int layers = 0;
|
||||
if (prod_id == "MK-51011" // Time Stalkers (US)
|
||||
|| prod_id == "MK-5101153") // Time Stalkers (EU)
|
||||
layers = 72;
|
||||
else if (prod_id == "T13001N" // Blue Stinger (US)
|
||||
|| prod_id == "HDR-0003" // Blue Stinger (JP)
|
||||
|| prod_id == "T13001D-05" // Blue Stinger (EU)
|
||||
|| prod_id == "T13001D 18") // Blue Stinger (DE)
|
||||
layers = 80;
|
||||
else if (prod_id == "T2102M" // Panzer Front
|
||||
|| prod_id == "T-8118N" // Spirit of Speed (US)
|
||||
|| prod_id == "T-8117D-50" // Spirit of Speed (EU)
|
||||
|| prod_id == "T13002N" // Vigilante 8 (US)
|
||||
|| prod_id == "T13002D") // Vigilante 8 (EU)
|
||||
layers = 64;
|
||||
else if (prod_id == "T2106M") // L.O.L. Lack of Love
|
||||
layers = 48;
|
||||
else if (prod_id == "T1212M") // Gaiamaster - Kessen! Seikioh Densetsu
|
||||
layers = 96;
|
||||
else if (prod_id == "T-9707N" // San Francisco Rush 2049 (US)
|
||||
|| prod_id == "T-9709D-50" // San Francisco Rush 2049 (EU)
|
||||
|| prod_id == "T17721N" // Conflict Zone (US)
|
||||
|| prod_id == "T46604D") // Conflict Zone (EU)
|
||||
layers = 152;
|
||||
else if (prod_id == "MK-51033" // ECCO the Dolphin (US)
|
||||
|| prod_id == "MK-5103350" // ECCO the Dolphin (EU)
|
||||
|| prod_id == "HDR-0103") // ECCO the Dolphin (JP)
|
||||
layers = 96;
|
||||
else if (prod_id == "T40203N") // Draconus: Cult of the Wyrm
|
||||
layers = 80;
|
||||
else if (prod_id == "T40212N" // Soldier of Fortune (US)
|
||||
|| prod_id == "T17726D 50") // Soldier of Fortune (EU)
|
||||
layers = 86;
|
||||
else if (prod_id == "T44102N") // BANG! Gunship Elite
|
||||
layers = 100;
|
||||
else if (prod_id == "T12502N" // MDK 2 (US)
|
||||
|| prod_id == "T12501D 50") // MDK 2 (EU)
|
||||
layers = 200;
|
||||
else if (prod_id == "T9708D 50") // Army Men
|
||||
layers = 173;
|
||||
else if (prod_id == "MK-51038" // Zombie Revenge (US)
|
||||
|| prod_id == "MK-5103850" // Zombie Revenge (EU)
|
||||
|| prod_id == "HDR-0026" // Zombie Revenge (JP)
|
||||
|| prod_id == "36801N" // Fighting Force 2 (US)
|
||||
|| prod_id == "36802D 80" // Fighting Force 2 (PAL, en-fr)
|
||||
|| prod_id == "36802D 18") // Fighting Force 2 (PAL, de)
|
||||
layers = 116;
|
||||
else if (prod_id == "T15112N") // Demolition Racer (US)
|
||||
layers = 44;
|
||||
else if (prod_id == "T1208N" // Tech Romancer (US)
|
||||
|| prod_id == "T7009D50") // Tech Romancer (EU)
|
||||
layers = 56;
|
||||
if (layers != 0) {
|
||||
NOTICE_LOG(BOOT, "Forcing %d transparent layers", layers);
|
||||
config::PerPixelLayers.override(layers);
|
||||
}
|
||||
}
|
||||
else if (settings.platform.isArcade())
|
||||
{
|
||||
@@ -625,6 +693,8 @@ void Emulator::runInternal()
|
||||
{
|
||||
nvmem::saveFiles();
|
||||
dc_reset(false);
|
||||
if (!restartCpu())
|
||||
resetRequested = false;
|
||||
}
|
||||
} while (resetRequested);
|
||||
}
|
||||
@@ -686,9 +756,12 @@ void Emulator::stop()
|
||||
// Avoid race condition with GGPO restarting the sh4 for a new frame
|
||||
if (config::GGPOEnable)
|
||||
NetworkHandshake::term();
|
||||
// must be updated after GGPO is stopped since it may run some rollback frames
|
||||
state = Loaded;
|
||||
sh4_cpu.Stop();
|
||||
{
|
||||
const std::lock_guard<std::mutex> _(mutex);
|
||||
// must be updated after GGPO is stopped since it may run some rollback frames
|
||||
state = Loaded;
|
||||
sh4_cpu.Stop();
|
||||
}
|
||||
if (config::ThreadedRendering)
|
||||
{
|
||||
rend_cancel_emu_wait();
|
||||
@@ -832,6 +905,8 @@ void Emulator::run()
|
||||
verify(state == Running);
|
||||
startTime = sh4_sched_now64();
|
||||
renderTimeout = false;
|
||||
if (!singleStep && stepRangeTo == 0)
|
||||
sh4_cpu.Start();
|
||||
try {
|
||||
runInternal();
|
||||
if (ggpo::active())
|
||||
@@ -874,6 +949,7 @@ void Emulator::start()
|
||||
if (config::ThreadedRendering)
|
||||
{
|
||||
const std::lock_guard<std::mutex> lock(mutex);
|
||||
sh4_cpu.Start();
|
||||
threadResult = std::async(std::launch::async, [this] {
|
||||
ThreadName _("Flycast-emu");
|
||||
InitAudio();
|
||||
@@ -938,16 +1014,17 @@ bool Emulator::render()
|
||||
|
||||
if (!config::ThreadedRendering)
|
||||
{
|
||||
if (state != Running)
|
||||
return false;
|
||||
run();
|
||||
if (stopRequested)
|
||||
{
|
||||
stopRequested = false;
|
||||
TermAudio();
|
||||
nvmem::saveFiles();
|
||||
EventManager::event(Event::Pause);
|
||||
return false;
|
||||
}
|
||||
if (state != Running)
|
||||
return false;
|
||||
run();
|
||||
// TODO if stopping due to a user request, no frame has been rendered
|
||||
return !renderTimeout;
|
||||
}
|
||||
@@ -971,4 +1048,13 @@ void Emulator::vblank()
|
||||
sh4_cpu.Stop();
|
||||
}
|
||||
|
||||
bool Emulator::restartCpu()
|
||||
{
|
||||
const std::lock_guard<std::mutex> _(mutex);
|
||||
if (state != Running)
|
||||
return false;
|
||||
sh4_cpu.Start();
|
||||
return true;
|
||||
}
|
||||
|
||||
Emulator emu;
|
||||
@@ -165,6 +165,11 @@ public:
|
||||
* Called internally on vblank.
|
||||
*/
|
||||
void vblank();
|
||||
/**
|
||||
* Restart the cpu iff the emu is still running
|
||||
* Returns true if the cpu was started
|
||||
*/
|
||||
bool restartCpu();
|
||||
|
||||
private:
|
||||
bool checkStatus(bool wait = false);
|
||||
|
||||
+16
-16
@@ -528,6 +528,7 @@ struct ChannelEx
|
||||
enabled=false;
|
||||
SetAegState(EG_Release);
|
||||
AEG.SetValue(0x3FF);
|
||||
CA = 0;
|
||||
}
|
||||
|
||||
void enable()
|
||||
@@ -611,9 +612,12 @@ struct ChannelEx
|
||||
clip_verify((s64)sample*oDsp>=0);
|
||||
|
||||
StepAEG(this);
|
||||
StepFEG(this);
|
||||
StepStream(this);
|
||||
lfo.Step(this);
|
||||
if (enabled)
|
||||
{
|
||||
StepFEG(this);
|
||||
StepStream(this);
|
||||
lfo.Step(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -854,8 +858,8 @@ struct ChannelEx
|
||||
{
|
||||
switch (offset)
|
||||
{
|
||||
case 0x00:
|
||||
case 0x01:
|
||||
case 0x00: // PCMS, SA
|
||||
case 0x01: // KYONEX, KYONB, SSCTL, LPCTL, PCMS
|
||||
UpdateStreamStep();
|
||||
if (offset == 0 || size == 2)
|
||||
UpdateSA();
|
||||
@@ -872,8 +876,8 @@ struct ChannelEx
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x04:
|
||||
case 0x05:
|
||||
case 0x04: // SA
|
||||
case 0x05: // SA
|
||||
UpdateSA();
|
||||
break;
|
||||
|
||||
@@ -1219,7 +1223,7 @@ void AegStep(ChannelEx* ch)
|
||||
if (ch->AEG.GetValue() >= 0x3FF)
|
||||
{
|
||||
aeg_printf("[%d]AEG_step : EG_Release End @ %x", ch->ChannelNumber, ch->AEG.GetValue());
|
||||
ch->AEG.SetValue(0x3FF);
|
||||
ch->disable();
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1440,8 +1444,8 @@ void ReadCommonReg(u32 reg,bool byte)
|
||||
CommonData->EG = aeg; //AEG is only 10 bits, FEG is 13 bits
|
||||
CommonData->SGC=Chans[chan].AEG.state;
|
||||
|
||||
if (! (byte && reg==0x2810))
|
||||
Chans[chan].loop.looped=0;
|
||||
if (!byte || reg == 0x2811)
|
||||
Chans[chan].loop.looped = 0;
|
||||
}
|
||||
break;
|
||||
case 0x2814: //CA
|
||||
@@ -1513,13 +1517,9 @@ void AICA_Sample()
|
||||
mixr += b;
|
||||
}
|
||||
|
||||
//Mono !
|
||||
// Mono
|
||||
if (CommonData->Mono)
|
||||
{
|
||||
//Yay for mono =P
|
||||
mixl+=mixr;
|
||||
mixr=mixl;
|
||||
}
|
||||
mixl = mixr = FPs(mixl + mixr, 1);
|
||||
|
||||
//MVOL !
|
||||
//we want to make sure mix* is *At least* 23 bits wide here, so 64 bit mul !
|
||||
|
||||
@@ -241,6 +241,10 @@ static void createNaomiDevices()
|
||||
{
|
||||
mcfg_Create(MDT_RFIDReaderWriter, 1, 5, 0);
|
||||
mcfg_Create(MDT_RFIDReaderWriter, 2, 5, 1);
|
||||
if (gameId.substr(0, 8) == "MKG TKOB") {
|
||||
insertRfidCard(0);
|
||||
insertRfidCard(1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1729,21 +1729,22 @@ struct RFIDReaderWriter : maple_base
|
||||
DEBUG_LOG(MAPLE, "RFID card read (data? %d)", d4Seen);
|
||||
w32(getStatus());
|
||||
if (!d4Seen)
|
||||
w32(0x12345678); // arbitrary value (unknown)
|
||||
// serial0 and serial1 only
|
||||
wptr(cardData, 8);
|
||||
else
|
||||
wptr(cardData, sizeof(cardData));
|
||||
return (MapleDeviceRV)0xfe;
|
||||
|
||||
case 0xD9: // lock card
|
||||
w32(getStatus());
|
||||
cardLocked = true;
|
||||
w32(getStatus());
|
||||
INFO_LOG(MAPLE, "RFID card %d locked", player_num);
|
||||
return (MapleDeviceRV)0xfe;
|
||||
|
||||
case 0xDA: // unlock card
|
||||
w32(getStatus());
|
||||
cardLocked = false;
|
||||
cardInserted = false;
|
||||
w32(getStatus());
|
||||
NOTICE_LOG(MAPLE, "RFID card %d unlocked", player_num);
|
||||
os_notify("Card ejected", 2000);
|
||||
return (MapleDeviceRV)0xfe;
|
||||
@@ -1760,6 +1761,34 @@ struct RFIDReaderWriter : maple_base
|
||||
return (MapleDeviceRV)0xfe;
|
||||
}
|
||||
|
||||
case 0xD1: // decrement counter
|
||||
{
|
||||
int counter = r8();
|
||||
switch (counter) {
|
||||
case 0x03:
|
||||
counter = 0;
|
||||
break;
|
||||
case 0x0c:
|
||||
counter = 1;
|
||||
break;
|
||||
case 0x30:
|
||||
counter = 2;
|
||||
break;
|
||||
case 0xc0:
|
||||
counter = 3;
|
||||
break;
|
||||
default:
|
||||
WARN_LOG(MAPLE, "Unknown counter selector %x", counter);
|
||||
counter = 0;
|
||||
break;
|
||||
}
|
||||
DEBUG_LOG(MAPLE, "RFID decrement %d", counter);
|
||||
cardData[19 - counter]--;
|
||||
saveCard();
|
||||
w32(getStatus());
|
||||
return (MapleDeviceRV)0xfe;
|
||||
}
|
||||
|
||||
default:
|
||||
INFO_LOG(MAPLE, "RFIDReaderWriter: unknown MAPLE COMMAND %d", cmd);
|
||||
return MDRE_UnknownCmd;
|
||||
@@ -1795,24 +1824,62 @@ struct RFIDReaderWriter : maple_base
|
||||
FILE *fp = nowide::fopen(path.c_str(), "rb");
|
||||
if (fp == nullptr)
|
||||
{
|
||||
static u8 blankCard[128] = {
|
||||
0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,0x6c, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0xff
|
||||
};
|
||||
// Generate random bytes used by vf4 vanilla to make the card id
|
||||
srand(time(0));
|
||||
blankCard[2] = rand() & 0xff;
|
||||
blankCard[4] = rand() & 0xff;
|
||||
blankCard[5] = rand() & 0xff;
|
||||
blankCard[6] = rand() & 0xff;
|
||||
blankCard[7] = rand() & 0xff;
|
||||
memcpy(cardData, blankCard, sizeof(blankCard));
|
||||
if (settings.content.gameId.substr(0, 8) == "MKG TKOB")
|
||||
{
|
||||
constexpr u8 MUSHIKING_CHIP_DATA[128] = {
|
||||
0x12, 0x34, 0x56, 0x78, // Serial No.0
|
||||
0x31, 0x00, 0x86, 0x07, // Serial No.1
|
||||
0x00, 0x00, 0x00, 0x00, // Key
|
||||
0x04, 0xf6, 0x00, 0xAA, // Extend Extend Access Mode
|
||||
0x23, 0xFF, 0xFF, 0xFF, // Counter4 Counter3 Counter2 Counter1
|
||||
0x00, 0x00, 0x00, 0x00, // User Data (first set date: day bits 0-4, month bits 5-8, year bits 9-... + 2000)
|
||||
0x00, 0x00, 0x00, 0x00, // User Data
|
||||
0x00, 0x00, 0x00, 0x00, // User Data
|
||||
0x00, 0x00, 0x00, 0x00, // User Data
|
||||
0x00, 0x00, 0x00, 0x00, // User Data
|
||||
0x00, 0x00, 0x00, 0x00, // User Data
|
||||
0x23, 0xFF, 0xFF, 0xFF, // User Data (max counters)
|
||||
};
|
||||
memcpy(cardData, MUSHIKING_CHIP_DATA, sizeof(MUSHIKING_CHIP_DATA));
|
||||
for (int i = 0; i < 8; i++)
|
||||
cardData[i] = rand() & 0xff;
|
||||
u32 mask = 0;
|
||||
if (settings.content.gameId == "MKG TKOB 2 JPN VER2.001-" // mushik2e
|
||||
|| settings.content.gameId == "MKG TKOB 4 JPN VER2.000-") // mushik4e
|
||||
mask = 0x40;
|
||||
cardData[4] &= ~0xc0;
|
||||
cardData[4] |= mask;
|
||||
|
||||
u32 serial1 = (cardData[4] << 24) | (cardData[5] << 16) | (cardData[6] << 8) | cardData[7];
|
||||
u32 key = ~serial1;
|
||||
key = ((key >> 4) & 0x0f0f0f0f)
|
||||
| ((key << 4) & 0xf0f0f0f0);
|
||||
cardData[8] = key >> 24;
|
||||
cardData[9] = key >> 16;
|
||||
cardData[10] = key >> 8;
|
||||
cardData[11] = key;
|
||||
}
|
||||
else
|
||||
{
|
||||
constexpr u8 VF4_CARD_DATA[128] = {
|
||||
0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,0x6c, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0xff
|
||||
};
|
||||
memcpy(cardData, VF4_CARD_DATA, sizeof(VF4_CARD_DATA));
|
||||
// Generate random bytes used by vf4 vanilla to make the card id
|
||||
srand(time(0));
|
||||
cardData[2] = rand() & 0xff;
|
||||
cardData[4] = rand() & 0xff;
|
||||
cardData[5] = rand() & 0xff;
|
||||
cardData[6] = rand() & 0xff;
|
||||
cardData[7] = rand() & 0xff;
|
||||
}
|
||||
INFO_LOG(NAOMI, "Card P%d initialized", player_num + 1);
|
||||
}
|
||||
else
|
||||
|
||||
+17
-22
@@ -47,34 +47,29 @@ void maple_vblank()
|
||||
{
|
||||
if (SB_MDEN & 1)
|
||||
{
|
||||
if (SDCKBOccupied)
|
||||
if (SB_MDTSEL == 1)
|
||||
{
|
||||
maple_schd(0, 0, 0, nullptr);
|
||||
SDCKBOccupied = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (SB_MDTSEL == 1)
|
||||
{
|
||||
if (maple_ddt_pending_reset)
|
||||
{
|
||||
DEBUG_LOG(MAPLE, "DDT vblank ; reset pending");
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_LOG(MAPLE, "DDT vblank");
|
||||
SB_MDST = 1;
|
||||
maple_DoDma();
|
||||
// if trigger reset is manual, mark it as pending
|
||||
if ((SB_MSYS >> 12) & 1)
|
||||
maple_ddt_pending_reset = true;
|
||||
}
|
||||
// Hardware trigger on vblank
|
||||
if (maple_ddt_pending_reset) {
|
||||
DEBUG_LOG(MAPLE, "DDT vblank ; reset pending");
|
||||
}
|
||||
else
|
||||
{
|
||||
maple_ddt_pending_reset = false;
|
||||
DEBUG_LOG(MAPLE, "DDT vblank");
|
||||
SB_MDST = 1;
|
||||
maple_DoDma();
|
||||
// if trigger reset is manual, mark it as pending
|
||||
if ((SB_MSYS >> 12) & 1)
|
||||
maple_ddt_pending_reset = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
maple_ddt_pending_reset = false;
|
||||
if (SDCKBOccupied)
|
||||
maple_schd(0, 0, 0, nullptr);
|
||||
}
|
||||
SDCKBOccupied = false;
|
||||
}
|
||||
if (settings.platform.isConsole())
|
||||
maple_handle_reconnect();
|
||||
|
||||
+41
-17
@@ -725,6 +725,10 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void setTokyoBusMode(bool enable) {
|
||||
tokyoBus = enable;
|
||||
}
|
||||
|
||||
protected:
|
||||
u8 process(u8 in) override
|
||||
{
|
||||
@@ -758,7 +762,7 @@ protected:
|
||||
break;
|
||||
|
||||
case 0xf1:
|
||||
out = 0x10; // needed by f355
|
||||
out = 0x10; // needed by f355 and tokyobus
|
||||
break;
|
||||
|
||||
case 0xfa:
|
||||
@@ -779,20 +783,31 @@ protected:
|
||||
{
|
||||
if (in >= 0x40 && in <= 0x7f)
|
||||
{
|
||||
// Spring
|
||||
// bits 0-3 sets the strength of the spring effect
|
||||
// bits 4-5 selects a table scaling the effect given the deflection:
|
||||
// (from the f355 rom EPR-21867)
|
||||
// 0: large deadzone then abrupt scaling (0 (* 129), 10, 20, 30, 40, 50, 60, 70, 7F)
|
||||
// used by 18wheeler in game but with a different rom (TODO reveng)
|
||||
// other tables scale linearly:
|
||||
// 1: light speed (96 steps from 0 to 7f)
|
||||
// 2: medium speed (48 steps, default)
|
||||
// 3: high speed (32 steps)
|
||||
springSat = (in & 0xf) / 15.f * motorPower;
|
||||
const int speedSel = (in >> 4) & 3;
|
||||
springSpeed = speedSel == 3 ? 1.f : speedSel == 2 ? 0.67f : speedSel == 1 ? 0.33f : 0.67f;
|
||||
haptic::setSpring(0, springSat, springSpeed);
|
||||
if (tokyoBus)
|
||||
{
|
||||
if (in <= 0x4a)
|
||||
// heavy: 4a
|
||||
// normal: 47
|
||||
// light: 44
|
||||
motorPower = (in & 0xf) / 10.f;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Spring
|
||||
// bits 0-3 sets the strength of the spring effect
|
||||
// bits 4-5 selects a table scaling the effect given the deflection:
|
||||
// (from the f355 rom EPR-21867)
|
||||
// 0: large deadzone then abrupt scaling (0 (* 129), 10, 20, 30, 40, 50, 60, 70, 7F)
|
||||
// used by 18wheeler in game but with a different rom (TODO reveng)
|
||||
// other tables scale linearly:
|
||||
// 1: light speed (96 steps from 0 to 7f)
|
||||
// 2: medium speed (48 steps, default)
|
||||
// 3: high speed (32 steps)
|
||||
springSat = (in & 0xf) / 15.f * motorPower;
|
||||
const int speedSel = (in >> 4) & 3;
|
||||
springSpeed = speedSel == 3 ? 1.f : speedSel == 2 ? 0.67f : speedSel == 1 ? 0.33f : 0.67f;
|
||||
haptic::setSpring(0, springSat, springSpeed);
|
||||
}
|
||||
}
|
||||
else if (in >= 0x80 && in <= 0xbf)
|
||||
{
|
||||
@@ -841,6 +856,7 @@ private:
|
||||
float springSpeed = 0.f;
|
||||
float torque = 0.f;
|
||||
float damper = 0.f;
|
||||
bool tokyoBus = false;
|
||||
};
|
||||
|
||||
// 18 Wheeler: fake the drive board and limit the wheel analog value
|
||||
@@ -1283,10 +1299,12 @@ maple_naomi_jamma::maple_naomi_jamma()
|
||||
INFO_LOG(MAPLE, "Enabling specific JVS setup for game %s", gameId.c_str());
|
||||
io_boards.push_back(std::make_unique<jvs_837_13844_18wheeler>(1, this));
|
||||
}
|
||||
else if (gameId == "F355 CHALLENGE JAPAN")
|
||||
else if (gameId == "F355 CHALLENGE JAPAN" || gameId == "TOKYO BUS GUIDE")
|
||||
{
|
||||
INFO_LOG(MAPLE, "Enabling specific JVS setup for game %s", gameId.c_str());
|
||||
io_boards.push_back(std::make_unique<jvs_837_13844_racing>(1, this));
|
||||
if (gameId == "TOKYO BUS GUIDE")
|
||||
static_cast<jvs_837_13844_racing *>(io_boards.back().get())->setTokyoBusMode(true);
|
||||
}
|
||||
else if (gameId.substr(0, 8) == "MKG TKOB"
|
||||
|| gameId.substr(0, 9) == "MUSHIKING"
|
||||
@@ -2249,7 +2267,7 @@ u32 jvs_io_board::handle_jvs_message(u8 *buffer_in, u32 length_in, u8 *buffer_ou
|
||||
}
|
||||
else
|
||||
{
|
||||
axis_value = read_analog_axis(player_num, axisDesc.axis, axisDesc.inverted);
|
||||
axis_value = read_analog_axis(player_num, axisDesc.axis, axisDesc.inverted);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -2262,6 +2280,12 @@ u32 jvs_io_board::handle_jvs_message(u8 *buffer_in, u32 length_in, u8 *buffer_ou
|
||||
axis_value = read_analog_axis(player_num, player_axis, false);
|
||||
}
|
||||
LOGJVS("%d:%4x ", axis, axis_value);
|
||||
// Strangely, the least significant byte appears to be handled as signed,
|
||||
// so we compensate when it's negative.
|
||||
// Avoid overflow (wild riders)
|
||||
axis_value = std::min<u16>(0xff7f, axis_value);
|
||||
if (axis_value & 0x80)
|
||||
axis_value += 0x100;
|
||||
JVS_OUT(axis_value >> 8);
|
||||
JVS_OUT(axis_value);
|
||||
}
|
||||
|
||||
@@ -631,13 +631,6 @@ void *GDCartridge::GetDmaPtr(u32 &size)
|
||||
return dimm_data + dimm_cur_address;
|
||||
}
|
||||
|
||||
void GDCartridge::AdvancePtr(u32 size)
|
||||
{
|
||||
dimm_cur_address += size;
|
||||
if(dimm_cur_address >= dimm_data_size)
|
||||
dimm_cur_address %= dimm_data_size;
|
||||
}
|
||||
|
||||
bool GDCartridge::Read(u32 offset, u32 size, void *dst)
|
||||
{
|
||||
if (dimm_data == NULL)
|
||||
|
||||
@@ -31,7 +31,6 @@ public:
|
||||
device_reset();
|
||||
}
|
||||
void* GetDmaPtr(u32 &size) override;
|
||||
void AdvancePtr(u32 size) override;
|
||||
bool Read(u32 offset, u32 size, void* dst) override;
|
||||
|
||||
void SetGDRomName(const char *name, const char *parentName) { this->gdrom_name = name; this->gdrom_parent_name = parentName; }
|
||||
|
||||
@@ -165,9 +165,9 @@ void *M4Cartridge::GetDmaPtr(u32 &size)
|
||||
if (cfi_mode) {
|
||||
u32 fpr_num = m4id & 0x7f;
|
||||
|
||||
if (((rom_cur_address >> 26) & 0x07) < fpr_num) {
|
||||
if (((DmaOffset >> 26) & 0x07) < fpr_num) {
|
||||
size = std::min(size, 2u);
|
||||
return &cfidata[rom_cur_address & 0xffff];
|
||||
return &cfidata[DmaOffset & 0xffff];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,9 +184,8 @@ void *M4Cartridge::GetDmaPtr(u32 &size)
|
||||
}
|
||||
if (encryption)
|
||||
{
|
||||
size = std::min(size, (u32)sizeof(buffer));
|
||||
size = std::min(size, buffer_actual_size);
|
||||
return buffer;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -207,17 +206,18 @@ void M4Cartridge::AdvancePtr(u32 size)
|
||||
{
|
||||
if (encryption)
|
||||
{
|
||||
if (size < buffer_actual_size)
|
||||
{
|
||||
if (size < buffer_actual_size) {
|
||||
memmove(buffer, buffer + size, buffer_actual_size - size);
|
||||
buffer_actual_size -= size;
|
||||
}
|
||||
else
|
||||
else {
|
||||
buffer_actual_size = 0;
|
||||
}
|
||||
enc_fill();
|
||||
}
|
||||
else
|
||||
rom_cur_address += size;
|
||||
else {
|
||||
NaomiCartridge::AdvancePtr(size);
|
||||
}
|
||||
}
|
||||
|
||||
void M4Cartridge::enc_reset()
|
||||
|
||||
@@ -127,6 +127,11 @@ void Multiboard::startSlave()
|
||||
int x = cfgLoadInt("window", "left", (1920 - 640) / 2);
|
||||
int y = cfgLoadInt("window", "top", (1080 - 480) / 2);
|
||||
int width = cfgLoadInt("window", "width", 640);
|
||||
std::string biosFile = CurrentCartridge->game->bios == nullptr ? "naomi" : CurrentCartridge->game->bios;
|
||||
std::string biosPath = hostfs::findNaomiBios(biosFile + ".zip");
|
||||
if (biosPath.empty())
|
||||
biosPath = hostfs::findNaomiBios(biosFile + ".7z");
|
||||
|
||||
for (int i = 0; i < slaves; i++)
|
||||
{
|
||||
std::string region = "config:Dreamcast.Region=" + std::to_string(config::Region);
|
||||
@@ -143,7 +148,7 @@ void Multiboard::startSlave()
|
||||
"-config", region.c_str(),
|
||||
"-config", left.c_str(),
|
||||
"-config", top.c_str(),
|
||||
CurrentCartridge->game->bios == nullptr ? "naomi" : CurrentCartridge->game->bios
|
||||
biosPath.c_str()
|
||||
};
|
||||
os_RunInstance(std::size(args), args);
|
||||
}
|
||||
|
||||
+26
-12
@@ -44,6 +44,7 @@ static X76F100SerialFlash mainSerialId;
|
||||
static X76F100SerialFlash romSerialId;
|
||||
|
||||
static int dmaSchedId = -1;
|
||||
static int dmaXferDelay = 10; // cart dma xfer speed, in cycles/byte (default 20 MB/s)
|
||||
|
||||
void NaomiBoardIDWrite(const u16 data)
|
||||
{
|
||||
@@ -111,9 +112,9 @@ void WriteMem_naomi(u32 address, u32 data, u32 size)
|
||||
|
||||
static int naomiDmaSched(int tag, int sch_cycl, int jitter, void *arg)
|
||||
{
|
||||
u32 start = SB_GDSTAR & 0x1FFFFFE0;
|
||||
u32 len = (SB_GDLEN + 31) & ~31;
|
||||
SB_GDLEND = 0;
|
||||
u32 start = SB_GDSTARD;
|
||||
u32 len = std::min<int>(((SB_GDLEN + 31) & ~31) - SB_GDLEND, 1024);
|
||||
SB_GDLEND += len;
|
||||
while (len > 0)
|
||||
{
|
||||
u32 block_len = len;
|
||||
@@ -123,20 +124,23 @@ static int naomiDmaSched(int tag, int sch_cycl, int jitter, void *arg)
|
||||
INFO_LOG(NAOMI, "Aborted DMA transfer. Read past end of cart?");
|
||||
for (u32 i = 0; i < len; i += 8, start += 8)
|
||||
addrspace::write64(start, 0);
|
||||
SB_GDLEND += len;
|
||||
break;
|
||||
}
|
||||
WriteMemBlock_nommu_ptr(start, (u32*)ptr, block_len);
|
||||
CurrentCartridge->AdvancePtr(block_len);
|
||||
len -= block_len;
|
||||
start += block_len;
|
||||
SB_GDLEND += block_len;
|
||||
}
|
||||
SB_GDSTARD = start;
|
||||
SB_GDST = 0;
|
||||
asic_RaiseInterrupt(holly_GDROM_DMA);
|
||||
|
||||
return 0;
|
||||
if (SB_GDLEN <= SB_GDLEND)
|
||||
{
|
||||
SB_GDST = 0;
|
||||
asic_RaiseInterrupt(holly_GDROM_DMA);
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
return std::min<int>(SB_GDLEN - SB_GDLEND, 1024) * dmaXferDelay;
|
||||
}
|
||||
}
|
||||
|
||||
//Dma Start
|
||||
@@ -155,13 +159,15 @@ static void Naomi_DmaStart(u32 addr, u32 data)
|
||||
}
|
||||
else if (!m3comm.DmaStart(addr, data) && CurrentCartridge != nullptr)
|
||||
{
|
||||
DEBUG_LOG(NAOMI, "NAOMI-DMA start addr %08X len %d", SB_GDSTAR, SB_GDLEN);
|
||||
DEBUG_LOG(NAOMI, "NAOMI-DMA start addr %08X len %x", SB_GDSTAR, SB_GDLEN);
|
||||
verify(1 == SB_GDDIR);
|
||||
SB_GDST = 1;
|
||||
SB_GDSTARD = SB_GDSTAR & 0x1FFFFFE0;
|
||||
SB_GDLEND = 0;
|
||||
// Max G1 bus rate: 50 MHz x 16 bits
|
||||
// SH4_access990312_e.xls: 14.4 MB/s from GD-ROM to system RAM
|
||||
// Here: 7 MB/s
|
||||
sh4_sched_request(dmaSchedId, std::min<int>(SB_GDLEN * 27, SH4_MAIN_CLOCK));
|
||||
// Here: 20 MB/s
|
||||
sh4_sched_request(dmaSchedId, std::min<int>(SB_GDLEN, 1024) * dmaXferDelay);
|
||||
return;
|
||||
}
|
||||
else
|
||||
@@ -172,6 +178,14 @@ static void Naomi_DmaStart(u32 addr, u32 data)
|
||||
asic_RaiseInterrupt(holly_GDROM_DMA);
|
||||
}
|
||||
|
||||
void Naomi_setDmaDelay()
|
||||
{
|
||||
if (settings.content.gameId == "FORCE FIVE")
|
||||
// 7 MB/s
|
||||
dmaXferDelay = 27;
|
||||
else
|
||||
dmaXferDelay = 10;
|
||||
}
|
||||
|
||||
static void Naomi_DmaEnable(u32 addr, u32 data)
|
||||
{
|
||||
|
||||
@@ -21,6 +21,7 @@ void NaomiGameIDWrite(u16 data);
|
||||
void setGameSerialId(const u8 *data);
|
||||
|
||||
void initDriveSimSerialPipe();
|
||||
void Naomi_setDmaDelay();
|
||||
|
||||
namespace midiffb {
|
||||
|
||||
|
||||
@@ -56,22 +56,40 @@ bool atomiswaveForceFeedback;
|
||||
|
||||
static bool loadBios(const char *filename, Archive *child_archive, Archive *parent_archive, int region)
|
||||
{
|
||||
std::string path;
|
||||
std::string biosName;
|
||||
if (settings.naomi.slave)
|
||||
{
|
||||
// extract basename of bios
|
||||
biosName = get_file_basename(filename);
|
||||
size_t idx = get_last_slash_pos(biosName);
|
||||
if (idx != std::string::npos)
|
||||
biosName = biosName.substr(idx + 1);
|
||||
path = filename;
|
||||
}
|
||||
else
|
||||
{
|
||||
biosName = filename;
|
||||
}
|
||||
int biosid = 0;
|
||||
for (; BIOS[biosid].name != nullptr; biosid++)
|
||||
if (!stricmp(BIOS[biosid].name, filename))
|
||||
if (!stricmp(BIOS[biosid].name, biosName.c_str()))
|
||||
break;
|
||||
if (BIOS[biosid].name == nullptr)
|
||||
{
|
||||
WARN_LOG(NAOMI, "Unknown BIOS %s", filename);
|
||||
WARN_LOG(NAOMI, "Unknown BIOS %s", biosName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
const BIOS_t *bios = &BIOS[biosid];
|
||||
|
||||
std::string arch_name(bios->filename != nullptr ? bios->filename : filename);
|
||||
std::string path = hostfs::findNaomiBios(arch_name + ".zip");
|
||||
if (path.empty())
|
||||
path = hostfs::findNaomiBios(arch_name + ".7z");
|
||||
{
|
||||
std::string arch_name(bios->filename != nullptr ? bios->filename : filename);
|
||||
path = hostfs::findNaomiBios(arch_name + ".zip");
|
||||
if (path.empty())
|
||||
path = hostfs::findNaomiBios(arch_name + ".7z");
|
||||
}
|
||||
DEBUG_LOG(NAOMI, "Loading BIOS from %s", path.c_str());
|
||||
std::unique_ptr<Archive> bios_archive(OpenArchive(path));
|
||||
|
||||
@@ -221,8 +239,8 @@ static void loadMameRom(const std::string& path, const std::string& fileName, Lo
|
||||
std::unique_ptr<Archive> parent_archive;
|
||||
if (game->parent_name != nullptr)
|
||||
{
|
||||
std::string parentPath = hostfs::storage().getParentPath(path);
|
||||
try {
|
||||
std::string parentPath = hostfs::storage().getParentPath(path);
|
||||
parentPath = hostfs::storage().getSubPath(parentPath, game->parent_name);
|
||||
parent_archive.reset(OpenArchive(parentPath));
|
||||
} catch (const FlycastException& e) {
|
||||
@@ -230,7 +248,7 @@ static void loadMameRom(const std::string& path, const std::string& fileName, Lo
|
||||
if (parent_archive != nullptr)
|
||||
INFO_LOG(NAOMI, "Opened %s", game->parent_name);
|
||||
else
|
||||
WARN_LOG(NAOMI, "Parent not found: %s", parentPath.c_str());
|
||||
WARN_LOG(NAOMI, "Parent not found: %s", game->parent_name);
|
||||
|
||||
}
|
||||
|
||||
@@ -704,6 +722,7 @@ void naomi_cart_LoadRom(const std::string& path, const std::string& fileName, Lo
|
||||
{
|
||||
hopper::init();
|
||||
}
|
||||
Naomi_setDmaDelay();
|
||||
|
||||
#ifdef NAOMI_MULTIBOARD
|
||||
// Not a multiboard game but needs the same desktop environment
|
||||
|
||||
@@ -86,7 +86,9 @@ public:
|
||||
u32 ReadMem(u32 address, u32 size) override;
|
||||
void WriteMem(u32 address, u32 data, u32 size) override;
|
||||
void* GetDmaPtr(u32 &size) override;
|
||||
void AdvancePtr(u32 size) override {}
|
||||
void AdvancePtr(u32 size) override {
|
||||
DmaOffset += size;
|
||||
}
|
||||
void Serialize(Serializer& ser) const override;
|
||||
void Deserialize(Deserializer& deser) override;
|
||||
bool GetBootId(RomBootID *bootId) override;
|
||||
|
||||
@@ -38,9 +38,12 @@ const BIOS_t BIOS[] =
|
||||
{
|
||||
"awbios",
|
||||
{
|
||||
// Sammy version
|
||||
{ 0, "bios0.ic23", 0x000000, 0x020000 },
|
||||
//or
|
||||
//{ "bios1.ic23", 0x000000, 0x020000 },
|
||||
//{ 0, "bios1.ic23", 0x000000, 0x020000 },
|
||||
// Sega version
|
||||
//{ 0, "fpr-24363.ic48", 0x000000, 0x020000 },
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -2489,7 +2492,7 @@ const Game Games[] =
|
||||
nullptr,
|
||||
// no free play with eeprom
|
||||
},
|
||||
// Mushiking The King Of Beetles - Mushiking IV / V / VI (World)
|
||||
// Mushiking The King Of Beetles 2004 Second (Japan)
|
||||
{
|
||||
"mushi2k4",
|
||||
NULL,
|
||||
@@ -4722,7 +4725,7 @@ const Game Games[] =
|
||||
{ NULL, 0, 0 },
|
||||
}
|
||||
},
|
||||
// MushiKing II - The King Of Beetle II ENG (Ver. 1.001)
|
||||
// Mushiking The King Of Beetles - Mushiking II / III / III+ (World, Ver. 2.001)
|
||||
{
|
||||
"mushik2e",
|
||||
NULL,
|
||||
@@ -4746,6 +4749,7 @@ const Game Games[] =
|
||||
},
|
||||
// Mushiking The King Of Beetles - Mushiking IV / V / VI (World)
|
||||
// change game version (4/5/6): in BACKUP DATA CLEAR menu hold P1 and P2 buttons 1 for 3 seconds, then change version number in appeared menu and select YES(CLEAR)
|
||||
// ~equivalent of Japanese 2K6 versions
|
||||
{
|
||||
"mushik4e",
|
||||
NULL,
|
||||
@@ -4756,14 +4760,31 @@ const Game Games[] =
|
||||
M4,
|
||||
ROT0,
|
||||
{
|
||||
{ "fpr-24417.ic8", 0x0000000, 0x4000000 },
|
||||
{ "fpr-24418.ic9", 0x4000000, 0x4000000 },
|
||||
{ "fpr-24417.ic8", 0x0000000, 0x4000000, 0x01278cbb },
|
||||
{ "fpr-24418.ic9", 0x4000000, 0x4000000, 0x7c8d7687 },
|
||||
|
||||
//ROM_REGION( 0x800, "pic_readout", 0 )
|
||||
//ROM_LOAD( "317-0437-com.ic3", 0, 0x800, CRC(3b6fcee8) SHA1(65fbdd3b8c61a4b5ccb6389b25483a7ecdc0794d) )
|
||||
{ "317-0437-com.ic3", 0, 0x800, 0x0000000, Key },
|
||||
{ "317-0437-com.ic3", 0, 0x800, 0x3b6fcee8, Key },
|
||||
}
|
||||
},
|
||||
// Mushiking The King Of Beetles - Mushiking IV / V / VI (Taiwan)
|
||||
// change game version (4/5/6): in BACKUP DATA CLEAR menu hold P1 and P2 buttons 1 for 3 seconds, then change version number in appeared menu and select YES(CLEAR)
|
||||
// ~equivalent of Japanese 2K6 versions
|
||||
{
|
||||
"mushik4t",
|
||||
"mushik4e",
|
||||
"Mushiking The King Of Beetles - Mushiking IV / V / VI (Taiwan)",
|
||||
0x8000000,
|
||||
0x5502,
|
||||
"naomi",
|
||||
M4,
|
||||
ROT0,
|
||||
{
|
||||
{ "fpr-24420.ic8", 0x0000000, 0x4000000, 0x4a44a604 },
|
||||
{ "fpr-24421.ic9", 0x4000000, 0x4000000, 0xaf137021 }, // BAD DUMP
|
||||
|
||||
{ NULL, 0, 0 },
|
||||
//ROM_REGION( 0x800, "pic_readout", 0 )
|
||||
{ "317-0437-com.ic3", 0, 0x800, 0x3b6fcee8, Key },
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -716,10 +716,11 @@ static InputDescriptors drvsim_inputs = {
|
||||
// WIPER-HI
|
||||
},
|
||||
{
|
||||
{ "Master", Full, 0, true },
|
||||
{ "", Full, ~0u, true }, // Master audio volume
|
||||
{ "Accelerator", Half, 4 },
|
||||
{ "Brake", Half, 5 },
|
||||
{ "Clutch", Full, 1 },
|
||||
{ "Clutch", Half, 6 },
|
||||
{ "Wheel", Full, 0, true }, // only read via ffb board, not jvs
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
+52
-59
@@ -983,7 +983,7 @@ public:
|
||||
tri.x2 * curMatrix[0][2] + tri.y2 * curMatrix[1][2] + tri.z2 * curMatrix[2][2] + curMatrix[3][2]
|
||||
};
|
||||
dist = -dist - nearPlane;
|
||||
ModTriangle newTri;
|
||||
ModTriangle newTri[2];
|
||||
int n = sutherlandHodgmanClip(dist, tri, newTri);
|
||||
switch (n)
|
||||
{
|
||||
@@ -993,9 +993,10 @@ public:
|
||||
case 3:
|
||||
ta_add_triangle(tri);
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
ta_add_triangle(tri);
|
||||
ta_add_triangle(newTri);
|
||||
ta_add_triangle(newTri[0]);
|
||||
ta_add_triangle(newTri[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1016,86 +1017,78 @@ private:
|
||||
}
|
||||
|
||||
// Clip the triangle 'trig' with respect to the provided distances to the clipping plane.
|
||||
int sutherlandHodgmanClip(glm::vec3& dist, ModTriangle& trig, ModTriangle& newTrig)
|
||||
int sutherlandHodgmanClip(glm::vec3& dist, ModTriangle& trig, ModTriangle *newTrig)
|
||||
{
|
||||
constexpr float clipEpsilon = 0.f; //0.00001;
|
||||
constexpr float clipEpsilon2 = 0.f; //0.01;
|
||||
|
||||
if (!glm::any(glm::greaterThanEqual(dist , glm::vec3(clipEpsilon2))))
|
||||
// all clipped
|
||||
return 0;
|
||||
if (!glm::any(glm::greaterThanEqual(dist , glm::vec3(clipEpsilon2)))) {
|
||||
// all clipped: leave it alone as it will be projected onto the near plane in the shader
|
||||
return 3;
|
||||
}
|
||||
if (glm::all(glm::greaterThanEqual(dist , glm::vec3(-clipEpsilon))))
|
||||
// none clipped
|
||||
return 3;
|
||||
|
||||
// There are either 1 or 2 vertices above the clipping plane.
|
||||
// There are either 1 or 2 vertices above the clipping plane. Tesselate into 3 triangles along the plane.
|
||||
glm::bvec3 above = glm::greaterThanEqual(dist, glm::vec3(0.f));
|
||||
bool nextIsAbove;
|
||||
glm::vec3 v0(trig.x0, trig.y0, trig.z0);
|
||||
glm::vec3 v1(trig.x1, trig.y1, trig.z1);
|
||||
glm::vec3 v2(trig.x2, trig.y2, trig.z2);
|
||||
glm::vec3 v3;
|
||||
// Find the CCW-most vertex above the plane.
|
||||
if (above[1] && !above[0])
|
||||
glm::vec3 v3, v4;
|
||||
// Find the lonely vertex on one side of the plane
|
||||
if (above[0] == above[2])
|
||||
{
|
||||
// Cycle once CCW. Use v3 as a temp
|
||||
nextIsAbove = above[2];
|
||||
// this is vertex 1 so cycle CCW
|
||||
v3 = v0;
|
||||
v0 = v1;
|
||||
v1 = v2;
|
||||
v2 = v3;
|
||||
dist = glm::vec3(dist.y, dist.z, dist.x);
|
||||
}
|
||||
else if (above[2] && !above[1])
|
||||
else if (above[0] == above[1])
|
||||
{
|
||||
// Cycle once CW. Use v3 as a temp.
|
||||
nextIsAbove = above[0];
|
||||
// this is vertex 2 so cycle CW
|
||||
v3 = v2;
|
||||
v2 = v1;
|
||||
v1 = v0;
|
||||
v0 = v3;
|
||||
dist = glm::vec3(dist.z, dist.x, dist.y);
|
||||
}
|
||||
else
|
||||
nextIsAbove = above[1];
|
||||
v3 = intersect(v0, dist[0], v1, dist[1]);
|
||||
v4 = intersect(v0, dist[0], v2, dist[2]);
|
||||
// v0 v3 v4
|
||||
trig.x0 = v0.x;
|
||||
trig.y0 = v0.y;
|
||||
trig.z0 = v0.z;
|
||||
// We always need to clip v2-v0.
|
||||
v3 = intersect(v0, dist[0], v2, dist[2]);
|
||||
if (nextIsAbove)
|
||||
{
|
||||
v2 = intersect(v1, dist[1], v2, dist[2]);
|
||||
trig.x1 = v1.x;
|
||||
trig.y1 = v1.y;
|
||||
trig.z1 = v1.z;
|
||||
trig.x2 = v2.x;
|
||||
trig.y2 = v2.y;
|
||||
trig.z2 = v2.z;
|
||||
newTrig.x0 = v0.x;
|
||||
newTrig.y0 = v0.y;
|
||||
newTrig.z0 = v0.z;
|
||||
newTrig.x1 = v2.x;
|
||||
newTrig.y1 = v2.y;
|
||||
newTrig.z1 = v2.z;
|
||||
newTrig.x2 = v3.x;
|
||||
newTrig.y2 = v3.y;
|
||||
newTrig.z2 = v3.z;
|
||||
trig.x1 = v3.x;
|
||||
trig.y1 = v3.y;
|
||||
trig.z1 = v3.z;
|
||||
trig.x2 = v4.x;
|
||||
trig.y2 = v4.y;
|
||||
trig.z2 = v4.z;
|
||||
// v3 v1 v4
|
||||
newTrig[0].x0 = v3.x;
|
||||
newTrig[0].y0 = v3.y;
|
||||
newTrig[0].z0 = v3.z;
|
||||
newTrig[0].x1 = v1.x;
|
||||
newTrig[0].y1 = v1.y;
|
||||
newTrig[0].z1 = v1.z;
|
||||
newTrig[0].x2 = v4.x;
|
||||
newTrig[0].y2 = v4.y;
|
||||
newTrig[0].z2 = v4.z;
|
||||
// v2 v4 v1
|
||||
newTrig[1].x0 = v2.x;
|
||||
newTrig[1].y0 = v2.y;
|
||||
newTrig[1].z0 = v2.z;
|
||||
newTrig[1].x1 = v4.x;
|
||||
newTrig[1].y1 = v4.y;
|
||||
newTrig[1].z1 = v4.z;
|
||||
newTrig[1].x2 = v1.x;
|
||||
newTrig[1].y2 = v1.y;
|
||||
newTrig[1].z2 = v1.z;
|
||||
|
||||
return 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
v1 = intersect(v0, dist[0], v1, dist[1]);
|
||||
trig.x1 = v1.x;
|
||||
trig.y1 = v1.y;
|
||||
trig.z1 = v1.z;
|
||||
trig.x2 = v3.x;
|
||||
trig.y2 = v3.y;
|
||||
trig.z2 = v3.z;
|
||||
|
||||
return 3;
|
||||
}
|
||||
return 5;
|
||||
}
|
||||
|
||||
bool enabled;
|
||||
@@ -1327,15 +1320,15 @@ static void sendPolygon(ICHList *list)
|
||||
case ICHList::VTX_TYPE_V:
|
||||
{
|
||||
N2_VERTEX *vtx = (N2_VERTEX *)((u8 *)list + sizeof(ICHList));
|
||||
if (!isBetweenNearAndFar(vtx, list->vtxCount, needClipping))
|
||||
break;
|
||||
int listType = ta_get_list_type();
|
||||
if (listType == -1)
|
||||
listType = list->pcw.listType;
|
||||
if (listType & 1)
|
||||
sendMVPolygon(list, vtx, needClipping);
|
||||
sendMVPolygon(list, vtx, true);
|
||||
else
|
||||
{
|
||||
if (!isBetweenNearAndFar(vtx, list->vtxCount, needClipping))
|
||||
break;
|
||||
PolyParam pp{};
|
||||
pp.pcw.Shadow = list->pcw.shadow;
|
||||
pp.pcw.Texture = 0;
|
||||
@@ -1356,15 +1349,15 @@ static void sendPolygon(ICHList *list)
|
||||
case ICHList::VTX_TYPE_VU:
|
||||
{
|
||||
N2_VERTEX_VU *vtx = (N2_VERTEX_VU *)((u8 *)list + sizeof(ICHList));
|
||||
if (!isBetweenNearAndFar(vtx, list->vtxCount, needClipping))
|
||||
break;
|
||||
int listType = ta_get_list_type();
|
||||
if (listType == -1)
|
||||
listType = list->pcw.listType;
|
||||
if (listType & 1)
|
||||
sendMVPolygon(list, vtx, needClipping);
|
||||
sendMVPolygon(list, vtx, true);
|
||||
else
|
||||
{
|
||||
if (!isBetweenNearAndFar(vtx, list->vtxCount, needClipping))
|
||||
break;
|
||||
PolyParam pp{};
|
||||
pp.pcw.Shadow = list->pcw.shadow;
|
||||
pp.pcw.Texture = list->pcw.texture;
|
||||
|
||||
@@ -219,10 +219,10 @@ template u32 pvr_read32p<u32>(u32 addr);
|
||||
template float pvr_read32p<float>(u32 addr);
|
||||
|
||||
//write
|
||||
template<typename T>
|
||||
template<typename T, bool Internal>
|
||||
void DYNACALL pvr_write32p(u32 addr, T data)
|
||||
{
|
||||
if constexpr (sizeof(T) == 1)
|
||||
if constexpr (!Internal && sizeof(T) == 1)
|
||||
{
|
||||
INFO_LOG(MEMORY, "%08x: 8-bit VRAM writes are not possible", addr);
|
||||
return;
|
||||
@@ -234,9 +234,12 @@ void DYNACALL pvr_write32p(u32 addr, T data)
|
||||
|
||||
*(T *)&vram[pvr_map32(addr)] = data;
|
||||
}
|
||||
template void pvr_write32p<u8>(u32 addr, u8 data);
|
||||
template void pvr_write32p<u16>(u32 addr, u16 data);
|
||||
template void pvr_write32p<u32>(u32 addr, u32 data);
|
||||
template void pvr_write32p<u8, false>(u32 addr, u8 data);
|
||||
template void pvr_write32p<u8, true>(u32 addr, u8 data);
|
||||
template void pvr_write32p<u16, false>(u32 addr, u16 data);
|
||||
template void pvr_write32p<u16, true>(u32 addr, u16 data);
|
||||
template void pvr_write32p<u32, false>(u32 addr, u32 data);
|
||||
template void pvr_write32p<u32, true>(u32 addr, u32 data);
|
||||
|
||||
void DYNACALL TAWrite(u32 address, const SQBuffer *data, u32 count)
|
||||
{
|
||||
|
||||
@@ -20,7 +20,7 @@ void YUV_reset();
|
||||
|
||||
// 32-bit vram path handlers
|
||||
template<typename T> T DYNACALL pvr_read32p(u32 addr);
|
||||
template<typename T> void DYNACALL pvr_write32p(u32 addr, T data);
|
||||
template<typename T, bool Internal = false> void DYNACALL pvr_write32p(u32 addr, T data);
|
||||
// Area 4 handlers
|
||||
template<typename T, bool upper> T DYNACALL pvr_read_area4(u32 addr);
|
||||
template<typename T, bool upper> void DYNACALL pvr_write_area4(u32 addr, T data);
|
||||
+14
-13
@@ -125,7 +125,6 @@ protected:
|
||||
|
||||
static void reset()
|
||||
{
|
||||
tileclip_val = 0;
|
||||
memset(FaceBaseColor, 0xff, sizeof(FaceBaseColor));
|
||||
memset(FaceOffsColor, 0xff, sizeof(FaceOffsColor));
|
||||
memset(FaceBaseColor1, 0xff, sizeof(FaceBaseColor1));
|
||||
@@ -398,7 +397,7 @@ strip_end:
|
||||
|
||||
//32B
|
||||
case ParamType_User_Tile_Clip:
|
||||
SetTileClip(data->data_32[3] & 63, data->data_32[4] & 31, data->data_32[5] & 63, data->data_32[6] & 31);
|
||||
setClipRect(data->data_32[3] & 63, data->data_32[4] & 31, data->data_32[5] & 63, data->data_32[6] & 31);
|
||||
data += SZ32;
|
||||
break;
|
||||
|
||||
@@ -414,7 +413,7 @@ strip_end:
|
||||
//PolyType :32B/64B
|
||||
case ParamType_Polygon_or_Modifier_Volume:
|
||||
{
|
||||
TileClipMode(data->pcw.User_Clip);
|
||||
setClipMode(data->pcw.User_Clip);
|
||||
//Yep , C++ IS lame & limited
|
||||
#include "ta_const_df.h"
|
||||
if (CurrentList == ListType_None && !startList(data->pcw.ListType))
|
||||
@@ -466,7 +465,7 @@ strip_end:
|
||||
//32B
|
||||
//Sets Sprite info , and switches to ta_sprite_data function
|
||||
case ParamType_Sprite:
|
||||
TileClipMode(data->pcw.User_Clip);
|
||||
setClipMode(data->pcw.User_Clip);
|
||||
if (CurrentList != ListType_None || startList(data->pcw.ListType))
|
||||
{
|
||||
VertexDataFP = ta_sprite_data;
|
||||
@@ -502,23 +501,25 @@ public:
|
||||
{
|
||||
TaCmd = ta_main;
|
||||
BaseTAParser::reset();
|
||||
setClipRect(0, 0, 39, 14);
|
||||
setClipMode(0);
|
||||
}
|
||||
|
||||
private:
|
||||
static void SetTileClip(u32 xmin,u32 ymin,u32 xmax,u32 ymax)
|
||||
static void setClipRect(u32 xmin, u32 ymin, u32 xmax, u32 ymax)
|
||||
{
|
||||
u32 rv=tileclip_val & 0xF0000000;
|
||||
rv|=xmin; //6 bits
|
||||
rv|=xmax<<6; //6 bits
|
||||
rv|=ymin<<12; //5 bits
|
||||
rv|=ymax<<17; //5 bits
|
||||
tileclip_val=rv;
|
||||
u32 rv = tileclip_val & 0xF0000000;
|
||||
rv |= xmin; // 6 bits
|
||||
rv |= xmax << 6; // 6 bits
|
||||
rv |= ymin << 12; // 5 bits
|
||||
rv |= ymax << 17; // 5 bits
|
||||
tileclip_val = rv;
|
||||
}
|
||||
|
||||
static void TileClipMode(u32 mode)
|
||||
static void setClipMode(u32 mode)
|
||||
{
|
||||
//Group_En bit seems ignored, thanks p1pkin
|
||||
tileclip_val=(tileclip_val&(~0xF0000000)) | (mode<<28);
|
||||
tileclip_val = (tileclip_val & ~0xF0000000) | (mode << 28);
|
||||
}
|
||||
|
||||
//Polys -- update code on sprites if that gets updated too --
|
||||
|
||||
@@ -101,7 +101,6 @@ static void recSh4_ClearCache()
|
||||
|
||||
static void recSh4_Run()
|
||||
{
|
||||
sh4_int_bCpuRun = true;
|
||||
RestoreHostRoundingMode();
|
||||
|
||||
u8 *sh4_dyna_rcb = (u8 *)&Sh4cntx + sizeof(Sh4cntx);
|
||||
@@ -341,6 +340,11 @@ void* DYNACALL rdv_LinkBlock(u8* code,u32 dpc)
|
||||
return (void*)rv;
|
||||
}
|
||||
|
||||
static void recSh4_Start()
|
||||
{
|
||||
sh4Interp.Start();
|
||||
}
|
||||
|
||||
static void recSh4_Stop()
|
||||
{
|
||||
sh4Interp.Stop();
|
||||
@@ -409,6 +413,7 @@ static bool recSh4_IsCpuRunning()
|
||||
void Get_Sh4Recompiler(sh4_if* cpu)
|
||||
{
|
||||
cpu->Run = recSh4_Run;
|
||||
cpu->Start = recSh4_Start;
|
||||
cpu->Stop = recSh4_Stop;
|
||||
cpu->Step = recSh4_Step;
|
||||
cpu->Reset = recSh4_Reset;
|
||||
|
||||
@@ -973,18 +973,7 @@ shil_opc_end()
|
||||
|
||||
//shop_fsrra
|
||||
shil_opc(fsrra)
|
||||
shil_canonical
|
||||
(
|
||||
f32,f1,(float fn),
|
||||
|
||||
return std::sqrt(1.f / fn);
|
||||
)
|
||||
shil_compile
|
||||
(
|
||||
shil_cf_arg_f32(rs1);
|
||||
shil_cf(f1);
|
||||
shil_cf_rv_f32(rd);
|
||||
)
|
||||
UN_OP_F(1/sqrtf)
|
||||
shil_opc_end()
|
||||
|
||||
|
||||
|
||||
@@ -362,7 +362,7 @@ sh4op(i1111_nnnn_0111_1101)
|
||||
u32 n = GetN(op);
|
||||
if (fpscr.PR==0)
|
||||
{
|
||||
fr[n] = sqrtf(1.f / fr[n]);
|
||||
fr[n] = 1.f / sqrtf(fr[n]);
|
||||
CHECK_FPU_32(fr[n]);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -47,7 +47,6 @@ static u16 ReadNexOp()
|
||||
|
||||
static void Sh4_int_Run()
|
||||
{
|
||||
sh4_int_bCpuRun = true;
|
||||
RestoreHostRoundingMode();
|
||||
|
||||
try {
|
||||
@@ -74,6 +73,11 @@ static void Sh4_int_Run()
|
||||
sh4_int_bCpuRun = false;
|
||||
}
|
||||
|
||||
static void Sh4_int_Start()
|
||||
{
|
||||
sh4_int_bCpuRun = true;
|
||||
}
|
||||
|
||||
static void Sh4_int_Stop()
|
||||
{
|
||||
sh4_int_bCpuRun = false;
|
||||
@@ -206,6 +210,7 @@ static void Sh4_int_Term()
|
||||
|
||||
void Get_Sh4Interpreter(sh4_if* cpu)
|
||||
{
|
||||
cpu->Start = Sh4_int_Start;
|
||||
cpu->Run = Sh4_int_Run;
|
||||
cpu->Stop = Sh4_int_Stop;
|
||||
cpu->Step = Sh4_int_Step;
|
||||
|
||||
+34
-37
@@ -122,59 +122,60 @@ enum Sh4RegType
|
||||
NoReg=-1
|
||||
};
|
||||
|
||||
//Varius sh4 registers
|
||||
// SR (status register)
|
||||
|
||||
union sr_status_t
|
||||
{
|
||||
struct
|
||||
{
|
||||
u32 T_h : 1;//<<0
|
||||
u32 S : 1;//<<1
|
||||
u32 rsvd0 : 2;//<<2
|
||||
u32 IMASK : 4;//<<4
|
||||
u32 Q : 1;//<<8
|
||||
u32 M : 1;//<<9
|
||||
u32 rsvd1 : 5;//<<10
|
||||
u32 FD : 1;//<<15
|
||||
u32 rsvd2 : 12;//<<16
|
||||
u32 BL : 1;//<<28
|
||||
u32 RB : 1;//<<29
|
||||
u32 MD : 1;//<<20
|
||||
u32 rsvd3 : 1;//<<31
|
||||
u32 T_h : 1;
|
||||
u32 S : 1;
|
||||
u32 : 2;
|
||||
u32 IMASK : 4;
|
||||
u32 Q : 1;
|
||||
u32 M : 1;
|
||||
u32 : 5;
|
||||
u32 FD : 1;
|
||||
u32 : 12;
|
||||
u32 BL : 1;
|
||||
u32 RB : 1;
|
||||
u32 MD : 1;
|
||||
u32 : 1;
|
||||
};
|
||||
u32 status;
|
||||
};
|
||||
|
||||
#define STATUS_MASK 0x700083F2
|
||||
|
||||
//Status register bitfield
|
||||
// Status register with isolated T bit.
|
||||
// Used in place of the normal SR bitfield so that the T bit can be
|
||||
// handled as a regular register. This simplifies dynarec implementations.
|
||||
struct sr_t
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
u32 T_h : 1;//<<0
|
||||
u32 S : 1;//<<1
|
||||
u32 rsvd0 : 2;//<<2
|
||||
u32 IMASK : 4;//<<4
|
||||
u32 Q : 1;//<<8
|
||||
u32 M : 1;//<<9
|
||||
u32 rsvd1 : 5;//<<10
|
||||
u32 FD : 1;//<<15
|
||||
u32 rsvd2 : 12;//<<16
|
||||
u32 BL : 1;//<<28
|
||||
u32 RB : 1;//<<29
|
||||
u32 MD : 1;//<<20
|
||||
u32 rsvd3 : 1;//<<31
|
||||
u32 T_h : 1;
|
||||
u32 S : 1;
|
||||
u32 : 2;
|
||||
u32 IMASK : 4;
|
||||
u32 Q : 1;
|
||||
u32 M : 1;
|
||||
u32 : 5;
|
||||
u32 FD : 1;
|
||||
u32 : 12;
|
||||
u32 BL : 1;
|
||||
u32 RB : 1;
|
||||
u32 MD : 1;
|
||||
u32 : 1;
|
||||
};
|
||||
u32 status;
|
||||
};
|
||||
u32 T;
|
||||
|
||||
};
|
||||
|
||||
//FPSCR (fpu status and control register) bitfield
|
||||
// FPSCR (fpu status and control register)
|
||||
struct fpscr_t
|
||||
{
|
||||
union
|
||||
@@ -203,13 +204,7 @@ struct fpscr_t
|
||||
u32 PR : 1;
|
||||
u32 SZ : 1;
|
||||
u32 FR : 1;
|
||||
u32 pad : 10;
|
||||
};
|
||||
struct
|
||||
{
|
||||
u32 _nil : 2+1+1+1+1+4+8+1;
|
||||
u32 PR_SZ : 2;
|
||||
u32 nilz : 11;
|
||||
u32 : 10;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -217,6 +212,7 @@ struct fpscr_t
|
||||
//sh4 interface
|
||||
struct sh4_if
|
||||
{
|
||||
void (*Start)();
|
||||
void (*Run)();
|
||||
void (*Stop)();
|
||||
void (*Step)();
|
||||
@@ -272,6 +268,7 @@ struct alignas(64) Sh4Context
|
||||
u64 raw[64-8];
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(Sh4Context) == 448, "Invalid Sh4Context size");
|
||||
|
||||
struct alignas(32) SQBuffer {
|
||||
u8 data[32];
|
||||
|
||||
+150
-139
@@ -16,149 +16,160 @@ Disc* cdi_parse(const char* file, std::vector<u8> *digest)
|
||||
WARN_LOG(GDROM, "Cannot open file '%s' errno %d", file, errno);
|
||||
throw FlycastException(std::string("Cannot open CDI file ") + file);
|
||||
}
|
||||
|
||||
image_s image = { 0 };
|
||||
track_s track = { 0 };
|
||||
if (!CDI_init(fsource, &image, file))
|
||||
{
|
||||
std::fclose(fsource);
|
||||
throw FlycastException(std::string("Invalid CDI file ") + file);
|
||||
}
|
||||
|
||||
CDI_get_sessions(fsource,&image);
|
||||
|
||||
Disc* rv= new Disc();
|
||||
|
||||
image.remaining_sessions = image.sessions;
|
||||
|
||||
/////////////////////////////////////////////////////////////// Loop sessions
|
||||
try {
|
||||
image_s image = { 0 };
|
||||
track_s track = { 0 };
|
||||
if (!CDI_init(fsource, &image, file))
|
||||
throw FlycastException(std::string("Invalid CDI file ") + file);
|
||||
|
||||
bool ft=true, CD_M2=false,CD_M1=false,CD_DA=false;
|
||||
|
||||
while(image.remaining_sessions > 0)
|
||||
{
|
||||
ft=true;
|
||||
image.global_current_session++;
|
||||
|
||||
CDI_get_tracks (fsource, &image);
|
||||
|
||||
image.header_position = std::ftell(fsource);
|
||||
|
||||
if (image.tracks == 0)
|
||||
INFO_LOG(GDROM, "Open session");
|
||||
else
|
||||
{
|
||||
// Clear cuesheet
|
||||
image.remaining_tracks = image.tracks;
|
||||
|
||||
///////////////////////////////////////////////////////////////// Loop tracks
|
||||
|
||||
while(image.remaining_tracks > 0)
|
||||
{
|
||||
track.global_current_track++;
|
||||
track.number = image.tracks - image.remaining_tracks + 1;
|
||||
|
||||
CDI_read_track (fsource, &image, &track);
|
||||
|
||||
if (track.sector_size != 2048 && track.sector_size != 2336 && track.sector_size != 2352 && track.sector_size != 2448)
|
||||
{
|
||||
delete rv;
|
||||
std::fclose(fsource);
|
||||
WARN_LOG(GDROM, "Invalid sector size: %lu", track.sector_size);
|
||||
throw FlycastException("Invalid CDI sector size");
|
||||
}
|
||||
|
||||
image.header_position = std::ftell(fsource);
|
||||
|
||||
// Show info
|
||||
#if 0
|
||||
printf("Saving ");
|
||||
printf("Track: %2d ",track.global_current_track);
|
||||
printf("Type: ");
|
||||
switch(track.mode)
|
||||
{
|
||||
case 0 : printf("Audio/"); break;
|
||||
case 1 : printf("Mode1/"); break;
|
||||
case 2 :
|
||||
default: printf("Mode2/"); break;
|
||||
}
|
||||
printf("%lu ",track.sector_size);
|
||||
|
||||
printf("Pregap: %-3ld ",track.pregap_length);
|
||||
printf("Size: %-6ld ",track.length);
|
||||
printf("LBA: %-6ld ",track.start_lba);
|
||||
#endif
|
||||
if (ft)
|
||||
{
|
||||
ft = false;
|
||||
Session s;
|
||||
s.StartFAD = track.pregap_length + track.start_lba;
|
||||
s.FirstTrack = (u8)track.global_current_track;
|
||||
rv->sessions.push_back(s);
|
||||
}
|
||||
|
||||
Track t;
|
||||
if (track.mode==2)
|
||||
CD_M2=true;
|
||||
if (track.mode==1)
|
||||
CD_M1=true;
|
||||
if (track.mode==0)
|
||||
CD_DA=true;
|
||||
|
||||
t.ADR=1;//hmm is that ok ?
|
||||
|
||||
t.CTRL=track.mode==0?0:4;
|
||||
t.StartFAD=track.start_lba+track.pregap_length;
|
||||
t.EndFAD=t.StartFAD+track.length-1;
|
||||
FILE *trackFile = hostfs::storage().openFile(file, "rb");
|
||||
if (trackFile == nullptr)
|
||||
{
|
||||
delete rv;
|
||||
std::fclose(fsource);
|
||||
WARN_LOG(GDROM, "Cannot re-open file '%s' errno %d", file, errno);
|
||||
throw FlycastException("Cannot re-open CDI file");
|
||||
}
|
||||
t.file = new RawTrackFile(trackFile, track.position + track.pregap_length * track.sector_size, t.StartFAD, track.sector_size);
|
||||
|
||||
rv->tracks.push_back(t);
|
||||
|
||||
if (track.length < 0)
|
||||
WARN_LOG(GDROM, "Negative track size found. You must extract image with /pregap option");
|
||||
|
||||
std::fseek(fsource, track.position, SEEK_SET);
|
||||
if (track.total_length < track.length + track.pregap_length)
|
||||
{
|
||||
WARN_LOG(GDROM, "This track seems truncated. Skipping...");
|
||||
// FIXME that can't be right
|
||||
std::fseek(fsource, track.total_length, SEEK_CUR);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::fseek(fsource, track.total_length * track.sector_size, SEEK_CUR);
|
||||
rv->EndFAD = track.start_lba + track.total_length - 1;
|
||||
}
|
||||
track.position = std::ftell(fsource);
|
||||
|
||||
std::fseek(fsource, image.header_position, SEEK_SET);
|
||||
|
||||
image.remaining_tracks--;
|
||||
}
|
||||
if (!CDI_get_sessions(fsource, &image)) {
|
||||
WARN_LOG(GDROM, "Invalid CDI file '%s': can't get sessions", file);
|
||||
throw FlycastException("Invalid CDI image");
|
||||
}
|
||||
if (image.sessions > 255) {
|
||||
WARN_LOG(GDROM, "Invalid CDI file '%s': too many sessions (%d)", file, image.sessions);
|
||||
throw FlycastException("Invalid CDI image");
|
||||
}
|
||||
|
||||
CDI_skip_next_session(fsource, &image);
|
||||
std::unique_ptr<Disc> rv = std::make_unique<Disc>();
|
||||
|
||||
image.remaining_sessions--;
|
||||
image.remaining_sessions = image.sessions;
|
||||
|
||||
/////////////////////////////////////////////////////////////// Loop sessions
|
||||
|
||||
bool ft=true, CD_M2=false,CD_M1=false,CD_DA=false;
|
||||
|
||||
while(image.remaining_sessions > 0)
|
||||
{
|
||||
ft=true;
|
||||
image.global_current_session++;
|
||||
|
||||
if (!CDI_get_tracks(fsource, &image)) {
|
||||
WARN_LOG(GDROM, "Invalid CDI file '%s': can't get tracks", file);
|
||||
throw FlycastException("Invalid CDI image");
|
||||
}
|
||||
|
||||
image.header_position = std::ftell(fsource);
|
||||
|
||||
if (image.tracks == 0)
|
||||
INFO_LOG(GDROM, "Open session");
|
||||
else
|
||||
{
|
||||
if (image.tracks > 100) {
|
||||
WARN_LOG(GDROM, "Invalid CDI file '%s': too many tracks (%d)", file, image.tracks);
|
||||
throw FlycastException("Invalid CDI image");
|
||||
}
|
||||
// Clear cuesheet
|
||||
image.remaining_tracks = image.tracks;
|
||||
|
||||
///////////////////////////////////////////////////////////////// Loop tracks
|
||||
|
||||
while(image.remaining_tracks > 0)
|
||||
{
|
||||
track.global_current_track++;
|
||||
track.number = image.tracks - image.remaining_tracks + 1;
|
||||
|
||||
if (!CDI_read_track (fsource, &image, &track))
|
||||
throw FlycastException("Invalid CDI image");
|
||||
|
||||
if (track.sector_size != 2048 && track.sector_size != 2336 && track.sector_size != 2352 && track.sector_size != 2448)
|
||||
{
|
||||
WARN_LOG(GDROM, "Invalid sector size: %lu", track.sector_size);
|
||||
throw FlycastException("Invalid CDI sector size");
|
||||
}
|
||||
|
||||
image.header_position = std::ftell(fsource);
|
||||
|
||||
// Show info
|
||||
#if 0
|
||||
printf("Saving ");
|
||||
printf("Track: %2d ",track.global_current_track);
|
||||
printf("Type: ");
|
||||
switch(track.mode)
|
||||
{
|
||||
case 0 : printf("Audio/"); break;
|
||||
case 1 : printf("Mode1/"); break;
|
||||
case 2 :
|
||||
default: printf("Mode2/"); break;
|
||||
}
|
||||
printf("%lu ",track.sector_size);
|
||||
|
||||
printf("Pregap: %-3ld ",track.pregap_length);
|
||||
printf("Size: %-6ld ",track.length);
|
||||
printf("LBA: %-6ld ",track.start_lba);
|
||||
#endif
|
||||
if (ft)
|
||||
{
|
||||
ft = false;
|
||||
Session s;
|
||||
s.StartFAD = track.pregap_length + track.start_lba;
|
||||
s.FirstTrack = (u8)track.global_current_track;
|
||||
rv->sessions.push_back(s);
|
||||
}
|
||||
|
||||
Track t;
|
||||
if (track.mode==2)
|
||||
CD_M2=true;
|
||||
if (track.mode==1)
|
||||
CD_M1=true;
|
||||
if (track.mode==0)
|
||||
CD_DA=true;
|
||||
|
||||
t.ADR=1;//hmm is that ok ?
|
||||
|
||||
t.CTRL=track.mode==0?0:4;
|
||||
t.StartFAD=track.start_lba+track.pregap_length;
|
||||
t.EndFAD=t.StartFAD+track.length-1;
|
||||
FILE *trackFile = hostfs::storage().openFile(file, "rb");
|
||||
if (trackFile == nullptr) {
|
||||
WARN_LOG(GDROM, "Cannot re-open file '%s' errno %d", file, errno);
|
||||
throw FlycastException("Cannot re-open CDI file");
|
||||
}
|
||||
t.file = new RawTrackFile(trackFile, track.position + track.pregap_length * track.sector_size, t.StartFAD, track.sector_size);
|
||||
|
||||
rv->tracks.push_back(t);
|
||||
|
||||
if (track.length < 0)
|
||||
WARN_LOG(GDROM, "Negative track size found. You must extract image with /pregap option");
|
||||
|
||||
std::fseek(fsource, track.position, SEEK_SET);
|
||||
if (track.total_length < track.length + track.pregap_length)
|
||||
{
|
||||
WARN_LOG(GDROM, "This track seems truncated. Skipping...");
|
||||
// FIXME that can't be right
|
||||
std::fseek(fsource, track.total_length, SEEK_CUR);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::fseek(fsource, track.total_length * track.sector_size, SEEK_CUR);
|
||||
rv->EndFAD = track.start_lba + track.total_length - 1;
|
||||
}
|
||||
track.position = std::ftell(fsource);
|
||||
|
||||
std::fseek(fsource, image.header_position, SEEK_SET);
|
||||
|
||||
image.remaining_tracks--;
|
||||
}
|
||||
}
|
||||
|
||||
CDI_skip_next_session(fsource, &image);
|
||||
|
||||
image.remaining_sessions--;
|
||||
}
|
||||
if (digest != nullptr)
|
||||
*digest = MD5Sum().add(fsource).getDigest();
|
||||
std::fclose(fsource);
|
||||
|
||||
rv->type=GuessDiscType(CD_M1,CD_M2,CD_DA);
|
||||
|
||||
rv->LeadOut.StartFAD = rv->EndFAD;
|
||||
rv->LeadOut.ADR = 1;
|
||||
rv->LeadOut.CTRL = 4;
|
||||
|
||||
return rv.release();
|
||||
} catch (...) {
|
||||
std::fclose(fsource);
|
||||
throw;
|
||||
}
|
||||
if (digest != nullptr)
|
||||
*digest = MD5Sum().add(fsource).getDigest();
|
||||
std::fclose(fsource);
|
||||
|
||||
rv->type=GuessDiscType(CD_M1,CD_M2,CD_DA);
|
||||
|
||||
rv->LeadOut.StartFAD = rv->EndFAD;
|
||||
rv->LeadOut.ADR = 1;
|
||||
rv->LeadOut.CTRL = 4;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
@@ -233,7 +233,8 @@ struct RawTrackFile : TrackFile
|
||||
*sector_type=SECFMT_2448_MODE2;
|
||||
else
|
||||
{
|
||||
verify(false);
|
||||
WARN_LOG(GDROM, "Unsupported sector size %d", fmt);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::fseek(file, offset + FAD * fmt, SEEK_SET);
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "cfg/option.h"
|
||||
#include "gamepad_device.h"
|
||||
#include "ui/gui.h"
|
||||
#include "hw/maple/maple_devs.h"
|
||||
#include <memory>
|
||||
|
||||
extern u8 kb_key[4][6]; // normal keys pressed
|
||||
@@ -82,11 +83,12 @@ protected:
|
||||
void input(u8 keycode, bool pressed, int modifier_keys)
|
||||
{
|
||||
const int port = maple_port();
|
||||
const bool portIsValid = port >= 0 && port < MAPLE_PORTS;
|
||||
|
||||
// Checking if we're in-game with an emulated keyboard first,
|
||||
// then check if the "Bypass Emulated Keyboard" hotkey is held and we're not in GUI,
|
||||
// if so: send emu hotkeys only and return earlier.
|
||||
if ((settings.platform.isConsole() && config::MapleMainDevices[port] == MDT_Keyboard)
|
||||
if ((settings.platform.isConsole() && portIsValid && config::MapleMainDevices[port] == MDT_Keyboard)
|
||||
|| (settings.platform.isArcade() && settings.input.keyboardGame))
|
||||
{
|
||||
if (keycode == input_mapper->get_button_code(0, EMU_BTN_BYPASS_KB))
|
||||
@@ -126,13 +128,13 @@ protected:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (port >= 0 && port < (int)std::size(kb_shift))
|
||||
if (portIsValid)
|
||||
kb_shift[port] = _modifier_keys;
|
||||
|
||||
if (keycode != 0)
|
||||
{
|
||||
gui_keyboard_key(keycode, pressed);
|
||||
if (keycode < 0xE0 && port >= 0 && port < (int)std::size(kb_key))
|
||||
if (keycode < 0xE0 && portIsValid)
|
||||
{
|
||||
if (pressed)
|
||||
{
|
||||
@@ -176,7 +178,7 @@ protected:
|
||||
// or the corresponding maple device (if any) isn't a keyboard
|
||||
else if (gui_is_open()
|
||||
|| port == (int)std::size(kb_key)
|
||||
|| (settings.platform.isConsole() && config::MapleMainDevices[port] != MDT_Keyboard)
|
||||
|| (settings.platform.isConsole() && (!portIsValid || config::MapleMainDevices[port] != MDT_Keyboard))
|
||||
|| (settings.platform.isArcade() && !settings.input.keyboardGame))
|
||||
gamepad_btn_input(keycode, pressed);
|
||||
}
|
||||
|
||||
@@ -756,11 +756,19 @@ static int modem_set_speed(pico_device *dev, uint32_t speed)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t dns_query_start;
|
||||
static uint32_t dns_query_attempts;
|
||||
|
||||
static void reset_dns_entries()
|
||||
{
|
||||
dns_query_attempts = 0;
|
||||
dns_query_start = 0;
|
||||
public_ip.addr = 0;
|
||||
afo_ip.addr = 0;
|
||||
}
|
||||
|
||||
static void check_dns_entries()
|
||||
{
|
||||
static uint32_t dns_query_start = 0;
|
||||
static uint32_t dns_query_attempts = 0;
|
||||
|
||||
if (public_ip.addr == 0)
|
||||
{
|
||||
u32 ip;
|
||||
@@ -787,6 +795,8 @@ static void check_dns_entries()
|
||||
{
|
||||
public_ip.addr = 0xffffffff; // Bogus but not null
|
||||
dns_query_attempts = 0;
|
||||
dns_query_start = 0;
|
||||
WARN_LOG(MODEM, "Can't resolve my IP");
|
||||
}
|
||||
else
|
||||
// Retry
|
||||
@@ -796,7 +806,7 @@ static void check_dns_entries()
|
||||
}
|
||||
else if (afo_ip.addr == 0)
|
||||
{
|
||||
if (!dns_query_start)
|
||||
if (dns_query_start == 0)
|
||||
{
|
||||
dns_query_start = PICO_TIME_MS();
|
||||
get_host_by_name("auriga.segasoft.com", dnsaddr); // Alien Front Online server
|
||||
@@ -821,6 +831,7 @@ static void check_dns_entries()
|
||||
pico_string_to_ipv4("146.185.135.179", &addr); // Default address
|
||||
memcpy(&afo_ip.addr, &addr, sizeof(addr));
|
||||
dns_query_attempts = 0;
|
||||
WARN_LOG(MODEM, "Can't resolve auriga.segasoft.com. Using default 146.185.135.179");
|
||||
}
|
||||
else
|
||||
// Retry
|
||||
@@ -995,12 +1006,33 @@ static void *pico_thread_func(void *)
|
||||
out_buffer_lock.unlock();
|
||||
}
|
||||
|
||||
u32 addr;
|
||||
pico_string_to_ipv4(config::DNS.get().c_str(), &addr);
|
||||
memcpy(&dnsaddr.addr, &addr, sizeof(addr));
|
||||
// Find DNS ip address
|
||||
{
|
||||
std::string dnsName = config::DNS;
|
||||
if (dnsName == "46.101.91.123")
|
||||
// override legacy default with current one
|
||||
dnsName = "dns.flyca.st";
|
||||
hostent *hp = gethostbyname(dnsName.c_str());
|
||||
if (hp != nullptr && hp->h_length > 0)
|
||||
{
|
||||
memcpy(&dnsaddr.addr, hp->h_addr_list[0], sizeof(dnsaddr.addr));
|
||||
char s[17];
|
||||
pico_ipv4_to_string(s, dnsaddr.addr);
|
||||
NOTICE_LOG(MODEM, "%s IP is %s", dnsName.c_str(), s);
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 addr;
|
||||
pico_string_to_ipv4("46.101.91.123", &addr);
|
||||
dnsaddr.addr = addr;
|
||||
WARN_LOG(MODEM, "Can't resolve dns.flyca.st. Using default 46.101.91.123");
|
||||
}
|
||||
}
|
||||
reset_dns_entries();
|
||||
|
||||
// Create ppp/eth device
|
||||
const bool usingPPP = !config::EmulateBBA;
|
||||
u32 addr;
|
||||
if (usingPPP)
|
||||
{
|
||||
// PPP
|
||||
|
||||
@@ -41,7 +41,8 @@ CustomStorage& customStorage()
|
||||
std::string getSubPath(const std::string& reference, const std::string& relative) override { die("Not implemented"); }
|
||||
FileInfo getFileInfo(const std::string& path) override { die("Not implemented"); }
|
||||
bool exists(const std::string& path) override { die("Not implemented"); }
|
||||
bool addStorage(bool isDirectory, bool writeAccess, void (*callback)(bool cancelled, std::string selectedPath)) override {
|
||||
bool addStorage(bool isDirectory, bool writeAccess, const std::string& description,
|
||||
void (*callback)(bool cancelled, std::string selectedPath)) override {
|
||||
die("Not implemented");
|
||||
}
|
||||
};
|
||||
@@ -392,9 +393,10 @@ AllStorage& storage()
|
||||
return storage;
|
||||
}
|
||||
|
||||
bool addStorage(bool isDirectory, bool writeAccess, void (*callback)(bool cancelled, std::string selectedPath))
|
||||
bool addStorage(bool isDirectory, bool writeAccess, const std::string& description,
|
||||
void (*callback)(bool cancelled, std::string selectedPath))
|
||||
{
|
||||
return customStorage().addStorage(isDirectory, writeAccess, callback);
|
||||
return customStorage().addStorage(isDirectory, writeAccess, description, callback);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -64,7 +64,8 @@ public:
|
||||
class CustomStorage : public Storage
|
||||
{
|
||||
public:
|
||||
virtual bool addStorage(bool isDirectory, bool writeAccess, void (*callback)(bool cancelled, std::string selectedPath)) = 0;
|
||||
virtual bool addStorage(bool isDirectory, bool writeAccess, const std::string& description,
|
||||
void (*callback)(bool cancelled, std::string selectedPath)) = 0;
|
||||
};
|
||||
|
||||
class AllStorage : public Storage
|
||||
@@ -82,7 +83,8 @@ public:
|
||||
};
|
||||
|
||||
AllStorage& storage();
|
||||
bool addStorage(bool isDirectory, bool writeAccess, void (*callback)(bool cancelled, std::string selectedPath));
|
||||
bool addStorage(bool isDirectory, bool writeAccess, const std::string& description,
|
||||
void (*callback)(bool cancelled, std::string selectedPath));
|
||||
|
||||
// iterate depth-first over the files contained in a folder hierarchy
|
||||
class DirectoryTree
|
||||
|
||||
@@ -50,6 +50,7 @@ using namespace vixl::aarch32;
|
||||
#include "cfg/option.h"
|
||||
#include "arm_unwind.h"
|
||||
#include "oslib/virtmem.h"
|
||||
#include "emulator.h"
|
||||
|
||||
//#define CANONICALTEST
|
||||
|
||||
@@ -428,8 +429,8 @@ public:
|
||||
generate_mainloop();
|
||||
|
||||
::mainloop(context);
|
||||
if (restarting)
|
||||
p_sh4rcb->cntx.CpuRunning = 1;
|
||||
if (restarting && !emu.restartCpu())
|
||||
restarting = false;
|
||||
} while (restarting);
|
||||
}
|
||||
|
||||
@@ -1933,8 +1934,8 @@ void Arm32Assembler::compileOp(RuntimeBlockInfo* block, shil_opcode* op, bool op
|
||||
|
||||
case shop_fsrra:
|
||||
Vmov(s1, 1.f);
|
||||
Vdiv(s0, s1, reg.mapFReg(op->rs1));
|
||||
Vsqrt(reg.mapFReg(op->rd), s0);
|
||||
Vsqrt(s0, reg.mapFReg(op->rs1));
|
||||
Vdiv(reg.mapFReg(op->rd), s1, s0);
|
||||
break;
|
||||
|
||||
case shop_fsetgt:
|
||||
|
||||
@@ -35,14 +35,15 @@ using namespace vixl::aarch64;
|
||||
|
||||
#include "hw/sh4/sh4_mmr.h"
|
||||
#include "hw/sh4/sh4_interrupts.h"
|
||||
#include "arm64_unwind.h"
|
||||
#include "hw/sh4/sh4_core.h"
|
||||
#include "hw/sh4/dyna/ngen.h"
|
||||
#include "hw/sh4/sh4_mem.h"
|
||||
#include "hw/sh4/sh4_rom.h"
|
||||
#include "arm64_regalloc.h"
|
||||
#include "hw/mem/addrspace.h"
|
||||
#include "arm64_unwind.h"
|
||||
#include "oslib/virtmem.h"
|
||||
#include "emulator.h"
|
||||
|
||||
#undef do_sqw_nommu
|
||||
|
||||
@@ -879,9 +880,9 @@ public:
|
||||
break;
|
||||
|
||||
case shop_fsrra:
|
||||
Fsqrt(s0, regalloc.MapVRegister(op.rs1));
|
||||
Fmov(s1, 1.f);
|
||||
Fdiv(s0, s1, regalloc.MapVRegister(op.rs1));
|
||||
Fsqrt(regalloc.MapVRegister(op.rd), s0);
|
||||
Fdiv(regalloc.MapVRegister(op.rd), s1, s0);
|
||||
break;
|
||||
|
||||
case shop_fsetgt:
|
||||
@@ -2213,8 +2214,8 @@ public:
|
||||
generate_mainloop();
|
||||
|
||||
::mainloop(v_cntx);
|
||||
if (restarting)
|
||||
p_sh4rcb->cntx.CpuRunning = 1;
|
||||
if (restarting && !emu.restartCpu())
|
||||
restarting = false;
|
||||
} while (restarting);
|
||||
} catch (const SH4ThrownException&) {
|
||||
ERROR_LOG(DYNAREC, "SH4ThrownException in mainloop");
|
||||
|
||||
@@ -537,18 +537,18 @@ protected:
|
||||
break;
|
||||
|
||||
case shop_fsrra:
|
||||
sqrtss(xmm0, mapXRegister(op.rs1));
|
||||
if (ArchX64)
|
||||
{
|
||||
mov(eax, 0x3f800000); // 1.0
|
||||
movd(xmm0, eax);
|
||||
movd(mapXRegister(op.rd), eax);
|
||||
}
|
||||
else
|
||||
{
|
||||
static float one = 1.f;
|
||||
movss(xmm0, dword[&one]);
|
||||
movss(mapXRegister(op.rd), dword[&one]);
|
||||
}
|
||||
divss(xmm0, mapXRegister(op.rs1));
|
||||
sqrtss(mapXRegister(op.rd), xmm0);
|
||||
divss(mapXRegister(op.rd), xmm0);
|
||||
break;
|
||||
|
||||
case shop_fsetgt:
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "hw/sh4/sh4_mem.h"
|
||||
#include "hw/mem/addrspace.h"
|
||||
#include "oslib/unwind_info.h"
|
||||
#include "emulator.h"
|
||||
|
||||
static void (*mainloop)();
|
||||
static void (*ngen_FailedToFindBlock_)();
|
||||
@@ -900,8 +901,8 @@ public:
|
||||
generate_mainloop();
|
||||
|
||||
::mainloop();
|
||||
if (restarting)
|
||||
p_sh4rcb->cntx.CpuRunning = 1;
|
||||
if (restarting && !emu.restartCpu())
|
||||
restarting = false;
|
||||
} while (restarting);
|
||||
} catch (const SH4ThrownException& e) {
|
||||
ERROR_LOG(DYNAREC, "SH4ThrownException in mainloop %x pc %x", e.expEvn, e.epc);
|
||||
|
||||
@@ -985,7 +985,7 @@ public:
|
||||
|
||||
template<typename T>
|
||||
void write(T pixel) {
|
||||
pvr_write32p(dstAddr, pixel);
|
||||
pvr_write32p<T, true>(dstAddr, pixel);
|
||||
dstAddr += sizeof(T);
|
||||
}
|
||||
|
||||
@@ -1173,14 +1173,12 @@ public:
|
||||
|
||||
void write(int xmin, int xmax, const u8 *& pixel, int y)
|
||||
{
|
||||
for (int c = xmin; c < xmax - 3; c += 4)
|
||||
for (int c = xmin; c < xmax; c++)
|
||||
{
|
||||
pixWriter.write((u32)((pixel[Blue + 4] << 24) | (pixel[Red] << 16) | (pixel[Green] << 8) | pixel[Blue]));
|
||||
pixWriter.write(pixel[Blue]);
|
||||
pixWriter.write(pixel[Green]);
|
||||
pixWriter.write(pixel[Red]);
|
||||
pixel += 4;
|
||||
pixWriter.write((u32)((pixel[Green + 4] << 24) | (pixel[Blue + 4] << 16) | (pixel[Red] << 8) | pixel[Green]));
|
||||
pixel += 4;
|
||||
pixWriter.write((u32)((pixel[Red + 4] << 24) | (pixel[Green + 4] << 16) | (pixel[Blue + 4] << 8) | pixel[Red]));
|
||||
pixel += 8;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -131,7 +131,9 @@ VertexOut main(in VertexIn vin)
|
||||
#endif
|
||||
vo.index = uint(polyNumber) + vin.vertexId;
|
||||
#endif
|
||||
|
||||
#if MODIFIER_VOLUME == 1
|
||||
vo.pos.z = min(vo.pos.z, -0.001f);
|
||||
#endif
|
||||
vo.pos = mul(projMat, vo.pos);
|
||||
|
||||
vo.pos = float4(vo.pos.xy / vo.pos.w, 1.f / vo.pos.w, 1.f);
|
||||
|
||||
@@ -326,6 +326,27 @@ void DX11Renderer::Process(TA_context* ctx)
|
||||
ta_parse(ctx, true);
|
||||
}
|
||||
|
||||
void DX11Renderer::resetContextState()
|
||||
{
|
||||
// Reset device context state. Very much needed for libretro where current state is unknown.
|
||||
deviceContext->OMSetRenderTargetsAndUnorderedAccessViews(0, nullptr, nullptr, 0, 0, nullptr, nullptr);
|
||||
deviceContext->PSSetShader(nullptr, nullptr, 0);
|
||||
deviceContext->GSSetShader(nullptr, nullptr, 0);
|
||||
deviceContext->HSSetShader(nullptr, nullptr, 0);
|
||||
deviceContext->DSSetShader(nullptr, nullptr, 0);
|
||||
deviceContext->CSSetShader(nullptr, nullptr, 0);
|
||||
deviceContext->VSSetShader(nullptr, nullptr, 0);
|
||||
ID3D11ShaderResourceView *nullview[D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT] {};
|
||||
deviceContext->CSSetShaderResources(0, D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, nullview);
|
||||
deviceContext->DSSetShaderResources(0, D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, nullview);
|
||||
deviceContext->GSSetShaderResources(0, D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, nullview);
|
||||
deviceContext->HSSetShaderResources(0, D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, nullview);
|
||||
deviceContext->PSSetShaderResources(0, D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, nullview);
|
||||
deviceContext->VSSetShaderResources(0, D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, nullview);
|
||||
deviceContext->SetPredication(nullptr, false);
|
||||
deviceContext->SOSetTargets(0, nullptr, nullptr);
|
||||
}
|
||||
|
||||
void DX11Renderer::configVertexShader()
|
||||
{
|
||||
matrices.CalcMatrices(&pvrrc, width, height);
|
||||
@@ -359,10 +380,6 @@ void DX11Renderer::configVertexShader()
|
||||
memcpy(mappedSubres.pData, &constant, sizeof(constant));
|
||||
deviceContext->Unmap(vtxConstants, 0);
|
||||
deviceContext->VSSetConstantBuffers(0, 1, &vtxConstants.get());
|
||||
deviceContext->GSSetShader(nullptr, nullptr, 0);
|
||||
deviceContext->HSSetShader(nullptr, nullptr, 0);
|
||||
deviceContext->DSSetShader(nullptr, nullptr, 0);
|
||||
deviceContext->CSSetShader(nullptr, nullptr, 0);
|
||||
}
|
||||
|
||||
void DX11Renderer::uploadGeometryBuffers()
|
||||
@@ -454,9 +471,7 @@ void DX11Renderer::setupPixelShaderConstants()
|
||||
|
||||
bool DX11Renderer::Render()
|
||||
{
|
||||
// make sure to unbind the framebuffer view before setting it as render target
|
||||
ID3D11ShaderResourceView *nullView = nullptr;
|
||||
deviceContext->PSSetShaderResources(0, 1, &nullView);
|
||||
resetContextState();
|
||||
bool is_rtt = pvrrc.isRTT;
|
||||
if (!is_rtt)
|
||||
{
|
||||
@@ -1201,26 +1216,27 @@ void DX11Renderer::readRttRenderTarget(u32 texAddress)
|
||||
|
||||
void DX11Renderer::updatePaletteTexture()
|
||||
{
|
||||
if (!palette_updated)
|
||||
return;
|
||||
palette_updated = false;
|
||||
|
||||
deviceContext->UpdateSubresource(paletteTexture, 0, nullptr, palette32_ram, 32 * sizeof(u32), 32 * sizeof(u32) * 32);
|
||||
|
||||
if (palette_updated)
|
||||
{
|
||||
palette_updated = false;
|
||||
deviceContext->UpdateSubresource(paletteTexture, 0, nullptr, palette32_ram, 32 * sizeof(u32), 32 * sizeof(u32) * 32);
|
||||
}
|
||||
deviceContext->PSSetShaderResources(1, 1, &paletteTextureView.get());
|
||||
deviceContext->PSSetSamplers(1, 1, &samplers->getSampler(false).get());
|
||||
}
|
||||
|
||||
void DX11Renderer::updateFogTexture()
|
||||
{
|
||||
if (!fog_needs_update || !config::Fog)
|
||||
if (!config::Fog)
|
||||
return;
|
||||
fog_needs_update = false;
|
||||
u8 temp_tex_buffer[256];
|
||||
MakeFogTexture(temp_tex_buffer);
|
||||
|
||||
deviceContext->UpdateSubresource(fogTexture, 0, nullptr, temp_tex_buffer, 128, 128 * 2);
|
||||
if (fog_needs_update)
|
||||
{
|
||||
fog_needs_update = false;
|
||||
u8 temp_tex_buffer[256];
|
||||
MakeFogTexture(temp_tex_buffer);
|
||||
|
||||
deviceContext->UpdateSubresource(fogTexture, 0, nullptr, temp_tex_buffer, 128, 128 * 2);
|
||||
}
|
||||
deviceContext->PSSetShaderResources(2, 1, &fogTextureView.get());
|
||||
deviceContext->PSSetSamplers(2, 1, &samplers->getSampler(true).get());
|
||||
}
|
||||
|
||||
@@ -46,6 +46,8 @@ struct DX11Renderer : public Renderer
|
||||
frameRendered = false;
|
||||
#ifndef LIBRETRO
|
||||
imguiDriver->setFrameRendered();
|
||||
#else
|
||||
theDX11Context.present();
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
@@ -99,6 +101,7 @@ protected:
|
||||
virtual void setRTTSize(int width, int height) {}
|
||||
void writeFramebufferToVRAM();
|
||||
void renderVideoRouting();
|
||||
void resetContextState();
|
||||
|
||||
ComPtr<ID3D11Device> device;
|
||||
ComPtr<ID3D11DeviceContext> deviceContext;
|
||||
|
||||
@@ -460,6 +460,7 @@ enum VertexMacroEnum {
|
||||
MacroPositionOnly,
|
||||
MacroTwoVolumes,
|
||||
MacroLightOn,
|
||||
MacroModifierVolume,
|
||||
};
|
||||
|
||||
static D3D_SHADER_MACRO VertexMacros[]
|
||||
@@ -469,6 +470,7 @@ static D3D_SHADER_MACRO VertexMacros[]
|
||||
{ "POSITION_ONLY", "0" },
|
||||
{ "pp_TwoVolumes", "0" },
|
||||
{ "LIGHT_ON", "1" },
|
||||
{ "MODIFIER_VOLUME", "0" },
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
@@ -573,6 +575,7 @@ const ComPtr<ID3D11VertexShader>& DX11Shaders::getVertexShader(bool gouraud, boo
|
||||
VertexMacros[MacroPositionOnly].Definition = MacroValues[false];
|
||||
VertexMacros[MacroTwoVolumes].Definition = MacroValues[false];
|
||||
VertexMacros[MacroLightOn].Definition = MacroValues[true];
|
||||
VertexMacros[MacroModifierVolume].Definition = MacroValues[false];
|
||||
std::string source(DX11N2VertexShader);
|
||||
source += std::string("\n") + DX11N2ColorShader;
|
||||
vertexShader = compileVS(source.c_str(), "main", VertexMacros);
|
||||
@@ -599,6 +602,7 @@ const ComPtr<ID3D11VertexShader>& DX11Shaders::getMVVertexShader(bool naomi2)
|
||||
VertexMacros[MacroPositionOnly].Definition = MacroValues[true];
|
||||
VertexMacros[MacroTwoVolumes].Definition = MacroValues[false];
|
||||
VertexMacros[MacroLightOn].Definition = MacroValues[false];
|
||||
VertexMacros[MacroModifierVolume].Definition = MacroValues[true];
|
||||
modVolVertexShaders[index] = compileVS(DX11N2VertexShader, "main", VertexMacros);
|
||||
}
|
||||
}
|
||||
@@ -689,6 +693,7 @@ ComPtr<ID3DBlob> DX11Shaders::getVertexShaderBlob()
|
||||
// FIXME code dup
|
||||
VertexMacros[MacroPositionOnly].Definition = MacroValues[false];
|
||||
VertexMacros[MacroTwoVolumes].Definition = MacroValues[false];
|
||||
VertexMacros[MacroModifierVolume].Definition = MacroValues[false];
|
||||
std::string source(DX11N2VertexShader);
|
||||
source += std::string("\n") + DX11N2ColorShader;
|
||||
return compileShader(source.c_str(), "main", "vs_4_0", VertexMacros);
|
||||
@@ -700,6 +705,7 @@ ComPtr<ID3DBlob> DX11Shaders::getMVVertexShaderBlob()
|
||||
VertexMacros[MacroGouraud].Definition = MacroValues[false];
|
||||
VertexMacros[MacroPositionOnly].Definition = MacroValues[true];
|
||||
VertexMacros[MacroTwoVolumes].Definition = MacroValues[false];
|
||||
VertexMacros[MacroModifierVolume].Definition = MacroValues[true];
|
||||
return compileShader(DX11N2VertexShader, "main", "vs_4_0", VertexMacros);
|
||||
}
|
||||
|
||||
|
||||
@@ -132,6 +132,8 @@ ComPtr<ID3D11RenderTargetView>& DX11Context::getRenderTarget(int width, int heig
|
||||
|
||||
void DX11Context::presentFrame(ComPtr<ID3D11ShaderResourceView>& textureView, int width, int height)
|
||||
{
|
||||
ID3D11ShaderResourceView *nullSRView = nullptr;
|
||||
pDeviceContext->PSSetShaderResources(0, 1, &nullSRView);
|
||||
ComPtr<ID3D11RenderTargetView> renderTarget = getRenderTarget(width, height);
|
||||
pDeviceContext->OMSetRenderTargets(1, &renderTarget.get(), nullptr);
|
||||
|
||||
@@ -160,7 +162,11 @@ void DX11Context::presentFrame(ComPtr<ID3D11ShaderResourceView>& textureView, in
|
||||
|
||||
ID3D11RenderTargetView *nullView = nullptr;
|
||||
pDeviceContext->OMSetRenderTargets(1, &nullView, nullptr);
|
||||
pDeviceContext->PSSetShaderResources(0, 1, &this->textureView.get());
|
||||
}
|
||||
|
||||
void DX11Context::present()
|
||||
{
|
||||
pDeviceContext->PSSetShaderResources(0, 1, &textureView.get());
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -35,6 +35,7 @@ class DX11Context : public GraphicsContext
|
||||
public:
|
||||
bool init(ID3D11Device *device, ID3D11DeviceContext *deviceContext, pD3DCompile D3DCompile, D3D_FEATURE_LEVEL featureLevel);
|
||||
void term() override;
|
||||
void present();
|
||||
|
||||
const ComPtr<ID3D11Device>& getDevice() const { return pDevice; }
|
||||
const ComPtr<ID3D11DeviceContext>& getDeviceContext() const { return pDeviceContext; }
|
||||
|
||||
@@ -649,6 +649,7 @@ struct DX11OITRenderer : public DX11Renderer
|
||||
|
||||
bool Render() override
|
||||
{
|
||||
resetContextState();
|
||||
bool is_rtt = pvrrc.isRTT;
|
||||
|
||||
if (!is_rtt)
|
||||
|
||||
@@ -781,6 +781,7 @@ enum VertexMacroEnum {
|
||||
MacroDivPosZ,
|
||||
MacroPositionOnly,
|
||||
MacroLightOn,
|
||||
MacroModifierVolume,
|
||||
};
|
||||
|
||||
static D3D_SHADER_MACRO VertexMacros[]
|
||||
@@ -790,6 +791,7 @@ static D3D_SHADER_MACRO VertexMacros[]
|
||||
{ "DIV_POS_Z", "0" },
|
||||
{ "POSITION_ONLY", "0" },
|
||||
{ "LIGHT_ON", "1" },
|
||||
{ "MODIFIER_VOLUME", "0" },
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
@@ -900,6 +902,7 @@ const ComPtr<ID3D11VertexShader>& DX11OITShaders::getVertexShader(bool gouraud,
|
||||
VertexMacros[MacroPositionOnly].Definition = MacroValues[positionOnly];
|
||||
VertexMacros[MacroTwoVolumes].Definition = MacroValues[twoVolumes];
|
||||
VertexMacros[MacroLightOn].Definition = MacroValues[lightOn];
|
||||
VertexMacros[MacroModifierVolume].Definition = MacroValues[false];
|
||||
std::string source(DX11N2VertexShader);
|
||||
if (!positionOnly && lightOn)
|
||||
source += std::string("\n") + DX11N2ColorShader;
|
||||
@@ -927,6 +930,7 @@ const ComPtr<ID3D11VertexShader>& DX11OITShaders::getMVVertexShader(bool naomi2)
|
||||
VertexMacros[MacroPositionOnly].Definition = MacroValues[true];
|
||||
VertexMacros[MacroTwoVolumes].Definition = MacroValues[false];
|
||||
VertexMacros[MacroLightOn].Definition = MacroValues[false];
|
||||
VertexMacros[MacroModifierVolume].Definition = MacroValues[true];
|
||||
mvVertexShader = compileVS(DX11N2VertexShader, "main", VertexMacros);
|
||||
}
|
||||
}
|
||||
@@ -1058,6 +1062,7 @@ ComPtr<ID3DBlob> DX11OITShaders::getVertexShaderBlob()
|
||||
// FIXME code dup
|
||||
VertexMacros[MacroPositionOnly].Definition = MacroValues[false];
|
||||
VertexMacros[MacroTwoVolumes].Definition = MacroValues[true];
|
||||
VertexMacros[MacroModifierVolume].Definition = MacroValues[false];
|
||||
std::string source(DX11N2VertexShader);
|
||||
source += std::string("\n") + DX11N2ColorShader;
|
||||
return compileShader(source.c_str(), "main", "vs_5_0", VertexMacros);
|
||||
@@ -1069,6 +1074,7 @@ ComPtr<ID3DBlob> DX11OITShaders::getMVVertexShaderBlob()
|
||||
VertexMacros[MacroGouraud].Definition = MacroValues[false];
|
||||
VertexMacros[MacroPositionOnly].Definition = MacroValues[true];
|
||||
VertexMacros[MacroTwoVolumes].Definition = MacroValues[false];
|
||||
VertexMacros[MacroModifierVolume].Definition = MacroValues[true];
|
||||
return compileShader(DX11N2VertexShader, "main", "vs_5_0", VertexMacros);
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ N2Vertex4Source::N2Vertex4Source(const gl4PipelineShader* shader) : OpenGl4Sourc
|
||||
{
|
||||
addConstant("OIT_RENDER");
|
||||
addConstant("DIV_POS_Z", false);
|
||||
addConstant("MODIFIER_VOLUME", 0);
|
||||
if (shader == nullptr)
|
||||
{
|
||||
addConstant("POSITION_ONLY", 1);
|
||||
|
||||
@@ -642,6 +642,7 @@ static void create_modvol_shader()
|
||||
gl4.modvol_shader.ndcMat = glGetUniformLocation(gl4.modvol_shader.program, "ndcMat");
|
||||
|
||||
N2Vertex4Source n2VertexShader;
|
||||
n2VertexShader.setConstant("MODIFIER_VOLUME", true);
|
||||
fragmentShader.setConstant("DIV_POS_Z", false);
|
||||
gl4.n2ModVolShader.program = gl_CompileAndLink(n2VertexShader.generate().c_str(), fragmentShader.generate().c_str());
|
||||
gl4.n2ModVolShader.ndcMat = glGetUniformLocation(gl4.n2ModVolShader.program, "ndcMat");
|
||||
|
||||
@@ -1175,7 +1175,8 @@ void OpenGLRenderer::DrawOSD(bool clear_screen)
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, i * 4, 4);
|
||||
|
||||
glCheck();
|
||||
imguiDriver->setFrameRendered();
|
||||
if (clear_screen)
|
||||
imguiDriver->setFrameRendered();
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -121,6 +121,9 @@ void main()
|
||||
computeEnvMap(vtx_uv.xy, vpos.xyz, vnorm);
|
||||
#endif
|
||||
#endif
|
||||
#if MODIFIER_VOLUME == 1
|
||||
vpos.z = min(vpos.z, -0.001);
|
||||
#endif
|
||||
|
||||
vpos = projMat * vpos;
|
||||
wDivide(vpos);
|
||||
@@ -348,17 +351,18 @@ void computeBumpMap(inout vec4 color0, vec4 color1, vec3 position, vec3 normal,
|
||||
|
||||
)";
|
||||
|
||||
N2VertexSource::N2VertexSource(bool gouraud, bool geometryOnly, bool texture) : OpenGlSource()
|
||||
N2VertexSource::N2VertexSource(bool gouraud, bool modifierVolume, bool texture) : OpenGlSource()
|
||||
{
|
||||
addConstant("pp_Gouraud", gouraud);
|
||||
addConstant("POSITION_ONLY", geometryOnly);
|
||||
addConstant("pp_Gouraud", (int)gouraud);
|
||||
addConstant("POSITION_ONLY", (int)modifierVolume);
|
||||
addConstant("pp_TwoVolumes", 0);
|
||||
addConstant("pp_Texture", (int)texture);
|
||||
addConstant("LIGHT_ON", 1);
|
||||
addConstant("MODIFIER_VOLUME", (int)modifierVolume);
|
||||
|
||||
addSource(VertexCompatShader);
|
||||
addSource(GouraudSource);
|
||||
if (!geometryOnly)
|
||||
if (!modifierVolume)
|
||||
addSource(N2ColorShader);
|
||||
addSource(N2VertexShader);
|
||||
}
|
||||
@@ -22,7 +22,7 @@
|
||||
class N2VertexSource : public OpenGlSource
|
||||
{
|
||||
public:
|
||||
N2VertexSource(bool gouraud, bool geometryOnly, bool texture);
|
||||
N2VertexSource(bool gouraud, bool modifierVolume, bool texture);
|
||||
};
|
||||
|
||||
template<typename ShaderType>
|
||||
|
||||
+5
-2
@@ -26,10 +26,12 @@
|
||||
|
||||
#include <stb_image.h>
|
||||
|
||||
#ifndef LIBRETRO
|
||||
|
||||
#define OSD_TEX_W 512
|
||||
#define OSD_TEX_H 256
|
||||
|
||||
#if defined(__ANDROID__) && !defined(LIBRETRO)
|
||||
#ifdef __ANDROID__
|
||||
extern float vjoy_pos[15][8];
|
||||
#else
|
||||
|
||||
@@ -74,7 +76,7 @@ static void DrawButton(const float xy[8], u32 state)
|
||||
OSDVertex vtx;
|
||||
|
||||
vtx.r = vtx.g = vtx.b = (0x7F - 0x40 * state / 255) * vjoy_pos[VJOY_VISIBLE][0];
|
||||
vtx.a = 0xA0 * vjoy_pos[VJOY_VISIBLE][4];
|
||||
vtx.a = (100 - config::VirtualGamepadTransparency) * 0xff / 100 * vjoy_pos[VJOY_VISIBLE][4];
|
||||
vjoy_pos[VJOY_VISIBLE][4] += (vjoy_pos[VJOY_VISIBLE][0] - vjoy_pos[VJOY_VISIBLE][4]) / 2;
|
||||
|
||||
vtx.x = xy[0]; vtx.y = xy[1];
|
||||
@@ -172,6 +174,7 @@ u8 *loadOSDButtons(int &width, int &height)
|
||||
}
|
||||
return image_data;
|
||||
}
|
||||
#endif // LIBRETRO
|
||||
|
||||
u32 vmu_lcd_data[8][48 * 32];
|
||||
bool vmu_lcd_status[8];
|
||||
|
||||
@@ -54,11 +54,19 @@ static inline TileClipping GetTileClip(u32 val, const glm::mat4& viewport, int *
|
||||
csy = csy * 32;
|
||||
cey = (cey + 1) * 32;
|
||||
|
||||
if (csx <= 0 && csy <= 0 && cex >= 640 && cey >= 480 && tileClippingMode == TileClipping::Outside)
|
||||
if (csx == 0 && csy == 0 && cex >= 640 && cey >= 480)
|
||||
return TileClipping::Off;
|
||||
|
||||
if (!pvrrc.isRTT)
|
||||
{
|
||||
if (tileClippingMode == TileClipping::Outside && !config::EmulateFramebuffer)
|
||||
{
|
||||
// Intersect with framebuffer clipping
|
||||
csx = std::max<float>(csx, pvrrc.fb_X_CLIP.min);
|
||||
csy = std::max<float>(csy, pvrrc.fb_Y_CLIP.min);
|
||||
cex = std::min<float>(cex, pvrrc.fb_X_CLIP.max + 1);
|
||||
cey = std::min<float>(cey, pvrrc.fb_Y_CLIP.max + 1);
|
||||
}
|
||||
glm::vec4 clip_start(csx, csy, 0, 1);
|
||||
glm::vec4 clip_end(cex, cey, 0, 1);
|
||||
clip_start = viewport * clip_start;
|
||||
|
||||
@@ -290,14 +290,14 @@ inline static void getVideoShift(float& x, float& y)
|
||||
switch (SPG_LOAD.hcount)
|
||||
{
|
||||
case 857: // NTSC, VGA
|
||||
x = VO_STARTX.HStart - (vga ? 0xa8 : 0xa4);
|
||||
x = (int)VO_STARTX.HStart - (vga ? 0xa8 : 0xa4);
|
||||
break;
|
||||
case 863: // PAL
|
||||
x = VO_STARTX.HStart - 0xae;
|
||||
x = (int)VO_STARTX.HStart - 0xae;
|
||||
break;
|
||||
case 851: // Naomi
|
||||
case 850: // meltyb
|
||||
x = VO_STARTX.HStart - 0xa5; // a0 for 15 kHz
|
||||
x = (int)VO_STARTX.HStart - 0xa5; // a0 for 15 kHz
|
||||
break;
|
||||
default:
|
||||
x = 0;
|
||||
@@ -307,24 +307,24 @@ inline static void getVideoShift(float& x, float& y)
|
||||
switch (SPG_LOAD.vcount)
|
||||
{
|
||||
case 524: // NTSC, VGA
|
||||
y = VO_STARTY.VStart_field1 - (vga ? 0x28 : 0x12);
|
||||
y = (int)VO_STARTY.VStart_field1 - (vga ? 0x28 : 0x12);
|
||||
break;
|
||||
case 262: // NTSC 240p
|
||||
y = VO_STARTY.VStart_field1 - 0x11;
|
||||
y = (int)VO_STARTY.VStart_field1 - 0x11;
|
||||
break;
|
||||
case 624: // PAL
|
||||
y = VO_STARTY.VStart_field1 - 0x2d;
|
||||
y = (int)VO_STARTY.VStart_field1 - 0x2d;
|
||||
break;
|
||||
case 312: // PAL 240p
|
||||
y = VO_STARTY.VStart_field1 - 0x2e;
|
||||
y = (int)VO_STARTY.VStart_field1 - 0x2e;
|
||||
break;
|
||||
case 529: // Naomi 31 kHz
|
||||
case 528: // meltyb
|
||||
y = VO_STARTY.VStart_field1 - 0x24;
|
||||
y = (int)VO_STARTY.VStart_field1 - 0x24;
|
||||
break;
|
||||
case 536: // Naomi 15 kHz 480i
|
||||
case 268: // Naomi 15 kHz 240p
|
||||
y = VO_STARTY.VStart_field1 - 0x17; // 16 for 240p
|
||||
y = (int)VO_STARTY.VStart_field1 - 0x17; // 16 for 240p
|
||||
break;
|
||||
default:
|
||||
y = 0;
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
using namespace nlohmann;
|
||||
#include "archive/ZipArchive.h"
|
||||
#include "oslib/directory.h"
|
||||
#include "oslib/storage.h"
|
||||
#include "stdclass.h"
|
||||
|
||||
std::string getNativeLibraryPath();
|
||||
@@ -147,7 +148,7 @@ bool getCustomGpuDriverInfo(std::string& name, std::string& description, std::st
|
||||
|
||||
void uploadCustomGpuDriver(const std::string& zipPath)
|
||||
{
|
||||
FILE *zipf = nowide::fopen(zipPath.c_str(), "rb");
|
||||
FILE *zipf = hostfs::storage().openFile(zipPath, "r");
|
||||
if (zipf == nullptr)
|
||||
throw FlycastException("Can't open zip file");
|
||||
ZipArchive archive;
|
||||
|
||||
@@ -82,6 +82,9 @@ void BaseDrawer::SetBaseScissor(const vk::Extent2D& viewport)
|
||||
|
||||
void BaseDrawer::scaleAndWriteFramebuffer(vk::CommandBuffer commandBuffer, FramebufferAttachment *finalFB)
|
||||
{
|
||||
static const float scopeColor[4] = { 0.25f, 0.25f, 0.25f, 0.25f };
|
||||
CommandBufferDebugScope _(commandBuffer, "scaleAndWriteFramebuffer", scopeColor);
|
||||
|
||||
u32 width = (pvrrc.ta_GLOB_TILE_CLIP.tile_x_num + 1) * 32;
|
||||
u32 height = (pvrrc.ta_GLOB_TILE_CLIP.tile_y_num + 1) * 32;
|
||||
|
||||
@@ -162,6 +165,9 @@ void BaseDrawer::scaleAndWriteFramebuffer(vk::CommandBuffer commandBuffer, Frame
|
||||
|
||||
void Drawer::DrawPoly(const vk::CommandBuffer& cmdBuffer, u32 listType, bool sortTriangles, const PolyParam& poly, u32 first, u32 count)
|
||||
{
|
||||
static const float scopeColor[4] = { 0.25f, 0.50f, 0.25f, 1.0f };
|
||||
CommandBufferDebugScope _(cmdBuffer, "DrawPoly", scopeColor);
|
||||
|
||||
vk::Rect2D scissorRect;
|
||||
TileClipping tileClip = SetTileClip(poly.tileclip, scissorRect);
|
||||
if (tileClip == TileClipping::Outside)
|
||||
@@ -194,7 +200,7 @@ void Drawer::DrawPoly(const vk::CommandBuffer& cmdBuffer, u32 listType, bool sor
|
||||
|
||||
if (tileClip == TileClipping::Inside || trilinearAlpha != 1.f || gpuPalette != 0)
|
||||
{
|
||||
std::array<float, 6> pushConstants = {
|
||||
const std::array<float, 6> pushConstants = {
|
||||
(float)scissorRect.offset.x,
|
||||
(float)scissorRect.offset.y,
|
||||
(float)scissorRect.offset.x + (float)scissorRect.extent.width,
|
||||
@@ -239,6 +245,10 @@ void Drawer::DrawSorted(const vk::CommandBuffer& cmdBuffer, const std::vector<So
|
||||
{
|
||||
if (first == last)
|
||||
return;
|
||||
|
||||
static const float scopeColor[4] = { 0.25f, 0.50f, 0.50f, 1.0f };
|
||||
CommandBufferDebugScope _(cmdBuffer, "DrawSorted", scopeColor);
|
||||
|
||||
for (u32 idx = first; idx < last; idx++)
|
||||
DrawPoly(cmdBuffer, ListType_Translucent, true, pvrrc.global_param_tr[polys[idx].polyIndex], polys[idx].first, polys[idx].count);
|
||||
if (multipass && config::TranslucentPolygonDepthMask)
|
||||
@@ -267,6 +277,10 @@ void Drawer::DrawList(const vk::CommandBuffer& cmdBuffer, u32 listType, bool sor
|
||||
{
|
||||
if (first == last)
|
||||
return;
|
||||
|
||||
static const float scopeColor[4] = { 0.50f, 0.25f, 0.50f, 1.0f };
|
||||
CommandBufferDebugScope _(cmdBuffer, "DrawList", scopeColor);
|
||||
|
||||
const PolyParam *pp_end = polys.data() + last;
|
||||
for (const PolyParam *pp = &polys[first]; pp != pp_end; pp++)
|
||||
if (pp->count > 2)
|
||||
@@ -278,6 +292,9 @@ void Drawer::DrawModVols(const vk::CommandBuffer& cmdBuffer, int first, int coun
|
||||
if (count == 0 || pvrrc.modtrig.empty() || !config::ModifierVolumes)
|
||||
return;
|
||||
|
||||
static const float scopeColor[4] = { 0.75f, 0.25f, 0.25f, 1.0f };
|
||||
CommandBufferDebugScope _(cmdBuffer, "DrawModVols", scopeColor);
|
||||
|
||||
cmdBuffer.bindVertexBuffers(0, curMainBuffer, offsets.modVolOffset);
|
||||
SetScissor(cmdBuffer, baseScissor);
|
||||
|
||||
@@ -319,7 +336,7 @@ void Drawer::DrawModVols(const vk::CommandBuffer& cmdBuffer, int first, int coun
|
||||
}
|
||||
cmdBuffer.bindVertexBuffers(0, curMainBuffer, {0});
|
||||
|
||||
std::array<float, 5> pushConstants = { 1 - FPU_SHAD_SCALE.scale_factor / 256.f, 0, 0, 0, 0 };
|
||||
const std::array<float, 6> pushConstants = { 1 - FPU_SHAD_SCALE.scale_factor / 256.f, 0, 0, 0, 0, 0 };
|
||||
cmdBuffer.pushConstants<float>(pipelineManager->GetPipelineLayout(), vk::ShaderStageFlagBits::eFragment, 0, pushConstants);
|
||||
|
||||
pipeline = pipelineManager->GetModifierVolumePipeline(ModVolMode::Final, 0, false);
|
||||
@@ -384,7 +401,18 @@ bool Drawer::Draw(const Texture *fogTexture, const Texture *paletteTexture)
|
||||
|
||||
vk::CommandBuffer cmdBuffer = BeginRenderPass();
|
||||
|
||||
setFirstProvokingVertex(pvrrc);
|
||||
static const float scopeColor[4] = { 0.75f, 0.75f, 0.75f, 1.0f };
|
||||
CommandBufferDebugScope _(cmdBuffer, "Draw", scopeColor);
|
||||
|
||||
if (VulkanContext::Instance()->hasProvokingVertex())
|
||||
{
|
||||
// Pipelines are using VK_EXT_provoking_vertex, no need to
|
||||
// re-order vertices
|
||||
}
|
||||
else
|
||||
{
|
||||
setFirstProvokingVertex(pvrrc);
|
||||
}
|
||||
|
||||
// Upload vertex and index buffers
|
||||
VertexShaderUniforms vtxUniforms;
|
||||
@@ -402,7 +430,7 @@ bool Drawer::Draw(const Texture *fogTexture, const Texture *paletteTexture)
|
||||
cmdBuffer.bindIndexBuffer(curMainBuffer, offsets.indexOffset, vk::IndexType::eUint32);
|
||||
|
||||
// Make sure to push constants even if not used
|
||||
std::array<float, 5> pushConstants = { 0, 0, 0, 0, 0 };
|
||||
const std::array<float, 6> pushConstants = { 0, 0, 0, 0, 0, 0 };
|
||||
cmdBuffer.pushConstants<float>(pipelineManager->GetPipelineLayout(), vk::ShaderStageFlagBits::eFragment, 0, pushConstants);
|
||||
|
||||
RenderPass previous_pass{};
|
||||
@@ -491,13 +519,15 @@ vk::CommandBuffer TextureDrawer::BeginRenderPass()
|
||||
}
|
||||
textureCache->SetInFlight(texture);
|
||||
|
||||
constexpr vk::ImageUsageFlags imageUsage = vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eSampled;
|
||||
if (!texture->image || texture->format != vk::Format::eR8G8B8A8Unorm
|
||||
|| texture->extent.width != widthPow2 || texture->extent.height != heightPow2)
|
||||
|| texture->extent.width != widthPow2 || texture->extent.height != heightPow2
|
||||
|| (texture->usageFlags & imageUsage) != imageUsage)
|
||||
{
|
||||
texture->extent = vk::Extent2D(widthPow2, heightPow2);
|
||||
texture->format = vk::Format::eR8G8B8A8Unorm;
|
||||
texture->needsStaging = true;
|
||||
texture->CreateImage(vk::ImageTiling::eOptimal, vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eSampled,
|
||||
texture->CreateImage(vk::ImageTiling::eOptimal, imageUsage,
|
||||
vk::ImageLayout::eUndefined, vk::ImageAspectFlagBits::eColor);
|
||||
colorImageCurrentLayout = vk::ImageLayout::eUndefined;
|
||||
}
|
||||
|
||||
@@ -28,6 +28,9 @@
|
||||
void OITDrawer::DrawPoly(const vk::CommandBuffer& cmdBuffer, u32 listType, bool autosort, Pass pass,
|
||||
const PolyParam& poly, u32 first, u32 count)
|
||||
{
|
||||
static const float scopeColor[4] = { 0.25f, 0.50f, 0.25f, 1.0f };
|
||||
CommandBufferDebugScope _(cmdBuffer, "DrawPoly(OIT)", scopeColor);
|
||||
|
||||
vk::Rect2D scissorRect;
|
||||
TileClipping tileClip = SetTileClip(poly.tileclip, scissorRect);
|
||||
if (tileClip == TileClipping::Outside)
|
||||
@@ -133,6 +136,10 @@ void OITDrawer::DrawList(const vk::CommandBuffer& cmdBuffer, u32 listType, bool
|
||||
{
|
||||
if (first == last)
|
||||
return;
|
||||
|
||||
static const float scopeColor[4] = { 0.50f, 0.25f, 0.50f, 1.0f };
|
||||
CommandBufferDebugScope _(cmdBuffer, "DrawList(OIT)", scopeColor);
|
||||
|
||||
const PolyParam *pp_end = polys.data() + last;
|
||||
for (const PolyParam *pp = &polys[first]; pp != pp_end; pp++)
|
||||
if (pp->count > 2)
|
||||
@@ -145,6 +152,9 @@ void OITDrawer::DrawModifierVolumes(const vk::CommandBuffer& cmdBuffer, int firs
|
||||
if (count == 0 || pvrrc.modtrig.empty() || !config::ModifierVolumes)
|
||||
return;
|
||||
|
||||
static const float scopeColor[4] = { 0.75f, 0.25f, 0.25f, 1.0f };
|
||||
CommandBufferDebugScope _(cmdBuffer, "DrawModVols(OIT)", scopeColor);
|
||||
|
||||
cmdBuffer.bindVertexBuffers(0, curMainBuffer, offsets.modVolOffset);
|
||||
SetScissor(cmdBuffer, baseScissor);
|
||||
|
||||
@@ -264,15 +274,23 @@ void OITDrawer::UploadMainBuffer(const OITDescriptorSets::VertexShaderUniforms&
|
||||
vk::Framebuffer OITTextureDrawer::getFramebuffer(int renderPass, int renderPassCount)
|
||||
{
|
||||
if (renderPass < renderPassCount - 1)
|
||||
return *tempFramebuffers[(renderPassCount - 1 - renderPass) % 2];
|
||||
else
|
||||
{
|
||||
framebufferIndex = (renderPassCount - renderPass) % 2;
|
||||
return *tempFramebuffers[framebufferIndex];
|
||||
}
|
||||
else {
|
||||
framebufferIndex = 0;
|
||||
return *framebuffer;
|
||||
}
|
||||
}
|
||||
|
||||
bool OITDrawer::Draw(const Texture *fogTexture, const Texture *paletteTexture)
|
||||
{
|
||||
vk::CommandBuffer cmdBuffer = NewFrame();
|
||||
|
||||
static const float scopeColor[4] = { 0.75f, 0.75f, 0.75f, 1.0f };
|
||||
CommandBufferDebugScope _(cmdBuffer, "Draw(OIT)", scopeColor);
|
||||
|
||||
if (needAttachmentTransition)
|
||||
{
|
||||
needAttachmentTransition = false;
|
||||
@@ -323,7 +341,15 @@ bool OITDrawer::Draw(const Texture *fogTexture, const Texture *paletteTexture)
|
||||
bool firstFrameAfterInit = oitBuffers->isFirstFrameAfterInit();
|
||||
oitBuffers->OnNewFrame(cmdBuffer);
|
||||
|
||||
setFirstProvokingVertex(pvrrc);
|
||||
if (VulkanContext::Instance()->hasProvokingVertex())
|
||||
{
|
||||
// Pipelines are using VK_EXT_provoking_vertex, no need to
|
||||
// re-order vertices
|
||||
}
|
||||
else
|
||||
{
|
||||
setFirstProvokingVertex(pvrrc);
|
||||
}
|
||||
|
||||
// Upload vertex and index buffers
|
||||
UploadMainBuffer(vtxUniforms, fragUniforms);
|
||||
@@ -396,11 +422,21 @@ bool OITDrawer::Draw(const Texture *fogTexture, const Texture *paletteTexture)
|
||||
DrawList(cmdBuffer, ListType_Punch_Through, false, Pass::Color, pvrrc.global_param_pt, previous_pass.pt_count, current_pass.pt_count);
|
||||
|
||||
// TR
|
||||
if (current_pass.autosort)
|
||||
if (firstFrameAfterInit)
|
||||
{
|
||||
if (!firstFrameAfterInit)
|
||||
DrawList(cmdBuffer, ListType_Translucent, true, Pass::OIT, pvrrc.global_param_tr, previous_pass.tr_count, current_pass.tr_count);
|
||||
// Clear abuffers
|
||||
cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipelineManager->GetClearPipeline());
|
||||
quadBuffer->Bind(cmdBuffer);
|
||||
quadBuffer->Draw(cmdBuffer);
|
||||
|
||||
vk::MemoryBarrier memoryBarrier(vk::AccessFlagBits::eShaderWrite, vk::AccessFlagBits::eShaderRead);
|
||||
cmdBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eFragmentShader, vk::PipelineStageFlagBits::eFragmentShader,
|
||||
vk::DependencyFlagBits::eByRegion, memoryBarrier, nullptr, nullptr);
|
||||
cmdBuffer.bindVertexBuffers(0, curMainBuffer, {0});
|
||||
firstFrameAfterInit = false;
|
||||
}
|
||||
if (current_pass.autosort)
|
||||
DrawList(cmdBuffer, ListType_Translucent, true, Pass::OIT, pvrrc.global_param_tr, previous_pass.tr_count, current_pass.tr_count);
|
||||
else
|
||||
DrawList(cmdBuffer, ListType_Translucent, false, Pass::Color, pvrrc.global_param_tr, previous_pass.tr_count, current_pass.tr_count);
|
||||
|
||||
@@ -416,21 +452,7 @@ bool OITDrawer::Draw(const Texture *fogTexture, const Texture *paletteTexture)
|
||||
cmdBuffer.clearAttachments(vk::ClearAttachment(vk::ImageAspectFlagBits::eColor, 0, clear_colors[0]),
|
||||
vk::ClearRect(viewport, 0, 1));
|
||||
}
|
||||
SetScissor(cmdBuffer, baseScissor);
|
||||
|
||||
if (firstFrameAfterInit)
|
||||
{
|
||||
// missing the transparent stuff on the first frame cuz I'm lazy
|
||||
// Clear
|
||||
cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipelineManager->GetClearPipeline());
|
||||
quadBuffer->Bind(cmdBuffer);
|
||||
quadBuffer->Draw(cmdBuffer);
|
||||
|
||||
vk::MemoryBarrier memoryBarrier(vk::AccessFlagBits::eShaderWrite, vk::AccessFlagBits::eShaderRead);
|
||||
cmdBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eFragmentShader, vk::PipelineStageFlagBits::eFragmentShader,
|
||||
vk::DependencyFlagBits::eByRegion, memoryBarrier, nullptr, nullptr);
|
||||
firstFrameAfterInit = false;
|
||||
}
|
||||
// Tr modifier volumes
|
||||
if (GetContext()->GetVendorID() != VulkanContext::VENDOR_QUALCOMM) // Adreno bug
|
||||
{
|
||||
@@ -440,6 +462,7 @@ bool OITDrawer::Draw(const Texture *fogTexture, const Texture *paletteTexture)
|
||||
DrawModifierVolumes<true>(cmdBuffer, previous_pass.mvo_tr_count, current_pass.mvo_tr_count - previous_pass.mvo_tr_count, pvrrc.global_param_mvo_tr.data());
|
||||
}
|
||||
|
||||
SetScissor(cmdBuffer, viewport);
|
||||
vk::Pipeline pipeline = pipelineManager->GetFinalPipeline(dithering && finalPass);
|
||||
cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline);
|
||||
quadBuffer->Bind(cmdBuffer);
|
||||
@@ -467,10 +490,11 @@ void OITDrawer::MakeBuffers(int width, int height, vk::ImageUsageFlags colorUsag
|
||||
{
|
||||
oitBuffers->Init(width, height);
|
||||
|
||||
if (width <= maxWidth && height <= maxHeight)
|
||||
if (width <= maxWidth && height <= maxHeight && colorUsage == currentBufferUsage)
|
||||
return;
|
||||
maxWidth = std::max(maxWidth, width);
|
||||
maxHeight = std::max(maxHeight, height);
|
||||
currentBufferUsage = colorUsage;
|
||||
|
||||
for (auto& framebuffer : tempFramebuffers) {
|
||||
if (framebuffer)
|
||||
@@ -576,13 +600,15 @@ vk::CommandBuffer OITTextureDrawer::NewFrame()
|
||||
}
|
||||
textureCache->SetInFlight(texture);
|
||||
|
||||
constexpr vk::ImageUsageFlags imageUsage = vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eSampled;
|
||||
if (!texture->image || texture->format != vk::Format::eR8G8B8A8Unorm
|
||||
|| texture->extent.width != widthPow2 || texture->extent.height != heightPow2)
|
||||
|| texture->extent.width != widthPow2 || texture->extent.height != heightPow2
|
||||
|| (texture->usageFlags & imageUsage) != imageUsage)
|
||||
{
|
||||
texture->extent = vk::Extent2D(widthPow2, heightPow2);
|
||||
texture->format = vk::Format::eR8G8B8A8Unorm;
|
||||
texture->needsStaging = true;
|
||||
texture->CreateImage(vk::ImageTiling::eOptimal, vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eSampled,
|
||||
texture->CreateImage(vk::ImageTiling::eOptimal, imageUsage,
|
||||
vk::ImageLayout::eUndefined, vk::ImageAspectFlagBits::eColor);
|
||||
colorImageCurrentLayout = vk::ImageLayout::eUndefined;
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ protected:
|
||||
|
||||
void MakeBuffers(int width, int height, vk::ImageUsageFlags colorUsage = {});
|
||||
virtual vk::Framebuffer getFramebuffer(int renderPass, int renderPassCount) = 0;
|
||||
virtual int getFramebufferIndex() { return 0; }
|
||||
int getFramebufferIndex() { return framebufferIndex; }
|
||||
|
||||
vk::Rect2D viewport;
|
||||
std::array<std::unique_ptr<FramebufferAttachment>, 2> colorAttachments;
|
||||
@@ -92,6 +92,7 @@ protected:
|
||||
std::vector<bool> clearNeeded;
|
||||
int maxWidth = 0;
|
||||
int maxHeight = 0;
|
||||
int framebufferIndex = 0;
|
||||
|
||||
private:
|
||||
void DrawPoly(const vk::CommandBuffer& cmdBuffer, u32 listType, bool autosort, Pass pass,
|
||||
@@ -128,6 +129,7 @@ private:
|
||||
OITDescriptorSets descriptorSets;
|
||||
vk::Buffer curMainBuffer;
|
||||
bool dithering = false;
|
||||
vk::ImageUsageFlags currentBufferUsage {};
|
||||
};
|
||||
|
||||
class OITScreenDrawer : public OITDrawer
|
||||
@@ -136,9 +138,10 @@ public:
|
||||
void Init(SamplerManager *samplerManager, OITShaderManager *shaderManager, OITBuffers *oitBuffers,
|
||||
const vk::Extent2D& viewport)
|
||||
{
|
||||
if (!screenPipelineManager)
|
||||
if (!screenPipelineManager) {
|
||||
screenPipelineManager = std::make_unique<OITPipelineManager>();
|
||||
screenPipelineManager->Init(shaderManager, oitBuffers);
|
||||
screenPipelineManager->Init(shaderManager, oitBuffers);
|
||||
}
|
||||
OITDrawer::Init(samplerManager, screenPipelineManager.get(), oitBuffers);
|
||||
|
||||
MakeFramebuffers(viewport);
|
||||
@@ -186,7 +189,6 @@ public:
|
||||
|
||||
protected:
|
||||
vk::Framebuffer getFramebuffer(int renderPass, int renderPassCount) override;
|
||||
int getFramebufferIndex() override { return framebufferIndex; }
|
||||
|
||||
private:
|
||||
void MakeFramebuffers(const vk::Extent2D& viewport);
|
||||
@@ -195,7 +197,6 @@ private:
|
||||
bool frameRendered = false;
|
||||
float aspectRatio = 0.f;
|
||||
bool frameStarted = false;
|
||||
int framebufferIndex = 0;
|
||||
};
|
||||
|
||||
class OITTextureDrawer : public OITDrawer
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
void OITPipelineManager::CreatePipeline(u32 listType, bool autosort, const PolyParam& pp, Pass pass, int gpuPalette)
|
||||
{
|
||||
vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo = GetMainVertexInputStateCreateInfo();
|
||||
vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo = GetMainVertexInputStateCreateInfo(true, pp.isNaomi2());
|
||||
|
||||
// Input assembly state
|
||||
vk::PipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo(vk::PipelineInputAssemblyStateCreateFlags(),
|
||||
@@ -49,6 +49,17 @@ void OITPipelineManager::CreatePipeline(u32 listType, bool autosort, const PolyP
|
||||
0.0f, // depthBiasSlopeFactor
|
||||
1.0f // lineWidth
|
||||
);
|
||||
|
||||
// Dreamcast uses the last vertex as the provoking vertex, but Vulkan uses the first.
|
||||
// Utilize VK_EXT_provoking_vertex when available to set the provoking vertex to be the
|
||||
// last vertex
|
||||
vk::PipelineRasterizationProvokingVertexStateCreateInfoEXT provokingVertexInfo{};
|
||||
if (GetContext()->hasProvokingVertex())
|
||||
{
|
||||
provokingVertexInfo.provokingVertexMode = vk::ProvokingVertexModeEXT::eLastVertex;
|
||||
pipelineRasterizationStateCreateInfo.pNext = &provokingVertexInfo;
|
||||
}
|
||||
|
||||
vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo;
|
||||
|
||||
// Depth and stencil
|
||||
@@ -144,7 +155,7 @@ void OITPipelineManager::CreatePipeline(u32 listType, bool autosort, const PolyP
|
||||
OITShaderManager::FragmentShaderParams params = {};
|
||||
params.alphaTest = listType == ListType_Punch_Through;
|
||||
params.bumpmap = pp.tcw.PixelFmt == PixelBumpMap;
|
||||
params.clamping = pp.tsp.ColorClamp && (pvrrc.fog_clamp_min.full != 0 || pvrrc.fog_clamp_max.full != 0xffffffff);
|
||||
params.clamping = pp.tsp.ColorClamp;
|
||||
params.insideClipTest = (pp.tileclip >> 28) == 3;
|
||||
params.fog = config::Fog ? pp.tsp.FogCtrl : 2;
|
||||
params.gouraud = pp.pcw.Gouraud;
|
||||
@@ -336,7 +347,7 @@ void OITPipelineManager::CreateClearPipeline()
|
||||
&pipelineDynamicStateCreateInfo, // pDynamicState
|
||||
*pipelineLayout, // layout
|
||||
renderPasses->GetRenderPass(true, true), // renderPass
|
||||
2 // subpass
|
||||
1 // subpass
|
||||
);
|
||||
|
||||
clearPipeline = GetContext()->GetDevice().createGraphicsPipelineUnique(GetContext()->GetPipelineCache(), graphicsPipelineCreateInfo).value;
|
||||
|
||||
@@ -417,7 +417,7 @@ private:
|
||||
return ((int)mode << 2) | cullMode | ((u32)naomi2 << 5) | ((u32)(!settings.platform.isNaomi2() && config::NativeDepthInterpolation) << 6);
|
||||
}
|
||||
|
||||
vk::PipelineVertexInputStateCreateInfo GetMainVertexInputStateCreateInfo(bool full = true) const
|
||||
vk::PipelineVertexInputStateCreateInfo GetMainVertexInputStateCreateInfo(bool full = true, bool naomi2 = false) const
|
||||
{
|
||||
// Vertex input state
|
||||
static const vk::VertexInputBindingDescription vertexBindingDescriptions[] =
|
||||
@@ -427,11 +427,11 @@ private:
|
||||
static const vk::VertexInputAttributeDescription vertexInputAttributeDescriptions[] =
|
||||
{
|
||||
vk::VertexInputAttributeDescription(0, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, x)), // pos
|
||||
vk::VertexInputAttributeDescription(1, 0, vk::Format::eR8G8B8A8Uint, offsetof(Vertex, col)), // base color
|
||||
vk::VertexInputAttributeDescription(2, 0, vk::Format::eR8G8B8A8Uint, offsetof(Vertex, spc)), // offset color
|
||||
vk::VertexInputAttributeDescription(1, 0, vk::Format::eR8G8B8A8Unorm, offsetof(Vertex, col)), // base color
|
||||
vk::VertexInputAttributeDescription(2, 0, vk::Format::eR8G8B8A8Unorm, offsetof(Vertex, spc)), // offset color
|
||||
vk::VertexInputAttributeDescription(3, 0, vk::Format::eR32G32Sfloat, offsetof(Vertex, u)), // tex coord
|
||||
vk::VertexInputAttributeDescription(4, 0, vk::Format::eR8G8B8A8Uint, offsetof(Vertex, col1)), // base1 color
|
||||
vk::VertexInputAttributeDescription(5, 0, vk::Format::eR8G8B8A8Uint, offsetof(Vertex, spc1)), // offset1 color
|
||||
vk::VertexInputAttributeDescription(4, 0, vk::Format::eR8G8B8A8Unorm, offsetof(Vertex, col1)), // base1 color
|
||||
vk::VertexInputAttributeDescription(5, 0, vk::Format::eR8G8B8A8Unorm, offsetof(Vertex, spc1)), // offset1 color
|
||||
vk::VertexInputAttributeDescription(6, 0, vk::Format::eR32G32Sfloat, offsetof(Vertex, u1)), // tex1 coord
|
||||
vk::VertexInputAttributeDescription(7, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, nx)), // naomi2 normal
|
||||
};
|
||||
@@ -439,12 +439,29 @@ private:
|
||||
{
|
||||
vk::VertexInputAttributeDescription(0, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, x)), // pos
|
||||
};
|
||||
|
||||
const vk::VertexInputAttributeDescription* attributeDescription = vertexInputLightAttributeDescriptions;
|
||||
u32 attributeDescriptionSize = std::size(vertexInputLightAttributeDescriptions);
|
||||
|
||||
if (full)
|
||||
{
|
||||
attributeDescription = vertexInputAttributeDescriptions;
|
||||
|
||||
if (naomi2)
|
||||
{
|
||||
attributeDescriptionSize = std::size(vertexInputAttributeDescriptions);
|
||||
}
|
||||
else
|
||||
{
|
||||
// naomi2 normal not needed
|
||||
attributeDescriptionSize = std::size(vertexInputAttributeDescriptions) - 1;
|
||||
}
|
||||
}
|
||||
return vk::PipelineVertexInputStateCreateInfo(
|
||||
vk::PipelineVertexInputStateCreateFlags(),
|
||||
std::size(vertexBindingDescriptions),
|
||||
vertexBindingDescriptions,
|
||||
full ? std::size(vertexInputAttributeDescriptions) : std::size(vertexInputLightAttributeDescriptions),
|
||||
full ? vertexInputAttributeDescriptions : vertexInputLightAttributeDescriptions);
|
||||
vk::PipelineVertexInputStateCreateFlags(),
|
||||
std::size(vertexBindingDescriptions), vertexBindingDescriptions,
|
||||
attributeDescriptionSize, attributeDescription
|
||||
);
|
||||
}
|
||||
|
||||
void CreatePipeline(u32 listType, bool autosort, const PolyParam& pp, Pass pass, int gpuPalette);
|
||||
|
||||
@@ -28,10 +28,10 @@ vk::UniqueRenderPass RenderPasses::MakeRenderPass(bool initial, bool last, bool
|
||||
attach0,
|
||||
// OP+PT color attachment
|
||||
vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), vk::Format::eR8G8B8A8Unorm, vk::SampleCountFlagBits::e1,
|
||||
initial ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad,
|
||||
loadClear && initial ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad,
|
||||
last ? vk::AttachmentStoreOp::eDontCare : vk::AttachmentStoreOp::eStore,
|
||||
vk::AttachmentLoadOp::eDontCare, vk::AttachmentStoreOp::eDontCare,
|
||||
initial ? vk::ImageLayout::eUndefined : attach0.initialLayout, attach0.finalLayout),
|
||||
loadClear && initial ? vk::ImageLayout::eUndefined : attach0.finalLayout, attach0.finalLayout), // initial layout is eUndefined for rtt, so use final layout instead
|
||||
// OP+PT depth attachment
|
||||
vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), GetContext()->GetDepthFormat(), vk::SampleCountFlagBits::e1,
|
||||
initial ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad,
|
||||
@@ -89,6 +89,10 @@ vk::UniqueRenderPass RenderPasses::MakeRenderPass(bool initial, bool last, bool
|
||||
vk::AccessFlagBits::eInputAttachmentRead | vk::AccessFlagBits::eShaderRead,
|
||||
vk::AccessFlagBits::eDepthStencilAttachmentRead | vk::AccessFlagBits::eDepthStencilAttachmentWrite,
|
||||
vk::DependencyFlagBits::eByRegion);
|
||||
dependencies.emplace_back(1, 1, vk::PipelineStageFlagBits::eFragmentShader, vk::PipelineStageFlagBits::eFragmentShader,
|
||||
vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite,
|
||||
vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite,
|
||||
vk::DependencyFlagBits::eByRegion);
|
||||
dependencies.emplace_back(2, 2, vk::PipelineStageFlagBits::eFragmentShader, vk::PipelineStageFlagBits::eFragmentShader,
|
||||
vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite,
|
||||
vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite,
|
||||
|
||||
@@ -44,7 +44,7 @@ protected:
|
||||
virtual vk::AttachmentDescription GetAttachment0Description(bool initial, bool last, bool loadClear) const
|
||||
{
|
||||
return vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), vk::Format::eR8G8B8A8Unorm, vk::SampleCountFlagBits::e1,
|
||||
loadClear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad, vk::AttachmentStoreOp::eStore,
|
||||
initial && loadClear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad, vk::AttachmentStoreOp::eStore,
|
||||
vk::AttachmentLoadOp::eDontCare, vk::AttachmentStoreOp::eDontCare,
|
||||
config::EmulateFramebuffer && initial ? vk::ImageLayout::eTransferSrcOptimal : vk::ImageLayout::eShaderReadOnlyOptimal,
|
||||
config::EmulateFramebuffer && last ? vk::ImageLayout::eTransferSrcOptimal : vk::ImageLayout::eShaderReadOnlyOptimal);
|
||||
|
||||
@@ -37,11 +37,11 @@ layout (push_constant) uniform constants
|
||||
} pushConstants;
|
||||
|
||||
layout (location = 0) in vec4 in_pos;
|
||||
layout (location = 1) in uvec4 in_base;
|
||||
layout (location = 2) in uvec4 in_offs;
|
||||
layout (location = 1) in vec4 in_base;
|
||||
layout (location = 2) in vec4 in_offs;
|
||||
layout (location = 3) in mediump vec2 in_uv;
|
||||
layout (location = 4) in uvec4 in_base1; // New for OIT, only for OP/PT with 2-volume
|
||||
layout (location = 5) in uvec4 in_offs1;
|
||||
layout (location = 4) in vec4 in_base1; // New for OIT, only for OP/PT with 2-volume
|
||||
layout (location = 5) in vec4 in_offs1;
|
||||
layout (location = 6) in mediump vec2 in_uv1;
|
||||
|
||||
layout (location = 0) INTERPOLATION out highp vec4 vtx_base;
|
||||
@@ -59,11 +59,11 @@ void main()
|
||||
vpos /= vpos.z;
|
||||
vpos.z = vpos.w;
|
||||
#endif
|
||||
vtx_base = vec4(in_base) / 255.0;
|
||||
vtx_offs = vec4(in_offs) / 255.0;
|
||||
vtx_base = in_base;
|
||||
vtx_offs = in_offs;
|
||||
vtx_uv = vec3(in_uv, vpos.z);
|
||||
vtx_base1 = vec4(in_base1) / 255.0;
|
||||
vtx_offs1 = vec4(in_offs1) / 255.0;
|
||||
vtx_base1 = in_base1;
|
||||
vtx_offs1 = in_offs1;
|
||||
vtx_uv1 = in_uv1;
|
||||
#if pp_Gouraud == 1 && DIV_POS_Z != 1
|
||||
vtx_base *= vpos.z;
|
||||
@@ -638,11 +638,11 @@ layout (push_constant) uniform constants
|
||||
} pushConstants;
|
||||
|
||||
layout (location = 0) in vec4 in_pos;
|
||||
layout (location = 1) in uvec4 in_base;
|
||||
layout (location = 2) in uvec4 in_offs;
|
||||
layout (location = 1) in vec4 in_base;
|
||||
layout (location = 2) in vec4 in_offs;
|
||||
layout (location = 3) in mediump vec2 in_uv;
|
||||
layout (location = 4) in uvec4 in_base1;
|
||||
layout (location = 5) in uvec4 in_offs1;
|
||||
layout (location = 4) in vec4 in_base1;
|
||||
layout (location = 5) in vec4 in_offs1;
|
||||
layout (location = 6) in mediump vec2 in_uv1;
|
||||
layout (location = 7) in vec3 in_normal;
|
||||
|
||||
@@ -677,16 +677,16 @@ void wDivide(inout vec4 vpos)
|
||||
void main()
|
||||
{
|
||||
vec4 vpos = n2Uniform.mvMat * in_pos;
|
||||
vtx_base = vec4(in_base) / 255.0;
|
||||
vtx_offs = vec4(in_offs) / 255.0;
|
||||
vtx_base = in_base;
|
||||
vtx_offs = in_offs;
|
||||
|
||||
#if LIGHT_ON == 1
|
||||
vec3 vnorm = normalize(mat3(n2Uniform.normalMat) * in_normal);
|
||||
#endif
|
||||
|
||||
#if pp_TwoVolumes == 1
|
||||
vtx_base1 = vec4(in_base1) / 255.0;
|
||||
vtx_offs1 = vec4(in_offs1) / 255.0;
|
||||
vtx_base1 = in_base1;
|
||||
vtx_offs1 = in_offs1;
|
||||
vtx_uv1 = in_uv1;
|
||||
#if LIGHT_ON == 1
|
||||
// FIXME need offset0 and offset1 for bump maps
|
||||
|
||||
@@ -89,7 +89,7 @@ void VulkanOverlay::Prepare(vk::CommandBuffer cmdBuffer, bool vmu, bool crosshai
|
||||
texture->deferDeleteResource(VulkanContext::Instance());
|
||||
texture = createTexture(cmdBuffer, 48, 32, (u8*)vmu_lcd_data[i]);
|
||||
#ifdef VK_DEBUG
|
||||
VulkanContext::Instance()->setObjectName((VkImageView)texture->GetImageView(), vk::ImageView::objectType, "VMU " + std::to_string(i));
|
||||
VulkanContext::Instance()->setObjectName(texture->GetImageView(), "VMU " + std::to_string(i));
|
||||
#endif
|
||||
this->vmuLastChanged[i] = ::vmuLastChanged[i];
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ void PipelineManager::CreateModVolPipeline(ModVolMode mode, int cullMode, bool n
|
||||
|
||||
if (mode == ModVolMode::Final)
|
||||
{
|
||||
pipelineVertexInputStateCreateInfo = GetMainVertexInputStateCreateInfo(false);
|
||||
pipelineVertexInputStateCreateInfo = GetMainVertexInputStateCreateInfo(false, naomi2);
|
||||
pipelineInputAssemblyStateCreateInfo = vk::PipelineInputAssemblyStateCreateInfo(vk::PipelineInputAssemblyStateCreateFlags(),
|
||||
vk::PrimitiveTopology::eTriangleStrip);
|
||||
}
|
||||
@@ -161,7 +161,7 @@ void PipelineManager::CreateModVolPipeline(ModVolMode mode, int cullMode, bool n
|
||||
void PipelineManager::CreateDepthPassPipeline(int cullMode, bool naomi2)
|
||||
{
|
||||
// Vertex input state
|
||||
vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo = GetMainVertexInputStateCreateInfo(false);
|
||||
vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo = GetMainVertexInputStateCreateInfo(false, false);
|
||||
// Input assembly state
|
||||
vk::PipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo
|
||||
(
|
||||
@@ -189,6 +189,17 @@ void PipelineManager::CreateDepthPassPipeline(int cullMode, bool naomi2)
|
||||
0.0f, // depthBiasSlopeFactor
|
||||
1.0f // lineWidth
|
||||
);
|
||||
|
||||
// Dreamcast uses the last vertex as the provoking vertex, but Vulkan uses the first.
|
||||
// Utilize VK_EXT_provoking_vertex when available to set the provoking vertex to be the
|
||||
// last vertex
|
||||
vk::PipelineRasterizationProvokingVertexStateCreateInfoEXT provokingVertexInfo{};
|
||||
if (GetContext()->hasProvokingVertex())
|
||||
{
|
||||
provokingVertexInfo.provokingVertexMode = vk::ProvokingVertexModeEXT::eLastVertex;
|
||||
pipelineRasterizationStateCreateInfo.pNext = &provokingVertexInfo;
|
||||
}
|
||||
|
||||
vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo;
|
||||
|
||||
// Depth and stencil
|
||||
@@ -262,7 +273,7 @@ void PipelineManager::CreateDepthPassPipeline(int cullMode, bool naomi2)
|
||||
|
||||
void PipelineManager::CreatePipeline(u32 listType, bool sortTriangles, const PolyParam& pp, int gpuPalette, bool dithering)
|
||||
{
|
||||
vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo = GetMainVertexInputStateCreateInfo();
|
||||
vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo = GetMainVertexInputStateCreateInfo(true, pp.isNaomi2());
|
||||
|
||||
// Input assembly state
|
||||
vk::PipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo;
|
||||
@@ -295,6 +306,17 @@ void PipelineManager::CreatePipeline(u32 listType, bool sortTriangles, const Pol
|
||||
0.0f, // depthBiasSlopeFactor
|
||||
1.0f // lineWidth
|
||||
);
|
||||
|
||||
// Dreamcast uses the last vertex as the provoking vertex, but Vulkan uses the first.
|
||||
// Utilize VK_EXT_provoking_vertex when available to set the provoking vertex to be the
|
||||
// last vertex
|
||||
vk::PipelineRasterizationProvokingVertexStateCreateInfoEXT provokingVertexInfo{};
|
||||
if (GetContext()->hasProvokingVertex())
|
||||
{
|
||||
provokingVertexInfo.provokingVertexMode = vk::ProvokingVertexModeEXT::eLastVertex;
|
||||
pipelineRasterizationStateCreateInfo.pNext = &provokingVertexInfo;
|
||||
}
|
||||
|
||||
vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo;
|
||||
|
||||
// Depth and stencil
|
||||
@@ -375,7 +397,7 @@ void PipelineManager::CreatePipeline(u32 listType, bool sortTriangles, const Pol
|
||||
FragmentShaderParams params = {};
|
||||
params.alphaTest = listType == ListType_Punch_Through;
|
||||
params.bumpmap = pp.tcw.PixelFmt == PixelBumpMap;
|
||||
params.clamping = pp.tsp.ColorClamp && (pvrrc.fog_clamp_min.full != 0 || pvrrc.fog_clamp_max.full != 0xffffffff);
|
||||
params.clamping = pp.tsp.ColorClamp;
|
||||
params.insideClipTest = (pp.tileclip >> 28) == 3;
|
||||
params.fog = config::Fog ? pp.tsp.FogCtrl : 2;
|
||||
params.gouraud = pp.pcw.Gouraud;
|
||||
@@ -421,7 +443,7 @@ void OSDPipeline::CreatePipeline()
|
||||
static const vk::VertexInputBindingDescription vertexInputBindingDescription(0, sizeof(OSDVertex));
|
||||
static const std::array<vk::VertexInputAttributeDescription, 3> vertexInputAttributeDescriptions = {
|
||||
vk::VertexInputAttributeDescription(0, 0, vk::Format::eR32G32Sfloat, offsetof(OSDVertex, x)), // pos
|
||||
vk::VertexInputAttributeDescription(1, 0, vk::Format::eR8G8B8A8Uint, offsetof(OSDVertex, r)), // color
|
||||
vk::VertexInputAttributeDescription(1, 0, vk::Format::eR8G8B8A8Unorm, offsetof(OSDVertex, r)), // color
|
||||
vk::VertexInputAttributeDescription(2, 0, vk::Format::eR32G32Sfloat, offsetof(OSDVertex, u)), // tex coord
|
||||
};
|
||||
vk::PipelineVertexInputStateCreateInfo vertexInputStateCreateInfo(
|
||||
|
||||
@@ -286,7 +286,7 @@ private:
|
||||
return cullMode | ((int)naomi2 << 2) | ((int)(!settings.platform.isNaomi2() && config::NativeDepthInterpolation) << 3);
|
||||
}
|
||||
|
||||
vk::PipelineVertexInputStateCreateInfo GetMainVertexInputStateCreateInfo(bool full = true) const
|
||||
vk::PipelineVertexInputStateCreateInfo GetMainVertexInputStateCreateInfo(bool full = true, bool naomi2 = false) const
|
||||
{
|
||||
// Vertex input state
|
||||
static const vk::VertexInputBindingDescription vertexBindingDescriptions[] =
|
||||
@@ -296,8 +296,8 @@ private:
|
||||
static const vk::VertexInputAttributeDescription vertexInputAttributeDescriptions[] =
|
||||
{
|
||||
vk::VertexInputAttributeDescription(0, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, x)), // pos
|
||||
vk::VertexInputAttributeDescription(1, 0, vk::Format::eR8G8B8A8Uint, offsetof(Vertex, col)), // base color
|
||||
vk::VertexInputAttributeDescription(2, 0, vk::Format::eR8G8B8A8Uint, offsetof(Vertex, spc)), // offset color
|
||||
vk::VertexInputAttributeDescription(1, 0, vk::Format::eR8G8B8A8Unorm, offsetof(Vertex, col)), // base color
|
||||
vk::VertexInputAttributeDescription(2, 0, vk::Format::eR8G8B8A8Unorm, offsetof(Vertex, spc)), // offset color
|
||||
vk::VertexInputAttributeDescription(3, 0, vk::Format::eR32G32Sfloat, offsetof(Vertex, u)), // tex coord
|
||||
vk::VertexInputAttributeDescription(4, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, nx)), // naomi2 normal
|
||||
};
|
||||
@@ -305,12 +305,33 @@ private:
|
||||
{
|
||||
vk::VertexInputAttributeDescription(0, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, x)), // pos
|
||||
};
|
||||
|
||||
|
||||
const vk::VertexInputAttributeDescription* attributeDescription = vertexInputLightAttributeDescriptions;
|
||||
u32 attributeDescriptionSize = std::size(vertexInputLightAttributeDescriptions);
|
||||
|
||||
if (full)
|
||||
{
|
||||
attributeDescription = vertexInputAttributeDescriptions;
|
||||
|
||||
if (naomi2)
|
||||
{
|
||||
attributeDescriptionSize = std::size(vertexInputAttributeDescriptions);
|
||||
}
|
||||
else
|
||||
{
|
||||
// naomi2 normal not needed
|
||||
attributeDescriptionSize = std::size(vertexInputAttributeDescriptions) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return vk::PipelineVertexInputStateCreateInfo(
|
||||
vk::PipelineVertexInputStateCreateFlags(),
|
||||
std::size(vertexBindingDescriptions),
|
||||
vertexBindingDescriptions,
|
||||
full ? std::size(vertexInputAttributeDescriptions) : std::size(vertexInputLightAttributeDescriptions),
|
||||
full ? vertexInputAttributeDescriptions : vertexInputLightAttributeDescriptions);
|
||||
std::size(vertexBindingDescriptions), vertexBindingDescriptions,
|
||||
attributeDescriptionSize, attributeDescription
|
||||
);
|
||||
}
|
||||
|
||||
void CreatePipeline(u32 listType, bool sortTriangles, const PolyParam& pp, int gpuPalette, bool dithering);
|
||||
@@ -398,7 +419,7 @@ public:
|
||||
vk::SamplerCreateInfo(vk::SamplerCreateFlags(), vk::Filter::eLinear, vk::Filter::eLinear,
|
||||
vk::SamplerMipmapMode::eLinear, vk::SamplerAddressMode::eClampToEdge, vk::SamplerAddressMode::eClampToEdge,
|
||||
vk::SamplerAddressMode::eClampToEdge, 0.0f, false, 16.0f, false,
|
||||
vk::CompareOp::eNever, 0.0f, 0.0f, vk::BorderColor::eFloatOpaqueBlack));
|
||||
vk::CompareOp::eNever, 0.0f, vk::LodClampNone, vk::BorderColor::eFloatOpaqueBlack));
|
||||
}
|
||||
if (this->renderPass != renderPass)
|
||||
{
|
||||
|
||||
@@ -23,6 +23,19 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace {
|
||||
size_t hashQuadVertex(const QuadVertex& vertex)
|
||||
{
|
||||
size_t seed = 0;
|
||||
seed ^= std::hash<float>{}(vertex.x) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||
seed ^= std::hash<float>{}(vertex.y) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||
seed ^= std::hash<float>{}(vertex.z) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||
seed ^= std::hash<float>{}(vertex.u) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||
seed ^= std::hash<float>{}(vertex.v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||
return seed;
|
||||
}
|
||||
}
|
||||
|
||||
static VulkanContext *GetContext()
|
||||
{
|
||||
return VulkanContext::Instance();
|
||||
@@ -137,7 +150,7 @@ void QuadPipeline::Init(ShaderManager *shaderManager, vk::RenderPass renderPass,
|
||||
vk::SamplerAddressMode::eClampToEdge,
|
||||
vk::SamplerAddressMode::eClampToEdge,
|
||||
vk::SamplerAddressMode::eClampToEdge, 0.0f, false,
|
||||
16.0f, false, vk::CompareOp::eNever, 0.0f, 0.0f,
|
||||
16.0f, false, vk::CompareOp::eNever, 0.0f, vk::LodClampNone,
|
||||
vk::BorderColor::eFloatOpaqueBlack));
|
||||
}
|
||||
if (!nearestSampler)
|
||||
@@ -149,7 +162,7 @@ void QuadPipeline::Init(ShaderManager *shaderManager, vk::RenderPass renderPass,
|
||||
vk::SamplerAddressMode::eClampToEdge,
|
||||
vk::SamplerAddressMode::eClampToEdge,
|
||||
vk::SamplerAddressMode::eClampToEdge, 0.0f, false,
|
||||
16.0f, false, vk::CompareOp::eNever, 0.0f, 0.0f,
|
||||
16.0f, false, vk::CompareOp::eNever, 0.0f, vk::LodClampNone,
|
||||
vk::BorderColor::eFloatOpaqueBlack));
|
||||
}
|
||||
if (this->renderPass != renderPass)
|
||||
@@ -169,7 +182,7 @@ void QuadDrawer::Init(QuadPipeline *pipeline)
|
||||
descSet.reset();
|
||||
}
|
||||
|
||||
void QuadDrawer::Draw(vk::CommandBuffer commandBuffer, vk::ImageView imageView, QuadVertex vertices[], bool nearestFilter, const float *color)
|
||||
void QuadDrawer::Draw(vk::CommandBuffer commandBuffer, vk::ImageView imageView, QuadVertex vertices[4], bool nearestFilter, const float *color)
|
||||
{
|
||||
VulkanContext *context = GetContext();
|
||||
auto &descSet = descriptorSets[context->GetCurrentImageIndex()];
|
||||
@@ -198,3 +211,13 @@ void QuadDrawer::Draw(vk::CommandBuffer commandBuffer, vk::ImageView imageView,
|
||||
commandBuffer.pushConstants(pipeline->GetPipelineLayout(), vk::ShaderStageFlagBits::eFragment, 0, sizeof(float) * 4, color);
|
||||
buffer->Draw(commandBuffer);
|
||||
}
|
||||
|
||||
size_t QuadBuffer::hashQuadVertexData(QuadVertex vertices[4]) const
|
||||
{
|
||||
size_t seed = 0;
|
||||
seed ^= hashQuadVertex(vertices[0]) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||
seed ^= hashQuadVertex(vertices[1]) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||
seed ^= hashQuadVertex(vertices[2]) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||
seed ^= hashQuadVertex(vertices[3]) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||
return seed;
|
||||
}
|
||||
+18
-3
@@ -50,11 +50,12 @@ public:
|
||||
commandBuffer.draw(4, 1, 0, 0);
|
||||
}
|
||||
|
||||
void Update(QuadVertex vertices[] = nullptr)
|
||||
void Update(QuadVertex vertices[4] = nullptr)
|
||||
{
|
||||
if (vertices == nullptr)
|
||||
{
|
||||
static QuadVertex defaultVtx[] {
|
||||
static QuadVertex defaultVtx[4]
|
||||
{
|
||||
{ -1.f, -1.f, 0.f, 0.f, 0.f },
|
||||
{ 1.f, -1.f, 0.f, 1.f, 0.f },
|
||||
{ -1.f, 1.f, 0.f, 0.f, 1.f },
|
||||
@@ -62,10 +63,24 @@ public:
|
||||
};
|
||||
vertices = defaultVtx;
|
||||
};
|
||||
|
||||
const size_t quadVerticesHash = hashQuadVertexData(vertices);;
|
||||
if (quadVerticesHash == lastUploadHash)
|
||||
{
|
||||
// data already uploaded
|
||||
return;
|
||||
}
|
||||
|
||||
// new data to upload, update hash
|
||||
lastUploadHash = quadVerticesHash;
|
||||
buffer->upload(sizeof(QuadVertex) * 4, vertices);
|
||||
|
||||
}
|
||||
private:
|
||||
std::unique_ptr<BufferData> buffer;
|
||||
|
||||
size_t hashQuadVertexData(QuadVertex vertices[4]) const;
|
||||
size_t lastUploadHash = 0;
|
||||
};
|
||||
|
||||
class QuadPipeline
|
||||
@@ -119,7 +134,7 @@ public:
|
||||
QuadDrawer& operator=(const QuadDrawer &) = delete;
|
||||
|
||||
void Init(QuadPipeline *pipeline);
|
||||
void Draw(vk::CommandBuffer commandBuffer, vk::ImageView imageView, QuadVertex vertices[] = nullptr, bool nearestFilter = false, const float *color = nullptr);
|
||||
void Draw(vk::CommandBuffer commandBuffer, vk::ImageView imageView, QuadVertex vertices[4] = nullptr, bool nearestFilter = false, const float *color = nullptr);
|
||||
private:
|
||||
QuadPipeline *pipeline = nullptr;
|
||||
std::unique_ptr<QuadBuffer> buffer;
|
||||
|
||||
@@ -30,8 +30,8 @@ layout (std140, set = 0, binding = 0) uniform VertexShaderUniforms
|
||||
} uniformBuffer;
|
||||
|
||||
layout (location = 0) in vec4 in_pos;
|
||||
layout (location = 1) in uvec4 in_base;
|
||||
layout (location = 2) in uvec4 in_offs;
|
||||
layout (location = 1) in vec4 in_base;
|
||||
layout (location = 2) in vec4 in_offs;
|
||||
layout (location = 3) in mediump vec2 in_uv;
|
||||
|
||||
layout (location = 0) INTERPOLATION out highp vec4 vtx_base;
|
||||
@@ -45,8 +45,8 @@ void main()
|
||||
vpos /= vpos.z;
|
||||
vpos.z = vpos.w;
|
||||
#endif
|
||||
vtx_base = vec4(in_base) / 255.0;
|
||||
vtx_offs = vec4(in_offs) / 255.0;
|
||||
vtx_base = in_base;
|
||||
vtx_offs = in_offs;
|
||||
vtx_uv = vec3(in_uv, vpos.z);
|
||||
#if pp_Gouraud == 1 && DIV_POS_Z != 1
|
||||
vtx_base *= vpos.z;
|
||||
@@ -396,14 +396,14 @@ void main()
|
||||
|
||||
static const char OSDVertexShaderSource[] = R"(
|
||||
layout (location = 0) in vec4 inPos;
|
||||
layout (location = 1) in uvec4 inColor;
|
||||
layout (location = 1) in vec4 inColor;
|
||||
layout (location = 2) in vec2 inUV;
|
||||
layout (location = 0) out lowp vec4 outColor;
|
||||
layout (location = 1) out mediump vec2 outUV;
|
||||
|
||||
void main()
|
||||
{
|
||||
outColor = inColor / 255.0;
|
||||
outColor = inColor;
|
||||
outUV = inUV;
|
||||
gl_Position = inPos;
|
||||
}
|
||||
@@ -659,8 +659,8 @@ layout (std140, set = 0, binding = 0) uniform VertexShaderUniforms
|
||||
} uniformBuffer;
|
||||
|
||||
layout (location = 0) in vec4 in_pos;
|
||||
layout (location = 1) in uvec4 in_base;
|
||||
layout (location = 2) in uvec4 in_offs;
|
||||
layout (location = 1) in vec4 in_base;
|
||||
layout (location = 2) in vec4 in_offs;
|
||||
layout (location = 3) in mediump vec2 in_uv;
|
||||
layout (location = 4) in vec3 in_normal;
|
||||
|
||||
@@ -684,8 +684,8 @@ void wDivide(inout vec4 vpos)
|
||||
void main()
|
||||
{
|
||||
vec4 vpos = n2Uniform.mvMat * in_pos;
|
||||
vtx_base = vec4(in_base) / 255.0;
|
||||
vtx_offs = vec4(in_offs) / 255.0;
|
||||
vtx_base = in_base;
|
||||
vtx_offs = in_offs;
|
||||
|
||||
vec3 vnorm = normalize(mat3(n2Uniform.normalMat) * in_normal);
|
||||
|
||||
@@ -743,6 +743,7 @@ void wDivide(inout vec4 vpos)
|
||||
void main()
|
||||
{
|
||||
vec4 vpos = n2Uniform.mvMat * in_pos;
|
||||
vpos.z = min(vpos.z, -0.001);
|
||||
vpos = n2Uniform.projMat * vpos;
|
||||
wDivide(vpos);
|
||||
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
|
||||
void setImageLayout(vk::CommandBuffer const& commandBuffer, vk::Image image, vk::Format format, u32 mipmapLevels, vk::ImageLayout oldImageLayout, vk::ImageLayout newImageLayout)
|
||||
{
|
||||
static const float scopeColor[4] = { 0.75f, 0.75f, 0.0f, 1.0f };
|
||||
CommandBufferDebugScope _(commandBuffer, "setImageLayout", scopeColor);
|
||||
|
||||
vk::AccessFlags sourceAccessMask;
|
||||
switch (oldImageLayout)
|
||||
{
|
||||
@@ -229,9 +232,10 @@ void Texture::Init(u32 width, u32 height, vk::Format format, u32 dataSize, bool
|
||||
CreateImage(imageTiling, usageFlags, initialLayout, vk::ImageAspectFlagBits::eColor);
|
||||
}
|
||||
|
||||
void Texture::CreateImage(vk::ImageTiling tiling, const vk::ImageUsageFlags& usage, vk::ImageLayout initialLayout,
|
||||
const vk::ImageAspectFlags& aspectMask)
|
||||
void Texture::CreateImage(vk::ImageTiling tiling, vk::ImageUsageFlags usage, vk::ImageLayout initialLayout,
|
||||
vk::ImageAspectFlags aspectMask)
|
||||
{
|
||||
this->usageFlags = usage;
|
||||
vk::ImageCreateInfo imageCreateInfo(vk::ImageCreateFlags(), vk::ImageType::e2D, format, vk::Extent3D(extent, 1), mipmapLevels, 1,
|
||||
vk::SampleCountFlagBits::e1, tiling, usage,
|
||||
vk::SharingMode::eExclusive, nullptr, initialLayout);
|
||||
@@ -250,8 +254,8 @@ void Texture::CreateImage(vk::ImageTiling tiling, const vk::ImageUsageFlags& usa
|
||||
#ifdef VK_DEBUG
|
||||
char name[128];
|
||||
sprintf(name, "texture @ %x", startAddress);
|
||||
VulkanContext::Instance()->setObjectName((VkImage)image.get(), vk::Image::objectType, name);
|
||||
VulkanContext::Instance()->setObjectName((VkImageView)imageView.get(), vk::ImageView::objectType, name);
|
||||
VulkanContext::Instance()->setObjectName(image.get(), name);
|
||||
VulkanContext::Instance()->setObjectName(imageView.get(), name);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -259,6 +263,9 @@ void Texture::SetImage(u32 srcSize, const void *srcData, bool isNew, bool genMip
|
||||
{
|
||||
verify((bool)commandBuffer);
|
||||
|
||||
static const float scopeColor[4] = { 1.0f, 1.0f, 0.0f, 1.0f };
|
||||
CommandBufferDebugScope _(commandBuffer, "SetImage", scopeColor);
|
||||
|
||||
if (!isNew && !needsStaging)
|
||||
setImageLayout(commandBuffer, image.get(), format, mipmapLevels, vk::ImageLayout::eShaderReadOnlyOptimal, vk::ImageLayout::eGeneral);
|
||||
|
||||
@@ -353,6 +360,9 @@ void Texture::SetImage(u32 srcSize, const void *srcData, bool isNew, bool genMip
|
||||
|
||||
void Texture::GenerateMipmaps()
|
||||
{
|
||||
static const float scopeColor[4] = { 0.75f, 0.75f, 0.0f, 1.0f };
|
||||
CommandBufferDebugScope _(commandBuffer, "GenerateMipmaps", scopeColor);
|
||||
|
||||
u32 mipWidth = extent.width;
|
||||
u32 mipHeight = extent.height;
|
||||
vk::ImageMemoryBarrier barrier(vk::AccessFlagBits::eTransferWrite, vk::AccessFlagBits::eTransferRead,
|
||||
@@ -443,7 +453,7 @@ void FramebufferAttachment::Init(u32 width, u32 height, vk::Format format, const
|
||||
image = device.createImageUnique(imageCreateInfo);
|
||||
#ifdef VK_DEBUG
|
||||
if (!name.empty())
|
||||
VulkanContext::Instance()->setObjectName((VkImage)image.get(), vk::Image::objectType, name);
|
||||
VulkanContext::Instance()->setObjectName(image.get(), name);
|
||||
#endif
|
||||
|
||||
VmaAllocationCreateInfo allocCreateInfo = { VmaAllocationCreateFlags(), VmaMemoryUsage::VMA_MEMORY_USAGE_GPU_ONLY };
|
||||
@@ -458,7 +468,7 @@ void FramebufferAttachment::Init(u32 width, u32 height, vk::Format format, const
|
||||
imageView = device.createImageViewUnique(imageViewCreateInfo);
|
||||
#ifdef VK_DEBUG
|
||||
if (!name.empty())
|
||||
VulkanContext::Instance()->setObjectName((VkImageView)imageView.get(), vk::ImageView::objectType, name);
|
||||
VulkanContext::Instance()->setObjectName(imageView.get(), name);
|
||||
#endif
|
||||
|
||||
if ((usage & vk::ImageUsageFlagBits::eDepthStencilAttachment) && (usage & vk::ImageUsageFlagBits::eInputAttachment))
|
||||
@@ -468,7 +478,7 @@ void FramebufferAttachment::Init(u32 width, u32 height, vk::Format format, const
|
||||
stencilView = device.createImageViewUnique(imageViewCreateInfo);
|
||||
#ifdef VK_DEBUG
|
||||
if (!name.empty())
|
||||
VulkanContext::Instance()->setObjectName((VkImageView)stencilView.get(), vk::ImageView::objectType, name);
|
||||
VulkanContext::Instance()->setObjectName(stencilView.get(), name);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,13 +69,14 @@ public:
|
||||
private:
|
||||
void Init(u32 width, u32 height, vk::Format format ,u32 dataSize, bool mipmapped, bool mipmapsIncluded);
|
||||
void SetImage(u32 size, const void *data, bool isNew, bool genMipmaps);
|
||||
void CreateImage(vk::ImageTiling tiling, const vk::ImageUsageFlags& usage, vk::ImageLayout initialLayout,
|
||||
const vk::ImageAspectFlags& aspectMask);
|
||||
void CreateImage(vk::ImageTiling tiling, vk::ImageUsageFlags usage, vk::ImageLayout initialLayout,
|
||||
vk::ImageAspectFlags aspectMask);
|
||||
void GenerateMipmaps();
|
||||
|
||||
vk::Format format = vk::Format::eUndefined;
|
||||
vk::Extent2D extent;
|
||||
u32 mipmapLevels = 1;
|
||||
vk::ImageUsageFlags usageFlags;
|
||||
bool needsStaging = false;
|
||||
std::unique_ptr<BufferData> stagingBufferData;
|
||||
vk::CommandBuffer commandBuffer;
|
||||
@@ -124,6 +125,11 @@ public:
|
||||
const vk::SamplerAddressMode vRepeat = tsp.ClampV ? vk::SamplerAddressMode::eClampToEdge
|
||||
: tsp.FlipV ? vk::SamplerAddressMode::eMirroredRepeat : vk::SamplerAddressMode::eRepeat;
|
||||
|
||||
// The W-axis is unused for 2D textures
|
||||
// Try to keep all three of the wrapping-modes the same by just repeating vRepeat for wRepeat
|
||||
// BestPractices-Arm-vkCreateSampler-different-wrapping-modes
|
||||
const vk::SamplerAddressMode wRepeat = vRepeat;
|
||||
|
||||
const bool anisotropicFiltering = config::AnisotropicFiltering > 1 && VulkanContext::Instance()->SupportsSamplerAnisotropy()
|
||||
&& filter == vk::Filter::eLinear && !punchThrough;
|
||||
#ifndef __APPLE__
|
||||
@@ -136,7 +142,7 @@ public:
|
||||
return samplers.emplace(
|
||||
std::make_pair(samplerHash, VulkanContext::Instance()->GetDevice().createSamplerUnique(
|
||||
vk::SamplerCreateInfo(vk::SamplerCreateFlags(), filter, filter,
|
||||
mipmapMode, uRepeat, vRepeat, vk::SamplerAddressMode::eClampToEdge, mipLodBias,
|
||||
mipmapMode, uRepeat, vRepeat, wRepeat, mipLodBias,
|
||||
anisotropicFiltering, std::min((float)config::AnisotropicFiltering, VulkanContext::Instance()->GetMaxSamplerAnisotropy()),
|
||||
false, vk::CompareOp::eNever,
|
||||
0.0f, vk::LodClampNone, vk::BorderColor::eFloatOpaqueBlack)))).first->second.get();
|
||||
|
||||
@@ -71,6 +71,9 @@ bool VkCreateDevice(retro_vulkan_context* context, VkInstance instance, VkPhysic
|
||||
physicalDevice = vkinstance.enumeratePhysicalDevices().front();
|
||||
}
|
||||
context->gpu = (VkPhysicalDevice)physicalDevice;
|
||||
|
||||
const vk::PhysicalDeviceProperties physicalDeviceProperties = physicalDevice.getProperties();
|
||||
|
||||
std::vector<vk::QueueFamilyProperties> queueFamilyProperties = physicalDevice.getQueueFamilyProperties();
|
||||
|
||||
// get the first index into queueFamilyProperties which supports graphics and compute
|
||||
@@ -124,54 +127,116 @@ bool VkCreateDevice(retro_vulkan_context* context, VkInstance instance, VkPhysic
|
||||
DEBUG_LOG(RENDERER, "Using distinct Graphics and Present queue families");
|
||||
}
|
||||
|
||||
vk::PhysicalDeviceFeatures supportedFeatures;
|
||||
physicalDevice.getFeatures(&supportedFeatures);
|
||||
VulkanContext::Instance()->fragmentStoresAndAtomics = supportedFeatures.fragmentStoresAndAtomics;
|
||||
VulkanContext::Instance()->samplerAnisotropy = supportedFeatures.samplerAnisotropy;
|
||||
|
||||
std::set<std::string> supportedExtensions;
|
||||
|
||||
const auto deviceExtensionProperties = physicalDevice.enumerateDeviceExtensionProperties();
|
||||
for (const auto& property : deviceExtensionProperties)
|
||||
{
|
||||
supportedExtensions.insert(property.extensionName);
|
||||
}
|
||||
|
||||
std::vector<const char*> enabledExtensions;
|
||||
|
||||
const auto tryAddDeviceExtension = [&supportedExtensions = std::as_const(supportedExtensions), &enabledExtensions]
|
||||
(std::string_view extensionName) -> bool
|
||||
{
|
||||
if (supportedExtensions.count(extensionName.data()))
|
||||
{
|
||||
enabledExtensions.push_back(extensionName.data());
|
||||
NOTICE_LOG(RENDERER, "Device extension enabled: %s", extensionName.data());
|
||||
return true;
|
||||
}
|
||||
NOTICE_LOG(RENDERER, "Device extension unavailable: %s", extensionName.data());
|
||||
return false;
|
||||
};
|
||||
|
||||
// Required swapchain extension
|
||||
tryAddDeviceExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
|
||||
|
||||
// Enable VK_KHR_dedicated_allocation if available
|
||||
bool getMemReq2Supported = false;
|
||||
VulkanContext::Instance()->dedicatedAllocationSupported = false;
|
||||
std::vector<const char *> deviceExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
|
||||
for (unsigned i = 0; i < num_required_device_extensions; i++)
|
||||
deviceExtensions.push_back(required_device_extensions[i]);
|
||||
for (const auto& property : physicalDevice.enumerateDeviceExtensionProperties())
|
||||
if (physicalDeviceProperties.apiVersion >= VK_API_VERSION_1_1)
|
||||
{
|
||||
if (!strcmp(property.extensionName, VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME))
|
||||
// Core in Vulkan 1.1
|
||||
VulkanContext::Instance()->dedicatedAllocationSupported = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
const bool getMemReq2Supported = tryAddDeviceExtension(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
|
||||
if (getMemReq2Supported)
|
||||
{
|
||||
deviceExtensions.push_back(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
|
||||
getMemReq2Supported = true;
|
||||
}
|
||||
else if (!strcmp(property.extensionName, VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME))
|
||||
{
|
||||
deviceExtensions.push_back(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME);
|
||||
VulkanContext::Instance()->dedicatedAllocationSupported = true;
|
||||
VulkanContext::Instance()->dedicatedAllocationSupported = tryAddDeviceExtension(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME);
|
||||
}
|
||||
}
|
||||
VulkanContext::Instance()->dedicatedAllocationSupported &= getMemReq2Supported;
|
||||
|
||||
// Check for VK_KHR_get_physical_device_properties2
|
||||
// Core as of Vulkan 1.1
|
||||
const bool getPhysicalDeviceProperties2Supported =
|
||||
(physicalDeviceProperties.apiVersion >= VK_API_VERSION_1_1)
|
||||
? true : tryAddDeviceExtension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
|
||||
|
||||
if (getPhysicalDeviceProperties2Supported)
|
||||
{
|
||||
// Enable VK_EXT_provoking_vertex if available
|
||||
VulkanContext::Instance()->provokingVertexSupported = tryAddDeviceExtension(VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
// Get device features
|
||||
|
||||
vk::PhysicalDeviceFeatures2 featuresChain{};
|
||||
vk::PhysicalDeviceFeatures& features = featuresChain.features;
|
||||
|
||||
vk::PhysicalDeviceProvokingVertexFeaturesEXT provokingVertexFeatures{};
|
||||
if (VulkanContext::Instance()->provokingVertexSupported)
|
||||
{
|
||||
featuresChain.pNext = &provokingVertexFeatures;
|
||||
}
|
||||
|
||||
// Get the physical device's features
|
||||
if (getPhysicalDeviceProperties2Supported && featuresChain.pNext)
|
||||
{
|
||||
physicalDevice.getFeatures2(&featuresChain);
|
||||
}
|
||||
else
|
||||
{
|
||||
physicalDevice.getFeatures(&features);
|
||||
}
|
||||
|
||||
if (VulkanContext::Instance()->provokingVertexSupported)
|
||||
{
|
||||
VulkanContext::Instance()->provokingVertexSupported &= provokingVertexFeatures.provokingVertexLast;
|
||||
}
|
||||
|
||||
VulkanContext::Instance()->samplerAnisotropy = features.samplerAnisotropy;
|
||||
VulkanContext::Instance()->fragmentStoresAndAtomics = features.fragmentStoresAndAtomics;
|
||||
|
||||
// create a Device
|
||||
float queuePriority = 1.0f;
|
||||
vk::DeviceQueueCreateInfo deviceQueueCreateInfos[] = {
|
||||
vk::DeviceQueueCreateInfo deviceQueueCreateInfo[] = {
|
||||
vk::DeviceQueueCreateInfo(vk::DeviceQueueCreateFlags(), context->queue_family_index, 1, &queuePriority),
|
||||
vk::DeviceQueueCreateInfo(vk::DeviceQueueCreateFlags(), context->presentation_queue_family_index, 1, &queuePriority),
|
||||
};
|
||||
vk::PhysicalDeviceFeatures features(*required_features);
|
||||
if (VulkanContext::Instance()->fragmentStoresAndAtomics)
|
||||
features.fragmentStoresAndAtomics = true;
|
||||
if (VulkanContext::Instance()->samplerAnisotropy)
|
||||
features.samplerAnisotropy = true;
|
||||
vk::Device device = physicalDevice.createDevice(vk::DeviceCreateInfo(vk::DeviceCreateFlags(),
|
||||
context->queue_family_index == context->presentation_queue_family_index ? 1 : 2, deviceQueueCreateInfos,
|
||||
num_required_device_layers, required_device_layers, deviceExtensions.size(), &deviceExtensions[0], &features));
|
||||
context->device = (VkDevice)device;
|
||||
bool singleQueue = context->queue_family_index == context->presentation_queue_family_index;
|
||||
vk::DeviceCreateInfo deviceCreateInfo;
|
||||
if (singleQueue)
|
||||
deviceCreateInfo = vk::DeviceCreateInfo(vk::DeviceCreateFlags(), deviceQueueCreateInfo[0], nullptr, enabledExtensions);
|
||||
else
|
||||
deviceCreateInfo = vk::DeviceCreateInfo(vk::DeviceCreateFlags(), deviceQueueCreateInfo, nullptr, enabledExtensions);
|
||||
|
||||
if (getPhysicalDeviceProperties2Supported)
|
||||
deviceCreateInfo.pNext = &featuresChain;
|
||||
else
|
||||
deviceCreateInfo.setPEnabledFeatures(&features);
|
||||
vk::Device newDevice = physicalDevice.createDevice(deviceCreateInfo);
|
||||
|
||||
context->device = (VkDevice)newDevice;
|
||||
#if VULKAN_HPP_DISPATCH_LOADER_DYNAMIC == 1
|
||||
VULKAN_HPP_DEFAULT_DISPATCHER.init(context->device);
|
||||
#endif
|
||||
|
||||
// Queues
|
||||
context->queue = (VkQueue)device.getQueue(context->queue_family_index, 0);
|
||||
context->presentation_queue = (VkQueue)device.getQueue(context->presentation_queue_family_index, 0);
|
||||
context->queue = (VkQueue)newDevice.getQueue(context->queue_family_index, 0);
|
||||
context->presentation_queue = (VkQueue)newDevice.getQueue(context->presentation_queue_family_index, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -329,7 +394,7 @@ void VulkanContext::PresentFrame(vk::Image image, vk::ImageView imageView, const
|
||||
getVideoShift(shiftX, shiftY);
|
||||
|
||||
beginFrame(extent);
|
||||
QuadVertex vtx[] {
|
||||
QuadVertex vtx[4] {
|
||||
{ -1, -1, 0, 0, 0 },
|
||||
{ 1, -1, 0, 1, 0 },
|
||||
{ -1, 1, 0, 0, 1 },
|
||||
|
||||
@@ -92,6 +92,7 @@ public:
|
||||
bool SupportsSamplerAnisotropy() const { return samplerAnisotropy; }
|
||||
bool SupportsDedicatedAllocation() const { return dedicatedAllocationSupported; }
|
||||
bool hasPerPixel() override { return fragmentStoresAndAtomics; }
|
||||
bool hasProvokingVertex() { return provokingVertexSupported; }
|
||||
const VMAllocator& GetAllocator() const { return allocator; }
|
||||
vk::DeviceSize GetMaxMemoryAllocationSize() const { return maxMemoryAllocationSize; }
|
||||
f32 GetMaxSamplerAnisotropy() const { return samplerAnisotropy ? maxSamplerAnisotropy : 1.f; }
|
||||
@@ -100,7 +101,10 @@ public:
|
||||
commandPool.addToFlight(object);
|
||||
}
|
||||
#ifdef VK_DEBUG
|
||||
void setObjectName(VkHandle object, vk::ObjectType objectType, const std::string& name) {}
|
||||
template<typename HandleType, typename = std::enable_if_t<vk::isVulkanHandleType<HandleType>::value>>
|
||||
void setObjectName(const HandleType& object, const std::string& name)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
constexpr static int VENDOR_AMD = 0x1022;
|
||||
@@ -126,10 +130,11 @@ private:
|
||||
bool optimalTilingSupported1555 = false;
|
||||
bool optimalTilingSupported4444 = false;
|
||||
public:
|
||||
bool fragmentStoresAndAtomics = false;
|
||||
bool samplerAnisotropy = false;
|
||||
f32 maxSamplerAnisotropy = 0.f;
|
||||
bool dedicatedAllocationSupported = false;
|
||||
bool fragmentStoresAndAtomics = false;
|
||||
bool provokingVertexSupported = false;
|
||||
private:
|
||||
u32 vendorID = 0;
|
||||
|
||||
|
||||
@@ -55,6 +55,8 @@ void VMAllocator::Init(vk::PhysicalDevice physicalDevice, vk::Device device, vk:
|
||||
allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT;
|
||||
|
||||
allocatorInfo.physicalDevice = (VkPhysicalDevice)physicalDevice;
|
||||
// Top-out at vulkan 1.1
|
||||
allocatorInfo.vulkanApiVersion = (physicalDevice.getProperties().apiVersion >= VK_API_VERSION_1_1) ? VK_API_VERSION_1_1 : VK_API_VERSION_1_0;
|
||||
allocatorInfo.device = (VkDevice)device;
|
||||
allocatorInfo.instance = (VkInstance)instance;
|
||||
#if !defined(NDEBUG) || defined(DEBUGFAST)
|
||||
|
||||
@@ -158,10 +158,9 @@ bool VulkanContext::InitInstance(const char** extensions, uint32_t extensions_co
|
||||
bool vulkan11 = false;
|
||||
if (VULKAN_HPP_DEFAULT_DISPATCHER.vkEnumerateInstanceVersion != nullptr)
|
||||
{
|
||||
u32 apiVersion = vk::enumerateInstanceVersion();
|
||||
const u32 apiVersion = vk::enumerateInstanceVersion();
|
||||
|
||||
vulkan11 = VK_API_VERSION_MAJOR(apiVersion) > 1
|
||||
|| (VK_API_VERSION_MAJOR(apiVersion) == 1 && VK_API_VERSION_MINOR(apiVersion) >= 1);
|
||||
vulkan11 = (apiVersion >= VK_API_VERSION_1_1);
|
||||
}
|
||||
|
||||
vk::ApplicationInfo applicationInfo("Flycast", 1, "Flycast", 1, vulkan11 ? VK_API_VERSION_1_1 : VK_API_VERSION_1_0);
|
||||
@@ -173,11 +172,11 @@ bool VulkanContext::InitInstance(const char** extensions, uint32_t extensions_co
|
||||
//layer_names.push_back("VK_LAYER_ARM_AGA");
|
||||
#ifdef VK_DEBUG
|
||||
#ifndef __ANDROID__
|
||||
vext.push_back("VK_EXT_debug_utils");
|
||||
vext.push_back("VK_EXT_debug_report");
|
||||
vext.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||
vext.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
|
||||
layer_names.push_back("VK_LAYER_KHRONOS_validation");
|
||||
#else
|
||||
vext.push_back("VK_EXT_debug_report"); // NDK <= 19?
|
||||
vext.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); // NDK <= 19?
|
||||
layer_names.push_back("VK_LAYER_GOOGLE_threading");
|
||||
layer_names.push_back("VK_LAYER_LUNARG_parameter_validation");
|
||||
layer_names.push_back("VK_LAYER_LUNARG_core_validation");
|
||||
@@ -207,25 +206,50 @@ bool VulkanContext::InitInstance(const char** extensions, uint32_t extensions_co
|
||||
#endif
|
||||
#endif
|
||||
|
||||
const auto devices = instance->enumeratePhysicalDevices();
|
||||
auto devices = instance->enumeratePhysicalDevices();
|
||||
if (devices.empty())
|
||||
{
|
||||
ERROR_LOG(RENDERER, "Vulkan error: no physical devices found");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Choose a discrete gpu if there's one, otherwise just pick the first one
|
||||
physicalDevice = nullptr;
|
||||
for (const auto& phyDev : devices)
|
||||
{
|
||||
if (phyDev.getProperties().deviceType == vk::PhysicalDeviceType::eDiscreteGpu)
|
||||
// The order of physical-devices provided by the driver should be somewhat preserved with stable-partitions/stable-sorts
|
||||
|
||||
// Prefer GPUs that support optimal R5G5B5/R5G6B5A1/R4G4B4A4
|
||||
const auto supportsOptimalFormat = [](vk::Format format)
|
||||
{
|
||||
physicalDevice = phyDev;
|
||||
break;
|
||||
return [format](const vk::PhysicalDevice& physicalDevice) -> bool
|
||||
{
|
||||
const vk::FormatProperties formatProperties = physicalDevice.getFormatProperties(format);
|
||||
return (formatProperties.optimalTilingFeatures & vk::FormatFeatureFlagBits::eSampledImage)
|
||||
&& (formatProperties.optimalTilingFeatures & vk::FormatFeatureFlagBits::eBlitDst)
|
||||
&& (formatProperties.optimalTilingFeatures & vk::FormatFeatureFlagBits::eBlitSrc);
|
||||
};
|
||||
};
|
||||
std::stable_partition(devices.begin(), devices.end(), supportsOptimalFormat(vk::Format::eR5G6B5UnormPack16));
|
||||
std::stable_partition(devices.begin(), devices.end(), supportsOptimalFormat(vk::Format::eR5G5B5A1UnormPack16));
|
||||
std::stable_partition(devices.begin(), devices.end(), supportsOptimalFormat(vk::Format::eR4G4B4A4UnormPack16));
|
||||
|
||||
// Prefer GPUs that support fragmentStoresAndAtomics
|
||||
std::stable_partition(
|
||||
devices.begin(), devices.end(),
|
||||
[](const vk::PhysicalDevice& physicalDevice) -> bool
|
||||
{
|
||||
return !!physicalDevice.getFeatures().fragmentStoresAndAtomics;
|
||||
}
|
||||
}
|
||||
if (!physicalDevice)
|
||||
physicalDevice = devices.front();
|
||||
);
|
||||
|
||||
// Finally, prefer Discrete GPUs
|
||||
std::stable_partition(
|
||||
devices.begin(), devices.end(),
|
||||
[](const vk::PhysicalDevice& physicalDevice) -> bool
|
||||
{
|
||||
return physicalDevice.getProperties().deviceType == vk::PhysicalDeviceType::eDiscreteGpu;
|
||||
}
|
||||
);
|
||||
|
||||
// Top of the device-list is the _most_ qualified GPU
|
||||
physicalDevice = devices.front();
|
||||
|
||||
vk::PhysicalDeviceProperties properties = physicalDevice.getProperties();
|
||||
if (vulkan11 && properties.apiVersion >= VK_API_VERSION_1_1)
|
||||
@@ -265,11 +289,6 @@ bool VulkanContext::InitInstance(const char** extensions, uint32_t extensions_co
|
||||
optimalTilingSupported4444 = true;
|
||||
else
|
||||
NOTICE_LOG(RENDERER, "eR4G4B4A4UnormPack16 not supported for optimal tiling");
|
||||
const auto features = physicalDevice.getFeatures();
|
||||
fragmentStoresAndAtomics = !!features.fragmentStoresAndAtomics;
|
||||
samplerAnisotropy = !!features.samplerAnisotropy;
|
||||
if (!fragmentStoresAndAtomics)
|
||||
NOTICE_LOG(RENDERER, "Fragment stores & atomic not supported: no per-pixel sorting");
|
||||
|
||||
ShaderCompiler::Init();
|
||||
|
||||
@@ -333,6 +352,8 @@ bool VulkanContext::InitDevice()
|
||||
return false;
|
||||
try
|
||||
{
|
||||
const vk::PhysicalDeviceProperties physicalDeviceProperties = physicalDevice.getProperties();
|
||||
|
||||
std::vector<vk::QueueFamilyProperties> queueFamilyProperties = physicalDevice.getQueueFamilyProperties();
|
||||
#ifdef VK_DEBUG
|
||||
std::for_each(queueFamilyProperties.begin(), queueFamilyProperties.end(),
|
||||
@@ -382,56 +403,117 @@ bool VulkanContext::InitDevice()
|
||||
else
|
||||
DEBUG_LOG(RENDERER, "Using distinct Graphics and Present queue families");
|
||||
|
||||
// Enable VK_KHR_dedicated_allocation if available
|
||||
bool getMemReq2Supported = false;
|
||||
dedicatedAllocationSupported = false;
|
||||
std::vector<const char *> deviceExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
|
||||
for (const auto& property : physicalDevice.enumerateDeviceExtensionProperties())
|
||||
|
||||
std::set<std::string> supportedExtensions;
|
||||
|
||||
const auto deviceExtensionProperties = physicalDevice.enumerateDeviceExtensionProperties();
|
||||
for (const auto& property : deviceExtensionProperties)
|
||||
{
|
||||
if (!strcmp(property.extensionName, VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME))
|
||||
{
|
||||
deviceExtensions.push_back(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
|
||||
getMemReq2Supported = true;
|
||||
}
|
||||
else if (!strcmp(property.extensionName, VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME))
|
||||
{
|
||||
deviceExtensions.push_back(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME);
|
||||
dedicatedAllocationSupported = true;
|
||||
}
|
||||
else if (!strcmp(property.extensionName, "VK_KHR_portability_subset"))
|
||||
deviceExtensions.push_back("VK_KHR_portability_subset");
|
||||
else if (!strcmp(property.extensionName, "VK_EXT_metal_objects"))
|
||||
deviceExtensions.push_back("VK_EXT_metal_objects");
|
||||
#ifdef VK_DEBUG
|
||||
else if (!strcmp(property.extensionName, VK_EXT_DEBUG_MARKER_EXTENSION_NAME))
|
||||
{
|
||||
NOTICE_LOG(RENDERER, "Debug extension %s available", property.extensionName.data());
|
||||
deviceExtensions.push_back(VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
|
||||
}
|
||||
else if(!strcmp(property.extensionName, VK_EXT_DEBUG_REPORT_EXTENSION_NAME))
|
||||
{
|
||||
NOTICE_LOG(RENDERER, "Debug extension %s available", property.extensionName.data());
|
||||
deviceExtensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
|
||||
}
|
||||
else if (!strcmp(property.extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME))
|
||||
{
|
||||
NOTICE_LOG(RENDERER, "Debug extension %s available", property.extensionName.data());
|
||||
deviceExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||
}
|
||||
#endif
|
||||
supportedExtensions.insert(property.extensionName);
|
||||
}
|
||||
dedicatedAllocationSupported &= getMemReq2Supported;
|
||||
|
||||
std::vector<const char*> enabledExtensions;
|
||||
|
||||
const auto tryAddDeviceExtension = [&supportedExtensions = std::as_const(supportedExtensions), &enabledExtensions]
|
||||
(std::string_view extensionName) -> bool
|
||||
{
|
||||
if (supportedExtensions.count(extensionName.data()))
|
||||
{
|
||||
enabledExtensions.push_back(extensionName.data());
|
||||
NOTICE_LOG(RENDERER, "Device extension enabled: %s", extensionName.data());
|
||||
return true;
|
||||
}
|
||||
NOTICE_LOG(RENDERER, "Device extension unavailable: %s", extensionName.data());
|
||||
return false;
|
||||
};
|
||||
|
||||
// Required swapchain extension
|
||||
tryAddDeviceExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
|
||||
|
||||
#ifdef VK_ENABLE_BETA_EXTENSIONS
|
||||
tryAddDeviceExtension(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME);
|
||||
#endif
|
||||
#ifdef VK_USE_PLATFORM_METAL_EXT
|
||||
tryAddDeviceExtension(VK_EXT_METAL_OBJECTS_EXTENSION_NAME);
|
||||
#endif
|
||||
#ifdef VK_DEBUG
|
||||
tryAddDeviceExtension(VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
|
||||
#endif
|
||||
|
||||
// Enable VK_KHR_dedicated_allocation if available
|
||||
if (physicalDeviceProperties.apiVersion >= VK_API_VERSION_1_1)
|
||||
{
|
||||
// Core in Vulkan 1.1
|
||||
dedicatedAllocationSupported = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
const bool getMemReq2Supported = tryAddDeviceExtension(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
|
||||
if (getMemReq2Supported)
|
||||
{
|
||||
dedicatedAllocationSupported = tryAddDeviceExtension(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for VK_KHR_get_physical_device_properties2
|
||||
// Core as of Vulkan 1.1
|
||||
const bool getPhysicalDeviceProperties2Supported =
|
||||
(physicalDeviceProperties.apiVersion >= VK_API_VERSION_1_1)
|
||||
? true : tryAddDeviceExtension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
|
||||
|
||||
if (getPhysicalDeviceProperties2Supported)
|
||||
{
|
||||
// Enable VK_EXT_provoking_vertex if available
|
||||
provokingVertexSupported = tryAddDeviceExtension(VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
// Get device features
|
||||
|
||||
vk::PhysicalDeviceFeatures2 featuresChain{};
|
||||
vk::PhysicalDeviceFeatures& features = featuresChain.features;
|
||||
|
||||
vk::PhysicalDeviceProvokingVertexFeaturesEXT provokingVertexFeatures{};
|
||||
if (provokingVertexSupported)
|
||||
{
|
||||
featuresChain.pNext = &provokingVertexFeatures;
|
||||
}
|
||||
|
||||
// Get the physical device's features
|
||||
if (getPhysicalDeviceProperties2Supported && featuresChain.pNext)
|
||||
{
|
||||
physicalDevice.getFeatures2(&featuresChain);
|
||||
}
|
||||
else
|
||||
{
|
||||
physicalDevice.getFeatures(&features);
|
||||
}
|
||||
|
||||
if (provokingVertexSupported)
|
||||
{
|
||||
provokingVertexSupported &= provokingVertexFeatures.provokingVertexLast;
|
||||
}
|
||||
|
||||
samplerAnisotropy = features.samplerAnisotropy;
|
||||
fragmentStoresAndAtomics = features.fragmentStoresAndAtomics;
|
||||
if (!fragmentStoresAndAtomics)
|
||||
NOTICE_LOG(RENDERER, "Fragment stores & atomic not supported: no per-pixel sorting");
|
||||
|
||||
// create a UniqueDevice
|
||||
float queuePriority = 1.0f;
|
||||
vk::DeviceQueueCreateInfo deviceQueueCreateInfo(vk::DeviceQueueCreateFlags(), graphicsQueueIndex, 1, &queuePriority);
|
||||
vk::PhysicalDeviceFeatures features;
|
||||
if (fragmentStoresAndAtomics)
|
||||
features.fragmentStoresAndAtomics = true;
|
||||
if (samplerAnisotropy)
|
||||
features.samplerAnisotropy = true;
|
||||
device = physicalDevice.createDeviceUnique(vk::DeviceCreateInfo(vk::DeviceCreateFlags(), deviceQueueCreateInfo,
|
||||
nullptr, deviceExtensions, &features));
|
||||
|
||||
if (getPhysicalDeviceProperties2Supported)
|
||||
{
|
||||
vk::DeviceCreateInfo deviceCreateInfo(vk::DeviceCreateFlags(), deviceQueueCreateInfo,
|
||||
nullptr, enabledExtensions);
|
||||
deviceCreateInfo.pNext = &featuresChain;
|
||||
device = physicalDevice.createDeviceUnique(deviceCreateInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
device = physicalDevice.createDeviceUnique(vk::DeviceCreateInfo(vk::DeviceCreateFlags(), deviceQueueCreateInfo,
|
||||
nullptr, enabledExtensions, &features));
|
||||
}
|
||||
|
||||
#if VULKAN_HPP_DISPATCH_LOADER_DYNAMIC == 1
|
||||
VULKAN_HPP_DEFAULT_DISPATCHER.init(*device);
|
||||
@@ -690,13 +772,16 @@ void VulkanContext::CreateSwapChain()
|
||||
|
||||
framebuffers.reserve(imageViews.size());
|
||||
drawFences.reserve(imageViews.size());
|
||||
renderCompleteSemaphores.reserve(imageViews.size());
|
||||
imageAcquiredSemaphores.reserve(imageViews.size());
|
||||
for (auto const& view : imageViews)
|
||||
{
|
||||
framebuffers.push_back(device->createFramebufferUnique(vk::FramebufferCreateInfo(vk::FramebufferCreateFlags(), *renderPass,
|
||||
view.get(), width, height, 1)));
|
||||
drawFences.push_back(device->createFenceUnique(vk::FenceCreateInfo(vk::FenceCreateFlagBits::eSignaled)));
|
||||
}
|
||||
renderCompleteSemaphores.reserve(imageViews.size() + 1);
|
||||
imageAcquiredSemaphores.reserve(imageViews.size() + 1);
|
||||
for (unsigned i = 0; i < imageViews.size() + 1; i++)
|
||||
{
|
||||
renderCompleteSemaphores.push_back(device->createSemaphoreUnique(vk::SemaphoreCreateInfo()));
|
||||
imageAcquiredSemaphores.push_back(device->createSemaphoreUnique(vk::SemaphoreCreateInfo()));
|
||||
}
|
||||
@@ -737,17 +822,15 @@ bool VulkanContext::init()
|
||||
SDL_Vulkan_GetInstanceExtensions((SDL_Window *)window, &extensionsCount, NULL);
|
||||
extensions.resize(extensionsCount + extensions.size());
|
||||
SDL_Vulkan_GetInstanceExtensions((SDL_Window *)window, &extensionsCount, &extensions[extensions.size() - extensionsCount]);
|
||||
#elif defined(_WIN32)
|
||||
#elif defined(VK_USE_PLATFORM_WIN32_KHR)
|
||||
extern void CreateMainWindow();
|
||||
CreateMainWindow();
|
||||
extensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
|
||||
#elif defined(VK_USE_PLATFORM_IOS_MVK)
|
||||
extensions.push_back(VK_MVK_IOS_SURFACE_EXTENSION_NAME);
|
||||
#elif defined(VK_USE_PLATFORM_METAL_EXT)
|
||||
extensions.push_back(VK_EXT_METAL_SURFACE_EXTENSION_NAME);
|
||||
#elif defined(SUPPORT_X11)
|
||||
#elif defined(VK_USE_PLATFORM_XLIB_KHR)
|
||||
extensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
|
||||
#elif defined(__ANDROID__)
|
||||
#elif defined(VK_USE_PLATFORM_ANDROID_KHR)
|
||||
extensions.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
|
||||
#endif
|
||||
if (!InitInstance(&extensions[0], extensions.size())) {
|
||||
@@ -772,18 +855,15 @@ bool VulkanContext::init()
|
||||
settings.display.dpi = roundf(std::max(hdpi, vdpi));
|
||||
|
||||
sdl_fix_steamdeck_dpi(sdlWin);
|
||||
#elif defined(_WIN32)
|
||||
#elif defined(VK_USE_PLATFORM_WIN32_KHR)
|
||||
vk::Win32SurfaceCreateInfoKHR createInfo(vk::Win32SurfaceCreateFlagsKHR(), GetModuleHandle(NULL), (HWND)window);
|
||||
surface = instance->createWin32SurfaceKHRUnique(createInfo);
|
||||
#elif defined(SUPPORT_X11)
|
||||
#elif defined(VK_USE_PLATFORM_XLIB_KHR)
|
||||
vk::XlibSurfaceCreateInfoKHR createInfo(vk::XlibSurfaceCreateFlagsKHR(), (Display*)display, (Window)window);
|
||||
surface = instance->createXlibSurfaceKHRUnique(createInfo);
|
||||
#elif defined(__ANDROID__)
|
||||
#elif defined(VK_USE_PLATFORM_ANDROID_KHR)
|
||||
vk::AndroidSurfaceCreateInfoKHR createInfo(vk::AndroidSurfaceCreateFlagsKHR(), (struct ANativeWindow*)window);
|
||||
surface = instance->createAndroidSurfaceKHRUnique(createInfo);
|
||||
#elif defined(VK_USE_PLATFORM_IOS_MVK)
|
||||
vk::IOSSurfaceCreateInfoMVK createInfo(vk::IOSSurfaceCreateFlagsMVK(), window);
|
||||
surface = instance->createIOSSurfaceMVKUnique(createInfo);
|
||||
#elif defined(VK_USE_PLATFORM_METAL_EXT)
|
||||
vk::MetalSurfaceCreateInfoEXT createInfo(vk::MetalSurfaceCreateFlagsEXT(), window);
|
||||
surface = instance->createMetalSurfaceEXTUnique(createInfo);
|
||||
@@ -866,14 +946,14 @@ void VulkanContext::Present() noexcept
|
||||
DoSwapAutomation();
|
||||
vk::Result res = presentQueue.presentKHR(vk::PresentInfoKHR(1, &(*renderCompleteSemaphores[currentSemaphore]), 1, &(*swapChain), ¤tImage));
|
||||
(void)res;
|
||||
currentSemaphore = (currentSemaphore + 1) % imageViews.size();
|
||||
currentSemaphore = (currentSemaphore + 1) % renderCompleteSemaphores.size();
|
||||
|
||||
if (lastFrameView && IsValid() && !gui_is_open())
|
||||
for (int i = 1; i < swapInterval; i++)
|
||||
{
|
||||
PresentFrame(vk::Image(), lastFrameView, lastFrameExtent, lastFrameAR);
|
||||
res = presentQueue.presentKHR(vk::PresentInfoKHR(1, &(*renderCompleteSemaphores[currentSemaphore]), 1, &(*swapChain), ¤tImage));
|
||||
currentSemaphore = (currentSemaphore + 1) % imageViews.size();
|
||||
currentSemaphore = (currentSemaphore + 1) % renderCompleteSemaphores.size();
|
||||
}
|
||||
} catch (const vk::SystemError& e) {
|
||||
// Happens when resizing the window
|
||||
@@ -897,7 +977,7 @@ void VulkanContext::Present() noexcept
|
||||
|
||||
void VulkanContext::DrawFrame(vk::ImageView imageView, const vk::Extent2D& extent, float aspectRatio)
|
||||
{
|
||||
QuadVertex vtx[] {
|
||||
QuadVertex vtx[4] {
|
||||
{ -1, -1, 0, 0, 0 },
|
||||
{ 1, -1, 0, 1, 0 },
|
||||
{ -1, 1, 0, 0, 1 },
|
||||
@@ -911,6 +991,10 @@ void VulkanContext::DrawFrame(vk::ImageView imageView, const vk::Extent2D& exten
|
||||
vtx[2].y = vtx[3].y = vtx[0].y + 2;
|
||||
|
||||
vk::CommandBuffer commandBuffer = GetCurrentCommandBuffer();
|
||||
|
||||
static const float scopeColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
|
||||
CommandBufferDebugScope _(commandBuffer, "DrawFrame", scopeColor);
|
||||
|
||||
if (config::Rotate90)
|
||||
quadRotatePipeline->BindPipeline(commandBuffer);
|
||||
else
|
||||
@@ -1264,6 +1348,10 @@ bool VulkanContext::GetLastFrame(std::vector<u8>& data, int& width, int& height)
|
||||
vk::UniqueCommandBuffer commandBuffer = std::move(device->allocateCommandBuffersUnique(
|
||||
vk::CommandBufferAllocateInfo(*commandPools.back(), vk::CommandBufferLevel::ePrimary, 1)).front());
|
||||
commandBuffer->begin(vk::CommandBufferBeginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit));
|
||||
|
||||
static const float scopeColor[4] = { 1.0f, 1.0f, 0.0f, 1.0f };
|
||||
CommandBufferDebugScope _(commandBuffer.get(), "GetLastFrame", scopeColor);
|
||||
|
||||
// render pass
|
||||
vk::AttachmentDescription attachmentDescription = vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), vk::Format::eR8G8B8A8Unorm, vk::SampleCountFlagBits::e1,
|
||||
vk::AttachmentLoadOp::eClear, vk::AttachmentStoreOp::eStore, vk::AttachmentLoadOp::eDontCare, vk::AttachmentStoreOp::eDontCare,
|
||||
@@ -1287,7 +1375,7 @@ bool VulkanContext::GetLastFrame(std::vector<u8>& data, int& width, int& height)
|
||||
pipeline.BindPipeline(*commandBuffer);
|
||||
|
||||
// Draw
|
||||
QuadVertex vtx[] {
|
||||
QuadVertex vtx[4] {
|
||||
{ -1, -1, 0, 0, 0 },
|
||||
{ 1, -1, 0, 1, 0 },
|
||||
{ -1, 1, 0, 0, 1 },
|
||||
|
||||
Loaded 100 of 131 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user