mirror of
https://github.com/PCSX2/pcsx2.git
synced 2026-07-15 03:34:29 +02:00
Compare commits
101 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a80b3144bb | |||
| 9f0206966b | |||
| dcae14398a | |||
| 14164e6592 | |||
| 873977d0b8 | |||
| 78319ef0ba | |||
| d1beb6be9a | |||
| 5df1662c1b | |||
| 45b8338f6d | |||
| 92914cee40 | |||
| 44079ebbe2 | |||
| 8d2d9ebb1d | |||
| 4f62bff866 | |||
| 8510ec29ba | |||
| cde272055f | |||
| 5e3ad282a8 | |||
| 091b3a8694 | |||
| 42ce97aca5 | |||
| a4df5c300e | |||
| 4bf51e735a | |||
| d7e7c8164c | |||
| eb5e3fcd13 | |||
| 8f8968cbb1 | |||
| ac83e93426 | |||
| 78d0cf4a4f | |||
| 2444c6090b | |||
| 7e49bc5a50 | |||
| 457719d0cc | |||
| c3e63d5a0e | |||
| 8c1b3f8ce3 | |||
| 55b37b794c | |||
| 1aca396e34 | |||
| cb000a5101 | |||
| 1ae2a96747 | |||
| 6834086c37 | |||
| b33b493c6e | |||
| 437fe1e442 | |||
| 2a348dcff9 | |||
| 2821986fb2 | |||
| 1a13f9707b | |||
| ff1b47942c | |||
| 13e4973b48 | |||
| fd46054fb7 | |||
| f618f39bee | |||
| 603486ad2f | |||
| a4b11651a4 | |||
| b6e9a04433 | |||
| 05a2bbbf35 | |||
| 16b6572826 | |||
| 806f06fba6 | |||
| 7a90caedec | |||
| 017b4d07da | |||
| 05015bc476 | |||
| 1f0f3b92b3 | |||
| fe3ce7f578 | |||
| 51c995168a | |||
| f83e4ff4bf | |||
| b67e793283 | |||
| d87ec54a5f | |||
| a6b1040caf | |||
| 9650c4b79a | |||
| 3f8eb5adf0 | |||
| 99a78f9a55 | |||
| c3ace64205 | |||
| 3b2c1ec6e3 | |||
| dba432639e | |||
| 166c4491c2 | |||
| d1cd5a62ed | |||
| b11a406519 | |||
| c7042e1877 | |||
| 896e0b82c2 | |||
| 7856757087 | |||
| b0ce5d7582 | |||
| e23416aa92 | |||
| 9a9cfca706 | |||
| 59feb88ef7 | |||
| 247cda0e26 | |||
| 9f3233224e | |||
| ef14238b4e | |||
| 13b7345189 | |||
| 48ca476a2f | |||
| 57ef8e7c25 | |||
| d09ce46700 | |||
| 3afc1c2663 | |||
| 5821762870 | |||
| 86b7a19f19 | |||
| cc85cef17c | |||
| 5f08f523c2 | |||
| b612959682 | |||
| aea83d5ece | |||
| 07aa5786e5 | |||
| 5bd70aff90 | |||
| 22b8d52311 | |||
| ee6c7e6286 | |||
| 66ea4c4967 | |||
| a6769818fd | |||
| 95535215aa | |||
| eadb46c5ac | |||
| b2b7943dbe | |||
| a0a261db50 | |||
| 8e73dea7e2 |
@@ -6,6 +6,9 @@ updates:
|
||||
schedule:
|
||||
# Check for updates to GitHub Actions every week
|
||||
interval: "weekly"
|
||||
commit-message:
|
||||
# Prefix all commit messages with "Deps/gha: "
|
||||
prefix: "Deps/gha"
|
||||
groups:
|
||||
ci-deps:
|
||||
patterns:
|
||||
|
||||
@@ -11,7 +11,7 @@ jobs:
|
||||
name: "Update Base Translation"
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: Update Base Translation
|
||||
run: ./.github/workflows/scripts/common/update_base_translation.sh
|
||||
|
||||
@@ -9,7 +9,7 @@ jobs:
|
||||
if: github.repository == 'PCSX2/pcsx2'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: Get Latest DB and Prepare DB File
|
||||
run: |
|
||||
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Install Packages
|
||||
run: |
|
||||
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
set-safe-directory: ${{ env.GITHUB_WORKSPACE }}
|
||||
# 10 here, since the odds of having 10 untagged commits in a row should be slim to none
|
||||
@@ -163,7 +163,7 @@ jobs:
|
||||
if: always() && needs.build_linux.result == 'failure' && inputs.publish == true
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: Push Discord Webhook
|
||||
env:
|
||||
|
||||
@@ -59,7 +59,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -92,7 +92,7 @@ jobs:
|
||||
run: echo "timestamp=$(date -u "+%Y-%m-%d-%H;%M;%S")" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: ccache cache files
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: .ccache
|
||||
key: ${{ inputs.os }} ${{ inputs.platform }} ${{ inputs.compiler }} ccache ${{ steps.ccache_cache_timestamp.outputs.timestamp }}
|
||||
@@ -114,7 +114,7 @@ jobs:
|
||||
|
||||
- name: Cache Dependencies
|
||||
id: cache-deps
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: ~/deps
|
||||
key: ${{ inputs.os }} ${{ inputs.platform }} deps ${{ hashFiles('.github/workflows/scripts/linux/build-dependencies-qt.sh', '.github/workflows/scripts/common/*.patch') }}
|
||||
|
||||
@@ -52,7 +52,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
|
||||
# actions/checkout elides tags, fetch them primarily for releases
|
||||
- name: Fetch Tags
|
||||
@@ -99,7 +99,7 @@ jobs:
|
||||
|
||||
- name: Cache Dependencies
|
||||
id: cache-deps
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: ~/deps
|
||||
key: ${{ inputs.os }} ${{ inputs.arch }} deps ${{ hashFiles('.github/workflows/scripts/macos/*', '.github/workflows/scripts/common/*.patch') }}
|
||||
@@ -120,7 +120,7 @@ jobs:
|
||||
run: echo "timestamp=$(date -u "+%Y-%m-%d-%H;%M;%S")" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Cache ccache cache
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: .ccache
|
||||
key: ${{ inputs.os }} ${{ inputs.arch }} ccache ${{ steps.ccache_cache_timestamp.outputs.timestamp }}
|
||||
@@ -176,7 +176,7 @@ jobs:
|
||||
|
||||
- name: Sign the Application
|
||||
if: ${{ inputs.sign_and_notarize == true && env.SIGN_KEY }}
|
||||
uses: indygreg/apple-code-sign-action@44d0985b7f4363198e80b6fea63ac3e9dd3e9957
|
||||
uses: indygreg/apple-code-sign-action@5e7a4a01fe4dffe948ef3bcf1f4538d963cb0c72
|
||||
with:
|
||||
input_path: 'PCSX2.app'
|
||||
p12_file: cert.p12
|
||||
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
outputs:
|
||||
new_tag: ${{ steps.tag_version.outputs.new_tag }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
# Docs - https://github.com/mathieudutour/github-tag-action
|
||||
- name: Bump Version and Push Tag
|
||||
@@ -176,7 +176,7 @@ jobs:
|
||||
name: "Upload Artifacts"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: Prepare Artifact Folder
|
||||
run: mkdir ./ci-artifacts/
|
||||
|
||||
@@ -23,11 +23,11 @@ QTAPNG=1.3.0
|
||||
|
||||
FFMPEG=8.1
|
||||
LIBBACKTRACE=ad106d5fdd5d960bd33fae1c48a351af567fd075
|
||||
LIBJPEGTURBO=3.1.4.1
|
||||
LIBJPEGTURBO=3.2.0
|
||||
LIBPNG=1.6.58
|
||||
LIBWEBP=1.6.0
|
||||
NVENC=13.0.19.0
|
||||
SDL=SDL3-3.4.10
|
||||
SDL=SDL3-3.4.12
|
||||
LZ4=1.10.0
|
||||
VULKAN=1.4.328.1
|
||||
ZSTD=1.5.7
|
||||
@@ -57,10 +57,10 @@ f1d3be3489f758efe1a8f12118a212febbe611aa670af32e0159fa3c1feab2a6 QtApng-$QTAPNG
|
||||
|
||||
b072aed6871998cce9b36e7774033105ca29e33632be5b6347f3206898e0756a ffmpeg-$FFMPEG.tar.xz
|
||||
96e5c2d7f2c482a60d5804da48a2eb9a0db0719b2c65dcc169fbfdcf37f3a45d libbacktrace-$LIBBACKTRACE.tar.gz
|
||||
ecae8008e2cc9ade2f2c1bb9d5e6d4fb73e7c433866a056bd82980741571a022 libjpeg-turbo-$LIBJPEGTURBO.tar.gz
|
||||
6f30092cef9fb839779646608f4ee14ae3cbac989c47fa05e841b0841f09878e libjpeg-turbo-$LIBJPEGTURBO.tar.gz
|
||||
28eb403f51f0f7405249132cecfe82ea5c0ef97f1b32c5a65828814ae0d34775 libpng-$LIBPNG.tar.xz
|
||||
e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 libwebp-$LIBWEBP.tar.gz
|
||||
12b34280415ec8418c864408b93d008a20a6530687ee613d60bfbd20411f2785 $SDL.tar.gz
|
||||
f07b958a9ac5020fb7a44cadb957f658b2149c3c8abb4f63145fac9303249db7 $SDL.tar.gz
|
||||
eee7dea22ed502868017971c86c63c4ed1e6085de0baebfdcc3d3322f00f3eb0 libpng-$LIBPNG-apng.patch.gz
|
||||
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
|
||||
13da39edb3a40ed9713ae390ca89faa2f1202c9dda869ef306a8d4383e242bee nv-codec-headers-$NVENC.tar.gz
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://libsdl.org/release/SDL3-3.4.10.tar.gz",
|
||||
"sha256": "12b34280415ec8418c864408b93d008a20a6530687ee613d60bfbd20411f2785"
|
||||
"url": "https://libsdl.org/release/SDL3-3.4.12.tar.gz",
|
||||
"sha256": "f07b958a9ac5020fb7a44cadb957f658b2149c3c8abb4f63145fac9303249db7"
|
||||
}
|
||||
],
|
||||
"cleanup": [
|
||||
|
||||
@@ -3,7 +3,7 @@ Version=1.0
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Name=PCSX2
|
||||
StartupWMClass=PCSX2
|
||||
StartupWMClass=pcsx2-qt
|
||||
GenericName=PlayStation 2 Emulator
|
||||
Comment=Sony PlayStation 2 emulator
|
||||
Exec=pcsx2-qt
|
||||
|
||||
@@ -47,12 +47,12 @@ QT=6.11.1
|
||||
QTAPNG=1.3.0
|
||||
|
||||
FREETYPE=2.14.3
|
||||
SDL=SDL3-3.4.10
|
||||
SDL=SDL3-3.4.12
|
||||
HARFBUZZ=14.2.0
|
||||
ZSTD=1.5.7
|
||||
LZ4=1.10.0
|
||||
LIBPNG=1.6.58
|
||||
LIBJPEGTURBO=3.1.4.1
|
||||
LIBJPEGTURBO=3.2.0
|
||||
LIBWEBP=1.6.0
|
||||
FFMPEG=8.1
|
||||
MOLTENVK=1.4.1
|
||||
@@ -93,14 +93,14 @@ b2bf6c6845ac175ed7f819145483ba4676f617aaa6a5012c8efee63c8bbac413 qtimageformats
|
||||
f1d3be3489f758efe1a8f12118a212febbe611aa670af32e0159fa3c1feab2a6 QtApng-$QTAPNG.tar.gz
|
||||
|
||||
36bc4f1cc413335368ee656c42afca65c5a3987e8768cc28cf11ba775e785a5f freetype-$FREETYPE.tar.xz
|
||||
12b34280415ec8418c864408b93d008a20a6530687ee613d60bfbd20411f2785 $SDL.tar.gz
|
||||
f07b958a9ac5020fb7a44cadb957f658b2149c3c8abb4f63145fac9303249db7 $SDL.tar.gz
|
||||
c652d5d94971031654ab3989891a490a895d3e3f2b71171c62692b28e94b1b93 harfbuzz-$HARFBUZZ.tar.gz
|
||||
eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 zstd-$ZSTD.tar.gz
|
||||
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
|
||||
28eb403f51f0f7405249132cecfe82ea5c0ef97f1b32c5a65828814ae0d34775 libpng-$LIBPNG.tar.xz
|
||||
e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 libwebp-$LIBWEBP.tar.gz
|
||||
eee7dea22ed502868017971c86c63c4ed1e6085de0baebfdcc3d3322f00f3eb0 libpng-$LIBPNG-apng.patch.gz
|
||||
ecae8008e2cc9ade2f2c1bb9d5e6d4fb73e7c433866a056bd82980741571a022 libjpeg-turbo-$LIBJPEGTURBO.tar.gz
|
||||
6f30092cef9fb839779646608f4ee14ae3cbac989c47fa05e841b0841f09878e libjpeg-turbo-$LIBJPEGTURBO.tar.gz
|
||||
b072aed6871998cce9b36e7774033105ca29e33632be5b6347f3206898e0756a ffmpeg-$FFMPEG.tar.xz
|
||||
9985f141902a17de818e264d17c1ce334b748e499ee02fcb4703e4dc0038f89c MoltenVK-$MOLTENVK.tar.gz
|
||||
51dbf24fe72e43dd7cb9a289d3cab47112010f1a2ed69b6fc8ac0dff31991ed2 KDDockWidgets-$KDDOCKWIDGETS.tar.gz
|
||||
|
||||
@@ -24,12 +24,12 @@ QT=6.11.1
|
||||
QTAPNG=1.3.0
|
||||
|
||||
FREETYPE=2.14.3
|
||||
SDL=SDL3-3.4.10
|
||||
SDL=SDL3-3.4.12
|
||||
HARFBUZZ=14.2.0
|
||||
ZSTD=1.5.7
|
||||
LZ4=1.10.0
|
||||
LIBPNG=1.6.58
|
||||
LIBJPEGTURBO=3.1.4.1
|
||||
LIBJPEGTURBO=3.2.0
|
||||
LIBWEBP=1.6.0
|
||||
FFMPEG=8.1
|
||||
MOLTENVK=1.4.1
|
||||
@@ -69,14 +69,14 @@ b2bf6c6845ac175ed7f819145483ba4676f617aaa6a5012c8efee63c8bbac413 qtimageformats
|
||||
f1d3be3489f758efe1a8f12118a212febbe611aa670af32e0159fa3c1feab2a6 QtApng-$QTAPNG.tar.gz
|
||||
|
||||
36bc4f1cc413335368ee656c42afca65c5a3987e8768cc28cf11ba775e785a5f freetype-$FREETYPE.tar.xz
|
||||
12b34280415ec8418c864408b93d008a20a6530687ee613d60bfbd20411f2785 $SDL.tar.gz
|
||||
f07b958a9ac5020fb7a44cadb957f658b2149c3c8abb4f63145fac9303249db7 $SDL.tar.gz
|
||||
c652d5d94971031654ab3989891a490a895d3e3f2b71171c62692b28e94b1b93 harfbuzz-$HARFBUZZ.tar.gz
|
||||
eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 zstd-$ZSTD.tar.gz
|
||||
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
|
||||
28eb403f51f0f7405249132cecfe82ea5c0ef97f1b32c5a65828814ae0d34775 libpng-$LIBPNG.tar.xz
|
||||
e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 libwebp-$LIBWEBP.tar.gz
|
||||
eee7dea22ed502868017971c86c63c4ed1e6085de0baebfdcc3d3322f00f3eb0 libpng-$LIBPNG-apng.patch.gz
|
||||
ecae8008e2cc9ade2f2c1bb9d5e6d4fb73e7c433866a056bd82980741571a022 libjpeg-turbo-$LIBJPEGTURBO.tar.gz
|
||||
6f30092cef9fb839779646608f4ee14ae3cbac989c47fa05e841b0841f09878e libjpeg-turbo-$LIBJPEGTURBO.tar.gz
|
||||
b072aed6871998cce9b36e7774033105ca29e33632be5b6347f3206898e0756a ffmpeg-$FFMPEG.tar.xz
|
||||
9985f141902a17de818e264d17c1ce334b748e499ee02fcb4703e4dc0038f89c v$MOLTENVK.tar.gz
|
||||
51dbf24fe72e43dd7cb9a289d3cab47112010f1a2ed69b6fc8ac0dff31991ed2 KDDockWidgets-$KDDOCKWIDGETS.tar.gz
|
||||
|
||||
Regular → Executable
+4
-4
@@ -60,8 +60,8 @@ set QTAPNG=1.3.0
|
||||
|
||||
set FREETYPE=2.14.3
|
||||
set HARFBUZZ=14.2.0
|
||||
set SDL=SDL3-3.4.10
|
||||
set LIBJPEGTURBO=3.1.4.1
|
||||
set SDL=SDL3-3.4.12
|
||||
set LIBJPEGTURBO=3.2.0
|
||||
set LIBPNG=1658
|
||||
set LIBPNGLONG=1.6.58
|
||||
set LZ4=1.10.0
|
||||
@@ -92,9 +92,9 @@ call :downloadfile "freetype-%FREETYPE%.tar.gz" https://sourceforge.net/projects
|
||||
call :downloadfile "harfbuzz-%HARFBUZZ%.zip" https://github.com/harfbuzz/harfbuzz/archive/refs/tags/%HARFBUZZ%.zip bb2f83255706b1c92d731541c7cefaf98bb5b93e8f76d16f6deda05225ff20ee || goto error
|
||||
call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lpng1658.zip b32f170855dbbe3e6d9e645af40b538137041773672c3ba3e02db5816c82d376 || goto error
|
||||
call :downloadfile "lpng%LIBPNG%-apng.patch.gz" https://download.sourceforge.net/libpng-apng/libpng-%LIBPNGLONG%-apng.patch.gz eee7dea22ed502868017971c86c63c4ed1e6085de0baebfdcc3d3322f00f3eb0 || goto error
|
||||
call :downloadfile "libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/%LIBJPEGTURBO%/libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" ecae8008e2cc9ade2f2c1bb9d5e6d4fb73e7c433866a056bd82980741571a022 || goto error
|
||||
call :downloadfile "libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/%LIBJPEGTURBO%/libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" 6f30092cef9fb839779646608f4ee14ae3cbac989c47fa05e841b0841f09878e || goto error
|
||||
call :downloadfile "libwebp-%WEBP%.tar.gz" "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-%WEBP%.tar.gz" e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 || goto error
|
||||
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" be8dafef3ecc956e736cfd1c242c10737f3ad589a866f25ba437b7f2f6d5ced2 || goto error
|
||||
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 3d4de8967a49c0451e775a0c1e9022092c19fdef41ba38a83fcf031c5a6496e2 || goto error
|
||||
call :downloadfile "lz4-%LZ4%.zip" "https://github.com/lz4/lz4/archive/refs/tags/v%LZ4%.zip" 3224b4c80f351f194984526ef396f6079bd6332dd9825c72ac0d7a37b3cdc565 || goto error
|
||||
call :downloadfile "zlib%ZLIBSHORT%.zip" "https://github.com/madler/zlib/releases/download/v%ZLIB%/zlib%ZLIBSHORT%.zip" e8bf55f3017aa181690990cb58a994e77885da140609fc8f94abe9b65d2cae28 || goto error
|
||||
call :downloadfile "zstd-%ZSTD%.zip" "https://github.com/facebook/zstd/archive/refs/tags/v%ZSTD%.zip" 7897bc5d620580d9b7cd3539c44b59d78f3657d33663fe97a145e07b4ebd69a4 || goto error
|
||||
|
||||
Regular → Executable
+4
-5
@@ -77,8 +77,8 @@ set LIBX264=b35605ace3ddf7c1a5d67a2eb553f034aef41d55
|
||||
|
||||
set FREETYPE=2.14.3
|
||||
set HARFBUZZ=14.2.0
|
||||
set SDL=SDL3-3.4.10
|
||||
set LIBJPEGTURBO=3.1.4.1
|
||||
set SDL=SDL3-3.4.12
|
||||
set LIBJPEGTURBO=3.2.0
|
||||
set LIBPNG=1658
|
||||
set LIBPNGLONG=1.6.58
|
||||
set LZ4=1.10.0
|
||||
@@ -120,9 +120,9 @@ call :downloadfile "freetype-%FREETYPE%.tar.gz" https://sourceforge.net/projects
|
||||
call :downloadfile "harfbuzz-%HARFBUZZ%.zip" https://github.com/harfbuzz/harfbuzz/archive/refs/tags/%HARFBUZZ%.zip bb2f83255706b1c92d731541c7cefaf98bb5b93e8f76d16f6deda05225ff20ee || goto error
|
||||
call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lpng1658.zip b32f170855dbbe3e6d9e645af40b538137041773672c3ba3e02db5816c82d376 || goto error
|
||||
call :downloadfile "lpng%LIBPNG%-apng.patch.gz" https://download.sourceforge.net/libpng-apng/libpng-%LIBPNGLONG%-apng.patch.gz eee7dea22ed502868017971c86c63c4ed1e6085de0baebfdcc3d3322f00f3eb0 || goto error
|
||||
call :downloadfile "libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/%LIBJPEGTURBO%/libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" ecae8008e2cc9ade2f2c1bb9d5e6d4fb73e7c433866a056bd82980741571a022 || goto error
|
||||
call :downloadfile "libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/%LIBJPEGTURBO%/libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" 6f30092cef9fb839779646608f4ee14ae3cbac989c47fa05e841b0841f09878e || goto error
|
||||
call :downloadfile "libwebp-%WEBP%.tar.gz" "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-%WEBP%.tar.gz" e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 || goto error
|
||||
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" be8dafef3ecc956e736cfd1c242c10737f3ad589a866f25ba437b7f2f6d5ced2 || goto error
|
||||
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 3d4de8967a49c0451e775a0c1e9022092c19fdef41ba38a83fcf031c5a6496e2 || goto error
|
||||
call :downloadfile "lz4-%LZ4%.zip" "https://github.com/lz4/lz4/archive/refs/tags/v%LZ4%.zip" 3224b4c80f351f194984526ef396f6079bd6332dd9825c72ac0d7a37b3cdc565 || goto error
|
||||
call :downloadfile "zlib%ZLIBSHORT%.zip" "https://github.com/madler/zlib/releases/download/v%ZLIB%/zlib%ZLIBSHORT%.zip" e8bf55f3017aa181690990cb58a994e77885da140609fc8f94abe9b65d2cae28 || goto error
|
||||
call :downloadfile "zstd-%ZSTD%.zip" "https://github.com/facebook/zstd/archive/refs/tags/v%ZSTD%.zip" 7897bc5d620580d9b7cd3539c44b59d78f3657d33663fe97a145e07b4ebd69a4 || goto error
|
||||
@@ -471,7 +471,6 @@ echo "Building PlutoVG..."
|
||||
rmdir /S /Q "plutovg-%PLUTOVG%"
|
||||
%SEVENZIP% x "plutovg-%PLUTOVG%.zip" || goto error
|
||||
cd "plutovg-%PLUTOVG%" || goto error
|
||||
%PATCH% -p1 < "%SCRIPTDIR%\plutovg-workaround-msvc-ice.patch" || goto error
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="%INSTALLDIR%" -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DBUILD_SHARED_LIBS=ON -DPLUTOVG_BUILD_EXAMPLES=OFF -B build -G Ninja || goto error
|
||||
cmake --build build --parallel || goto error
|
||||
ninja -C build install || goto error
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
diff --git a/source/plutovg-font.c b/source/plutovg-font.c
|
||||
index 0ad89b1..110de7f 100644
|
||||
--- a/source/plutovg-font.c
|
||||
+++ b/source/plutovg-font.c
|
||||
@@ -836,7 +836,8 @@ int plutovg_font_face_cache_load_file(plutovg_font_face_cache_t* cache, const ch
|
||||
size_t family_index = 0;
|
||||
if(unicode_family_name) {
|
||||
const stbtt_uint8* family_name = unicode_family_name;
|
||||
- while(family_length) {
|
||||
+ //while(family_length) {
|
||||
+ for (int i = family_length; i > 0; i -= 2) {
|
||||
stbtt_uint16 ch = family_name[0] * 256 + family_name[1];
|
||||
if(ch < 0x80) {
|
||||
entry->family[family_index++] = ch;
|
||||
@@ -853,7 +854,8 @@ int plutovg_font_face_cache_load_file(plutovg_font_face_cache_t* cache, const ch
|
||||
entry->family[family_index++] = (0x80 + ((c) & 0x3f));
|
||||
|
||||
family_name += 2;
|
||||
- family_length -= 2;
|
||||
+ //family_length -= 2;
|
||||
+ i -= 2;
|
||||
} else {
|
||||
entry->family[family_index++] = (0xe0 + (ch >> 12));
|
||||
entry->family[family_index++] = (0x80 + ((ch >> 6) & 0x3f));
|
||||
@@ -861,7 +863,7 @@ int plutovg_font_face_cache_load_file(plutovg_font_face_cache_t* cache, const ch
|
||||
}
|
||||
|
||||
family_name += 2;
|
||||
- family_length -= 2;
|
||||
+ //family_length -= 2;
|
||||
}
|
||||
|
||||
entry->family[family_index] = '\0';
|
||||
@@ -902,7 +904,8 @@ int plutovg_font_face_cache_load_file(plutovg_font_face_cache_t* cache, const ch
|
||||
};
|
||||
|
||||
const stbtt_uint8* family_name = roman_family_name;
|
||||
- while(family_length) {
|
||||
+ //while(family_length) {
|
||||
+ for (int i2 = family_length; i2 > 0; i2--) {
|
||||
stbtt_uint16 ch = MAC_ROMAN_TABLE[family_name[0]];
|
||||
if(ch < 0x80) {
|
||||
entry->family[family_index++] = ch;
|
||||
@@ -916,7 +919,7 @@ int plutovg_font_face_cache_load_file(plutovg_font_face_cache_t* cache, const ch
|
||||
}
|
||||
|
||||
family_name += 1;
|
||||
- family_length -= 1;
|
||||
+ //family_length -= 1;
|
||||
}
|
||||
|
||||
entry->family[family_index] = '\0';
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
runs-on: windows-2025-vs2026
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
- name: Verify VS Project Files
|
||||
run: .github\workflows\scripts\windows\validate-vs-filters.ps1
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Configure MSBuild Clang Version
|
||||
if: inputs.configuration != 'CMake'
|
||||
@@ -122,7 +122,7 @@ jobs:
|
||||
|
||||
- name: Cache Dependencies
|
||||
id: cache-deps
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: deps
|
||||
key: ${{ inputs.os }} ${{ inputs.platform }} deps ${{ hashFiles('.github/workflows/scripts/windows/build-dependencies.bat', '.github/workflows/scripts/common/*.patch') }}
|
||||
|
||||
@@ -63,6 +63,9 @@ oprofile_data/
|
||||
*.kdev4
|
||||
/.kdev4*
|
||||
|
||||
# Kate Projects
|
||||
.kateproject*
|
||||
|
||||
# Resources and docs in /bin are tracked
|
||||
/bin/**/*.dll
|
||||
/bin/**/*.dmp
|
||||
|
||||
Vendored
+2
@@ -361,6 +361,8 @@ enum cpuinfo_uarch {
|
||||
cpuinfo_uarch_raptor_cove = 0x0010020F,
|
||||
/** Intel Redwood Cove microarchitecture (Granite Rapids). */
|
||||
cpuinfo_uarch_redwood_cove = 0x00100210,
|
||||
/** Intel Coyote Cove microarchitecture. */
|
||||
cpuinfo_uarch_coyote_cove = 0x00100211,
|
||||
|
||||
/** Pentium 4 with Willamette, Northwood, or Foster cores. */
|
||||
cpuinfo_uarch_willamette = 0x00100300,
|
||||
|
||||
Vendored
+3
@@ -1,3 +1,6 @@
|
||||
/* for syscall() */
|
||||
#define _DEFAULT_SOURCE
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
Vendored
+2
@@ -36,6 +36,7 @@ enum cpuinfo_arm_chipset_vendor {
|
||||
cpuinfo_arm_chipset_vendor_texas_instruments,
|
||||
cpuinfo_arm_chipset_vendor_unisoc,
|
||||
cpuinfo_arm_chipset_vendor_wondermedia,
|
||||
cpuinfo_arm_chipset_vendor_google,
|
||||
cpuinfo_arm_chipset_vendor_max,
|
||||
};
|
||||
|
||||
@@ -73,6 +74,7 @@ enum cpuinfo_arm_chipset_series {
|
||||
cpuinfo_arm_chipset_series_unisoc_t,
|
||||
cpuinfo_arm_chipset_series_unisoc_ums,
|
||||
cpuinfo_arm_chipset_series_wondermedia_wm,
|
||||
cpuinfo_arm_chipset_series_google_tensor,
|
||||
cpuinfo_arm_chipset_series_max,
|
||||
};
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -366,7 +366,7 @@ CPUINFO_INTERNAL struct cpuinfo_arm_chipset cpuinfo_arm_android_decode_chipset_f
|
||||
CPUINFO_INTERNAL struct cpuinfo_arm_chipset cpuinfo_arm_android_decode_chipset_from_ro_hardware_chipname(
|
||||
const char ro_hardware_chipname[restrict static CPUINFO_BUILD_PROP_VALUE_MAX]);
|
||||
CPUINFO_INTERNAL struct cpuinfo_arm_chipset cpuinfo_arm_android_decode_chipset_from_ro_soc_model(
|
||||
const char ro_soc_model[restrict static CPUINFO_BUILD_PROP_VALUE_MAX]);
|
||||
const char soc_model[restrict static CPUINFO_BUILD_PROP_VALUE_MAX]);
|
||||
#else
|
||||
CPUINFO_INTERNAL struct cpuinfo_arm_chipset cpuinfo_arm_linux_decode_chipset_from_proc_cpuinfo_revision(
|
||||
const char proc_cpuinfo_revision[restrict static CPUINFO_REVISION_VALUE_MAX]);
|
||||
|
||||
+61
-10
@@ -3492,9 +3492,20 @@ struct cpuinfo_arm_chipset cpuinfo_arm_android_decode_chipset_from_ro_chipname(
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* Decodes chipset name from ro.soc.model Android system property.
|
||||
*
|
||||
* @param[in] soc_model - ro.soc.model value.
|
||||
*
|
||||
* @returns Decoded chipset name. If chipset could not be decoded, the resulting
|
||||
* structure would use `unknown` vendor and series identifiers.
|
||||
*/
|
||||
struct cpuinfo_arm_chipset cpuinfo_arm_android_decode_chipset_from_ro_soc_model(
|
||||
const char soc_model[restrict static CPUINFO_BUILD_PROP_VALUE_MAX]) {
|
||||
struct cpuinfo_arm_chipset chipset;
|
||||
struct cpuinfo_arm_chipset chipset = {
|
||||
.vendor = cpuinfo_arm_chipset_vendor_unknown,
|
||||
.series = cpuinfo_arm_chipset_series_unknown,
|
||||
};
|
||||
const size_t soc_model_length = strnlen(soc_model, CPUINFO_BUILD_PROP_VALUE_MAX);
|
||||
const char* soc_model_end = soc_model + soc_model_length;
|
||||
|
||||
@@ -3516,10 +3527,28 @@ struct cpuinfo_arm_chipset cpuinfo_arm_android_decode_chipset_from_ro_soc_model(
|
||||
return chipset;
|
||||
}
|
||||
|
||||
return (struct cpuinfo_arm_chipset){
|
||||
.vendor = cpuinfo_arm_chipset_vendor_unknown,
|
||||
.series = cpuinfo_arm_chipset_series_unknown,
|
||||
};
|
||||
if (soc_model[0] != '\0') {
|
||||
if (strncmp(soc_model, "Tensor", 6) == 0) {
|
||||
chipset.vendor = cpuinfo_arm_chipset_vendor_google;
|
||||
chipset.series = cpuinfo_arm_chipset_series_google_tensor;
|
||||
const char* suffix_start = soc_model + 6;
|
||||
while (*suffix_start == ' ') {
|
||||
suffix_start++;
|
||||
}
|
||||
const size_t suffix_length = strnlen(suffix_start, CPUINFO_ARM_CHIPSET_SUFFIX_MAX);
|
||||
if (suffix_length > 0) {
|
||||
strncpy(chipset.suffix, suffix_start, suffix_length);
|
||||
}
|
||||
return chipset;
|
||||
} else if (strncmp(soc_model, "GS201", 5) == 0) {
|
||||
chipset.vendor = cpuinfo_arm_chipset_vendor_google;
|
||||
chipset.series = cpuinfo_arm_chipset_series_google_tensor;
|
||||
strncpy(chipset.suffix, "G2", 2);
|
||||
return chipset;
|
||||
}
|
||||
}
|
||||
|
||||
return chipset;
|
||||
}
|
||||
#endif /* __ANDROID__ */
|
||||
|
||||
@@ -3858,6 +3887,7 @@ static const char* chipset_vendor_string[cpuinfo_arm_chipset_vendor_max] = {
|
||||
[cpuinfo_arm_chipset_vendor_texas_instruments] = "Texas Instruments",
|
||||
[cpuinfo_arm_chipset_vendor_unisoc] = "Unisoc",
|
||||
[cpuinfo_arm_chipset_vendor_wondermedia] = "WonderMedia",
|
||||
[cpuinfo_arm_chipset_vendor_google] = "Google",
|
||||
};
|
||||
|
||||
/* Map from ARM chipset series ID to its string representation */
|
||||
@@ -3895,6 +3925,7 @@ static const char* chipset_series_string[cpuinfo_arm_chipset_series_max] = {
|
||||
[cpuinfo_arm_chipset_series_unisoc_t] = "T",
|
||||
[cpuinfo_arm_chipset_series_unisoc_ums] = "UMS",
|
||||
[cpuinfo_arm_chipset_series_wondermedia_wm] = "WM",
|
||||
[cpuinfo_arm_chipset_series_google_tensor] = "Tensor",
|
||||
};
|
||||
|
||||
/* Convert chipset name represented by cpuinfo_arm_chipset structure to a string
|
||||
@@ -3913,14 +3944,35 @@ void cpuinfo_arm_chipset_to_string(
|
||||
const char* vendor_string = chipset_vendor_string[vendor];
|
||||
const char* series_string = chipset_series_string[series];
|
||||
const uint32_t model = chipset->model;
|
||||
const size_t suffix_length = strnlen(chipset->suffix, CPUINFO_ARM_CHIPSET_SUFFIX_MAX);
|
||||
if (model == 0) {
|
||||
if (series == cpuinfo_arm_chipset_series_unknown) {
|
||||
strncpy(name, vendor_string, CPUINFO_ARM_CHIPSET_NAME_MAX);
|
||||
if (suffix_length > 0) {
|
||||
if (series == cpuinfo_arm_chipset_series_unknown) {
|
||||
snprintf(
|
||||
name,
|
||||
CPUINFO_ARM_CHIPSET_NAME_MAX,
|
||||
"%s %.*s",
|
||||
vendor_string,
|
||||
(int)suffix_length,
|
||||
chipset->suffix);
|
||||
} else {
|
||||
snprintf(
|
||||
name,
|
||||
CPUINFO_ARM_CHIPSET_NAME_MAX,
|
||||
"%s %s %.*s",
|
||||
vendor_string,
|
||||
series_string,
|
||||
(int)suffix_length,
|
||||
chipset->suffix);
|
||||
}
|
||||
} else {
|
||||
snprintf(name, CPUINFO_ARM_CHIPSET_NAME_MAX, "%s %s", vendor_string, series_string);
|
||||
if (series == cpuinfo_arm_chipset_series_unknown) {
|
||||
strncpy(name, vendor_string, CPUINFO_ARM_CHIPSET_NAME_MAX);
|
||||
} else {
|
||||
snprintf(name, CPUINFO_ARM_CHIPSET_NAME_MAX, "%s %s", vendor_string, series_string);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const size_t suffix_length = strnlen(chipset->suffix, CPUINFO_ARM_CHIPSET_SUFFIX_MAX);
|
||||
snprintf(
|
||||
name,
|
||||
CPUINFO_ARM_CHIPSET_NAME_MAX,
|
||||
@@ -4051,7 +4103,6 @@ static enum cpuinfo_arm_chipset_vendor disambiguate_chipset_vendor(
|
||||
(vendor_a == cpuinfo_arm_chipset_vendor_spreadtrum && vendor_b == cpuinfo_arm_chipset_vendor_unisoc)) {
|
||||
return cpuinfo_arm_chipset_vendor_unisoc;
|
||||
}
|
||||
|
||||
return cpuinfo_arm_chipset_vendor_unknown;
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -256,12 +256,12 @@ void cpuinfo_arm_linux_init(void) {
|
||||
}
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
struct cpuinfo_android_properties android_properties;
|
||||
struct cpuinfo_android_properties android_properties = {0};
|
||||
cpuinfo_arm_android_parse_properties(&android_properties);
|
||||
#else
|
||||
char proc_cpuinfo_hardware[CPUINFO_HARDWARE_VALUE_MAX];
|
||||
char proc_cpuinfo_hardware[CPUINFO_HARDWARE_VALUE_MAX] = {0};
|
||||
#endif
|
||||
char proc_cpuinfo_revision[CPUINFO_REVISION_VALUE_MAX];
|
||||
char proc_cpuinfo_revision[CPUINFO_REVISION_VALUE_MAX] = {0};
|
||||
|
||||
if (!cpuinfo_arm_linux_parse_proc_cpuinfo(
|
||||
#if defined(__ANDROID__)
|
||||
|
||||
Vendored
+49
@@ -11,6 +11,11 @@ void cpuinfo_arm_decode_vendor_uarch(
|
||||
#endif /* CPUINFO_ARCH_ARM */
|
||||
enum cpuinfo_vendor vendor[RESTRICT_STATIC 1],
|
||||
enum cpuinfo_uarch uarch[RESTRICT_STATIC 1]) {
|
||||
/* Ensure the out-parameters are always initialized, including for
|
||||
* implementers that are not handled in the switch below. */
|
||||
*vendor = cpuinfo_vendor_unknown;
|
||||
*uarch = cpuinfo_uarch_unknown;
|
||||
|
||||
switch (midr_get_implementer(midr)) {
|
||||
case 'A':
|
||||
*vendor = cpuinfo_vendor_arm;
|
||||
@@ -438,6 +443,50 @@ void cpuinfo_arm_decode_vendor_uarch(
|
||||
midr_get_part(midr));
|
||||
}
|
||||
break;
|
||||
case 'a':
|
||||
*vendor = cpuinfo_vendor_apple;
|
||||
switch (midr_get_part(midr)) {
|
||||
case 0x022:
|
||||
case 0x024:
|
||||
case 0x028:
|
||||
*uarch = cpuinfo_uarch_icestorm;
|
||||
break;
|
||||
case 0x023:
|
||||
case 0x025:
|
||||
case 0x029:
|
||||
*uarch = cpuinfo_uarch_firestorm;
|
||||
break;
|
||||
case 0x032:
|
||||
case 0x034:
|
||||
case 0x038:
|
||||
*uarch = cpuinfo_uarch_blizzard;
|
||||
break;
|
||||
case 0x033:
|
||||
case 0x035:
|
||||
case 0x039:
|
||||
*uarch = cpuinfo_uarch_avalanche;
|
||||
break;
|
||||
case 0x042:
|
||||
case 0x044:
|
||||
case 0x048:
|
||||
*uarch = cpuinfo_uarch_coll_sawtooth;
|
||||
break;
|
||||
case 0x043:
|
||||
case 0x045:
|
||||
case 0x049:
|
||||
*uarch = cpuinfo_uarch_coll_everest;
|
||||
break;
|
||||
case 0x052:
|
||||
*uarch = cpuinfo_uarch_donan_sawtooth;
|
||||
break;
|
||||
case 0x053:
|
||||
*uarch = cpuinfo_uarch_donan_everest;
|
||||
break;
|
||||
default:
|
||||
cpuinfo_log_warning(
|
||||
"unknown Apple CPU part 0x%03" PRIx32 " ignored", midr_get_part(midr));
|
||||
}
|
||||
break;
|
||||
#if CPUINFO_ARCH_ARM
|
||||
case 'V':
|
||||
*vendor = cpuinfo_vendor_marvell;
|
||||
|
||||
Vendored
+9
@@ -307,6 +307,15 @@ static bool transform_token(char* token_start, char* token_end, struct parser_st
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
/*
|
||||
* Erase everything starting with "with" on AMD
|
||||
* processors, e.g. "AMD Ryzen 5 PRO 6650U with Radeon
|
||||
* Graphics"
|
||||
*/
|
||||
if (erase_matching(token_start, token_length, "with")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Remember to erase "Dual Core" in "AMD Athlon(tm) 64
|
||||
* X2 Dual Core Processor 3800+" */
|
||||
if (memcmp(token_start, "Dual", token_length) == 0) {
|
||||
|
||||
Vendored
+7
@@ -270,8 +270,15 @@ enum cpuinfo_uarch cpuinfo_x86_decode_uarch(
|
||||
return cpuinfo_uarch_prescott;
|
||||
}
|
||||
break;
|
||||
case 0x12:
|
||||
switch (model_info->model) {
|
||||
case 0x01: // Nova Lake P-core (Coyote Cove)
|
||||
return cpuinfo_uarch_coyote_cove;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case cpuinfo_vendor_amd:
|
||||
switch (model_info->family) {
|
||||
#if CPUINFO_ARCH_X86
|
||||
|
||||
-2
@@ -265,8 +265,6 @@ BOOL CALLBACK cpuinfo_x86_windows_init(PINIT_ONCE init_once, PVOID parameter, PV
|
||||
* APIC order */
|
||||
const uint32_t core_id = cores_count++;
|
||||
uint32_t smt_id = 0;
|
||||
/* Reconstruct core part of APIC ID */
|
||||
const uint32_t core_apic_id = (core_id & core_bits_mask) << x86_processor.topology.core_bits_offset;
|
||||
/* Iterate processor groups and set the core & SMT parts of APIC
|
||||
* ID */
|
||||
for (uint32_t i = 0; i < core_info->Processor.GroupCount; i++) {
|
||||
|
||||
Vendored
+4
-3
@@ -1,12 +1,13 @@
|
||||
Copyright 2015-2023 The Khronos Group Inc.
|
||||
Copyright 2015-2026 The Khronos Group Inc.
|
||||
|
||||
Files in this repository fall under one of these licenses:
|
||||
|
||||
- `Apache-2.0`
|
||||
- `MIT`
|
||||
|
||||
Note: With the exception of `parse_dependency.py` the files using `MIT` license
|
||||
also fall under `Apache-2.0`. Example:
|
||||
Note: With the exception of `parse_dependency.py`, which is based on an
|
||||
external file, files using the `MIT` license also fall under `Apache-2.0`.
|
||||
Example:
|
||||
|
||||
```
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
/*
|
||||
** Copyright 2015-2026 The Khronos Group Inc.
|
||||
**
|
||||
** SPDX-License-Identifier: Apache-2.0
|
||||
** SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
/*
|
||||
** Copyright 2015-2026 The Khronos Group Inc.
|
||||
**
|
||||
** SPDX-License-Identifier: Apache-2.0
|
||||
** SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
/*
|
||||
** Copyright 2015-2026 The Khronos Group Inc.
|
||||
**
|
||||
** SPDX-License-Identifier: Apache-2.0
|
||||
** SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
/*
|
||||
** Copyright 2015-2026 The Khronos Group Inc.
|
||||
**
|
||||
** SPDX-License-Identifier: Apache-2.0
|
||||
** SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
/*
|
||||
** Copyright 2015-2026 The Khronos Group Inc.
|
||||
**
|
||||
** SPDX-License-Identifier: Apache-2.0
|
||||
** SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
/*
|
||||
** Copyright 2015-2026 The Khronos Group Inc.
|
||||
**
|
||||
** SPDX-License-Identifier: Apache-2.0
|
||||
** SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
/*
|
||||
** Copyright 2015-2026 The Khronos Group Inc.
|
||||
**
|
||||
** SPDX-License-Identifier: Apache-2.0
|
||||
** SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
/*
|
||||
** Copyright 2015-2026 The Khronos Group Inc.
|
||||
**
|
||||
** SPDX-License-Identifier: Apache-2.0
|
||||
** SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
/*
|
||||
** Copyright 2015-2026 The Khronos Group Inc.
|
||||
**
|
||||
** SPDX-License-Identifier: Apache-2.0
|
||||
** SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
/*
|
||||
** Copyright 2015-2026 The Khronos Group Inc.
|
||||
**
|
||||
** SPDX-License-Identifier: Apache-2.0
|
||||
** SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
/*
|
||||
** Copyright 2015-2026 The Khronos Group Inc.
|
||||
**
|
||||
** SPDX-License-Identifier: Apache-2.0
|
||||
** SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
/*
|
||||
** Copyright 2015-2026 The Khronos Group Inc.
|
||||
**
|
||||
** SPDX-License-Identifier: Apache-2.0
|
||||
** SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
+1
-2
@@ -2,8 +2,7 @@
|
||||
* Copyright 2015-2023 The Khronos Group Inc.
|
||||
* Copyright 2015-2023 Valve Corporation
|
||||
* Copyright 2015-2023 LunarG, Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
||||
+1
-2
@@ -2,8 +2,7 @@
|
||||
* Copyright 2015-2023 The Khronos Group Inc.
|
||||
* Copyright 2015-2023 Valve Corporation
|
||||
* Copyright 2015-2023 LunarG, Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
||||
+1
-2
@@ -3,8 +3,7 @@
|
||||
//
|
||||
/*
|
||||
** Copyright 2014-2026 The Khronos Group Inc.
|
||||
**
|
||||
** SPDX-License-Identifier: Apache-2.0
|
||||
** SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*/
|
||||
|
||||
|
||||
|
||||
+1
-2
@@ -3,8 +3,7 @@
|
||||
|
||||
/*
|
||||
** Copyright 2015-2026 The Khronos Group Inc.
|
||||
**
|
||||
** SPDX-License-Identifier: Apache-2.0
|
||||
** SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*/
|
||||
|
||||
#include "vk_platform.h"
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
/*
|
||||
** Copyright 2015-2026 The Khronos Group Inc.
|
||||
**
|
||||
** SPDX-License-Identifier: Apache-2.0
|
||||
** SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
/*
|
||||
** Copyright 2015-2026 The Khronos Group Inc.
|
||||
**
|
||||
** SPDX-License-Identifier: Apache-2.0
|
||||
** SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
+994
-43
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -4,7 +4,7 @@
|
||||
/*
|
||||
** Copyright 2015-2026 The Khronos Group Inc.
|
||||
**
|
||||
** SPDX-License-Identifier: Apache-2.0
|
||||
** SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
/*
|
||||
** Copyright 2015-2026 The Khronos Group Inc.
|
||||
**
|
||||
** SPDX-License-Identifier: Apache-2.0
|
||||
** SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
/*
|
||||
** Copyright 2015-2026 The Khronos Group Inc.
|
||||
**
|
||||
** SPDX-License-Identifier: Apache-2.0
|
||||
** SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
/*
|
||||
** Copyright 2015-2026 The Khronos Group Inc.
|
||||
**
|
||||
** SPDX-License-Identifier: Apache-2.0
|
||||
** SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
/*
|
||||
** Copyright 2015-2026 The Khronos Group Inc.
|
||||
**
|
||||
** SPDX-License-Identifier: Apache-2.0
|
||||
** SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
/*
|
||||
** Copyright 2015-2026 The Khronos Group Inc.
|
||||
**
|
||||
** SPDX-License-Identifier: Apache-2.0
|
||||
** SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
/*
|
||||
** Copyright 2015-2026 The Khronos Group Inc.
|
||||
**
|
||||
** SPDX-License-Identifier: Apache-2.0
|
||||
** SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
/*
|
||||
** Copyright 2015-2026 The Khronos Group Inc.
|
||||
**
|
||||
** SPDX-License-Identifier: Apache-2.0
|
||||
** SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
/*
|
||||
** Copyright 2015-2026 The Khronos Group Inc.
|
||||
**
|
||||
** SPDX-License-Identifier: Apache-2.0
|
||||
** SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
/*
|
||||
** Copyright 2015-2026 The Khronos Group Inc.
|
||||
**
|
||||
** SPDX-License-Identifier: Apache-2.0
|
||||
** SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
/*
|
||||
** Copyright 2015-2026 The Khronos Group Inc.
|
||||
**
|
||||
** SPDX-License-Identifier: Apache-2.0
|
||||
** SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
/*
|
||||
** Copyright 2015-2026 The Khronos Group Inc.
|
||||
**
|
||||
** SPDX-License-Identifier: Apache-2.0
|
||||
** SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
/*
|
||||
** Copyright 2015-2026 The Khronos Group Inc.
|
||||
**
|
||||
** SPDX-License-Identifier: Apache-2.0
|
||||
** SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
/*
|
||||
** Copyright 2015-2026 The Khronos Group Inc.
|
||||
**
|
||||
** SPDX-License-Identifier: Apache-2.0
|
||||
** SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
+185
-340
File diff suppressed because it is too large
Load Diff
@@ -1253,12 +1253,16 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
03000000c82d00001230000011010000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000c82d00001330000011010000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
05000000c82d00001230000000010000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000c82d00001260000011010000,8BitDo Ultimate 2,a:b0,b:b1,x:b3,y:b4,back:b10,guide:b12,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,paddle1:b17,paddle2:b16,paddle3:b2,paddle4:b5,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4,platform:Linux,
|
||||
03000000c82d00000b31000014010000,8BitDo Ultimate 2,a:b0,b:b1,x:b2,y:b3,back:b6,guide:b8,start:b7,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,
|
||||
05000000c82d00001260000001000000,8BitDo Ultimate 2,a:b0,b:b1,x:b3,y:b4,back:b10,guide:b12,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,paddle1:b17,paddle2:b16,paddle3:b2,paddle4:b5,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4,platform:Linux,
|
||||
03000000c82d00000a31000014010000,8BitDo Ultimate 2C,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
03000000c82d00001d30000011010000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
05000000c82d00001b30000001000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000c82d00001530000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000c82d00001630000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000c82d00001730000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
06000000c82d00000320000007010000,8BitDo Ultimate Wired Controller for Xbox,a:b0,b:b1,x:b2,y:b3,back:b6,guide:b8,start:b7,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,
|
||||
03000000c82d00000121000011010000,8BitDo Xbox One SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
05000000c82d00000121000000010000,8BitDo Xbox One SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
05000000a00500003232000001000000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,
|
||||
|
||||
@@ -192,13 +192,13 @@ struct PS_INPUT
|
||||
|
||||
#ifdef PIXEL_SHADER
|
||||
|
||||
struct PS_OUTPUT_REAL
|
||||
struct PS_OUTPUT
|
||||
{
|
||||
#define NUM_RTS 0
|
||||
|
||||
#if PS_RETURN_COLOR
|
||||
#if PS_DATE == 1 || PS_DATE == 2
|
||||
float c : SV_Target;
|
||||
float c0 : SV_Target;
|
||||
#else
|
||||
|
||||
float4 c0 : SV_Target0;
|
||||
@@ -214,7 +214,7 @@ struct PS_OUTPUT_REAL
|
||||
|
||||
#if PS_RETURN_DEPTH
|
||||
// In DX12 we do depth feedback loops with a color copy.
|
||||
#if SW_DEPTH && PS_NO_COLOR1 && DX12
|
||||
#if SW_DEPTH && PS_NO_COLOR1 && PS_DEPTH_FEEDBACK_SUPPORT == 2
|
||||
#if NUM_RTS > 0
|
||||
float depth_color : SV_Target1;
|
||||
#else
|
||||
@@ -231,20 +231,6 @@ struct PS_OUTPUT_REAL
|
||||
#undef NUM_RTS
|
||||
};
|
||||
|
||||
struct PS_OUTPUT
|
||||
{
|
||||
#if !PS_NO_COLOR
|
||||
#if PS_DATE == 1 || PS_DATE == 2
|
||||
float c;
|
||||
#else
|
||||
float4 c0;
|
||||
#if !PS_NO_COLOR1
|
||||
float4 c1;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
Texture2D<float4> Texture : register(t0);
|
||||
Texture2D<float4> Palette : register(t1);
|
||||
#if !PS_ROV_COLOR
|
||||
@@ -361,23 +347,28 @@ bool2 nan_or_inf(float2 xy)
|
||||
float4 sample_c_af(float2 uv, float uv_w)
|
||||
{
|
||||
// HW sampler will reject bad UVs, match that here.
|
||||
uv = any(nan_or_inf(uv)) ? float2(0, 0) : uv;
|
||||
uv = any(nan_or_inf(uv)) ? float2(0.0f, 0.0f) : uv;
|
||||
|
||||
// Large floating point values risk NaN/Inf values.
|
||||
// Above this value floats lose decimal precision, so seems a resonable limit for UVs.
|
||||
uv = clamp(uv, -8388608.0f, 8388608.0f);
|
||||
|
||||
// Below taken from https://microsoft.github.io/DirectX-Specs/d3d/archive/D3D11_3_FunctionalSpec.htm#7.18.11%20LOD%20Calculations
|
||||
// And https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_filter_anisotropic.txt
|
||||
// With guidance from https://pema.dev/2025/05/09/mipmaps-too-much-detail/
|
||||
float2 sz;
|
||||
Texture.GetDimensions(sz.x, sz.y);
|
||||
float2 dX = ddx(uv) * sz;
|
||||
float2 dY = ddy(uv) * sz;
|
||||
|
||||
float length_x = length(dX);
|
||||
float length_y = length(dY);
|
||||
|
||||
// Calculate Ellipse Transform
|
||||
bool d_zero = length(dX) == 0 || length(dY) == 0;
|
||||
bool d_par = (dX.x * dY.y - dY.x * dX.y) == 0;
|
||||
bool d_per = dot(dX, dY) == 0;
|
||||
bool d_zero = length_x < 0.001f || length_y < 0.001f;
|
||||
float f = (dX.x * dY.y - dX.y * dY.x);
|
||||
bool d_par = f < 0.001f;
|
||||
bool d_per = dot(dX, dY) < 0.001f;
|
||||
bool d_inf_nan = any(nan_or_inf(dX) | nan_or_inf(dY));
|
||||
|
||||
if (!(d_zero || d_par || d_per || d_inf_nan))
|
||||
@@ -385,42 +376,49 @@ float4 sample_c_af(float2 uv, float uv_w)
|
||||
float A = dX.y * dX.y + dY.y * dY.y;
|
||||
float B = -2 * (dX.x * dX.y + dY.x * dY.y);
|
||||
float C = dX.x * dX.x + dY.x * dY.x;
|
||||
float f = (dX.x * dY.y - dY.x * dX.y);
|
||||
float F = f * f;
|
||||
|
||||
float p = A - C;
|
||||
float q = A + C;
|
||||
float t = sqrt(p * p + B * B);
|
||||
|
||||
float sqrt_num_plus = sqrt(F * (t + p));
|
||||
float sqrt_num_minus = sqrt(F * (t - p));
|
||||
|
||||
float inv_sqrt_denom_plus = rsqrt(t * (q + t));
|
||||
float inv_sqrt_denom_minus = rsqrt(t * (q - t));
|
||||
|
||||
float signB = sign(B);
|
||||
|
||||
float2 new_dX = float2(
|
||||
sqrt(F * (t + p) / (t * (q + t))),
|
||||
sqrt(F * (t - p) / (t * (q + t))) * sign(B)
|
||||
sqrt_num_plus * inv_sqrt_denom_plus,
|
||||
sqrt_num_minus * inv_sqrt_denom_plus * signB
|
||||
);
|
||||
|
||||
|
||||
float2 new_dY = float2(
|
||||
sqrt(F * (t - p) / (t * (q - t))) * -sign(B),
|
||||
sqrt(F * (t + p) / (t * (q - t)))
|
||||
sqrt_num_minus * inv_sqrt_denom_minus * -signB,
|
||||
sqrt_num_plus * inv_sqrt_denom_minus
|
||||
);
|
||||
|
||||
|
||||
d_inf_nan = any(nan_or_inf(new_dX) | nan_or_inf(new_dY));
|
||||
if (!d_inf_nan)
|
||||
{
|
||||
dX = new_dX;
|
||||
dY = new_dY;
|
||||
length_x = length(dX);
|
||||
length_y = length(dY);
|
||||
}
|
||||
}
|
||||
|
||||
// Compute AF values
|
||||
float squared_length_x = dX.x * dX.x + dX.y * dX.y;
|
||||
float squared_length_y = dY.x * dY.x + dY.y * dY.y;
|
||||
float determinant = abs(dX.x * dY.y - dX.y * dY.x);
|
||||
bool is_major_x = squared_length_x > squared_length_y;
|
||||
float squared_length_major = is_major_x ? squared_length_x : squared_length_y;
|
||||
float length_major = sqrt(squared_length_major);
|
||||
bool is_major_x = length_x > length_y;
|
||||
float length_major = is_major_x ? length_x : length_y;
|
||||
float length_minor = is_major_x ? length_y : length_x;
|
||||
|
||||
float aniso_ratio;
|
||||
float length_lod;
|
||||
float2 aniso_line;
|
||||
|
||||
if (length_major <= 1.0f)
|
||||
{
|
||||
// A zero length_major would result in NaN Lod and break sampling.
|
||||
@@ -428,57 +426,44 @@ float4 sample_c_af(float2 uv, float uv_w)
|
||||
// Perform isotropic filtering instead.
|
||||
aniso_ratio = 1.0f;
|
||||
length_lod = length_major;
|
||||
aniso_line = float2(0, 0);
|
||||
aniso_line = float2(0.0f, 0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
float norm_major = 1.0f / length_major;
|
||||
|
||||
float2 aniso_line_dir = float2(
|
||||
(is_major_x ? dX.x : dY.x) * norm_major,
|
||||
(is_major_x ? dX.y : dY.y) * norm_major
|
||||
);
|
||||
|
||||
aniso_ratio = squared_length_major / determinant;
|
||||
float2 aniso_line_dir = is_major_x ? dX : dY;
|
||||
|
||||
// Calculate the minor length of the ellipse for Lod, while also clamping the ratio of anisotropy.
|
||||
if (aniso_ratio > PS_ANISOTROPIC_FILTERING)
|
||||
{
|
||||
// ratio is clamped - Lod is based on ratio (preserves area)
|
||||
aniso_ratio = PS_ANISOTROPIC_FILTERING;
|
||||
length_lod = length_major / PS_ANISOTROPIC_FILTERING;
|
||||
}
|
||||
else
|
||||
{
|
||||
// ratio not clamped - Lod is based on area
|
||||
length_lod = determinant / length_major;
|
||||
}
|
||||
aniso_ratio = min(length_major / length_minor, PS_ANISOTROPIC_FILTERING);
|
||||
length_lod = length_major / aniso_ratio;
|
||||
|
||||
// clamp to top Lod
|
||||
if (length_lod < 1.0f)
|
||||
aniso_ratio = max(1.0f, aniso_ratio * length_lod);
|
||||
|
||||
aniso_ratio = round(aniso_ratio);
|
||||
aniso_line = aniso_line_dir * 0.5f * length_major * (1.0f / sz);
|
||||
|
||||
aniso_line = aniso_line_dir * 0.5f * (1.0f / sz);
|
||||
}
|
||||
|
||||
|
||||
#if PS_AUTOMATIC_LOD == 1
|
||||
float lod = log2(length_lod);
|
||||
#elif PS_MANUAL_LOD == 1
|
||||
float lod = manual_lod(uv_w);
|
||||
#else
|
||||
float lod = 0; // No Lod
|
||||
float lod = 0.0f; // No Lod
|
||||
#endif
|
||||
|
||||
|
||||
float4 colour;
|
||||
if (aniso_ratio == 1.0f)
|
||||
colour = Texture.SampleLevel(TextureSampler, uv, lod);
|
||||
else
|
||||
{
|
||||
float4 num = float4(0, 0, 0, 0);
|
||||
for (int i = 0; i < aniso_ratio; i++)
|
||||
{
|
||||
float2 d = -aniso_line + (0.5f + i) * (2.0f * aniso_line) / aniso_ratio;
|
||||
float4 num = float4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
float2 segment = (2.0f * aniso_line) / aniso_ratio;
|
||||
|
||||
int aniso_ratio_i = (int)aniso_ratio;
|
||||
for (int i = 0; i < aniso_ratio_i; i++)
|
||||
{
|
||||
float2 d = -aniso_line + (0.5f + i) * segment;
|
||||
float2 uv_sample = uv + d;
|
||||
float4 sample_colour = Texture.SampleLevel(TextureSampler, uv_sample, lod);
|
||||
num += sample_colour;
|
||||
@@ -1327,13 +1312,17 @@ void ps_blend(inout float4 Color, inout float4 As_rgba, float2 pos_xy)
|
||||
#endif
|
||||
|
||||
#if PS_ROV_COLOR || PS_ROV_DEPTH
|
||||
#define DISCARD rov_discard = true
|
||||
#define DISCARD { rov_discard_color = true; rov_discard_depth = true; }
|
||||
#define DISCARD_COLOR rov_discard_color = true
|
||||
#define DISCARD_DEPTH rov_discard_depth = true
|
||||
#else
|
||||
#define DISCARD discard
|
||||
#define DISCARD discard
|
||||
#define DISCARD_COLOR o_col0 = RtLoad(input.p.xy)
|
||||
#define DISCARD_DEPTH input.p.z = DepthLoad(input.p.xy)
|
||||
#endif
|
||||
|
||||
#if (PS_RETURN_COLOR || PS_RETURN_DEPTH)
|
||||
PS_OUTPUT_REAL ps_main(PS_INPUT input)
|
||||
PS_OUTPUT ps_main(PS_INPUT input)
|
||||
#else
|
||||
void ps_main(PS_INPUT input)
|
||||
#endif
|
||||
@@ -1352,7 +1341,8 @@ void ps_main(PS_INPUT input)
|
||||
#endif
|
||||
|
||||
#if PS_ROV_COLOR || PS_ROV_DEPTH
|
||||
bool rov_discard = false;
|
||||
bool rov_discard_color = false;
|
||||
bool rov_discard_depth = false;
|
||||
#endif
|
||||
|
||||
// Use ROV discard macro for since we cannot do
|
||||
@@ -1459,19 +1449,29 @@ if (bad)
|
||||
discard;
|
||||
#endif
|
||||
|
||||
PS_OUTPUT output;
|
||||
// Output values
|
||||
#if !PS_NO_COLOR
|
||||
#if PS_DATE == 1 || PS_DATE == 2
|
||||
float o_col0;
|
||||
#else
|
||||
float4 o_col0;
|
||||
#if !PS_NO_COLOR1
|
||||
float4 o_col1;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Get first primitive that will write a failling alpha value
|
||||
#if PS_DATE == 1
|
||||
// DATM == 0
|
||||
// Pixel with alpha equal to 1 will failed (128-255)
|
||||
output.c = (C.a > 127.5f) ? float(input.primid) : float(0x7FFFFFFF);
|
||||
o_col0 = (C.a > 127.5f) ? float(input.primid) : float(0x7FFFFFFF);
|
||||
|
||||
#elif PS_DATE == 2
|
||||
|
||||
// DATM == 1
|
||||
// Pixel with alpha equal to 0 will failed (0-127)
|
||||
output.c = (C.a < 127.5f) ? float(input.primid) : float(0x7FFFFFFF);
|
||||
o_col0 = (C.a < 127.5f) ? float(input.primid) : float(0x7FFFFFFF);
|
||||
|
||||
#else
|
||||
// Not primid DATE setup
|
||||
@@ -1548,26 +1548,26 @@ if (bad)
|
||||
|
||||
// Output color scaling
|
||||
#if !PS_NO_COLOR
|
||||
output.c0.a = PS_RTA_CORRECTION ? C.a / 128.0f : C.a / 255.0f;
|
||||
output.c0.rgb = PS_COLCLIP_HW ? float3(C.rgb / 65535.0f) : C.rgb / 255.0f;
|
||||
o_col0.a = PS_RTA_CORRECTION ? C.a / 128.0f : C.a / 255.0f;
|
||||
o_col0.rgb = PS_COLCLIP_HW ? float3(C.rgb / 65535.0f) : C.rgb / 255.0f;
|
||||
#if !PS_NO_COLOR1
|
||||
output.c1 = alpha_blend;
|
||||
o_col1 = alpha_blend;
|
||||
#endif
|
||||
#endif // !PS_NO_COLOR
|
||||
|
||||
// Alpha test with feedback
|
||||
#if PS_AFAIL == AFAIL_FB_ONLY
|
||||
if (!atst_pass)
|
||||
input.p.z = DepthLoad(input.p.xy);
|
||||
DISCARD_DEPTH;
|
||||
#elif PS_AFAIL == AFAIL_ZB_ONLY
|
||||
if (!atst_pass)
|
||||
output.c0 = RtLoad(input.p.xy);
|
||||
DISCARD_COLOR;
|
||||
#elif PS_AFAIL == AFAIL_RGB_ONLY || PS_AFAIL == AFAIL_RGB_ONLY_SW_Z
|
||||
if (!atst_pass)
|
||||
{
|
||||
output.c0.a = RtLoad(input.p.xy).a;
|
||||
o_col0.a = RtLoad(input.p.xy).a; // discard alpha
|
||||
#if PS_AFAIL == AFAIL_RGB_ONLY_SW_Z
|
||||
input.p.z = DepthLoad(input.p.xy);
|
||||
DISCARD_DEPTH;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
@@ -1580,46 +1580,39 @@ if (bad)
|
||||
|
||||
#if PS_AA1 == PS_AA1_TRIANGLE_SW_Z
|
||||
if (!bool(input.interior))
|
||||
input.p.z = DepthLoad(input.p.xy); // No depth update for triangle edges.
|
||||
DISCARD_DEPTH; // No depth update for triangle edges.
|
||||
#endif
|
||||
|
||||
#if (PS_RETURN_COLOR || PS_RETURN_DEPTH)
|
||||
// Output struct with the actual system values output semantics.
|
||||
PS_OUTPUT_REAL output_real;
|
||||
PS_OUTPUT output;
|
||||
#endif
|
||||
|
||||
// Color write back
|
||||
#if PS_RETURN_COLOR
|
||||
#if PS_DATE == 1 || PS_DATE == 2
|
||||
output_real.c = output.c;
|
||||
#else
|
||||
output_real.c0 = output.c0;
|
||||
#if !PS_NO_COLOR1
|
||||
output_real.c1 = output.c1;
|
||||
#endif
|
||||
output.c0 = o_col0;
|
||||
#if !PS_NO_COLOR1
|
||||
output.c1 = o_col1;
|
||||
#endif
|
||||
#elif PS_RETURN_COLOR_ROV
|
||||
output.c0 = (rov_discard | (FbMask == 0xFFu)) ? RtLoad(input.p.xy) : output.c0;
|
||||
|
||||
RtWrite(input.p.xy, output.c0);
|
||||
o_col0 = (FbMask == 0xFFu) ? RtLoad(input.p.xy) : o_col0; // channel masking
|
||||
if (!rov_discard_color)
|
||||
RtWrite(input.p.xy, o_col0);
|
||||
#endif
|
||||
|
||||
// Depth write back
|
||||
#if PS_RETURN_DEPTH
|
||||
output_real.depth = input.p.z;
|
||||
#if SW_DEPTH && PS_NO_COLOR1 && DX12
|
||||
output.depth = input.p.z;
|
||||
#if SW_DEPTH && PS_NO_COLOR1 && PS_DEPTH_FEEDBACK_SUPPORT == 2
|
||||
// Output color clone for feedback.
|
||||
output_real.depth_color = input.p.z;
|
||||
output.depth_color = input.p.z;
|
||||
#endif
|
||||
#elif PS_RETURN_DEPTH_ROV
|
||||
#if SW_DEPTH
|
||||
input.p.z = rov_discard ? DepthLoad(input.p.xy) : input.p.z;
|
||||
#endif
|
||||
DepthWrite(input.p.xy, input.p.z);
|
||||
if (!rov_discard_depth)
|
||||
DepthWrite(input.p.xy, input.p.z);
|
||||
#endif
|
||||
|
||||
#if (PS_RETURN_COLOR || PS_RETURN_DEPTH)
|
||||
return output_real;
|
||||
return output;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -2021,11 +2014,6 @@ VS_OUTPUT vs_main_expand(uint vid : SV_VertexID)
|
||||
vtx.inv_cov = is_near_corner ? 0.0f : 1.0f; // Full coverage at near corner, otherwise none.
|
||||
|
||||
vtx.interior = 0;
|
||||
|
||||
#if !VS_IIP
|
||||
// Get the provoking vertex color (first vertex in DX)
|
||||
vtx.c = i0 == 0 ? vtx.c : (i1 == 0 ? other.c : opposite.c);
|
||||
#endif
|
||||
}
|
||||
|
||||
return vtx;
|
||||
|
||||
@@ -220,22 +220,27 @@ float manual_lod(float uv_w)
|
||||
vec4 sample_c_af(vec2 uv, float uv_w)
|
||||
{
|
||||
// HW sampler will reject bad UVs, match that here.
|
||||
uv = (any(isnan(uv)) || any(isinf(uv))) ? vec2(0, 0) : uv;
|
||||
uv = (any(isnan(uv)) || any(isinf(uv))) ? vec2(0.0f, 0.0f) : uv;
|
||||
|
||||
// Large floating point values risk NaN/Inf values.
|
||||
// Above this value floats lose decimal precision, so seems a resonable limit for UVs.
|
||||
uv = clamp(uv, -8388608.0f, 8388608.0f);
|
||||
|
||||
// Below taken from https://microsoft.github.io/DirectX-Specs/d3d/archive/D3D11_3_FunctionalSpec.htm#7.18.11%20LOD%20Calculations
|
||||
// And https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_filter_anisotropic.txt
|
||||
// With guidance from https://pema.dev/2025/05/09/mipmaps-too-much-detail/
|
||||
vec2 sz = textureSize(TextureSampler, 0);
|
||||
vec2 dX = dFdx(uv) * sz;
|
||||
vec2 dY = dFdy(uv) * sz;
|
||||
|
||||
float length_x = length(dX);
|
||||
float length_y = length(dY);
|
||||
|
||||
// Calculate Ellipse Transform
|
||||
bool d_zero = length(dX) == 0 || length(dY) == 0;
|
||||
bool d_par = (dX.x * dY.y - dY.x * dX.y) == 0;
|
||||
bool d_per = dot(dX, dY) == 0;
|
||||
bool d_zero = length_x < 0.001f || length_y < 0.001f;
|
||||
float f = (dX.x * dY.y - dX.y * dY.x);
|
||||
bool d_par = f < 0.001f;
|
||||
bool d_per = dot(dX, dY) < 0.001f;
|
||||
bool d_inf_nan = any(isinf(dX)) || any(isinf(dY)) || any(isnan(dX)) || any(isnan(dY));
|
||||
|
||||
if (!(d_zero || d_par || d_per || d_inf_nan))
|
||||
@@ -243,38 +248,46 @@ vec4 sample_c_af(vec2 uv, float uv_w)
|
||||
float A = dX.y * dX.y + dY.y * dY.y;
|
||||
float B = -2 * (dX.x * dX.y + dY.x * dY.y);
|
||||
float C = dX.x * dX.x + dY.x * dY.x;
|
||||
float f = (dX.x * dY.y - dY.x * dX.y);
|
||||
float F = f * f;
|
||||
|
||||
float p = A - C;
|
||||
float q = A + C;
|
||||
float t = sqrt(p * p + B * B);
|
||||
|
||||
float signB = sign(B);
|
||||
float denom_plus = t * (q + t);
|
||||
float denom_minus = t * (q - t);
|
||||
|
||||
float sqrtA = sqrt(F * (t + p));
|
||||
float sqrtB = sqrt(F * (t - p));
|
||||
|
||||
float inv_sqrt_denom_plus = inversesqrt(denom_plus);
|
||||
float inv_sqrt_denom_minus = inversesqrt(denom_minus);
|
||||
|
||||
vec2 new_dX = vec2(
|
||||
sqrt(F * (t + p) / (t * (q + t))),
|
||||
sqrt(F * (t - p) / (t * (q + t))) * sign(B)
|
||||
sqrtA * inv_sqrt_denom_plus,
|
||||
sqrtB * inv_sqrt_denom_plus * signB
|
||||
);
|
||||
|
||||
|
||||
vec2 new_dY = vec2(
|
||||
sqrt(F * (t - p) / (t * (q - t))) * -sign(B),
|
||||
sqrt(F * (t + p) / (t * (q - t)))
|
||||
sqrtB * inv_sqrt_denom_minus * -signB,
|
||||
sqrtA * inv_sqrt_denom_minus
|
||||
);
|
||||
|
||||
|
||||
d_inf_nan = any(isinf(new_dX)) || any(isinf(new_dY)) || any(isnan(new_dX)) || any(isnan(new_dY));
|
||||
if (!d_inf_nan)
|
||||
{
|
||||
dX = new_dX;
|
||||
dY = new_dY;
|
||||
length_x = length(dX);
|
||||
length_y = length(dY);
|
||||
}
|
||||
}
|
||||
|
||||
// Compute AF values
|
||||
float squared_length_x = dX.x * dX.x + dX.y * dX.y;
|
||||
float squared_length_y = dY.x * dY.x + dY.y * dY.y;
|
||||
float determinant = abs(dX.x * dY.y - dX.y * dY.x);
|
||||
bool is_major_x = squared_length_x > squared_length_y;
|
||||
float squared_length_major = is_major_x ? squared_length_x : squared_length_y;
|
||||
float length_major = sqrt(squared_length_major);
|
||||
bool is_major_x = length_x > length_y;
|
||||
float length_major = is_major_x ? length_x : length_y;
|
||||
float length_minor = is_major_x ? length_y : length_x;
|
||||
|
||||
float aniso_ratio;
|
||||
float length_lod;
|
||||
@@ -286,57 +299,42 @@ vec4 sample_c_af(vec2 uv, float uv_w)
|
||||
// Perform isotropic filtering instead.
|
||||
aniso_ratio = 1.0f;
|
||||
length_lod = length_major;
|
||||
aniso_line = vec2(0, 0);
|
||||
aniso_line = vec2(0.0f, 0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
float norm_major = 1.0f / length_major;
|
||||
|
||||
vec2 aniso_line_dir = vec2(
|
||||
(is_major_x ? dX.x : dY.x) * norm_major,
|
||||
(is_major_x ? dX.y : dY.y) * norm_major
|
||||
);
|
||||
|
||||
aniso_ratio = squared_length_major / determinant;
|
||||
vec2 aniso_line_dir = is_major_x ? dX : dY;
|
||||
|
||||
// Calculate the minor length of the ellipse for Lod, while also clamping the ratio of anisotropy.
|
||||
if (aniso_ratio > PS_ANISOTROPIC_FILTERING)
|
||||
{
|
||||
// ratio is clamped - Lod is based on ratio (preserves area)
|
||||
aniso_ratio = PS_ANISOTROPIC_FILTERING;
|
||||
length_lod = length_major / PS_ANISOTROPIC_FILTERING;
|
||||
}
|
||||
else
|
||||
{
|
||||
// ratio not clamped - Lod is based on area
|
||||
length_lod = determinant / length_major;
|
||||
}
|
||||
aniso_ratio = min(length_major / length_minor, PS_ANISOTROPIC_FILTERING);
|
||||
length_lod = length_major / aniso_ratio;
|
||||
|
||||
// clamp to top Lod
|
||||
if (length_lod < 1.0f)
|
||||
aniso_ratio = max(1.0f, aniso_ratio * length_lod);
|
||||
|
||||
aniso_ratio = round(aniso_ratio);
|
||||
aniso_line = aniso_line_dir * 0.5f * length_major * (1.0f / sz);
|
||||
|
||||
aniso_line = aniso_line_dir * 0.5f * (1.0f / sz);
|
||||
}
|
||||
|
||||
|
||||
#if PS_AUTOMATIC_LOD == 1
|
||||
float lod = log2(length_lod);
|
||||
#elif PS_MANUAL_LOD == 1
|
||||
float lod = manual_lod(uv_w);
|
||||
#else
|
||||
float lod = 0; // No Lod
|
||||
float lod = 0.0f; // No Lod
|
||||
#endif
|
||||
|
||||
|
||||
vec4 colour;
|
||||
if (aniso_ratio == 1.0f)
|
||||
colour = textureLod(TextureSampler, uv, lod);
|
||||
else
|
||||
{
|
||||
vec4 num = vec4(0, 0, 0, 0);
|
||||
vec4 num = vec4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
vec2 segment = (2.0f * aniso_line) / aniso_ratio;
|
||||
for (int i = 0; i < aniso_ratio; i++)
|
||||
{
|
||||
vec2 d = -aniso_line + (0.5f + i) * (2.0f * aniso_line) / aniso_ratio;
|
||||
{
|
||||
vec2 d = -aniso_line + (0.5f + i) * segment;
|
||||
vec2 uv_sample = uv + d;
|
||||
vec4 sample_colour = textureLod(TextureSampler, uv_sample, lod);
|
||||
num += sample_colour;
|
||||
|
||||
@@ -324,7 +324,8 @@ void main()
|
||||
// Use bottom minus top for delta regardless of which vertex we are expanding.
|
||||
vec2 line_delta = is_bottom ? (vtx.p.xy - other.p.xy) : (other.p.xy - vtx.p.xy);
|
||||
vec2 line_vector = normalize(line_delta / VertexScale);
|
||||
if VS_EXPAND == VS_EXPAND_LINE_AA1
|
||||
vec2 line_expand = vec2(line_vector.y, -line_vector.x);
|
||||
#if VS_EXPAND == VS_EXPAND_LINE_AA1
|
||||
line_expand *= 2.0f * LineAA1Width;
|
||||
#endif
|
||||
vec2 line_width = (line_expand * PointSize) / 2;
|
||||
@@ -454,11 +455,6 @@ if VS_EXPAND == VS_EXPAND_LINE_AA1
|
||||
VSout.inv_cov = is_near_corner ? 0.0f : 1.0f; // Full coverage at near corner, otherwise none.
|
||||
|
||||
VSout.interior = 0;
|
||||
|
||||
#if !VS_IIP
|
||||
// Get the provoking vertex color (last vertex in GL)
|
||||
vtx.c = i0 == 2 ? vtx.c : (i1 == 2 ? other.c : opposite.c);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -450,11 +450,6 @@ void main()
|
||||
vsOut.inv_cov = is_near_corner ? 0.0f : 1.0f; // Full coverage at near corner, otherwise none.
|
||||
|
||||
vsOut.interior = 0;
|
||||
|
||||
#if !VS_IIP
|
||||
// Get the provoking vertex color (last vertex in VK)
|
||||
vtx.c = i0 == 2 ? vtx.c : (i1 == 2 ? other.c : opposite.c);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -728,22 +723,27 @@ float manual_lod(float uv_w)
|
||||
vec4 sample_c_af(vec2 uv, float uv_w)
|
||||
{
|
||||
// HW sampler will reject bad UVs, match that here.
|
||||
uv = (any(isnan(uv)) || any(isinf(uv))) ? vec2(0, 0) : uv;
|
||||
uv = (any(isnan(uv)) || any(isinf(uv))) ? vec2(0.0f, 0.0f) : uv;
|
||||
|
||||
// Large floating point values risk NaN/Inf values.
|
||||
// Above this value floats lose decimal precision, so seems a resonable limit for UVs.
|
||||
uv = clamp(uv, -8388608.0f, 8388608.0f);
|
||||
|
||||
// Below taken from https://microsoft.github.io/DirectX-Specs/d3d/archive/D3D11_3_FunctionalSpec.htm#7.18.11%20LOD%20Calculations
|
||||
// And https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_filter_anisotropic.txt
|
||||
// With guidance from https://pema.dev/2025/05/09/mipmaps-too-much-detail/
|
||||
vec2 sz = textureSize(Texture, 0);
|
||||
vec2 dX = dFdx(uv) * sz;
|
||||
vec2 dY = dFdy(uv) * sz;
|
||||
|
||||
float length_x = length(dX);
|
||||
float length_y = length(dY);
|
||||
|
||||
// Calculate Ellipse Transform
|
||||
bool d_zero = length(dX) == 0 || length(dY) == 0;
|
||||
bool d_par = (dX.x * dY.y - dY.x * dX.y) == 0;
|
||||
bool d_per = dot(dX, dY) == 0;
|
||||
bool d_zero = length_x < 0.001f || length_y < 0.001f;
|
||||
float f = (dX.x * dY.y - dX.y * dY.x);
|
||||
bool d_par = f < 0.001f;
|
||||
bool d_per = dot(dX, dY) < 0.001f;
|
||||
bool d_inf_nan = any(isinf(dX)) || any(isinf(dY)) || any(isnan(dX)) || any(isnan(dY));
|
||||
|
||||
if (!(d_zero || d_par || d_per || d_inf_nan))
|
||||
@@ -758,31 +758,40 @@ vec4 sample_c_af(vec2 uv, float uv_w)
|
||||
float q = A + C;
|
||||
float t = sqrt(p * p + B * B);
|
||||
|
||||
float signB = sign(B);
|
||||
float denom_plus = t * (q + t);
|
||||
float denom_minus = t * (q - t);
|
||||
|
||||
float sqrtA = sqrt(F * (t + p));
|
||||
float sqrtB = sqrt(F * (t - p));
|
||||
|
||||
float inv_sqrt_denom_plus = inversesqrt(denom_plus);
|
||||
float inv_sqrt_denom_minus = inversesqrt(denom_minus);
|
||||
|
||||
vec2 new_dX = vec2(
|
||||
sqrt(F * (t + p) / (t * (q + t))),
|
||||
sqrt(F * (t - p) / (t * (q + t))) * sign(B)
|
||||
sqrtA * inv_sqrt_denom_plus,
|
||||
sqrtB * inv_sqrt_denom_plus * signB
|
||||
);
|
||||
|
||||
|
||||
vec2 new_dY = vec2(
|
||||
sqrt(F * (t - p) / (t * (q - t))) * -sign(B),
|
||||
sqrt(F * (t + p) / (t * (q - t)))
|
||||
sqrtB * inv_sqrt_denom_minus * -signB,
|
||||
sqrtA * inv_sqrt_denom_minus
|
||||
);
|
||||
|
||||
|
||||
d_inf_nan = any(isinf(new_dX)) || any(isinf(new_dY)) || any(isnan(new_dX)) || any(isnan(new_dY));
|
||||
if (!d_inf_nan)
|
||||
{
|
||||
dX = new_dX;
|
||||
dY = new_dY;
|
||||
length_x = length(dX);
|
||||
length_y = length(dY);
|
||||
}
|
||||
}
|
||||
|
||||
// Compute AF values
|
||||
float squared_length_x = dX.x * dX.x + dX.y * dX.y;
|
||||
float squared_length_y = dY.x * dY.x + dY.y * dY.y;
|
||||
float determinant = abs(dX.x * dY.y - dX.y * dY.x);
|
||||
bool is_major_x = squared_length_x > squared_length_y;
|
||||
float squared_length_major = is_major_x ? squared_length_x : squared_length_y;
|
||||
float length_major = sqrt(squared_length_major);
|
||||
bool is_major_x = length_x > length_y;
|
||||
float length_major = is_major_x ? length_x : length_y;
|
||||
float length_minor = is_major_x ? length_y : length_x;
|
||||
|
||||
float aniso_ratio;
|
||||
float length_lod;
|
||||
@@ -794,57 +803,42 @@ vec4 sample_c_af(vec2 uv, float uv_w)
|
||||
// Perform isotropic filtering instead.
|
||||
aniso_ratio = 1.0f;
|
||||
length_lod = length_major;
|
||||
aniso_line = vec2(0, 0);
|
||||
aniso_line = vec2(0.0f, 0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
float norm_major = 1.0f / length_major;
|
||||
|
||||
vec2 aniso_line_dir = vec2(
|
||||
(is_major_x ? dX.x : dY.x) * norm_major,
|
||||
(is_major_x ? dX.y : dY.y) * norm_major
|
||||
);
|
||||
|
||||
aniso_ratio = squared_length_major / determinant;
|
||||
vec2 aniso_line_dir = is_major_x ? dX : dY;
|
||||
|
||||
// Calculate the minor length of the ellipse for Lod, while also clamping the ratio of anisotropy.
|
||||
if (aniso_ratio > PS_ANISOTROPIC_FILTERING)
|
||||
{
|
||||
// ratio is clamped - Lod is based on ratio (preserves area)
|
||||
aniso_ratio = PS_ANISOTROPIC_FILTERING;
|
||||
length_lod = length_major / PS_ANISOTROPIC_FILTERING;
|
||||
}
|
||||
else
|
||||
{
|
||||
// ratio not clamped - Lod is based on area
|
||||
length_lod = determinant / length_major;
|
||||
}
|
||||
aniso_ratio = min(length_major / length_minor, PS_ANISOTROPIC_FILTERING);
|
||||
length_lod = length_major / aniso_ratio;
|
||||
|
||||
// clamp to top Lod
|
||||
if (length_lod < 1.0f)
|
||||
aniso_ratio = max(1.0f, aniso_ratio * length_lod);
|
||||
|
||||
aniso_ratio = round(aniso_ratio);
|
||||
aniso_line = aniso_line_dir * 0.5f * length_major * (1.0f / sz);
|
||||
|
||||
aniso_line = aniso_line_dir * 0.5f * (1.0f / sz);
|
||||
}
|
||||
|
||||
|
||||
#if PS_AUTOMATIC_LOD == 1
|
||||
float lod = log2(length_lod);
|
||||
#elif PS_MANUAL_LOD == 1
|
||||
float lod = manual_lod(uv_w);
|
||||
#else
|
||||
float lod = 0; // No Lod
|
||||
float lod = 0.0f; // No Lod
|
||||
#endif
|
||||
|
||||
|
||||
vec4 colour;
|
||||
if (aniso_ratio == 1.0f)
|
||||
colour = textureLod(Texture, uv, lod);
|
||||
else
|
||||
{
|
||||
vec4 num = vec4(0, 0, 0, 0);
|
||||
vec4 num = vec4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
vec2 segment = (2.0f * aniso_line) / aniso_ratio;
|
||||
for (int i = 0; i < aniso_ratio; i++)
|
||||
{
|
||||
vec2 d = -aniso_line + (0.5f + i) * (2.0f * aniso_line) / aniso_ratio;
|
||||
{
|
||||
vec2 d = -aniso_line + (0.5f + i) * segment;
|
||||
vec2 uv_sample = uv + d;
|
||||
vec4 sample_colour = textureLod(Texture, uv_sample, lod);
|
||||
num += sample_colour;
|
||||
@@ -1721,9 +1715,13 @@ layout(early_fragment_tests) in;
|
||||
#endif
|
||||
|
||||
#if PS_ROV_COLOR || PS_ROV_DEPTH
|
||||
#define DISCARD rov_discard = true
|
||||
#define DISCARD { rov_discard_color = true; rov_discard_depth = true; }
|
||||
#define DISCARD_COLOR rov_discard_color = true
|
||||
#define DISCARD_DEPTH rov_discard_depth = true
|
||||
#else
|
||||
#define DISCARD discard
|
||||
#define DISCARD discard
|
||||
#define DISCARD_COLOR o_col0 = sample_from_rt()
|
||||
#define DISCARD_DEPTH input_z = sample_from_depth()
|
||||
#endif
|
||||
|
||||
void main()
|
||||
@@ -1748,7 +1746,8 @@ void main()
|
||||
#endif
|
||||
|
||||
#if PS_ROV_COLOR || PS_ROV_DEPTH
|
||||
bool rov_discard = gl_HelperInvocation;
|
||||
bool rov_discard_color = gl_HelperInvocation;
|
||||
bool rov_discard_depth = gl_HelperInvocation;
|
||||
#endif
|
||||
|
||||
#if PS_ZTST == ZTST_GEQUAL
|
||||
@@ -1941,16 +1940,16 @@ void main()
|
||||
// Alpha test with feedback
|
||||
#if PS_AFAIL == AFAIL_FB_ONLY
|
||||
if (!atst_pass)
|
||||
input_z = sample_from_depth();
|
||||
DISCARD_DEPTH;
|
||||
#elif PS_AFAIL == AFAIL_ZB_ONLY
|
||||
if (!atst_pass)
|
||||
o_col0 = sample_from_rt();
|
||||
DISCARD_COLOR;
|
||||
#elif (PS_AFAIL == AFAIL_RGB_ONLY || PS_AFAIL == AFAIL_RGB_ONLY_SW_Z)
|
||||
if (!atst_pass)
|
||||
{
|
||||
o_col0.a = sample_from_rt().a;
|
||||
o_col0.a = sample_from_rt().a; // discard alpha
|
||||
#if PS_AFAIL == AFAIL_RGB_ONLY_SW_Z
|
||||
input_z = sample_from_depth();
|
||||
DISCARD_DEPTH;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
@@ -1962,24 +1961,23 @@ void main()
|
||||
|
||||
#if PS_AA1 == PS_AA1_TRIANGLE_SW_Z
|
||||
if (!bool(vsIn.interior))
|
||||
input_z = sample_from_depth(); // No depth update for triangle edges.
|
||||
DISCARD_DEPTH; // No depth update for triangle edges.
|
||||
#endif
|
||||
|
||||
// Writing back color (result already written to o_col0 for non-ROV)
|
||||
#if PS_RETURN_COLOR_ROV
|
||||
bvec4 discard_channels = bvec4(uvec4(rov_discard) | uvec4(equal(FbMask, uvec4(0xFFu))));
|
||||
o_col0 = mix(o_col0, sample_from_rt(), discard_channels);
|
||||
o_col0 = mix(o_col0, sample_from_rt(), equal(FbMask, uvec4(0xFFu))); // channel masking
|
||||
|
||||
imageStore(RtImageRov, ivec2(gl_FragCoord.xy), o_col0);
|
||||
if (!rov_discard_color)
|
||||
imageStore(RtImageRov, ivec2(gl_FragCoord.xy), o_col0);
|
||||
#endif
|
||||
|
||||
// Writing back depth
|
||||
#if PS_RETURN_DEPTH
|
||||
gl_FragDepth = input_z;
|
||||
#elif PS_RETURN_DEPTH_ROV
|
||||
input_z = rov_discard ? sample_from_depth() : input_z;
|
||||
|
||||
imageStore(DepthImageRov, ivec2(gl_FragCoord.xy), vec4(input_z, 0, 0, 1.0f));
|
||||
if (!rov_discard_depth)
|
||||
imageStore(DepthImageRov, ivec2(gl_FragCoord.xy), vec4(input_z, 0, 0, 1.0f));
|
||||
#endif
|
||||
|
||||
#if PS_ROV_COLOR || PS_ROV_DEPTH
|
||||
|
||||
@@ -34,6 +34,34 @@ Value MemoryInterface::Read(u32 address, bool* valid)
|
||||
return Value(0);
|
||||
}
|
||||
|
||||
std::optional<std::string> MemoryInterface::ReadString(u32 address, u32 max_size, ReadStringFlags flags)
|
||||
{
|
||||
std::string string;
|
||||
|
||||
for (u32 i = 0; i < max_size; i++)
|
||||
{
|
||||
bool valid;
|
||||
char c = Read8(address + i, &valid);
|
||||
if (!valid)
|
||||
return std::nullopt;
|
||||
|
||||
if (c == '\0')
|
||||
return string;
|
||||
else if (!(flags & ALLOW_NON_PRINTABLE_CHARACTERS) && (c < ' ' || c > '~'))
|
||||
return std::nullopt;
|
||||
|
||||
string += c;
|
||||
}
|
||||
|
||||
if (flags & ALLOW_LONG_STRINGS)
|
||||
{
|
||||
string += '~';
|
||||
return string;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
template <MemoryAccessType Value>
|
||||
bool MemoryInterface::Write(u32 address, Value value)
|
||||
{
|
||||
@@ -62,6 +90,17 @@ bool MemoryInterface::Write(u32 address, Value value)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MemoryInterface::WriteString(u32 address, std::string_view string)
|
||||
{
|
||||
if (!WriteBytes(address, string.data(), static_cast<u32>(string.size())))
|
||||
return false;
|
||||
|
||||
if (!Write8(address + static_cast<u32>(string.size()), '\0'))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MemoryInterface::IdempotentWrite8(u32 address, u8 value)
|
||||
{
|
||||
bool valid;
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
|
||||
#include "Pcsx2Types.h"
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
template <typename Value>
|
||||
@@ -31,16 +34,27 @@ public:
|
||||
template <MemoryAccessType Value>
|
||||
Value Read(u32 address, bool* valid = nullptr);
|
||||
|
||||
enum ReadStringFlags
|
||||
{
|
||||
NO_FLAGS = 0,
|
||||
ALLOW_LONG_STRINGS = 1 << 0,
|
||||
ALLOW_NON_PRINTABLE_CHARACTERS = 1 << 1
|
||||
};
|
||||
|
||||
std::optional<std::string> ReadString(u32 address, u32 max_size = 256, ReadStringFlags flags = NO_FLAGS);
|
||||
|
||||
virtual bool Write8(u32 address, u8 value) = 0;
|
||||
virtual bool Write16(u32 address, u16 value) = 0;
|
||||
virtual bool Write32(u32 address, u32 value) = 0;
|
||||
virtual bool Write64(u32 address, u64 value) = 0;
|
||||
virtual bool Write128(u32 address, u128 value) = 0;
|
||||
virtual bool WriteBytes(u32 address, void* src, u32 size) = 0;
|
||||
virtual bool WriteBytes(u32 address, const void* src, u32 size) = 0;
|
||||
|
||||
template <MemoryAccessType Value>
|
||||
bool Write(u32 address, Value value);
|
||||
|
||||
bool WriteString(u32 address, std::string_view string);
|
||||
|
||||
bool IdempotentWrite8(u32 address, u8 value);
|
||||
bool IdempotentWrite16(u32 address, u16 value);
|
||||
bool IdempotentWrite32(u32 address, u32 value);
|
||||
@@ -51,5 +65,5 @@ public:
|
||||
template <MemoryAccessType Value>
|
||||
bool IdempotentWrite(u32 address, Value value);
|
||||
|
||||
virtual bool CompareBytes(u32 address, void* src, u32 size) = 0;
|
||||
virtual bool CompareBytes(u32 address, const void* src, u32 size) = 0;
|
||||
};
|
||||
|
||||
+102
-30
@@ -17,7 +17,7 @@ struct RapidYAMLContext
|
||||
Error* error = nullptr;
|
||||
};
|
||||
|
||||
std::optional<ryml::Tree> ParseYAMLFromString(ryml::csubstr yaml, ryml::csubstr file_name, Error* error)
|
||||
std::optional<ryml::Tree> ParseYAMLFromString(ryml::csubstr yaml, ryml::csubstr file_name, Error* error, bool resolve_anchors)
|
||||
{
|
||||
RapidYAMLContext context;
|
||||
context.error = error;
|
||||
@@ -28,63 +28,135 @@ std::optional<ryml::Tree> ParseYAMLFromString(ryml::csubstr yaml, ryml::csubstr
|
||||
callbacks.set_user_data(static_cast<void*>(&context));
|
||||
|
||||
callbacks.set_error_basic([](ryml::csubstr msg, const ryml::ErrorDataBasic& errdata, void* user_data) {
|
||||
std::string description;
|
||||
auto callback = [&description](ryml::csubstr string) {
|
||||
description.append(string.str, string.len);
|
||||
};
|
||||
ryml::err_basic_format(std::move(callback), msg, errdata);
|
||||
RapidYAMLContext* context = static_cast<RapidYAMLContext*>(user_data);
|
||||
// This scope needs to stay, so all objects destruct before std::longjump
|
||||
{
|
||||
std::string description;
|
||||
auto callback = [&description](ryml::csubstr string) {
|
||||
description.append(string.str, string.len);
|
||||
};
|
||||
ryml::err_basic_format(std::move(callback), msg, errdata);
|
||||
|
||||
// We might have already returned, so don't try to recover.
|
||||
pxFailRel(description.c_str());
|
||||
std::abort();
|
||||
if (context != nullptr)
|
||||
{
|
||||
Error::SetString(context->error, std::move(description));
|
||||
}
|
||||
else
|
||||
{
|
||||
pxFailRel(description.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (context != nullptr)
|
||||
{
|
||||
std::longjmp(context->env, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::terminate();
|
||||
}
|
||||
});
|
||||
|
||||
callbacks.set_error_parse([](ryml::csubstr msg, const ryml::ErrorDataParse& errdata, void* user_data) {
|
||||
RapidYAMLContext* context = static_cast<RapidYAMLContext*>(user_data);
|
||||
// This scope needs to stay, so all objects destruct before std::longjump
|
||||
{
|
||||
std::string description;
|
||||
auto callback = [&description](ryml::csubstr string) {
|
||||
description.append(string.str, string.len);
|
||||
};
|
||||
ryml::err_parse_format(std::move(callback), msg, errdata);
|
||||
|
||||
std::string description;
|
||||
auto callback = [&description](ryml::csubstr string) {
|
||||
description.append(string.str, string.len);
|
||||
};
|
||||
ryml::err_parse_format(std::move(callback), msg, errdata);
|
||||
if (context != nullptr)
|
||||
{
|
||||
Error::SetString(context->error, std::move(description));
|
||||
}
|
||||
else
|
||||
{
|
||||
pxFailRel(description.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
Error::SetString(context->error, std::move(description));
|
||||
std::longjmp(context->env, 1);
|
||||
if (context != nullptr)
|
||||
{
|
||||
std::longjmp(context->env, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::terminate();
|
||||
}
|
||||
});
|
||||
|
||||
callbacks.set_error_visit([](ryml::csubstr msg, const ryml::ErrorDataVisit& errdata, void* user_data) {
|
||||
std::string description;
|
||||
auto callback = [&description](ryml::csubstr string) {
|
||||
description.append(string.str, string.len);
|
||||
};
|
||||
ryml::err_visit_format(std::move(callback), msg, errdata);
|
||||
RapidYAMLContext* context = static_cast<RapidYAMLContext*>(user_data);
|
||||
// This scope needs to stay, so all objects destruct before std::longjump
|
||||
{
|
||||
std::string description;
|
||||
auto callback = [&description](ryml::csubstr string) {
|
||||
description.append(string.str, string.len);
|
||||
};
|
||||
ryml::err_visit_format(std::move(callback), msg, errdata);
|
||||
|
||||
// We've probably already returned, so don't try to recover.
|
||||
pxFailRel(description.c_str());
|
||||
std::abort();
|
||||
if (context != nullptr)
|
||||
{
|
||||
Error::SetString(context->error, std::move(description));
|
||||
}
|
||||
else
|
||||
{
|
||||
pxFailRel(description.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (context != nullptr)
|
||||
{
|
||||
std::longjmp(context->env, 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::terminate();
|
||||
}
|
||||
});
|
||||
#else
|
||||
callbacks.m_user_data = static_cast<void*>(&context);
|
||||
callbacks.m_error = [](const char* msg, size_t msg_len, ryml::Location location, void* user_data) {
|
||||
RapidYAMLContext* context = static_cast<RapidYAMLContext*>(user_data);
|
||||
|
||||
Error::SetString(context->error, std::string(msg, msg_len));
|
||||
std::longjmp(context->env, 1);
|
||||
if (RapidYAMLContext* context = static_cast<RapidYAMLContext*>(user_data))
|
||||
{
|
||||
Error::SetString(context->error, std::string(msg, msg_len));
|
||||
std::longjmp(context->env, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string description(msg, msg_len);
|
||||
pxFailRel(description.c_str());
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
ryml::EventHandlerTree event_handler(callbacks);
|
||||
ryml::Parser parser(&event_handler);
|
||||
|
||||
ryml::Tree tree;
|
||||
ryml::Tree tree(callbacks);
|
||||
|
||||
// The only options RapidYAML provides for recovering from errors are
|
||||
// throwing an exception or using setjmp/longjmp. Since we have exceptions
|
||||
// disabled we have to use the latter option.
|
||||
if (setjmp(context.env))
|
||||
if (setjmp(context.env) != 0)
|
||||
return std::nullopt;
|
||||
|
||||
ryml::parse_in_arena(&parser, file_name, yaml, &tree);
|
||||
if (resolve_anchors)
|
||||
{
|
||||
tree.resolve();
|
||||
}
|
||||
|
||||
// Callbacks passed to ryml::Tree are used for value parsing errors later,
|
||||
// so we need to clear the context before it goes out of scope.
|
||||
#if RYML_VERSION_MAJOR > 0 || RYML_VERSION_MINOR >= 11
|
||||
callbacks.set_user_data(nullptr);
|
||||
#else
|
||||
callbacks.m_user_data = nullptr;
|
||||
#endif
|
||||
tree.callbacks(callbacks);
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
+3
-1
@@ -14,4 +14,6 @@
|
||||
/// parsing errors (as is recommended by the documentation for cases where
|
||||
/// exceptions are disabled). The file_name parameter is only used for error
|
||||
/// messages, which are returned via the error parameter.
|
||||
std::optional<ryml::Tree> ParseYAMLFromString(ryml::csubstr yaml, ryml::csubstr file_name, Error* error);
|
||||
/// If resolve_anchors is set to true, YAML anchors and aliases will be resolved.
|
||||
/// It's a potentially slow operation, so it's opt-in.
|
||||
std::optional<ryml::Tree> ParseYAMLFromString(ryml::csubstr yaml, ryml::csubstr file_name, Error* error, bool resolve_anchors = false);
|
||||
|
||||
@@ -163,6 +163,7 @@
|
||||
<ClInclude Include="WindowInfo.h" />
|
||||
<ClInclude Include="YAML.h" />
|
||||
<ClInclude Include="Threading.h" />
|
||||
<ClInclude Include="EnumOps.h" />
|
||||
<ClInclude Include="emitter\implement\avx.h" />
|
||||
<ClInclude Include="emitter\implement\bmi.h" />
|
||||
<ClInclude Include="emitter\instructions.h" />
|
||||
|
||||
@@ -101,7 +101,7 @@ static u64 s_total_barriers = 0;
|
||||
static u64 s_total_copies = 0;
|
||||
static u64 s_total_uploads = 0;
|
||||
static u64 s_total_readbacks = 0;
|
||||
static u64 s_total_depth_copies_rov = 0;
|
||||
static u64 s_total_copies_rov = 0;
|
||||
static u64 s_total_draws_rov = 0;
|
||||
static u64 s_total_barriers_rov = 0;
|
||||
static u32 s_total_frames = 0;
|
||||
@@ -298,7 +298,7 @@ void Host::BeginPresentFrame()
|
||||
update_stat(GSPerfMon::TextureCopies, s_total_copies, s_last_copies);
|
||||
update_stat(GSPerfMon::TextureUploads, s_total_uploads, s_last_uploads);
|
||||
update_stat(GSPerfMon::Readbacks, s_total_readbacks, s_last_readbacks);
|
||||
update_stat(GSPerfMon::DepthCopiesROV, s_total_depth_copies_rov, s_last_depth_copies_rov);
|
||||
update_stat(GSPerfMon::TextureCopiesROV, s_total_copies_rov, s_last_depth_copies_rov);
|
||||
update_stat(GSPerfMon::DrawCallsROV, s_total_draws_rov, s_last_draws_rov);
|
||||
update_stat(GSPerfMon::BarriersROV, s_total_barriers_rov, s_last_barriers_rov);
|
||||
|
||||
@@ -917,7 +917,7 @@ void GSRunner::DumpStats()
|
||||
Console.WriteLn(fmt::format("@HWSTAT@ Copies: {} (avg {})", s_total_copies, static_cast<u64>(std::ceil(s_total_copies / static_cast<double>(s_total_drawn_frames)))));
|
||||
Console.WriteLn(fmt::format("@HWSTAT@ Uploads: {} (avg {})", s_total_uploads, static_cast<u64>(std::ceil(s_total_uploads / static_cast<double>(s_total_drawn_frames)))));
|
||||
Console.WriteLn(fmt::format("@HWSTAT@ Readbacks: {} (avg {})", s_total_readbacks, static_cast<u64>(std::ceil(s_total_readbacks / static_cast<double>(s_total_drawn_frames)))));
|
||||
Console.WriteLn(fmt::format("@HWSTAT@ Depth Copies (ROV): {} (avg {})", s_total_depth_copies_rov, static_cast<u64>(std::ceil(s_total_depth_copies_rov / static_cast<double>(s_total_drawn_frames)))));
|
||||
Console.WriteLn(fmt::format("@HWSTAT@ Copies (ROV): {} (avg {})", s_total_copies_rov, static_cast<u64>(std::ceil(s_total_copies_rov / static_cast<double>(s_total_drawn_frames)))));
|
||||
Console.WriteLn(fmt::format("@HWSTAT@ Draws Calls (ROV): {} (avg {})", s_total_draws_rov, static_cast<u64>(std::ceil(s_total_draws_rov / static_cast<double>(s_total_drawn_frames)))));
|
||||
Console.WriteLn(fmt::format("@HWSTAT@ Barriers (ROV): {} (avg {})", s_total_barriers_rov, static_cast<u64>(std::ceil(s_total_barriers_rov / static_cast<double>(s_total_drawn_frames)))));
|
||||
if (s_perf_enable)
|
||||
|
||||
@@ -196,6 +196,7 @@ target_sources(pcsx2-qt PRIVATE
|
||||
Debugger/ModuleModel.h
|
||||
Debugger/ModuleView.cpp
|
||||
Debugger/ModuleView.h
|
||||
Debugger/NavigationHistoryStack.h
|
||||
Debugger/RegisterView.cpp
|
||||
Debugger/RegisterView.h
|
||||
Debugger/RegisterView.ui
|
||||
|
||||
@@ -206,6 +206,29 @@ void DebuggerView::updateStyleSheet()
|
||||
setStyleSheet(stylesheet);
|
||||
}
|
||||
|
||||
bool DebuggerView::supportsNavigation()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DebuggerView::canNavigateBack()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DebuggerView::canNavigateForward()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void DebuggerView::navigateBack()
|
||||
{
|
||||
}
|
||||
|
||||
void DebuggerView::navigateForward()
|
||||
{
|
||||
}
|
||||
|
||||
void DebuggerView::goToInDisassembler(u32 address, bool switch_to_tab)
|
||||
{
|
||||
DebuggerEvents::GoToAddress event;
|
||||
@@ -268,7 +291,7 @@ std::vector<QAction*> DebuggerView::createEventActionsImplementation(
|
||||
if (lhs->displayNameWithoutSuffix() == rhs->displayNameWithoutSuffix())
|
||||
return lhs->displayNameSuffixNumber() < rhs->displayNameSuffixNumber();
|
||||
|
||||
return lhs->displayNameWithoutSuffix() < rhs->displayNameWithoutSuffix();
|
||||
return QtHost::LocaleSensitiveCompare(lhs->displayNameWithoutSuffix(), rhs->displayNameWithoutSuffix()) < 0;
|
||||
});
|
||||
|
||||
QMenu* submenu = nullptr;
|
||||
|
||||
@@ -153,6 +153,12 @@ public:
|
||||
|
||||
void updateStyleSheet();
|
||||
|
||||
virtual bool supportsNavigation();
|
||||
virtual bool canNavigateBack();
|
||||
virtual bool canNavigateForward();
|
||||
virtual void navigateBack();
|
||||
virtual void navigateForward();
|
||||
|
||||
static void goToInDisassembler(u32 address, bool switch_to_tab);
|
||||
static void goToInMemoryView(u32 address, bool switch_to_tab);
|
||||
|
||||
|
||||
@@ -115,6 +115,28 @@ DebuggerWindow::DebuggerWindow(QWidget* parent)
|
||||
|
||||
setMenuWidget(m_dock_manager->createMenuBar(menu_bar));
|
||||
|
||||
connect(m_dock_manager, &DockManager::focusedViewForNavigationChanged,
|
||||
this, &DebuggerWindow::updateNavigationButtons);
|
||||
|
||||
m_ui.actionNavigateBack->setEnabled(false);
|
||||
m_ui.actionNavigateForward->setEnabled(false);
|
||||
|
||||
connect(m_ui.actionNavigateBack, &QAction::triggered, this, [this]() {
|
||||
DebuggerView* view = m_dock_manager->focusedViewForNavigation();
|
||||
if (!view)
|
||||
return;
|
||||
|
||||
view->navigateBack();
|
||||
});
|
||||
|
||||
connect(m_ui.actionNavigateForward, &QAction::triggered, this, [this]() {
|
||||
DebuggerView* view = m_dock_manager->focusedViewForNavigation();
|
||||
if (!view)
|
||||
return;
|
||||
|
||||
view->navigateForward();
|
||||
});
|
||||
|
||||
updateTheme();
|
||||
|
||||
Host::RunOnCPUThread([]() {
|
||||
@@ -307,6 +329,21 @@ void DebuggerWindow::updateFromSettings()
|
||||
}
|
||||
}
|
||||
|
||||
void DebuggerWindow::updateNavigationButtons()
|
||||
{
|
||||
DebuggerView* view = m_dock_manager->focusedViewForNavigation();
|
||||
if (view)
|
||||
{
|
||||
m_ui.actionNavigateBack->setEnabled(view->canNavigateBack());
|
||||
m_ui.actionNavigateForward->setEnabled(view->canNavigateForward());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ui.actionNavigateBack->setEnabled(false);
|
||||
m_ui.actionNavigateForward->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
void DebuggerWindow::onVMStarting()
|
||||
{
|
||||
m_ui.actionRun->setEnabled(true);
|
||||
|
||||
@@ -40,7 +40,9 @@ public:
|
||||
|
||||
void updateFromSettings();
|
||||
|
||||
public slots:
|
||||
void updateNavigationButtons();
|
||||
|
||||
private slots:
|
||||
void onVMStarting();
|
||||
void onVMPaused();
|
||||
void onVMResumed();
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1000</width>
|
||||
<height>750</height>
|
||||
<width>1322</width>
|
||||
<height>974</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -23,8 +23,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1000</width>
|
||||
<height>21</height>
|
||||
<width>1322</width>
|
||||
<height>30</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuFile">
|
||||
@@ -58,6 +58,9 @@
|
||||
</property>
|
||||
<addaction name="actionOnTop"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionNavigateBack"/>
|
||||
<addaction name="actionNavigateForward"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionIncreaseFontSize"/>
|
||||
<addaction name="actionDecreaseFontSize"/>
|
||||
<addaction name="actionResetFontSize"/>
|
||||
@@ -82,12 +85,25 @@
|
||||
<addaction name="menuWindows"/>
|
||||
<addaction name="menuLayouts"/>
|
||||
</widget>
|
||||
<widget class="QToolBar" name="toolBarNavigation">
|
||||
<property name="windowTitle">
|
||||
<string>Navigation</string>
|
||||
</property>
|
||||
<attribute name="toolBarArea">
|
||||
<enum>TopToolBarArea</enum>
|
||||
</attribute>
|
||||
<attribute name="toolBarBreak">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<addaction name="actionNavigateBack"/>
|
||||
<addaction name="actionNavigateForward"/>
|
||||
</widget>
|
||||
<widget class="QToolBar" name="toolBarDebug">
|
||||
<property name="windowTitle">
|
||||
<string>Debug</string>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
<enum>Qt::ToolButtonStyle::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
<attribute name="toolBarArea">
|
||||
<enum>TopToolBarArea</enum>
|
||||
@@ -105,7 +121,7 @@
|
||||
<string>File</string>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
<enum>Qt::ToolButtonStyle::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
<attribute name="toolBarArea">
|
||||
<enum>TopToolBarArea</enum>
|
||||
@@ -122,7 +138,7 @@
|
||||
<string>System</string>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
<enum>Qt::ToolButtonStyle::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
<attribute name="toolBarArea">
|
||||
<enum>TopToolBarArea</enum>
|
||||
@@ -138,7 +154,7 @@
|
||||
<string>View</string>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
<enum>Qt::ToolButtonStyle::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
<attribute name="toolBarArea">
|
||||
<enum>TopToolBarArea</enum>
|
||||
@@ -237,7 +253,7 @@
|
||||
<string>Shut Down</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::NoRole</enum>
|
||||
<enum>QAction::MenuRole::NoRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionReset">
|
||||
@@ -248,7 +264,7 @@
|
||||
<string>Reset</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::NoRole</enum>
|
||||
<enum>QAction::MenuRole::NoRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionClose">
|
||||
@@ -259,7 +275,7 @@
|
||||
<string>Close</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::NoRole</enum>
|
||||
<enum>QAction::MenuRole::NoRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionIncreaseFontSize">
|
||||
@@ -270,7 +286,7 @@
|
||||
<string>Increase Font Size</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::NoRole</enum>
|
||||
<enum>QAction::MenuRole::NoRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDecreaseFontSize">
|
||||
@@ -284,7 +300,7 @@
|
||||
<string>Ctrl+-</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::NoRole</enum>
|
||||
<enum>QAction::MenuRole::NoRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionResetFontSize">
|
||||
@@ -295,7 +311,7 @@
|
||||
<string>Reset Font Size</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::NoRole</enum>
|
||||
<enum>QAction::MenuRole::NoRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSettings">
|
||||
@@ -306,7 +322,7 @@
|
||||
<string>Settings</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::NoRole</enum>
|
||||
<enum>QAction::MenuRole::NoRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionGameSettings">
|
||||
@@ -317,7 +333,7 @@
|
||||
<string>Game Settings</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::NoRole</enum>
|
||||
<enum>QAction::MenuRole::NoRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionToolsDummy">
|
||||
@@ -330,6 +346,37 @@
|
||||
<string/>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionNavigateBack">
|
||||
<property name="icon">
|
||||
<iconset theme="arrow-left-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Navigate Back</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Navigate Back</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Alt+Left</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::MenuRole::NoRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionNavigateForward">
|
||||
<property name="icon">
|
||||
<iconset theme="arrow-right-line"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Navigate Forward</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Alt+Right</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::MenuRole::NoRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
||||
@@ -76,7 +76,11 @@ bool DisassemblyView::fromJson(const JsonValueWrapper& json)
|
||||
|
||||
auto start_address = json.value().FindMember("startAddress");
|
||||
if (start_address != json.value().MemberEnd() && start_address->value.IsUint())
|
||||
{
|
||||
m_visibleStart = start_address->value.GetUint() & ~3;
|
||||
m_navigation_history.clear();
|
||||
m_navigation_history.pushInstantly(m_visibleStart);
|
||||
}
|
||||
|
||||
auto go_to_pc_on_pause = json.value().FindMember("goToPCOnPause");
|
||||
if (go_to_pc_on_pause != json.value().MemberEnd() && go_to_pc_on_pause->value.IsBool())
|
||||
@@ -388,6 +392,41 @@ QString DisassemblyView::GetLineDisasm(u32 address)
|
||||
return QString("%1 %2").arg(lineInfo.name.c_str()).arg(lineInfo.params.c_str());
|
||||
};
|
||||
|
||||
bool DisassemblyView::supportsNavigation()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DisassemblyView::canNavigateBack()
|
||||
{
|
||||
return m_navigation_history.canGoBack();
|
||||
}
|
||||
|
||||
bool DisassemblyView::canNavigateForward()
|
||||
{
|
||||
return m_navigation_history.canGoForward();
|
||||
}
|
||||
|
||||
void DisassemblyView::navigateBack()
|
||||
{
|
||||
std::optional<u32> address = m_navigation_history.back();
|
||||
if (!address.has_value())
|
||||
return;
|
||||
|
||||
m_visibleStart = *address;
|
||||
update();
|
||||
}
|
||||
|
||||
void DisassemblyView::navigateForward()
|
||||
{
|
||||
std::optional<u32> address = m_navigation_history.forward();
|
||||
if (!address.has_value())
|
||||
return;
|
||||
|
||||
m_visibleStart = *address;
|
||||
update();
|
||||
}
|
||||
|
||||
// Here we go!
|
||||
void DisassemblyView::paintEvent(QPaintEvent* event)
|
||||
{
|
||||
@@ -629,19 +668,21 @@ void DisassemblyView::mouseDoubleClickEvent(QMouseEvent* event)
|
||||
|
||||
void DisassemblyView::wheelEvent(QWheelEvent* event)
|
||||
{
|
||||
if (event->angleDelta().y() < 0) // todo: max address bounds check?
|
||||
{
|
||||
if (event->angleDelta().y() < 0)
|
||||
m_visibleStart += 4;
|
||||
}
|
||||
else if (event->angleDelta().y() && m_visibleStart > 0)
|
||||
{
|
||||
m_visibleStart -= 4;
|
||||
}
|
||||
|
||||
m_navigation_history.pushWithDelay(m_visibleStart);
|
||||
update();
|
||||
}
|
||||
|
||||
void DisassemblyView::keyPressEvent(QKeyEvent* event)
|
||||
{
|
||||
// Alt is used for the global navigation shortcuts.
|
||||
if (event->modifiers() & Qt::AltModifier)
|
||||
return;
|
||||
|
||||
switch (event->key())
|
||||
{
|
||||
case Qt::Key_Up:
|
||||
@@ -653,15 +694,19 @@ void DisassemblyView::keyPressEvent(QKeyEvent* event)
|
||||
// Auto scroll
|
||||
if (m_visibleStart > m_selectedAddressStart)
|
||||
m_visibleStart -= 4;
|
||||
|
||||
m_navigation_history.pushWithDelay(m_visibleStart);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Qt::Key_PageUp:
|
||||
{
|
||||
m_selectedAddressStart -= m_visibleRows * 4;
|
||||
m_selectedAddressEnd = m_selectedAddressStart;
|
||||
m_visibleStart -= m_visibleRows * 4;
|
||||
|
||||
m_navigation_history.pushWithDelay(m_visibleStart);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Qt::Key_Down:
|
||||
{
|
||||
m_selectedAddressEnd += 4;
|
||||
@@ -673,6 +718,7 @@ void DisassemblyView::keyPressEvent(QKeyEvent* event)
|
||||
if (m_visibleStart + ((m_visibleRows - 1) * 4) < m_selectedAddressEnd)
|
||||
m_visibleStart += 4;
|
||||
|
||||
m_navigation_history.pushWithDelay(m_visibleStart);
|
||||
break;
|
||||
}
|
||||
case Qt::Key_PageDown:
|
||||
@@ -680,6 +726,7 @@ void DisassemblyView::keyPressEvent(QKeyEvent* event)
|
||||
m_selectedAddressStart += m_visibleRows * 4;
|
||||
m_selectedAddressEnd = m_selectedAddressStart;
|
||||
m_visibleStart += m_visibleRows * 4;
|
||||
m_navigation_history.pushWithDelay(m_visibleStart);
|
||||
break;
|
||||
}
|
||||
case Qt::Key_G:
|
||||
@@ -1014,6 +1061,8 @@ void DisassemblyView::gotoAddress(u32 address, bool should_set_focus)
|
||||
m_selectedAddressStart = destAddress;
|
||||
m_selectedAddressEnd = destAddress;
|
||||
|
||||
m_navigation_history.pushInstantly(m_visibleStart);
|
||||
|
||||
update();
|
||||
if (should_set_focus)
|
||||
setFocus();
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
|
||||
#include "ui_DisassemblyView.h"
|
||||
|
||||
#include "DebuggerView.h"
|
||||
#include "Debugger/DebuggerView.h"
|
||||
#include "Debugger/NavigationHistoryStack.h"
|
||||
|
||||
#include "pcsx2/DebugTools/DisassemblyManager.h"
|
||||
|
||||
@@ -26,6 +27,12 @@ public:
|
||||
// Required for the breakpoint list (ugh wtf)
|
||||
QString GetLineDisasm(u32 address);
|
||||
|
||||
bool supportsNavigation() override;
|
||||
bool canNavigateBack() override;
|
||||
bool canNavigateForward() override;
|
||||
void navigateBack() override;
|
||||
void navigateForward() override;
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent* event) override;
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
@@ -68,7 +75,7 @@ public slots:
|
||||
private:
|
||||
Ui::DisassemblyView m_ui;
|
||||
|
||||
u32 m_visibleStart = 0x100000; // The address of the first instruction shown.
|
||||
u32 m_visibleStart = STARTING_ADDRESS; // The address of the first instruction shown.
|
||||
u32 m_visibleRows;
|
||||
u32 m_selectedAddressStart = 0;
|
||||
u32 m_selectedAddressEnd = 0;
|
||||
@@ -81,6 +88,8 @@ private:
|
||||
bool m_goToProgramCounterOnPause = true;
|
||||
DisassemblyManager m_disassemblyManager;
|
||||
|
||||
NavigationHistoryStack<u32> m_navigation_history{STARTING_ADDRESS};
|
||||
|
||||
QString GetDisassemblyTitleLine();
|
||||
QColor GetDisassemblyTitleLineColor();
|
||||
inline QString DisassemblyStringFromAddress(u32 address, QFont font, u32 pc, bool selected);
|
||||
@@ -96,4 +105,6 @@ private:
|
||||
void setInstructions(u32 start, u32 end, u32 value);
|
||||
bool AddressCanRestore(u32 start, u32 end);
|
||||
bool FunctionCanRestore(u32 address);
|
||||
|
||||
static constexpr u32 STARTING_ADDRESS = 0x100000;
|
||||
};
|
||||
|
||||
@@ -339,15 +339,28 @@ void DockManager::createToolsMenu(QMenu* menu)
|
||||
if (m_current_layout == DockLayout::INVALID_INDEX || !g_debugger_window)
|
||||
return;
|
||||
|
||||
for (QToolBar* widget : g_debugger_window->findChildren<QToolBar*>())
|
||||
std::vector<QToolBar*> toolbars;
|
||||
for (QToolBar* toolbar : g_debugger_window->findChildren<QToolBar*>())
|
||||
toolbars.emplace_back(toolbar);
|
||||
|
||||
std::sort(toolbars.begin(), toolbars.end(), [](QToolBar* lhs, QToolBar* rhs) {
|
||||
return QtHost::LocaleSensitiveCompare(lhs->windowTitle(), rhs->windowTitle()) < 0;
|
||||
});
|
||||
|
||||
for (QToolBar* toolbar : toolbars)
|
||||
{
|
||||
QAction* action = menu->addAction(widget->windowTitle());
|
||||
action->setText(widget->windowTitle());
|
||||
QAction* action = menu->addAction(toolbar->windowTitle());
|
||||
action->setText(toolbar->windowTitle());
|
||||
action->setCheckable(true);
|
||||
action->setChecked(widget->isVisible());
|
||||
connect(action, &QAction::triggered, this, [widget]() {
|
||||
widget->setVisible(!widget->isVisible());
|
||||
action->setChecked(toolbar->isVisible());
|
||||
|
||||
connect(action, &QAction::triggered, this, [toolbar = QPointer<QToolBar>(toolbar)]() {
|
||||
if (!toolbar)
|
||||
return;
|
||||
|
||||
toolbar->setVisible(!toolbar->isVisible());
|
||||
});
|
||||
|
||||
menu->addAction(action);
|
||||
}
|
||||
}
|
||||
@@ -383,7 +396,7 @@ void DockManager::createWindowsMenu(QMenu* menu)
|
||||
if (lhs->displayNameWithoutSuffix() == rhs->displayNameWithoutSuffix())
|
||||
return lhs->displayNameSuffixNumber() < rhs->displayNameSuffixNumber();
|
||||
|
||||
return lhs->displayNameWithoutSuffix() < rhs->displayNameWithoutSuffix();
|
||||
return QtHost::LocaleSensitiveCompare(lhs->displayNameWithoutSuffix(), rhs->displayNameWithoutSuffix()) < 0;
|
||||
});
|
||||
|
||||
for (DebuggerView* widget : add_another_widgets)
|
||||
@@ -472,7 +485,7 @@ void DockManager::createWindowsMenu(QMenu* menu)
|
||||
if (lhs.display_name == rhs.display_name)
|
||||
return lhs.suffix_number < rhs.suffix_number;
|
||||
|
||||
return lhs.display_name < rhs.display_name;
|
||||
return QtHost::LocaleSensitiveCompare(lhs.display_name, rhs.display_name) < 0;
|
||||
});
|
||||
|
||||
for (const DebuggerViewToggle& toggle : toggles)
|
||||
@@ -862,6 +875,24 @@ std::optional<BreakPointCpu> DockManager::cpu()
|
||||
return m_layouts.at(m_current_layout).cpu();
|
||||
}
|
||||
|
||||
DebuggerView* DockManager::focusedViewForNavigation()
|
||||
{
|
||||
if (!m_focused_view_for_navigation)
|
||||
return nullptr;
|
||||
|
||||
return m_focused_view_for_navigation;
|
||||
}
|
||||
|
||||
void DockManager::onFocusedDockWidgetChanged(KDDockWidgets::QtWidgets::DockWidget* widget)
|
||||
{
|
||||
DebuggerView* view = qobject_cast<DebuggerView*>(widget->widget());
|
||||
if (view && view->supportsNavigation() && view != m_focused_view_for_navigation)
|
||||
{
|
||||
m_focused_view_for_navigation = view;
|
||||
emit focusedViewForNavigationChanged();
|
||||
}
|
||||
}
|
||||
|
||||
KDDockWidgets::Core::DockWidget* DockManager::dockWidgetFactory(const QString& name)
|
||||
{
|
||||
if (!g_debugger_window)
|
||||
|
||||
@@ -98,6 +98,14 @@ public:
|
||||
|
||||
std::optional<BreakPointCpu> cpu();
|
||||
|
||||
/// Returns the last focused view that supports back/forward navigation.
|
||||
DebuggerView* focusedViewForNavigation();
|
||||
|
||||
void onFocusedDockWidgetChanged(KDDockWidgets::QtWidgets::DockWidget* widget);
|
||||
|
||||
Q_SIGNALS:
|
||||
void focusedViewForNavigationChanged();
|
||||
|
||||
private:
|
||||
static KDDockWidgets::Core::DockWidget* dockWidgetFactory(const QString& name);
|
||||
static bool dragAboutToStart(KDDockWidgets::Core::Draggable* draggable);
|
||||
@@ -108,4 +116,6 @@ private:
|
||||
DockMenuBar* m_menu_bar = nullptr;
|
||||
|
||||
bool m_layout_locked = true;
|
||||
|
||||
QPointer<DebuggerView> m_focused_view_for_navigation;
|
||||
};
|
||||
|
||||
@@ -82,6 +82,7 @@ const std::vector<DockTables::DefaultDockLayout> DockTables::DEFAULT_DOCK_LAYOUT
|
||||
{"MemorySearchView", DefaultDockGroup::TOP_LEFT},
|
||||
},
|
||||
.toolbars = {
|
||||
"toolBarNavigation",
|
||||
"toolBarDebug",
|
||||
"toolBarFile",
|
||||
},
|
||||
@@ -113,6 +114,7 @@ const std::vector<DockTables::DefaultDockLayout> DockTables::DEFAULT_DOCK_LAYOUT
|
||||
{"MemorySearchView", DefaultDockGroup::TOP_LEFT},
|
||||
},
|
||||
.toolbars = {
|
||||
"toolBarNavigation",
|
||||
"toolBarDebug",
|
||||
"toolBarFile",
|
||||
},
|
||||
|
||||
@@ -82,6 +82,7 @@ DockWidget::DockWidget(
|
||||
: KDDockWidgets::QtWidgets::DockWidget(unique_name, options, layout_saver_options, window_flags)
|
||||
{
|
||||
connect(this, &DockWidget::isOpenChanged, this, &DockWidget::openStateChanged);
|
||||
connect(this, &DockWidget::isFocusedChanged, this, &DockWidget::focusStateChanged);
|
||||
}
|
||||
|
||||
void DockWidget::openStateChanged(bool open)
|
||||
@@ -95,6 +96,12 @@ void DockWidget::openStateChanged(bool open)
|
||||
g_debugger_window->dockManager().destroyDebuggerView(uniqueName());
|
||||
}
|
||||
|
||||
void DockWidget::focusStateChanged(bool focused)
|
||||
{
|
||||
if (focused && g_debugger_window)
|
||||
g_debugger_window->dockManager().onFocusedDockWidgetChanged(this);
|
||||
}
|
||||
|
||||
// *****************************************************************************
|
||||
|
||||
DockTitleBar::DockTitleBar(KDDockWidgets::Core::TitleBar* controller, KDDockWidgets::Core::View* parent)
|
||||
|
||||
@@ -63,6 +63,7 @@ public:
|
||||
|
||||
protected:
|
||||
void openStateChanged(bool open);
|
||||
void focusStateChanged(bool focused);
|
||||
};
|
||||
|
||||
class DockTitleBar : public KDDockWidgets::QtWidgets::TitleBar
|
||||
|
||||
@@ -10,6 +10,12 @@
|
||||
<height>150</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>600</width>
|
||||
<height>300</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string/>
|
||||
</property>
|
||||
@@ -19,7 +25,7 @@
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="nameLabel">
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
<string>Name:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>nameEditor</cstring>
|
||||
@@ -29,7 +35,7 @@
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="cpuLabel">
|
||||
<property name="text">
|
||||
<string>Target</string>
|
||||
<string>Target:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>cpuEditor</cstring>
|
||||
@@ -39,7 +45,7 @@
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="initialStateLabel">
|
||||
<property name="text">
|
||||
<string>Initial State</string>
|
||||
<string>Initial State:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>initialStateEditor</cstring>
|
||||
|
||||
@@ -19,9 +19,11 @@ using namespace QtUtils;
|
||||
/*
|
||||
MemoryViewTable
|
||||
*/
|
||||
void MemoryViewTable::UpdateStartAddress(u32 start)
|
||||
void MemoryViewTable::UpdateStartAddress(u32 start, NavigationHistoryOperation history_operation)
|
||||
{
|
||||
startAddress = start & ~0xF;
|
||||
navigation_history.push(startAddress, history_operation);
|
||||
parent->update();
|
||||
}
|
||||
|
||||
void MemoryViewTable::UpdateSelectedAddress(u32 selected, bool page)
|
||||
@@ -41,6 +43,13 @@ void MemoryViewTable::UpdateSelectedAddress(u32 selected, bool page)
|
||||
else
|
||||
startAddress += 0x10;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
navigation_history.pushWithDelay(startAddress);
|
||||
parent->update();
|
||||
}
|
||||
|
||||
void MemoryViewTable::DrawTable(QPainter& painter, const QPalette& palette, s32 height, DebugInterface& cpu)
|
||||
@@ -684,7 +693,7 @@ MemoryView::MemoryView(const DebuggerViewParameters& parameters)
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(this, &MemoryView::customContextMenuRequested, this, &MemoryView::openContextMenu);
|
||||
|
||||
m_table.UpdateStartAddress(0x100000);
|
||||
m_table.UpdateStartAddress(0x100000, NavigationHistoryOperation::INSTANT_PUSH);
|
||||
|
||||
receiveEvent<DebuggerEvents::Refresh>([this](const DebuggerEvents::Refresh& event) -> bool {
|
||||
update();
|
||||
@@ -723,7 +732,10 @@ bool MemoryView::fromJson(const JsonValueWrapper& json)
|
||||
|
||||
auto start_address = json.value().FindMember("startAddress");
|
||||
if (start_address != json.value().MemberEnd() && start_address->value.IsUint())
|
||||
m_table.UpdateStartAddress(start_address->value.GetUint());
|
||||
{
|
||||
m_table.navigation_history.clear();
|
||||
m_table.UpdateStartAddress(start_address->value.GetUint(), NavigationHistoryOperation::INSTANT_PUSH);
|
||||
}
|
||||
|
||||
auto view_type = json.value().FindMember("viewType");
|
||||
if (view_type != json.value().MemberEnd() && view_type->value.IsInt())
|
||||
@@ -746,6 +758,40 @@ bool MemoryView::fromJson(const JsonValueWrapper& json)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MemoryView::supportsNavigation()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MemoryView::canNavigateBack()
|
||||
{
|
||||
return m_table.navigation_history.canGoBack();
|
||||
}
|
||||
|
||||
bool MemoryView::canNavigateForward()
|
||||
{
|
||||
return m_table.navigation_history.canGoForward();
|
||||
}
|
||||
|
||||
void MemoryView::navigateBack()
|
||||
{
|
||||
std::optional<u32> address = m_table.navigation_history.back();
|
||||
if (!address.has_value())
|
||||
return;
|
||||
|
||||
m_table.UpdateStartAddress(*address, NavigationHistoryOperation::NO_PUSH);
|
||||
}
|
||||
|
||||
void MemoryView::navigateForward()
|
||||
{
|
||||
std::optional<u32> address = m_table.navigation_history.forward();
|
||||
if (!address.has_value())
|
||||
return;
|
||||
|
||||
m_table.UpdateStartAddress(*address, NavigationHistoryOperation::NO_PUSH);
|
||||
}
|
||||
|
||||
|
||||
void MemoryView::paintEvent(QPaintEvent* event)
|
||||
{
|
||||
QPainter painter(this);
|
||||
@@ -928,17 +974,20 @@ void MemoryView::wheelEvent(QWheelEvent* event)
|
||||
{
|
||||
if (event->angleDelta().y() < 0)
|
||||
{
|
||||
m_table.UpdateStartAddress(m_table.startAddress + 0x10);
|
||||
m_table.UpdateStartAddress(m_table.startAddress + 0x10, NavigationHistoryOperation::DELAYED_PUSH);
|
||||
}
|
||||
else if (event->angleDelta().y() > 0)
|
||||
{
|
||||
m_table.UpdateStartAddress(m_table.startAddress - 0x10);
|
||||
m_table.UpdateStartAddress(m_table.startAddress - 0x10, NavigationHistoryOperation::DELAYED_PUSH);
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
void MemoryView::keyPressEvent(QKeyEvent* event)
|
||||
{
|
||||
// Alt is used for the global navigation shortcuts.
|
||||
if (event->modifiers() & Qt::AltModifier)
|
||||
return;
|
||||
|
||||
if (!m_table.KeyPress(event->key(), event->text().size() ? event->text()[0] : '\0', cpu()))
|
||||
{
|
||||
switch (event->key())
|
||||
@@ -960,9 +1009,8 @@ void MemoryView::keyPressEvent(QKeyEvent* event)
|
||||
|
||||
void MemoryView::gotoAddress(u32 address)
|
||||
{
|
||||
m_table.UpdateStartAddress(address & ~0xF);
|
||||
m_table.UpdateStartAddress(address & ~0xF, NavigationHistoryOperation::INSTANT_PUSH);
|
||||
m_table.selectedAddress = address;
|
||||
update();
|
||||
setFocus();
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "ui_MemoryView.h"
|
||||
|
||||
#include "Debugger/DebuggerView.h"
|
||||
#include "Debugger/NavigationHistoryStack.h"
|
||||
|
||||
#include "DebugTools/DebugInterface.h"
|
||||
#include "DebugTools/DisassemblyManager.h"
|
||||
@@ -50,15 +51,15 @@ private:
|
||||
QWidget* parent;
|
||||
MemoryViewType displayType = MemoryViewType::BYTE;
|
||||
bool littleEndian = true;
|
||||
u32 rowCount;
|
||||
u32 rowVisible;
|
||||
s32 rowHeight;
|
||||
u32 rowCount = 0;
|
||||
u32 rowVisible = 0;
|
||||
s32 rowHeight = 0;
|
||||
|
||||
// Stuff used for selection handling
|
||||
// This gets set every paint and depends on the window size / current display mode (1byte,2byte,etc)
|
||||
s32 valuexAxis; // Where the hexadecimal view begins
|
||||
s32 textXAxis; // Where the text view begins
|
||||
s32 row1YAxis; // Where the first row starts
|
||||
s32 valuexAxis = 0; // Where the hexadecimal view begins
|
||||
s32 textXAxis = 0; // Where the text view begins
|
||||
s32 row1YAxis = 0; // Where the first row starts
|
||||
s32 segmentXAxis[16]; // Where the segments begin
|
||||
bool selectedText = false; // Whether the user has clicked on text or hex
|
||||
|
||||
@@ -89,11 +90,13 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
u32 startAddress;
|
||||
u32 selectedAddress;
|
||||
s32 selectedIndex;
|
||||
u32 startAddress = 0;
|
||||
u32 selectedAddress = 0;
|
||||
s32 selectedIndex = 0;
|
||||
|
||||
void UpdateStartAddress(u32 start);
|
||||
NavigationHistoryStack<u32> navigation_history;
|
||||
|
||||
void UpdateStartAddress(u32 start, NavigationHistoryOperation history_operation);
|
||||
void UpdateSelectedAddress(u32 selected, bool page = false);
|
||||
void DrawTable(QPainter& painter, const QPalette& palette, s32 height, DebugInterface& cpu);
|
||||
void SelectAt(QPoint pos);
|
||||
@@ -136,6 +139,12 @@ public:
|
||||
void toJson(JsonValueWrapper& json) override;
|
||||
bool fromJson(const JsonValueWrapper& json) override;
|
||||
|
||||
bool supportsNavigation() override;
|
||||
bool canNavigateBack() override;
|
||||
bool canNavigateForward() override;
|
||||
void navigateBack() override;
|
||||
void navigateForward() override;
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent* event) override;
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Debugger/DebuggerWindow.h"
|
||||
|
||||
#include <QtCore/QTimer>
|
||||
|
||||
#include <deque>
|
||||
|
||||
enum class NavigationHistoryOperation
|
||||
{
|
||||
INSTANT_PUSH,
|
||||
DELAYED_PUSH,
|
||||
NO_PUSH
|
||||
};
|
||||
|
||||
/// Data structure for storing navigation history, to be used for the back and
|
||||
/// forward buttons.
|
||||
template <typename Element>
|
||||
class NavigationHistoryStack
|
||||
{
|
||||
public:
|
||||
NavigationHistoryStack()
|
||||
: m_position(m_elements.end())
|
||||
{
|
||||
m_timer.setInterval(DELAY_MILLISECONDS);
|
||||
|
||||
QObject::connect(&m_timer, &QTimer::timeout, [this]() {
|
||||
pushInstantly(std::move(m_pending));
|
||||
m_timer.stop();
|
||||
});
|
||||
}
|
||||
|
||||
NavigationHistoryStack(Element element)
|
||||
: NavigationHistoryStack()
|
||||
{
|
||||
pushInstantly(std::move(element));
|
||||
}
|
||||
|
||||
NavigationHistoryStack(const NavigationHistoryStack<Element>&) = delete;
|
||||
NavigationHistoryStack<Element>& operator=(const NavigationHistoryStack<Element>&) = delete;
|
||||
|
||||
NavigationHistoryStack(NavigationHistoryStack<Element>&&) = delete;
|
||||
NavigationHistoryStack<Element>& operator=(NavigationHistoryStack<Element>&&) = delete;
|
||||
|
||||
/// Push an element onto the stack.
|
||||
void push(Element element, NavigationHistoryOperation operation)
|
||||
{
|
||||
switch (operation)
|
||||
{
|
||||
case NavigationHistoryOperation::INSTANT_PUSH:
|
||||
pushInstantly(std::move(element));
|
||||
break;
|
||||
case NavigationHistoryOperation::DELAYED_PUSH:
|
||||
pushWithDelay(std::move(element));
|
||||
break;
|
||||
case NavigationHistoryOperation::NO_PUSH:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// Wait a second, and then push an element onto the stack. If another
|
||||
/// element is pushed, then that will take priority. This is useful if we're
|
||||
/// storing a scroll position, and don't want the stack to be spammed with
|
||||
/// new values when the user scrolls, for example.
|
||||
void pushWithDelay(Element element)
|
||||
{
|
||||
m_pending = std::move(element);
|
||||
m_timer.start();
|
||||
}
|
||||
|
||||
/// Push an element onto the stack instantly.
|
||||
void pushInstantly(Element element)
|
||||
{
|
||||
m_timer.stop();
|
||||
|
||||
if (m_position != m_elements.end() && element == *(m_position - 1))
|
||||
return;
|
||||
|
||||
updateNavigationButtons();
|
||||
|
||||
m_elements.erase(m_position, m_elements.end());
|
||||
m_elements.push_back(std::move(element));
|
||||
|
||||
if (m_elements.size() > MAX_ELEMENTS)
|
||||
m_elements.pop_front();
|
||||
|
||||
m_position = m_elements.end();
|
||||
}
|
||||
|
||||
/// Should to user have an option to go back?
|
||||
bool canGoBack() const
|
||||
{
|
||||
return m_elements.begin() != m_elements.end() && m_position > m_elements.begin() + 1;
|
||||
}
|
||||
|
||||
/// Should the user have an option to go forward?
|
||||
bool canGoForward() const
|
||||
{
|
||||
return m_position != m_elements.end();
|
||||
}
|
||||
|
||||
// Retrieve the current element.
|
||||
std::optional<Element> current() const
|
||||
{
|
||||
if (m_position == m_elements.begin())
|
||||
return std::nullopt;
|
||||
|
||||
return *(m_position - 1);
|
||||
}
|
||||
|
||||
/// Go back one.
|
||||
std::optional<Element> back()
|
||||
{
|
||||
updateNavigationButtons();
|
||||
|
||||
if (!canGoBack())
|
||||
return std::nullopt;
|
||||
|
||||
m_position--;
|
||||
return *(m_position - 1);
|
||||
}
|
||||
|
||||
/// Go forward one.
|
||||
std::optional<Element> forward()
|
||||
{
|
||||
updateNavigationButtons();
|
||||
|
||||
if (!canGoForward())
|
||||
return std::nullopt;
|
||||
|
||||
Element element = *m_position;
|
||||
m_position++;
|
||||
return element;
|
||||
}
|
||||
|
||||
/// Remove all elements.
|
||||
void clear()
|
||||
{
|
||||
m_elements.clear();
|
||||
m_position = m_elements.end();
|
||||
m_timer.stop();
|
||||
}
|
||||
|
||||
private:
|
||||
void updateNavigationButtons()
|
||||
{
|
||||
QTimer::singleShot(0, []() {
|
||||
if (g_debugger_window)
|
||||
g_debugger_window->updateNavigationButtons();
|
||||
});
|
||||
}
|
||||
|
||||
std::deque<Element> m_elements;
|
||||
std::deque<Element>::iterator m_position;
|
||||
|
||||
QTimer m_timer;
|
||||
Element m_pending;
|
||||
|
||||
static constexpr int DELAY_MILLISECONDS = 1000;
|
||||
static constexpr size_t MAX_ELEMENTS = 1000;
|
||||
};
|
||||
@@ -7,6 +7,8 @@
|
||||
#include "Debugger/SymbolTree/SymbolTreeModel.h"
|
||||
#include "Debugger/SymbolTree/TypeString.h"
|
||||
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
#include <QtWidgets/QCheckBox>
|
||||
#include <QtWidgets/QComboBox>
|
||||
#include <QtWidgets/QDoubleSpinBox>
|
||||
@@ -44,12 +46,28 @@ QWidget* SymbolTreeValueDelegate::createEditor(QWidget* parent, const QStyleOpti
|
||||
const ccc::ast::Node& physical_type = *logical_type->physical_type(database).first;
|
||||
QVariant value = node->readValueAsVariant(physical_type, m_cpu, database);
|
||||
|
||||
const ccc::ast::Node& type = *logical_type->physical_type(database).first;
|
||||
switch (type.descriptor)
|
||||
switch (physical_type.descriptor)
|
||||
{
|
||||
case ccc::ast::ARRAY:
|
||||
{
|
||||
const ccc::ast::Array& array = physical_type.as<ccc::ast::Array>();
|
||||
const ccc::ast::Node& element_type = *array.element_type->physical_type(database).first;;
|
||||
|
||||
if (array.element_count > 0 &&
|
||||
element_type.name == "char" &&
|
||||
node->location.type == SymbolTreeLocation::MEMORY &&
|
||||
!value.isNull())
|
||||
{
|
||||
QLineEdit* editor = new QLineEdit(parent);
|
||||
editor->setText(value.toString());
|
||||
result = editor;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ccc::ast::BUILTIN:
|
||||
{
|
||||
const ccc::ast::BuiltIn& builtin = type.as<ccc::ast::BuiltIn>();
|
||||
const ccc::ast::BuiltIn& builtin = physical_type.as<ccc::ast::BuiltIn>();
|
||||
|
||||
switch (builtin.bclass)
|
||||
{
|
||||
@@ -111,7 +129,7 @@ QWidget* SymbolTreeValueDelegate::createEditor(QWidget* parent, const QStyleOpti
|
||||
}
|
||||
case ccc::ast::ENUM:
|
||||
{
|
||||
const ccc::ast::Enum& enumeration = type.as<ccc::ast::Enum>();
|
||||
const ccc::ast::Enum& enumeration = physical_type.as<ccc::ast::Enum>();
|
||||
|
||||
QComboBox* combo_box = new QComboBox(parent);
|
||||
bool named = false;
|
||||
@@ -186,12 +204,30 @@ void SymbolTreeValueDelegate::setModelData(QWidget* editor, QAbstractItemModel*
|
||||
if (!logical_type)
|
||||
return;
|
||||
|
||||
const ccc::ast::Node& type = *logical_type->physical_type(database).first;
|
||||
switch (type.descriptor)
|
||||
const ccc::ast::Node& physical_type = *logical_type->physical_type(database).first;
|
||||
|
||||
switch (physical_type.descriptor)
|
||||
{
|
||||
case ccc::ast::ARRAY:
|
||||
{
|
||||
const ccc::ast::Array& array = physical_type.as<ccc::ast::Array>();
|
||||
const ccc::ast::Node& element_type = *array.element_type->physical_type(database).first;
|
||||
|
||||
QLineEdit* line_edit = qobject_cast<QLineEdit*>(editor);
|
||||
|
||||
if (array.element_count > 0 &&
|
||||
element_type.name == "char" &&
|
||||
node->location.type == SymbolTreeLocation::MEMORY &&
|
||||
line_edit)
|
||||
{
|
||||
value = line_edit->text();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ccc::ast::BUILTIN:
|
||||
{
|
||||
const ccc::ast::BuiltIn& builtin = type.as<ccc::ast::BuiltIn>();
|
||||
const ccc::ast::BuiltIn& builtin = physical_type.as<ccc::ast::BuiltIn>();
|
||||
|
||||
switch (builtin.bclass)
|
||||
{
|
||||
|
||||
@@ -67,7 +67,7 @@ bool SymbolTreeNode::writeToVM(
|
||||
if (logical_type)
|
||||
{
|
||||
const ccc::ast::Node& physical_type = *logical_type->physical_type(database).first;
|
||||
writeValueFromVariant(m_value, physical_type, cpu);
|
||||
writeValueFromVariant(m_value, physical_type, cpu, database);
|
||||
}
|
||||
|
||||
data_changed |= updateDisplayString(cpu, database, display_options);
|
||||
@@ -76,10 +76,29 @@ bool SymbolTreeNode::writeToVM(
|
||||
return data_changed;
|
||||
}
|
||||
|
||||
QVariant SymbolTreeNode::readValueAsVariant(const ccc::ast::Node& physical_type, DebugInterface& cpu, const ccc::SymbolDatabase& database) const
|
||||
QVariant SymbolTreeNode::readValueAsVariant(
|
||||
const ccc::ast::Node& physical_type,
|
||||
DebugInterface& cpu,
|
||||
const ccc::SymbolDatabase& database) const
|
||||
{
|
||||
switch (physical_type.descriptor)
|
||||
{
|
||||
case ccc::ast::ARRAY:
|
||||
{
|
||||
const ccc::ast::Array& array = physical_type.as<ccc::ast::Array>();
|
||||
const ccc::ast::Node& element_type = *array.element_type->physical_type(database).first;
|
||||
|
||||
if (array.element_count > 0 &&
|
||||
element_type.name == "char" &&
|
||||
location.type == SymbolTreeLocation::MEMORY)
|
||||
{
|
||||
std::optional<std::string> string = cpu.ReadString(location.address, 256);
|
||||
if (string.has_value())
|
||||
return QString::fromStdString(*string);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ccc::ast::BUILTIN:
|
||||
{
|
||||
const ccc::ast::BuiltIn& builtIn = physical_type.as<ccc::ast::BuiltIn>();
|
||||
@@ -134,10 +153,36 @@ QVariant SymbolTreeNode::readValueAsVariant(const ccc::ast::Node& physical_type,
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
bool SymbolTreeNode::writeValueFromVariant(QVariant value, const ccc::ast::Node& physical_type, DebugInterface& cpu) const
|
||||
bool SymbolTreeNode::writeValueFromVariant(
|
||||
QVariant value,
|
||||
const ccc::ast::Node& physical_type,
|
||||
DebugInterface& cpu,
|
||||
const ccc::SymbolDatabase& database) const
|
||||
{
|
||||
if (value.isNull())
|
||||
return false;
|
||||
|
||||
switch (physical_type.descriptor)
|
||||
{
|
||||
case ccc::ast::ARRAY:
|
||||
{
|
||||
const ccc::ast::Array& array = physical_type.as<ccc::ast::Array>();
|
||||
const ccc::ast::Node& element_type = *array.element_type->physical_type(database).first;
|
||||
|
||||
if (array.element_count > 0 &&
|
||||
element_type.name == "char" &&
|
||||
location.type == SymbolTreeLocation::MEMORY)
|
||||
{
|
||||
QByteArray byte_array = value.toString().toLatin1();
|
||||
std::string_view view(
|
||||
byte_array.data(),
|
||||
std::min(static_cast<s32>(byte_array.size()), array.element_count - 1));
|
||||
cpu.WriteString(location.address, view);
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
case ccc::ast::BUILTIN:
|
||||
{
|
||||
const ccc::ast::BuiltIn& built_in = physical_type.as<ccc::ast::BuiltIn>();
|
||||
@@ -191,6 +236,7 @@ bool SymbolTreeNode::writeValueFromVariant(QVariant value, const ccc::ast::Node&
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ccc::ast::ENUM:
|
||||
@@ -266,11 +312,14 @@ QString SymbolTreeNode::generateDisplayString(
|
||||
const ccc::ast::Array& array = physical_type.as<ccc::ast::Array>();
|
||||
const ccc::ast::Node& element_type = *array.element_type->physical_type(database).first;
|
||||
|
||||
if (element_type.name == "char" && location.type == SymbolTreeLocation::MEMORY)
|
||||
if (array.element_count > 0 &&
|
||||
element_type.name == "char" &&
|
||||
location.type == SymbolTreeLocation::MEMORY)
|
||||
{
|
||||
char* string = cpu.stringFromPointer(location.address);
|
||||
if (string)
|
||||
return QString("\"%1\"").arg(string);
|
||||
std::optional<std::string> string = cpu.ReadString(
|
||||
location.address, 256, MemoryInterface::ALLOW_LONG_STRINGS);
|
||||
if (string.has_value())
|
||||
return QString("\"%1\"").arg(*string);
|
||||
}
|
||||
|
||||
QString result;
|
||||
@@ -412,9 +461,10 @@ QString SymbolTreeNode::generateDisplayString(
|
||||
|
||||
if (pointer_or_reference.is_pointer && value_type.name == "char")
|
||||
{
|
||||
const char* string = cpu.stringFromPointer(address);
|
||||
if (string)
|
||||
result += QString(" \"%1\"").arg(string);
|
||||
std::optional<std::string> string = cpu.ReadString(
|
||||
address, 256, MemoryInterface::ALLOW_LONG_STRINGS);
|
||||
if (string.has_value())
|
||||
result += QString(" \"%1\"").arg(*string);
|
||||
}
|
||||
else if (depth == 0)
|
||||
{
|
||||
|
||||
@@ -62,11 +62,20 @@ public:
|
||||
const ccc::SymbolDatabase& database,
|
||||
const SymbolTreeDisplayOptions& display_options);
|
||||
|
||||
QVariant readValueAsVariant(const ccc::ast::Node& physical_type, DebugInterface& cpu, const ccc::SymbolDatabase& database) const;
|
||||
bool writeValueFromVariant(QVariant value, const ccc::ast::Node& physical_type, DebugInterface& cpu) const;
|
||||
QVariant readValueAsVariant(
|
||||
const ccc::ast::Node& physical_type,
|
||||
DebugInterface& cpu,
|
||||
const ccc::SymbolDatabase& database) const;
|
||||
|
||||
bool writeValueFromVariant(
|
||||
QVariant value,
|
||||
const ccc::ast::Node& physical_type,
|
||||
DebugInterface& cpu,
|
||||
const ccc::SymbolDatabase& database) const;
|
||||
|
||||
bool updateDisplayString(
|
||||
DebugInterface& cpu, const ccc::SymbolDatabase& database, const SymbolTreeDisplayOptions& display);
|
||||
|
||||
QString generateDisplayString(
|
||||
const ccc::ast::Node& physical_type,
|
||||
DebugInterface& cpu,
|
||||
|
||||
@@ -73,9 +73,10 @@ const char* GameListModel::getColumnName(const Column col)
|
||||
return s_column_names[static_cast<int>(col)];
|
||||
}
|
||||
|
||||
GameListModel::GameListModel(const float cover_scale, const bool show_cover_titles, const qreal dpr, QObject* parent /* = nullptr */)
|
||||
GameListModel::GameListModel(const float cover_scale, const bool show_cover_titles, bool show_full_cover_titles, const qreal dpr, QObject* parent /* = nullptr */)
|
||||
: QAbstractTableModel(parent)
|
||||
, m_show_titles_for_covers(show_cover_titles)
|
||||
, m_show_full_titles_for_covers(show_full_cover_titles)
|
||||
, m_dpr{dpr}
|
||||
{
|
||||
loadSettings();
|
||||
@@ -315,9 +316,33 @@ QVariant GameListModel::data(const QModelIndex& index, const int role) const
|
||||
switch (index.column())
|
||||
{
|
||||
case Column_Cover:
|
||||
return QSize(static_cast<int>(static_cast<float>(SIZE_HINT_WIDTH) * m_cover_scale),
|
||||
static_cast<int>(static_cast<float>(m_show_titles_for_covers ? SIZE_HINT_HEIGHT_TITLES : SIZE_HINT_HEIGHT) * m_cover_scale));
|
||||
{
|
||||
const int cover_width = static_cast<int>(static_cast<float>(SIZE_HINT_WIDTH) * m_cover_scale);
|
||||
const int cover_height = static_cast<int>(static_cast<float>(SIZE_HINT_HEIGHT) * m_cover_scale);
|
||||
|
||||
if (m_show_full_titles_for_covers && m_show_titles_for_covers)
|
||||
{
|
||||
// Find height needed for the longest title at the current font/width
|
||||
const int text_width = static_cast<int>(static_cast<float>(COVER_ART_WIDTH) * m_cover_scale);
|
||||
QFont font;
|
||||
font.setPointSizeF(20.0f * m_cover_scale);
|
||||
const QFontMetrics fm(font);
|
||||
int max_text_height = fm.height();
|
||||
const auto lock = GameList::GetLock();
|
||||
const u32 count = GameList::GetEntryCount();
|
||||
for (u32 i = 0; i < count; i++)
|
||||
{
|
||||
const GameList::Entry* entry = GameList::GetEntryByIndex(i);
|
||||
if (!entry) continue;
|
||||
const QString title = QString::fromStdString(entry->GetTitle(m_prefer_english_titles));
|
||||
const QRect bound = fm.boundingRect(QRect(0, 0, text_width, 0),
|
||||
Qt::TextWordWrap | Qt::AlignHCenter, title);
|
||||
max_text_height = std::max(max_text_height, bound.height());
|
||||
}
|
||||
return QSize(cover_width, cover_height + max_text_height);
|
||||
}
|
||||
return QSize(cover_width, static_cast<int>(static_cast<float>(m_show_titles_for_covers ? SIZE_HINT_HEIGHT_TITLES : SIZE_HINT_HEIGHT) * m_cover_scale));
|
||||
}
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
@@ -354,7 +379,7 @@ bool GameListModel::titlesLessThan(const int left_row, const int right_row) cons
|
||||
const GameList::Entry* left = GameList::GetEntryByIndex(left_row);
|
||||
const GameList::Entry* right = GameList::GetEntryByIndex(right_row);
|
||||
return QtHost::LocaleSensitiveCompare(QString::fromStdString(left->GetTitleSort(m_prefer_english_titles)),
|
||||
QString::fromStdString(right->GetTitleSort(m_prefer_english_titles))) < 0;
|
||||
QString::fromStdString(right->GetTitleSort(m_prefer_english_titles))) < 0;
|
||||
}
|
||||
|
||||
bool GameListModel::lessThan(const QModelIndex& left_index, const QModelIndex& right_index, const int column) const
|
||||
|
||||
@@ -43,7 +43,7 @@ public:
|
||||
static QIcon getIconForType(GameList::EntryType type);
|
||||
static QIcon getIconForRegion(GameList::Region region);
|
||||
|
||||
GameListModel(float cover_scale, bool show_cover_titles, qreal dpr, QObject* parent = nullptr);
|
||||
GameListModel(float cover_scale, bool show_cover_titles, bool show_full_cover_titles, qreal dpr, QObject* parent = nullptr);
|
||||
~GameListModel();
|
||||
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
@@ -63,6 +63,9 @@ public:
|
||||
bool getShowCoverTitles() const { return m_show_titles_for_covers; }
|
||||
void setShowCoverTitles(bool enabled) { m_show_titles_for_covers = enabled; }
|
||||
|
||||
bool getShowFullCoverTitles() const { return m_show_full_titles_for_covers; }
|
||||
void setShowFullCoverTitles(bool enabled) { m_show_full_titles_for_covers = enabled; }
|
||||
|
||||
float getCoverScale() const { return m_cover_scale; }
|
||||
void setCoverScale(float scale);
|
||||
int getCoverArtWidth() const;
|
||||
@@ -89,6 +92,7 @@ private:
|
||||
float m_cover_scale = 0.0f;
|
||||
std::atomic<u32> m_cover_scale_counter{0};
|
||||
bool m_show_titles_for_covers = false;
|
||||
bool m_show_full_titles_for_covers = false;
|
||||
bool m_prefer_english_titles = false;
|
||||
|
||||
std::array<QString, Column_Count> m_column_display_names;
|
||||
|
||||
@@ -22,7 +22,10 @@
|
||||
#include <QtCore/QSortFilterProxyModel>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QString>
|
||||
#include <QtGui/QColor>
|
||||
#include <QtGui/QImage>
|
||||
#include <QtGui/QPainter>
|
||||
#include <QtGui/QPalette>
|
||||
#include <QtGui/QPixmap>
|
||||
#include <QtGui/QPixmapCache>
|
||||
#include <QtGui/QWheelEvent>
|
||||
@@ -191,6 +194,29 @@ namespace
|
||||
|
||||
painter->drawPixmap(rect.topLeft() + icon_top_left, highlighted_icon);
|
||||
}
|
||||
// Recolor the icon based on the custom background color
|
||||
else if (index.column() == GameListModel::Column_Type)
|
||||
{
|
||||
// Fetch pixmap from cache or construct a new one.
|
||||
const QColor color = option.palette.color(QPalette::Text);
|
||||
const QString key = QString::fromStdString(fmt::format("type-{:016X}-{:08X}", icon.cacheKey(), color.rgba()));
|
||||
|
||||
QPixmap tinted_icon;
|
||||
if (!QPixmapCache::find(key, &tinted_icon))
|
||||
{
|
||||
QImage img = icon.toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied);
|
||||
|
||||
QPainter tinted_painter(&img);
|
||||
tinted_painter.setCompositionMode(QPainter::CompositionMode_SourceAtop);
|
||||
tinted_painter.fillRect(0, 0, img.width(), img.height(), color);
|
||||
tinted_painter.end();
|
||||
|
||||
tinted_icon = QPixmap(QPixmap::fromImage(img));
|
||||
QPixmapCache::insert(key, tinted_icon);
|
||||
}
|
||||
|
||||
painter->drawPixmap(rect.topLeft() + icon_top_left, tinted_icon);
|
||||
}
|
||||
else
|
||||
{
|
||||
painter->drawPixmap(rect.topLeft() + icon_top_left, icon);
|
||||
@@ -213,7 +239,8 @@ void GameListWidget::initialize()
|
||||
{
|
||||
const float cover_scale = Host::GetBaseFloatSettingValue("UI", "GameListCoverArtScale", 0.45f);
|
||||
const bool show_cover_titles = Host::GetBaseBoolSettingValue("UI", "GameListShowCoverTitles", true);
|
||||
m_model = new GameListModel(cover_scale, show_cover_titles, devicePixelRatioF(), this);
|
||||
const bool show_full_cover_titles = Host::GetBaseBoolSettingValue("UI", "GameListShowFullCoverTitles", true);
|
||||
m_model = new GameListModel(cover_scale, show_cover_titles, show_full_cover_titles, devicePixelRatioF(), this);
|
||||
m_model->updateCacheSize(width(), height());
|
||||
|
||||
m_sort_model = new GameListSortModel(m_model);
|
||||
@@ -242,6 +269,7 @@ void GameListWidget::initialize()
|
||||
connect(m_ui.viewGameGrid, &QPushButton::clicked, this, &GameListWidget::showGameGrid);
|
||||
connect(m_ui.gridScale, &QSlider::valueChanged, this, &GameListWidget::gridIntScale);
|
||||
connect(m_ui.viewGridTitles, &QPushButton::toggled, this, &GameListWidget::setShowCoverTitles);
|
||||
connect(m_ui.viewFullGridTitles, &QPushButton::toggled, this, &GameListWidget::setShowFullCoverTitles);
|
||||
connect(m_ui.filterType, &QComboBox::currentIndexChanged, this, [this](int index) {
|
||||
m_sort_model->setFilterType((index == 0) ? GameList::EntryType::Count : static_cast<GameList::EntryType>(index - 1));
|
||||
});
|
||||
@@ -322,6 +350,7 @@ void GameListWidget::initialize()
|
||||
m_list_view->setFrameStyle(QFrame::NoFrame);
|
||||
m_list_view->setVerticalScrollMode(QAbstractItemView::ScrollMode::ScrollPerPixel);
|
||||
m_list_view->verticalScrollBar()->setSingleStep(15);
|
||||
m_list_view->setWordWrap(show_full_cover_titles);
|
||||
onCoverScaleChanged();
|
||||
|
||||
connect(m_list_view->selectionModel(), &QItemSelectionModel::currentChanged, this,
|
||||
@@ -381,8 +410,14 @@ void GameListWidget::setCustomBackground()
|
||||
delete m_background_movie;
|
||||
m_background_movie = nullptr;
|
||||
}
|
||||
// Cache all frames for small images so loops don't keep re-decoding
|
||||
else if (const s64 file_size = FileSystem::GetPathFileSize(path.c_str()); file_size > 0 && file_size < 64 * 1024 * 1024)
|
||||
m_background_movie->setCacheMode(QMovie::CacheAll);
|
||||
}
|
||||
|
||||
// Invalidate frame cache so the next animated frame triggers full reprocessing
|
||||
m_background_last_size = QSize();
|
||||
|
||||
// If there is no valid background then reset fallback to default UI state
|
||||
if (!m_background_movie)
|
||||
{
|
||||
@@ -391,6 +426,11 @@ void GameListWidget::setCustomBackground()
|
||||
m_table_view->viewport()->setAutoFillBackground(true);
|
||||
m_list_view->viewport()->setAutoFillBackground(true);
|
||||
|
||||
m_ui.stack->setPalette(QPalette());
|
||||
m_background_text_color = QColor();
|
||||
m_empty_widget->setPalette(QPalette());
|
||||
m_empty_widget->setAutoFillBackground(false);
|
||||
|
||||
m_ui.stack->update();
|
||||
m_table_view->setAlternatingRowColors(true);
|
||||
return;
|
||||
@@ -415,45 +455,79 @@ void GameListWidget::setCustomBackground()
|
||||
m_background_opacity = Host::GetBaseFloatSettingValue("UI", "GameListBackgroundOpacity", 100.0f);
|
||||
|
||||
// Selected Custom background is valid, connect the signals and start animation in gamelist
|
||||
connect(m_background_movie, &QMovie::frameChanged, this, &GameListWidget::processBackgroundFrames, Qt::UniqueConnection);
|
||||
connect(m_background_movie, &QMovie::frameChanged, this, &GameListWidget::processBackgroundFrames);
|
||||
m_ui.stack->setAutoFillBackground(false);
|
||||
|
||||
m_table_view->viewport()->setAutoFillBackground(false);
|
||||
m_list_view->viewport()->setAutoFillBackground(false);
|
||||
updateCustomBackgroundState(true);
|
||||
m_background_movie->start();
|
||||
updateCustomBackgroundState();
|
||||
m_table_view->setAlternatingRowColors(false);
|
||||
processBackgroundFrames();
|
||||
}
|
||||
|
||||
void GameListWidget::updateCustomBackgroundState(const bool force_start)
|
||||
void GameListWidget::updateCustomBackgroundState()
|
||||
{
|
||||
if (m_background_movie && m_background_movie->isValid())
|
||||
{
|
||||
if ((isVisible() && (isActiveWindow() || force_start)) && qGuiApp->applicationState() == Qt::ApplicationActive)
|
||||
m_background_movie->setPaused(false);
|
||||
else
|
||||
m_background_movie->setPaused(true);
|
||||
}
|
||||
m_background_movie->setPaused(!(isVisible() && qGuiApp->applicationState() == Qt::ApplicationActive));
|
||||
}
|
||||
|
||||
void GameListWidget::processBackgroundFrames()
|
||||
{
|
||||
if (m_background_movie && m_background_movie->isValid() && isVisible())
|
||||
{
|
||||
const int widget_width = m_ui.stack->width();
|
||||
const int widget_height = m_ui.stack->height();
|
||||
if (!m_background_movie || !m_background_movie->isValid() || !isVisible())
|
||||
return;
|
||||
|
||||
if (widget_width <= 0 || widget_height <= 0)
|
||||
return;
|
||||
const QSize widget_size(m_ui.stack->width(), m_ui.stack->height());
|
||||
if (widget_size.isEmpty())
|
||||
return;
|
||||
|
||||
QPixmap pm = m_background_movie->currentPixmap();
|
||||
const qreal dpr = devicePixelRatioF();
|
||||
const int frame_number = m_background_movie->currentFrameNumber();
|
||||
const qreal dpr = devicePixelRatioF();
|
||||
|
||||
QtUtils::resizeAndScalePixmap(&pm, widget_width, widget_height, dpr, m_background_scaling, m_background_opacity);
|
||||
if (frame_number == m_background_last_frame && widget_size == m_background_last_size && qFuzzyCompare(dpr, m_background_last_dpr))
|
||||
return;
|
||||
|
||||
m_background_pixmap = std::move(pm);
|
||||
m_ui.stack->update();
|
||||
}
|
||||
QPixmap pm = m_background_movie->currentPixmap();
|
||||
updateBackgroundTextColor(pm);
|
||||
QtUtils::resizeAndScalePixmap(&pm, widget_size.width(), widget_size.height(), dpr, m_background_scaling, m_background_opacity);
|
||||
|
||||
m_background_pixmap = std::move(pm);
|
||||
m_background_last_frame = frame_number;
|
||||
m_background_last_size = widget_size;
|
||||
m_background_last_dpr = dpr;
|
||||
m_ui.stack->update();
|
||||
}
|
||||
|
||||
void GameListWidget::updateBackgroundTextColor(const QPixmap& frame)
|
||||
{
|
||||
if (frame.isNull())
|
||||
return;
|
||||
|
||||
const QImage sampled = frame.scaled(32, 32, Qt::IgnoreAspectRatio, Qt::FastTransformation).toImage();
|
||||
const QColor average = sampled.scaled(1, 1, Qt::IgnoreAspectRatio, Qt::SmoothTransformation).pixelColor(0, 0);
|
||||
const QColor base = qApp->palette().color(QPalette::Base);
|
||||
const qreal coverage = average.alphaF() * std::clamp(m_background_opacity / 100.0f, 0.0f, 1.0f);
|
||||
const qreal brightness = qGray(average.rgb()) * coverage + qGray(base.rgb()) * (1.0 - coverage);
|
||||
const QColor text_color = (brightness > 127.5) ? Qt::black : Qt::white;
|
||||
|
||||
if (m_background_text_color == text_color)
|
||||
return;
|
||||
m_background_text_color = text_color;
|
||||
|
||||
QColor highlight_color = qApp->palette().color(QPalette::Highlight);
|
||||
highlight_color.setAlpha(128);
|
||||
const QColor empty_backdrop_color = (text_color == Qt::black) ? QColor(255, 255, 255, 128) : QColor(0, 0, 0, 128);
|
||||
|
||||
QPalette palette;
|
||||
palette.setColor(QPalette::Text, text_color);
|
||||
palette.setColor(QPalette::WindowText, text_color);
|
||||
palette.setColor(QPalette::Highlight, highlight_color);
|
||||
m_ui.stack->setPalette(palette);
|
||||
|
||||
QPalette empty_palette;
|
||||
empty_palette.setColor(QPalette::Window, empty_backdrop_color);
|
||||
m_empty_widget->setPalette(empty_palette);
|
||||
m_empty_widget->setAutoFillBackground(true);
|
||||
}
|
||||
|
||||
bool GameListWidget::isShowingGameList() const
|
||||
@@ -471,6 +545,11 @@ bool GameListWidget::getShowGridCoverTitles() const
|
||||
return m_model->getShowCoverTitles();
|
||||
}
|
||||
|
||||
bool GameListWidget::getShowGridFullCoverTitles() const
|
||||
{
|
||||
return m_model->getShowFullCoverTitles();
|
||||
}
|
||||
|
||||
void GameListWidget::refresh(bool invalidate_cache, bool popup_on_error)
|
||||
{
|
||||
cancelRefresh();
|
||||
@@ -499,6 +578,8 @@ void GameListWidget::cancelRefresh()
|
||||
void GameListWidget::reloadThemeSpecificImages()
|
||||
{
|
||||
m_model->reloadThemeSpecificImages();
|
||||
m_background_last_size = QSize();
|
||||
processBackgroundFrames();
|
||||
}
|
||||
|
||||
void GameListWidget::onRefreshProgress(const QString& status, int current, int total)
|
||||
@@ -703,6 +784,21 @@ void GameListWidget::setShowCoverTitles(bool enabled)
|
||||
emit layoutChange();
|
||||
}
|
||||
|
||||
void GameListWidget::setShowFullCoverTitles(bool enabled)
|
||||
{
|
||||
if (m_model->getShowFullCoverTitles() == enabled)
|
||||
return;
|
||||
|
||||
Host::SetBaseBoolSettingValue("UI", "GameListShowFullCoverTitles", enabled);
|
||||
Host::CommitBaseSettingChanges();
|
||||
m_model->setShowFullCoverTitles(enabled);
|
||||
m_list_view->setWordWrap(enabled);
|
||||
if (isShowingGameGrid())
|
||||
m_model->refresh();
|
||||
updateToolbar();
|
||||
emit layoutChange();
|
||||
}
|
||||
|
||||
void GameListWidget::updateToolbar()
|
||||
{
|
||||
const bool grid_view = Host::GetBaseBoolSettingValue("UI", "GameListGridView", false);
|
||||
@@ -718,12 +814,17 @@ void GameListWidget::updateToolbar()
|
||||
QSignalBlocker sb(m_ui.viewGridTitles);
|
||||
m_ui.viewGridTitles->setChecked(m_model->getShowCoverTitles());
|
||||
}
|
||||
{
|
||||
QSignalBlocker sb(m_ui.viewFullGridTitles);
|
||||
m_ui.viewFullGridTitles->setChecked(m_model->getShowFullCoverTitles());
|
||||
}
|
||||
{
|
||||
QSignalBlocker sb(m_ui.gridScale);
|
||||
m_ui.gridScale->setValue(static_cast<int>(m_model->getCoverScale() * 100.0f));
|
||||
}
|
||||
|
||||
m_ui.viewGridTitles->setEnabled(grid_view);
|
||||
m_ui.viewFullGridTitles->setEnabled(grid_view && m_model->getShowCoverTitles());
|
||||
m_ui.gridScale->setEnabled(grid_view);
|
||||
}
|
||||
|
||||
@@ -731,6 +832,7 @@ void GameListWidget::showEvent(QShowEvent* event)
|
||||
{
|
||||
QWidget::showEvent(event);
|
||||
updateCustomBackgroundState();
|
||||
processBackgroundFrames();
|
||||
}
|
||||
|
||||
void GameListWidget::hideEvent(QHideEvent* event)
|
||||
@@ -767,11 +869,8 @@ bool GameListWidget::eventFilter(QObject* watched, QEvent* event)
|
||||
if (!m_background_pixmap.isNull())
|
||||
{
|
||||
QPainter painter(m_ui.stack);
|
||||
const auto* paint_event = static_cast<QPaintEvent*>(event);
|
||||
painter.save();
|
||||
painter.setClipRect(paint_event->rect());
|
||||
painter.drawTiledPixmap(m_ui.stack->rect(), m_background_pixmap);
|
||||
painter.restore();
|
||||
painter.setClipRect(static_cast<QPaintEvent*>(event)->rect());
|
||||
painter.drawPixmap(0, 0, m_background_pixmap);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "pcsx2/GameList.h"
|
||||
|
||||
#include <QtGui/QColor>
|
||||
#include <QtGui/QMovie>
|
||||
#include <QtGui/QPixmap>
|
||||
#include <QtWidgets/QListView>
|
||||
@@ -53,12 +54,14 @@ public:
|
||||
void cancelRefresh();
|
||||
void reloadThemeSpecificImages();
|
||||
void setCustomBackground();
|
||||
void updateCustomBackgroundState(const bool force_start = false);
|
||||
void updateCustomBackgroundState();
|
||||
void processBackgroundFrames();
|
||||
void updateBackgroundTextColor(const QPixmap& frame);
|
||||
|
||||
bool isShowingGameList() const;
|
||||
bool isShowingGameGrid() const;
|
||||
bool getShowGridCoverTitles() const;
|
||||
bool getShowGridFullCoverTitles() const;
|
||||
|
||||
std::optional<GameList::Entry> getSelectedEntry() const;
|
||||
|
||||
@@ -93,6 +96,7 @@ public Q_SLOTS:
|
||||
void showGameList();
|
||||
void showGameGrid();
|
||||
void setShowCoverTitles(bool enabled);
|
||||
void setShowFullCoverTitles(bool enabled);
|
||||
void gridZoomIn();
|
||||
void gridZoomOut();
|
||||
void gridIntScale(int int_scale);
|
||||
@@ -128,6 +132,10 @@ private:
|
||||
|
||||
QMovie* m_background_movie = nullptr;
|
||||
QPixmap m_background_pixmap;
|
||||
QColor m_background_text_color;
|
||||
QtUtils::ScalingMode m_background_scaling = QtUtils::ScalingMode::Fit;
|
||||
float m_background_opacity = 100.0f;
|
||||
int m_background_last_frame = -1;
|
||||
QSize m_background_last_size;
|
||||
qreal m_background_last_dpr = 0.0;
|
||||
};
|
||||
|
||||
@@ -120,6 +120,31 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="viewFullGridTitles">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Show Full Titles</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Show Full Titles</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="price-tag-2-line"/>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="gridScale">
|
||||
<property name="minimumSize">
|
||||
@@ -218,6 +243,7 @@
|
||||
<tabstop>viewGameList</tabstop>
|
||||
<tabstop>viewGameGrid</tabstop>
|
||||
<tabstop>viewGridTitles</tabstop>
|
||||
<tabstop>viewFullGridTitles</tabstop>
|
||||
<tabstop>gridScale</tabstop>
|
||||
<tabstop>filterType</tabstop>
|
||||
<tabstop>filterRegion</tabstop>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user