Compare commits
145 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f5310f0ca4 | |||
| 972fcfa0f0 | |||
| b0582f6ea3 | |||
| 8b6c28b374 | |||
| 0a3c27765d | |||
| 040a8b8fc6 | |||
| 9f67f72d3c | |||
| 04438ace57 | |||
| e16236e450 | |||
| 3807517e02 | |||
| ab8bd8e754 | |||
| 65d63085f8 | |||
| 2c8ecc3ea7 | |||
| 5fbc79c466 | |||
| 8fe4183856 | |||
| 0d52608f1d | |||
| 5df3c54b84 | |||
| b2db2e71b6 | |||
| 7978370b39 | |||
| d187c61599 | |||
| fa745a58b2 | |||
| 16592f3a6e | |||
| 2b5d3510c3 | |||
| 76f690d96e | |||
| d5f75c9e42 | |||
| 13d3491571 | |||
| 8a6d6818fb | |||
| d7c1cbbc4e | |||
| 5644ca09c4 | |||
| 863307d871 | |||
| 904a0942f0 | |||
| 7f20ff60ea | |||
| 1ce246e05d | |||
| f79461b407 | |||
| bbfe32e963 | |||
| 06c67b0319 | |||
| c68753d776 | |||
| 15172303e4 | |||
| 9017ad626d | |||
| ef18567d9f | |||
| d0c2097cd4 | |||
| b5ed65ddd6 | |||
| 673917ae66 | |||
| e31f57a02f | |||
| ee57382d6e | |||
| 6683f84da0 | |||
| d2cb0aaf63 | |||
| 51a5e10b4e | |||
| c2dfc10c18 | |||
| 6628de5262 | |||
| 37f2837a55 | |||
| 4b76157b9e | |||
| 3b2962fe1c | |||
| 20721b05fb | |||
| 75187d69c6 | |||
| 3df128d9f9 | |||
| 426d77a932 | |||
| b3b1ff0f50 | |||
| 88029a1411 | |||
| 57e46f07fe | |||
| 254d955f89 | |||
| e490685b42 | |||
| 7b0c39a20d | |||
| 059d631a6d | |||
| 7c255bd58d | |||
| bbcd5ec25e | |||
| 5369d6b38e | |||
| b3834427bc | |||
| d7002433a2 | |||
| e1291d5b7c | |||
| e1fd632ba3 | |||
| fbaba52bc2 | |||
| 224e82f33e | |||
| 8df49d7676 | |||
| 88d8c561db | |||
| 78697466e0 | |||
| 5e150cd3eb | |||
| 23dd23b468 | |||
| 7d47dab728 | |||
| 74f2f73ce6 | |||
| 1e2f60a9b8 | |||
| bf892e9ece | |||
| a44fe80caa | |||
| 093287f6bf | |||
| e0365c1d10 | |||
| 6cb2086dfc | |||
| 3f4b424200 | |||
| 051fdd55b0 | |||
| 29f4318ef8 | |||
| 30d7fed0e5 | |||
| f0177a879c | |||
| 88af1e54a4 | |||
| d3ba0542f2 | |||
| 4d25bdcabb | |||
| 02c5f8f648 | |||
| fc23783224 | |||
| 7ade24fc77 | |||
| db16fa4403 | |||
| 1bc08722fd | |||
| 3a0a9f6705 | |||
| ae0b5834a0 | |||
| 55eddba08f | |||
| 0783ec1b9c | |||
| fc77fa1389 | |||
| 0f9b8afe04 | |||
| d39457951a | |||
| 9fcb526aca | |||
| 9cd0ec1634 | |||
| 0e178aecb8 | |||
| 66f764cf02 | |||
| 63f2d8d880 | |||
| b123f016ab | |||
| f08f679ba7 | |||
| 054e0509c4 | |||
| f86cd4c3d2 | |||
| a19b3747ea | |||
| 30440099c2 | |||
| 30dd35a743 | |||
| 4f1dab7b5f | |||
| baad1891eb | |||
| 6ea9e4a5b0 | |||
| 534330aa99 | |||
| 58a0ced8be | |||
| e18d5b7183 | |||
| b006366ea3 | |||
| 1db914f964 | |||
| 6b34999a51 | |||
| 85ea218b5d | |||
| 137f4f2666 | |||
| 4cc2a81f00 | |||
| da340c9e92 | |||
| a7356d3765 | |||
| 3aa0685786 | |||
| 2f8c98dccf | |||
| 59e422c538 | |||
| 5ef5e41e69 | |||
| 58facc8ab1 | |||
| 3341514bb4 | |||
| 0434aba3a8 | |||
| c1cb214a03 | |||
| 3233a3cf8f | |||
| 469a3d60b2 | |||
| bb35bed104 | |||
| 624acf5533 | |||
| f71f0b9cb0 |
@@ -8,4 +8,4 @@
|
||||
<!-- If applicable, including examples you've already tested with / recommendations for how to test further is very helpful! -->
|
||||
|
||||
### Did you use AI to help find, test, or implement this issue or feature?
|
||||
<!-- Answer yes or no. If you answer yes, please provide a brief explanation how. -->
|
||||
<!-- Answer yes or no. If you answer yes, please provide a brief explanation as to how you used it. Please see the Large Language Model (LLM) Usage Policy for more information: https://pcsx2.net/docs/contributing/#large-language-model-llm-usage-policy -->
|
||||
@@ -157,4 +157,21 @@ jobs:
|
||||
with:
|
||||
name: ${{ steps.artifact-metadata.outputs.artifact-name }}
|
||||
path: ci-artifacts
|
||||
report-error:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build_linux
|
||||
if: always() && needs.build_linux.result == 'failure' && inputs.publish == true
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Push Discord Webhook
|
||||
env:
|
||||
OWNER: PCSX2
|
||||
REPO: pcsx2
|
||||
DISCORD_BUILD_WEBHOOK: ${{ secrets.DISCORD_BUILD_WEBHOOK }}
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
cd ./.github/workflows/scripts/releases/announce-release
|
||||
npm ci
|
||||
FLATHUB_FAILURE=1 node index.js
|
||||
|
||||
@@ -20,6 +20,18 @@ jobs:
|
||||
cmakeflags: ""
|
||||
buildAppImage: true
|
||||
secrets: inherit
|
||||
build_linux_debug:
|
||||
name: "Debug"
|
||||
if: github.repository != 'PCSX2/pcsx2' || github.event_name == 'pull_request'
|
||||
uses: ./.github/workflows/linux_build_qt.yml
|
||||
with:
|
||||
jobName: "Debug Build"
|
||||
artifactPrefixName: "PCSX2-linux-Qt-x64-debug"
|
||||
compiler: clang
|
||||
cmakeflags: ""
|
||||
buildAppImage: true
|
||||
cmakeBuildType: Debug
|
||||
secrets: inherit
|
||||
build_linux_flatpak:
|
||||
name: "Flatpak"
|
||||
if: github.repository != 'PCSX2/pcsx2' || github.event_name == 'pull_request'
|
||||
|
||||
@@ -39,6 +39,10 @@ on:
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
cmakeBuildType:
|
||||
required: false
|
||||
type: string
|
||||
default: Release
|
||||
|
||||
jobs:
|
||||
build_linux:
|
||||
@@ -129,7 +133,7 @@ jobs:
|
||||
ADDITIONAL_CMAKE_ARGS: ${{ inputs.cmakeflags }}
|
||||
run: |
|
||||
cmake -B build -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_BUILD_TYPE="${{ inputs.cmakeBuildType }}" \
|
||||
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON \
|
||||
-DCMAKE_PREFIX_PATH="$HOME/deps" \
|
||||
-DCMAKE_C_COMPILER=clang-17 \
|
||||
|
||||
@@ -209,6 +209,7 @@ jobs:
|
||||
gh release upload "${TAG_VAL}" ${{ github.WORKSPACE }}/ci-artifacts/out/* --repo PCSX2/pcsx2 --clobber
|
||||
|
||||
- name: Publish Release
|
||||
if: github.event_name != 'workflow_dispatch' || inputs.is_prerelease == 'true'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
|
||||
@@ -18,7 +18,7 @@ if [ "${INSTALLDIR:0:1}" != "/" ]; then
|
||||
INSTALLDIR="$PWD/$INSTALLDIR"
|
||||
fi
|
||||
|
||||
QT=6.11.0
|
||||
QT=6.11.1
|
||||
QTAPNG=1.3.0
|
||||
|
||||
FFMPEG=8.1
|
||||
@@ -27,7 +27,7 @@ LIBJPEGTURBO=3.1.4.1
|
||||
LIBPNG=1.6.58
|
||||
LIBWEBP=1.6.0
|
||||
NVENC=13.0.19.0
|
||||
SDL=SDL3-3.4.8
|
||||
SDL=SDL3-3.4.10
|
||||
LZ4=1.10.0
|
||||
VULKAN=1.4.328.1
|
||||
ZSTD=1.5.7
|
||||
@@ -47,12 +47,12 @@ cd deps-build
|
||||
export PKG_CONFIG_PATH="$INSTALLDIR/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||
|
||||
grep . > SHASUMS <<EOF
|
||||
231ad85979864d914dc9568a1b71c91d6cf20d7b2021d059103bf0eb51cb755e qtbase-everywhere-src-$QT.tar.xz
|
||||
d3adb02ac5e2fe24068dbdaee0d7cc68cc3fa8553291c1bfce77c9fe8e940cc8 qtimageformats-everywhere-src-$QT.tar.xz
|
||||
dfa8d653be07087d9407ed4a4ebae847f8953e0b7abd829f089803ab652a30e6 qtsvg-everywhere-src-$QT.tar.xz
|
||||
cfb1993d7a10848965b01b9cf33a54b8a4ba4e5e3a6d28d59483e73f10d9fc76 qttools-everywhere-src-$QT.tar.xz
|
||||
54f48b2fe4316892ff930195f170a5385644acc7393505f3155c066b8e1ffe56 qttranslations-everywhere-src-$QT.tar.xz
|
||||
e710e6e760f92922b86e4dd68f6bbe94ef6510919519d1b0068e874b5ad84d37 qtwayland-everywhere-src-$QT.tar.xz
|
||||
d9594a31228aa23ad6b531719a29b45f0f3989fe6c136d45767ea179f233c1ac qtbase-everywhere-src-$QT.tar.xz
|
||||
b2bf6c6845ac175ed7f819145483ba4676f617aaa6a5012c8efee63c8bbac413 qtimageformats-everywhere-src-$QT.tar.xz
|
||||
7f3cf02f4824bf03c2c5859ea6db173bf1482a1daf24e6cdf7bc78cfa26a8a94 qtsvg-everywhere-src-$QT.tar.xz
|
||||
8e61835a679c93fa9c6065b142353c2071ba68e297898937c32a03777fcaf50d qttools-everywhere-src-$QT.tar.xz
|
||||
37c02c81206594c7bb4edca85ac93e8e55a9836b70c960fde6cb0f8623ec5677 qttranslations-everywhere-src-$QT.tar.xz
|
||||
95788aa502f75441d4edf65932b235f76523084e13dbbb7b9ee2d207b32bd9b3 qtwayland-everywhere-src-$QT.tar.xz
|
||||
f1d3be3489f758efe1a8f12118a212febbe611aa670af32e0159fa3c1feab2a6 QtApng-$QTAPNG.tar.gz
|
||||
|
||||
b072aed6871998cce9b36e7774033105ca29e33632be5b6347f3206898e0756a ffmpeg-$FFMPEG.tar.xz
|
||||
@@ -60,7 +60,7 @@ b072aed6871998cce9b36e7774033105ca29e33632be5b6347f3206898e0756a ffmpeg-$FFMPEG
|
||||
ecae8008e2cc9ade2f2c1bb9d5e6d4fb73e7c433866a056bd82980741571a022 libjpeg-turbo-$LIBJPEGTURBO.tar.gz
|
||||
28eb403f51f0f7405249132cecfe82ea5c0ef97f1b32c5a65828814ae0d34775 libpng-$LIBPNG.tar.xz
|
||||
e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 libwebp-$LIBWEBP.tar.gz
|
||||
e9fff7467fb60f037e6708da18b25560649e4c63edc2a69bb871b960d9cbfbba $SDL.tar.gz
|
||||
12b34280415ec8418c864408b93d008a20a6530687ee613d60bfbd20411f2785 $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.8.tar.gz",
|
||||
"sha256": "e9fff7467fb60f037e6708da18b25560649e4c63edc2a69bb871b960d9cbfbba"
|
||||
"url": "https://libsdl.org/release/SDL3-3.4.10.tar.gz",
|
||||
"sha256": "12b34280415ec8418c864408b93d008a20a6530687ee613d60bfbd20411f2785"
|
||||
}
|
||||
],
|
||||
"cleanup": [
|
||||
|
||||
@@ -43,11 +43,11 @@ if [ "${INSTALLDIR:0:1}" != "/" ]; then
|
||||
INSTALLDIR="$PWD/$INSTALLDIR"
|
||||
fi
|
||||
|
||||
QT=6.11.0
|
||||
QT=6.11.1
|
||||
QTAPNG=1.3.0
|
||||
|
||||
FREETYPE=2.14.3
|
||||
SDL=SDL3-3.4.8
|
||||
SDL=SDL3-3.4.10
|
||||
HARFBUZZ=14.2.0
|
||||
ZSTD=1.5.7
|
||||
LZ4=1.10.0
|
||||
@@ -85,15 +85,15 @@ CMAKE_ARCH_ARM64=-DCMAKE_OSX_ARCHITECTURES="arm64"
|
||||
CMAKE_ARCH_UNIVERSAL=-DCMAKE_OSX_ARCHITECTURES="x86_64;arm64"
|
||||
|
||||
grep . > SHASUMS <<EOF
|
||||
231ad85979864d914dc9568a1b71c91d6cf20d7b2021d059103bf0eb51cb755e qtbase-everywhere-src-$QT.tar.xz
|
||||
d3adb02ac5e2fe24068dbdaee0d7cc68cc3fa8553291c1bfce77c9fe8e940cc8 qtimageformats-everywhere-src-$QT.tar.xz
|
||||
dfa8d653be07087d9407ed4a4ebae847f8953e0b7abd829f089803ab652a30e6 qtsvg-everywhere-src-$QT.tar.xz
|
||||
cfb1993d7a10848965b01b9cf33a54b8a4ba4e5e3a6d28d59483e73f10d9fc76 qttools-everywhere-src-$QT.tar.xz
|
||||
54f48b2fe4316892ff930195f170a5385644acc7393505f3155c066b8e1ffe56 qttranslations-everywhere-src-$QT.tar.xz
|
||||
d9594a31228aa23ad6b531719a29b45f0f3989fe6c136d45767ea179f233c1ac qtbase-everywhere-src-$QT.tar.xz
|
||||
b2bf6c6845ac175ed7f819145483ba4676f617aaa6a5012c8efee63c8bbac413 qtimageformats-everywhere-src-$QT.tar.xz
|
||||
7f3cf02f4824bf03c2c5859ea6db173bf1482a1daf24e6cdf7bc78cfa26a8a94 qtsvg-everywhere-src-$QT.tar.xz
|
||||
8e61835a679c93fa9c6065b142353c2071ba68e297898937c32a03777fcaf50d qttools-everywhere-src-$QT.tar.xz
|
||||
37c02c81206594c7bb4edca85ac93e8e55a9836b70c960fde6cb0f8623ec5677 qttranslations-everywhere-src-$QT.tar.xz
|
||||
f1d3be3489f758efe1a8f12118a212febbe611aa670af32e0159fa3c1feab2a6 QtApng-$QTAPNG.tar.gz
|
||||
|
||||
36bc4f1cc413335368ee656c42afca65c5a3987e8768cc28cf11ba775e785a5f freetype-$FREETYPE.tar.xz
|
||||
e9fff7467fb60f037e6708da18b25560649e4c63edc2a69bb871b960d9cbfbba $SDL.tar.gz
|
||||
12b34280415ec8418c864408b93d008a20a6530687ee613d60bfbd20411f2785 $SDL.tar.gz
|
||||
c652d5d94971031654ab3989891a490a895d3e3f2b71171c62692b28e94b1b93 harfbuzz-$HARFBUZZ.tar.gz
|
||||
eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 zstd-$ZSTD.tar.gz
|
||||
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
|
||||
|
||||
@@ -20,11 +20,11 @@ if [ "${INSTALLDIR:0:1}" != "/" ]; then
|
||||
INSTALLDIR="$PWD/$INSTALLDIR"
|
||||
fi
|
||||
|
||||
QT=6.11.0
|
||||
QT=6.11.1
|
||||
QTAPNG=1.3.0
|
||||
|
||||
FREETYPE=2.14.3
|
||||
SDL=SDL3-3.4.8
|
||||
SDL=SDL3-3.4.10
|
||||
HARFBUZZ=14.2.0
|
||||
ZSTD=1.5.7
|
||||
LZ4=1.10.0
|
||||
@@ -61,15 +61,15 @@ CMAKE_COMMON=(
|
||||
)
|
||||
|
||||
grep . > SHASUMS <<EOF
|
||||
231ad85979864d914dc9568a1b71c91d6cf20d7b2021d059103bf0eb51cb755e qtbase-everywhere-src-$QT.tar.xz
|
||||
d3adb02ac5e2fe24068dbdaee0d7cc68cc3fa8553291c1bfce77c9fe8e940cc8 qtimageformats-everywhere-src-$QT.tar.xz
|
||||
dfa8d653be07087d9407ed4a4ebae847f8953e0b7abd829f089803ab652a30e6 qtsvg-everywhere-src-$QT.tar.xz
|
||||
cfb1993d7a10848965b01b9cf33a54b8a4ba4e5e3a6d28d59483e73f10d9fc76 qttools-everywhere-src-$QT.tar.xz
|
||||
54f48b2fe4316892ff930195f170a5385644acc7393505f3155c066b8e1ffe56 qttranslations-everywhere-src-$QT.tar.xz
|
||||
d9594a31228aa23ad6b531719a29b45f0f3989fe6c136d45767ea179f233c1ac qtbase-everywhere-src-$QT.tar.xz
|
||||
b2bf6c6845ac175ed7f819145483ba4676f617aaa6a5012c8efee63c8bbac413 qtimageformats-everywhere-src-$QT.tar.xz
|
||||
7f3cf02f4824bf03c2c5859ea6db173bf1482a1daf24e6cdf7bc78cfa26a8a94 qtsvg-everywhere-src-$QT.tar.xz
|
||||
8e61835a679c93fa9c6065b142353c2071ba68e297898937c32a03777fcaf50d qttools-everywhere-src-$QT.tar.xz
|
||||
37c02c81206594c7bb4edca85ac93e8e55a9836b70c960fde6cb0f8623ec5677 qttranslations-everywhere-src-$QT.tar.xz
|
||||
f1d3be3489f758efe1a8f12118a212febbe611aa670af32e0159fa3c1feab2a6 QtApng-$QTAPNG.tar.gz
|
||||
|
||||
36bc4f1cc413335368ee656c42afca65c5a3987e8768cc28cf11ba775e785a5f freetype-$FREETYPE.tar.xz
|
||||
e9fff7467fb60f037e6708da18b25560649e4c63edc2a69bb871b960d9cbfbba $SDL.tar.gz
|
||||
12b34280415ec8418c864408b93d008a20a6530687ee613d60bfbd20411f2785 $SDL.tar.gz
|
||||
c652d5d94971031654ab3989891a490a895d3e3f2b71171c62692b28e94b1b93 harfbuzz-$HARFBUZZ.tar.gz
|
||||
eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 zstd-$ZSTD.tar.gz
|
||||
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
|
||||
|
||||
@@ -38,39 +38,54 @@ const octokit = new Octokit({
|
||||
}
|
||||
});
|
||||
|
||||
if (process.env.TAG_VAL === undefined || process.env.TAG_VAL === "") {
|
||||
console.log(`Not announcing - TAG_VAL not defined`);
|
||||
process.exit(1);
|
||||
let embed;
|
||||
|
||||
if (process.env.FLATHUB_FAILURE === undefined)
|
||||
{
|
||||
if (process.env.TAG_VAL === undefined || process.env.TAG_VAL === "") {
|
||||
console.log(`Not announcing - TAG_VAL not defined`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const { data: releaseInfo } = await octokit.rest.repos.getReleaseByTag({
|
||||
owner: owner,
|
||||
repo: repo,
|
||||
tag: process.env.TAG_VAL,
|
||||
});
|
||||
|
||||
if (releaseInfo === undefined) {
|
||||
console.log(`Not announcing - could not locate release with tag ${process.env.TAG_VAL}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!releaseInfo.prerelease) {
|
||||
console.log("Not announcing - release was not a pre-release (aka a Nightly)");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Publish Webhook
|
||||
embed = new MessageEmbed()
|
||||
.setColor('#FF8000')
|
||||
.setTitle('New PCSX2 Nightly Build Available!')
|
||||
.setDescription("To download the latest or previous builds, [visit the official downloads page](https://pcsx2.net/downloads/).")
|
||||
.addFields(
|
||||
{ name: 'Version', value: releaseInfo.tag_name, inline: true },
|
||||
{ name: 'Installation Steps', value: '[See Here](https://pcsx2.net/docs/category/setup)', inline: true },
|
||||
{ name: 'Included Changes', value: releaseInfo.body, inline: false }
|
||||
);
|
||||
console.log(embed);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Flathub upload failure
|
||||
|
||||
const { data: releaseInfo } = await octokit.rest.repos.getReleaseByTag({
|
||||
owner: owner,
|
||||
repo: repo,
|
||||
tag: process.env.TAG_VAL,
|
||||
});
|
||||
|
||||
if (releaseInfo === undefined) {
|
||||
console.log(`Not announcing - could not locate release with tag ${process.env.TAG_VAL}`);
|
||||
process.exit(1);
|
||||
embed = new MessageEmbed()
|
||||
.setColor('#FF0000')
|
||||
.setTitle('PCSX2 Failed to Build/Upload to FlatHub')
|
||||
.setDescription("Please check the latest Flathub build job to determine the root cause.")
|
||||
console.log(embed);
|
||||
}
|
||||
|
||||
if (!releaseInfo.prerelease) {
|
||||
console.log("Not announcing - release was not a pre-release (aka a Nightly)");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Publish Webhook
|
||||
const embed = new MessageEmbed()
|
||||
.setColor('#FF8000')
|
||||
.setTitle('New PCSX2 Nightly Build Available!')
|
||||
.setDescription("To download the latest or previous builds, [visit the official downloads page](https://pcsx2.net/downloads/).")
|
||||
.addFields(
|
||||
{ name: 'Version', value: releaseInfo.tag_name, inline: true },
|
||||
{ name: 'Installation Steps', value: '[See Here](https://pcsx2.net/docs/category/setup)', inline: true },
|
||||
{ name: 'Included Changes', value: releaseInfo.body, inline: false }
|
||||
);
|
||||
console.log(embed);
|
||||
|
||||
// Get all webhooks, simple comma-sep string
|
||||
const webhookUrls = process.env.DISCORD_BUILD_WEBHOOK.split(",");
|
||||
|
||||
|
||||
@@ -54,13 +54,13 @@ echo INSTALLDIR=%INSTALLDIR%
|
||||
|
||||
cd "%BUILDDIR%"
|
||||
|
||||
set QT=6.11.0
|
||||
set QT=6.11.1
|
||||
set QTMINOR=6.11
|
||||
set QTAPNG=1.3.0
|
||||
|
||||
set FREETYPE=2.14.3
|
||||
set HARFBUZZ=14.2.0
|
||||
set SDL=SDL3-3.4.8
|
||||
set SDL=SDL3-3.4.10
|
||||
set LIBJPEGTURBO=3.1.4.1
|
||||
set LIBPNG=1658
|
||||
set LIBPNGLONG=1.6.58
|
||||
@@ -81,11 +81,11 @@ set SHADERC_SPIRVTOOLS=6337eb62cadd7d124ac6789bf39c0f71148f0a73
|
||||
|
||||
set AGILITYSDK=1.619.2
|
||||
|
||||
call :downloadfile "qtbase-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtbase-everywhere-src-%QT%.zip" 590d5ae246c85fa14d6458a36ff75a11236acfe8987c2475090aab1770acbdf8 || goto error
|
||||
call :downloadfile "qtimageformats-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtimageformats-everywhere-src-%QT%.zip" 5dfb3c0cb84d2c935c1716b3b86358ca496fb9216676e7e28fee1357fb4a050e || goto error
|
||||
call :downloadfile "qtsvg-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtsvg-everywhere-src-%QT%.zip" c92e1b46170b8671bbd46c783939f0c2dba4986882c5ebd37aaa9eba5b224fbe || goto error
|
||||
call :downloadfile "qttools-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttools-everywhere-src-%QT%.zip" ded4420823cb7b1bc49f38c5c56dd43ec27b6577ccbc7f620790abfabcc33b62 || goto error
|
||||
call :downloadfile "qttranslations-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttranslations-everywhere-src-%QT%.zip" f9c4fd4fd73dff51e9043d2825b04361b678f63798ed798021ef47f1ff0bfccf || goto error
|
||||
call :downloadfile "qtbase-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtbase-everywhere-src-%QT%.zip" 3529cc37297a5a7aae4486843b9fd41c30df1d79a770f85e240b537dcc327ca5 || goto error
|
||||
call :downloadfile "qtimageformats-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtimageformats-everywhere-src-%QT%.zip" 37fba768f2780580dfae535ad6654cb9dc0bf2272e71b9b9781988de9ed0dac0 || goto error
|
||||
call :downloadfile "qtsvg-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtsvg-everywhere-src-%QT%.zip" 767730188d4610a89bf8da502f87acf1c8881a3ac54f1e0eb167ab1e08b03a75 || goto error
|
||||
call :downloadfile "qttools-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttools-everywhere-src-%QT%.zip" 2d6ed2a98f458152e3cb17bf0be0494250194933a2d937b5a434e5b2006efca9 || goto error
|
||||
call :downloadfile "qttranslations-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttranslations-everywhere-src-%QT%.zip" eef43700ffd079f5893e435aca1330c8bdbf2a94ae45013e3fc63870df53d3b0 || goto error
|
||||
call :downloadfile "QtApng-%QTAPNG%.zip" "https://github.com/jurplel/QtApng/archive/refs/tags/%QTAPNG%.zip" 5176082cdd468047a7eb1ec1f106b032f57df207aa318d559b29606b00d159ac || goto error
|
||||
|
||||
call :downloadfile "freetype-%FREETYPE%.tar.gz" https://sourceforge.net/projects/freetype/files/freetype2/%FREETYPE%/freetype-%FREETYPE%.tar.gz/download e61b31ab26358b946e767ed7eb7f4bb2e507da1cfefeb7a8861ace7fd5c899a1 || goto error
|
||||
@@ -94,7 +94,7 @@ call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lp
|
||||
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 "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" 506206e02f90c1f37e048eacaf9e8a3a7dc682fd27783eb0ff15a7d2dcc9c2af || goto error
|
||||
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" be8dafef3ecc956e736cfd1c242c10737f3ad589a866f25ba437b7f2f6d5ced2 || 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
|
||||
|
||||
@@ -61,7 +61,7 @@ set "PATH=%PATH%;%INSTALLDIR%\bin"
|
||||
|
||||
cd "%BUILDDIR%"
|
||||
|
||||
set QT=6.11.0
|
||||
set QT=6.11.1
|
||||
set QTMINOR=6.11
|
||||
set QTAPNG=1.3.0
|
||||
|
||||
@@ -77,7 +77,7 @@ set LIBX264=b35605ace3ddf7c1a5d67a2eb553f034aef41d55
|
||||
|
||||
set FREETYPE=2.14.3
|
||||
set HARFBUZZ=14.2.0
|
||||
set SDL=SDL3-3.4.8
|
||||
set SDL=SDL3-3.4.10
|
||||
set LIBJPEGTURBO=3.1.4.1
|
||||
set LIBPNG=1658
|
||||
set LIBPNGLONG=1.6.58
|
||||
@@ -97,12 +97,13 @@ set SHADERC_SPIRVHEADERS=58006c901d1d5c37dece6b6610e9af87fa951375
|
||||
set SHADERC_SPIRVTOOLS=6337eb62cadd7d124ac6789bf39c0f71148f0a73
|
||||
|
||||
set AGILITYSDK=1.619.2
|
||||
set DXHEADERS=1.619.1
|
||||
|
||||
call :downloadfile "qtbase-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtbase-everywhere-src-%QT%.zip" 590d5ae246c85fa14d6458a36ff75a11236acfe8987c2475090aab1770acbdf8 || goto error
|
||||
call :downloadfile "qtimageformats-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtimageformats-everywhere-src-%QT%.zip" 5dfb3c0cb84d2c935c1716b3b86358ca496fb9216676e7e28fee1357fb4a050e || goto error
|
||||
call :downloadfile "qtsvg-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtsvg-everywhere-src-%QT%.zip" c92e1b46170b8671bbd46c783939f0c2dba4986882c5ebd37aaa9eba5b224fbe || goto error
|
||||
call :downloadfile "qttools-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttools-everywhere-src-%QT%.zip" ded4420823cb7b1bc49f38c5c56dd43ec27b6577ccbc7f620790abfabcc33b62 || goto error
|
||||
call :downloadfile "qttranslations-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttranslations-everywhere-src-%QT%.zip" f9c4fd4fd73dff51e9043d2825b04361b678f63798ed798021ef47f1ff0bfccf || goto error
|
||||
call :downloadfile "qtbase-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtbase-everywhere-src-%QT%.zip" 3529cc37297a5a7aae4486843b9fd41c30df1d79a770f85e240b537dcc327ca5 || goto error
|
||||
call :downloadfile "qtimageformats-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtimageformats-everywhere-src-%QT%.zip" 37fba768f2780580dfae535ad6654cb9dc0bf2272e71b9b9781988de9ed0dac0 || goto error
|
||||
call :downloadfile "qtsvg-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtsvg-everywhere-src-%QT%.zip" 767730188d4610a89bf8da502f87acf1c8881a3ac54f1e0eb167ab1e08b03a75 || goto error
|
||||
call :downloadfile "qttools-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttools-everywhere-src-%QT%.zip" 2d6ed2a98f458152e3cb17bf0be0494250194933a2d937b5a434e5b2006efca9 || goto error
|
||||
call :downloadfile "qttranslations-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttranslations-everywhere-src-%QT%.zip" eef43700ffd079f5893e435aca1330c8bdbf2a94ae45013e3fc63870df53d3b0 || goto error
|
||||
call :downloadfile "QtApng-%QTAPNG%.zip" "https://github.com/jurplel/QtApng/archive/refs/tags/%QTAPNG%.zip" 5176082cdd468047a7eb1ec1f106b032f57df207aa318d559b29606b00d159ac || goto error
|
||||
|
||||
call :downloadfile "ffmpeg-%FFMPEG%.tar.xz" "https://ffmpeg.org/releases/ffmpeg-%FFMPEG%.tar.xz" b072aed6871998cce9b36e7774033105ca29e33632be5b6347f3206898e0756a || goto error
|
||||
@@ -121,7 +122,7 @@ call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lp
|
||||
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 "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" 506206e02f90c1f37e048eacaf9e8a3a7dc682fd27783eb0ff15a7d2dcc9c2af || goto error
|
||||
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" be8dafef3ecc956e736cfd1c242c10737f3ad589a866f25ba437b7f2f6d5ced2 || 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
|
||||
@@ -129,6 +130,7 @@ call :downloadfile "KDDockWidgets-%KDDOCKWIDGETS%.zip" "https://github.com/KDAB/
|
||||
call :downloadfile "plutovg-%PLUTOVG%.zip" "https://github.com/sammycage/plutovg/archive/v%PLUTOVG%.zip" 4fe4e48f28aa80171b2166d45c0976ab0f21eecedb52cd4c3ef73b5afb48fac9 || goto error
|
||||
call :downloadfile "plutosvg-%PLUTOSVG%.zip" "https://github.com/sammycage/plutosvg/archive/v%PLUTOSVG%.zip" 82dee2c57ad712bdd6d6d81d3e76249d89caa4b5a4214353660fd5adff12201a || goto error
|
||||
call :downloadfile "agility-sdk-%AGILITYSDK%.nupkg" "https://www.nuget.org/api/v2/package/Microsoft.Direct3D.D3D12/%AGILITYSDK%" eb92d90bb23b2ec23410c41d791e41dbdbec942ab946924d1fdcb31eac6f0735 || goto error
|
||||
call :downloadfile "DirectX-Headers-%DXHEADERS%.zip" "https://github.com/microsoft/DirectX-Headers/archive/v%DXHEADERS%.zip" 9eb8b102a90a42e4ea72a825f7d249d55ec90d164f030966c9b7784b93374927 || goto error
|
||||
call :downloadfile "rapidyaml-%RAPIDYAML%-src.zip" "https://github.com/biojppm/rapidyaml/releases/download/v%RAPIDYAML%/rapidyaml-%RAPIDYAML%-src.zip" 96276f55b9fa7837ac8f3f72fd52965879cbb5d5d2e6af548c69a177fb078304 || goto error
|
||||
|
||||
call :downloadfile "shaderc-%SHADERC%.zip" "https://github.com/google/shaderc/archive/refs/tags/v%SHADERC%.zip" f9401cc5cb36c276cd1e072b6595dbd728148e8dba389e50f7339e2d388dbc08 || goto error
|
||||
@@ -469,6 +471,7 @@ 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
|
||||
@@ -506,6 +509,16 @@ if %DEBUG%==1 (
|
||||
)
|
||||
cd .. || goto error
|
||||
|
||||
rem DirectX Headers include a CMakeList file, which is absent in the Nuget package
|
||||
echo Unpacking DirectX Headers
|
||||
rmdir /S /Q "DirectX-Headers-%DXHEADERS%"
|
||||
%SEVENZIP% x "DirectX-Headers-%DXHEADERS%.zip" || goto error
|
||||
cd "DirectX-Headers-%DXHEADERS%" || goto error
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="%INSTALLDIR%" -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DDXHEADERS_BUILD_TEST=OFF -DDXHEADERS_BUILD_GOOGLE_TEST=OFF -B build -G Ninja || goto error
|
||||
cmake --build build --parallel || goto error
|
||||
ninja -C build install || goto error
|
||||
cd .. || goto error
|
||||
|
||||
echo Building shaderc...
|
||||
rmdir /S /Q "shaderc-%SHADERC%"
|
||||
%SEVENZIP% x "shaderc-%SHADERC%.zip" || goto error
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
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';
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
lint_vs_proj_files:
|
||||
name: Lint VS Project Files
|
||||
if: github.repository != 'PCSX2/pcsx2' || github.event_name == 'pull_request'
|
||||
runs-on: windows-2025
|
||||
runs-on: windows-2025-vs2026
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
@@ -12,7 +12,7 @@ on:
|
||||
os:
|
||||
required: false
|
||||
type: string
|
||||
default: windows-2025
|
||||
default: windows-2025-vs2026
|
||||
platform:
|
||||
required: false
|
||||
type: string
|
||||
|
||||
@@ -3,5 +3,7 @@ add_library(D3D12MemAlloc
|
||||
src/D3D12MemAlloc.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(D3D12MemAlloc PRIVATE Microsoft::DirectX-Headers)
|
||||
|
||||
target_include_directories(D3D12MemAlloc PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
disable_compiler_warnings_for_target(D3D12MemAlloc)
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="..\DefaultProjectRootDir.props" />
|
||||
<Import Project="..\3rdparty.props" />
|
||||
<Import Project="..\..\common\vsprops\DepsDir.props" />
|
||||
<Import Condition="$(Configuration.Contains(Debug))" Project="..\..\common\vsprops\CodeGen_Debug.props" />
|
||||
<Import Condition="$(Configuration.Contains(Devel))" Project="..\..\common\vsprops\CodeGen_Devel.props" />
|
||||
<Import Condition="$(Configuration.Contains(Release))" Project="..\..\common\vsprops\CodeGen_Release.props" />
|
||||
@@ -37,9 +38,9 @@
|
||||
</ItemGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>D3D12MA_USING_DIRECTX_HEADERS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<WarningLevel>TurnOffAllWarnings</WarningLevel>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir)include;$(ProjectDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(DepsIncludeDir);$(ProjectDir)include;$(ProjectDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
/** \mainpage D3D12 Memory Allocator
|
||||
|
||||
<b>Version 3.1.0</b> (2026-02-23)
|
||||
<b>Version 3.2.0</b> (2026-06-05)
|
||||
|
||||
Copyright (c) 2019-2026 Advanced Micro Devices, Inc. All rights reserved. \n
|
||||
License: MIT
|
||||
@@ -889,10 +889,17 @@ enum POOL_FLAGS
|
||||
*/
|
||||
POOL_FLAG_MSAA_TEXTURES_ALWAYS_COMMITTED = 0x2,
|
||||
/** Every allocation made in this pool will be created as a committed resource - will have its own memory block.
|
||||
|
||||
|
||||
There is also an equivalent flag for the entire allocator: D3D12MA::ALLOCATOR_FLAG_ALWAYS_COMMITTED.
|
||||
*/
|
||||
POOL_FLAG_ALWAYS_COMMITTED = 0x4,
|
||||
/** Disables tight resource alignment for all resources created from this pool.
|
||||
|
||||
When tight alignment is supported and enabled for the allocator, resources created from custom pools
|
||||
normally use it automatically. Specify this flag to restore pre-tight-alignment behavior for this pool:
|
||||
tight alignment won't be requested and small buffers may again be preferred as committed resources.
|
||||
*/
|
||||
POOL_FLAG_DONT_USE_TIGHT_ALIGNMENT = 0x8,
|
||||
|
||||
// Bit mask to extract only `ALGORITHM` bits from entire set of flags.
|
||||
POOL_FLAG_ALGORITHM_MASK = POOL_FLAG_ALGORITHM_LINEAR
|
||||
@@ -1114,7 +1121,8 @@ enum ALLOCATOR_FLAGS
|
||||
It can also be disabled for a single allocation by using #ALLOCATION_FLAG_STRATEGY_MIN_TIME.
|
||||
|
||||
If the tight resource alignment feature is used by the library (which happens automatically whenever supported,
|
||||
unless you use flag #ALLOCATOR_FLAG_DONT_USE_TIGHT_ALIGNMENT), then small buffers are not preferred as committed.
|
||||
unless you use flag #ALLOCATOR_FLAG_DONT_USE_TIGHT_ALIGNMENT or
|
||||
#POOL_FLAG_DONT_USE_TIGHT_ALIGNMENT on a custom pool), then small buffers are not preferred as committed.
|
||||
Long story short, you don't need to specify any of these flags.
|
||||
The library chooses the most optimal method automatically.
|
||||
*/
|
||||
@@ -2892,6 +2900,7 @@ as it is no longer needed.
|
||||
|
||||
You can check if the tight alignment it is available in the current system by calling D3D12MA::Allocator::IsTightAlignmentSupported().
|
||||
You can tell the library to not use it by specifying D3D12MA::ALLOCATOR_FLAG_DONT_USE_TIGHT_ALIGNMENT.
|
||||
Custom pools can opt out independently by specifying D3D12MA::POOL_FLAG_DONT_USE_TIGHT_ALIGNMENT.
|
||||
Typically, you don't need to do any of those.
|
||||
|
||||
The library automatically aligns all buffers to at least 256 B, even when the system supports smaller alignment.
|
||||
|
||||
@@ -163,6 +163,14 @@ especially to test compatibility with D3D12_RESOURCE_HEAP_TIER_1 on modern GPUs.
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef D3D12MA_OPTIONS4_SUPPORTED
|
||||
#ifdef __ID3D12Device5_INTERFACE_DEFINED__
|
||||
#define D3D12MA_OPTIONS4_SUPPORTED 1
|
||||
#else
|
||||
#define D3D12MA_OPTIONS4_SUPPORTED 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef D3D12MA_DEBUG_LOG
|
||||
#define D3D12MA_DEBUG_LOG(format, ...)
|
||||
/*
|
||||
@@ -703,6 +711,13 @@ static UINT GetBitsPerPixel(DXGI_FORMAT format)
|
||||
case DXGI_FORMAT_R24_UNORM_X8_TYPELESS:
|
||||
case DXGI_FORMAT_X24_TYPELESS_G8_UINT:
|
||||
return 32;
|
||||
case DXGI_FORMAT_B8G8R8A8_UNORM:
|
||||
case DXGI_FORMAT_B8G8R8X8_UNORM:
|
||||
case DXGI_FORMAT_B8G8R8A8_TYPELESS:
|
||||
case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
|
||||
case DXGI_FORMAT_B8G8R8X8_TYPELESS:
|
||||
case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB:
|
||||
return 32;
|
||||
case DXGI_FORMAT_R8G8_TYPELESS:
|
||||
case DXGI_FORMAT_R8G8_UNORM:
|
||||
case DXGI_FORMAT_R8G8_UINT:
|
||||
@@ -717,6 +732,9 @@ static UINT GetBitsPerPixel(DXGI_FORMAT format)
|
||||
case DXGI_FORMAT_R16_SNORM:
|
||||
case DXGI_FORMAT_R16_SINT:
|
||||
return 16;
|
||||
case DXGI_FORMAT_B5G6R5_UNORM:
|
||||
case DXGI_FORMAT_B5G5R5A1_UNORM:
|
||||
return 16;
|
||||
case DXGI_FORMAT_R8_TYPELESS:
|
||||
case DXGI_FORMAT_R8_UNORM:
|
||||
case DXGI_FORMAT_R8_UINT:
|
||||
@@ -767,46 +785,175 @@ static ResourceClass ResourceDescToResourceClass(const D3D12_RESOURCE_DESC_T& re
|
||||
(resDesc.Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) != 0;
|
||||
return isRenderTargetOrDepthStencil ? ResourceClass::RT_DS_Texture : ResourceClass::Non_RT_DS_Texture;
|
||||
}
|
||||
|
||||
static bool ResourceDimensionIsTexture(D3D12_RESOURCE_DIMENSION dimension)
|
||||
{
|
||||
return dimension == D3D12_RESOURCE_DIMENSION_TEXTURE1D ||
|
||||
dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D ||
|
||||
dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D;
|
||||
}
|
||||
|
||||
static bool ResourceFlagsContainRenderTargetOrDepthStencil(D3D12_RESOURCE_FLAGS flags)
|
||||
{
|
||||
return (flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) != 0;
|
||||
}
|
||||
|
||||
// This algorithm is overly conservative.
|
||||
struct SmallAlignmentTileShape
|
||||
{
|
||||
UINT Width;
|
||||
UINT Height;
|
||||
UINT Depth;
|
||||
};
|
||||
|
||||
static bool GetSmallAlignmentTileShape(
|
||||
D3D12_RESOURCE_DIMENSION dimension,
|
||||
bool isMsaa,
|
||||
UINT bitsPerUnit,
|
||||
SmallAlignmentTileShape& outTileShape)
|
||||
{
|
||||
if (isMsaa)
|
||||
{
|
||||
if (dimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D)
|
||||
return false;
|
||||
|
||||
switch (bitsPerUnit)
|
||||
{
|
||||
case 8: outTileShape = { 256, 256, 1 }; return true;
|
||||
case 16: outTileShape = { 256, 128, 1 }; return true;
|
||||
case 32: outTileShape = { 128, 128, 1 }; return true;
|
||||
case 64: outTileShape = { 128, 64, 1 }; return true;
|
||||
case 128: outTileShape = { 64, 64, 1 }; return true;
|
||||
case 256: outTileShape = { 64, 32, 1 }; return true;
|
||||
case 512: outTileShape = { 32, 32, 1 }; return true;
|
||||
case 1024: outTileShape = { 32, 16, 1 }; return true;
|
||||
case 2048: outTileShape = { 16, 16, 1 }; return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
switch (dimension)
|
||||
{
|
||||
case D3D12_RESOURCE_DIMENSION_TEXTURE1D:
|
||||
switch (bitsPerUnit)
|
||||
{
|
||||
case 8: outTileShape = { 4096, 1, 1 }; return true;
|
||||
case 16: outTileShape = { 2048, 1, 1 }; return true;
|
||||
case 32: outTileShape = { 1024, 1, 1 }; return true;
|
||||
case 64: outTileShape = { 512, 1, 1 }; return true;
|
||||
case 128: outTileShape = { 256, 1, 1 }; return true;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
case D3D12_RESOURCE_DIMENSION_TEXTURE2D:
|
||||
switch (bitsPerUnit)
|
||||
{
|
||||
case 8: outTileShape = { 64, 64, 1 }; return true;
|
||||
case 16: outTileShape = { 64, 32, 1 }; return true;
|
||||
case 32: outTileShape = { 32, 32, 1 }; return true;
|
||||
case 64: outTileShape = { 32, 16, 1 }; return true;
|
||||
case 128: outTileShape = { 16, 16, 1 }; return true;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
case D3D12_RESOURCE_DIMENSION_TEXTURE3D:
|
||||
// 4 KB counterparts of the documented 64 KB volume-tile shapes.
|
||||
switch (bitsPerUnit)
|
||||
{
|
||||
case 8: outTileShape = { 16, 16, 16 }; return true;
|
||||
case 16: outTileShape = { 16, 16, 8 }; return true;
|
||||
case 32: outTileShape = { 16, 8, 8 }; return true;
|
||||
case 64: outTileShape = { 8, 8, 8 }; return true;
|
||||
case 128: outTileShape = { 8, 8, 4 }; return true;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// This algorithm is overly conservative and applies only to the legacy small-alignment heuristic path.
|
||||
template<typename D3D12_RESOURCE_DESC_T>
|
||||
static bool CanUseSmallAlignment(const D3D12_RESOURCE_DESC_T& resourceDesc)
|
||||
{
|
||||
if (resourceDesc.Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D)
|
||||
if (resourceDesc.SampleDesc.Count == 0 ||
|
||||
resourceDesc.Layout != D3D12_TEXTURE_LAYOUT_UNKNOWN ||
|
||||
resourceDesc.Width > UINT_MAX)
|
||||
{
|
||||
return false;
|
||||
if ((resourceDesc.Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) != 0)
|
||||
}
|
||||
|
||||
const bool isMsaa = resourceDesc.SampleDesc.Count > 1;
|
||||
const bool isRtDs = ResourceFlagsContainRenderTargetOrDepthStencil(resourceDesc.Flags);
|
||||
if (isMsaa && !isRtDs)
|
||||
{
|
||||
return false;
|
||||
if (resourceDesc.SampleDesc.Count > 1)
|
||||
return false;
|
||||
if (resourceDesc.DepthOrArraySize != 1)
|
||||
}
|
||||
if (!isMsaa &&
|
||||
isRtDs)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
UINT sizeX = (UINT)resourceDesc.Width;
|
||||
UINT sizeY = resourceDesc.Height;
|
||||
UINT bitsPerPixel = GetBitsPerPixel(resourceDesc.Format);
|
||||
if (bitsPerPixel == 0)
|
||||
UINT sizeY = 1;
|
||||
UINT sizeZ = 1;
|
||||
UINT bitsPerUnit = GetBitsPerPixel(resourceDesc.Format);
|
||||
if (bitsPerUnit == 0)
|
||||
return false;
|
||||
|
||||
if (IsFormatCompressed(resourceDesc.Format))
|
||||
switch (resourceDesc.Dimension)
|
||||
{
|
||||
sizeX = DivideRoundingUp(sizeX, 4u);
|
||||
sizeY = DivideRoundingUp(sizeY, 4u);
|
||||
bitsPerPixel *= 16;
|
||||
case D3D12_RESOURCE_DIMENSION_TEXTURE1D:
|
||||
if (isMsaa)
|
||||
return false;
|
||||
// For 1D arrays, small-alignment eligibility depends on the mip0 slice size, not array size.
|
||||
if (IsFormatCompressed(resourceDesc.Format))
|
||||
return false;
|
||||
break;
|
||||
|
||||
case D3D12_RESOURCE_DIMENSION_TEXTURE2D:
|
||||
// For 2D arrays, small-alignment eligibility depends on mip0 width/height, not array size.
|
||||
sizeY = resourceDesc.Height;
|
||||
if (isMsaa && IsFormatCompressed(resourceDesc.Format))
|
||||
return false;
|
||||
if (IsFormatCompressed(resourceDesc.Format))
|
||||
{
|
||||
sizeX = DivideRoundingUp(sizeX, 4u);
|
||||
sizeY = DivideRoundingUp(sizeY, 4u);
|
||||
bitsPerUnit *= 16;
|
||||
}
|
||||
break;
|
||||
|
||||
case D3D12_RESOURCE_DIMENSION_TEXTURE3D:
|
||||
if (isMsaa)
|
||||
return false;
|
||||
sizeY = resourceDesc.Height;
|
||||
sizeZ = resourceDesc.DepthOrArraySize;
|
||||
if (IsFormatCompressed(resourceDesc.Format))
|
||||
{
|
||||
sizeX = DivideRoundingUp(sizeX, 4u);
|
||||
sizeY = DivideRoundingUp(sizeY, 4u);
|
||||
bitsPerUnit *= 16;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
UINT tileSizeX = 0, tileSizeY = 0;
|
||||
switch (bitsPerPixel)
|
||||
{
|
||||
case 8: tileSizeX = 64; tileSizeY = 64; break;
|
||||
case 16: tileSizeX = 64; tileSizeY = 32; break;
|
||||
case 32: tileSizeX = 32; tileSizeY = 32; break;
|
||||
case 64: tileSizeX = 32; tileSizeY = 16; break;
|
||||
case 128: tileSizeX = 16; tileSizeY = 16; break;
|
||||
default: return false;
|
||||
}
|
||||
bitsPerUnit *= resourceDesc.SampleDesc.Count;
|
||||
|
||||
const UINT tileCount = DivideRoundingUp(sizeX, tileSizeX) * DivideRoundingUp(sizeY, tileSizeY);
|
||||
return tileCount <= 16;
|
||||
SmallAlignmentTileShape tileShape = {};
|
||||
if (!GetSmallAlignmentTileShape(resourceDesc.Dimension, isMsaa, bitsPerUnit, tileShape))
|
||||
return false;
|
||||
|
||||
const UINT64 tileCount =
|
||||
UINT64(DivideRoundingUp(sizeX, tileShape.Width)) *
|
||||
UINT64(DivideRoundingUp(sizeY, tileShape.Height)) *
|
||||
UINT64(DivideRoundingUp(sizeZ, tileShape.Depth));
|
||||
// Non-MSAA compares 64 KB against 4 KB tiles => 16 pages. MSAA compares 4 MB against 64 KB tiles => 64 pages.
|
||||
return tileCount <= (isMsaa ? 64 : 16);
|
||||
}
|
||||
|
||||
static bool ValidateAllocateMemoryParameters(
|
||||
@@ -5988,6 +6135,7 @@ public:
|
||||
BOOL IsCacheCoherentUMA() const { return m_D3D12Architecture.CacheCoherentUMA; }
|
||||
bool SupportsResourceHeapTier2() const { return m_D3D12Options.ResourceHeapTier >= D3D12_RESOURCE_HEAP_TIER_2; }
|
||||
bool IsGPUUploadHeapSupported() const { return m_GPUUploadHeapSupported != FALSE; }
|
||||
bool IsMsaa64KBAlignedTextureSupported() const { return m_MSAA64KBAlignedTextureSupported != FALSE; }
|
||||
bool IsTightAlignmentSupported() const { return m_TightAlignmentSupported != FALSE; }
|
||||
bool IsTightAlignmentEnabled() const { return IsTightAlignmentSupported() && m_UseTightAlignment; }
|
||||
bool UseMutex() const { return m_UseMutex; }
|
||||
@@ -6076,7 +6224,7 @@ private:
|
||||
const bool m_UseMutex;
|
||||
const bool m_AlwaysCommitted;
|
||||
const bool m_MsaaAlwaysCommitted;
|
||||
bool m_PreferSmallBuffersCommitted;
|
||||
const bool m_PreferSmallBuffersCommitted;
|
||||
const bool m_UseTightAlignment;
|
||||
bool m_DefaultPoolsNotZeroed = false;
|
||||
ID3D12Device* m_Device; // AddRef
|
||||
@@ -6105,6 +6253,7 @@ private:
|
||||
DXGI_ADAPTER_DESC m_AdapterDesc;
|
||||
D3D12_FEATURE_DATA_D3D12_OPTIONS m_D3D12Options;
|
||||
BOOL m_GPUUploadHeapSupported = FALSE;
|
||||
BOOL m_MSAA64KBAlignedTextureSupported = FALSE;
|
||||
BOOL m_TightAlignmentSupported = FALSE;
|
||||
D3D12_FEATURE_DATA_ARCHITECTURE m_D3D12Architecture;
|
||||
AllocationObjectAllocator m_AllocationObjectAllocator;
|
||||
@@ -6121,7 +6270,7 @@ private:
|
||||
*/
|
||||
template<typename D3D12_RESOURCE_DESC_T>
|
||||
bool PrefersCommittedAllocation(const D3D12_RESOURCE_DESC_T& resourceDesc,
|
||||
ALLOCATION_FLAGS strategy);
|
||||
ALLOCATION_FLAGS strategy, bool useTightAlignment) const;
|
||||
|
||||
// Allocates and registers new committed resource with implicit heap, as dedicated allocation.
|
||||
// Creates and returns Allocation object and optionally D3D12 resource.
|
||||
@@ -6141,6 +6290,7 @@ private:
|
||||
template<typename D3D12_RESOURCE_DESC_T>
|
||||
HRESULT CalcAllocationParams(const ALLOCATION_DESC& allocDesc, UINT64 allocSize,
|
||||
const D3D12_RESOURCE_DESC_T* resDesc, // Optional
|
||||
bool useTightAlignment,
|
||||
BlockVector*& outBlockVector, CommittedAllocationParameters& outCommittedAllocationParams, bool& outPreferCommitted);
|
||||
|
||||
// Returns UINT32_MAX if index cannot be calculcated.
|
||||
@@ -6174,7 +6324,10 @@ private:
|
||||
template<typename D3D12_RESOURCE_DESC_T>
|
||||
HRESULT GetResourceAllocationInfo(D3D12_RESOURCE_DESC_T& inOutResourceDesc,
|
||||
UINT32 NumCastableFormats, const DXGI_FORMAT* pCastableFormats,
|
||||
D3D12_RESOURCE_ALLOCATION_INFO& outAllocInfo) const;
|
||||
D3D12_RESOURCE_ALLOCATION_INFO& outAllocInfo,
|
||||
bool useTightAlignment) const;
|
||||
|
||||
bool IsTightAlignmentEnabled(const ALLOCATION_DESC& allocDesc) const;
|
||||
|
||||
bool NewAllocationWithinBudget(D3D12_HEAP_TYPE heapType, UINT64 size);
|
||||
|
||||
@@ -6277,6 +6430,17 @@ HRESULT AllocatorPimpl::Init(const ALLOCATOR_DESC& desc)
|
||||
}
|
||||
#endif // #if D3D12MA_OPTIONS16_SUPPORTED
|
||||
|
||||
#if D3D12MA_OPTIONS4_SUPPORTED
|
||||
{
|
||||
D3D12_FEATURE_DATA_D3D12_OPTIONS4 options4 = {};
|
||||
hr = m_Device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS4, &options4, sizeof(options4));
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
m_MSAA64KBAlignedTextureSupported = options4.MSAA64KBAlignedTextureSupported;
|
||||
}
|
||||
}
|
||||
#endif // #if D3D12MA_OPTIONS4_SUPPORTED
|
||||
|
||||
#if D3D12MA_TIGHT_ALIGNMENT_SUPPORTED
|
||||
{
|
||||
D3D12_FEATURE_DATA_TIGHT_ALIGNMENT tightAlignment = {};
|
||||
@@ -6285,13 +6449,6 @@ HRESULT AllocatorPimpl::Init(const ALLOCATOR_DESC& desc)
|
||||
{
|
||||
m_TightAlignmentSupported = tightAlignment.SupportTier >= D3D12_TIGHT_ALIGNMENT_TIER_1;
|
||||
|
||||
// If tight alignment is supported (checked by the code above) and wasn't disabled by the developer
|
||||
// (with ALLOCATOR_FLAG_DONT_USE_TIGHT_ALIGNMENT), disable the preference for creating small buffers as committed,
|
||||
// as if ALLOCATOR_FLAG_DONT_PREFER_SMALL_BUFFERS_COMMITTED was specified.
|
||||
if (IsTightAlignmentEnabled())
|
||||
{
|
||||
m_PreferSmallBuffersCommitted = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // #if D3D12MA_TIGHT_ALIGNMENT_SUPPORTED
|
||||
@@ -6490,6 +6647,7 @@ HRESULT AllocatorPimpl::CreateResource(
|
||||
}
|
||||
|
||||
HRESULT hr = E_NOINTERFACE;
|
||||
const bool useTightAlignment = IsTightAlignmentEnabled(*pAllocDesc);
|
||||
CREATE_RESOURCE_PARAMS finalCreateParams = createParams;
|
||||
D3D12_RESOURCE_DESC finalResourceDesc;
|
||||
#ifdef __ID3D12Device8_INTERFACE_DEFINED__
|
||||
@@ -6500,7 +6658,7 @@ HRESULT AllocatorPimpl::CreateResource(
|
||||
{
|
||||
finalResourceDesc = *createParams.GetResourceDesc();
|
||||
finalCreateParams.AccessResourceDesc() = &finalResourceDesc;
|
||||
hr = GetResourceAllocationInfo(finalResourceDesc, 0, NULL, resAllocInfo);
|
||||
hr = GetResourceAllocationInfo(finalResourceDesc, 0, NULL, resAllocInfo, useTightAlignment);
|
||||
}
|
||||
#ifdef __ID3D12Device8_INTERFACE_DEFINED__
|
||||
else if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE_AND_DESC1)
|
||||
@@ -6509,7 +6667,7 @@ HRESULT AllocatorPimpl::CreateResource(
|
||||
{
|
||||
finalResourceDesc1 = *createParams.GetResourceDesc1();
|
||||
finalCreateParams.AccessResourceDesc1() = &finalResourceDesc1;
|
||||
hr = GetResourceAllocationInfo(finalResourceDesc1, 0, NULL, resAllocInfo);
|
||||
hr = GetResourceAllocationInfo(finalResourceDesc1, 0, NULL, resAllocInfo, useTightAlignment);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -6521,7 +6679,7 @@ HRESULT AllocatorPimpl::CreateResource(
|
||||
finalResourceDesc1 = *createParams.GetResourceDesc1();
|
||||
finalCreateParams.AccessResourceDesc1() = &finalResourceDesc1;
|
||||
hr = GetResourceAllocationInfo(finalResourceDesc1,
|
||||
createParams.GetNumCastableFormats(), createParams.GetCastableFormats(), resAllocInfo);
|
||||
createParams.GetNumCastableFormats(), createParams.GetCastableFormats(), resAllocInfo, useTightAlignment);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -6547,14 +6705,14 @@ HRESULT AllocatorPimpl::CreateResource(
|
||||
if (createParams.Variant >= CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE_AND_DESC1)
|
||||
{
|
||||
hr = CalcAllocationParams<D3D12_RESOURCE_DESC1>(*pAllocDesc, resAllocInfo.SizeInBytes,
|
||||
createParams.GetResourceDesc1(),
|
||||
createParams.GetResourceDesc1(), useTightAlignment,
|
||||
blockVector, committedAllocationParams, preferCommitted);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
hr = CalcAllocationParams<D3D12_RESOURCE_DESC>(*pAllocDesc, resAllocInfo.SizeInBytes,
|
||||
createParams.GetResourceDesc(),
|
||||
createParams.GetResourceDesc(), useTightAlignment,
|
||||
blockVector, committedAllocationParams, preferCommitted);
|
||||
}
|
||||
if (FAILED(hr))
|
||||
@@ -6601,6 +6759,7 @@ HRESULT AllocatorPimpl::AllocateMemory(
|
||||
bool preferCommitted = false;
|
||||
HRESULT hr = CalcAllocationParams<D3D12_RESOURCE_DESC>(*pAllocDesc, pAllocInfo->SizeInBytes,
|
||||
NULL, // pResDesc
|
||||
false, // useTightAlignment - irrelevant for AllocateMemory
|
||||
blockVector, committedAllocationParams, preferCommitted);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
@@ -6649,7 +6808,7 @@ HRESULT AllocatorPimpl::CreateAliasingResource(
|
||||
{
|
||||
finalResourceDesc = *createParams.GetResourceDesc();
|
||||
finalCreateParams.AccessResourceDesc() = &finalResourceDesc;
|
||||
hr = GetResourceAllocationInfo(finalResourceDesc, 0, NULL, resAllocInfo);
|
||||
hr = GetResourceAllocationInfo(finalResourceDesc, 0, NULL, resAllocInfo, IsTightAlignmentEnabled());
|
||||
}
|
||||
#ifdef __ID3D12Device8_INTERFACE_DEFINED__
|
||||
else if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE_AND_DESC1)
|
||||
@@ -6658,7 +6817,7 @@ HRESULT AllocatorPimpl::CreateAliasingResource(
|
||||
{
|
||||
finalResourceDesc1 = *createParams.GetResourceDesc1();
|
||||
finalCreateParams.AccessResourceDesc1() = &finalResourceDesc1;
|
||||
hr = GetResourceAllocationInfo(finalResourceDesc1, 0, NULL, resAllocInfo);
|
||||
hr = GetResourceAllocationInfo(finalResourceDesc1, 0, NULL, resAllocInfo, IsTightAlignmentEnabled());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -6670,7 +6829,7 @@ HRESULT AllocatorPimpl::CreateAliasingResource(
|
||||
finalResourceDesc1 = *createParams.GetResourceDesc1();
|
||||
finalCreateParams.AccessResourceDesc1() = &finalResourceDesc1;
|
||||
hr = GetResourceAllocationInfo(finalResourceDesc1,
|
||||
createParams.GetNumCastableFormats(), createParams.GetCastableFormats(), resAllocInfo);
|
||||
createParams.GetNumCastableFormats(), createParams.GetCastableFormats(), resAllocInfo, IsTightAlignmentEnabled());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -7305,13 +7464,14 @@ void AllocatorPimpl::FreeStatsString(WCHAR* pStatsString)
|
||||
|
||||
template<typename D3D12_RESOURCE_DESC_T>
|
||||
bool AllocatorPimpl::PrefersCommittedAllocation(const D3D12_RESOURCE_DESC_T& resourceDesc,
|
||||
ALLOCATION_FLAGS strategy)
|
||||
ALLOCATION_FLAGS strategy, bool useTightAlignment) const
|
||||
{
|
||||
// Prefer creating small buffers <= 32 KB as committed, because drivers pack them better,
|
||||
// while placed buffers require 64 KB alignment.
|
||||
if(resourceDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER &&
|
||||
resourceDesc.Width <= D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT / 2 &&
|
||||
strategy != ALLOCATION_FLAG_STRATEGY_MIN_TIME && // Creating as committed would be slower.
|
||||
!useTightAlignment &&
|
||||
m_PreferSmallBuffersCommitted)
|
||||
{
|
||||
return true;
|
||||
@@ -7526,7 +7686,7 @@ HRESULT AllocatorPimpl::AllocateHeap(
|
||||
|
||||
template<typename D3D12_RESOURCE_DESC_T>
|
||||
HRESULT AllocatorPimpl::CalcAllocationParams(const ALLOCATION_DESC& allocDesc, UINT64 allocSize,
|
||||
const D3D12_RESOURCE_DESC_T* resDesc,
|
||||
const D3D12_RESOURCE_DESC_T* resDesc, bool useTightAlignment,
|
||||
BlockVector*& outBlockVector, CommittedAllocationParameters& outCommittedAllocationParams, bool& outPreferCommitted)
|
||||
{
|
||||
outBlockVector = NULL;
|
||||
@@ -7599,7 +7759,7 @@ HRESULT AllocatorPimpl::CalcAllocationParams(const ALLOCATION_DESC& allocDesc, U
|
||||
{
|
||||
if (resDesc->SampleDesc.Count > 1 && msaaAlwaysCommitted)
|
||||
outBlockVector = NULL;
|
||||
if (!outPreferCommitted && PrefersCommittedAllocation(*resDesc, allocDesc.Flags & ALLOCATION_FLAG_STRATEGY_MASK))
|
||||
if (!outPreferCommitted && PrefersCommittedAllocation(*resDesc, allocDesc.Flags & ALLOCATION_FLAG_STRATEGY_MASK, useTightAlignment))
|
||||
outPreferCommitted = true;
|
||||
}
|
||||
|
||||
@@ -7810,12 +7970,13 @@ template<typename D3D12_RESOURCE_DESC_T>
|
||||
HRESULT AllocatorPimpl::GetResourceAllocationInfo(
|
||||
D3D12_RESOURCE_DESC_T& inOutResourceDesc,
|
||||
UINT32 NumCastableFormats, const DXGI_FORMAT* pCastableFormats,
|
||||
D3D12_RESOURCE_ALLOCATION_INFO& outAllocInfo) const
|
||||
D3D12_RESOURCE_ALLOCATION_INFO& outAllocInfo,
|
||||
bool useTightAlignment) const
|
||||
{
|
||||
#ifdef __ID3D12Device1_INTERFACE_DEFINED__
|
||||
|
||||
#if D3D12MA_TIGHT_ALIGNMENT_SUPPORTED
|
||||
if (IsTightAlignmentEnabled())
|
||||
if (useTightAlignment)
|
||||
{
|
||||
// Don't allow USE_TIGHT_ALIGNMENT together with ALLOW_CROSS_ADAPTER as there is a D3D Debug Layer error:
|
||||
// D3D12 ERROR: ID3D12Device::GetResourceAllocationInfo: D3D12_RESOURCE_DESC::Flag D3D12_RESOURCE_FLAG_USE_TIGHT_ALIGNMENT will be ignored since D3D12_RESOURCE_FLAG_ALLOW_CROSS_ADAPTER is set. [ STATE_CREATION ERROR #599: CREATERESOURCE_INVALIDMISCFLAGS]
|
||||
@@ -7839,7 +8000,7 @@ HRESULT AllocatorPimpl::GetResourceAllocationInfo(
|
||||
*/
|
||||
if (inOutResourceDesc.Alignment == 0 &&
|
||||
inOutResourceDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER &&
|
||||
!IsTightAlignmentEnabled())
|
||||
!useTightAlignment)
|
||||
{
|
||||
outAllocInfo = {
|
||||
AlignUp<UINT64>(inOutResourceDesc.Width, D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT), // SizeInBytes
|
||||
@@ -7852,16 +8013,33 @@ HRESULT AllocatorPimpl::GetResourceAllocationInfo(
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
#if D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT
|
||||
if (inOutResourceDesc.Alignment == 0 &&
|
||||
(inOutResourceDesc.Flags & D3D12_RESOURCE_FLAG_USE_TIGHT_ALIGNMENT_COPY) == 0 &&
|
||||
(inOutResourceDesc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE1D ||
|
||||
inOutResourceDesc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D ||
|
||||
inOutResourceDesc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D) &&
|
||||
(inOutResourceDesc.Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) == 0
|
||||
bool trySmallAlignment = false;
|
||||
|
||||
if (inOutResourceDesc.Alignment == 0
|
||||
&& (inOutResourceDesc.Flags & D3D12_RESOURCE_FLAG_USE_TIGHT_ALIGNMENT_COPY) == 0
|
||||
&& inOutResourceDesc.Layout == D3D12_TEXTURE_LAYOUT_UNKNOWN
|
||||
&& ResourceDimensionIsTexture(inOutResourceDesc.Dimension))
|
||||
{
|
||||
// MSAA texture.
|
||||
if(inOutResourceDesc.SampleDesc.Count > 1)
|
||||
{
|
||||
trySmallAlignment = IsMsaa64KBAlignedTextureSupported()
|
||||
&& ResourceFlagsContainRenderTargetOrDepthStencil(inOutResourceDesc.Flags)
|
||||
&& inOutResourceDesc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D;
|
||||
}
|
||||
// Non-MSAA texture.
|
||||
else
|
||||
{
|
||||
trySmallAlignment = !ResourceFlagsContainRenderTargetOrDepthStencil(inOutResourceDesc.Flags);
|
||||
}
|
||||
}
|
||||
|
||||
#if D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT == 1
|
||||
&& CanUseSmallAlignment(inOutResourceDesc)
|
||||
if(trySmallAlignment)
|
||||
trySmallAlignment = CanUseSmallAlignment(inOutResourceDesc);
|
||||
#endif
|
||||
)
|
||||
|
||||
if (trySmallAlignment)
|
||||
{
|
||||
/*
|
||||
The algorithm here is based on Microsoft sample: "Small Resources Sample"
|
||||
@@ -7886,6 +8064,20 @@ HRESULT AllocatorPimpl::GetResourceAllocationInfo(
|
||||
inOutResourceDesc, NumCastableFormats, pCastableFormats, outAllocInfo);
|
||||
}
|
||||
|
||||
bool AllocatorPimpl::IsTightAlignmentEnabled(const ALLOCATION_DESC& allocDesc) const
|
||||
{
|
||||
if (!IsTightAlignmentEnabled())
|
||||
return false;
|
||||
|
||||
if (allocDesc.CustomPool != NULL &&
|
||||
(allocDesc.CustomPool->m_Pimpl->GetDesc().Flags & POOL_FLAG_DONT_USE_TIGHT_ALIGNMENT) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AllocatorPimpl::NewAllocationWithinBudget(D3D12_HEAP_TYPE heapType, UINT64 size)
|
||||
{
|
||||
Budget budget = {};
|
||||
|
||||
@@ -39,6 +39,197 @@ HOW TO UPDATE?
|
||||
and API updates have been a little more frequent lately. They are documented below and in imgui.cpp and should not affect all users.
|
||||
- Please report any issue!
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
VERSION 1.92.8 (Released 2026-05-12)
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.92.8
|
||||
|
||||
Breaking Changes:
|
||||
|
||||
- DrawList: swapped the last two arguments of `AddRect()`, `AddPolyline()`, `PathStroke()`.
|
||||
- Before: `void ImDrawList::AddRect(ImVec2 p_min, ImVec2 p_max, ImU32 col, float rounding = 0.0f, ImDrawFlags flags = 0, float thickness = 1.0f);`
|
||||
- After: `void ImDrawList::AddRect(ImVec2 p_min, ImVec2 p_max, ImU32 col, float rounding = 0.0f, float thickness = 1.0f, ImDrawFlags flags = 0);`
|
||||
- Before: `void ImDrawList::AddPolyline(const ImVec2* points, int num_points, ImU32 col, ImDrawFlags flags, float thickness);`
|
||||
- After: `void ImDrawList::AddPolyline(const ImVec2* points, int num_points, ImU32 col, float thickness, ImDrawFlags flags = 0);`
|
||||
- Before: `void ImDrawList::PathStroke(ImU32 col, ImDrawFlags flags = 0, float thickness = 1.0f);`
|
||||
- After: `void ImDrawList::PathStroke(ImU32 col, float thickness = 1.0f, ImDrawFlags flags = 0);`
|
||||
Added inline redirection functions when IMGUI_DISABLE_OBSOLETE_FUNCTIONS is off.
|
||||
Marked the old functions are =delete when IMGUI_DISABLE_OBSOLETE_FUNCTIONS is on, to allow for better type-checking.
|
||||
Effectively the typical call site is changing from:
|
||||
- Before: `window->DrawList->AddRect(p_min, p_max, color, rounding, ImDrawFlags_None, border_size);`
|
||||
- After: `window->DrawList->AddRect(p_min, p_max, color, rounding, border_size);`
|
||||
Notes:
|
||||
- Users of C++ and other languages with type-checking will be notified at compile-time of any mistakes.
|
||||
- Users of high-level bindings or languages with no type-checking will be notified at runtime via an assert for invalid flags value.
|
||||
If you are a binding maintainer consider doing something to facilitate transition or error detection.
|
||||
- This is perhaps the worst breaking change in our history :( but it makes ImDrawList function signatures consistent.
|
||||
As we are aiming to add flags and features to variety of ImDrawList functions, that consistency becomes more important.
|
||||
The new order is also more convenient as `flags` are less frequently used than `thickness` in real code.
|
||||
- As a general policy in Dear ImGui, all our flags default to 0 so ImDrawFlags_None was likely written 0 in some call sites.
|
||||
- Consider adding `#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS` in your imconfig.h, even temporarily, to clean up legacy code.
|
||||
- DrawList: obsoleted `ImDrawCallback_ResetRenderState` in favor of using `ImGui::GetPlatformIO().DrawCallback_ResetRenderState`,
|
||||
which is part of our new standard draw callbacks. (#9378)
|
||||
Redirecting the earlier value into the later one when set, so both old and new code should work.
|
||||
- Backends:
|
||||
- Vulkan: redesigned to use separate ImageView + Sampler instead of Combined Image Sampler. (#914)
|
||||
This change allows us to facilitate changing samplers, in line with other backends. [@yaz0r, @ocornut]
|
||||
- When creating your own descriptor pool (instead of letting backend creates its own):
|
||||
- Before: need at least `IMGUI_IMPL_VULKAN_MINIMUM_IMAGE_SAMPLER_POOL_SIZE` descriptors of type `VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER`.
|
||||
- After: need at least `IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE` descriptors of type `VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE`.
|
||||
+ `IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE` descriptors of type `VK_DESCRIPTOR_TYPE_SAMPLER`.
|
||||
- When registering custom textures: changed ImGui_ImplVulkan_AddTexture() signature to remove Sampler.
|
||||
- Before: `ImGui_ImplVulkan_AddTexture(VkSampler, VkImageView, VkImageLayout)`
|
||||
- After: `ImGui_ImplVulkan_AddTexture(VkImageView, VkImageLayout)`
|
||||
- Kept inline redirection function that ignores the sampler (will obsolete).
|
||||
- DirectX10, DirectX11, SDLGPU3, Vulkan: removed samplers from `ImGui_ImplXXXX_RenderState`.
|
||||
Prefer to use backend-agnostic DrawCallback_SetSamplerLinear which works everywhere! (#9378)
|
||||
If there is a legit need/request for them or any render state we can always add them back.
|
||||
|
||||
Other Changes:
|
||||
|
||||
- DrawList:
|
||||
- Added room in `ImGuiPlatformIO` for standard backend-agnostic draw callbacks. Those callbacks
|
||||
are setup/provided by the backend and available in most of our standard backends.
|
||||
They allow backend-agnostic code from e.g. switching to a Nearest/Point sampler without
|
||||
messing with custom Renderer-specific callbacks.
|
||||
platform_io.DrawCallback_ResetRenderState; // Request to reset the graphics/render state.
|
||||
platform_io.DrawCallback_SetSamplerLinear; // Request to set current texture sampling to Linear
|
||||
platform_io.DrawCallback_SetSamplerNearest; // Request to set current texture sampling to Nearest/Point
|
||||
Note that some backends might not support all callbacks.
|
||||
(#9378, #9371, #3590, #8926, #2973, #7485, #7468, #6969, #5118, #7616, #9173, #8322, #7230,
|
||||
#5999, #6452, #5156, #7342, #7592, #7511)
|
||||
- Made `AddCallback()` user data default to Null for convenience.
|
||||
- Added `AddLineH()`, `AddLineV()` helpers to draw horizontal and vertical lines. [@memononen]
|
||||
- InputText:
|
||||
- InputTextMultiline: fixed an issue processing deactivation logic when an active
|
||||
multi-line edit is clipped due to being out of view.
|
||||
- Fixed a crash when toggling ReadOnly while active. (#9354)
|
||||
- `CharFilter` callback event sets CursorPos/SelectionStart/SelectionEnd. (#816)
|
||||
- Tables:
|
||||
- Fixed issues reporting ideal size to parent window/container: (#9352, #7651)
|
||||
- When both scrollbars are visible but only one of ScrollX/ScrollY was explicitly requested.
|
||||
- When vertical scrollbar was not at the top, the computation was often incorrect.
|
||||
- Windows:
|
||||
- Fixed a single-axis auto-resizing feedback loop issue with nested containers
|
||||
and varying scrollbar visibility. (#9352)
|
||||
- Detect and report error when calling End() instead of EndPopup() on a popup. (#9351)
|
||||
- Child windows with only `ImGuiChildFlags_AutoResizeY` flag keep using the proportional
|
||||
default `ItemWidth`. (#9355)
|
||||
- Using mouse wheel to scroll takes and keeps ownership of the corresponding keys
|
||||
(e.g. `ImGuiKey_MouseWheelY`) while a wheeling window is locked. (#2604, #3795)
|
||||
- InputInt, InputFloat, InputScalar:
|
||||
- Reinstated `ImGuiInputTextFlags_EnterReturnsTrue` support which was removed in 1.91.4.
|
||||
(#8665, #9299, #8065, #3946, #6284, #9117)
|
||||
- Fixed the fact that it didn't return true when validating same value.
|
||||
- Fixed losing value when tabbing out or losing focus.
|
||||
- Made it that pressing +/- step buttons also return true, which is in line
|
||||
with 1.91.4 behavior.
|
||||
- In a majority of cases you should use `IsItemDeactivatedAfterEdit()` instead,
|
||||
but it still has a few edge cases flaws (to be addressed soon).
|
||||
- Allow passing a format string that does not display the scalar value.
|
||||
Parsing input with default format for the type. (#9385) [@FireFox2000000]
|
||||
- Multi-Select:
|
||||
- Fixed an issue using Multi-Select within a Table causing column width measurement to
|
||||
be invalid when trailing column contents is not submitted in the last row. (#9341, #8250)
|
||||
- Fixed an issue using Multi-Select within a Table with the right-most column visible,
|
||||
which could lead to an extra vertical offset in the Header row. (#8250)
|
||||
- Multi-Select + Box-Select:
|
||||
- Fixed an issue using `ImGuiMultiSelectFlags_BoxSelect1d` mode while scrolling.
|
||||
Notably, using mouse wheel while holding a box-selection could lead items close to windows
|
||||
edges from not being correctly unselected. (#7994, #8250, #7821, #7850, #7970)
|
||||
- Improved dirty/unclip rectangle logic for `ImGuiMultiSelectFlags_BoxSelect2d`.
|
||||
- Fixed an issue using `ImGuiMultiSelectFlags_BoxSelect2d` mode, where
|
||||
items out of view wouldn't be properly selected while scrolling while mouse cursor
|
||||
is hovering outside of selection scope. (#7994, #1861, #6518)
|
||||
- Fixed an issue where items out of horizontal view would sometimes lead
|
||||
to incorrect merging of sequential selection requests while also scrolling fast
|
||||
enough to overlap multiple rows during a frame. (#7994, #1861, #6518)
|
||||
- Fixed an issue using `ImGuiMultiSelectFlags_BoxSelect2d` mode in a Table
|
||||
while relying on the `TableNextColumn()` return value to perform coarse clipping. (#7994)
|
||||
- Disabled merging consecutive selection requests as we have no reliable
|
||||
way of detecting if user has submitted all consecutive items without clipping gaps,
|
||||
and `ImGuiSelectionUserData` is technically opaque storage. (#7994, #1861)
|
||||
(we will probably bring this back as a minor optimization if we have a way to for
|
||||
user to tell us `ImGuiSelectionUserData` are indices)
|
||||
- Fixes for using across nested child windows. (#8364)
|
||||
- Box-Select + Clipper: fixed an issue selecting items while scrolling while a clipper
|
||||
active. (#7994, #8250, #7821, #7850, #7970)
|
||||
- Box-Select + Tables: fixed an issue using box-selection in a tables with
|
||||
items straying out of columns boundaries. (#7994, #2221)
|
||||
- Box-Select + Tables: fixed an issue when calling `BeginMultiSelect()` in a table
|
||||
before layout has been locked (first row or headers row submitted). (#8250)
|
||||
- Menus:
|
||||
- BeginMenu()/MenuItem(): fixed accidental triggering of child menu items when
|
||||
opening a menu inside a small host window forcing the child menu window to be
|
||||
repositioned under the mouse cursor. (#8233, #9394)
|
||||
Done by reworking `BeginMenu()`/`MenuItem()`: they previously avoiding taking
|
||||
ActiveID + key/click ownership (in order to allow releasing button on another item).
|
||||
Now they take them and release them once the mouse is moved outside item boundaries.
|
||||
- Inputs:
|
||||
- SetItemKeyOwner(): return true if ownership has been requested, which typically
|
||||
needs to to checked for gating further tests. This is important as the function
|
||||
may fail. (#456, #2637, #2620, #2891, #3370, #3724, #4828, #5108, #5242, #5641)
|
||||
- SetItemKeyOwner(): does not set ownership is key is already taken. Effectively
|
||||
this makes using `SetItemKeyOwner(ImGuiKey_MouseWheelY)` over an item work as
|
||||
expected while not having item react if a scroll wheel is actively in progress.
|
||||
May be subject to further redesign, e.g. conditional flags. Feedback welcome!
|
||||
- Style:
|
||||
- Checkbox: added `ImGuiCol_CheckboxSelectedBg` to change or accentuate the
|
||||
background color of checked/mixed checkboxes. (#9392)
|
||||
- Added `ImGuiStyleVar_DragDropTargetRounding`. (#9056)
|
||||
- Fixed vertical scrollbar top coordinates when using thick borders on windows
|
||||
with no title bar and no menu bar. (#9366)
|
||||
- Fonts:
|
||||
- imgui_freetype: add FreeType headers & compiled version in 'About Dear ImGui' details.
|
||||
- Assert when using MergeMode with an explicit size over a target font with an implicit
|
||||
size, as scale factor are likely erroneous. (#9361)
|
||||
- Clipper:
|
||||
- Improved error reporting when misusing the clipper inside a table (prioritize
|
||||
reporting the common clipper error over a table sanity check assert). (#9350)
|
||||
- Tweaked assert triggering when first item height measurement fails, and made it
|
||||
a better recoverable error. (#9350)
|
||||
- Misc:
|
||||
- Minor optimization: reduce redundant label scanning in common widgets.
|
||||
- Added missing Test Engine hooks for `PlotLines()`, `PlotHistogram()`, `VSliderXXX()`
|
||||
and `TableHeader()` functions.
|
||||
- Demo:
|
||||
- Added simple demo for a scrollable/zoomable image viewer with a grid.
|
||||
Available in `Examples->Image Viewer` and `Widgets->Images`.
|
||||
- Backends:
|
||||
- Added support for new standardized draw callbacks in most backends: (#9378)
|
||||
- Allegro5: Reset n/a n/a
|
||||
- DX9: Reset SetSamplerLinear SetSamplerNearest
|
||||
- DX10: Reset SetSamplerLinear SetSamplerNearest
|
||||
- DX11: Reset SetSamplerLinear SetSamplerNearest
|
||||
- DX12: Reset SetSamplerLinear SetSamplerNearest
|
||||
- Metal: Reset SetSamplerLinear SetSamplerNearest
|
||||
- OpenGL2: Reset SetSamplerLinear SetSamplerNearest
|
||||
- OpenGL3+: Reset SetSamplerLinear SetSamplerNearest
|
||||
- SDLGPU3: Reset SetSamplerLinear SetSamplerNearest
|
||||
- SDLRenderer2: Reset n/a n/a
|
||||
- SDLRenderer3: Reset SetSamplerLinear SetSamplerNearest
|
||||
- Vulkan: Reset SetSamplerLinear SetSamplerNearest
|
||||
- WebGPU: Reset SetSamplerLinear SetSamplerNearest
|
||||
(Vulkan backend by @yaz0r, Metal by @ssh4net, others by @ocornut)
|
||||
- GLFW: added a Win32-specific implementation of `ImGui_ImplGlfw_GetContentScaleXXXX`
|
||||
functions for legacy GLFW 3.2. (#9003)
|
||||
- Metal: avoid redundant vertex buffer bind in `SetupRenderState()`, which leads
|
||||
to validation issue. (#9343) [@Hunam6]
|
||||
- Metal: use a dedicated `bufferCacheLock` to avoid crashing when `bufferCache` is
|
||||
replaced by a new object while being used for `@synchronize()`. (#9367) [@andygrundman]
|
||||
- SDL2: made `ImGui_ImplSDL2_GetContentScaleForWindow()`/`ImGui_ImplSDL2_GetContentScaleForDisplay()`
|
||||
helpers return a minimum of 1.0f, as some Linux setup seems to report <1.0f value
|
||||
and this breaks scaling border size. (#9369)
|
||||
- WebGPU: rework choice/detection of using WGSL/SPIR-V shader on WGVK. (#9316, #9246, #9257, #9387)
|
||||
- Examples:
|
||||
- Update VS toolset in all .vcxproj from VS2015 (v140) to VS2017 (v141). The later is the
|
||||
first that supports vcpkg. Onward we will likely stop testing building the library with VS2015.
|
||||
- GLFW+Vulkan, SDL2+Vulkan, SDL3+Vulkan, Win32+Vulkan: reworked to create a descriptor pool with:
|
||||
- `IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE` descriptors of type `VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE`.
|
||||
- `IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE` descriptors of type `VK_DESCRIPTOR_TYPE_SAMPLER`.
|
||||
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
VERSION 1.92.7 (Released 2026-04-02)
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// dear imgui, v1.92.7
|
||||
// dear imgui, v1.92.8
|
||||
// (headers)
|
||||
|
||||
// Help:
|
||||
@@ -29,8 +29,8 @@
|
||||
|
||||
// Library Version
|
||||
// (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
|
||||
#define IMGUI_VERSION "1.92.7"
|
||||
#define IMGUI_VERSION_NUM 19270
|
||||
#define IMGUI_VERSION "1.92.8"
|
||||
#define IMGUI_VERSION_NUM 19280
|
||||
#define IMGUI_HAS_TABLE // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000
|
||||
#define IMGUI_HAS_TEXTURES // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198
|
||||
|
||||
@@ -1092,10 +1092,11 @@ namespace ImGui
|
||||
// Inputs Utilities: Key/Input Ownership [BETA]
|
||||
// - One common use case would be to allow your items to disable standard inputs behaviors such
|
||||
// as Tab or Alt key handling, Mouse Wheel scrolling, etc.
|
||||
// e.g. Button(...); SetItemKeyOwner(ImGuiKey_MouseWheelY); to make hovering/activating a button disable wheel for scrolling.
|
||||
// e.g. `Button(...); if (SetItemKeyOwner(ImGuiKey_MouseWheelY)) { ... }` to make hovering/activating a button disable wheel for scrolling.
|
||||
// - Reminder ImGuiKey enum include access to mouse buttons and gamepad, so key ownership can apply to them.
|
||||
// - The return value of SetItemKeyOwner() says if ownership has been requested for the item, which is a shortcut to calling yet non-public TestKeyOwner() function.
|
||||
// - Many related features are still in imgui_internal.h. For instance, most IsKeyXXX()/IsMouseXXX() functions have an owner-id-aware version.
|
||||
IMGUI_API void SetItemKeyOwner(ImGuiKey key); // Set key owner to last item ID if it is hovered or active. Equivalent to 'if (IsItemHovered() || IsItemActive()) { SetKeyOwner(key, GetItemID());'.
|
||||
IMGUI_API bool SetItemKeyOwner(ImGuiKey key); // Set key owner to last item ID if it is hovered or active. Return true when ownership has been set. Roughly equivalent to 'if (TestKeyOwner(key, GetItemID()) && (IsItemHovered() || IsItemActive())) { SetKeyOwner(key, GetItemID());'.
|
||||
|
||||
// Inputs Utilities: Mouse
|
||||
// - To refer to a mouse button, you may use named enums in your code e.g. ImGuiMouseButton_Left, ImGuiMouseButton_Right.
|
||||
@@ -1401,7 +1402,7 @@ enum ImGuiTabBarFlags_
|
||||
ImGuiTabBarFlags_DrawSelectedOverline = 1 << 6, // Draw selected overline markers over selected tab
|
||||
|
||||
// Fitting/Resize policy
|
||||
ImGuiTabBarFlags_FittingPolicyMixed = 1 << 7, // Shrink down tabs when they don't fit, until width is style.TabMinWidthShrink, then enable scrolling buttons.
|
||||
ImGuiTabBarFlags_FittingPolicyMixed = 1 << 7, // Shrink down tabs when they don't fit, until width is style.TabMinWidthShrink, then enable scrolling. Setting TabMinWidthShrink to FLT_MAX makes this behave like ImGuiTabBarFlags_FittingPolicyScroll.
|
||||
ImGuiTabBarFlags_FittingPolicyShrink = 1 << 8, // Shrink down tabs when they don't fit
|
||||
ImGuiTabBarFlags_FittingPolicyScroll = 1 << 9, // Enable scrolling buttons when tabs don't fit
|
||||
ImGuiTabBarFlags_FittingPolicyMask_ = ImGuiTabBarFlags_FittingPolicyMixed | ImGuiTabBarFlags_FittingPolicyShrink | ImGuiTabBarFlags_FittingPolicyScroll,
|
||||
@@ -1755,6 +1756,7 @@ enum ImGuiCol_
|
||||
ImGuiCol_ScrollbarGrabHovered,
|
||||
ImGuiCol_ScrollbarGrabActive,
|
||||
ImGuiCol_CheckMark, // Checkbox tick and RadioButton circle
|
||||
ImGuiCol_CheckboxSelectedBg, // Checkbox background when Selected, otherwise use FrameBg
|
||||
ImGuiCol_SliderGrab,
|
||||
ImGuiCol_SliderGrabActive,
|
||||
ImGuiCol_Button,
|
||||
@@ -1852,6 +1854,7 @@ enum ImGuiStyleVar_
|
||||
ImGuiStyleVar_TableAngledHeadersTextAlign,// ImVec2 TableAngledHeadersTextAlign
|
||||
ImGuiStyleVar_TreeLinesSize, // float TreeLinesSize
|
||||
ImGuiStyleVar_TreeLinesRounding, // float TreeLinesRounding
|
||||
ImGuiStyleVar_DragDropTargetRounding, // float DragDropTargetRounding
|
||||
ImGuiStyleVar_ButtonTextAlign, // ImVec2 ButtonTextAlign
|
||||
ImGuiStyleVar_SelectableTextAlign, // ImVec2 SelectableTextAlign
|
||||
ImGuiStyleVar_SeparatorSize, // float SeparatorSize
|
||||
@@ -2319,7 +2322,7 @@ struct ImGuiStyle
|
||||
float TabBorderSize; // Thickness of border around tabs.
|
||||
float TabMinWidthBase; // Minimum tab width, to make tabs larger than their contents. TabBar buttons are not affected.
|
||||
float TabMinWidthShrink; // Minimum tab width after shrinking, when using ImGuiTabBarFlags_FittingPolicyMixed policy.
|
||||
float TabCloseButtonMinWidthSelected; // -1: always visible. 0.0f: visible when hovered. >0.0f: visible when hovered if minimum width.
|
||||
float TabCloseButtonMinWidthSelected; // -1: always visible. 0.0f: visible when hovered. >0.0f: visible when hovered if minimum width. FLT_MAX: never shrink, will behave like ImGuiTabBarFlags_FittingPolicyScroll.
|
||||
float TabCloseButtonMinWidthUnselected; // -1: always visible. 0.0f: visible when hovered. >0.0f: visible when hovered if minimum width. FLT_MAX: never show close button when unselected.
|
||||
float TabBarBorderSize; // Thickness of tab-bar separator, which takes on the tab active color to denote focus.
|
||||
float TabBarOverlineSize; // Thickness of tab-bar overline, which highlights the selected tab-bar.
|
||||
@@ -2328,14 +2331,14 @@ struct ImGuiStyle
|
||||
ImGuiTreeNodeFlags TreeLinesFlags; // Default way to draw lines connecting TreeNode hierarchy. ImGuiTreeNodeFlags_DrawLinesNone or ImGuiTreeNodeFlags_DrawLinesFull or ImGuiTreeNodeFlags_DrawLinesToNodes.
|
||||
float TreeLinesSize; // Thickness of outlines when using ImGuiTreeNodeFlags_DrawLines.
|
||||
float TreeLinesRounding; // Radius of lines connecting child nodes to the vertical line.
|
||||
float DragDropTargetRounding; // Radius of the drag and drop target frame.
|
||||
float DragDropTargetRounding; // Radius of the drag and drop target frame. When <0.0f: use FrameRounding.
|
||||
float DragDropTargetBorderSize; // Thickness of the drag and drop target border.
|
||||
float DragDropTargetPadding; // Size to expand the drag and drop target from actual target item size.
|
||||
float ColorMarkerSize; // Size of R/G/B/A color markers for ColorEdit4() and for Drags/Sliders when using ImGuiSliderFlags_ColorMarkers.
|
||||
ImGuiDir ColorButtonPosition; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.
|
||||
ImVec2 ButtonTextAlign; // Alignment of button text when button is larger than text. Defaults to (0.5f, 0.5f) (centered).
|
||||
ImVec2 SelectableTextAlign; // Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line.
|
||||
float SeparatorSize; // Thickness of border in Separator()
|
||||
float SeparatorSize; // Thickness of border in Separator(). Must be >= 1.0f.
|
||||
float SeparatorTextBorderSize; // Thickness of border in SeparatorText()
|
||||
ImVec2 SeparatorTextAlign; // Alignment of text within the separator. Defaults to (0.0f, 0.5f) (left aligned, center).
|
||||
ImVec2 SeparatorTextPadding; // Horizontal offset of text from each edge of the separator + spacing on other axis. Generally small values. .y is recommended to be == FramePadding.y.
|
||||
@@ -2365,7 +2368,7 @@ struct ImGuiStyle
|
||||
|
||||
// Functions
|
||||
IMGUI_API ImGuiStyle();
|
||||
IMGUI_API void ScaleAllSizes(float scale_factor); // Scale all spacing/padding/thickness values. Do not scale fonts.
|
||||
IMGUI_API void ScaleAllSizes(float scale_factor); // Scale all spacing/padding/thickness values. Do not scale fonts. See comments in definition. Consider not calling this if your initial scale factor if <1.0.
|
||||
|
||||
// Obsolete names
|
||||
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
@@ -2642,7 +2645,7 @@ struct ImGuiInputTextCallbackData
|
||||
ImGuiInputTextFlags EventFlag; // One ImGuiInputTextFlags_Callback* // Read-only
|
||||
ImGuiInputTextFlags Flags; // What user passed to InputText() // Read-only
|
||||
void* UserData; // What user passed to InputText() // Read-only
|
||||
ImGuiID ID; // Widget ID // Read-only
|
||||
ImGuiID ID; // Widget ID // Read-only
|
||||
|
||||
// Arguments for the different callback events
|
||||
// - During Resize callback, Buf will be same as your input buffer.
|
||||
@@ -2656,9 +2659,9 @@ struct ImGuiInputTextCallbackData
|
||||
char* Buf; // Text buffer // Read-write // [Resize] Can replace pointer / [Completion,History,Always] Only write to pointed data, don't replace the actual pointer!
|
||||
int BufTextLen; // Text length (in bytes) // Read-write // [Resize,Completion,History,Always] Exclude zero-terminator storage. In C land: == strlen(some_text), in C++ land: string.length()
|
||||
int BufSize; // Buffer size (in bytes) = capacity+1 // Read-only // [Resize,Completion,History,Always] Include zero-terminator storage. In C land: == ARRAYSIZE(my_char_array), in C++ land: string.capacity()+1
|
||||
int CursorPos; // // Read-write // [Completion,History,Always]
|
||||
int SelectionStart; // // Read-write // [Completion,History,Always] == to SelectionEnd when no selection
|
||||
int SelectionEnd; // // Read-write // [Completion,History,Always]
|
||||
int CursorPos; // // Read-write // [Completion,History,Always,CharFilter]
|
||||
int SelectionStart; // // Read-write // [Completion,History,Always,CharFilter] == to SelectionEnd when no selection
|
||||
int SelectionEnd; // // Read-write // [Completion,History,Always,CharFilter]
|
||||
|
||||
// Helper functions for text manipulation.
|
||||
// Use those function to benefit from the CallbackResize behaviors. Calling those function reset the selection.
|
||||
@@ -3166,12 +3169,6 @@ typedef unsigned short ImDrawIdx; // Default: 16-bit (for maximum compatibilit
|
||||
typedef void (*ImDrawCallback)(const ImDrawList* parent_list, const ImDrawCmd* cmd);
|
||||
#endif
|
||||
|
||||
// Special Draw callback value to request renderer backend to reset the graphics/render state.
|
||||
// The renderer backend needs to handle this special value, otherwise it will crash trying to call a function at this address.
|
||||
// This is useful, for example, if you submitted callbacks which you know have altered the render state and you want it to be restored.
|
||||
// Render state is not reset by default because they are many perfectly useful way of altering render state (e.g. changing shader/blending settings before an Image call).
|
||||
#define ImDrawCallback_ResetRenderState (ImDrawCallback)(-8)
|
||||
|
||||
// Typically, 1 command = 1 GPU draw call (unless command is a callback)
|
||||
// - VtxOffset: When 'io.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset' is enabled,
|
||||
// this fields allow us to render meshes larger than 64K vertices while keeping 16-bit indices.
|
||||
@@ -3245,16 +3242,15 @@ struct ImDrawListSplitter
|
||||
};
|
||||
|
||||
// Flags for ImDrawList functions
|
||||
// (Legacy: bit 0 must always correspond to ImDrawFlags_Closed to be backward compatible with old API using a bool. Bits 1..3 must be unused)
|
||||
enum ImDrawFlags_
|
||||
{
|
||||
ImDrawFlags_None = 0,
|
||||
ImDrawFlags_Closed = 1 << 0, // PathStroke(), AddPolyline(): specify that shape should be closed (Important: this is always == 1 for legacy reason)
|
||||
ImDrawFlags_RoundCornersTopLeft = 1 << 4, // AddRect(), AddRectFilled(), PathRect(): enable rounding top-left corner only (when rounding > 0.0f, we default to all corners). Was 0x01.
|
||||
ImDrawFlags_RoundCornersTopRight = 1 << 5, // AddRect(), AddRectFilled(), PathRect(): enable rounding top-right corner only (when rounding > 0.0f, we default to all corners). Was 0x02.
|
||||
ImDrawFlags_RoundCornersBottomLeft = 1 << 6, // AddRect(), AddRectFilled(), PathRect(): enable rounding bottom-left corner only (when rounding > 0.0f, we default to all corners). Was 0x04.
|
||||
ImDrawFlags_RoundCornersBottomRight = 1 << 7, // AddRect(), AddRectFilled(), PathRect(): enable rounding bottom-right corner only (when rounding > 0.0f, we default to all corners). Wax 0x08.
|
||||
ImDrawFlags_RoundCornersNone = 1 << 8, // AddRect(), AddRectFilled(), PathRect(): disable rounding on all corners (when rounding > 0.0f). This is NOT zero, NOT an implicit flag!
|
||||
ImDrawFlags_Closed = 1 << 9, // PathStroke(), AddPolyline(): specify that shape should be closed (Important: this is always == 1 for legacy reason)
|
||||
ImDrawFlags_RoundCornersTop = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight,
|
||||
ImDrawFlags_RoundCornersBottom = ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersBottomRight,
|
||||
ImDrawFlags_RoundCornersLeft = ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersTopLeft,
|
||||
@@ -3262,6 +3258,7 @@ enum ImDrawFlags_
|
||||
ImDrawFlags_RoundCornersAll = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight | ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersBottomRight,
|
||||
ImDrawFlags_RoundCornersDefault_ = ImDrawFlags_RoundCornersAll, // Default to ALL corners if none of the _RoundCornersXX flags are specified.
|
||||
ImDrawFlags_RoundCornersMask_ = ImDrawFlags_RoundCornersAll | ImDrawFlags_RoundCornersNone,
|
||||
ImDrawFlags_InvalidMask_ = (ImDrawFlags)0x8000000F,
|
||||
};
|
||||
|
||||
// Flags for ImDrawList instance. Those are set automatically by ImGui:: functions from ImGuiIO settings, and generally not manipulated directly.
|
||||
@@ -3327,7 +3324,9 @@ struct ImDrawList
|
||||
// In future versions we will use textures to provide cheaper and higher-quality circles.
|
||||
// Use AddNgon() and AddNgonFilled() functions if you need to guarantee a specific number of sides.
|
||||
IMGUI_API void AddLine(const ImVec2& p1, const ImVec2& p2, ImU32 col, float thickness = 1.0f);
|
||||
IMGUI_API void AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding = 0.0f, ImDrawFlags flags = 0, float thickness = 1.0f); // a: upper-left, b: lower-right (== upper-left + size)
|
||||
IMGUI_API void AddLineH(float min_x, float max_x, float y, ImU32 col, float thickness = 1.0f);
|
||||
IMGUI_API void AddLineV(float x, float min_y, float max_y, ImU32 col, float thickness = 1.0f);
|
||||
IMGUI_API void AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding = 0.0f, float thickness = 1.0f, ImDrawFlags flags = 0); // a: upper-left, b: lower-right (== upper-left + size)
|
||||
IMGUI_API void AddRectFilled(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding = 0.0f, ImDrawFlags flags = 0); // a: upper-left, b: lower-right (== upper-left + size)
|
||||
IMGUI_API void AddRectFilledMultiColor(const ImVec2& p_min, const ImVec2& p_max, ImU32 col_upr_left, ImU32 col_upr_right, ImU32 col_bot_right, ImU32 col_bot_left);
|
||||
IMGUI_API void AddQuad(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness = 1.0f);
|
||||
@@ -3348,7 +3347,7 @@ struct ImDrawList
|
||||
// General polygon
|
||||
// - Only simple polygons are supported by filling functions (no self-intersections, no holes).
|
||||
// - Concave polygon fill is more expensive than convex one: it has O(N^2) complexity. Provided as a convenience for the user but not used by the main library.
|
||||
IMGUI_API void AddPolyline(const ImVec2* points, int num_points, ImU32 col, ImDrawFlags flags, float thickness);
|
||||
IMGUI_API void AddPolyline(const ImVec2* points, int num_points, ImU32 col, float thickness, ImDrawFlags flags = 0);
|
||||
IMGUI_API void AddConvexPolyFilled(const ImVec2* points, int num_points, ImU32 col);
|
||||
IMGUI_API void AddConcavePolyFilled(const ImVec2* points, int num_points, ImU32 col);
|
||||
|
||||
@@ -3368,7 +3367,7 @@ struct ImDrawList
|
||||
inline void PathLineToMergeDuplicate(const ImVec2& pos) { if (_Path.Size == 0 || memcmp(&_Path.Data[_Path.Size - 1], &pos, 8) != 0) _Path.push_back(pos); }
|
||||
inline void PathFillConvex(ImU32 col) { AddConvexPolyFilled(_Path.Data, _Path.Size, col); _Path.Size = 0; }
|
||||
inline void PathFillConcave(ImU32 col) { AddConcavePolyFilled(_Path.Data, _Path.Size, col); _Path.Size = 0; }
|
||||
inline void PathStroke(ImU32 col, ImDrawFlags flags = 0, float thickness = 1.0f) { AddPolyline(_Path.Data, _Path.Size, col, flags, thickness); _Path.Size = 0; }
|
||||
inline void PathStroke(ImU32 col, float thickness = 1.0f, ImDrawFlags flags = 0) { AddPolyline(_Path.Data, _Path.Size, col, thickness, flags); _Path.Size = 0; }
|
||||
IMGUI_API void PathArcTo(const ImVec2& center, float radius, float a_min, float a_max, int num_segments = 0);
|
||||
IMGUI_API void PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12); // Use precomputed angles for a 12 steps circle
|
||||
IMGUI_API void PathEllipticalArcTo(const ImVec2& center, const ImVec2& radius, float rot, float a_min, float a_max, int num_segments = 0); // Ellipse
|
||||
@@ -3378,14 +3377,15 @@ struct ImDrawList
|
||||
|
||||
// Advanced: Draw Callbacks
|
||||
// - May be used to alter render state (change sampler, blending, current shader). May be used to emit custom rendering commands (difficult to do correctly, but possible).
|
||||
// - Use special ImDrawCallback_ResetRenderState callback to instruct backend to reset its render state to the default.
|
||||
// - Use special GetPlatformIO().DrawCallback_ResetRenderState callback to instruct backend to reset its render state to the default.
|
||||
// - See other standard callbacks in GetPlatformIO(), which may or not be supported by your backend.
|
||||
// - Your rendering loop must check for 'UserCallback' in ImDrawCmd and call the function instead of rendering triangles. All standard backends are honoring this.
|
||||
// - For some backends, the callback may access selected render-states exposed by the backend in a ImGui_ImplXXXX_RenderState structure pointed to by platform_io.Renderer_RenderState.
|
||||
// - IMPORTANT: please be mindful of the different level of indirection between using size==0 (copying argument) and using size>0 (copying pointed data into a buffer).
|
||||
// - If userdata_size == 0: we copy/store the 'userdata' argument as-is. It will be available unmodified in ImDrawCmd::UserCallbackData during render.
|
||||
// - If userdata_size > 0, we copy/store 'userdata_size' bytes pointed to by 'userdata'. We store them in a buffer stored inside the drawlist. ImDrawCmd::UserCallbackData will point inside that buffer so you have to retrieve data from there. Your callback may need to use ImDrawCmd::UserCallbackDataSize if you expect dynamically-sized data.
|
||||
// - Support for userdata_size > 0 was added in v1.91.4, October 2024. So earlier code always only allowed to copy/store a simple void*.
|
||||
IMGUI_API void AddCallback(ImDrawCallback callback, void* userdata, size_t userdata_size = 0);
|
||||
IMGUI_API void AddCallback(ImDrawCallback callback, void* userdata = NULL, size_t userdata_size = 0);
|
||||
|
||||
// Advanced: Miscellaneous
|
||||
IMGUI_API void AddDrawCmd(); // This is useful if you need to forcefully create a new draw call (to allow for dependent rendering / blending). Otherwise primitives are merged into the same draw-call as much as possible
|
||||
@@ -3415,8 +3415,15 @@ struct ImDrawList
|
||||
|
||||
// Obsolete names
|
||||
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
inline void AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding, ImDrawFlags flags, float thickness) { AddRect(p_min, p_max, col, rounding, thickness, flags); } // OBSOLETED in 1.92.8: NEW FUNCTION SIGNATURE HAS 'thickness' AND 'flags' SWAPPED.
|
||||
inline void AddPolyline(const ImVec2* points, int num_points, ImU32 col, ImDrawFlags flags, float thickness) { AddPolyline(points, num_points, col, thickness, flags); } // OBSOLETED in 1.92.8: NEW FUNCTION SIGNATURE HAS 'thickness' AND 'flags' SWAPPED.
|
||||
inline void PathStroke(ImU32 col, ImDrawFlags flags, float thickness) { PathStroke(col, thickness, flags); } // OBSOLETED in 1.92.8: NEW FUNCTION SIGNATURE HAS 'thickness' AND 'flags' SWAPPED.
|
||||
inline void PushTextureID(ImTextureRef tex_ref) { PushTexture(tex_ref); } // RENAMED in 1.92.0
|
||||
inline void PopTextureID() { PopTexture(); } // RENAMED in 1.92.0
|
||||
#else
|
||||
IMGUI_API void AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding /*= 0.0f*/, ImDrawFlags flags /*= 0*/, float thickness /*= 1.0f*/) = delete;
|
||||
IMGUI_API void AddPolyline(const ImVec2* points, int num_points, ImU32 col, ImDrawFlags flags, float thickness) = delete;
|
||||
inline void PathStroke(ImU32 col, ImDrawFlags flags /*= 0*/, float thickness /*= 1.0f*/) = delete;
|
||||
#endif
|
||||
//inline void AddEllipse(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot = 0.0f, int num_segments = 0, float thickness = 1.0f) { AddEllipse(center, ImVec2(radius_x, radius_y), col, rot, num_segments, thickness); } // OBSOLETED in 1.90.5 (Mar 2024)
|
||||
//inline void AddEllipseFilled(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot = 0.0f, int num_segments = 0) { AddEllipseFilled(center, ImVec2(radius_x, radius_y), col, rot, num_segments); } // OBSOLETED in 1.90.5 (Mar 2024)
|
||||
@@ -3528,6 +3535,7 @@ struct ImTextureData
|
||||
bool WantDestroyNextFrame; // rw - // [Internal] Queued to set ImTextureStatus_WantDestroy next frame. May still be used in the current frame.
|
||||
|
||||
// Functions
|
||||
// - If GetPixels() functions asserts while being called by your render loop, it could be caused by calling ImFontAtlas::Clear() instead of ClearFonts()?
|
||||
ImTextureData() { memset((void*)this, 0, sizeof(*this)); Status = ImTextureStatus_Destroyed; TexID = ImTextureID_Invalid; }
|
||||
~ImTextureData() { DestroyPixels(); }
|
||||
IMGUI_API void Create(ImTextureFormat format, int w, int h);
|
||||
@@ -3684,13 +3692,13 @@ struct ImFontAtlas
|
||||
IMGUI_API ImFont* AddFontFromMemoryCompressedBase85TTF(const char* compressed_font_data_base85, float size_pixels = 0.0f, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_font_data_base85' still owned by caller. Compress with binary_to_compressed_c.cpp with -base85 parameter.
|
||||
IMGUI_API void RemoveFont(ImFont* font);
|
||||
|
||||
IMGUI_API void Clear(); // Clear everything (input fonts, output glyphs/textures).
|
||||
IMGUI_API void Clear(); // Clear everything (fonts + textures). Don't call mid-frame!
|
||||
IMGUI_API void ClearFonts(); // Clear input+output font data/glyphs. You can call this mid-frame if you load new fonts afterwards!
|
||||
IMGUI_API void CompactCache(); // Compact cached glyphs and texture.
|
||||
IMGUI_API void SetFontLoader(const ImFontLoader* font_loader); // Change font loader at runtime.
|
||||
|
||||
// As we are transitioning toward a new font system, we expect to obsolete those soon:
|
||||
IMGUI_API void ClearInputData(); // [OBSOLETE] Clear input data (all ImFontConfig structures including sizes, TTF data, glyph ranges, etc.) = all the data used to build the texture and fonts.
|
||||
IMGUI_API void ClearFonts(); // [OBSOLETE] Clear input+output font data (same as ClearInputData() + glyphs storage, UV coordinates).
|
||||
IMGUI_API void ClearTexData(); // [OBSOLETE] Clear CPU-side copy of the texture data. Saves RAM once the texture has been copied to graphics memory.
|
||||
|
||||
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
@@ -3862,6 +3870,7 @@ enum ImFontFlags_
|
||||
ImFontFlags_NoLoadError = 1 << 1, // Disable throwing an error/assert when calling AddFontXXX() with missing file/data. Calling code is expected to check AddFontXXX() return value.
|
||||
ImFontFlags_NoLoadGlyphs = 1 << 2, // [Internal] Disable loading new glyphs.
|
||||
ImFontFlags_LockBakedSizes = 1 << 3, // [Internal] Disable loading new baked sizes, disable garbage collecting current ones. e.g. if you want to lock a font to a single size. Important: if you use this to preload given sizes, consider the possibility of multiple font density used on Retina display.
|
||||
ImFontFlags_ImplicitRefSize = 1 << 4, // [Internal] Reference size was not set explicitly.
|
||||
};
|
||||
|
||||
// Font runtime data and rendering
|
||||
@@ -3876,7 +3885,7 @@ struct ImFont
|
||||
ImFontAtlas* OwnerAtlas; // 4-8 // What we have been loaded into.
|
||||
ImFontFlags Flags; // 4 // Font flags.
|
||||
float CurrentRasterizerDensity; // Current rasterizer density. This is a varying state of the font.
|
||||
float LineHeight; // 4 // Line Height
|
||||
float LineHeight; // 4 // Line Height
|
||||
|
||||
// [Internal] Members: Cold ~24-52 bytes
|
||||
// Conceptually Sources[] is the list of font sources merged to create this font.
|
||||
@@ -4023,6 +4032,12 @@ struct ImGuiPlatformIO
|
||||
// Written by some backends during ImGui_ImplXXXX_RenderDrawData() call to point backend_specific ImGui_ImplXXXX_RenderState* structure.
|
||||
void* Renderer_RenderState;
|
||||
|
||||
// Standard draw callbacks provided by renderer backend.
|
||||
ImDrawCallback DrawCallback_ResetRenderState; // Request to reset the graphics/render state.
|
||||
ImDrawCallback DrawCallback_SetSamplerLinear; // Request backend to set texture sampling to Linear.
|
||||
ImDrawCallback DrawCallback_SetSamplerNearest; // Request backend to set texture sampling to Nearest/Point.
|
||||
//ImDrawCallback DrawCallback_SetSamplerCustom; // Request backend to set texture sampling using Backend Specific data.
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// Output
|
||||
//------------------------------------------------------------------
|
||||
@@ -4151,6 +4166,8 @@ namespace ImGui
|
||||
//static inline void SetScrollPosHere() { SetScrollHere(); } // OBSOLETED in 1.42
|
||||
}
|
||||
|
||||
#define ImDrawCallback_ResetRenderState (ImDrawCallback)(-8) // OBSOLETED in 1.92.8: Use ImGui::GetPlatformIO().DrawCallback_ResetRenderState
|
||||
|
||||
//-- OBSOLETED in 1.92.0: ImFontAtlasCustomRect becomes ImTextureRect
|
||||
// - ImFontAtlasCustomRect::X,Y --> ImTextureRect::x,y
|
||||
// - ImFontAtlasCustomRect::Width,Height --> ImTextureRect::w,h
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// dear imgui, v1.92.7
|
||||
// dear imgui, v1.92.8
|
||||
// (internal structures/api)
|
||||
|
||||
// You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility.
|
||||
@@ -252,6 +252,9 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer
|
||||
#define IMGUI_DEBUG_LOG_FONT(...) do { ImGuiContext* g2 = GImGui; if (g2 && g2->DebugLogFlags & ImGuiDebugLogFlags_EventFont) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) // Called from ImFontAtlas function which may operate without a context.
|
||||
#define IMGUI_DEBUG_LOG_INPUTROUTING(...) do{if (g.DebugLogFlags & ImGuiDebugLogFlags_EventInputRouting)IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
|
||||
|
||||
// Debug options (also see ones on top of imgui.cpp)
|
||||
//#define IMGUI_DEBUG_BOXSELECT
|
||||
|
||||
// Static Asserts
|
||||
#define IM_STATIC_ASSERT(_COND) static_assert(_COND, "")
|
||||
|
||||
@@ -506,7 +509,8 @@ inline double ImRsqrt(double x) { return 1.0 / sqrt(x); }
|
||||
template<typename T> T ImMin(T lhs, T rhs) { return lhs < rhs ? lhs : rhs; }
|
||||
template<typename T> T ImMax(T lhs, T rhs) { return lhs >= rhs ? lhs : rhs; }
|
||||
template<typename T> T ImClamp(T v, T mn, T mx) { return (v < mn) ? mn : (v > mx) ? mx : v; }
|
||||
template<typename T> T ImLerp(T a, T b, float t) { return (T)(a + (b - a) * t); }
|
||||
template<typename T> T ImLerp(double a, double b, float t) { return (T)(a + (b - a) * (double)t); }
|
||||
template<typename T> T ImLerp(T a, T b, float t) { return (T)((float)a + (float)(b - a) * t); }
|
||||
template<typename T> void ImSwap(T& a, T& b) { T tmp = a; a = b; b = tmp; }
|
||||
template<typename T> T ImAddClampOverflow(T a, T b, T mn, T mx) { if (b < 0 && (a < mn - b)) return mn; if (b > 0 && (a > mx - b)) return mx; return a + b; }
|
||||
template<typename T> T ImSubClampOverflow(T a, T b, T mn, T mx) { if (b > 0 && (a < mn + b)) return mn; if (b < 0 && (a > mx + b)) return mx; return a - b; }
|
||||
@@ -603,6 +607,8 @@ struct IMGUI_API ImRect
|
||||
bool Overlaps(const ImRect& r) const { return r.Min.y < Max.y && r.Max.y > Min.y && r.Min.x < Max.x && r.Max.x > Min.x; }
|
||||
void Add(const ImVec2& p) { if (Min.x > p.x) Min.x = p.x; if (Min.y > p.y) Min.y = p.y; if (Max.x < p.x) Max.x = p.x; if (Max.y < p.y) Max.y = p.y; }
|
||||
void Add(const ImRect& r) { if (Min.x > r.Min.x) Min.x = r.Min.x; if (Min.y > r.Min.y) Min.y = r.Min.y; if (Max.x < r.Max.x) Max.x = r.Max.x; if (Max.y < r.Max.y) Max.y = r.Max.y; }
|
||||
void AddX(float x) { if (Min.x > x) Min.x = x; if (Max.x < x) Max.x = x; }
|
||||
void AddY(float y) { if (Min.y > y) Min.y = y; if (Max.y < y) Max.y = y; }
|
||||
void Expand(const float amount) { Min.x -= amount; Min.y -= amount; Max.x += amount; Max.y += amount; }
|
||||
void Expand(const ImVec2& amount) { Min.x -= amount.x; Min.y -= amount.y; Max.x += amount.x; Max.y += amount.y; }
|
||||
void Translate(const ImVec2& d) { Min.x += d.x; Min.y += d.y; Max.x += d.x; Max.y += d.y; }
|
||||
@@ -999,6 +1005,7 @@ enum ImGuiItemStatusFlags_
|
||||
ImGuiItemStatusFlags_HasClipRect = 1 << 9, // g.LastItemData.ClipRect is valid.
|
||||
ImGuiItemStatusFlags_HasShortcut = 1 << 10, // g.LastItemData.Shortcut valid. Set by SetNextItemShortcut() -> ItemAdd().
|
||||
//ImGuiItemStatusFlags_FocusedByTabbing = 1 << 8, // Removed IN 1.90.1 (Dec 2023). The trigger is part of g.NavActivateId. See commit 54c1bdeceb.
|
||||
ImGuiItemStatusFlags_EditedInternal = 1 << 11, // Similar to ImGuiItemStatusFlags_Edited but bypassing ImGuiItemFlags_NoMarkEdited.
|
||||
|
||||
// Additional status + semantic for ImGuiTestEngine
|
||||
#ifdef IMGUI_ENABLE_TEST_ENGINE
|
||||
@@ -1204,6 +1211,7 @@ struct IMGUI_API ImGuiMenuColumns
|
||||
};
|
||||
|
||||
// Internal temporary state for deactivating InputText() instances.
|
||||
// Store as part of ImGuiDeactivatedItemData?
|
||||
struct IMGUI_API ImGuiInputTextDeactivatedState
|
||||
{
|
||||
ImGuiID ID; // widget id owning the text state (which just got deactivated)
|
||||
@@ -1449,6 +1457,7 @@ struct ImGuiPtrOrIndex
|
||||
};
|
||||
|
||||
// Data used by IsItemDeactivated()/IsItemDeactivatedAfterEdit() functions
|
||||
// Also see ImGuiInputTextDeactivatedState which is an extension for this for InputText()
|
||||
struct ImGuiDeactivatedItemData
|
||||
{
|
||||
ImGuiID ID;
|
||||
@@ -1890,6 +1899,7 @@ struct ImGuiBoxSelectState
|
||||
// Temporary/Transient data
|
||||
bool UnclipMode; // (Temp/Transient, here in hot area). Set/cleared by the BeginMultiSelect()/EndMultiSelect() owning active box-select.
|
||||
ImRect UnclipRect; // Rectangle where ItemAdd() clipping may be temporarily disabled. Need support by multi-select supporting widgets.
|
||||
ImRect UnclipRects[2]; // Per-axis versions.
|
||||
ImRect BoxSelectRectPrev; // Selection rectangle in absolute coordinates (derived every frame from BoxSelectStartPosRel and MousePos)
|
||||
ImRect BoxSelectRectCurr;
|
||||
|
||||
@@ -1912,7 +1922,8 @@ struct IMGUI_API ImGuiMultiSelectTempData
|
||||
ImGuiMultiSelectFlags Flags;
|
||||
ImVec2 ScopeRectMin;
|
||||
ImVec2 BackupCursorMaxPos;
|
||||
ImGuiSelectionUserData LastSubmittedItem; // Copy of last submitted item data, used to merge output ranges.
|
||||
//ImGuiSelectionUserData CurrSubmittedItem; // Copy of last submitted item data, used to merge output ranges.
|
||||
//ImGuiSelectionUserData PrevSubmittedItem; // Copy of previous submitted item data, used to merge output ranges.
|
||||
ImGuiID BoxSelectId;
|
||||
ImGuiKeyChord KeyMods;
|
||||
ImS8 LoopRequestSetAll; // -1: no operation, 0: clear all, 1: select all.
|
||||
@@ -2213,6 +2224,7 @@ struct ImGuiContext
|
||||
float CurrentDpiScale; // Current window/viewport DpiScale == CurrentViewport->DpiScale
|
||||
ImDrawListSharedData DrawListSharedData;
|
||||
ImGuiID WithinEndChildID; // Set within EndChild()
|
||||
ImGuiID WithinEndPopupID; // Set within EndPopup()
|
||||
void* TestEngine; // Test engine user data
|
||||
|
||||
// Inputs
|
||||
@@ -3205,7 +3217,7 @@ namespace ImGui
|
||||
IMGUI_API void SetNextWindowRefreshPolicy(ImGuiWindowRefreshFlags flags);
|
||||
|
||||
// Fonts, drawing
|
||||
IMGUI_API void RegisterUserTexture(ImTextureData* tex); // Register external texture. EXPERIMENTAL: DO NOT USE YET.
|
||||
IMGUI_API void RegisterUserTexture(ImTextureData* tex); // Register external texture. EXPERIMENTAL.
|
||||
IMGUI_API void UnregisterUserTexture(ImTextureData* tex);
|
||||
IMGUI_API void RegisterFontAtlas(ImFontAtlas* atlas);
|
||||
IMGUI_API void UnregisterFontAtlas(ImFontAtlas* atlas);
|
||||
@@ -3320,6 +3332,7 @@ namespace ImGui
|
||||
|
||||
// Childs
|
||||
IMGUI_API bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags);
|
||||
IMGUI_API ImGuiWindow* FindFrontMostVisibleChildWindow(ImGuiWindow* window);
|
||||
|
||||
// Popups, Modals
|
||||
IMGUI_API bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_window_flags);
|
||||
@@ -3425,7 +3438,7 @@ namespace ImGui
|
||||
IMGUI_API ImGuiID GetKeyOwner(ImGuiKey key);
|
||||
IMGUI_API void SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags = 0);
|
||||
IMGUI_API void SetKeyOwnersForKeyChord(ImGuiKeyChord key, ImGuiID owner_id, ImGuiInputFlags flags = 0);
|
||||
IMGUI_API void SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags); // Set key owner to last item if it is hovered or active. Equivalent to 'if (IsItemHovered() || IsItemActive()) { SetKeyOwner(key, GetItemID());'.
|
||||
IMGUI_API bool SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags);
|
||||
IMGUI_API bool TestKeyOwner(ImGuiKey key, ImGuiID owner_id); // Test that key is either not owned, either owned by 'owner_id'
|
||||
inline ImGuiKeyOwnerData* GetKeyOwnerData(ImGuiContext* ctx, ImGuiKey key) { if (key & ImGuiMod_Mask_) key = ConvertSingleModFlagToKey(key); IM_ASSERT(IsNamedKey(key)); return &ctx->KeysOwnerData[key - ImGuiKey_NamedKey_BEGIN]; }
|
||||
|
||||
@@ -3473,6 +3486,7 @@ namespace ImGui
|
||||
// We don't use the ID Stack for this as it is common to want them separate.
|
||||
IMGUI_API void PushFocusScope(ImGuiID id);
|
||||
IMGUI_API void PopFocusScope();
|
||||
IMGUI_API bool IsInNavFocusRoute(ImGuiID focus_scope_id);
|
||||
inline ImGuiID GetCurrentFocusScope() { ImGuiContext& g = *GImGui; return g.CurrentFocusScopeId; } // Focus scope we are outputting into, set by PushFocusScope()
|
||||
|
||||
// Drag and Drop
|
||||
@@ -3482,7 +3496,7 @@ namespace ImGui
|
||||
IMGUI_API void ClearDragDrop();
|
||||
IMGUI_API bool IsDragDropPayloadBeingAccepted();
|
||||
IMGUI_API void RenderDragDropTargetRectForItem(const ImRect& bb);
|
||||
IMGUI_API void RenderDragDropTargetRectEx(ImDrawList* draw_list, const ImRect& bb);
|
||||
IMGUI_API void RenderDragDropTargetRectEx(ImDrawList* draw_list, const ImRect& bb, float rounding);
|
||||
|
||||
// Typing-Select API
|
||||
// (provide Windows Explorer style "select items by typing partial name" + "cycle through items by typing same letter" feature)
|
||||
@@ -3539,6 +3553,7 @@ namespace ImGui
|
||||
IMGUI_API void TableUpdateLayout(ImGuiTable* table);
|
||||
IMGUI_API void TableUpdateBorders(ImGuiTable* table);
|
||||
IMGUI_API void TableUpdateColumnsWeightFromWidth(ImGuiTable* table);
|
||||
IMGUI_API void TableApplyExternalUnclipRect(ImGuiTable* table, ImRect& rect);
|
||||
IMGUI_API void TableDrawBorders(ImGuiTable* table);
|
||||
IMGUI_API void TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags_for_section_to_display);
|
||||
IMGUI_API bool TableBeginContextMenuPopup(ImGuiTable* table);
|
||||
@@ -3964,6 +3979,7 @@ IMGUI_API void ImFontAtlasTextureBlockFill(ImTextureData* dst_tex,
|
||||
IMGUI_API void ImFontAtlasTextureBlockCopy(ImTextureData* src_tex, int src_x, int src_y, ImTextureData* dst_tex, int dst_x, int dst_y, int w, int h);
|
||||
IMGUI_API void ImFontAtlasTextureBlockQueueUpload(ImFontAtlas* atlas, ImTextureData* tex, int x, int y, int w, int h);
|
||||
|
||||
IMGUI_API void ImTextureDataQueueUpload(ImTextureData* tex, int x, int y, int w, int h);
|
||||
IMGUI_API int ImTextureDataGetFormatBytesPerPixel(ImTextureFormat format);
|
||||
IMGUI_API const char* ImTextureDataGetStatusName(ImTextureStatus status);
|
||||
IMGUI_API const char* ImTextureDataGetFormatName(ImTextureFormat format);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// dear imgui, v1.92.7
|
||||
// dear imgui, v1.92.8
|
||||
// (main code and documentation)
|
||||
|
||||
// Help:
|
||||
@@ -395,6 +395,31 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures:
|
||||
When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files.
|
||||
You can read releases logs https://github.com/ocornut/imgui/releases for more details.
|
||||
|
||||
- 2026/05/07 (1.92.8) - DrawList: swapped the last two arguments of AddRect(), AddPolyline(), PathStroke().
|
||||
- Before: void ImDrawList::AddRect(ImVec2 p_min, ImVec2 p_max, ImU32 col, float rounding = 0.0f, ImDrawFlags flags = 0, float thickness = 1.0f);
|
||||
- After: void ImDrawList::AddRect(ImVec2 p_min, ImVec2 p_max, ImU32 col, float rounding = 0.0f, float thickness = 1.0f, ImDrawFlags flags = 0);
|
||||
- Before: void ImDrawList::AddPolyline(const ImVec2* points, int num_points, ImU32 col, ImDrawFlags flags, float thickness);
|
||||
- After: void ImDrawList::AddPolyline(const ImVec2* points, int num_points, ImU32 col, float thickness, ImDrawFlags flags = 0);
|
||||
- Before: void ImDrawList::PathStroke(ImU32 col, ImDrawFlags flags = 0, float thickness = 1.0f);
|
||||
- After: void ImDrawList::PathStroke(ImU32 col, float thickness = 1.0f, ImDrawFlags flags = 0);
|
||||
Added inline redirection functions when IMGUI_DISABLE_OBSOLETE_FUNCTIONS is off.
|
||||
Marked the old functions are =delete when IMGUI_DISABLE_OBSOLETE_FUNCTIONS is on, to allow for better type-checking.
|
||||
Effectively the typical call site is changing from:
|
||||
- Before: window->DrawList->AddRect(p_min, p_max, color, rounding, ImDrawFlags_None, border_size);
|
||||
- After: window->DrawList->AddRect(p_min, p_max, color, rounding, border_size);
|
||||
Notes:
|
||||
- Users of C++ and other languages with type-checking will be notified at compile-time of any mistakes.
|
||||
- Users of high-level bindings or languages with no type-checking will be notified at runtime via an assert for invalid flags value.
|
||||
If you are a binding maintainer consider doing something to facilitate transition or error detection.
|
||||
- This is perhaps the worst breaking change in our history :( but it makes ImDrawList function signatures consistent.
|
||||
As we are aiming to add flags and features to variety of ImDrawList functions, that consistency becomes more important.
|
||||
The new order is also more convenient as `flags` are less frequently used than `thickness` in real code.
|
||||
- As a general policy in Dear ImGui, all our flags default to 0 so ImDrawFlags_None was likely written 0 in some call sites.
|
||||
- Consider adding `#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS` in your imconfig.h, even temporarily, to clean up legacy code.
|
||||
- 2026/04/23 (1.92.8) - DrawList: obsoleted `ImDrawCallback_ResetRenderState` in favor of using `ImGui::GetPlatformIO().DrawCallback_ResetRenderState`, which is part of our new standard draw callbacks. (#9378)
|
||||
- 2026/04/22 (1.92.8) - Backends: Vulkan: redesigned to use separate ImageView + Sampler instead of Combined Image Sampler.
|
||||
- When registering custom textures: changed ImGui_ImplVulkan_AddTexture() signature to remove Sampler.
|
||||
- When creating your own descriptor pool (instead of letting backend creates its own): need at least IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE descriptors of type VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE + IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE descriptors of type VK_DESCRIPTOR_TYPE_SAMPLER.
|
||||
- 2026/03/19 (1.92.7) - MultiSelect: renamed ImGuiMultiSelectFlags_SelectOnClick to ImGuiMultiSelectFlags_SelectOnAuto.
|
||||
- 2026/02/26 (1.92.7) - Separator: fixed a legacy quirk where Separator() was submitting a zero-height item for layout purpose, even though it draws a 1-pixel separator.
|
||||
The fix could affect code e.g. computing height from multiple widgets in order to allocate vertical space for a footer or multi-line status bar. (#2657, #9263)
|
||||
@@ -1495,7 +1520,7 @@ ImGuiStyle::ImGuiStyle()
|
||||
TabRounding = 5.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
|
||||
TabBorderSize = 0.0f; // Thickness of border around tabs.
|
||||
TabMinWidthBase = 1.0f; // Minimum tab width, to make tabs larger than their contents. TabBar buttons are not affected.
|
||||
TabMinWidthShrink = 80.0f; // Minimum tab width after shrinking, when using ImGuiTabBarFlags_FittingPolicyMixed policy.
|
||||
TabMinWidthShrink = 80.0f; // Minimum tab width after shrinking, when using ImGuiTabBarFlags_FittingPolicyMixed policy. FLT_MAX: never shrink, will behave like ImGuiTabBarFlags_FittingPolicyScroll.
|
||||
TabCloseButtonMinWidthSelected = -1.0f; // -1: always visible. 0.0f: visible when hovered. >0.0f: visible when hovered if minimum width.
|
||||
TabCloseButtonMinWidthUnselected = 0.0f; // -1: always visible. 0.0f: visible when hovered. >0.0f: visible when hovered if minimum width. FLT_MAX: never show close button when unselected.
|
||||
TabBarBorderSize = 1.0f; // Thickness of tab-bar separator, which takes on the tab active color to denote focus.
|
||||
@@ -1542,6 +1567,7 @@ ImGuiStyle::ImGuiStyle()
|
||||
|
||||
|
||||
// Scale all spacing/padding/thickness values. Do not scale fonts.
|
||||
// Consider not calling this if your initial scale factor if <1.0.
|
||||
// Important: This operation is lossy because we round all sizes to integer. If you need to change your scale multiples, call this over a freshly initialized ImGuiStyle structure rather than scaling multiple times.
|
||||
void ImGuiStyle::ScaleAllSizes(float scale_factor)
|
||||
{
|
||||
@@ -3387,9 +3413,6 @@ static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper)
|
||||
if (clipper->ItemsHeight <= 0.0f)
|
||||
{
|
||||
IM_ASSERT(data->StepNo == 1);
|
||||
if (table)
|
||||
IM_ASSERT(table->RowPosY1 == clipper->StartPosY && table->RowPosY2 == window->DC.CursorPos.y);
|
||||
|
||||
bool affected_by_floating_point_precision = ImIsFloatAboveGuaranteedIntegerPrecision((float)clipper->StartPosY) || ImIsFloatAboveGuaranteedIntegerPrecision(window->DC.CursorPos.y);
|
||||
if (affected_by_floating_point_precision)
|
||||
{
|
||||
@@ -3403,7 +3426,14 @@ static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper)
|
||||
}
|
||||
if (clipper->ItemsHeight == 0.0f && clipper->ItemsCount == INT_MAX) // Accept that no item have been submitted if in indeterminate mode.
|
||||
return false;
|
||||
IM_ASSERT(clipper->ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!");
|
||||
if (clipper->ItemsHeight <= 0.0f)
|
||||
{
|
||||
IM_ASSERT_USER_ERROR(clipper->ItemsHeight > 0.0f, "ImGuiListClipper: Failed to calculate item height! First item hasn't been submitted by user code, or has not moved the cursor vertically!");
|
||||
return false;
|
||||
}
|
||||
if (table)
|
||||
IM_ASSERT(table->RowPosY1 == clipper->StartPosY && table->RowPosY2 == window->DC.CursorPos.y);
|
||||
|
||||
calc_clipping = true; // If item height had to be calculated, calculate clipping afterwards.
|
||||
}
|
||||
|
||||
@@ -3449,12 +3479,14 @@ static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper)
|
||||
// FIXME: Selectable() use of half-ItemSpacing isn't consistent in matter of layout, as ItemAdd(bb) stray above ItemSize()'s CursorPos.
|
||||
// RangeSelect's BoxSelect relies on comparing overlap of previous and current rectangle and is sensitive to that.
|
||||
// As a workaround we currently half ItemSpacing worth on each side.
|
||||
min_y -= g.Style.ItemSpacing.y;
|
||||
max_y += g.Style.ItemSpacing.y;
|
||||
float pad_y = g.Style.ItemSpacing.y;
|
||||
min_y -= pad_y;
|
||||
max_y += pad_y;
|
||||
|
||||
// Box-select on 2D area requires different clipping.
|
||||
// (best adding pad_y here than in BeginBoxSelect() as we are closer to current state)
|
||||
if (bs->UnclipMode)
|
||||
data->Ranges.push_back(ImGuiListClipperRange::FromPositions(bs->UnclipRect.Min.y, bs->UnclipRect.Max.y, 0, 0));
|
||||
data->Ranges.push_back(ImGuiListClipperRange::FromPositions(bs->UnclipRect.Min.y - pad_y, bs->UnclipRect.Max.y + pad_y, 0, 0));
|
||||
}
|
||||
|
||||
// Add main visible range
|
||||
@@ -3648,6 +3680,7 @@ static const ImGuiStyleVarInfo GStyleVarsInfo[] =
|
||||
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TableAngledHeadersTextAlign)},// ImGuiStyleVar_TableAngledHeadersTextAlign
|
||||
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TreeLinesSize)}, // ImGuiStyleVar_TreeLinesSize
|
||||
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TreeLinesRounding)}, // ImGuiStyleVar_TreeLinesRounding
|
||||
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, DragDropTargetRounding)}, // ImGuiStyleVar_DragDropTargetRounding
|
||||
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign
|
||||
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign
|
||||
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SeparatorSize)}, // ImGuiStyleVar_SeparatorSize
|
||||
@@ -3748,6 +3781,7 @@ const char* ImGui::GetStyleColorName(ImGuiCol idx)
|
||||
case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
|
||||
case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
|
||||
case ImGuiCol_CheckMark: return "CheckMark";
|
||||
case ImGuiCol_CheckboxSelectedBg: return "CheckboxSelectedBg";
|
||||
case ImGuiCol_SliderGrab: return "SliderGrab";
|
||||
case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
|
||||
case ImGuiCol_Button: return "Button";
|
||||
@@ -3828,7 +3862,7 @@ void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool
|
||||
else
|
||||
{
|
||||
if (!text_end)
|
||||
text_end = text + ImStrlen(text); // FIXME-OPT
|
||||
text_end = text + ImStrlen(text); // FIXME-OPT (not reached by our internal calls)
|
||||
text_display_end = text_end;
|
||||
}
|
||||
|
||||
@@ -3846,7 +3880,7 @@ void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end
|
||||
ImGuiWindow* window = g.CurrentWindow;
|
||||
|
||||
if (!text_end)
|
||||
text_end = text + ImStrlen(text); // FIXME-OPT
|
||||
text_end = text + ImStrlen(text); // FIXME-OPT (not reached by our internal calls)
|
||||
|
||||
if (text != text_end)
|
||||
{
|
||||
@@ -3915,8 +3949,8 @@ void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, con
|
||||
text_end_full = FindRenderedTextEnd(text);
|
||||
const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end_full, false, 0.0f);
|
||||
|
||||
//draw_list->AddLine(ImVec2(pos_max.x, pos_min.y - 4), ImVec2(pos_max.x, pos_max.y + 6), IM_COL32(0, 0, 255, 255));
|
||||
//draw_list->AddLine(ImVec2(ellipsis_max_x, pos_min.y - 2), ImVec2(ellipsis_max_x, pos_max.y + 3), IM_COL32(0, 255, 0, 255));
|
||||
//draw_list->AddLineV(pos_max.x, pos_min.y - 4, pos_max.y + 6, IM_COL32(0, 0, 255, 255));
|
||||
//draw_list->AddLineV(ellipsis_max_x, pos_min.y - 2, pos_max.y + 3, IM_COL32(0, 255, 0, 255));
|
||||
|
||||
// FIXME: We could technically remove (last_glyph->AdvanceX - last_glyph->X1) from text_size.x here and save a few pixels.
|
||||
if (text_size.x > pos_max.x - pos_min.x)
|
||||
@@ -3961,8 +3995,8 @@ void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool borders
|
||||
const float border_size = g.Style.FrameBorderSize;
|
||||
if (borders && border_size > 0.0f)
|
||||
{
|
||||
window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size);
|
||||
window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size);
|
||||
window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, border_size);
|
||||
window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, border_size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3973,8 +4007,8 @@ void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
|
||||
const float border_size = g.Style.FrameBorderSize;
|
||||
if (border_size > 0.0f)
|
||||
{
|
||||
window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size);
|
||||
window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size);
|
||||
window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, border_size);
|
||||
window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, border_size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4009,7 +4043,7 @@ void ImGui::RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFl
|
||||
const float thickness = 2.0f;
|
||||
if (flags & ImGuiNavRenderCursorFlags_Compact)
|
||||
{
|
||||
window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavCursor), rounding, 0, thickness);
|
||||
window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavCursor), rounding, thickness);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -4018,7 +4052,7 @@ void ImGui::RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFl
|
||||
bool fully_visible = window->ClipRect.Contains(display_rect);
|
||||
if (!fully_visible)
|
||||
window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);
|
||||
window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavCursor), rounding, 0, thickness);
|
||||
window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavCursor), rounding, thickness);
|
||||
if (!fully_visible)
|
||||
window->DrawList->PopClipRect();
|
||||
}
|
||||
@@ -4052,7 +4086,7 @@ void ImGui::RenderMouseCursor(ImVec2 base_pos, float base_scale, ImGuiMouseCurso
|
||||
float a_min = ImFmod((float)g.Time * 5.0f, 2.0f * IM_PI);
|
||||
float a_max = a_min + IM_PI * 1.65f;
|
||||
draw_list->PathArcTo(pos + ImVec2(14, -1) * scale, 6.0f * scale, a_min, a_max);
|
||||
draw_list->PathStroke(col_fill, ImDrawFlags_None, 3.0f * scale);
|
||||
draw_list->PathStroke(col_fill, 3.0f * scale);
|
||||
}
|
||||
draw_list->PopTexture();
|
||||
}
|
||||
@@ -4150,7 +4184,7 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas)
|
||||
IO.Fonts = shared_font_atlas ? shared_font_atlas : IM_NEW(ImFontAtlas)();
|
||||
if (shared_font_atlas == NULL)
|
||||
IO.Fonts->OwnerContext = this;
|
||||
WithinEndChildID = 0;
|
||||
WithinEndChildID = WithinEndPopupID = 0;
|
||||
TestEngine = NULL;
|
||||
|
||||
InputEventsNextMouseSource = ImGuiMouseSource_Mouse;
|
||||
@@ -4686,7 +4720,8 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
|
||||
g.ActiveIdIsJustActivated = (g.ActiveId != id);
|
||||
if (g.ActiveIdIsJustActivated)
|
||||
{
|
||||
IMGUI_DEBUG_LOG_ACTIVEID("SetActiveID() old:0x%08X (window \"%s\") -> new:0x%08X (window \"%s\")\n", g.ActiveId, g.ActiveIdWindow ? g.ActiveIdWindow->Name : "", id, window ? window->Name : "");
|
||||
IMGUI_DEBUG_LOG_ACTIVEID("SetActiveID() 0x%08X in \"%s\"%*s(previously 0x%08X in \"%s\")\n", id, window ? window->Name : "",
|
||||
ImMax(0, 20 - (int)(window ? strlen(window->Name) : 0)), "", g.ActiveId, g.ActiveIdWindow ? g.ActiveIdWindow->Name : "");
|
||||
g.ActiveIdTimer = 0.0f;
|
||||
g.ActiveIdHasBeenPressedBefore = false;
|
||||
g.ActiveIdHasBeenEditedBefore = false;
|
||||
@@ -4742,8 +4777,12 @@ void ImGui::MarkItemEdited(ImGuiID id)
|
||||
// This marking is to be able to provide info for IsItemDeactivatedAfterEdit().
|
||||
// ActiveId might have been released by the time we call this (as in the typical press/release button behavior) but still need to fill the data.
|
||||
ImGuiContext& g = *GImGui;
|
||||
|
||||
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_EditedInternal;
|
||||
if (g.LastItemData.ItemFlags & ImGuiItemFlags_NoMarkEdited)
|
||||
return;
|
||||
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited;
|
||||
|
||||
if (g.ActiveId == id || g.ActiveId == 0)
|
||||
{
|
||||
// FIXME: Can't we fully rely on LastItemData yet?
|
||||
@@ -4757,9 +4796,6 @@ void ImGui::MarkItemEdited(ImGuiID id)
|
||||
// We accept 'ActiveIdPreviousFrame == id' for InputText() returning an edit after it has been taken ActiveId away (#4714)
|
||||
// FIXME: This assert is getting a bit meaningless over time. It helped detect some unusual use cases but eventually it is becoming an unnecessary restriction.
|
||||
IM_ASSERT(g.DragDropActive || g.ActiveId == id || g.ActiveId == 0 || g.ActiveIdPreviousFrame == id || g.NavJustMovedToId || (g.CurrentMultiSelect != NULL && g.BoxSelectState.IsActive));
|
||||
|
||||
//IM_ASSERT(g.CurrentWindow->DC.LastItemId == id);
|
||||
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited;
|
||||
}
|
||||
|
||||
bool ImGui::IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
|
||||
@@ -4924,7 +4960,7 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flag
|
||||
{
|
||||
g.HoveredIdPreviousFrameItemCount++;
|
||||
if (g.DebugDrawIdConflictsId == id)
|
||||
window->DrawList->AddRect(bb.Min - ImVec2(1,1), bb.Max + ImVec2(1,1), IM_COL32(255, 0, 0, 255), 0.0f, ImDrawFlags_None, 2.0f);
|
||||
window->DrawList->AddRect(bb.Min - ImVec2(1,1), bb.Max + ImVec2(1,1), IM_COL32(255, 0, 0, 255), 0.0f, 2.0f);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -5721,7 +5757,7 @@ void ImGui::NewFrame()
|
||||
g.CurrentWindowStack.resize(0);
|
||||
g.BeginPopupStack.resize(0);
|
||||
g.ItemFlagsStack.resize(0);
|
||||
g.ItemFlagsStack.push_back(ImGuiItemFlags_AutoClosePopups); // Default flags
|
||||
g.ItemFlagsStack.push_back(ImGuiItemFlags_Default_); // Default flags
|
||||
g.CurrentItemFlags = g.ItemFlagsStack.back();
|
||||
g.GroupStack.resize(0);
|
||||
|
||||
@@ -5956,7 +5992,7 @@ static void ImGui::RenderDimmedBackgrounds()
|
||||
if (window->DrawList->CmdBuffer.Size == 0)
|
||||
window->DrawList->AddDrawCmd();
|
||||
window->DrawList->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size);
|
||||
window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), window->WindowRounding, 0, 3.0f); // FIXME-DPI
|
||||
window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), window->WindowRounding, 3.0f); // FIXME-DPI
|
||||
window->DrawList->PopClipRect();
|
||||
}
|
||||
}
|
||||
@@ -6432,7 +6468,7 @@ bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, I
|
||||
window_flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar;
|
||||
window_flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag
|
||||
if (child_flags & (ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize))
|
||||
window_flags |= ImGuiWindowFlags_AlwaysAutoResize;
|
||||
window_flags |= ImGuiWindowFlags_AlwaysAutoResize; // FIXME: Would be sane to not make single-axis flag set this. (#9355)
|
||||
if ((child_flags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY)) == 0)
|
||||
window_flags |= ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings;
|
||||
|
||||
@@ -6581,6 +6617,14 @@ void ImGui::EndChild()
|
||||
g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
|
||||
}
|
||||
|
||||
ImGuiWindow* ImGui::FindFrontMostVisibleChildWindow(ImGuiWindow* window)
|
||||
{
|
||||
for (int n = window->DC.ChildWindows.Size - 1; n >= 0; n--)
|
||||
if (IsWindowActiveAndVisible(window->DC.ChildWindows[n]))
|
||||
return FindFrontMostVisibleChildWindow(window->DC.ChildWindows[n]);
|
||||
return window;
|
||||
}
|
||||
|
||||
static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
|
||||
{
|
||||
window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags);
|
||||
@@ -6744,8 +6788,8 @@ static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_cont
|
||||
const float decoration_h_without_scrollbars = window->DecoOuterSizeY1 + window->DecoOuterSizeY2 - window->ScrollbarSizes.y;
|
||||
ImVec2 size_pad = window->WindowPadding * 2.0f;
|
||||
ImVec2 size_desired;
|
||||
size_desired[ImGuiAxis_X] = (axis_mask & 1) ? size_contents.x + size_pad.x + decoration_w_without_scrollbars : window->Size.x;
|
||||
size_desired[ImGuiAxis_Y] = (axis_mask & 2) ? size_contents.y + size_pad.y + decoration_h_without_scrollbars : window->Size.y;
|
||||
size_desired.x = (axis_mask & 1) ? size_contents.x + size_pad.x + decoration_w_without_scrollbars : window->Size.x;
|
||||
size_desired.y = (axis_mask & 2) ? size_contents.y + size_pad.y + decoration_h_without_scrollbars : window->Size.y;
|
||||
|
||||
// Determine maximum window size
|
||||
// Child windows are laid within their parent (unless they are also popups/menus) and thus have no restriction
|
||||
@@ -6764,8 +6808,10 @@ static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_cont
|
||||
// When the window cannot fit all contents (either because of constraints, either because screen is too small),
|
||||
// we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding.
|
||||
ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit);
|
||||
bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x - size_pad.x - decoration_w_without_scrollbars < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar);
|
||||
bool will_have_scrollbar_y = (size_auto_fit_after_constraint.y - size_pad.y - decoration_h_without_scrollbars < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar);
|
||||
float size_contents_for_scrollbar_x = (axis_mask & 1) ? size_contents.x : window->ContentSize.x; // See #9352. In theory this should use same logic as `window->ScrollbarY = ...` codepath in Begin(). Needs some plumbling.
|
||||
float size_contents_for_scrollbar_y = (axis_mask & 2) ? size_contents.y : window->ContentSize.y;
|
||||
bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x < size_contents_for_scrollbar_x + size_pad.x + decoration_w_without_scrollbars && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar);
|
||||
bool will_have_scrollbar_y = (size_auto_fit_after_constraint.y < size_contents_for_scrollbar_y + size_pad.y + decoration_h_without_scrollbars && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar);
|
||||
if (will_have_scrollbar_x)
|
||||
size_auto_fit.y += style.ScrollbarSize;
|
||||
if (will_have_scrollbar_y)
|
||||
@@ -7106,7 +7152,7 @@ static void RenderWindowOuterSingleBorder(ImGuiWindow* window, int border_n, ImU
|
||||
const ImRect border_r = GetResizeBorderRect(window, border_n, rounding, 0.0f);
|
||||
window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN1) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle - IM_PI * 0.25f, def.OuterAngle);
|
||||
window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + IM_PI * 0.25f);
|
||||
window->DrawList->PathStroke(border_col, ImDrawFlags_None, border_size);
|
||||
window->DrawList->PathStroke(border_col, border_size);
|
||||
}
|
||||
|
||||
static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window)
|
||||
@@ -7115,7 +7161,7 @@ static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window)
|
||||
const float border_size = window->WindowBorderSize;
|
||||
const ImU32 border_col = GetColorU32(ImGuiCol_Border);
|
||||
if (border_size > 0.0f && (window->Flags & ImGuiWindowFlags_NoBackground) == 0)
|
||||
window->DrawList->AddRect(window->Pos, window->Pos + window->Size, border_col, window->WindowRounding, 0, window->WindowBorderSize);
|
||||
window->DrawList->AddRect(window->Pos, window->Pos + window->Size, border_col, window->WindowRounding, window->WindowBorderSize);
|
||||
else if (border_size > 0.0f)
|
||||
{
|
||||
if (window->ChildFlags & ImGuiChildFlags_ResizeX) // Similar code as 'resize_border_mask' computation in UpdateWindowManualResize() but we specifically only always draw explicit child resize border.
|
||||
@@ -7132,7 +7178,7 @@ static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window)
|
||||
if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
|
||||
{
|
||||
float y = window->Pos.y + window->TitleBarHeight - 1;
|
||||
window->DrawList->AddLine(ImVec2(window->Pos.x + border_size * 0.5f, y), ImVec2(window->Pos.x + window->Size.x - border_size * 0.5f, y), border_col, g.Style.FrameBorderSize);
|
||||
window->DrawList->AddLineH(window->Pos.x + border_size * 0.5f, window->Pos.x + window->Size.x - border_size * 0.5f, y, border_col, g.Style.FrameBorderSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7180,7 +7226,7 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar
|
||||
if (bg_col & IM_COL32_A_MASK)
|
||||
{
|
||||
ImRect bg_rect(window->Pos + ImVec2(0, window->TitleBarHeight), window->Pos + window->Size);
|
||||
ImDrawFlags bg_rounding_flags = (flags & ImGuiWindowFlags_NoTitleBar) ? 0 : ImDrawFlags_RoundCornersBottom;
|
||||
ImDrawFlags bg_rounding_flags = (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersBottom;
|
||||
ImDrawList* bg_draw_list = window->DrawList;
|
||||
bg_draw_list->AddRectFilled(bg_rect.Min, bg_rect.Max, bg_col, window_rounding, bg_rounding_flags);
|
||||
}
|
||||
@@ -7200,7 +7246,7 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar
|
||||
menu_bar_rect.ClipWith(window->Rect()); // Soft clipping, in particular child window don't have minimum size covering the menu bar so this is useful for them.
|
||||
window->DrawList->AddRectFilled(menu_bar_rect.Min, menu_bar_rect.Max, GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawFlags_RoundCornersTop);
|
||||
if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
|
||||
window->DrawList->AddLine(menu_bar_rect.GetBL() + ImVec2(window_border_size * 0.5f, 0.0f), menu_bar_rect.GetBR() - ImVec2(window_border_size * 0.5f, 0.0f), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
|
||||
window->DrawList->AddLineH(menu_bar_rect.Min.x + window_border_size * 0.5f, menu_bar_rect.Max.x - window_border_size * 0.5f, menu_bar_rect.Max.y, GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
|
||||
}
|
||||
|
||||
// Scrollbars
|
||||
@@ -7844,12 +7890,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
|
||||
ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - (window->DecoOuterSizeY1 + window->DecoOuterSizeY2));
|
||||
ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + scrollbar_sizes_from_last_frame;
|
||||
ImVec2 needed_size_from_last_frame = window_just_created ? ImVec2(0, 0) : window->ContentSize + window->WindowPadding * 2.0f;
|
||||
float size_x_for_scrollbars = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x;
|
||||
float size_y_for_scrollbars = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y;
|
||||
float size_for_scrollbars_x = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x;
|
||||
float size_for_scrollbars_y = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y;
|
||||
bool scrollbar_x_prev = window->ScrollbarX;
|
||||
//bool scrollbar_y_from_last_frame = window->ScrollbarY; // FIXME: May want to use that in the ScrollbarX expression? How many pros vs cons?
|
||||
window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));
|
||||
window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((needed_size_from_last_frame.x > size_x_for_scrollbars - (window->ScrollbarY ? style.ScrollbarSize : 0.0f)) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar));
|
||||
window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_for_scrollbars_y) && !(flags & ImGuiWindowFlags_NoScrollbar));
|
||||
window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((needed_size_from_last_frame.x > size_for_scrollbars_x - (window->ScrollbarY ? style.ScrollbarSize : 0.0f)) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar));
|
||||
|
||||
// Track when ScrollbarX visibility keeps toggling, which is a sign of a feedback loop, and stabilize by enforcing visibility (#3285, #8488)
|
||||
// (Feedback loops of this sort can manifest in various situations, but combining horizontal + vertical scrollbar + using a clipper with varying width items is one frequent cause.
|
||||
@@ -7864,7 +7910,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
|
||||
window->ScrollbarXStabilizeEnabled = scrollbar_x_stabilize;
|
||||
|
||||
if (window->ScrollbarX && !window->ScrollbarY)
|
||||
window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars - style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar);
|
||||
window->ScrollbarY = (needed_size_from_last_frame.y > size_for_scrollbars_y - style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar);
|
||||
window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
|
||||
|
||||
// Amend the partially filled window->DecorationXXX values.
|
||||
@@ -8029,9 +8075,14 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
|
||||
window->DC.LayoutType = ImGuiLayoutType_Vertical;
|
||||
window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;
|
||||
|
||||
// Default item width. Make it proportional to window size if window manually resizes
|
||||
const bool is_resizable_window = (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize));
|
||||
if (is_resizable_window)
|
||||
// Default item width. Make it proportional to window size if window can be manually resized.
|
||||
// (we cannot use AutoFitFramesX/AutoFitFramesY which is a temporary state)
|
||||
bool is_resizable_width;
|
||||
if (flags & ImGuiWindowFlags_ChildWindow)
|
||||
is_resizable_width = (window->Size.x > 0.0f) && !(window->ChildFlags & (ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AlwaysAutoResize));
|
||||
else
|
||||
is_resizable_width = (window->Size.x > 0.0f) && !(flags & ImGuiWindowFlags_AlwaysAutoResize);
|
||||
if (is_resizable_width)
|
||||
window->DC.ItemWidthDefault = ImTrunc(window->Size.x * 0.65f);
|
||||
else
|
||||
window->DC.ItemWidthDefault = ImTrunc(g.FontSize * 16.0f);
|
||||
@@ -8201,6 +8252,8 @@ void ImGui::End()
|
||||
ImGuiWindowStackData& window_stack_data = g.CurrentWindowStack.back();
|
||||
|
||||
// Error checking: verify that user doesn't directly call End() on a child window.
|
||||
if (window->Flags & ImGuiWindowFlags_Popup)
|
||||
IM_ASSERT_USER_ERROR(g.WithinEndPopupID == window->ID, "Must call EndPopup() and not End()!");
|
||||
if (window->Flags & ImGuiWindowFlags_ChildWindow)
|
||||
IM_ASSERT_USER_ERROR(g.WithinEndChildID == window->ID, "Must call EndChild() and not End()!");
|
||||
|
||||
@@ -8738,6 +8791,17 @@ void ImGui::PopFocusScope()
|
||||
g.CurrentFocusScopeId = g.FocusScopeStack.Size ? g.FocusScopeStack.back().ID : 0;
|
||||
}
|
||||
|
||||
bool ImGui::IsInNavFocusRoute(ImGuiID focus_scope_id)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
if (g.NavFocusScopeId == focus_scope_id)
|
||||
return true;
|
||||
for (const ImGuiFocusScopeData& focus_scope : g.NavFocusRoute)
|
||||
if (focus_scope.ID == focus_scope_id)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void ImGui::SetNavFocusScope(ImGuiID focus_scope_id)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
@@ -8972,7 +9036,7 @@ ImFont* ImGui::GetDefaultFont()
|
||||
return g.IO.FontDefault ? g.IO.FontDefault : atlas->Fonts[0];
|
||||
}
|
||||
|
||||
// EXPERIMENTAL: DO NOT USE YET.
|
||||
// EXPERIMENTAL. Use ImTextureDataQueueUpload() to queue updates.
|
||||
void ImGui::RegisterUserTexture(ImTextureData* tex)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
@@ -9094,7 +9158,7 @@ void ImGui::UpdateCurrentFontSize(float restore_font_size_after_scaling)
|
||||
}
|
||||
|
||||
g.FontBaked = (g.Font != NULL && window != NULL) ? g.Font->GetFontBaked(final_size) : NULL;
|
||||
g.FontBakedScale = (g.Font != NULL && window != NULL) ? (g.FontSize / g.FontBaked->Size) : 0.0f;
|
||||
g.FontBakedScale = (g.FontBaked != NULL) ? (g.FontSize / g.FontBaked->Size) : 0.0f;
|
||||
g.DrawListSharedData.FontScale = g.FontBakedScale;
|
||||
}
|
||||
|
||||
@@ -10302,15 +10366,21 @@ void ImGui::UpdateMouseWheel()
|
||||
LockWheelingWindow(NULL, 0.0f);
|
||||
}
|
||||
|
||||
ImVec2 wheel;
|
||||
wheel.x = TestKeyOwner(ImGuiKey_MouseWheelX, ImGuiKeyOwner_NoOwner) ? g.IO.MouseWheelH : 0.0f;
|
||||
wheel.y = TestKeyOwner(ImGuiKey_MouseWheelY, ImGuiKeyOwner_NoOwner) ? g.IO.MouseWheel : 0.0f;
|
||||
|
||||
//IMGUI_DEBUG_LOG("MouseWheel X:%.3f Y:%.3f\n", wheel_x, wheel_y);
|
||||
ImGuiWindow* mouse_window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow;
|
||||
if (!mouse_window || mouse_window->Collapsed)
|
||||
return;
|
||||
|
||||
ImGuiID owner_id = mouse_window->ID;
|
||||
ImVec2 wheel;
|
||||
wheel.x = TestKeyOwner(ImGuiKey_MouseWheelX, owner_id) ? g.IO.MouseWheelH : 0.0f;
|
||||
wheel.y = TestKeyOwner(ImGuiKey_MouseWheelY, owner_id) ? g.IO.MouseWheel : 0.0f;
|
||||
//IMGUI_DEBUG_LOG("MouseWheel X:%.3f Y:%.3f\n", wheel_x, wheel_y);
|
||||
if (g.WheelingWindow != NULL)
|
||||
{
|
||||
SetKeyOwner(ImGuiKey_MouseWheelX, owner_id);
|
||||
SetKeyOwner(ImGuiKey_MouseWheelY, owner_id);
|
||||
}
|
||||
|
||||
// Zoom / Scale window
|
||||
// FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned.
|
||||
if (wheel.y != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
|
||||
@@ -10614,6 +10684,7 @@ bool ImGui::TestKeyOwner(ImGuiKey key, ImGuiID owner_id)
|
||||
// - SetKeyOwner(..., None) : clears owner
|
||||
// - SetKeyOwner(..., Any, !Lock) : illegal (assert)
|
||||
// - SetKeyOwner(..., Any or None, Lock) : set lock
|
||||
// Ownership is automatically released on the frame after a release, see code in UpdateKeyboardInputs().
|
||||
void ImGui::SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
@@ -10640,30 +10711,34 @@ void ImGui::SetKeyOwnersForKeyChord(ImGuiKeyChord key_chord, ImGuiID owner_id, I
|
||||
if (key_chord & ~ImGuiMod_Mask_) { SetKeyOwner((ImGuiKey)(key_chord & ~ImGuiMod_Mask_), owner_id, flags); }
|
||||
}
|
||||
|
||||
// This is more or less equivalent to:
|
||||
// This is more or less equivalent to a fancier version of:
|
||||
// if (IsItemHovered() || IsItemActive())
|
||||
// SetKeyOwner(key, GetItemID());
|
||||
// Extensive uses of that (e.g. many calls for a single item) may want to manually perform the tests once and then call SetKeyOwner() multiple times.
|
||||
// More advanced usage scenarios may want to call SetKeyOwner() manually based on different condition.
|
||||
// Worth noting is that only one item can be hovered and only one item can be active, therefore this usage pattern doesn't need to bother with routing and priority.
|
||||
void ImGui::SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags)
|
||||
bool ImGui::SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiID id = g.LastItemData.ID;
|
||||
if (id == 0 || (g.HoveredId != id && g.ActiveId != id))
|
||||
return;
|
||||
return false;
|
||||
if ((flags & ImGuiInputFlags_CondMask_) == 0)
|
||||
flags |= ImGuiInputFlags_CondDefault_;
|
||||
if ((g.HoveredId == id && (flags & ImGuiInputFlags_CondHovered)) || (g.ActiveId == id && (flags & ImGuiInputFlags_CondActive)))
|
||||
{
|
||||
IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetItemKeyOwner) == 0); // Passing flags not supported by this function!
|
||||
if (!TestKeyOwner(key, id))
|
||||
return false;
|
||||
SetKeyOwner(key, id, flags & ~ImGuiInputFlags_CondMask_);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ImGui::SetItemKeyOwner(ImGuiKey key)
|
||||
bool ImGui::SetItemKeyOwner(ImGuiKey key)
|
||||
{
|
||||
SetItemKeyOwner(key, ImGuiInputFlags_None);
|
||||
return SetItemKeyOwner(key, ImGuiInputFlags_None);
|
||||
}
|
||||
|
||||
// This is the only public API until we expose owner_id versions of the API as replacements.
|
||||
@@ -10804,7 +10879,8 @@ bool ImGui::DebugCheckVersionAndDataLayout(const char* version, size_t sz_io, si
|
||||
// to extend contents size of our parent container (e.g. window contents size, which is used for auto-resizing
|
||||
// windows, table column contents size used for auto-resizing columns, group size).
|
||||
// This was causing issues and ambiguities and we needed to retire that.
|
||||
// From 1.89, extending contents size boundaries REQUIRES AN ITEM TO BE SUBMITTED.
|
||||
// 2022/08/05 (1.89): extending contents size boundaries REQUIRES AN ITEM TO BE SUBMITTED. However we gated the new logic behind a '#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS' block.
|
||||
// 2025/06/25 (1.92): removed the legacy path and turned into an assert. It was a mistake that there was a #ifndef before: our obsolescence schedule gets pushed back a bit more :(
|
||||
//
|
||||
// Previously this would make the window content size ~200x200:
|
||||
// Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + End(); // NOT OK ANYMORE
|
||||
@@ -12413,6 +12489,17 @@ bool ImGui::BeginPopupMenuEx(ImGuiID id, const char* label, ImGuiWindowFlags ext
|
||||
return false;
|
||||
}
|
||||
|
||||
// As we bypass BeginChild(), set ImGuiChildFlags_AlwaysAutoResize as it is checked independently from ImGuiWindowFlags_AlwaysAutoResize for now (see #9355)
|
||||
// Ideally we should remove setting ImGuiWindowFlags_AlwaysAutoResize in BeginChild().
|
||||
if ((extra_window_flags & ImGuiWindowFlags_ChildWindow) && (extra_window_flags & ImGuiWindowFlags_AlwaysAutoResize))
|
||||
{
|
||||
if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasChildFlags)
|
||||
g.NextWindowData.ChildFlags |= ImGuiChildFlags_AlwaysAutoResize;
|
||||
else
|
||||
g.NextWindowData.ChildFlags = ImGuiChildFlags_AlwaysAutoResize;
|
||||
g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasChildFlags;
|
||||
}
|
||||
|
||||
char name[128];
|
||||
IM_ASSERT(extra_window_flags & ImGuiWindowFlags_ChildMenu);
|
||||
ImFormatString(name, IM_COUNTOF(name), "%s###Menu_%02d", label, g.BeginMenuDepth); // Recycle windows based on depth
|
||||
@@ -12485,10 +12572,13 @@ void ImGui::EndPopup()
|
||||
NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY);
|
||||
|
||||
// Child-popups don't need to be laid out
|
||||
const ImGuiID backup_within_end_popup_id = g.WithinEndPopupID;
|
||||
const ImGuiID backup_within_end_child_id = g.WithinEndChildID;
|
||||
g.WithinEndPopupID = window->ID;
|
||||
if (window->Flags & ImGuiWindowFlags_ChildWindow)
|
||||
g.WithinEndChildID = window->ID;
|
||||
End();
|
||||
g.WithinEndPopupID = backup_within_end_popup_id;
|
||||
g.WithinEndChildID = backup_within_end_child_id;
|
||||
}
|
||||
|
||||
@@ -13310,7 +13400,7 @@ static void ImGui::NavProcessItem()
|
||||
const ImGuiID id = g.LastItemData.ID;
|
||||
const ImGuiItemFlags item_flags = g.LastItemData.ItemFlags;
|
||||
|
||||
// When inside a container that isn't scrollable with Left<>Right, clip NavRect accordingly (#2221, #8816)
|
||||
// When inside a container that isn't scrollable with Left<>Right, clip NavRect accordingly (#2221, #8816, #7994)
|
||||
ImRect nav_bb = g.LastItemData.NavRect;
|
||||
if (window->DC.NavIsScrollPushableX == false)
|
||||
{
|
||||
@@ -15074,7 +15164,7 @@ const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDrop
|
||||
{
|
||||
ImRect bb = g.DragDropTargetRect;
|
||||
bb.Expand(-3.5f);
|
||||
RenderDragDropTargetRectEx(GetForegroundDrawList(), bb);
|
||||
RenderDragDropTargetRectEx(GetForegroundDrawList(), bb, g.Style.DragDropTargetRounding);
|
||||
}
|
||||
else if (draw_target_rect)
|
||||
{
|
||||
@@ -15105,16 +15195,16 @@ void ImGui::RenderDragDropTargetRectForItem(const ImRect& bb)
|
||||
bool push_clip_rect = !window->ClipRect.Contains(bb_display);
|
||||
if (push_clip_rect)
|
||||
window->DrawList->PushClipRectFullScreen();
|
||||
RenderDragDropTargetRectEx(window->DrawList, bb_display);
|
||||
RenderDragDropTargetRectEx(window->DrawList, bb_display, g.Style.DragDropTargetRounding);
|
||||
if (push_clip_rect)
|
||||
window->DrawList->PopClipRect();
|
||||
}
|
||||
|
||||
void ImGui::RenderDragDropTargetRectEx(ImDrawList* draw_list, const ImRect& bb)
|
||||
void ImGui::RenderDragDropTargetRectEx(ImDrawList* draw_list, const ImRect& bb, float rounding)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
draw_list->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_DragDropTargetBg), g.Style.DragDropTargetRounding, 0);
|
||||
draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_DragDropTarget), g.Style.DragDropTargetRounding, 0, g.Style.DragDropTargetBorderSize);
|
||||
draw_list->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_DragDropTargetBg), rounding, 0);
|
||||
draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_DragDropTarget), rounding, g.Style.DragDropTargetBorderSize);
|
||||
}
|
||||
|
||||
const ImGuiPayload* ImGui::GetDragDropPayload()
|
||||
@@ -15774,6 +15864,7 @@ void ImGuiPlatformIO::ClearRendererHandlers()
|
||||
{
|
||||
Renderer_TextureMaxWidth = Renderer_TextureMaxHeight = 0;
|
||||
Renderer_RenderState = NULL;
|
||||
DrawCallback_ResetRenderState = DrawCallback_SetSamplerLinear = DrawCallback_SetSamplerNearest = NULL;
|
||||
}
|
||||
|
||||
ImGuiViewport* ImGui::GetMainViewport()
|
||||
@@ -16215,7 +16306,7 @@ void ImGui::DebugRenderKeyboardPreview(ImDrawList* draw_list)
|
||||
draw_list->AddRect(key_min, key_max, IM_COL32(24, 24, 24, 255), key_rounding);
|
||||
ImVec2 face_min = ImVec2(key_min.x + key_face_pos.x, key_min.y + key_face_pos.y);
|
||||
ImVec2 face_max = ImVec2(face_min.x + key_face_size.x, face_min.y + key_face_size.y);
|
||||
draw_list->AddRect(face_min, face_max, IM_COL32(193, 193, 193, 255), key_face_rounding, ImDrawFlags_None, 2.0f);
|
||||
draw_list->AddRect(face_min, face_max, IM_COL32(193, 193, 193, 255), key_face_rounding, 2.0f);
|
||||
draw_list->AddRectFilled(face_min, face_max, IM_COL32(252, 252, 252, 255), key_face_rounding);
|
||||
ImVec2 label_min = ImVec2(key_min.x + key_label_pos.x, key_min.y + key_label_pos.y);
|
||||
draw_list->AddText(label_min, IM_COL32(64, 64, 64, 255), key_data->Label);
|
||||
@@ -16638,7 +16729,7 @@ void ImGui::ShowMetricsWindow(bool* p_open)
|
||||
|
||||
BulletText("Table 0x%08X (%d columns, in '%s')", table->ID, table->ColumnsCount, table->OuterWindow->Name);
|
||||
if (IsItemHovered())
|
||||
GetForegroundDrawList(table->OuterWindow)->AddRect(table->OuterRect.Min - ImVec2(1, 1), table->OuterRect.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);
|
||||
GetForegroundDrawList(table->OuterWindow)->AddRect(table->OuterRect.Min - ImVec2(1, 1), table->OuterRect.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 2.0f);
|
||||
Indent();
|
||||
char buf[128];
|
||||
for (int rect_n = 0; rect_n < TRT_Count; rect_n++)
|
||||
@@ -16653,7 +16744,7 @@ void ImGui::ShowMetricsWindow(bool* p_open)
|
||||
ImFormatString(buf, IM_COUNTOF(buf), "(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) Col %d %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), column_n, trt_rects_names[rect_n]);
|
||||
Selectable(buf);
|
||||
if (IsItemHovered())
|
||||
GetForegroundDrawList(table->OuterWindow)->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);
|
||||
GetForegroundDrawList(table->OuterWindow)->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 2.0f);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -16662,7 +16753,7 @@ void ImGui::ShowMetricsWindow(bool* p_open)
|
||||
ImFormatString(buf, IM_COUNTOF(buf), "(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), trt_rects_names[rect_n]);
|
||||
Selectable(buf);
|
||||
if (IsItemHovered())
|
||||
GetForegroundDrawList(table->OuterWindow)->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);
|
||||
GetForegroundDrawList(table->OuterWindow)->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 2.0f);
|
||||
}
|
||||
}
|
||||
Unindent();
|
||||
@@ -17070,7 +17161,7 @@ void ImGui::ShowMetricsWindow(bool* p_open)
|
||||
ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, column_n);
|
||||
ImU32 col = (table->HoveredColumnBody == column_n) ? IM_COL32(255, 255, 128, 255) : IM_COL32(255, 0, 128, 255);
|
||||
float thickness = (table->HoveredColumnBody == column_n) ? 3.0f : 1.0f;
|
||||
draw_list->AddRect(r.Min, r.Max, col, 0.0f, 0, thickness);
|
||||
draw_list->AddRect(r.Min, r.Max, col, 0.0f, thickness);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -17247,7 +17338,7 @@ void ImGui::DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, con
|
||||
{
|
||||
ImDrawListFlags backup_flags = fg_draw_list->Flags;
|
||||
fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
|
||||
fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f);
|
||||
fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), 1.0f, ImDrawFlags_Closed);
|
||||
fg_draw_list->Flags = backup_flags;
|
||||
}
|
||||
}
|
||||
@@ -17275,7 +17366,7 @@ void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, co
|
||||
for (int n = 0; n < 3; n++, idx_n++)
|
||||
vtxs_rect.Add((triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos));
|
||||
if (show_mesh)
|
||||
out_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f); // In yellow: mesh triangles
|
||||
out_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), 1.0f, ImDrawFlags_Closed); // In yellow: mesh triangles
|
||||
}
|
||||
// Draw bounding boxes
|
||||
if (show_aabb)
|
||||
@@ -17556,8 +17647,8 @@ void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label)
|
||||
{
|
||||
ImDrawList* draw_list = GetForegroundDrawList(tab_bar->Window);
|
||||
draw_list->AddRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max, IM_COL32(255, 255, 0, 255));
|
||||
draw_list->AddLine(ImVec2(tab_bar->ScrollingRectMinX, tab_bar->BarRect.Min.y), ImVec2(tab_bar->ScrollingRectMinX, tab_bar->BarRect.Max.y), IM_COL32(0, 255, 0, 255));
|
||||
draw_list->AddLine(ImVec2(tab_bar->ScrollingRectMaxX, tab_bar->BarRect.Min.y), ImVec2(tab_bar->ScrollingRectMaxX, tab_bar->BarRect.Max.y), IM_COL32(0, 255, 0, 255));
|
||||
draw_list->AddLineV(tab_bar->ScrollingRectMinX, tab_bar->BarRect.Min.y, tab_bar->BarRect.Max.y, IM_COL32(0, 255, 0, 255));
|
||||
draw_list->AddLineV(tab_bar->ScrollingRectMaxX, tab_bar->BarRect.Min.y, tab_bar->BarRect.Max.y, IM_COL32(0, 255, 0, 255));
|
||||
}
|
||||
if (open)
|
||||
{
|
||||
@@ -17886,8 +17977,8 @@ void ImGui::DebugDrawCursorPos(ImU32 col)
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiWindow* window = g.CurrentWindow;
|
||||
ImVec2 pos = window->DC.CursorPos;
|
||||
window->DrawList->AddLine(ImVec2(pos.x, pos.y - 3.0f), ImVec2(pos.x, pos.y + 4.0f), col, 1.0f);
|
||||
window->DrawList->AddLine(ImVec2(pos.x - 3.0f, pos.y), ImVec2(pos.x + 4.0f, pos.y), col, 1.0f);
|
||||
window->DrawList->AddLineV(pos.x, pos.y - 3.0f, pos.y + 4.0f, col, 1.0f);
|
||||
window->DrawList->AddLineH(pos.x - 3.0f, pos.x + 4.0f, pos.y, col, 1.0f);
|
||||
}
|
||||
|
||||
// Draw a 10px wide rectangle around CurposPos.x using Line Y1/Y2 in current window's DrawList
|
||||
@@ -17898,9 +17989,9 @@ void ImGui::DebugDrawLineExtents(ImU32 col)
|
||||
float curr_x = window->DC.CursorPos.x;
|
||||
float line_y1 = (window->DC.IsSameLine ? window->DC.CursorPosPrevLine.y : window->DC.CursorPos.y);
|
||||
float line_y2 = line_y1 + (window->DC.IsSameLine ? window->DC.PrevLineSize.y : window->DC.CurrLineSize.y);
|
||||
window->DrawList->AddLine(ImVec2(curr_x - 5.0f, line_y1), ImVec2(curr_x + 5.0f, line_y1), col, 1.0f);
|
||||
window->DrawList->AddLine(ImVec2(curr_x - 0.5f, line_y1), ImVec2(curr_x - 0.5f, line_y2), col, 1.0f);
|
||||
window->DrawList->AddLine(ImVec2(curr_x - 5.0f, line_y2), ImVec2(curr_x + 5.0f, line_y2), col, 1.0f);
|
||||
window->DrawList->AddLineH(curr_x - 5.0f, curr_x + 5.0f, line_y1, col, 1.0f);
|
||||
window->DrawList->AddLineV(curr_x - 0.5f, line_y1, line_y2, col, 1.0f);
|
||||
window->DrawList->AddLineH(curr_x - 5.0f, curr_x + 5.0f, line_y2, col, 1.0f);
|
||||
}
|
||||
|
||||
// Draw last item rect in ForegroundDrawList (so it is always visible)
|
||||
@@ -18270,7 +18361,7 @@ void ImGui::ShowFontSelector(const char* label)
|
||||
"- Load additional fonts with io.Fonts->AddFontXXX() functions.\n"
|
||||
"- The font atlas is built when calling io.Fonts->GetTexDataAsXXXX() or io.Fonts->Build().\n"
|
||||
"- Read FAQ and docs/FONTS.md for more details.\n"
|
||||
"- If you need to add/remove fonts at runtime (e.g. for DPI change), do it before calling NewFrame().");
|
||||
"- Legacy backend: if you need to add/remove fonts at runtime (e.g. for DPI change), do it before calling NewFrame().");
|
||||
}
|
||||
#endif // #if !defined(IMGUI_DISABLE_DEMO_WINDOWS) || !defined(IMGUI_DISABLE_DEBUG_TOOLS)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// dear imgui, v1.92.7
|
||||
// dear imgui, v1.92.8
|
||||
// (demo code)
|
||||
|
||||
// Help:
|
||||
@@ -73,6 +73,7 @@ Index of this file:
|
||||
// [SECTION] Demo Window / ShowDemoWindow()
|
||||
// [SECTION] DemoWindowMenuBar()
|
||||
// [SECTION] Helpers: ExampleTreeNode, ExampleMemberInfo (for use by Property Editor & Multi-Select demos)
|
||||
// [SECTION] Helpers: ExampleImageViewer
|
||||
// [SECTION] DemoWindowWidgetsBasic()
|
||||
// [SECTION] DemoWindowWidgetsBullets()
|
||||
// [SECTION] DemoWindowWidgetsCollapsingHeaders()
|
||||
@@ -108,6 +109,7 @@ Index of this file:
|
||||
// [SECTION] User Guide / ShowUserGuide()
|
||||
// [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar()
|
||||
// [SECTION] Example App: Debug Console / ShowExampleAppConsole()
|
||||
// [SECTION] Example App: Image Viewer / ShowExampleAppImageViewer()
|
||||
// [SECTION] Example App: Debug Log / ShowExampleAppLog()
|
||||
// [SECTION] Example App: Simple Layout / ShowExampleAppLayout()
|
||||
// [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor()
|
||||
@@ -238,6 +240,7 @@ static void ShowExampleAppAssetsBrowser(bool* p_open);
|
||||
static void ShowExampleAppConsole(bool* p_open);
|
||||
static void ShowExampleAppCustomRendering(bool* p_open);
|
||||
static void ShowExampleAppDocuments(bool* p_open);
|
||||
static void ShowExampleAppImageViewer(bool* p_open);
|
||||
static void ShowExampleAppLog(bool* p_open);
|
||||
static void ShowExampleAppLayout(bool* p_open);
|
||||
static void ShowExampleAppPropertyEditor(bool* p_open, ImGuiDemoWindowData* demo_data);
|
||||
@@ -308,6 +311,7 @@ struct ImGuiDemoWindowData
|
||||
bool ShowAppConsole = false;
|
||||
bool ShowAppCustomRendering = false;
|
||||
bool ShowAppDocuments = false;
|
||||
bool ShowAppImageViewer = false;
|
||||
bool ShowAppLog = false;
|
||||
bool ShowAppLayout = false;
|
||||
bool ShowAppPropertyEditor = false;
|
||||
@@ -353,6 +357,7 @@ void ImGui::ShowDemoWindow(bool* p_open)
|
||||
if (demo_data.ShowAppAssetsBrowser) { ShowExampleAppAssetsBrowser(&demo_data.ShowAppAssetsBrowser); }
|
||||
if (demo_data.ShowAppConsole) { ShowExampleAppConsole(&demo_data.ShowAppConsole); }
|
||||
if (demo_data.ShowAppCustomRendering) { ShowExampleAppCustomRendering(&demo_data.ShowAppCustomRendering); }
|
||||
if (demo_data.ShowAppImageViewer) { ShowExampleAppImageViewer(&demo_data.ShowAppImageViewer); }
|
||||
if (demo_data.ShowAppLog) { ShowExampleAppLog(&demo_data.ShowAppLog); }
|
||||
if (demo_data.ShowAppLayout) { ShowExampleAppLayout(&demo_data.ShowAppLayout); }
|
||||
if (demo_data.ShowAppPropertyEditor) { ShowExampleAppPropertyEditor(&demo_data.ShowAppPropertyEditor, &demo_data); }
|
||||
@@ -677,6 +682,7 @@ static void DemoWindowMenuBar(ImGuiDemoWindowData* demo_data)
|
||||
ImGui::MenuItem("Console", NULL, &demo_data->ShowAppConsole);
|
||||
ImGui::MenuItem("Custom rendering", NULL, &demo_data->ShowAppCustomRendering);
|
||||
ImGui::MenuItem("Documents", NULL, &demo_data->ShowAppDocuments);
|
||||
ImGui::MenuItem("Image Viewer", NULL, &demo_data->ShowAppImageViewer);
|
||||
ImGui::MenuItem("Log", NULL, &demo_data->ShowAppLog);
|
||||
ImGui::MenuItem("Property editor", NULL, &demo_data->ShowAppPropertyEditor);
|
||||
ImGui::MenuItem("Simple layout", NULL, &demo_data->ShowAppLayout);
|
||||
@@ -708,7 +714,7 @@ static void DemoWindowMenuBar(ImGuiDemoWindowData* demo_data)
|
||||
ImGui::Checkbox("Highlight ID Conflicts", &io.ConfigDebugHighlightIdConflicts);
|
||||
ImGui::EndDisabled();
|
||||
ImGui::Checkbox("Assert on error recovery", &io.ConfigErrorRecoveryEnableAssert);
|
||||
ImGui::TextDisabled("(see Demo->Configuration for details & more)");
|
||||
ImGui::TextDisabled("(see Demo->Configuration for more)");
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::MenuItem("Debug Log", NULL, &demo_data->ShowDebugLog, has_debug_tools);
|
||||
@@ -825,6 +831,87 @@ static ExampleTreeNode* ExampleTree_CreateDemoTree()
|
||||
return node_L0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] Helpers: ExampleImageViewer
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
struct ExampleImageViewerData
|
||||
{
|
||||
ImU32 ImageBgColor = IM_COL32(100, 100, 100, 255);
|
||||
ImU32 GridColor = IM_COL32(255, 255, 255, 100);
|
||||
bool GridEnabled = true;
|
||||
bool ViewReset = true;
|
||||
ImVec2 ViewOffset; // in image space
|
||||
float Zoom = 10.0f;
|
||||
float ZoomMin = 1.0f;
|
||||
float ZoomMax = 10000.0f;
|
||||
};
|
||||
|
||||
static void ExampleImageViewer_DrawOptions(ExampleImageViewerData* data)
|
||||
{
|
||||
ImGui::SetNextItemShortcut(ImGuiKey_G, ImGuiInputFlags_Tooltip); // | ImGuiInputFlags_RouteGlobal
|
||||
ImGui::Checkbox("Grid", &data->GridEnabled);
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 10.0f);
|
||||
float zoom_100 = data->Zoom * 100.0f;
|
||||
if (ImGui::DragFloat("##Zoom", &zoom_100, 5.0f, data->ZoomMin * 100.0f, data->ZoomMax * 100.0f, "%.0f%%", ImGuiSliderFlags_AlwaysClamp))
|
||||
data->Zoom = zoom_100 / 100.0f;
|
||||
}
|
||||
|
||||
static void ExampleImageViewer_DrawCanvas(ExampleImageViewerData* data, ImVec2 canvas_size, ImTextureRef image_tex_ref, int image_w, int image_h)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
IM_ASSERT(canvas_size.x >= 0.0f && canvas_size.y >= 0.0f);
|
||||
|
||||
// Layout canvas
|
||||
ImGui::InvisibleButton("##Canvas", canvas_size);
|
||||
ImVec2 canvas_min = ImGui::GetItemRectMin();
|
||||
ImVec2 canvas_max = ImGui::GetItemRectMax();
|
||||
|
||||
if (data->ViewReset)
|
||||
data->ViewOffset = ImVec2((canvas_size.x * 0.5f / data->Zoom) - 0.5f, (canvas_size.y * 0.5f / data->Zoom) - 0.5f); // Add half a pixel padding
|
||||
data->ViewReset = false;
|
||||
|
||||
// Handle inputs
|
||||
if (ImGui::SetItemKeyOwner(ImGuiKey_MouseWheelY))
|
||||
if (io.MouseWheel != 0.0f)
|
||||
data->Zoom = IM_CLAMP(data->Zoom * (1.0f + io.MouseWheel * 0.10f), data->ZoomMin, data->ZoomMax);
|
||||
float zoom = data->Zoom; // (float)(int)ViewZoom;
|
||||
if (ImGui::IsItemActive() && ImGui::IsMouseDragging(0))
|
||||
{
|
||||
data->ViewOffset.x -= io.MouseDelta.x / zoom;
|
||||
data->ViewOffset.y -= io.MouseDelta.y / zoom;
|
||||
}
|
||||
|
||||
// Display image
|
||||
ImVec2 image_min, image_max;
|
||||
image_min.x = (float)(int)((canvas_min.x - (data->ViewOffset.x * zoom)) + (canvas_size.x * 0.5f));
|
||||
image_min.y = (float)(int)((canvas_min.y - (data->ViewOffset.y * zoom)) + (canvas_size.y * 0.5f));
|
||||
image_max.x = (float)(int)(image_min.x + image_w * zoom);
|
||||
image_max.y = (float)(int)(image_min.y + image_h * zoom);
|
||||
draw_list->AddRect(ImVec2(canvas_min.x - 1.0f, canvas_min.y - 1.0f), ImVec2(canvas_max.x + 1.0f, canvas_max.y + 1.0f), IM_COL32(255, 255, 255, 255));
|
||||
draw_list->PushClipRect(canvas_min, canvas_max, true);
|
||||
draw_list->AddRectFilled(image_min, image_max, data->ImageBgColor);
|
||||
if (platform_io.DrawCallback_SetSamplerNearest != NULL)
|
||||
draw_list->AddCallback(platform_io.DrawCallback_SetSamplerNearest);
|
||||
draw_list->AddImage(image_tex_ref, image_min, image_max);
|
||||
if (platform_io.DrawCallback_SetSamplerLinear != NULL)
|
||||
draw_list->AddCallback(ImGui::GetPlatformIO().DrawCallback_SetSamplerLinear);
|
||||
|
||||
// Display grid lines for visible pixels
|
||||
if (data->GridEnabled && zoom > 6.0f)
|
||||
{
|
||||
const float step = (float)zoom;
|
||||
for (int px = (int)((canvas_min.x - image_min.x) / step); px <= (int)((canvas_max.x - image_min.x) / step); px++)
|
||||
draw_list->AddLineV(image_min.x + px * step, canvas_min.y, canvas_max.y, data->GridColor, 1.0f);
|
||||
for (int py = (int)((canvas_min.y - image_min.y) / step); py <= (int)((canvas_max.y - image_min.y) / step); py++)
|
||||
draw_list->AddLineH(canvas_min.x, canvas_max.x, image_min.y + py * step, data->GridColor, 1.0f);
|
||||
}
|
||||
draw_list->PopClipRect();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] DemoWindowWidgetsBasic()
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -1802,40 +1889,29 @@ static void DemoWindowWidgetsImages()
|
||||
// - Read https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
|
||||
|
||||
// Grab the current texture identifier used by the font atlas.
|
||||
ImTextureRef my_tex_id = io.Fonts->TexRef;
|
||||
ImFontAtlas* atlas = io.Fonts;
|
||||
ImTextureRef my_tex_id = atlas->TexRef;
|
||||
float my_tex_w = (float)atlas->TexData->Width; // Regular user code should never have to care about TexData-> fields, but since we want to display the entire texture here, we pull Width/Height from it.
|
||||
float my_tex_h = (float)atlas->TexData->Height;
|
||||
ImGui::Text("%.0fx%.0f", my_tex_w, my_tex_h);
|
||||
|
||||
// Regular user code should never have to care about TexData-> fields, but since we want to display the entire texture here, we pull Width/Height from it.
|
||||
float my_tex_w = (float)io.Fonts->TexData->Width;
|
||||
float my_tex_h = (float)io.Fonts->TexData->Height;
|
||||
// Basic drawing
|
||||
ImGui::SeparatorText("Image()/ImageWithBg() function");
|
||||
ImVec2 uv_min = ImVec2(0.0f, 0.0f); // Top-left
|
||||
ImVec2 uv_max = ImVec2(1.0f, 1.0f); // Lower-right
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ImageBorderSize, IM_MAX(1.0f, ImGui::GetStyle().ImageBorderSize));
|
||||
ImGui::ImageWithBg(my_tex_id, ImVec2(my_tex_w, my_tex_h), uv_min, uv_max, ImVec4(0.0f, 0.0f, 0.0f, 1.0f));
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
{
|
||||
ImGui::Text("%.0fx%.0f", my_tex_w, my_tex_h);
|
||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||
ImVec2 uv_min = ImVec2(0.0f, 0.0f); // Top-left
|
||||
ImVec2 uv_max = ImVec2(1.0f, 1.0f); // Lower-right
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ImageBorderSize, IM_MAX(1.0f, ImGui::GetStyle().ImageBorderSize));
|
||||
ImGui::ImageWithBg(my_tex_id, ImVec2(my_tex_w, my_tex_h), uv_min, uv_max, ImVec4(0.0f, 0.0f, 0.0f, 1.0f));
|
||||
if (ImGui::BeginItemTooltip())
|
||||
{
|
||||
float region_sz = 32.0f;
|
||||
float region_x = io.MousePos.x - pos.x - region_sz * 0.5f;
|
||||
float region_y = io.MousePos.y - pos.y - region_sz * 0.5f;
|
||||
float zoom = 4.0f;
|
||||
if (region_x < 0.0f) { region_x = 0.0f; }
|
||||
else if (region_x > my_tex_w - region_sz) { region_x = my_tex_w - region_sz; }
|
||||
if (region_y < 0.0f) { region_y = 0.0f; }
|
||||
else if (region_y > my_tex_h - region_sz) { region_y = my_tex_h - region_sz; }
|
||||
ImGui::Text("Min: (%.2f, %.2f)", region_x, region_y);
|
||||
ImGui::Text("Max: (%.2f, %.2f)", region_x + region_sz, region_y + region_sz);
|
||||
ImVec2 uv0 = ImVec2((region_x) / my_tex_w, (region_y) / my_tex_h);
|
||||
ImVec2 uv1 = ImVec2((region_x + region_sz) / my_tex_w, (region_y + region_sz) / my_tex_h);
|
||||
ImGui::ImageWithBg(my_tex_id, ImVec2(region_sz * zoom, region_sz * zoom), uv0, uv1, ImVec4(0.0f, 0.0f, 0.0f, 1.0f));
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
// Fancy widget
|
||||
ImGui::SeparatorText("Interactive Image Viewer");
|
||||
static ExampleImageViewerData image_viewer;
|
||||
ImVec2 canvas_size(ImGui::GetContentRegionAvail().x, my_tex_h * 2.0f);
|
||||
ExampleImageViewer_DrawOptions(&image_viewer);
|
||||
ExampleImageViewer_DrawCanvas(&image_viewer, canvas_size, my_tex_id, (int)my_tex_w, (int)my_tex_h);
|
||||
|
||||
IMGUI_DEMO_MARKER("Widgets/Images/Textured buttons");
|
||||
ImGui::SeparatorText("Textured Buttons");
|
||||
ImGui::TextWrapped("And now some textured buttons..");
|
||||
static int pressed_count = 0;
|
||||
for (int i = 0; i < 8; i++)
|
||||
@@ -8827,7 +8903,7 @@ static void ShowExampleMenuFile()
|
||||
IMGUI_DEMO_MARKER("Examples/Menu/Options");
|
||||
static bool enabled = true;
|
||||
ImGui::MenuItem("Enabled", "", &enabled);
|
||||
ImGui::BeginChild("child", ImVec2(0, 60), ImGuiChildFlags_Borders);
|
||||
ImGui::BeginChild("child", ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 5.0f), ImGuiChildFlags_Borders);
|
||||
for (int i = 0; i < 10; i++)
|
||||
ImGui::Text("Scrolling Text %d", i);
|
||||
ImGui::EndChild();
|
||||
@@ -9239,6 +9315,28 @@ static void ShowExampleAppConsole(bool* p_open)
|
||||
console.Draw("Example: Console", p_open);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] Example App: Image Viewer / ShowExampleAppImageViewer()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static void ShowExampleAppImageViewer(bool* p_open)
|
||||
{
|
||||
ImFontAtlas* atlas = ImGui::GetIO().Fonts;
|
||||
ImTextureRef tex_ref = atlas->TexRef; // We don't have access to other textures in this demo!
|
||||
int tex_w = atlas->TexData->Width;
|
||||
int tex_h = atlas->TexData->Height;
|
||||
if (ImGui::Begin("Example: Image Viewer", p_open))
|
||||
{
|
||||
static ExampleImageViewerData image_viewer;
|
||||
ExampleImageViewer_DrawOptions(&image_viewer);
|
||||
ImVec2 canvas_size = ImGui::GetContentRegionAvail();
|
||||
ImVec2 canvas_min_size = ImGui::IsWindowAppearing() ? ImVec2(3.0f * tex_w, 4.0f * tex_h) : ImVec2(1.0f, 1.0f);
|
||||
canvas_size = ImVec2(IM_MAX(canvas_size.x, canvas_min_size.x), IM_MAX(canvas_size.y, canvas_min_size.y));
|
||||
ExampleImageViewer_DrawCanvas(&image_viewer, canvas_size, tex_ref, tex_w, tex_h);
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] Example App: Debug Log / ShowExampleAppLog()
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -10131,20 +10229,20 @@ static void ShowExampleAppCustomRendering(bool* p_open)
|
||||
draw_list->AddNgon(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, ngon_sides, th); x += sz + spacing; // N-gon
|
||||
draw_list->AddCircle(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, circle_segments, th); x += sz + spacing; // Circle
|
||||
draw_list->AddEllipse(ImVec2(x + sz*0.5f, y + sz*0.5f), ImVec2(sz*0.5f, sz*0.3f), col, -0.3f, circle_segments, th); x += sz + spacing; // Ellipse
|
||||
draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 0.0f, ImDrawFlags_None, th); x += sz + spacing; // Square
|
||||
draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding, ImDrawFlags_None, th); x += sz + spacing; // Square with all rounded corners
|
||||
draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding, corners_tl_br, th); x += sz + spacing; // Square with two rounded corners
|
||||
draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 0.0f, th); x += sz + spacing; // Square
|
||||
draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding, th); x += sz + spacing; // Square with all rounded corners
|
||||
draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding, th, corners_tl_br); x += sz + spacing; // Square with two rounded corners
|
||||
draw_list->AddTriangle(ImVec2(x+sz*0.5f,y), ImVec2(x+sz, y+sz-0.5f), ImVec2(x, y+sz-0.5f), col, th);x += sz + spacing; // Triangle
|
||||
//draw_list->AddTriangle(ImVec2(x+sz*0.2f,y), ImVec2(x, y+sz-0.5f), ImVec2(x+sz*0.4f, y+sz-0.5f), col, th);x+= sz*0.4f + spacing; // Thin triangle
|
||||
PathConcaveShape(draw_list, x, y, sz); draw_list->PathStroke(col, ImDrawFlags_Closed, th); x += sz + spacing; // Concave Shape
|
||||
PathConcaveShape(draw_list, x, y, sz); draw_list->PathStroke(col, th, ImDrawFlags_Closed); x += sz + spacing; // Concave Shape
|
||||
//draw_list->AddPolyline(concave_shape, IM_COUNTOF(concave_shape), col, ImDrawFlags_Closed, th);
|
||||
draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y), col, th); x += sz + spacing; // Horizontal line (note: drawing a filled rectangle will be faster!)
|
||||
draw_list->AddLine(ImVec2(x, y), ImVec2(x, y + sz), col, th); x += spacing; // Vertical line (note: drawing a filled rectangle will be faster!)
|
||||
draw_list->AddLineH(x, x + sz, y, col, th); x += sz + spacing; // Horizontal line (note: drawing a filled rectangle will be faster!)
|
||||
draw_list->AddLineV(x, y, y + sz, col, th); x += spacing; // Vertical line (note: drawing a filled rectangle will be faster!)
|
||||
draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y + sz), col, th); x += sz + spacing; // Diagonal line
|
||||
|
||||
// Path
|
||||
draw_list->PathArcTo(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, 3.141592f, 3.141592f * -0.5f);
|
||||
draw_list->PathStroke(col, ImDrawFlags_None, th);
|
||||
draw_list->PathStroke(col, th);
|
||||
x += sz + spacing;
|
||||
|
||||
// Quadratic Bezier Curve (3 control points)
|
||||
@@ -10278,9 +10376,9 @@ static void ShowExampleAppCustomRendering(bool* p_open)
|
||||
{
|
||||
const float GRID_STEP = 64.0f;
|
||||
for (float x = fmodf(scrolling.x, GRID_STEP); x < canvas_sz.x; x += GRID_STEP)
|
||||
draw_list->AddLine(ImVec2(canvas_p0.x + x, canvas_p0.y), ImVec2(canvas_p0.x + x, canvas_p1.y), IM_COL32(200, 200, 200, 40));
|
||||
draw_list->AddLineV(canvas_p0.x + x, canvas_p0.y, canvas_p1.y, IM_COL32(200, 200, 200, 40));
|
||||
for (float y = fmodf(scrolling.y, GRID_STEP); y < canvas_sz.y; y += GRID_STEP)
|
||||
draw_list->AddLine(ImVec2(canvas_p0.x, canvas_p0.y + y), ImVec2(canvas_p1.x, canvas_p0.y + y), IM_COL32(200, 200, 200, 40));
|
||||
draw_list->AddLineH(canvas_p0.x, canvas_p1.x, canvas_p0.y + y, IM_COL32(200, 200, 200, 40));
|
||||
}
|
||||
for (int n = 0; n < points.Size; n += 2)
|
||||
draw_list->AddLine(ImVec2(origin.x + points[n].x, origin.y + points[n].y), ImVec2(origin.x + points[n + 1].x, origin.y + points[n + 1].y), IM_COL32(255, 255, 0, 255), 2.0f);
|
||||
@@ -10724,10 +10822,11 @@ struct ExampleAssetsBrowser
|
||||
bool AllowBoxSelect = true; // Will set ImGuiMultiSelectFlags_BoxSelect2d
|
||||
bool AllowBoxSelectInsideSelection = false; // Will set ImGuiMultiSelectFlags_SelectOnClickAlways
|
||||
bool AllowDragUnselected = false; // Will set ImGuiMultiSelectFlags_SelectOnClickRelease
|
||||
float IconSize = 32.0f;
|
||||
float IconSize = 0;
|
||||
int IconSpacing = 10;
|
||||
int IconHitSpacing = 4; // Increase hit-spacing if you want to make it possible to clear or box-select from gaps. Some spacing is required to able to amend with Shift+box-select. Value is small in Explorer.
|
||||
int IconHitSpacing = 4; // Increase hit-spacing if you want to make it possible to clear or box-select from gaps. Some spacing is required to able to amend with Shift+box-select. Value is small in Explorer.
|
||||
bool StretchSpacing = true;
|
||||
bool UseScrollX = false; // Debug: submit twice the number of items per line (overflow horizontally to exercise ScrollX + box-select)
|
||||
|
||||
// State
|
||||
ImVector<ExampleAsset> Items; // Our items
|
||||
@@ -10778,12 +10877,15 @@ struct ExampleAssetsBrowser
|
||||
// Layout: calculate number of icon per line and number of lines
|
||||
LayoutItemSize = ImVec2(floorf(IconSize), floorf(IconSize));
|
||||
LayoutColumnCount = IM_MAX((int)(avail_width / (LayoutItemSize.x + LayoutItemSpacing)), 1);
|
||||
LayoutLineCount = (Items.Size + LayoutColumnCount - 1) / LayoutColumnCount;
|
||||
|
||||
// Layout: when stretching: allocate remaining space to more spacing. Round before division, so item_spacing may be non-integer.
|
||||
if (StretchSpacing && LayoutColumnCount > 1)
|
||||
LayoutItemSpacing = floorf(avail_width - LayoutItemSize.x * LayoutColumnCount) / LayoutColumnCount;
|
||||
|
||||
if (UseScrollX)
|
||||
LayoutColumnCount *= 2;
|
||||
LayoutLineCount = (Items.Size + LayoutColumnCount - 1) / LayoutColumnCount;
|
||||
|
||||
LayoutItemStep = ImVec2(LayoutItemSize.x + LayoutItemSpacing, LayoutItemSize.y + LayoutItemSpacing);
|
||||
LayoutSelectableSpacing = IM_MAX(floorf(LayoutItemSpacing) - IconHitSpacing, 0.0f);
|
||||
LayoutOuterPadding = floorf(LayoutItemSpacing * 0.5f);
|
||||
@@ -10791,6 +10893,9 @@ struct ExampleAssetsBrowser
|
||||
|
||||
void Draw(const char* title, bool* p_open)
|
||||
{
|
||||
if (IconSize <= 0.0f)
|
||||
IconSize = ImGui::CalcTextSize("99999").x;
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(IconSize * 25, IconSize * 15), ImGuiCond_FirstUseEver);
|
||||
if (!ImGui::Begin(title, p_open, ImGuiWindowFlags_MenuBar))
|
||||
{
|
||||
@@ -10840,6 +10945,7 @@ struct ExampleAssetsBrowser
|
||||
ImGui::SliderInt("Icon Spacing", &IconSpacing, 0, 32);
|
||||
ImGui::SliderInt("Icon Hit Spacing", &IconHitSpacing, 0, 32);
|
||||
ImGui::Checkbox("Stretch Spacing", &StretchSpacing);
|
||||
ImGui::Checkbox("Use ScrollX", &UseScrollX);
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
@@ -10869,7 +10975,7 @@ struct ExampleAssetsBrowser
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImGui::SetNextWindowContentSize(ImVec2(0.0f, LayoutOuterPadding + LayoutLineCount * (LayoutItemSize.y + LayoutItemSpacing)));
|
||||
if (ImGui::BeginChild("Assets", ImVec2(0.0f, -ImGui::GetTextLineHeightWithSpacing()), ImGuiChildFlags_Borders, ImGuiWindowFlags_NoMove))
|
||||
if (ImGui::BeginChild("Assets", ImVec2(0.0f, -ImGui::GetTextLineHeightWithSpacing()), ImGuiChildFlags_Borders, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_HorizontalScrollbar))
|
||||
{
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
|
||||
@@ -11013,6 +11119,8 @@ struct ExampleAssetsBrowser
|
||||
}
|
||||
}
|
||||
clipper.End();
|
||||
if (Items.Size == 0)
|
||||
ImGui::Dummy(ImVec2(0, 0));
|
||||
ImGui::PopStyleVar(); // ImGuiStyleVar_ItemSpacing
|
||||
|
||||
// Context menu
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// dear imgui, v1.92.7
|
||||
// dear imgui, v1.92.8
|
||||
// (drawing and font code)
|
||||
|
||||
/*
|
||||
@@ -208,6 +208,7 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst)
|
||||
colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f);
|
||||
colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.51f, 0.51f, 0.51f, 1.00f);
|
||||
colors[ImGuiCol_CheckMark] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
|
||||
colors[ImGuiCol_CheckboxSelectedBg] = ImLerp(colors[ImGuiCol_FrameBg], colors[ImGuiCol_FrameBgHovered], 0.65f);
|
||||
colors[ImGuiCol_SliderGrab] = ImVec4(0.24f, 0.52f, 0.88f, 1.00f);
|
||||
colors[ImGuiCol_SliderGrabActive] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
|
||||
colors[ImGuiCol_Button] = ImVec4(0.26f, 0.59f, 0.98f, 0.40f);
|
||||
@@ -275,6 +276,7 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst)
|
||||
colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.40f, 0.40f, 0.80f, 0.40f);
|
||||
colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.41f, 0.39f, 0.80f, 0.60f);
|
||||
colors[ImGuiCol_CheckMark] = ImVec4(0.90f, 0.90f, 0.90f, 0.50f);
|
||||
colors[ImGuiCol_CheckboxSelectedBg] = ImLerp(colors[ImGuiCol_FrameBg], colors[ImGuiCol_FrameBgActive], 0.65f);
|
||||
colors[ImGuiCol_SliderGrab] = ImVec4(1.00f, 1.00f, 1.00f, 0.30f);
|
||||
colors[ImGuiCol_SliderGrabActive] = ImVec4(0.41f, 0.39f, 0.80f, 0.60f);
|
||||
colors[ImGuiCol_Button] = ImVec4(0.35f, 0.40f, 0.61f, 0.62f);
|
||||
@@ -343,6 +345,7 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst)
|
||||
colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.49f, 0.49f, 0.49f, 0.80f);
|
||||
colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.49f, 0.49f, 0.49f, 1.00f);
|
||||
colors[ImGuiCol_CheckMark] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
|
||||
colors[ImGuiCol_CheckboxSelectedBg] = ImVec4(0.95f, 0.97f, 1.00f, 1.00f);
|
||||
colors[ImGuiCol_SliderGrab] = ImVec4(0.26f, 0.59f, 0.98f, 0.78f);
|
||||
colors[ImGuiCol_SliderGrabActive] = ImVec4(0.46f, 0.54f, 0.80f, 0.60f);
|
||||
colors[ImGuiCol_Button] = ImVec4(0.26f, 0.59f, 0.98f, 0.40f);
|
||||
@@ -528,9 +531,14 @@ void ImDrawList::_PopUnusedDrawCmd()
|
||||
|
||||
void ImDrawList::AddCallback(ImDrawCallback callback, void* userdata, size_t userdata_size)
|
||||
{
|
||||
IM_ASSERT(callback != NULL);
|
||||
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
if (callback == ImDrawCallback_ResetRenderState && _Data->Context != NULL && _Data->Context->PlatformIO.DrawCallback_ResetRenderState != NULL)
|
||||
callback = _Data->Context->PlatformIO.DrawCallback_ResetRenderState; // == ImGui::GetPlatformIO().DrawCallback_ResetRenderState
|
||||
#endif
|
||||
|
||||
IM_ASSERT_PARANOID(CmdBuffer.Size > 0);
|
||||
ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
|
||||
IM_ASSERT(callback != NULL);
|
||||
IM_ASSERT(curr_cmd->UserCallback == NULL);
|
||||
if (curr_cmd->ElemCount != 0)
|
||||
{
|
||||
@@ -662,7 +670,7 @@ void ImDrawList::PushClipRect(const ImVec2& cr_min, const ImVec2& cr_max, bool i
|
||||
if (intersect_with_current_clip_rect)
|
||||
{
|
||||
ImVec4 current = _CmdHeader.ClipRect;
|
||||
if (cr.x < current.x) cr.x = current.x;
|
||||
if (cr.x < current.x) cr.x = current.x; // = ClipWith(). Note that passing inverted range wouldn't be fixed here.
|
||||
if (cr.y < current.y) cr.y = current.y;
|
||||
if (cr.z > current.z) cr.z = current.z;
|
||||
if (cr.w > current.w) cr.w = current.w;
|
||||
@@ -806,7 +814,7 @@ void ImDrawList::PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, c
|
||||
|
||||
// TODO: Thickness anti-aliased lines cap are missing their AA fringe.
|
||||
// We avoid using the ImVec2 math operators here to reduce cost to a minimum for debug/non-inlined builds.
|
||||
void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 col, ImDrawFlags flags, float thickness)
|
||||
void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 col, float thickness, ImDrawFlags flags)
|
||||
{
|
||||
if (points_count < 2 || (col & IM_COL32_A_MASK) == 0)
|
||||
return;
|
||||
@@ -815,6 +823,7 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32
|
||||
const ImVec2 opaque_uv = _Data->TexUvWhitePixel;
|
||||
const int count = closed ? points_count : points_count - 1; // The number of line segments we need to draw
|
||||
const bool thick_line = (thickness > _FringeScale);
|
||||
IM_ASSERT((flags & ImDrawFlags_InvalidMask_) == 0 && "Incorrect parameter. Did you swapped 'thickness' and 'flags'?");
|
||||
|
||||
if (Flags & ImDrawListFlags_AntiAliasedLines)
|
||||
{
|
||||
@@ -1432,35 +1441,13 @@ void ImDrawList::PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3,
|
||||
}
|
||||
}
|
||||
|
||||
static inline ImDrawFlags FixRectCornerFlags(ImDrawFlags flags)
|
||||
{
|
||||
/*
|
||||
IM_STATIC_ASSERT(ImDrawFlags_RoundCornersTopLeft == (1 << 4));
|
||||
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
// Obsoleted in 1.82 (from February 2021). This code was stripped/simplified and mostly commented in 1.90 (from September 2023)
|
||||
// - Legacy Support for hard coded ~0 (used to be a suggested equivalent to ImDrawCornerFlags_All)
|
||||
if (flags == ~0) { return ImDrawFlags_RoundCornersAll; }
|
||||
// - Legacy Support for hard coded 0x01 to 0x0F (matching 15 out of 16 old flags combinations). Read details in older version of this code.
|
||||
if (flags >= 0x01 && flags <= 0x0F) { return (flags << 4); }
|
||||
// We cannot support hard coded 0x00 with 'float rounding > 0.0f' --> replace with ImDrawFlags_RoundCornersNone or use 'float rounding = 0.0f'
|
||||
#endif
|
||||
*/
|
||||
// If this assert triggers, please update your code replacing hardcoded values with new ImDrawFlags_RoundCorners* values.
|
||||
// Note that ImDrawFlags_Closed (== 0x01) is an invalid flag for AddRect(), AddRectFilled(), PathRect() etc. anyway.
|
||||
// See details in 1.82 Changelog as well as 2021/03/12 and 2023/09/08 entries in "API BREAKING CHANGES" section.
|
||||
IM_ASSERT((flags & 0x0F) == 0 && "Misuse of legacy hardcoded ImDrawCornerFlags values!");
|
||||
|
||||
if ((flags & ImDrawFlags_RoundCornersMask_) == 0)
|
||||
flags |= ImDrawFlags_RoundCornersAll;
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
void ImDrawList::PathRect(const ImVec2& a, const ImVec2& b, float rounding, ImDrawFlags flags)
|
||||
{
|
||||
if (rounding >= 0.5f)
|
||||
{
|
||||
flags = FixRectCornerFlags(flags);
|
||||
if ((flags & ImDrawFlags_RoundCornersMask_) == 0)
|
||||
flags |= ImDrawFlags_RoundCornersAll;
|
||||
|
||||
rounding = ImMin(rounding, ImFabs(b.x - a.x) * (((flags & ImDrawFlags_RoundCornersTop) == ImDrawFlags_RoundCornersTop) || ((flags & ImDrawFlags_RoundCornersBottom) == ImDrawFlags_RoundCornersBottom) ? 0.5f : 1.0f) - 1.0f);
|
||||
rounding = ImMin(rounding, ImFabs(b.y - a.y) * (((flags & ImDrawFlags_RoundCornersLeft) == ImDrawFlags_RoundCornersLeft) || ((flags & ImDrawFlags_RoundCornersRight) == ImDrawFlags_RoundCornersRight) ? 0.5f : 1.0f) - 1.0f);
|
||||
}
|
||||
@@ -1490,20 +1477,48 @@ void ImDrawList::AddLine(const ImVec2& p1, const ImVec2& p2, ImU32 col, float th
|
||||
return;
|
||||
PathLineTo(p1 + ImVec2(0.5f, 0.5f));
|
||||
PathLineTo(p2 + ImVec2(0.5f, 0.5f));
|
||||
PathStroke(col, 0, thickness);
|
||||
PathStroke(col, thickness);
|
||||
}
|
||||
|
||||
void ImDrawList::AddLineH(float min_x, float max_x, float y, ImU32 col, float thickness)
|
||||
{
|
||||
if ((col & IM_COL32_A_MASK) == 0)
|
||||
return;
|
||||
PathLineTo(ImVec2(min_x + 0.5f, y + 0.5f)); // Same as AddLine() above.
|
||||
PathLineTo(ImVec2(max_x + 0.5f, y + 0.5f));
|
||||
PathStroke(col, thickness);
|
||||
}
|
||||
|
||||
void ImDrawList::AddLineV(float x, float min_y, float max_y, ImU32 col, float thickness)
|
||||
{
|
||||
if ((col & IM_COL32_A_MASK) == 0)
|
||||
return;
|
||||
PathLineTo(ImVec2(x + 0.5f, min_y + 0.5f)); // Same as AddLine() above.
|
||||
PathLineTo(ImVec2(x + 0.5f, max_y + 0.5f));
|
||||
PathStroke(col, thickness);
|
||||
}
|
||||
|
||||
// p_min = upper-left, p_max = lower-right
|
||||
// Note we don't render 1 pixels sized rectangles properly.
|
||||
void ImDrawList::AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding, ImDrawFlags flags, float thickness)
|
||||
void ImDrawList::AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding, float thickness, ImDrawFlags flags)
|
||||
{
|
||||
// If this assert triggers on legacy code:
|
||||
// - 1.92.8 (2025/04): swapped two last parameters order: flags, thickness --> thickness, flags. This should normally be caught by compile-time type-checking.
|
||||
// - 1.82.0 (2021/03): changed ImDrawCornerFlags to ImDrawFlags_RoundCornersXXX values.
|
||||
// If you used hard-coded 1 to 15 or ~0 in flags to configure corner rounding use the new flags!
|
||||
// - Hard coded support for ~0 == ImDrawFlags_RoundCornersAll.
|
||||
// - Hard coded support for values 0x01 to 0x0F (matching 15 out of 16 old flags combinations) --> see FixRectCornerFlags() in <1.90 code.
|
||||
// - Hard coded 0x00 with 'float rounding > 0.0f' --> replace with ImDrawFlags_RoundCornersNone or use 'float rounding = 0.0f'.
|
||||
// See "API BREAKING CHANGES" section for 1.82 and 1.90.
|
||||
IM_ASSERT((flags & ImDrawFlags_InvalidMask_) == 0 && "Incorrect parameter. Did you swapped 'thickness' and 'flags'?"); // Or misuse of legacy hard-coded ImDrawCornerFlags values
|
||||
|
||||
if ((col & IM_COL32_A_MASK) == 0)
|
||||
return;
|
||||
if (Flags & ImDrawListFlags_AntiAliasedLines)
|
||||
PathRect(p_min + ImVec2(0.50f, 0.50f), p_max - ImVec2(0.50f, 0.50f), rounding, flags);
|
||||
else
|
||||
PathRect(p_min + ImVec2(0.50f, 0.50f), p_max - ImVec2(0.49f, 0.49f), rounding, flags); // Better looking lower-right corner and rounded non-AA shapes.
|
||||
PathStroke(col, ImDrawFlags_Closed, thickness);
|
||||
PathStroke(col, thickness, ImDrawFlags_Closed);
|
||||
}
|
||||
|
||||
void ImDrawList::AddRectFilled(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding, ImDrawFlags flags)
|
||||
@@ -1547,7 +1562,7 @@ void ImDrawList::AddQuad(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, c
|
||||
PathLineTo(p2);
|
||||
PathLineTo(p3);
|
||||
PathLineTo(p4);
|
||||
PathStroke(col, ImDrawFlags_Closed, thickness);
|
||||
PathStroke(col, thickness, ImDrawFlags_Closed);
|
||||
}
|
||||
|
||||
void ImDrawList::AddQuadFilled(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col)
|
||||
@@ -1570,7 +1585,7 @@ void ImDrawList::AddTriangle(const ImVec2& p1, const ImVec2& p2, const ImVec2& p
|
||||
PathLineTo(p1);
|
||||
PathLineTo(p2);
|
||||
PathLineTo(p3);
|
||||
PathStroke(col, ImDrawFlags_Closed, thickness);
|
||||
PathStroke(col, thickness, ImDrawFlags_Closed);
|
||||
}
|
||||
|
||||
void ImDrawList::AddTriangleFilled(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col)
|
||||
@@ -1605,7 +1620,7 @@ void ImDrawList::AddCircle(const ImVec2& center, float radius, ImU32 col, int nu
|
||||
PathArcTo(center, radius - 0.5f, 0.0f, a_max, num_segments - 1);
|
||||
}
|
||||
|
||||
PathStroke(col, ImDrawFlags_Closed, thickness);
|
||||
PathStroke(col, thickness, ImDrawFlags_Closed);
|
||||
}
|
||||
|
||||
void ImDrawList::AddCircleFilled(const ImVec2& center, float radius, ImU32 col, int num_segments)
|
||||
@@ -1641,7 +1656,7 @@ void ImDrawList::AddNgon(const ImVec2& center, float radius, ImU32 col, int num_
|
||||
// Because we are filling a closed shape we remove 1 from the count of segments/points
|
||||
const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments;
|
||||
PathArcTo(center, radius - 0.5f, 0.0f, a_max, num_segments - 1);
|
||||
PathStroke(col, ImDrawFlags_Closed, thickness);
|
||||
PathStroke(col, thickness, ImDrawFlags_Closed);
|
||||
}
|
||||
|
||||
// Guaranteed to honor 'num_segments'
|
||||
@@ -1668,7 +1683,7 @@ void ImDrawList::AddEllipse(const ImVec2& center, const ImVec2& radius, ImU32 co
|
||||
// Because we are filling a closed shape we remove 1 from the count of segments/points
|
||||
const float a_max = IM_PI * 2.0f * ((float)num_segments - 1.0f) / (float)num_segments;
|
||||
PathEllipticalArcTo(center, radius, rot, 0.0f, a_max, num_segments - 1);
|
||||
PathStroke(col, true, thickness);
|
||||
PathStroke(col, thickness, ImDrawFlags_Closed);
|
||||
}
|
||||
|
||||
void ImDrawList::AddEllipseFilled(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot, int num_segments)
|
||||
@@ -1693,7 +1708,7 @@ void ImDrawList::AddBezierCubic(const ImVec2& p1, const ImVec2& p2, const ImVec2
|
||||
|
||||
PathLineTo(p1);
|
||||
PathBezierCubicCurveTo(p2, p3, p4, num_segments);
|
||||
PathStroke(col, 0, thickness);
|
||||
PathStroke(col, thickness);
|
||||
}
|
||||
|
||||
// Quadratic Bezier takes 3 controls points
|
||||
@@ -1704,7 +1719,7 @@ void ImDrawList::AddBezierQuadratic(const ImVec2& p1, const ImVec2& p2, const Im
|
||||
|
||||
PathLineTo(p1);
|
||||
PathBezierQuadraticCurveTo(p2, p3, num_segments);
|
||||
PathStroke(col, 0, thickness);
|
||||
PathStroke(col, thickness);
|
||||
}
|
||||
|
||||
void ImDrawList::AddText(ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end, float wrap_width, const ImVec4* cpu_fine_clip_rect)
|
||||
@@ -1776,7 +1791,10 @@ void ImDrawList::AddImageRounded(ImTextureRef tex_ref, const ImVec2& p_min, cons
|
||||
if ((col & IM_COL32_A_MASK) == 0)
|
||||
return;
|
||||
|
||||
flags = FixRectCornerFlags(flags);
|
||||
IM_ASSERT((flags & 0x0F) == 0 && "Misuse of legacy hardcoded ImDrawCornerFlags values!"); // If this assert triggers on legacy code: see comments in ImDrawList::PathRect().
|
||||
if ((flags & ImDrawFlags_RoundCornersMask_) == 0)
|
||||
flags |= ImDrawFlags_RoundCornersAll;
|
||||
|
||||
if (rounding < 0.5f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone)
|
||||
{
|
||||
AddImage(tex_ref, p_min, p_max, uv_min, uv_max, col);
|
||||
@@ -2502,10 +2520,11 @@ void ImTextureData::DestroyPixels()
|
||||
// - Default texture data encoded in ASCII
|
||||
// - ImFontAtlas()
|
||||
// - ImFontAtlas::Clear()
|
||||
// - ImFontAtlas::CompactCache()
|
||||
// - ImFontAtlas::ClearFonts()
|
||||
// - ImFontAtlas::ClearInputData()
|
||||
// - ImFontAtlas::ClearTexData()
|
||||
// - ImFontAtlas::ClearFonts()
|
||||
// - ImFontAtlas::CompactCache()
|
||||
// - ImFontAtlas::SetFontLoader()
|
||||
//-----------------------------------------------------------------------------
|
||||
// - ImFontAtlasUpdateNewFrame()
|
||||
// - ImFontAtlasTextureBlockConvert()
|
||||
@@ -2666,7 +2685,9 @@ ImFontAtlas::~ImFontAtlas()
|
||||
TexData = NULL;
|
||||
}
|
||||
|
||||
// If you call this mid-frame, you would need to add new font and bind them!
|
||||
// You probably should not call this directly. It is not well specified.
|
||||
// If you want to replace all your fonts mid-frame, most likely you should instead call ClearFonts() then load the new fonts.
|
||||
// Calling this mid-frame will discard the CPU-side copy of the texture data which is generally unreliable as you may have textures queued for creation or updates.
|
||||
void ImFontAtlas::Clear()
|
||||
{
|
||||
bool backup_renderer_has_textures = RendererHasTextures;
|
||||
@@ -2676,20 +2697,27 @@ void ImFontAtlas::Clear()
|
||||
RendererHasTextures = backup_renderer_has_textures;
|
||||
}
|
||||
|
||||
void ImFontAtlas::CompactCache()
|
||||
void ImFontAtlas::ClearFonts()
|
||||
{
|
||||
ImFontAtlasTextureCompact(this);
|
||||
}
|
||||
|
||||
void ImFontAtlas::SetFontLoader(const ImFontLoader* font_loader)
|
||||
{
|
||||
ImFontAtlasBuildSetupFontLoader(this, font_loader);
|
||||
// FIXME-NEWATLAS: Illegal to remove currently bound font.
|
||||
IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!");
|
||||
for (ImFont* font : Fonts)
|
||||
ImFontAtlasBuildNotifySetFont(this, font, NULL);
|
||||
ImFontAtlasBuildDestroy(this);
|
||||
ClearInputData();
|
||||
Fonts.clear_delete();
|
||||
TexIsBuilt = false;
|
||||
for (ImDrawListSharedData* shared_data : DrawListSharedDatas)
|
||||
if (shared_data->FontAtlas == this)
|
||||
{
|
||||
shared_data->Font = NULL;
|
||||
shared_data->FontScale = shared_data->FontSize = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void ImFontAtlas::ClearInputData()
|
||||
{
|
||||
IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!");
|
||||
|
||||
for (ImFont* font : Fonts)
|
||||
ImFontAtlasFontDestroyOutput(this, font);
|
||||
for (ImFontConfig& font_cfg : Sources)
|
||||
@@ -2713,22 +2741,14 @@ void ImFontAtlas::ClearTexData()
|
||||
//Locked = true; // Hoped to be able to lock this down but some reload patterns may not be happy with it.
|
||||
}
|
||||
|
||||
void ImFontAtlas::ClearFonts()
|
||||
void ImFontAtlas::CompactCache()
|
||||
{
|
||||
// FIXME-NEWATLAS: Illegal to remove currently bound font.
|
||||
IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!");
|
||||
for (ImFont* font : Fonts)
|
||||
ImFontAtlasBuildNotifySetFont(this, font, NULL);
|
||||
ImFontAtlasBuildDestroy(this);
|
||||
ClearInputData();
|
||||
Fonts.clear_delete();
|
||||
TexIsBuilt = false;
|
||||
for (ImDrawListSharedData* shared_data : DrawListSharedDatas)
|
||||
if (shared_data->FontAtlas == this)
|
||||
{
|
||||
shared_data->Font = NULL;
|
||||
shared_data->FontScale = shared_data->FontSize = 0.0f;
|
||||
}
|
||||
ImFontAtlasTextureCompact(this);
|
||||
}
|
||||
|
||||
void ImFontAtlas::SetFontLoader(const ImFontLoader* font_loader)
|
||||
{
|
||||
ImFontAtlasBuildSetupFontLoader(this, font_loader);
|
||||
}
|
||||
|
||||
static void ImFontAtlasBuildUpdateRendererHasTexturesFromContext(ImFontAtlas* atlas)
|
||||
@@ -2962,12 +2982,17 @@ void ImFontAtlasTextureBlockCopy(ImTextureData* src_tex, int src_x, int src_y, I
|
||||
memcpy(dst_tex->GetPixelsAt(dst_x, dst_y + y), src_tex->GetPixelsAt(src_x, src_y + y), w * dst_tex->BytesPerPixel);
|
||||
}
|
||||
|
||||
// Queue texture block update for renderer backend
|
||||
void ImFontAtlasTextureBlockQueueUpload(ImFontAtlas* atlas, ImTextureData* tex, int x, int y, int w, int h)
|
||||
{
|
||||
ImTextureDataQueueUpload(tex, x, y, w, h);
|
||||
atlas->TexIsBuilt = false;
|
||||
}
|
||||
|
||||
// Queue texture block update for renderer backend
|
||||
void ImTextureDataQueueUpload(ImTextureData* tex, int x, int y, int w, int h)
|
||||
{
|
||||
IM_ASSERT(tex->Status != ImTextureStatus_WantDestroy && tex->Status != ImTextureStatus_Destroyed);
|
||||
IM_ASSERT(x >= 0 && x <= 0xFFFF && y >= 0 && y <= 0xFFFF && w >= 0 && x + w <= 0x10000 && h >= 0 && y + h <= 0x10000);
|
||||
IM_UNUSED(atlas);
|
||||
|
||||
ImTextureRect req = { (unsigned short)x, (unsigned short)y, (unsigned short)w, (unsigned short)h };
|
||||
int new_x1 = ImMax(tex->UpdateRect.w == 0 ? 0 : tex->UpdateRect.x + tex->UpdateRect.w, req.x + req.w);
|
||||
@@ -2980,7 +3005,6 @@ void ImFontAtlasTextureBlockQueueUpload(ImFontAtlas* atlas, ImTextureData* tex,
|
||||
tex->UsedRect.y = ImMin(tex->UsedRect.y, req.y);
|
||||
tex->UsedRect.w = (unsigned short)(ImMax(tex->UsedRect.x + tex->UsedRect.w, req.x + req.w) - tex->UsedRect.x);
|
||||
tex->UsedRect.h = (unsigned short)(ImMax(tex->UsedRect.y + tex->UsedRect.h, req.y + req.h) - tex->UsedRect.y);
|
||||
atlas->TexIsBuilt = false;
|
||||
|
||||
// No need to queue if status is == ImTextureStatus_WantCreate
|
||||
if (tex->Status == ImTextureStatus_OK || tex->Status == ImTextureStatus_WantUpdates)
|
||||
@@ -3052,7 +3076,7 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg_in)
|
||||
}
|
||||
else
|
||||
{
|
||||
IM_ASSERT(Fonts.Size > 0 && "Cannot use MergeMode for the first font"); // When using MergeMode make sure that a font has already been added before.
|
||||
IM_ASSERT(Fonts.Size > 0 && "Cannot use MergeMode for the first font!"); // When using MergeMode make sure that a font has already been added before.
|
||||
font = font_cfg_in->DstFont ? font_cfg_in->DstFont : Fonts.back();
|
||||
ImFontAtlasFontDiscardBakes(this, font, 0); // Need to discard bakes if the font was already used, because baked->FontLoaderDatas[] will change size. (#9162)
|
||||
}
|
||||
@@ -3080,6 +3104,11 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg_in)
|
||||
IM_ASSERT(font_cfg->FontLoader->FontBakedLoadGlyph != NULL);
|
||||
IM_ASSERT(font_cfg->FontLoader->LoaderInit == NULL && font_cfg->FontLoader->LoaderShutdown == NULL); // FIXME-NEWATLAS: Unsupported yet.
|
||||
}
|
||||
// | Target w/ Implicit RefSize | Target w/ Explicit RefSize |
|
||||
// Adding w/ Implicit RefSize: | OK (same scale) | OK (same scale) |
|
||||
// Adding w/ Explicit RefSize: | KO | OK (custom scale) |
|
||||
if (font_cfg_in->MergeMode && font_cfg_in->SizePixels > 0)
|
||||
IM_ASSERT((font->Flags & ImFontFlags_ImplicitRefSize) == 0 && "Cannot use MergeMode with an explicit reference size when the destination font used an implicit reference size!");
|
||||
IM_ASSERT(font_cfg->FontLoaderData == NULL);
|
||||
|
||||
if (!ImFontAtlasFontSourceInit(this, font_cfg))
|
||||
@@ -3147,7 +3176,10 @@ ImFont* ImFontAtlas::AddFontDefaultBitmap(const ImFontConfig* font_cfg_template)
|
||||
if (!font_cfg_template)
|
||||
font_cfg.PixelSnapH = true; // Prevents sub-integer scaling factors at lower-level layers.
|
||||
if (font_cfg.SizePixels <= 0.0f)
|
||||
{
|
||||
font_cfg.SizePixels = 13.0f; // This only serves (1) as a reference for GlyphOffset.y setting and (2) as a default for pre-1.92 backend.
|
||||
font_cfg.Flags |= ImFontFlags_ImplicitRefSize;
|
||||
}
|
||||
if (font_cfg.Name[0] == '\0')
|
||||
ImFormatString(font_cfg.Name, IM_COUNTOF(font_cfg.Name), "ProggyClean.ttf");
|
||||
font_cfg.EllipsisChar = (ImWchar)0x0085;
|
||||
@@ -3172,7 +3204,10 @@ ImFont* ImFontAtlas::AddFontDefaultVector(const ImFontConfig* font_cfg_template)
|
||||
if (!font_cfg_template)
|
||||
font_cfg.PixelSnapH = true; // Precisely match ProggyClean, but prevents sub-integer scaling factors at lower-level layers.
|
||||
if (font_cfg.SizePixels <= 0.0f)
|
||||
{
|
||||
font_cfg.SizePixels = 13.0f;
|
||||
font_cfg.Flags |= ImFontFlags_ImplicitRefSize;
|
||||
}
|
||||
if (font_cfg.Name[0] == '\0')
|
||||
ImFormatString(font_cfg.Name, IM_COUNTOF(font_cfg.Name), "ProggyForever.ttf");
|
||||
font_cfg.ExtraSizeScale *= 1.015f; // Match ProggyClean
|
||||
@@ -6016,7 +6051,7 @@ void ImGui::RenderCheckMark(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float
|
||||
draw_list->PathLineTo(ImVec2(bx - third, by - third));
|
||||
draw_list->PathLineTo(ImVec2(bx, by));
|
||||
draw_list->PathLineTo(ImVec2(bx + third * 2.0f, by - third * 2.0f));
|
||||
draw_list->PathStroke(col, 0, thickness);
|
||||
draw_list->PathStroke(col, thickness);
|
||||
}
|
||||
|
||||
// Render an arrow. 'pos' is position of the arrow tip. half_sz.x is length from base to tip. half_sz.y is length on each side.
|
||||
|
||||
@@ -95,6 +95,10 @@ static void* (*GImGuiFreeTypeAllocFunc)(size_t size, void* user_data) = ImGuiFre
|
||||
static void (*GImGuiFreeTypeFreeFunc)(void* ptr, void* user_data) = ImGuiFreeTypeDefaultFreeFunc;
|
||||
static void* GImGuiFreeTypeAllocatorUserData = nullptr;
|
||||
|
||||
// Load struct
|
||||
static ImFontLoader GImGuiFreeTypeLoader;
|
||||
static char GImGuiFreeTypeLoaderName[48] = "FreeType";
|
||||
|
||||
// Lunasvg support
|
||||
#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||
static FT_Error ImGuiLunasvgPortInit(FT_Pointer* state);
|
||||
@@ -146,6 +150,7 @@ struct ImGui_ImplFreeType_Data
|
||||
{
|
||||
FT_Library Library;
|
||||
FT_MemoryRec_ MemoryManager;
|
||||
char BackendName[48];
|
||||
ImGui_ImplFreeType_Data() { memset((void*)this, 0, sizeof(*this)); }
|
||||
};
|
||||
|
||||
@@ -359,6 +364,11 @@ static bool ImGui_ImplFreeType_LoaderInit(ImFontAtlas* atlas)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update ImFontLoader::Name field with linked version
|
||||
FT_Int ver_linked_major, ver_linked_minor, ver_linked_patch;
|
||||
FT_Library_Version(bd->Library, &ver_linked_major, &ver_linked_minor, &ver_linked_patch);
|
||||
snprintf(GImGuiFreeTypeLoaderName, sizeof(GImGuiFreeTypeLoaderName), "FreeType (%d.%d.%d; %d.%d.%d)", FREETYPE_MAJOR, FREETYPE_MINOR, FREETYPE_PATCH, ver_linked_major, ver_linked_minor, ver_linked_patch);
|
||||
|
||||
// If you don't call FT_Add_Default_Modules() the rest of code may work, but FreeType won't use our custom allocator.
|
||||
FT_Add_Default_Modules(bd->Library);
|
||||
|
||||
@@ -418,8 +428,9 @@ static bool ImGui_ImplFreeType_FontBakedInit(ImFontAtlas* atlas, ImFontConfig* s
|
||||
{
|
||||
IM_UNUSED(atlas);
|
||||
float size = baked->Size;
|
||||
const float ref_size = baked->OwnerFont->Sources[0]->SizePixels;
|
||||
if (src->MergeMode && src->SizePixels != 0.0f)
|
||||
size *= (src->SizePixels / baked->OwnerFont->Sources[0]->SizePixels);
|
||||
size *= (src->SizePixels / ref_size);
|
||||
size *= src->ExtraSizeScale;
|
||||
|
||||
ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData;
|
||||
@@ -569,18 +580,18 @@ static bool ImGui_ImplFreetype_FontSrcContainsGlyph(ImFontAtlas* atlas, ImFontCo
|
||||
|
||||
const ImFontLoader* ImGuiFreeType::GetFontLoader()
|
||||
{
|
||||
static ImFontLoader loader;
|
||||
loader.Name = "FreeType";
|
||||
loader.LoaderInit = ImGui_ImplFreeType_LoaderInit;
|
||||
loader.LoaderShutdown = ImGui_ImplFreeType_LoaderShutdown;
|
||||
loader.FontSrcInit = ImGui_ImplFreeType_FontSrcInit;
|
||||
loader.FontSrcDestroy = ImGui_ImplFreeType_FontSrcDestroy;
|
||||
loader.FontSrcContainsGlyph = ImGui_ImplFreetype_FontSrcContainsGlyph;
|
||||
loader.FontBakedInit = ImGui_ImplFreeType_FontBakedInit;
|
||||
loader.FontBakedDestroy = ImGui_ImplFreeType_FontBakedDestroy;
|
||||
loader.FontBakedLoadGlyph = ImGui_ImplFreeType_FontBakedLoadGlyph;
|
||||
loader.FontBakedSrcLoaderDataSize = sizeof(ImGui_ImplFreeType_FontSrcBakedData);
|
||||
return &loader;
|
||||
ImFontLoader* loader = &GImGuiFreeTypeLoader;
|
||||
loader->Name = GImGuiFreeTypeLoaderName; // Initially "FreeType" then updated during the call to ImGui_ImplFreeType_LoaderInit()
|
||||
loader->LoaderInit = ImGui_ImplFreeType_LoaderInit;
|
||||
loader->LoaderShutdown = ImGui_ImplFreeType_LoaderShutdown;
|
||||
loader->FontSrcInit = ImGui_ImplFreeType_FontSrcInit;
|
||||
loader->FontSrcDestroy = ImGui_ImplFreeType_FontSrcDestroy;
|
||||
loader->FontSrcContainsGlyph = ImGui_ImplFreetype_FontSrcContainsGlyph;
|
||||
loader->FontBakedInit = ImGui_ImplFreeType_FontBakedInit;
|
||||
loader->FontBakedDestroy = ImGui_ImplFreeType_FontBakedDestroy;
|
||||
loader->FontBakedLoadGlyph = ImGui_ImplFreeType_FontBakedLoadGlyph;
|
||||
loader->FontBakedSrcLoaderDataSize = sizeof(ImGui_ImplFreeType_FontSrcBakedData);
|
||||
return loader;
|
||||
}
|
||||
|
||||
void ImGuiFreeType::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// dear imgui, v1.92.7
|
||||
// dear imgui, v1.92.8
|
||||
// (tables and columns code)
|
||||
|
||||
/*
|
||||
@@ -240,6 +240,7 @@ Index of this file:
|
||||
#pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'int'/'void*', but argument X has type 'unsigned int'/'ImGuiWindow*'
|
||||
#pragma GCC diagnostic ignored "-Wstrict-overflow"
|
||||
#pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
|
||||
#pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may change value
|
||||
#pragma GCC diagnostic ignored "-Wsign-conversion" // warning: conversion to 'xxxx' from 'xxxx' may change the sign of the result
|
||||
#endif
|
||||
|
||||
@@ -1315,14 +1316,32 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
table->InnerWindow->DecoInnerSizeY1 = table_instance->LastFrozenHeight;
|
||||
table_instance->LastFrozenHeight = 0.0f;
|
||||
|
||||
// Initial state
|
||||
ImGuiWindow* inner_window = table->InnerWindow;
|
||||
ImGuiBoxSelectState* bs = &g.BoxSelectState;
|
||||
if (bs->Window == inner_window && bs->UnclipMode)
|
||||
TableApplyExternalUnclipRect(table, bs->UnclipRect);
|
||||
|
||||
// Initial state
|
||||
if (table->Flags & ImGuiTableFlags_NoClip)
|
||||
table->DrawSplitter->SetCurrentChannel(inner_window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP);
|
||||
else
|
||||
inner_window->DrawList->PushClipRect(inner_window->InnerClipRect.Min, inner_window->InnerClipRect.Max, false); // FIXME: use table->InnerClipRect?
|
||||
}
|
||||
|
||||
// When starting a BeginMultiSelect() after table has been layout we update IsRequestOutput fields.
|
||||
void ImGui::TableApplyExternalUnclipRect(ImGuiTable* table, ImRect& rect)
|
||||
{
|
||||
if (rect.IsInverted())
|
||||
return;
|
||||
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
||||
{
|
||||
ImGuiTableColumn* column = &table->Columns[column_n];
|
||||
if (!column->IsRequestOutput)
|
||||
if (rect.Overlaps(ImRect(column->MinX, table->WorkRect.Min.y, column->MaxX, FLT_MAX)))
|
||||
column->IsRequestOutput = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Process hit-testing on resizing borders. Actual size change will be applied in EndTable()
|
||||
// - Set table->HoveredColumnBorder with a short delay/timer to reduce visual feedback noise.
|
||||
void ImGui::TableUpdateBorders(ImGuiTable* table)
|
||||
@@ -1436,12 +1455,12 @@ void ImGui::EndTable()
|
||||
if (table->Flags & ImGuiTableFlags_ScrollX)
|
||||
{
|
||||
const float outer_padding_for_border = (table->Flags & ImGuiTableFlags_BordersOuterV) ? TABLE_BORDER_SIZE : 0.0f;
|
||||
float max_pos_x = table->InnerWindow->DC.CursorMaxPos.x;
|
||||
float max_pos_x = inner_window->DC.CursorMaxPos.x;
|
||||
if (table->RightMostEnabledColumn != -1)
|
||||
max_pos_x = ImMax(max_pos_x, table->Columns[table->RightMostEnabledColumn].WorkMaxX + table->CellPaddingX + table->OuterPaddingX - outer_padding_for_border);
|
||||
if (table->ResizedColumn != -1)
|
||||
max_pos_x = ImMax(max_pos_x, table->ResizeLockMinContentsX2);
|
||||
table->InnerWindow->DC.CursorMaxPos.x = max_pos_x + table->TempData->AngledHeadersExtraWidth;
|
||||
inner_window->DC.CursorMaxPos.x = max_pos_x + table->TempData->AngledHeadersExtraWidth;
|
||||
}
|
||||
|
||||
// Pop clipping rect
|
||||
@@ -1550,7 +1569,7 @@ void ImGui::EndTable()
|
||||
}
|
||||
else
|
||||
{
|
||||
table->InnerWindow->DC.TreeDepth--;
|
||||
inner_window->DC.TreeDepth--;
|
||||
ItemSize(table->OuterRect.GetSize());
|
||||
ItemAdd(table->OuterRect, 0);
|
||||
}
|
||||
@@ -1565,13 +1584,12 @@ void ImGui::EndTable()
|
||||
}
|
||||
else if (temp_data->UserOuterSize.x <= 0.0f)
|
||||
{
|
||||
// Some references for this: #7651 + tests "table_reported_size", "table_reported_size_outer" equivalent Y block
|
||||
// - Checking for ImGuiTableFlags_ScrollX/ScrollY flag makes us a frame ahead when disabling those flags.
|
||||
// - FIXME-TABLE: Would make sense to pre-compute expected scrollbar visibility/sizes to generally save a frame of feedback.
|
||||
const float inner_content_max_x = table->OuterRect.Min.x + table->ColumnsAutoFitWidth; // Slightly misleading name but used for code symmetry with inner_content_max_y
|
||||
const float decoration_size = table->TempData->AngledHeadersExtraWidth + ((table->Flags & ImGuiTableFlags_ScrollY) ? inner_window->ScrollbarSizes.x : 0.0f);
|
||||
outer_window->DC.IdealMaxPos.x = ImMax(outer_window->DC.IdealMaxPos.x, inner_content_max_x + decoration_size - temp_data->UserOuterSize.x);
|
||||
outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, ImMin(table->OuterRect.Max.x, inner_content_max_x + decoration_size));
|
||||
// Some references for this: #7651 + tests "table_reported_size", "table_reported_size_outer" equivalent Y block, #9352
|
||||
// - FIXME-TABLE: Would make sense to pre-compute expected scrollbar visibility/sizes to generally save a frame of feedback? See broken test in 'table_reported_size_outer'
|
||||
const float outer_content_max_x = table->OuterRect.Min.x + table->ColumnsAutoFitWidth;
|
||||
const float decoration_size = table->TempData->AngledHeadersExtraWidth + ((inner_window != outer_window) ? inner_window->ScrollbarSizes.x : 0.0f);
|
||||
outer_window->DC.IdealMaxPos.x = ImMax(outer_window->DC.IdealMaxPos.x, outer_content_max_x + decoration_size - temp_data->UserOuterSize.x);
|
||||
outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, ImMin(table->OuterRect.Max.x, outer_content_max_x + decoration_size));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1579,9 +1597,12 @@ void ImGui::EndTable()
|
||||
}
|
||||
if (temp_data->UserOuterSize.y <= 0.0f)
|
||||
{
|
||||
const float decoration_size = (table->Flags & ImGuiTableFlags_ScrollX) ? inner_window->ScrollbarSizes.y : 0.0f;
|
||||
outer_window->DC.IdealMaxPos.y = ImMax(outer_window->DC.IdealMaxPos.y, inner_content_max_y + decoration_size - temp_data->UserOuterSize.y);
|
||||
outer_window->DC.CursorMaxPos.y = ImMax(backup_outer_max_pos.y, ImMin(table->OuterRect.Max.y, inner_content_max_y + decoration_size));
|
||||
// (same comment as above)
|
||||
const float outer_content_size_y = (inner_window == outer_window) ? (inner_content_max_y - table->InnerRect.Min.y) : (inner_content_max_y - inner_window->DC.CursorStartPos.y);
|
||||
const float outer_content_max_y = table->OuterRect.Min.y + outer_content_size_y;
|
||||
const float decoration_size = (inner_window != outer_window ? inner_window->ScrollbarSizes.y : 0.0f);
|
||||
outer_window->DC.IdealMaxPos.y = ImMax(outer_window->DC.IdealMaxPos.y, outer_content_max_y + decoration_size - temp_data->UserOuterSize.y);
|
||||
outer_window->DC.CursorMaxPos.y = ImMax(backup_outer_max_pos.y, ImMin(table->OuterRect.Max.y, outer_content_max_y + decoration_size));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1641,7 +1662,7 @@ void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, flo
|
||||
ImGuiTable* table = g.CurrentTable;
|
||||
IM_ASSERT_USER_ERROR_RET(table != NULL, "Call should only be done while in BeginTable() scope!");
|
||||
IM_ASSERT_USER_ERROR_RET(table->DeclColumnsCount < table->ColumnsCount, "TableSetupColumn(): called too many times!");
|
||||
IM_ASSERT_USER_ERROR_RET(table->IsLayoutLocked == false, "TableSetupColumn(): need to call before first row!");
|
||||
IM_ASSERT_USER_ERROR_RET(table->IsLayoutLocked == false, "TableSetupColumn(): need to call before first row!"); // Table layout is locked when submitting a row or when calling BeginMultiSelect() with box-select.
|
||||
IM_ASSERT((flags & ImGuiTableColumnFlags_StatusMask_) == 0 && "Illegal to pass StatusMask values to TableSetupColumn()");
|
||||
|
||||
ImGuiTableColumn* column = &table->Columns[table->DeclColumnsCount];
|
||||
@@ -2053,11 +2074,11 @@ void ImGui::TableEndRow(ImGuiTable* table)
|
||||
|
||||
// Draw top border
|
||||
if (top_border_col && bg_y1 >= table->BgClipRect.Min.y && bg_y1 < table->BgClipRect.Max.y)
|
||||
window->DrawList->AddLine(ImVec2(table->BorderX1, bg_y1), ImVec2(table->BorderX2, bg_y1), top_border_col, border_size);
|
||||
window->DrawList->AddLineH(table->BorderX1, table->BorderX2, bg_y1, top_border_col, border_size);
|
||||
|
||||
// Draw bottom border at the row unfreezing mark (always strong)
|
||||
if (draw_strong_bottom_border && bg_y2 >= table->BgClipRect.Min.y && bg_y2 < table->BgClipRect.Max.y)
|
||||
window->DrawList->AddLine(ImVec2(table->BorderX1, bg_y2), ImVec2(table->BorderX2, bg_y2), table->BorderColorStrong, border_size);
|
||||
window->DrawList->AddLineH(table->BorderX1, table->BorderX2, bg_y2, table->BorderColorStrong, border_size);
|
||||
}
|
||||
|
||||
// End frozen rows (when we are past the last frozen row line, teleport cursor and alter clipping rectangle)
|
||||
@@ -2834,7 +2855,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table)
|
||||
else if ((table->Flags & (ImGuiTableFlags_NoBordersInBodyUntilResize | ImGuiTableFlags_NoBordersInBody)) == 0)
|
||||
draw_y2 = draw_y2_body;
|
||||
if (draw_y2 > draw_y1)
|
||||
inner_drawlist->AddLine(ImVec2(column->MaxX, draw_y1), ImVec2(column->MaxX, draw_y2), TableGetColumnBorderCol(table, order_n, column_n), border_size);
|
||||
inner_drawlist->AddLineV(column->MaxX, draw_y1, draw_y2, TableGetColumnBorderCol(table, order_n, column_n), border_size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2851,17 +2872,17 @@ void ImGui::TableDrawBorders(ImGuiTable* table)
|
||||
const ImU32 outer_col = table->BorderColorStrong;
|
||||
if ((table->Flags & ImGuiTableFlags_BordersOuter) == ImGuiTableFlags_BordersOuter)
|
||||
{
|
||||
inner_drawlist->AddRect(outer_border.Min, outer_border.Max, outer_col, 0.0f, 0, border_size);
|
||||
inner_drawlist->AddRect(outer_border.Min, outer_border.Max, outer_col, 0.0f, border_size);
|
||||
}
|
||||
else if (table->Flags & ImGuiTableFlags_BordersOuterV)
|
||||
{
|
||||
inner_drawlist->AddLine(outer_border.Min, ImVec2(outer_border.Min.x, outer_border.Max.y), outer_col, border_size);
|
||||
inner_drawlist->AddLine(ImVec2(outer_border.Max.x, outer_border.Min.y), outer_border.Max, outer_col, border_size);
|
||||
inner_drawlist->AddLineV(outer_border.Min.x, outer_border.Min.y, outer_border.Max.y, outer_col, border_size);
|
||||
inner_drawlist->AddLineV(outer_border.Max.x, outer_border.Min.y, outer_border.Max.y, outer_col, border_size);
|
||||
}
|
||||
else if (table->Flags & ImGuiTableFlags_BordersOuterH)
|
||||
{
|
||||
inner_drawlist->AddLine(outer_border.Min, ImVec2(outer_border.Max.x, outer_border.Min.y), outer_col, border_size);
|
||||
inner_drawlist->AddLine(ImVec2(outer_border.Min.x, outer_border.Max.y), outer_border.Max, outer_col, border_size);
|
||||
inner_drawlist->AddLineH(outer_border.Min.x, outer_border.Max.x, outer_border.Min.y, outer_col, border_size);
|
||||
inner_drawlist->AddLineH(outer_border.Min.x, outer_border.Max.x, outer_border.Max.y, outer_col, border_size);
|
||||
}
|
||||
}
|
||||
if ((table->Flags & ImGuiTableFlags_BordersInnerH) && table->RowPosY2 < table->OuterRect.Max.y)
|
||||
@@ -2869,7 +2890,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table)
|
||||
// Draw bottom-most row border between it is above outer border.
|
||||
const float border_y = table->RowPosY2;
|
||||
if (border_y >= table->BgClipRect.Min.y && border_y < table->BgClipRect.Max.y)
|
||||
inner_drawlist->AddLine(ImVec2(table->BorderX1, border_y), ImVec2(table->BorderX2, border_y), table->BorderColorLight, border_size);
|
||||
inner_drawlist->AddLineH(table->BorderX1, table->BorderX2, border_y, table->BorderColorLight, border_size);
|
||||
}
|
||||
|
||||
inner_drawlist->PopClipRect();
|
||||
@@ -3174,7 +3195,7 @@ void ImGui::TableHeader(const char* label)
|
||||
if (label == NULL)
|
||||
label = "";
|
||||
const char* label_end = FindRenderedTextEnd(label);
|
||||
ImVec2 label_size = CalcTextSize(label, label_end, true);
|
||||
ImVec2 label_size = CalcTextSize(label, label_end, false);
|
||||
ImVec2 label_pos = window->DC.CursorPos;
|
||||
|
||||
// If we already got a row height, there's use that.
|
||||
@@ -3293,6 +3314,8 @@ void ImGui::TableHeader(const char* label)
|
||||
// We don't use BeginPopupContextItem() because we want the popup to stay up even after the column is hidden
|
||||
if (IsPopupOpenRequestForItem(ImGuiPopupFlags_None, id))
|
||||
TableOpenContextMenu(column_n);
|
||||
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);
|
||||
}
|
||||
|
||||
// Unlike TableHeadersRow() it is not expected that you can reimplement or customize this with custom widgets.
|
||||
@@ -4595,7 +4618,7 @@ void ImGui::EndColumns()
|
||||
// Draw column
|
||||
const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator);
|
||||
const float xi = IM_TRUNC(x);
|
||||
window->DrawList->AddLine(ImVec2(xi, y1 + 1.0f), ImVec2(xi, y2), col);
|
||||
window->DrawList->AddLineV(xi, y1 + 1.0f, y2, col);
|
||||
}
|
||||
|
||||
// Apply dragging after drawing the column lines, so our rendered lines are in sync with how items were displayed during the frame.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// dear imgui, v1.92.7
|
||||
// dear imgui, v1.92.8
|
||||
// (widgets code)
|
||||
|
||||
/*
|
||||
@@ -92,6 +92,7 @@ Index of this file:
|
||||
#pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when simplifying division / ..when changing X +- C1 cmp C2 to X cmp C2 -+ C1
|
||||
#pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
|
||||
#pragma GCC diagnostic ignored "-Wcast-qual" // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers
|
||||
#pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may change value
|
||||
#pragma GCC diagnostic ignored "-Wsign-conversion" // warning: conversion to 'xxxx' from 'xxxx' may change the sign of the result
|
||||
#endif
|
||||
|
||||
@@ -401,7 +402,8 @@ void ImGui::LabelTextV(const char* label, const char* fmt, va_list args)
|
||||
const char* value_text_begin, *value_text_end;
|
||||
ImFormatStringToTempBufferV(&value_text_begin, &value_text_end, fmt, args);
|
||||
const ImVec2 value_size = CalcTextSize(value_text_begin, value_text_end, false);
|
||||
const ImVec2 label_size = CalcTextSize(label, NULL, true);
|
||||
const char* label_end = FindRenderedTextEnd(label);
|
||||
const ImVec2 label_size = CalcTextSize(label, label_end, false);
|
||||
|
||||
const ImVec2 pos = window->DC.CursorPos;
|
||||
const ImRect value_bb(pos, pos + ImVec2(w, value_size.y + style.FramePadding.y * 2));
|
||||
@@ -413,7 +415,7 @@ void ImGui::LabelTextV(const char* label, const char* fmt, va_list args)
|
||||
// Render
|
||||
RenderTextClipped(value_bb.Min + style.FramePadding, value_bb.Max, value_text_begin, value_text_end, &value_size, ImVec2(0.0f, 0.0f));
|
||||
if (label_size.x > 0.0f)
|
||||
RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label);
|
||||
RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label, label_end, false);
|
||||
}
|
||||
|
||||
void ImGui::BulletText(const char* fmt, ...)
|
||||
@@ -788,7 +790,8 @@ bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags
|
||||
ImGuiContext& g = *GImGui;
|
||||
const ImGuiStyle& style = g.Style;
|
||||
const ImGuiID id = window->GetID(label);
|
||||
const ImVec2 label_size = CalcTextSize(label, NULL, true);
|
||||
const char* label_end = FindRenderedTextEnd(label);
|
||||
const ImVec2 label_size = CalcTextSize(label, label_end, false);
|
||||
|
||||
ImVec2 pos = window->DC.CursorPos;
|
||||
if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrLineTextBaseOffset) // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag)
|
||||
@@ -810,7 +813,7 @@ bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags
|
||||
|
||||
if (g.LogEnabled)
|
||||
LogSetNextTextDecoration("[", "]");
|
||||
RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb);
|
||||
RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, label_end, &label_size, style.ButtonTextAlign, &bb);
|
||||
|
||||
// Automatically close popups
|
||||
//if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup))
|
||||
@@ -975,7 +978,7 @@ ImRect ImGui::GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis)
|
||||
const float scrollbar_size = window->ScrollbarSizes[axis ^ 1]; // (ScrollbarSizes.x = width of Y scrollbar; ScrollbarSizes.y = height of X scrollbar)
|
||||
IM_ASSERT(scrollbar_size >= 0.0f);
|
||||
const float border_size = IM_ROUND(window->WindowBorderSize * 0.5f);
|
||||
const float border_top = (window->Flags & ImGuiWindowFlags_MenuBar) ? IM_ROUND(g.Style.FrameBorderSize * 0.5f) : 0.0f;
|
||||
const float border_top = (window->Flags & ImGuiWindowFlags_MenuBar) ? IM_ROUND(g.Style.FrameBorderSize * 0.5f) : (window->Flags & ImGuiWindowFlags_NoTitleBar) ? border_size : 0;
|
||||
if (axis == ImGuiAxis_X)
|
||||
return ImRect(inner_rect.Min.x + border_size, ImMax(outer_rect.Min.y + border_size, outer_rect.Max.y - border_size - scrollbar_size), inner_rect.Max.x - border_size, outer_rect.Max.y - border_size);
|
||||
else
|
||||
@@ -1148,7 +1151,7 @@ void ImGui::ImageWithBg(ImTextureRef tex_ref, const ImVec2& image_size, const Im
|
||||
else
|
||||
window->DrawList->AddImage(tex_ref, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col));
|
||||
if (g.Style.ImageBorderSize > 0.0f)
|
||||
window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border), rounding, ImDrawFlags_None, g.Style.ImageBorderSize);
|
||||
window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border), rounding, g.Style.ImageBorderSize);
|
||||
}
|
||||
|
||||
void ImGui::Image(ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1)
|
||||
@@ -1243,7 +1246,8 @@ bool ImGui::Checkbox(const char* label, bool* v)
|
||||
ImGuiContext& g = *GImGui;
|
||||
const ImGuiStyle& style = g.Style;
|
||||
const ImGuiID id = window->GetID(label);
|
||||
const ImVec2 label_size = CalcTextSize(label, NULL, true);
|
||||
const char* label_end = FindRenderedTextEnd(label);
|
||||
const ImVec2 label_size = CalcTextSize(label, label_end, false);
|
||||
|
||||
const float square_sz = GetFrameHeight();
|
||||
const ImVec2 pos = window->DC.CursorPos;
|
||||
@@ -1284,8 +1288,9 @@ bool ImGui::Checkbox(const char* label, bool* v)
|
||||
if (is_visible)
|
||||
{
|
||||
RenderNavCursor(total_bb, id);
|
||||
RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding);
|
||||
ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : (mixed_value || checked) ? ImGuiCol_CheckboxSelectedBg : ImGuiCol_FrameBg);
|
||||
ImU32 check_col = GetColorU32(ImGuiCol_CheckMark);
|
||||
RenderFrame(check_bb.Min, check_bb.Max, bg_col, true, style.FrameRounding);
|
||||
if (mixed_value)
|
||||
{
|
||||
// Undocumented tristate/mixed/indeterminate checkbox (#2644)
|
||||
@@ -1303,7 +1308,7 @@ bool ImGui::Checkbox(const char* label, bool* v)
|
||||
if (g.LogEnabled)
|
||||
LogRenderedText(&label_pos, mixed_value ? "[~]" : *v ? "[x]" : "[ ]");
|
||||
if (is_visible && label_size.x > 0.0f)
|
||||
RenderText(label_pos, label);
|
||||
RenderText(label_pos, label, label_end, false);
|
||||
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0));
|
||||
return pressed;
|
||||
@@ -1365,7 +1370,8 @@ bool ImGui::RadioButton(const char* label, bool active)
|
||||
ImGuiContext& g = *GImGui;
|
||||
const ImGuiStyle& style = g.Style;
|
||||
const ImGuiID id = window->GetID(label);
|
||||
const ImVec2 label_size = CalcTextSize(label, NULL, true);
|
||||
const char* label_end = FindRenderedTextEnd(label);
|
||||
const ImVec2 label_size = CalcTextSize(label, label_end, false);
|
||||
|
||||
const float square_sz = GetFrameHeight();
|
||||
const ImVec2 pos = window->DC.CursorPos;
|
||||
@@ -1404,7 +1410,7 @@ bool ImGui::RadioButton(const char* label, bool active)
|
||||
if (g.LogEnabled)
|
||||
LogRenderedText(&label_pos, active ? "(x)" : "( )");
|
||||
if (label_size.x > 0.0f)
|
||||
RenderText(label_pos, label);
|
||||
RenderText(label_pos, label, label_end, false);
|
||||
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);
|
||||
return pressed;
|
||||
@@ -1520,7 +1526,7 @@ bool ImGui::TextLink(const char* label)
|
||||
const char* label_end = FindRenderedTextEnd(label);
|
||||
|
||||
ImVec2 pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);
|
||||
ImVec2 size = CalcTextSize(label, label_end, true);
|
||||
ImVec2 size = CalcTextSize(label, label_end, false);
|
||||
ImRect bb(pos, pos + size);
|
||||
ItemSize(size, 0.0f);
|
||||
if (!ItemAdd(bb, id))
|
||||
@@ -1551,10 +1557,10 @@ bool ImGui::TextLink(const char* label)
|
||||
}
|
||||
|
||||
float line_y = bb.Max.y + ImFloor(g.FontBaked->Descent * g.FontBakedScale * 0.20f);
|
||||
window->DrawList->AddLine(ImVec2(bb.Min.x, line_y), ImVec2(bb.Max.x, line_y), GetColorU32(line_colf), 1.0f * (float)(int)g.Style._MainScale); // FIXME-TEXT: Underline mode // FIXME-DPI
|
||||
window->DrawList->AddLineH(bb.Min.x, bb.Max.x, line_y, GetColorU32(line_colf), 1.0f * (float)(int)g.Style._MainScale); // FIXME-TEXT: Underline mode // FIXME-DPI
|
||||
|
||||
PushStyleColor(ImGuiCol_Text, GetColorU32(text_colf));
|
||||
RenderText(bb.Min, label, label_end);
|
||||
RenderText(bb.Min, label, label_end, false);
|
||||
PopStyleColor();
|
||||
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);
|
||||
@@ -1724,7 +1730,7 @@ void ImGui::Separator()
|
||||
if (window->DC.CurrentColumns)
|
||||
flags |= ImGuiSeparatorFlags_SpanAllColumns;
|
||||
|
||||
SeparatorEx(flags, g.Style.SeparatorSize);
|
||||
SeparatorEx(flags, ImMax(g.Style.SeparatorSize, 1.0f));
|
||||
}
|
||||
|
||||
void ImGui::SeparatorTextEx(ImGuiID id, const char* label, const char* label_end, float extra_w)
|
||||
@@ -1761,9 +1767,9 @@ void ImGui::SeparatorTextEx(ImGuiID id, const char* label, const char* label_end
|
||||
const float sep1_x2 = label_pos.x - style.ItemSpacing.x;
|
||||
const float sep2_x1 = label_pos.x + label_size.x + extra_w + style.ItemSpacing.x;
|
||||
if (sep1_x2 > sep1_x1 && separator_thickness > 0.0f)
|
||||
window->DrawList->AddLine(ImVec2(sep1_x1, seps_y), ImVec2(sep1_x2, seps_y), separator_col, separator_thickness);
|
||||
window->DrawList->AddLineH(sep1_x1, sep1_x2, seps_y, separator_col, separator_thickness);
|
||||
if (sep2_x2 > sep2_x1 && separator_thickness > 0.0f)
|
||||
window->DrawList->AddLine(ImVec2(sep2_x1, seps_y), ImVec2(sep2_x2, seps_y), separator_col, separator_thickness);
|
||||
window->DrawList->AddLineH(sep2_x1, sep2_x2, seps_y, separator_col, separator_thickness);
|
||||
if (g.LogEnabled)
|
||||
LogSetNextTextDecoration("---", NULL);
|
||||
RenderTextEllipsis(window->DrawList, label_pos, ImVec2(bb.Max.x, bb.Max.y + style.ItemSpacing.y), bb.Max.x, label, label_end, &label_size);
|
||||
@@ -1773,7 +1779,7 @@ void ImGui::SeparatorTextEx(ImGuiID id, const char* label, const char* label_end
|
||||
if (g.LogEnabled)
|
||||
LogText("---");
|
||||
if (separator_thickness > 0.0f)
|
||||
window->DrawList->AddLine(ImVec2(sep1_x1, seps_y), ImVec2(sep2_x2, seps_y), separator_col, separator_thickness);
|
||||
window->DrawList->AddLineH(sep1_x1, sep2_x2, seps_y, separator_col, separator_thickness);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1944,8 +1950,9 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboF
|
||||
IM_ASSERT((flags & (ImGuiComboFlags_NoPreview | (ImGuiComboFlags)ImGuiComboFlags_CustomPreview)) == 0);
|
||||
|
||||
const float arrow_size = (flags & ImGuiComboFlags_NoArrowButton) ? 0.0f : GetFrameHeight();
|
||||
const ImVec2 label_size = CalcTextSize(label, NULL, true);
|
||||
const float preview_width = ((flags & ImGuiComboFlags_WidthFitPreview) && (preview_value != NULL)) ? CalcTextSize(preview_value, NULL, true).x : 0.0f;
|
||||
const char* label_end = FindRenderedTextEnd(label);
|
||||
const ImVec2 label_size = CalcTextSize(label, label_end, false);
|
||||
const float preview_width = ((flags & ImGuiComboFlags_WidthFitPreview) && (preview_value != NULL)) ? CalcTextSize(preview_value, NULL, false).x : 0.0f;
|
||||
const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : ((flags & ImGuiComboFlags_WidthFitPreview) ? (arrow_size + preview_width + style.FramePadding.x * 2.0f) : CalcItemWidth());
|
||||
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f));
|
||||
const ImRect total_bb(bb.Min, bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
|
||||
@@ -1996,7 +2003,7 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboF
|
||||
RenderTextClipped(bb.Min + style.FramePadding, ImVec2(value_x2, bb.Max.y), preview_value, NULL, NULL);
|
||||
}
|
||||
if (label_size.x > 0)
|
||||
RenderText(ImVec2(bb.Max.x + style.ItemInnerSpacing.x, bb.Min.y + style.FramePadding.y), label);
|
||||
RenderText(ImVec2(bb.Max.x + style.ItemInnerSpacing.x, bb.Min.y + style.FramePadding.y), label, label_end, false);
|
||||
|
||||
if (!popup_open)
|
||||
return false;
|
||||
@@ -2369,12 +2376,17 @@ bool ImGui::DataTypeApplyFromText(const char* buf, ImGuiDataType data_type, void
|
||||
|
||||
// Sanitize format
|
||||
// - For float/double we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in, so force them into %f and %lf
|
||||
// - In theory could treat empty format as using default, but this would only cover rare/bizarre case of using InputScalar() + integer + format string without %.
|
||||
char format_sanitized[32];
|
||||
if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double)
|
||||
{
|
||||
format = type_info->ScanFmt;
|
||||
}
|
||||
else
|
||||
{
|
||||
format = ImParseFormatSanitizeForScanning(format, format_sanitized, IM_COUNTOF(format_sanitized));
|
||||
if (format[0] == '\0')
|
||||
format = type_info->ScanFmt; // Format doesn't want us to show the number currently, but we still need to parse the resulting input
|
||||
}
|
||||
|
||||
// Small types need a 32-bit buffer to receive the result from scanf()
|
||||
int v32 = 0;
|
||||
@@ -2709,7 +2721,8 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data,
|
||||
const float w = CalcItemWidth();
|
||||
const ImU32 color_marker = (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasColorMarker) ? g.NextItemData.ColorMarker : 0;
|
||||
|
||||
const ImVec2 label_size = CalcTextSize(label, NULL, true);
|
||||
const char* label_end = FindRenderedTextEnd(label);
|
||||
const ImVec2 label_size = CalcTextSize(label, label_end, false);
|
||||
const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f));
|
||||
const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
|
||||
|
||||
@@ -2785,7 +2798,7 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data,
|
||||
RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f));
|
||||
|
||||
if (label_size.x > 0.0f)
|
||||
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
|
||||
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label, label_end, false);
|
||||
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | (temp_input_allowed ? ImGuiItemStatusFlags_Inputable : 0));
|
||||
return value_changed;
|
||||
@@ -3310,7 +3323,8 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat
|
||||
const float w = CalcItemWidth();
|
||||
const ImU32 color_marker = (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasColorMarker) ? g.NextItemData.ColorMarker : 0;
|
||||
|
||||
const ImVec2 label_size = CalcTextSize(label, NULL, true);
|
||||
const char* label_end = FindRenderedTextEnd(label);
|
||||
const ImVec2 label_size = CalcTextSize(label, label_end, false);
|
||||
const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f));
|
||||
const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
|
||||
|
||||
@@ -3382,7 +3396,7 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat
|
||||
RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f));
|
||||
|
||||
if (label_size.x > 0.0f)
|
||||
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
|
||||
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label, label_end, false);
|
||||
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | (temp_input_allowed ? ImGuiItemStatusFlags_Inputable : 0));
|
||||
return value_changed;
|
||||
@@ -3487,7 +3501,8 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d
|
||||
const ImGuiStyle& style = g.Style;
|
||||
const ImGuiID id = window->GetID(label);
|
||||
|
||||
const ImVec2 label_size = CalcTextSize(label, NULL, true);
|
||||
const char* label_end = FindRenderedTextEnd(label);
|
||||
const ImVec2 label_size = CalcTextSize(label, label_end, false);
|
||||
const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size);
|
||||
const ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
|
||||
|
||||
@@ -3532,8 +3547,9 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d
|
||||
const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_COUNTOF(value_buf), data_type, p_data, format);
|
||||
RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.0f));
|
||||
if (label_size.x > 0.0f)
|
||||
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
|
||||
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label, label_end, false);
|
||||
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);
|
||||
return value_changed;
|
||||
}
|
||||
|
||||
@@ -3556,7 +3572,8 @@ bool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min,
|
||||
// - ImParseFormatSanitizeForPrinting() [Internal]
|
||||
// - ImParseFormatSanitizeForScanning() [Internal]
|
||||
// - ImParseFormatPrecision() [Internal]
|
||||
// - TempInputTextScalar() [Internal]
|
||||
// - TempInputText() [Internal]
|
||||
// - TempInputScalar() [Internal]
|
||||
// - InputScalar()
|
||||
// - InputScalarN()
|
||||
// - InputFloat()
|
||||
@@ -3785,7 +3802,7 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data
|
||||
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiStyle& style = g.Style;
|
||||
IM_ASSERT((flags & ImGuiInputTextFlags_EnterReturnsTrue) == 0); // Not supported by InputScalar(). Please open an issue if you this would be useful to you. Otherwise use IsItemDeactivatedAfterEdit()!
|
||||
//IM_ASSERT((flags & ImGuiInputTextFlags_EnterReturnsTrue) == 0); // Not supported by InputScalar(). Please open an issue if you this would be useful to you. Otherwise use IsItemDeactivatedAfterEdit()!
|
||||
|
||||
if (format == NULL)
|
||||
format = DataTypeGetInfo(data_type)->PrintFmt;
|
||||
@@ -3822,7 +3839,8 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data
|
||||
}
|
||||
|
||||
// Apply
|
||||
bool value_changed = ret ? DataTypeApplyFromText(buf, data_type, p_data, format, (flags & ImGuiInputTextFlags_ParseEmptyRefVal) ? p_data_default : NULL) : false;
|
||||
bool input_edited = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_EditedInternal) != 0; // We would be using 'ret' if ImGuiInputTextFlags_EnterReturnsTrue was not involved.
|
||||
bool value_changed = input_edited ? DataTypeApplyFromText(buf, data_type, p_data, format, (flags & ImGuiInputTextFlags_ParseEmptyRefVal) ? p_data_default : NULL) : false;
|
||||
|
||||
// Step buttons
|
||||
if (has_step_buttons)
|
||||
@@ -3836,13 +3854,13 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data
|
||||
if (ButtonEx("-", ImVec2(button_size, button_size)))
|
||||
{
|
||||
DataTypeApplyOp(data_type, '-', p_data, p_data, g.IO.KeyCtrl && p_step_fast ? p_step_fast : p_step);
|
||||
value_changed = true;
|
||||
value_changed = ret = true;
|
||||
}
|
||||
SameLine(0, style.ItemInnerSpacing.x);
|
||||
if (ButtonEx("+", ImVec2(button_size, button_size)))
|
||||
{
|
||||
DataTypeApplyOp(data_type, '+', p_data, p_data, g.IO.KeyCtrl && p_step_fast ? p_step_fast : p_step);
|
||||
value_changed = true;
|
||||
value_changed = ret = true;
|
||||
}
|
||||
PopItemFlag();
|
||||
if (flags & ImGuiInputTextFlags_ReadOnly)
|
||||
@@ -3864,6 +3882,8 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data
|
||||
if (value_changed)
|
||||
MarkItemEdited(g.LastItemData.ID);
|
||||
|
||||
if (flags & ImGuiInputTextFlags_EnterReturnsTrue)
|
||||
return ret;
|
||||
return value_changed;
|
||||
}
|
||||
|
||||
@@ -4513,6 +4533,9 @@ static bool InputTextFilterCharacter(ImGuiContext* ctx, ImGuiInputTextState* sta
|
||||
callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter;
|
||||
callback_data.EventChar = (ImWchar)c;
|
||||
callback_data.EventActivated = (g.ActiveId == state->ID && g.ActiveIdIsJustActivated);
|
||||
callback_data.CursorPos = state->Stb->cursor;
|
||||
callback_data.SelectionStart = state->Stb->select_start;
|
||||
callback_data.SelectionEnd = state->Stb->select_end;
|
||||
callback_data.UserData = user_data;
|
||||
if (callback(&callback_data) != 0)
|
||||
return false;
|
||||
@@ -4561,6 +4584,7 @@ void ImGui::InputTextDeactivateHook(ImGuiID id)
|
||||
ImGuiInputTextState* state = &g.InputTextState;
|
||||
if (id == 0 || state->ID != id)
|
||||
return;
|
||||
//IMGUI_DEBUG_LOG_ACTIVEID("InputTextDeactivateHook() id = 0x%08X\n", id);
|
||||
g.InputTextDeactivatedState.ID = state->ID;
|
||||
if (state->Flags & ImGuiInputTextFlags_ReadOnly)
|
||||
{
|
||||
@@ -4695,7 +4719,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
if (is_multiline) // Open group before calling GetID() because groups tracks id created within their scope (including the scrollbar)
|
||||
BeginGroup();
|
||||
const ImGuiID id = window->GetID(label);
|
||||
const ImVec2 label_size = CalcTextSize(label, NULL, true);
|
||||
const char* label_end = FindRenderedTextEnd(label);
|
||||
const ImVec2 label_size = CalcTextSize(label, label_end, false);
|
||||
const ImVec2 frame_size = CalcItemSize(size_arg, CalcItemWidth(), (is_multiline ? g.FontSize * 8.0f : label_size.y) + style.FramePadding.y * 2.0f); // Arbitrary default of 8 lines high for multi-line
|
||||
const ImVec2 total_size = ImVec2(frame_size.x + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), frame_size.y);
|
||||
|
||||
@@ -4709,7 +4734,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
{
|
||||
ImVec2 backup_pos = window->DC.CursorPos;
|
||||
ItemSize(total_bb, style.FramePadding.y);
|
||||
if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_Inputable))
|
||||
bool no_clip = (g.InputTextDeactivatedState.ID == id) || (g.ActiveId == id) || (id == g.NavActivateId); // Mimic some of ItemAdd() logic + add InputTextDeactivatedState.ID check.
|
||||
if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_Inputable) && !no_clip)
|
||||
{
|
||||
EndGroup();
|
||||
return false;
|
||||
@@ -4735,7 +4761,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
g.NavActivateId = backup_activate_id;
|
||||
PopStyleVar(3);
|
||||
PopStyleColor();
|
||||
if (!child_visible)
|
||||
if (!child_visible && !no_clip)
|
||||
{
|
||||
EndChild();
|
||||
EndGroup();
|
||||
@@ -4799,7 +4825,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
float scroll_y = is_multiline ? draw_window->Scroll.y : FLT_MAX;
|
||||
|
||||
const bool init_reload_from_user_buf = (state != NULL && state->WantReloadUserBuf);
|
||||
const bool init_changed_specs = (state != NULL && state->Stb->single_line != !is_multiline); // state != NULL means its our state.
|
||||
const bool init_changed_specs_multiline = (state != NULL && (state->Stb->single_line != !is_multiline)); // state != NULL means its our state.
|
||||
const bool init_changed_specs_readonly = (state != NULL && ((state->Flags ^ flags) & ImGuiInputTextFlags_ReadOnly)); // state != NULL means its our state.
|
||||
const bool init_make_active = (input_requested_by_user || input_requested_by_nav || input_requested_by_reactivate || user_scroll_finish);
|
||||
if (init_reload_from_user_buf)
|
||||
{
|
||||
@@ -4813,7 +4840,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
state->Stb->select_start = state->ReloadSelectionStart;
|
||||
state->Stb->cursor = state->Stb->select_end = state->ReloadSelectionEnd; // will be clamped to bounds below
|
||||
}
|
||||
else if ((init_make_active && g.ActiveId != id) || init_changed_specs)
|
||||
else if ((init_make_active && g.ActiveId != id) || init_changed_specs_multiline || init_changed_specs_readonly)
|
||||
{
|
||||
// Access state even if we don't own it yet.
|
||||
state = &g.InputTextState;
|
||||
@@ -4834,8 +4861,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
|
||||
// Preserve cursor position and undo/redo stack if we come back to same widget
|
||||
// FIXME: Since we reworked this on 2022/06, may want to differentiate recycle_cursor vs recycle_undostate?
|
||||
bool recycle_state = (state->ID == id && !init_changed_specs);
|
||||
if (recycle_state && (state->TextLen != buf_len || (state->TextA.Data == NULL || strncmp(state->TextA.Data, buf, buf_len) != 0)))
|
||||
bool recycle_state = (state->ID == id && !init_changed_specs_multiline);
|
||||
if (recycle_state && !init_changed_specs_readonly && (state->TextLen != buf_len || (state->TextA.Data == NULL || strncmp(state->TextA.Data, buf, buf_len) != 0)))
|
||||
recycle_state = false;
|
||||
|
||||
// Start edition
|
||||
@@ -4873,7 +4900,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
}
|
||||
|
||||
const bool is_osx = io.ConfigMacOSXBehaviors;
|
||||
if (g.ActiveId != id && init_make_active)
|
||||
if (init_make_active && g.ActiveId != id)
|
||||
{
|
||||
IM_ASSERT(state && state->ID == id);
|
||||
SetActiveID(id, window);
|
||||
@@ -4944,13 +4971,17 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
if (is_password && !is_displaying_hint)
|
||||
PushPasswordFont();
|
||||
|
||||
// Word-wrapping: attempt to keep cursor in view while resizing frame/parent
|
||||
// FIXME-WORDWRAP: It would be better to preserve same relative offset.
|
||||
if (is_wordwrap && state != NULL && state->ID == id && state->WrapWidth != wrap_width)
|
||||
if (state != NULL && state->ID == id)
|
||||
{
|
||||
state->CursorCenterY = true;
|
||||
state->WrapWidth = wrap_width;
|
||||
render_cursor = true;
|
||||
state->Flags = flags;
|
||||
|
||||
// Word-wrapping: attempt to keep cursor in view while resizing frame/parent (FIXME-WORDWRAP: would be better to preserve same relative offset)
|
||||
if (is_wordwrap && state->WrapWidth != wrap_width)
|
||||
{
|
||||
state->CursorCenterY = true;
|
||||
state->WrapWidth = wrap_width;
|
||||
render_cursor = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Process mouse inputs and character inputs
|
||||
@@ -4959,7 +4990,6 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
IM_ASSERT(state != NULL);
|
||||
state->EditedThisFrame = false;
|
||||
state->BufCapacity = buf_size;
|
||||
state->Flags = flags;
|
||||
state->WrapWidth = wrap_width;
|
||||
|
||||
// Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget.
|
||||
@@ -5611,7 +5641,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
ImVec2 cursor_screen_pos = ImTrunc(draw_pos + cursor_offset - draw_scroll);
|
||||
ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y - g.FontSize + 0.5f, cursor_screen_pos.x + 1.0f, cursor_screen_pos.y - 1.5f);
|
||||
if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect))
|
||||
draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_InputTextCursor), 1.0f * (float)(int)style._MainScale); // FIXME-DPI: Cursor thickness (#7031)
|
||||
draw_window->DrawList->AddLineV(cursor_screen_rect.Min.x, cursor_screen_rect.Min.y, cursor_screen_rect.Max.y, GetColorU32(ImGuiCol_InputTextCursor), 1.0f * (float)(int)style._MainScale); // FIXME-DPI: Cursor thickness (#7031)
|
||||
|
||||
// Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.)
|
||||
// This is required for some backends (SDL3) to start emitting character/text inputs.
|
||||
@@ -5659,7 +5689,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
||||
}
|
||||
|
||||
if (label_size.x > 0)
|
||||
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
|
||||
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label, label_end, false);
|
||||
|
||||
if (value_changed)
|
||||
MarkItemEdited(id);
|
||||
@@ -6325,7 +6355,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
|
||||
const float a1 = (n+1.0f)/6.0f * 2.0f * IM_PI + aeps;
|
||||
const int vert_start_idx = draw_list->VtxBuffer.Size;
|
||||
draw_list->PathArcTo(wheel_center, (wheel_r_inner + wheel_r_outer)*0.5f, a0, a1, segment_per_arc);
|
||||
draw_list->PathStroke(col_white, 0, wheel_thickness);
|
||||
draw_list->PathStroke(col_white, wheel_thickness);
|
||||
const int vert_end_idx = draw_list->VtxBuffer.Size;
|
||||
|
||||
// Paint colors over existing vertices
|
||||
@@ -6469,7 +6499,7 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl
|
||||
if (g.Style.FrameBorderSize > 0.0f)
|
||||
RenderFrameBorder(bb.Min, bb.Max, rounding);
|
||||
else
|
||||
window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding, 0, 1.0f * (float)(int)g.Style._MainScale); // Color buttons are often in need of some sort of border // FIXME-DPI
|
||||
window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding, 1.0f * (float)(int)g.Style._MainScale); // Color buttons are often in need of some sort of border // FIXME-DPI
|
||||
}
|
||||
|
||||
// Drag and Drop Source
|
||||
@@ -7142,11 +7172,11 @@ void ImGui::TreeNodeDrawLineToChildNode(const ImVec2& target_pos)
|
||||
window->DrawList->PathArcToFast(ImVec2(x1, y - rounding), rounding, 6, 3);
|
||||
if (x1 < x2)
|
||||
window->DrawList->PathLineTo(ImVec2(x2, y));
|
||||
window->DrawList->PathStroke(GetColorU32(ImGuiCol_TreeLines), ImDrawFlags_None, g.Style.TreeLinesSize);
|
||||
window->DrawList->PathStroke(GetColorU32(ImGuiCol_TreeLines), g.Style.TreeLinesSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
window->DrawList->AddLine(ImVec2(x1, y), ImVec2(x2, y), GetColorU32(ImGuiCol_TreeLines), g.Style.TreeLinesSize);
|
||||
window->DrawList->AddLineH(x1, x2, y, GetColorU32(ImGuiCol_TreeLines), g.Style.TreeLinesSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7172,7 +7202,7 @@ void ImGui::TreeNodeDrawLineToTreePop(const ImGuiTreeNodeStackData* data)
|
||||
float x = ImTrunc(data->DrawLinesX1);
|
||||
if (data->DrawLinesTableColumn != -1)
|
||||
TablePushColumnChannel(data->DrawLinesTableColumn);
|
||||
window->DrawList->AddLine(ImVec2(x, y1), ImVec2(x, y2), GetColorU32(ImGuiCol_TreeLines), g.Style.TreeLinesSize);
|
||||
window->DrawList->AddLineV(x, y1, y2, GetColorU32(ImGuiCol_TreeLines), g.Style.TreeLinesSize);
|
||||
if (data->DrawLinesTableColumn != -1)
|
||||
TablePopColumnChannel();
|
||||
}
|
||||
@@ -7331,7 +7361,8 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
|
||||
|
||||
// Submit label or explicit size to ItemSize(), whereas ItemAdd() will submit a larger/spanning rectangle.
|
||||
ImGuiID id = window->GetID(label);
|
||||
ImVec2 label_size = CalcTextSize(label, NULL, true);
|
||||
const char* label_end = FindRenderedTextEnd(label);
|
||||
ImVec2 label_size = CalcTextSize(label, label_end, false);
|
||||
ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y);
|
||||
ImVec2 pos = window->DC.CursorPos;
|
||||
pos.y += window->DC.CurrLineTextBaseOffset;
|
||||
@@ -7486,7 +7517,11 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
|
||||
|
||||
// Text stays at the submission position. Alignment/clipping extents ignore SpanAllColumns.
|
||||
if (is_visible)
|
||||
RenderTextClipped(pos, ImVec2(ImMin(pos.x + size.x, window->WorkRect.Max.x), pos.y + size.y), label, NULL, &label_size, style.SelectableTextAlign, &bb);
|
||||
RenderTextClipped(pos, ImVec2(ImMin(pos.x + size.x, window->WorkRect.Max.x), pos.y + size.y), label, label_end, &label_size, style.SelectableTextAlign, &bb);
|
||||
|
||||
#ifdef IMGUI_DEBUG_BOXSELECT
|
||||
if (g.BoxSelectState.UnclipMode) { GetForegroundDrawList()->AddText(pos, IM_COL32(255,255,0,200), label, label_end); }
|
||||
#endif
|
||||
|
||||
// Automatically close popups
|
||||
if (pressed && !auto_selected && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_NoAutoClosePopups) && (g.LastItemData.ItemFlags & ImGuiItemFlags_AutoClosePopups))
|
||||
@@ -7803,7 +7838,7 @@ bool ImGui::BeginBoxSelect(const ImRect& scope_rect, ImGuiWindow* window, ImGuiI
|
||||
return false;
|
||||
|
||||
// Current frame absolute prev/current rectangles are used to toggle selection.
|
||||
// They are derived from positions relative to scrolling space.
|
||||
// They are derived from positions relative to scrolling space, so "previous" rectangle is reprojected for current frame coordinates.
|
||||
ImVec2 start_pos_abs = WindowPosRelToAbs(window, bs->StartPosRel);
|
||||
ImVec2 prev_end_pos_abs = WindowPosRelToAbs(window, bs->EndPosRel); // Clamped already
|
||||
ImVec2 curr_end_pos_abs = g.IO.MousePos;
|
||||
@@ -7813,20 +7848,69 @@ bool ImGui::BeginBoxSelect(const ImRect& scope_rect, ImGuiWindow* window, ImGuiI
|
||||
bs->BoxSelectRectPrev.Max = ImMax(start_pos_abs, prev_end_pos_abs);
|
||||
bs->BoxSelectRectCurr.Min = ImMin(start_pos_abs, curr_end_pos_abs);
|
||||
bs->BoxSelectRectCurr.Max = ImMax(start_pos_abs, curr_end_pos_abs);
|
||||
//IMGUI_DEBUG_LOG("StartPosRel (%.2f,%.2f) EndPosRel (%.2f,%.2f) -> (%.2f,%.2f)\n", bs->StartPosRel.x, bs->StartPosRel.y, bs->EndPosRel.x, bs->EndPosRel.y, WindowPosAbsToRel(window, g.IO.MousePos).x, WindowPosAbsToRel(window, g.IO.MousePos).y);
|
||||
|
||||
// Box-select 2D mode detects horizontal changes (vertical ones are already picked by Clipper)
|
||||
// Storing an extra rect used by widgets supporting box-select.
|
||||
if (ms_flags & ImGuiMultiSelectFlags_BoxSelect2d)
|
||||
if (bs->BoxSelectRectPrev.Min.x != bs->BoxSelectRectCurr.Min.x || bs->BoxSelectRectPrev.Max.x != bs->BoxSelectRectCurr.Max.x)
|
||||
// Box-select 2D mode detects change of the rectangle.
|
||||
// Storing unclip rects which will be tested by widgets supporting box-select. Always update rectangles when active (even if we don't use them).
|
||||
// To facilitate understanding this: enable IMGUI_DEBUG_BOXSELECT and visualize all geometry.
|
||||
if (ms_flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d))
|
||||
{
|
||||
// For both sides, compute the area differing between Prev and Curr rectangles.
|
||||
bs->UnclipRects[0] = bs->UnclipRects[1] = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX);
|
||||
for (int side = 0; side < 2; side++)
|
||||
{
|
||||
bs->UnclipMode = true;
|
||||
bs->UnclipRect = bs->BoxSelectRectPrev; // FIXME-OPT: UnclipRect x coordinates could be intersection of Prev and Curr rect on X axis.
|
||||
bs->UnclipRect.Add(bs->BoxSelectRectCurr);
|
||||
ImVec2 d_min = (side == 0) ? ImMin(bs->BoxSelectRectCurr.Min, bs->BoxSelectRectPrev.Min) : ImMin(bs->BoxSelectRectCurr.Max, bs->BoxSelectRectPrev.Max);
|
||||
ImVec2 d_max = (side == 0) ? ImMax(bs->BoxSelectRectCurr.Min, bs->BoxSelectRectPrev.Min) : ImMax(bs->BoxSelectRectCurr.Max, bs->BoxSelectRectPrev.Max);
|
||||
if (d_min.x != d_max.x)
|
||||
{
|
||||
bs->UnclipRects[0].AddX(d_min.x);
|
||||
bs->UnclipRects[0].AddX(d_max.x);
|
||||
}
|
||||
if (d_min.y != d_max.y)
|
||||
{
|
||||
bs->UnclipRects[1].AddY(d_min.y);
|
||||
bs->UnclipRects[1].AddY(d_max.y);
|
||||
}
|
||||
}
|
||||
|
||||
//GetForegroundDrawList()->AddRect(bs->UnclipRect.Min, bs->UnclipRect.Max, IM_COL32(255,0,0,200), 0.0f, 0, 3.0f);
|
||||
ImRect box_select_intersection = bs->BoxSelectRectPrev;
|
||||
box_select_intersection.Add(bs->BoxSelectRectCurr);
|
||||
if (ms_flags & ImGuiMultiSelectFlags_BoxSelect2d)
|
||||
if (bs->BoxSelectRectPrev.Min.x != bs->BoxSelectRectCurr.Min.x || bs->BoxSelectRectPrev.Max.x != bs->BoxSelectRectCurr.Max.x)
|
||||
{
|
||||
bs->UnclipRects[0].AddY(box_select_intersection.Min.y);
|
||||
bs->UnclipRects[0].AddY(box_select_intersection.Max.y);
|
||||
}
|
||||
if (ms_flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d))
|
||||
if (bs->BoxSelectRectPrev.Min.y != bs->BoxSelectRectCurr.Min.y || bs->BoxSelectRectPrev.Max.y != bs->BoxSelectRectCurr.Max.y)
|
||||
{
|
||||
bs->UnclipRects[1].AddX(box_select_intersection.Min.x);
|
||||
bs->UnclipRects[1].AddX(box_select_intersection.Max.x);
|
||||
}
|
||||
|
||||
// Merge both rectangles into one.
|
||||
// FIXME-OPT: When UnclipRect.Area() is much larger than the sum of UnclipRects[0]/[1] Areas, widgets should
|
||||
// ideally first use UnclipRect as a first coarse cull layer + the individual ones as a second validation.
|
||||
bs->UnclipRect = bs->UnclipRects[0];
|
||||
bs->UnclipRect.Add(bs->UnclipRects[1]);
|
||||
if (!bs->UnclipRect.IsInverted() && (!window->ClipRect.Contains(bs->UnclipRect.Min) || !window->ClipRect.Contains(bs->UnclipRect.Max))) // !! Don't use Contains(ImRect)
|
||||
bs->UnclipMode = true;
|
||||
if (bs->UnclipMode && g.CurrentTable != NULL)
|
||||
TableApplyExternalUnclipRect(g.CurrentTable, bs->UnclipRect); // No need submitting both
|
||||
}
|
||||
|
||||
#ifdef IMGUI_DEBUG_BOXSELECT
|
||||
//GetForegroundDrawList()->AddRect(scope_rect.Min, scope_rect.Max, IM_COL32(0, 255, 0, 200), 0.0f, 0, 4.0f);
|
||||
//GetForegroundDrawList()->AddRect(bs->BoxSelectRectPrev.Min, bs->BoxSelectRectPrev.Max, IM_COL32(255,0,0,200), 0.0f, 0, 3.0f);
|
||||
//GetForegroundDrawList()->AddRect(bs->BoxSelectRectCurr.Min, bs->BoxSelectRectCurr.Max, IM_COL32(0,255,0,200), 0.0f, 0, 1.0f);
|
||||
if (ms_flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d))
|
||||
{
|
||||
for (ImRect& unclip_r : bs->UnclipRects)
|
||||
if (!unclip_r.IsInverted())
|
||||
GetForegroundDrawList()->AddRect(unclip_r.Min, unclip_r.Max, bs->UnclipMode ? IM_COL32(255, 255, 0, 200) : IM_COL32(255, 0, 0, 200), 0.0f, 0, 4.0f);
|
||||
GetForegroundDrawList()->AddRect(bs->UnclipRect.Min, bs->UnclipRect.Max, bs->UnclipMode ? IM_COL32(255, 255, 0, 200) : IM_COL32(255, 0, 0, 200), 0.0f, 0, 2.0f);
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -7842,8 +7926,9 @@ void ImGui::EndBoxSelect(const ImRect& scope_rect, ImGuiMultiSelectFlags ms_flag
|
||||
bs->EndPosRel = WindowPosAbsToRel(window, ImClamp(g.IO.MousePos, scope_rect.Min, scope_rect.Max)); // Clamp stored position according to current scrolling view
|
||||
ImRect box_select_r = bs->BoxSelectRectCurr;
|
||||
box_select_r.ClipWith(scope_rect);
|
||||
window->DrawList->AddRectFilled(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_SeparatorHovered, 0.30f)); // FIXME-MULTISELECT: Styling
|
||||
window->DrawList->AddRect(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_NavCursor)); // FIXME-MULTISELECT FIXME-DPI: Styling
|
||||
ImGuiWindow* draw_window = FindFrontMostVisibleChildWindow(window);
|
||||
draw_window->DrawList->AddRectFilled(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_SeparatorHovered, 0.30f)); // FIXME-MULTISELECT: Styling
|
||||
draw_window->DrawList->AddRect(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_NavCursor)); // FIXME-MULTISELECT FIXME-DPI: Styling
|
||||
|
||||
// Scroll
|
||||
const bool enable_scroll = (ms_flags & ImGuiMultiSelectFlags_ScopeWindow) && (ms_flags & ImGuiMultiSelectFlags_BoxSelectNoScroll) == 0;
|
||||
@@ -7883,18 +7968,18 @@ static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSe
|
||||
|
||||
static ImRect CalcScopeRect(ImGuiMultiSelectTempData* ms, ImGuiWindow* window)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
if (ms->Flags & ImGuiMultiSelectFlags_ScopeRect)
|
||||
{
|
||||
// Warning: this depends on CursorMaxPos so it means to be called by EndMultiSelect() only
|
||||
// This probably doesn't work inside a table as there are ample ambiguities related to exact time of calling BeginMultiSelect()/EndMultiSelect().
|
||||
return ImRect(ms->ScopeRectMin, ImMax(window->DC.CursorMaxPos, ms->ScopeRectMin));
|
||||
}
|
||||
else
|
||||
{
|
||||
// When a table, pull HostClipRect, which allows us to predict ClipRect before first row/layout is performed. (#7970)
|
||||
//// When a table, pull HostClipRect, which allows us to predict ClipRect before first row/layout is performed. (#7970)
|
||||
ImRect scope_rect = window->InnerClipRect;
|
||||
if (g.CurrentTable != NULL)
|
||||
scope_rect = g.CurrentTable->HostClipRect;
|
||||
//if (g.CurrentTable != NULL)
|
||||
// scope_rect = g.CurrentTable->HostClipRect;
|
||||
|
||||
// Add inner table decoration (#7821) // FIXME: Why not baking in InnerClipRect?
|
||||
scope_rect.Min = ImMin(scope_rect.Min + ImVec2(window->DecoInnerSizeX1, window->DecoInnerSizeY1), scope_rect.Max);
|
||||
@@ -7930,18 +8015,24 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int sel
|
||||
// FIXME: Workaround to the fact we override CursorMaxPos, meaning size measurement are lost. (#8250)
|
||||
// They should perhaps be stacked properly?
|
||||
if (ImGuiTable* table = g.CurrentTable)
|
||||
if (table->CurrentColumn != -1)
|
||||
{
|
||||
if (!table->IsLayoutLocked)
|
||||
TableUpdateLayout(table);
|
||||
else if (table->CurrentColumn != -1)
|
||||
TableEndCell(table); // This is currently safe to call multiple time. If that properly is lost we can extract the "save measurement" part of it.
|
||||
}
|
||||
|
||||
// FIXME: BeginFocusScope()
|
||||
const ImGuiID id = window->IDStack.back();
|
||||
ms->Clear();
|
||||
ms->FocusScopeId = id;
|
||||
ms->Flags = flags;
|
||||
ms->IsFocused = (ms->FocusScopeId == g.NavFocusScopeId);
|
||||
ms->BackupCursorMaxPos = window->DC.CursorMaxPos;
|
||||
ms->ScopeRectMin = window->DC.CursorMaxPos = window->DC.CursorPos;
|
||||
ms->ScopeRectMin = window->DC.CursorPos;
|
||||
if (flags & ImGuiMultiSelectFlags_ScopeRect)
|
||||
window->DC.CursorMaxPos = ms->ScopeRectMin; // CalcScopeRect() for ImGuiMultiSelectFlags_ScopeRect will measure in EndMultiSelect().
|
||||
PushFocusScope(ms->FocusScopeId);
|
||||
ms->IsFocused = IsInNavFocusRoute(g.CurrentFocusScopeId);
|
||||
if (flags & ImGuiMultiSelectFlags_ScopeWindow) // Mark parent child window as navigable into, with highlight. Assume user will always submit interactive items.
|
||||
window->DC.NavLayersActiveMask |= 1 << ImGuiNavLayer_Main;
|
||||
|
||||
@@ -8023,7 +8114,7 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int sel
|
||||
storage->LastSelectionSize = 0;
|
||||
}
|
||||
ms->LoopRequestSetAll = request_select_all ? 1 : request_clear ? 0 : -1;
|
||||
ms->LastSubmittedItem = ImGuiSelectionUserData_Invalid;
|
||||
//ms->PrevSubmittedItem = ImGuiSelectionUserData_Invalid;
|
||||
|
||||
if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection)
|
||||
DebugLogMultiSelectRequests("BeginMultiSelect", &ms->IO);
|
||||
@@ -8069,7 +8160,7 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect()
|
||||
// Clear selection when clicking void?
|
||||
// We specifically test for IsMouseDragPastThreshold(0) == false to allow box-selection!
|
||||
// The InnerRect test is necessary for non-child/decorated windows.
|
||||
bool scope_hovered = IsWindowHovered() && window->InnerRect.Contains(g.IO.MousePos);
|
||||
bool scope_hovered = window->InnerRect.Contains(g.IO.MousePos) && IsWindowHovered(ImGuiHoveredFlags_ChildWindows);
|
||||
if (scope_hovered && (ms->Flags & ImGuiMultiSelectFlags_ScopeRect))
|
||||
scope_hovered &= scope_rect.Contains(g.IO.MousePos);
|
||||
if (scope_hovered && g.HoveredId == 0 && g.ActiveId == 0)
|
||||
@@ -8095,10 +8186,13 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect()
|
||||
if (ms->Flags & ImGuiMultiSelectFlags_NavWrapX)
|
||||
{
|
||||
IM_ASSERT(ms->Flags & ImGuiMultiSelectFlags_ScopeWindow); // Only supported at window scope
|
||||
ImGui::NavMoveRequestTryWrapping(ImGui::GetCurrentWindow(), ImGuiNavMoveFlags_WrapX);
|
||||
NavMoveRequestTryWrapping(GetCurrentWindow(), ImGuiNavMoveFlags_WrapX);
|
||||
}
|
||||
|
||||
// Unwind
|
||||
if (ImGuiTable* table = g.CurrentTable)
|
||||
if (table->IsInsideRow)
|
||||
TableEndRow(table);
|
||||
window->DC.CursorMaxPos = ImMax(ms->BackupCursorMaxPos, window->DC.CursorMaxPos);
|
||||
PopFocusScope();
|
||||
|
||||
@@ -8126,6 +8220,8 @@ void ImGui::SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_d
|
||||
g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData | ImGuiItemFlags_IsMultiSelect;
|
||||
if (ms->IO.RangeSrcItem == selection_user_data)
|
||||
ms->RangeSrcPassedBy = true;
|
||||
//ms->PrevSubmittedItem = ms->CurrSubmittedItem; // Can't rely on previous g.NextItemData.SelectionUserData because NextItemData is not restored on nested multi-select.
|
||||
//ms->CurrSubmittedItem = selection_user_data;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -8274,8 +8370,31 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed)
|
||||
if (ms->BoxSelectId != 0)
|
||||
if (ImGuiBoxSelectState* bs = GetBoxSelectState(ms->BoxSelectId))
|
||||
{
|
||||
const bool rect_overlap_curr = bs->BoxSelectRectCurr.Overlaps(g.LastItemData.Rect);
|
||||
const bool rect_overlap_prev = bs->BoxSelectRectPrev.Overlaps(g.LastItemData.Rect);
|
||||
ImRect item_rect = g.LastItemData.Rect;
|
||||
if (!window->DC.NavIsScrollPushableX) // FIXME: Rename to be more generic.
|
||||
if (ImGuiTable* table = g.CurrentTable)
|
||||
if (table->CurrentColumn != -1)
|
||||
{
|
||||
// FIXME: We cannot solely use current ClipRect as it includes HostClipRect.
|
||||
// However we account for ClipRect being larger than current column (e.g. when using SpanAllColumns)
|
||||
// A more generic version would be nice, but window->WorkRect.Min/Max exclude CellPadding. (#7994, #9383)
|
||||
ImGuiTableColumn* column = &table->Columns[table->CurrentColumn];
|
||||
float clip_min_x = (g.LastItemData.ItemFlags & ImGuiItemStatusFlags_HasClipRect) ? g.LastItemData.ClipRect.Min.x : window->ClipRect.Min.x;
|
||||
float clip_max_x = (g.LastItemData.ItemFlags & ImGuiItemStatusFlags_HasClipRect) ? g.LastItemData.ClipRect.Max.x : window->ClipRect.Max.x;
|
||||
if (clip_min_x != clip_max_x) // When zero sized we expect that bounds have been clamped and thus are unreliable
|
||||
{
|
||||
item_rect.Min.x = ImMax(item_rect.Min.x, ImMin(column->MinX, clip_min_x));
|
||||
item_rect.Max.x = ImMin(item_rect.Max.x, ImMax(column->MaxX, clip_max_x));
|
||||
}
|
||||
else
|
||||
{
|
||||
item_rect.Min.x = ImMax(item_rect.Min.x, column->MinX);
|
||||
item_rect.Max.x = ImMin(item_rect.Max.x, column->MaxX);
|
||||
}
|
||||
//GetForegroundDrawList()->AddRect(item_rect.Min, item_rect.Max, IM_COL32(255, 0, 255, 255));
|
||||
}
|
||||
const bool rect_overlap_curr = bs->BoxSelectRectCurr.Overlaps(item_rect);
|
||||
const bool rect_overlap_prev = bs->BoxSelectRectPrev.Overlaps(item_rect);
|
||||
if ((rect_overlap_curr && !rect_overlap_prev && !selected) || (rect_overlap_prev && !rect_overlap_curr))
|
||||
{
|
||||
if (storage->LastSelectionSize <= 0 && bs->IsStartedSetNavIdOnce)
|
||||
@@ -8287,6 +8406,9 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed)
|
||||
{
|
||||
selected = !selected;
|
||||
MultiSelectAddSetRange(ms, selected, +1, item_data, item_data);
|
||||
#ifdef IMGUI_DEBUG_BOXSELECT
|
||||
GetForegroundDrawList()->AddRectFilled(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, selected ? IM_COL32(0, 255, 0, 200) : IM_COL32(255, 0, 0, 200));
|
||||
#endif
|
||||
}
|
||||
storage->LastSelectionSize = ImMax(storage->LastSelectionSize + 1, 1);
|
||||
}
|
||||
@@ -8400,7 +8522,6 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed)
|
||||
}
|
||||
if (storage->NavIdItem == item_data)
|
||||
ms->NavIdPassedBy = true;
|
||||
ms->LastSubmittedItem = item_data;
|
||||
|
||||
*p_selected = selected;
|
||||
*p_pressed = pressed;
|
||||
@@ -8416,15 +8537,20 @@ void ImGui::MultiSelectAddSetAll(ImGuiMultiSelectTempData* ms, bool selected)
|
||||
void ImGui::MultiSelectAddSetRange(ImGuiMultiSelectTempData* ms, bool selected, int range_dir, ImGuiSelectionUserData first_item, ImGuiSelectionUserData last_item)
|
||||
{
|
||||
// Merge contiguous spans into same request (unless NoRangeSelect is set which guarantees single-item ranges)
|
||||
// FIXME-OPT: Disabled on 2026/04/09 as this would break with any form of coarse clipping that we don't know about (e.g. TableNextColumn() return value).
|
||||
// The low-hanging fruit would be to know that ImGuiSelectionUserData are sequential indices, in which case we can trivially compare PrevSubmittedItem + RangeDir == FirstItem.
|
||||
// User can always perform this merge if required.
|
||||
#if 0
|
||||
if (ms->IO.Requests.Size > 0 && first_item == last_item && (ms->Flags & ImGuiMultiSelectFlags_NoRangeSelect) == 0)
|
||||
{
|
||||
ImGuiSelectionRequest* prev = &ms->IO.Requests.Data[ms->IO.Requests.Size - 1];
|
||||
if (prev->Type == ImGuiSelectionRequestType_SetRange && prev->RangeLastItem == ms->LastSubmittedItem && prev->Selected == selected)
|
||||
if (prev->Type == ImGuiSelectionRequestType_SetRange && prev->RangeLastItem == ms->PrevSubmittedItem && prev->Selected == selected)
|
||||
{
|
||||
prev->RangeLastItem = last_item;
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetRange, selected, (ImS8)range_dir, (range_dir > 0) ? first_item : last_item, (range_dir > 0) ? last_item : first_item };
|
||||
ms->IO.Requests.push_back(req); // Add new request
|
||||
@@ -8662,7 +8788,8 @@ bool ImGui::BeginListBox(const char* label, const ImVec2& size_arg)
|
||||
|
||||
const ImGuiStyle& style = g.Style;
|
||||
const ImGuiID id = GetID(label);
|
||||
const ImVec2 label_size = CalcTextSize(label, NULL, true);
|
||||
const char* label_end = FindRenderedTextEnd(label);
|
||||
const ImVec2 label_size = CalcTextSize(label, label_end, false);
|
||||
|
||||
// Size default to hold ~7.25 items.
|
||||
// Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
|
||||
@@ -8685,7 +8812,7 @@ bool ImGui::BeginListBox(const char* label, const ImVec2& size_arg)
|
||||
if (label_size.x > 0.0f)
|
||||
{
|
||||
ImVec2 label_pos = ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y);
|
||||
RenderText(label_pos, label);
|
||||
RenderText(label_pos, label, label_end, false);
|
||||
window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, label_pos + label_size);
|
||||
AlignTextToFramePadding();
|
||||
}
|
||||
@@ -8766,9 +8893,7 @@ bool ImGui::ListBox(const char* label, int* current_item, const char* (*getter)(
|
||||
// - PlotHistogram()
|
||||
//-------------------------------------------------------------------------
|
||||
// Plot/Graph widgets are not very good.
|
||||
// Consider writing your own, or using a third-party one, see:
|
||||
// - ImPlot https://github.com/epezent/implot
|
||||
// - others https://github.com/ocornut/imgui/wiki/Useful-Extensions
|
||||
// Consider using ImPlot (https://github.com/epezent/implot) which is much better!
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
int ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, const ImVec2& size_arg)
|
||||
@@ -8781,7 +8906,8 @@ int ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_get
|
||||
const ImGuiStyle& style = g.Style;
|
||||
const ImGuiID id = window->GetID(label);
|
||||
|
||||
const ImVec2 label_size = CalcTextSize(label, NULL, true);
|
||||
const char* label_end = FindRenderedTextEnd(label);
|
||||
const ImVec2 label_size = CalcTextSize(label, label_end, false);
|
||||
const ImVec2 frame_size = CalcItemSize(size_arg, CalcItemWidth(), label_size.y + style.FramePadding.y * 2.0f);
|
||||
|
||||
const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size);
|
||||
@@ -8880,10 +9006,11 @@ int ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_get
|
||||
RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, overlay_text, NULL, NULL, ImVec2(0.5f, 0.0f));
|
||||
|
||||
if (label_size.x > 0.0f)
|
||||
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label);
|
||||
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label, label_end, false);
|
||||
|
||||
// Return hovered index or -1 if none are hovered.
|
||||
// This is currently not exposed in the public API because we need a larger redesign of the whole thing, but in the short-term we are making it available in PlotEx().
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);
|
||||
return idx_hovered;
|
||||
}
|
||||
|
||||
@@ -8913,6 +9040,7 @@ void ImGui::PlotLines(const char* label, float (*values_getter)(void* data, int
|
||||
PlotEx(ImGuiPlotType_Lines, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
|
||||
}
|
||||
|
||||
// Plot Histogram (the data provided _is_ histogram data. it doesn't compute the histogram of your data)
|
||||
void ImGui::PlotHistogram(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride)
|
||||
{
|
||||
ImGuiPlotArrayGetterData data(values, stride);
|
||||
@@ -9245,7 +9373,8 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
|
||||
// Tag menu as used. Next time BeginMenu() with same ID is called it will append to existing menu
|
||||
g.MenusIdSubmittedThisFrame.push_back(id);
|
||||
|
||||
ImVec2 label_size = CalcTextSize(label, NULL, true);
|
||||
const char* label_end = FindRenderedTextEnd(label);
|
||||
ImVec2 label_size = CalcTextSize(label, label_end, false);
|
||||
|
||||
// Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent without always being a Child window)
|
||||
// This is only done for items for the menu set and not the full parent window.
|
||||
@@ -9264,8 +9393,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
|
||||
|
||||
bool pressed;
|
||||
|
||||
// We use ImGuiSelectableFlags_NoSetKeyOwner to allow down on one menu item, move, up on another.
|
||||
const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_NoSetKeyOwner | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_NoAutoClosePopups;
|
||||
const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_NoAutoClosePopups | (ImGuiSelectableFlags)ImGuiSelectableFlags_SelectOnClick;
|
||||
ImGuiMenuColumns* offsets = &window->DC.MenuColumns;
|
||||
if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
|
||||
{
|
||||
@@ -9277,7 +9405,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
|
||||
ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, pos.y + window->DC.CurrLineTextBaseOffset);
|
||||
pressed = Selectable("", menu_is_open, selectable_flags, label_size);
|
||||
LogSetNextTextDecoration("[", "]");
|
||||
RenderText(text_pos, label);
|
||||
RenderText(text_pos, label, label_end, false);
|
||||
PopStyleVar();
|
||||
window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().
|
||||
popup_pos = ImVec2(pos.x - 1.0f - IM_TRUNC(style.ItemSpacing.x * 0.5f), text_pos.y - style.FramePadding.y + window->MenuBarHeight);
|
||||
@@ -9294,7 +9422,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
|
||||
ImVec2 text_pos(window->DC.CursorPos.x, pos.y + window->DC.CurrLineTextBaseOffset);
|
||||
pressed = Selectable("", menu_is_open, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, label_size.y));
|
||||
LogSetNextTextDecoration("", ">");
|
||||
RenderText(ImVec2(text_pos.x + offsets->OffsetLabel, text_pos.y), label);
|
||||
RenderText(ImVec2(text_pos.x + offsets->OffsetLabel, text_pos.y), label, label_end, false);
|
||||
if (icon_w > 0.0f)
|
||||
RenderText(ImVec2(text_pos.x + offsets->OffsetIcon, text_pos.y), icon);
|
||||
RenderArrow(window->DrawList, ImVec2(text_pos.x + offsets->OffsetMark + extra_w + g.FontSize * 0.30f, text_pos.y), GetColorU32(ImGuiCol_Text), ImGuiDir_Right);
|
||||
@@ -9303,6 +9431,14 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
|
||||
if (!enabled)
|
||||
EndDisabled();
|
||||
|
||||
// Once dragged, release ActiveId + key ownership. This is to allow the idiom of mouse down a menu, dragging elsewhere, up on some other MenuItem(). (#8233, #9394)
|
||||
// Could move logic into lower-level ImGuiButtonFlags_AutoReleaseActiveId + ImGuiButtonFlags_AutoReleaseKeyOwner? Easier once we get rid of the Selectable() middle-man here.
|
||||
if (g.ActiveId == id && g.HoveredId != id && g.ActiveIdSource == ImGuiInputSource_Mouse && IsMouseDragging(0))
|
||||
{
|
||||
ClearActiveID();
|
||||
SetKeyOwner(ImGuiKey_MouseLeft, ImGuiKeyOwner_NoOwner);
|
||||
}
|
||||
|
||||
const bool hovered = (g.HoveredId == id) && enabled && !g.NavHighlightItemUnderNav;
|
||||
if (menuset_is_open)
|
||||
PopItemFlag();
|
||||
@@ -9383,6 +9519,9 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0));
|
||||
PopID();
|
||||
|
||||
if (g.ActiveId == id && want_open)
|
||||
g.ActiveIdNoClearOnFocusLoss = true;
|
||||
|
||||
if (want_open && !menu_is_open && g.OpenPopupStack.Size > g.BeginPopupStack.Size)
|
||||
{
|
||||
// Don't reopen/recycle same menu level in the same frame if it is a different menu ID, first close the other menu and yield for a frame.
|
||||
@@ -9459,7 +9598,8 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiStyle& style = g.Style;
|
||||
ImVec2 pos = window->DC.CursorPos;
|
||||
ImVec2 label_size = CalcTextSize(label, NULL, true);
|
||||
const char* label_end = FindRenderedTextEnd(label);
|
||||
ImVec2 label_size = CalcTextSize(label, label_end, false);
|
||||
|
||||
// See BeginMenuEx() for comments about this.
|
||||
const bool menuset_is_open = IsRootOfOpenMenuSet();
|
||||
@@ -9474,7 +9614,7 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut
|
||||
BeginDisabled();
|
||||
|
||||
// We use ImGuiSelectableFlags_NoSetKeyOwner to allow down on one menu item, move, up on another.
|
||||
const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_NoSetKeyOwner | ImGuiSelectableFlags_SetNavIdOnHover;
|
||||
const ImGuiSelectableFlags selectable_flags = (ImGuiSelectableFlags)ImGuiSelectableFlags_SelectOnRelease | (ImGuiSelectableFlags)ImGuiSelectableFlags_SetNavIdOnHover;
|
||||
ImGuiMenuColumns* offsets = &window->DC.MenuColumns;
|
||||
if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
|
||||
{
|
||||
@@ -9486,7 +9626,7 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut
|
||||
pressed = Selectable("", selected, selectable_flags, ImVec2(label_size.x, 0.0f));
|
||||
PopStyleVar();
|
||||
if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible)
|
||||
RenderText(text_pos, label);
|
||||
RenderText(text_pos, label, label_end, false);
|
||||
window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().
|
||||
}
|
||||
else
|
||||
@@ -9503,7 +9643,7 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut
|
||||
pressed = Selectable("", false, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, label_size.y));
|
||||
if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible)
|
||||
{
|
||||
RenderText(text_pos + ImVec2(offsets->OffsetLabel, 0.0f), label);
|
||||
RenderText(text_pos + ImVec2(offsets->OffsetLabel, 0.0f), label, label_end, false);
|
||||
if (icon_w > 0.0f)
|
||||
RenderText(text_pos + ImVec2(offsets->OffsetIcon, 0.0f), icon);
|
||||
if (shortcut_w > 0.0f)
|
||||
@@ -9517,6 +9657,17 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut
|
||||
RenderCheckMark(window->DrawList, text_pos + ImVec2(offsets->OffsetMark + stretch_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(ImGuiCol_Text), g.FontSize * 0.866f);
|
||||
}
|
||||
}
|
||||
|
||||
// Once dragged, release ActiveId + key ownership. This is to allow the idiom of mouse down a menu, dragging elsewhere, up on some other MenuItem(). (#8233, #9394)
|
||||
// Could move logic into lower-level ImGuiButtonFlags_AutoReleaseActiveId + ImGuiButtonFlags_AutoReleaseKeyOwner? Easier once we get rid of the Selectable() middle-man here.
|
||||
const ImGuiID id = g.LastItemData.ID;
|
||||
if (g.ActiveId == id && g.HoveredId != id && g.ActiveIdSource == ImGuiInputSource_Mouse && IsMouseDragging(0))
|
||||
{
|
||||
ClearActiveID();
|
||||
SetKeyOwner(ImGuiKey_MouseLeft, ImGuiKeyOwner_NoOwner);
|
||||
}
|
||||
|
||||
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0));
|
||||
if (!enabled)
|
||||
EndDisabled();
|
||||
@@ -10557,7 +10708,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
|
||||
// We don't have CPU clipping primitives to clip the CloseButton (until it becomes a texture), so need to add an extra draw call (temporary in the case of vertical animation)
|
||||
const bool want_clip_rect = is_central_section && (bb.Min.x < tab_bar->ScrollingRectMinX || bb.Max.x > tab_bar->ScrollingRectMaxX);
|
||||
if (want_clip_rect)
|
||||
PushClipRect(ImVec2(ImMax(bb.Min.x, tab_bar->ScrollingRectMinX), bb.Min.y - 1), ImVec2(tab_bar->ScrollingRectMaxX, bb.Max.y), true);
|
||||
PushClipRect(ImVec2(ImClamp(bb.Min.x, tab_bar->ScrollingRectMinX, tab_bar->ScrollingRectMaxX), bb.Min.y - 1), ImVec2(tab_bar->ScrollingRectMaxX, bb.Max.y), true);
|
||||
|
||||
ImVec2 backup_cursor_max_pos = window->DC.CursorMaxPos;
|
||||
ItemSize(bb.GetSize(), style.FramePadding.y);
|
||||
@@ -10629,7 +10780,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
|
||||
float rounding = style.TabRounding;
|
||||
display_draw_list->PathArcToFast(tl + ImVec2(+rounding, +rounding), rounding, 7, 9);
|
||||
display_draw_list->PathArcToFast(tr + ImVec2(-rounding, +rounding), rounding, 9, 11);
|
||||
display_draw_list->PathStroke(overline_col, 0, style.TabBarOverlineSize);
|
||||
display_draw_list->PathStroke(overline_col, style.TabBarOverlineSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -10733,7 +10884,7 @@ void ImGui::TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabI
|
||||
draw_list->PathArcToFast(ImVec2(bb.Min.x + rounding + 0.5f, y1 + rounding + 0.5f), rounding, 6, 9);
|
||||
draw_list->PathArcToFast(ImVec2(bb.Max.x - rounding - 0.5f, y1 + rounding + 0.5f), rounding, 9, 12);
|
||||
draw_list->PathLineTo(ImVec2(bb.Max.x - 0.5f, y2));
|
||||
draw_list->PathStroke(GetColorU32(ImGuiCol_Border), 0, g.Style.TabBorderSize);
|
||||
draw_list->PathStroke(GetColorU32(ImGuiCol_Border), g.Style.TabBorderSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10742,7 +10893,8 @@ void ImGui::TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabI
|
||||
void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImVec2 frame_padding, const char* label, ImGuiID tab_id, ImGuiID close_button_id, bool is_contents_visible, bool* out_just_closed, bool* out_text_clipped)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImVec2 label_size = CalcTextSize(label, NULL, true);
|
||||
const char* label_end = FindRenderedTextEnd(label);
|
||||
ImVec2 label_size = CalcTextSize(label, label_end, false);
|
||||
|
||||
if (out_just_closed)
|
||||
*out_just_closed = false;
|
||||
@@ -10827,7 +10979,7 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb,
|
||||
}
|
||||
}
|
||||
LogSetNextTextDecoration("/", "\\");
|
||||
RenderTextEllipsis(draw_list, text_ellipsis_clip_bb.Min, text_ellipsis_clip_bb.Max, ellipsis_max_x, label, NULL, &label_size);
|
||||
RenderTextEllipsis(draw_list, text_ellipsis_clip_bb.Min, text_ellipsis_clip_bb.Max, ellipsis_max_x, label, label_end, &label_size);
|
||||
|
||||
#if 0
|
||||
if (!is_contents_visible)
|
||||
|
||||
@@ -683,7 +683,7 @@
|
||||
03000000a306000020f6000000000000,Saitek PS2700 PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,
|
||||
03000000300f00001101000000000000,Saitek Rumble,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,
|
||||
03000000e804000000a0000000000000,Samsung EIGP20,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000bd12000010d1000000000000,Sanwa 4Button,a:b0,b:b1,x:b2,y:b3,dpup:-a1,dpdown:+a1,dpleft:-a0,dpright:+a0,platform:Windows,
|
||||
03000000bd12000010d1000000000000,Sanwa 4Button,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,x:b2,y:b3,platform:Windows,
|
||||
03000000c01100000252000000000000,Sanwa Easy Grip,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,
|
||||
03000000c01100004350000000000000,Sanwa Micro Grip P3,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,x:b3,y:b2,platform:Windows,
|
||||
03000000411200004550000000000000,Sanwa Micro Grip Pro,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a1,righty:a2,start:b9,x:b1,y:b3,platform:Windows,
|
||||
@@ -697,14 +697,14 @@
|
||||
030000004f04000028b3000000000000,Score A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
|
||||
03000000952e00002577000000000000,Scuf PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,
|
||||
03000000a30c00002500000000000000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Windows,
|
||||
03000000a30c00002400000000000000,Sega Mega Drive Mini 6B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,
|
||||
03000000a30c00002400000000000000,Sega Mega Drive Mini 6B Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,
|
||||
03000000d804000086e6000000000000,Sega Multi Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:a2,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows,
|
||||
0300000000050000289b000000000000,Sega Saturn Adapter,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,
|
||||
0300000000f000000800000000000000,Sega Saturn Controller,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,righttrigger:b3,start:b0,x:b5,y:b6,platform:Windows,
|
||||
03000000730700000601000000000000,Sega Saturn Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,
|
||||
03000000b40400000a01000000000000,Sega Saturn Controller,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows,
|
||||
030000003b07000004a1000000000000,SFX,a:b0,b:b2,back:b7,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b5,start:b8,x:b1,y:b3,platform:Windows,
|
||||
03000000632500002705000000000000,ShanWan Q36,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,righttrigger:b9,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000632500002705000000000000,ShanWan Gamepad,+lefty:+a2,-lefty:-a1,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,rightshoulder:b7,rightstick:b14,righttrigger:b9,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000f82100001900000000000000,Shogun Bros Chameleon X1,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,
|
||||
03000000120c00001c1e000000000000,SnakeByte 4S PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,
|
||||
03000000140300000918000000000000,SNES Controller,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,
|
||||
@@ -733,6 +733,7 @@
|
||||
03000000120c0000160e000000000000,Steel Play Metaltech PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,
|
||||
03000000110100001914000000000000,SteelSeries,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000381000001214000000000000,SteelSeries Free,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000381000004114000000000000,SteelSeries Nimbus Cloud,a:b0,b:b1,x:b3,y:b4,back:b10,guide:b15,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,paddle1:b5,paddle2:b2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b8,righttrigger:b9,platform:Windows,
|
||||
03000000110100003114000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000381000003014000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
|
||||
03000000381000003114000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
|
||||
@@ -776,6 +777,7 @@
|
||||
030000005f140000c501000000000000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
|
||||
03000000b80500000210000000000000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
|
||||
030000005f1400002a01000000000000,Trust Predator GM1200,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,x:b3,y:b4,platform:Windows,
|
||||
03000000580400000a10000000000000,Trust Sight Fighter,a:b0,b:b1,back:b5,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,start:b2,x:b3,y:b4,platform:Windows,
|
||||
030000004f04000087b6000000000000,TWCS Throttle,dpdown:b8,dpleft:b9,dpright:b7,dpup:b6,leftstick:b5,lefttrigger:-a5,leftx:a0,lefty:a1,righttrigger:+a5,platform:Windows,
|
||||
03000000411200000450000000000000,Twin Shock,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a4,start:b11,x:b3,y:b0,platform:Windows,
|
||||
03000000d90400000200000000000000,TwinShock PS2 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,
|
||||
@@ -1116,6 +1118,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
03000000110100002014000000000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,
|
||||
03000000110100002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,
|
||||
03000000381000002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,
|
||||
03000000381000004114000000010000,SteelSeries Nimbus Cloud,a:b0,b:b1,x:b3,y:b4,back:b10,guide:b15,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,paddle1:b5,paddle2:b2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4,platform:Mac OS X,
|
||||
05000000484944204465766963650000,SteelSeries Nimbus Plus,a:b0,b:b1,back:b15,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b16,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b14,x:b2,y:b3,platform:Mac OS X,
|
||||
050000004e696d6275732b0000000000,SteelSeries Nimbus Plus,a:b0,b:b1,back:b15,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b16,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b14,x:b2,y:b3,platform:Mac OS X,
|
||||
03000000381000003014000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
|
||||
@@ -1338,7 +1341,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
03000000b40400001124000011010000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b14,paddle1:b2,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000b40400001224000011010000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b2,paddle1:b16,paddle2:b17,paddle3:b14,paddle4:b15,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
05000000151900004000000001000000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b14,paddle1:b2,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
06000000d73700000124000000000000,Flydigi Vader 5 Pro,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,misc1:b11,misc2:b12,misc3:b13,misc4:b14,misc5:b19,paddle1:b15,paddle2:b17,paddle3:b16,paddle4:b18,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
03000000defa000001c0000000000000,Flydigi Vader 5 Pro,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,misc1:b11,misc2:b12,misc3:b13,misc4:b14,misc5:b19,paddle1:b15,paddle2:b16,paddle3:b17,paddle4:b18,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
030000007e0500003703000000000000,GameCube Adapter,a:b0,b:b1,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux,
|
||||
19000000030000000300000002030000,GameForce Controller,a:b1,b:b0,back:b8,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b16,leftshoulder:b4,leftstick:b14,lefttrigger:b6,leftx:a1,lefty:a0,rightshoulder:b5,rightstick:b15,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,
|
||||
03000000373500000b10000019010000,GameSir Cyclone 2,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,
|
||||
@@ -1536,10 +1539,10 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
03000000853200000706000012010000,Nacon GC100,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,
|
||||
030000006b140000010c000010010000,Nacon GC400ES,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
|
||||
05000000853200000503000000010000,Nacon MGX 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,
|
||||
0300000085320000170d000011010000,Nacon Revolution 5 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,
|
||||
0300000085320000190d000011010000,Nacon Revolution 5 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,
|
||||
0300000085320000180d000011010000,Nacon Revolution 5 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
|
||||
0300000085320000160d000011010000,Nacon Revolution 5 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,
|
||||
0300000085320000170d000011010000,Nacon Revolution 5 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,
|
||||
0300000085320000180d000011010000,Nacon Revolution 5 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
|
||||
0300000085320000190d000011010000,Nacon Revolution 5 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,
|
||||
030000000d0f00000900000010010000,Natec Genesis P44,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
|
||||
030000004f1f00000800000011010000,NeoGeo PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
|
||||
0300000092120000474e000000010000,NeoGeo X Arcade Stick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b3,y:b2,platform:Linux,
|
||||
@@ -1751,6 +1754,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
030000005e0400008e02000073050000,Speedlink Torid,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,
|
||||
030000005e0400008e02000020200000,SpeedLink Xeox Pro Analog,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,
|
||||
03000000de280000ff11000001000000,Steam Virtual Gamepad,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,
|
||||
05000000381000001214000001000000,SteelSeries Free,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:a2,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a3,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
05000000381000004114000000010000,SteelSeries Nimbus Cloud,a:b0,b:b1,x:b3,y:b4,back:b10,guide:b15,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,paddle1:b5,paddle2:b2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4,platform:Linux,
|
||||
050000004e696d6275732b0000000000,SteelSeries Nimbus Plus,a:b0,b:b1,back:b10,guide:b11,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Linux,
|
||||
03000000381000003014000075010000,SteelSeries Stratus Duo,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,
|
||||
03000000381000003114000075010000,SteelSeries Stratus Duo,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,
|
||||
@@ -1796,9 +1801,10 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
03000000e00d00000300000003000000,TRBot Virtual Joypad,a:b11,b:b12,back:b15,dpdown:b6,dpleft:b3,dpright:b4,dpup:b5,leftshoulder:b17,leftstick:b21,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b22,righttrigger:a2,rightx:a3,righty:a4,start:b16,x:b13,y:b14,platform:Linux,
|
||||
03000000f00600000300000003000000,TRBot Virtual Joypad,a:b11,b:b12,back:b15,dpdown:b6,dpleft:b3,dpright:b4,dpup:b5,leftshoulder:b17,leftstick:b21,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b22,righttrigger:a2,rightx:a3,righty:a4,start:b16,x:b13,y:b14,platform:Linux,
|
||||
030000005f140000c501000010010000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
|
||||
03000000f51000001370000000000000,Turtle Beach ReactR,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,
|
||||
03000000f51000001370000000010000,Turtle Beach ReactR,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,
|
||||
06000000f51000000870000003010000,Turtle Beach Recon,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,
|
||||
03000000f51000001370000000010000,Turtle Beach ReactR,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,
|
||||
03000000f51000001370000000000000,Turtle Beach ReactR,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,
|
||||
05000000f51000008f01000001000000,Turtle Beach Rematch Nintendo Switch Controller,a:b0,b:b1,x:b2,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,lefttrigger:b6,righttrigger:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,platform:Linux,
|
||||
03000000100800000100000010010000,Twin PlayStation Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,
|
||||
03000000c0160000e105000010010000,Ultimate Atari Fight Stick,a:b1,b:b2,back:b9,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b4,righttrigger:b5,start:b8,x:b0,y:b3,platform:Linux,
|
||||
03000000151900005678000010010000,Uniplay U6,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,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,
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-az" viewBox="0 0 640 480">
|
||||
<path fill="#3f9c35" d="M.1 0h640v480H.1z"/>
|
||||
<path fill="#ed2939" d="M.1 0h640v320H.1z"/>
|
||||
<path fill="#00b9e4" d="M.1 0h640v160H.1z"/>
|
||||
<circle cx="304" cy="240" r="72" fill="#fff"/>
|
||||
<circle cx="320" cy="240" r="60" fill="#ed2939"/>
|
||||
<path fill="#fff" d="m384 200 7.7 21.5 20.6-9.8-9.8 20.7L424 240l-21.5 7.7 9.8 20.6-20.6-9.8L384 280l-7.7-21.5-20.6 9.8 9.8-20.6L344 240l21.5-7.7-9.8-20.6 20.6 9.8z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 501 B |
@@ -1,6 +1,8 @@
|
||||
// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#if defined(VERTEX_SHADER)
|
||||
|
||||
struct VS_INPUT
|
||||
{
|
||||
float4 p : POSITION;
|
||||
@@ -15,6 +17,21 @@ struct VS_OUTPUT
|
||||
float4 c : COLOR;
|
||||
};
|
||||
|
||||
VS_OUTPUT vs_main(VS_INPUT input)
|
||||
{
|
||||
VS_OUTPUT output;
|
||||
|
||||
output.p = input.p;
|
||||
output.t = input.t;
|
||||
output.c = input.c;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
#endif // VERTEX_SHADER
|
||||
|
||||
#if defined(PIXEL_SHADER)
|
||||
|
||||
cbuffer cb0 : register(b0)
|
||||
{
|
||||
float4 BGColor;
|
||||
@@ -33,11 +50,22 @@ static const float3x3 rgb2yuv =
|
||||
Texture2D Texture;
|
||||
SamplerState TextureSampler;
|
||||
|
||||
#if HAS_FLOAT32_INPUT
|
||||
|
||||
float sample_c(float2 uv)
|
||||
{
|
||||
return Texture.Sample(TextureSampler, uv).r;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
float4 sample_c(float2 uv)
|
||||
{
|
||||
return Texture.Sample(TextureSampler, uv);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
struct PS_INPUT
|
||||
{
|
||||
float4 p : SV_Position;
|
||||
@@ -45,36 +73,117 @@ struct PS_INPUT
|
||||
float4 c : COLOR;
|
||||
};
|
||||
|
||||
#if HAS_INTEGER_OUTPUT
|
||||
#define OUTPUT_TYPE uint
|
||||
#define OUTPUT_SV SV_Target
|
||||
#elif HAS_DEPTH_OUTPUT
|
||||
#define OUTPUT_TYPE float
|
||||
#define OUTPUT_SV SV_Depth
|
||||
#elif HAS_FLOAT32_OUTPUT
|
||||
#define OUTPUT_TYPE float
|
||||
#define OUTPUT_SV SV_Target
|
||||
#else
|
||||
#define OUTPUT_TYPE float4
|
||||
#define OUTPUT_SV SV_Target
|
||||
#endif
|
||||
|
||||
struct PS_OUTPUT
|
||||
{
|
||||
float4 c : SV_Target0;
|
||||
OUTPUT_TYPE o : OUTPUT_SV;
|
||||
};
|
||||
|
||||
VS_OUTPUT vs_main(VS_INPUT input)
|
||||
uint rgba8_to_uint(float4 c)
|
||||
{
|
||||
VS_OUTPUT output;
|
||||
|
||||
output.p = input.p;
|
||||
output.t = input.t;
|
||||
output.c = input.c;
|
||||
|
||||
return output;
|
||||
uint4 i = uint4(c * 255.5f) & 0xFFu;
|
||||
return i.r | (i.g << 8) | (i.b << 16) | (i.a << 24);
|
||||
}
|
||||
|
||||
PS_OUTPUT ps_copy(PS_INPUT input)
|
||||
uint rgb5a1_to_uint(float4 c)
|
||||
{
|
||||
PS_OUTPUT output;
|
||||
|
||||
output.c = sample_c(input.t);
|
||||
|
||||
return output;
|
||||
uint4 i = uint4(c * 255.5f) & uint4(0xF8u, 0xF8u, 0xF8u, 0x80u);
|
||||
return (i.r >> 3) | (i.g << 2) | (i.b << 7) | (i.a << 8);
|
||||
}
|
||||
|
||||
float ps_depth_copy(PS_INPUT input) : SV_Depth
|
||||
uint depth_to_uint(float d)
|
||||
{
|
||||
return sample_c(input.t).r;
|
||||
return uint(d * exp2(32.0f));
|
||||
}
|
||||
|
||||
float4 uint_to_rgba8(uint i)
|
||||
{
|
||||
return float4((i & 0xFFu), ((i >> 8) & 0xFFu), ((i >> 16) & 0xFFu), ((i >> 24) & 0xFFu)) / 255.0f;
|
||||
}
|
||||
|
||||
float4 uint_to_rgb5a1(uint i)
|
||||
{
|
||||
return float4(uint4(i << 3, i >> 2, i >> 7, i >> 8) & uint4(0xF8u, 0xF8u, 0xF8u, 0x80u)) / 255.0f;
|
||||
}
|
||||
|
||||
float uint_to_depth32(uint i)
|
||||
{
|
||||
return float(i) * exp2(-32.0f);
|
||||
}
|
||||
|
||||
float uint_to_depth24(uint i)
|
||||
{
|
||||
return float(i & 0xFFFFFFu) * exp2(-32.0f);
|
||||
}
|
||||
|
||||
float uint_to_depth16(uint i)
|
||||
{
|
||||
return float(i & 0xFFFFu) * exp2(-32.0f);
|
||||
}
|
||||
|
||||
float rgba8_to_depth32(float4 val)
|
||||
{
|
||||
return uint_to_depth32(rgba8_to_uint(val));
|
||||
}
|
||||
|
||||
float rgba8_to_depth24(float4 val)
|
||||
{
|
||||
return uint_to_depth24(rgba8_to_uint(val));
|
||||
}
|
||||
|
||||
float rgba8_to_depth16(float4 val)
|
||||
{
|
||||
return uint_to_depth16(rgba8_to_uint(val));
|
||||
}
|
||||
|
||||
float rgb5a1_to_depth16(float4 val)
|
||||
{
|
||||
return uint_to_depth16(rgb5a1_to_uint(val));
|
||||
}
|
||||
|
||||
float4 depth32_to_rgba8(float d)
|
||||
{
|
||||
return uint_to_rgba8(depth_to_uint(d));
|
||||
}
|
||||
|
||||
float4 depth16_to_rgb5a1(float d)
|
||||
{
|
||||
return uint_to_rgb5a1(depth_to_uint(d));
|
||||
}
|
||||
|
||||
float depth32_to_depth24(float d)
|
||||
{
|
||||
return uint_to_depth24(depth_to_uint(d));
|
||||
}
|
||||
|
||||
#if defined(__ps_copy__)
|
||||
OUTPUT_TYPE ps_copy(PS_INPUT input) : OUTPUT_SV
|
||||
{
|
||||
return sample_c(input.t);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__ps_depth_copy__)
|
||||
OUTPUT_TYPE ps_depth_copy(PS_INPUT input) : OUTPUT_SV
|
||||
{
|
||||
return sample_c(input.t);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__ps_downsample_copy__)
|
||||
PS_OUTPUT ps_downsample_copy(PS_INPUT input)
|
||||
{
|
||||
int DownsampleFactor = DOFFSET;
|
||||
@@ -85,179 +194,134 @@ PS_OUTPUT ps_downsample_copy(PS_INPUT input)
|
||||
int2 coord = max(int2(input.p.xy) * DownsampleFactor, ClampMin);
|
||||
|
||||
PS_OUTPUT output;
|
||||
output.c = (float4)0;
|
||||
output.o = (float4)0;
|
||||
for (int yoff = 0; yoff < DownsampleFactor; yoff++)
|
||||
{
|
||||
for (int xoff = 0; xoff < DownsampleFactor; xoff++)
|
||||
output.c += Texture.Load(int3(coord + int2(xoff * step_multiplier, yoff * step_multiplier), 0));
|
||||
output.o += Texture.Load(int3(coord + int2(xoff * step_multiplier, yoff * step_multiplier), 0));
|
||||
}
|
||||
output.c /= Weight;
|
||||
output.o /= Weight;
|
||||
return output;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__ps_filter_transparency__)
|
||||
PS_OUTPUT ps_filter_transparency(PS_INPUT input)
|
||||
{
|
||||
PS_OUTPUT output;
|
||||
float4 c = sample_c(input.t);
|
||||
output.c = float4(c.rgb, 1.0);
|
||||
output.o = float4(c.rgb, 1.0);
|
||||
return output;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Need to be careful with precision here, it can break games like Spider-Man 3 and Dogs Life
|
||||
uint ps_convert_rgba8_16bits(PS_INPUT input) : SV_Target0
|
||||
#if defined(__ps_convert_rgb5a1_16bits__)
|
||||
OUTPUT_TYPE ps_convert_rgb5a1_16bits(PS_INPUT input) : OUTPUT_SV
|
||||
{
|
||||
uint4 i = sample_c(input.t) * float4(255.5f, 255.5f, 255.5f, 255.5f);
|
||||
|
||||
return ((i.x & 0x00F8u) >> 3) | ((i.y & 0x00F8u) << 2) | ((i.z & 0x00f8u) << 7) | ((i.w & 0x80u) << 8);
|
||||
// Need to be careful with precision here, it can break games like Spider-Man 3 and Dogs Life
|
||||
return rgb5a1_to_uint(sample_c(input.t));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__ps_datm1__)
|
||||
void ps_datm1(PS_INPUT input)
|
||||
{
|
||||
clip(sample_c(input.t).a - 127.5f / 255); // >= 0x80 pass
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__ps_datm0__)
|
||||
void ps_datm0(PS_INPUT input)
|
||||
{
|
||||
clip(127.5f / 255 - sample_c(input.t).a); // < 0x80 pass (== 0x80 should not pass)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__ps_datm1_rta_correction__)
|
||||
void ps_datm1_rta_correction(PS_INPUT input)
|
||||
{
|
||||
clip(sample_c(input.t).a - 254.5f / 255); // >= 0x80 pass
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__ps_datm0_rta_correction__)
|
||||
void ps_datm0_rta_correction(PS_INPUT input)
|
||||
{
|
||||
clip(254.5f / 255 - sample_c(input.t).a); // < 0x80 pass (== 0x80 should not pass)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__ps_rta_correction__)
|
||||
PS_OUTPUT ps_rta_correction(PS_INPUT input)
|
||||
{
|
||||
PS_OUTPUT output;
|
||||
float4 value = sample_c(input.t);
|
||||
output.c = float4(value.rgb, value.a / (128.25f / 255.0f));
|
||||
output.o = float4(value.rgb, value.a / (128.25f / 255.0f));
|
||||
return output;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__ps_rta_decorrection__)
|
||||
PS_OUTPUT ps_rta_decorrection(PS_INPUT input)
|
||||
{
|
||||
PS_OUTPUT output;
|
||||
float4 value = sample_c(input.t);
|
||||
output.c = float4(value.rgb, value.a * (128.25f / 255.0f));
|
||||
output.o = float4(value.rgb, value.a * (128.25f / 255.0f));
|
||||
return output;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__ps_colclip_init__)
|
||||
PS_OUTPUT ps_colclip_init(PS_INPUT input)
|
||||
{
|
||||
PS_OUTPUT output;
|
||||
float4 value = sample_c(input.t);
|
||||
output.c = float4(round(value.rgb * 255) / 65535, value.a);
|
||||
output.o = float4(round(value.rgb * 255) / 65535, value.a);
|
||||
return output;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__ps_colclip_resolve__)
|
||||
PS_OUTPUT ps_colclip_resolve(PS_INPUT input)
|
||||
{
|
||||
PS_OUTPUT output;
|
||||
float4 value = sample_c(input.t);
|
||||
output.c = float4(float3(uint3(value.rgb * 65535.5) & 255) / 255, value.a);
|
||||
output.o = float4(float3(uint3(value.rgb * 65535.5) & 255) / 255, value.a);
|
||||
return output;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint ps_convert_float32_32bits(PS_INPUT input) : SV_Target0
|
||||
#if defined(__ps_convert_depth32_32bits__)
|
||||
OUTPUT_TYPE ps_convert_depth32_32bits(PS_INPUT input) : OUTPUT_SV
|
||||
{
|
||||
// Convert a FLOAT32 depth texture into a 32 bits UINT texture
|
||||
return uint(exp2(32.0f) * sample_c(input.t).r);
|
||||
// Convert a depth texture into a 32 bits UINT texture
|
||||
return depth_to_uint(sample_c(input.t));
|
||||
}
|
||||
#endif
|
||||
|
||||
PS_OUTPUT ps_convert_float32_rgba8(PS_INPUT input)
|
||||
#if defined(__ps_convert_depth32_rgba8__)
|
||||
OUTPUT_TYPE ps_convert_depth32_rgba8(PS_INPUT input) : OUTPUT_SV
|
||||
{
|
||||
PS_OUTPUT output;
|
||||
|
||||
// Convert a FLOAT32 depth texture into a RGBA color texture
|
||||
uint d = uint(sample_c(input.t).r * exp2(32.0f));
|
||||
output.c = float4(uint4((d & 0xFFu), ((d >> 8) & 0xFFu), ((d >> 16) & 0xFFu), (d >> 24))) / 255.0f;
|
||||
|
||||
return output;
|
||||
// Convert a depth texture into a RGBA color texture
|
||||
return depth32_to_rgba8(sample_c(input.t));
|
||||
}
|
||||
#endif
|
||||
|
||||
PS_OUTPUT ps_convert_float16_rgb5a1(PS_INPUT input)
|
||||
#if defined(__ps_convert_depth16_rgb5a1__)
|
||||
OUTPUT_TYPE ps_convert_depth16_rgb5a1(PS_INPUT input) : OUTPUT_SV
|
||||
{
|
||||
PS_OUTPUT output;
|
||||
|
||||
// Convert a FLOAT32 (only 16 lsb) depth into a RGB5A1 color texture
|
||||
uint d = uint(sample_c(input.t).r * exp2(32.0f));
|
||||
output.c = float4(uint4(d << 3, d >> 2, d >> 7, d >> 8) & uint4(0xf8, 0xf8, 0xf8, 0x80)) / 255.0f;
|
||||
return output;
|
||||
// Convert depth (only 16 lsb) into a RGB5A1 color texture
|
||||
return depth16_to_rgb5a1(sample_c(input.t));
|
||||
}
|
||||
#endif
|
||||
|
||||
float rgba8_to_depth32(float4 val)
|
||||
{
|
||||
uint4 c = uint4(val * 255.5f);
|
||||
return float(c.r | (c.g << 8) | (c.b << 16) | (c.a << 24)) * exp2(-32.0f);
|
||||
}
|
||||
|
||||
float rgba8_to_depth24(float4 val)
|
||||
{
|
||||
uint3 c = uint3(val.rgb * 255.5f);
|
||||
return float(c.r | (c.g << 8) | (c.b << 16)) * exp2(-32.0f);
|
||||
}
|
||||
|
||||
float rgba8_to_depth16(float4 val)
|
||||
{
|
||||
uint2 c = uint2(val.rg * 255.5f);
|
||||
return float(c.r | (c.g << 8)) * exp2(-32.0f);
|
||||
}
|
||||
|
||||
float rgb5a1_to_depth16(float4 val)
|
||||
{
|
||||
uint4 c = uint4(val * 255.5f);
|
||||
return float(((c.r & 0xF8u) >> 3) | ((c.g & 0xF8u) << 2) | ((c.b & 0xF8u) << 7) | ((c.a & 0x80u) << 8)) * exp2(-32.0f);
|
||||
}
|
||||
|
||||
float ps_convert_float32_depth_to_color(PS_INPUT input) : SV_Target0
|
||||
{
|
||||
return sample_c(input.t).r;
|
||||
}
|
||||
|
||||
float ps_convert_float32_color_to_depth(PS_INPUT input) : SV_Depth
|
||||
{
|
||||
return sample_c(input.t).r;
|
||||
}
|
||||
|
||||
float ps_convert_float32_float24(PS_INPUT input) : SV_Depth
|
||||
#if defined(__ps_convert_depth32_depth24__)
|
||||
OUTPUT_TYPE ps_convert_depth32_depth24(PS_INPUT input) : OUTPUT_SV
|
||||
{
|
||||
// Truncates depth value to 24bits
|
||||
uint d = uint(sample_c(input.t).r * exp2(32.0f)) & 0xFFFFFFu;
|
||||
return float(d) * exp2(-32.0f);
|
||||
}
|
||||
|
||||
float ps_convert_rgba8_float32(PS_INPUT input) : SV_Depth
|
||||
{
|
||||
// Convert an RGBA texture into a float depth texture
|
||||
return rgba8_to_depth32(sample_c(input.t));
|
||||
}
|
||||
|
||||
float ps_convert_rgba8_float24(PS_INPUT input) : SV_Depth
|
||||
{
|
||||
// Same as above but without the alpha channel (24 bits Z)
|
||||
|
||||
// Convert an RGBA texture into a float depth texture
|
||||
return rgba8_to_depth24(sample_c(input.t));
|
||||
}
|
||||
|
||||
float ps_convert_rgba8_float16(PS_INPUT input) : SV_Depth
|
||||
{
|
||||
// Same as above but without the A/B channels (16 bits Z)
|
||||
|
||||
// Convert an RGBA texture into a float depth texture
|
||||
return rgba8_to_depth16(sample_c(input.t));
|
||||
}
|
||||
|
||||
float ps_convert_rgb5a1_float16(PS_INPUT input) : SV_Depth
|
||||
{
|
||||
// Convert an RGB5A1 (saved as RGBA8) color to a 16 bit Z
|
||||
return rgb5a1_to_depth16(sample_c(input.t));
|
||||
return depth32_to_depth24(sample_c(input.t));
|
||||
}
|
||||
#endif
|
||||
|
||||
#define SAMPLE_RGBA_DEPTH_BILN(CONVERT_FN) \
|
||||
uint width, height; \
|
||||
@@ -272,34 +336,57 @@ float ps_convert_rgb5a1_float16(PS_INPUT input) : SV_Depth
|
||||
float depthBR = CONVERT_FN(Texture.Load(int3(coords.zw, 0))); \
|
||||
return lerp(lerp(depthTL, depthTR, mix_vals.x), lerp(depthBL, depthBR, mix_vals.x), mix_vals.y);
|
||||
|
||||
float ps_convert_rgba8_float32_biln(PS_INPUT input) : SV_Depth
|
||||
#if defined(__ps_convert_rgba8_depth32__)
|
||||
OUTPUT_TYPE ps_convert_rgba8_depth32(PS_INPUT input) : OUTPUT_SV
|
||||
{
|
||||
// Convert an RGBA texture into a float depth texture
|
||||
#if HAS_BILN
|
||||
SAMPLE_RGBA_DEPTH_BILN(rgba8_to_depth32);
|
||||
#else
|
||||
return rgba8_to_depth32(sample_c(input.t));
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
float ps_convert_rgba8_float24_biln(PS_INPUT input) : SV_Depth
|
||||
#if defined(__ps_convert_rgba8_depth24__)
|
||||
OUTPUT_TYPE ps_convert_rgba8_depth24(PS_INPUT input) : OUTPUT_SV
|
||||
{
|
||||
// Same as above but without the alpha channel (24 bits Z)
|
||||
|
||||
// Convert an RGBA texture into a float depth texture
|
||||
#if HAS_BILN
|
||||
SAMPLE_RGBA_DEPTH_BILN(rgba8_to_depth24);
|
||||
#else
|
||||
return rgba8_to_depth24(sample_c(input.t));
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
float ps_convert_rgba8_float16_biln(PS_INPUT input) : SV_Depth
|
||||
#if defined(__ps_convert_rgba8_depth16__)
|
||||
OUTPUT_TYPE ps_convert_rgba8_depth16(PS_INPUT input) : OUTPUT_SV
|
||||
{
|
||||
// Same as above but without the A/B channels (16 bits Z)
|
||||
|
||||
// Convert an RGBA texture into a float depth texture
|
||||
#if HAS_BILN
|
||||
SAMPLE_RGBA_DEPTH_BILN(rgba8_to_depth16);
|
||||
#else
|
||||
return rgba8_to_depth16(sample_c(input.t));
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
float ps_convert_rgb5a1_float16_biln(PS_INPUT input) : SV_Depth
|
||||
#if defined(__ps_convert_rgb5a1_depth16__)
|
||||
OUTPUT_TYPE ps_convert_rgb5a1_depth16(PS_INPUT input) : OUTPUT_SV
|
||||
{
|
||||
// Convert an RGB5A1 (saved as RGBA8) color to a 16 bit Z
|
||||
#if HAS_BILN
|
||||
SAMPLE_RGBA_DEPTH_BILN(rgb5a1_to_depth16);
|
||||
#else
|
||||
return rgb5a1_to_depth16(sample_c(input.t));
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__ps_convert_rgb5a1_8i__)
|
||||
PS_OUTPUT ps_convert_rgb5a1_8i(PS_INPUT input)
|
||||
{
|
||||
PS_OUTPUT output;
|
||||
@@ -406,7 +493,7 @@ PS_OUTPUT ps_convert_rgb5a1_8i(PS_INPUT input)
|
||||
uint red = (denorm_c.r >> 3) & 0x1Fu;
|
||||
uint green = (denorm_c.g >> 3) & 0x1Fu;
|
||||
|
||||
output.c = (float4)(((float)(((green << 5) | red) & 0xFFu)) / 255.0f);
|
||||
output.o = (float4)(((float)(((green << 5) | red) & 0xFFu)) / 255.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -414,11 +501,13 @@ PS_OUTPUT ps_convert_rgb5a1_8i(PS_INPUT input)
|
||||
uint blue = (denorm_c.b >> 3) & 0x1Fu;
|
||||
uint alpha = denorm_c.a & 0x80u;
|
||||
|
||||
output.c = (float4)(((float)((alpha | (blue << 2) | (green >> 3)) & 0xFFu)) / 255.0f);
|
||||
output.o = (float4)(((float)((alpha | (blue << 2) | (green >> 3)) & 0xFFu)) / 255.0f);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__ps_convert_rgba_8i__)
|
||||
PS_OUTPUT ps_convert_rgba_8i(PS_INPUT input)
|
||||
{
|
||||
PS_OUTPUT output;
|
||||
@@ -462,10 +551,12 @@ PS_OUTPUT ps_convert_rgba_8i(PS_INPUT input)
|
||||
float4 pixel = Texture.Load(int3(int2(coord), 0));
|
||||
float2 sel0 = (pos.y & 2u) == 0u ? pixel.rb : pixel.ga;
|
||||
float sel1 = (pos.x & 8u) == 0u ? sel0.x : sel0.y;
|
||||
output.c = (float4)(sel1); // Divide by something here?
|
||||
output.o = (float4)(sel1); // Divide by something here?
|
||||
return output;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__ps_convert_clut_4__)
|
||||
PS_OUTPUT ps_convert_clut_4(PS_INPUT input)
|
||||
{
|
||||
// Borrowing the YUV constant buffer.
|
||||
@@ -478,10 +569,12 @@ PS_OUTPUT ps_convert_clut_4(PS_INPUT input)
|
||||
|
||||
int2 final = int2(floor(float2(offset + pos) * scale));
|
||||
PS_OUTPUT output;
|
||||
output.c = Texture.Load(int3(final, 0), 0);
|
||||
output.o = Texture.Load(int3(final, 0), 0);
|
||||
return output;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__ps_convert_clut_8__)
|
||||
PS_OUTPUT ps_convert_clut_8(PS_INPUT input)
|
||||
{
|
||||
float scale = BGColor.x;
|
||||
@@ -497,10 +590,12 @@ PS_OUTPUT ps_convert_clut_8(PS_INPUT input)
|
||||
|
||||
int2 final = int2(floor(float2(offset + pos) * scale));
|
||||
PS_OUTPUT output;
|
||||
output.c = Texture.Load(int3(final, 0), 0);
|
||||
output.o = Texture.Load(int3(final, 0), 0);
|
||||
return output;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__ps_yuv__)
|
||||
PS_OUTPUT ps_yuv(PS_INPUT input)
|
||||
{
|
||||
PS_OUTPUT output;
|
||||
@@ -515,41 +610,43 @@ PS_OUTPUT ps_yuv(PS_INPUT input)
|
||||
switch (EMODA)
|
||||
{
|
||||
case 0:
|
||||
output.c.a = i.a;
|
||||
output.o.a = i.a;
|
||||
break;
|
||||
case 1:
|
||||
output.c.a = Y;
|
||||
output.o.a = Y;
|
||||
break;
|
||||
case 2:
|
||||
output.c.a = Y / 2.0f;
|
||||
output.o.a = Y / 2.0f;
|
||||
break;
|
||||
case 3:
|
||||
default:
|
||||
output.c.a = 0.0f;
|
||||
output.o.a = 0.0f;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (EMODC)
|
||||
{
|
||||
case 0:
|
||||
output.c.rgb = i.rgb;
|
||||
output.o.rgb = i.rgb;
|
||||
break;
|
||||
case 1:
|
||||
output.c.rgb = float3(Y, Y, Y);
|
||||
output.o.rgb = float3(Y, Y, Y);
|
||||
break;
|
||||
case 2:
|
||||
output.c.rgb = float3(Y, Cb, Cr);
|
||||
output.o.rgb = float3(Y, Cb, Cr);
|
||||
break;
|
||||
case 3:
|
||||
default:
|
||||
output.c.rgb = float3(i.a, i.a, i.a);
|
||||
output.o.rgb = float3(i.a, i.a, i.a);
|
||||
break;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
#endif
|
||||
|
||||
float ps_stencil_image_init_0(PS_INPUT input) : SV_Target
|
||||
#if defined(__ps_primid_image_init_0__)
|
||||
float ps_primid_image_init_0(PS_INPUT input) : SV_Target
|
||||
{
|
||||
float c;
|
||||
if ((127.5f / 255.0f) < sample_c(input.t).a) // < 0x80 pass (== 0x80 should not pass)
|
||||
@@ -558,8 +655,10 @@ float ps_stencil_image_init_0(PS_INPUT input) : SV_Target
|
||||
c = float(0x7FFFFFFF);
|
||||
return c;
|
||||
}
|
||||
#endif
|
||||
|
||||
float ps_stencil_image_init_1(PS_INPUT input) : SV_Target
|
||||
#if defined(__ps_primid_image_init_1__)
|
||||
float ps_primid_image_init_1(PS_INPUT input) : SV_Target
|
||||
{
|
||||
float c;
|
||||
if (sample_c(input.t).a < (127.5f / 255.0f)) // >= 0x80 pass
|
||||
@@ -568,9 +667,10 @@ float ps_stencil_image_init_1(PS_INPUT input) : SV_Target
|
||||
c = float(0x7FFFFFFF);
|
||||
return c;
|
||||
}
|
||||
#endif
|
||||
|
||||
float ps_stencil_image_init_2(PS_INPUT input)
|
||||
: SV_Target
|
||||
#if defined(__ps_primid_image_init_2__)
|
||||
float ps_primid_image_init_2(PS_INPUT input) : SV_Target
|
||||
{
|
||||
float c;
|
||||
if ((254.5f / 255.0f) < sample_c(input.t).a) // < 0x80 pass (== 0x80 should not pass)
|
||||
@@ -579,9 +679,10 @@ float ps_stencil_image_init_2(PS_INPUT input)
|
||||
c = float(0x7FFFFFFF);
|
||||
return c;
|
||||
}
|
||||
#endif
|
||||
|
||||
float ps_stencil_image_init_3(PS_INPUT input)
|
||||
: SV_Target
|
||||
#if defined(__ps_primid_image_init_3__)
|
||||
float ps_primid_image_init_3(PS_INPUT input) : SV_Target
|
||||
{
|
||||
float c;
|
||||
if (sample_c(input.t).a < (254.5f / 255.0f)) // >= 0x80 pass
|
||||
@@ -590,3 +691,6 @@ float ps_stencil_image_init_3(PS_INPUT input)
|
||||
c = float(0x7FFFFFFF);
|
||||
return c;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // PIXEL_SHADER
|
||||
@@ -50,6 +50,12 @@
|
||||
#define PS_AA1_TRIANGLE_SW_Z 3
|
||||
#endif
|
||||
|
||||
#ifndef PS_ROV_DEPTH_NONE
|
||||
#define PS_ROV_DEPTH_NONE 0
|
||||
#define PS_ROV_DEPTH_READ_WRITE 1
|
||||
#define PS_ROV_DEPTH_READ_ONLY 2
|
||||
#endif
|
||||
|
||||
#ifndef PS_FST
|
||||
#define PS_IIP 0
|
||||
#define PS_FST 0
|
||||
@@ -111,6 +117,8 @@
|
||||
#define PS_TEX_IS_FB 0
|
||||
#define PS_AA1 0
|
||||
#define PS_ABE 0
|
||||
#define PS_ROV_COLOR 0
|
||||
#define PS_ROV_DEPTH 0
|
||||
#endif
|
||||
|
||||
#ifndef VS_EXPAND_NONE
|
||||
@@ -132,6 +140,12 @@
|
||||
#define SW_DEPTH (NEEDS_DEPTH_FOR_AFAIL || NEEDS_DEPTH_FOR_ZTST || NEEDS_DEPTH_FOR_AA1)
|
||||
#define ZWRITE (PS_ZFLOOR || PS_ZCLAMP || SW_DEPTH)
|
||||
|
||||
#define PS_RETURN_COLOR_ROV (!PS_NO_COLOR && PS_ROV_COLOR)
|
||||
#define PS_RETURN_COLOR (!PS_NO_COLOR && !PS_ROV_COLOR)
|
||||
#define PS_RETURN_DEPTH_ROV (PS_ROV_DEPTH == PS_ROV_DEPTH_READ_WRITE)
|
||||
#define PS_RETURN_DEPTH (ZWRITE && !PS_ROV_DEPTH)
|
||||
#define PS_ROV_EARLYDEPTHSTENCIL (PS_ROV_COLOR && !PS_ROV_DEPTH && !ZWRITE)
|
||||
|
||||
struct VS_INPUT
|
||||
{
|
||||
float2 st : TEXCOORD0;
|
||||
@@ -178,22 +192,27 @@ struct PS_INPUT
|
||||
|
||||
#ifdef PIXEL_SHADER
|
||||
|
||||
struct PS_OUTPUT
|
||||
struct PS_OUTPUT_REAL
|
||||
{
|
||||
#define NUM_RTS 0
|
||||
#if !PS_NO_COLOR
|
||||
#if PS_DATE == 1 || PS_DATE == 2
|
||||
float c : SV_Target;
|
||||
#else
|
||||
float4 c0 : SV_Target0;
|
||||
#undef NUM_RTS
|
||||
#define NUM_RTS 1
|
||||
#if !PS_NO_COLOR1
|
||||
float4 c1 : SV_Target1;
|
||||
|
||||
#if PS_RETURN_COLOR
|
||||
#if PS_DATE == 1 || PS_DATE == 2
|
||||
float c : SV_Target;
|
||||
#else
|
||||
|
||||
float4 c0 : SV_Target0;
|
||||
|
||||
#undef NUM_RTS
|
||||
#define NUM_RTS 1
|
||||
|
||||
#if !PS_NO_COLOR1
|
||||
float4 c1 : SV_Target1;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#if ZWRITE
|
||||
|
||||
#if PS_RETURN_DEPTH
|
||||
// In DX12 we do depth feedback loops with a color copy.
|
||||
#if SW_DEPTH && PS_NO_COLOR1 && DX12
|
||||
#if NUM_RTS > 0
|
||||
@@ -208,16 +227,45 @@ struct PS_OUTPUT
|
||||
float depth : SV_Depth;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#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
|
||||
Texture2D<float4> RtTexture : register(t2);
|
||||
#endif
|
||||
Texture2D<float> PrimMinTexture : register(t3);
|
||||
#if !PS_ROV_DEPTH
|
||||
Texture2D<float> DepthTexture : register(t4);
|
||||
#endif
|
||||
SamplerState TextureSampler : register(s0);
|
||||
|
||||
#if PS_ROV_COLOR
|
||||
RasterizerOrderedTexture2D<unorm float4> RtTextureRov : register(u0);
|
||||
static float4 rov_rt_value;
|
||||
#endif
|
||||
|
||||
#if PS_ROV_DEPTH
|
||||
RasterizerOrderedTexture2D<float> DepthTextureRov : register(u1);
|
||||
static float rov_depth_value;
|
||||
#endif
|
||||
|
||||
#ifdef DX12
|
||||
cbuffer cb1 : register(b1)
|
||||
#else
|
||||
@@ -244,6 +292,38 @@ cbuffer cb1
|
||||
float RcpScaleFactor;
|
||||
};
|
||||
|
||||
float4 RtLoad(int2 xy)
|
||||
{
|
||||
#if PS_ROV_COLOR
|
||||
return rov_rt_value;
|
||||
#else
|
||||
return RtTexture.Load(int3(int2(xy), 0));
|
||||
#endif
|
||||
}
|
||||
|
||||
float DepthLoad(int2 xy)
|
||||
{
|
||||
#if PS_ROV_DEPTH
|
||||
return rov_depth_value;
|
||||
#else
|
||||
return DepthTexture.Load(int3(int2(xy), 0));
|
||||
#endif
|
||||
}
|
||||
|
||||
void RtWrite(int2 xy, float4 c)
|
||||
{
|
||||
#if PS_ROV_COLOR
|
||||
RtTextureRov[xy] = c;
|
||||
#endif
|
||||
}
|
||||
|
||||
void DepthWrite(int2 xy, float d)
|
||||
{
|
||||
#if PS_ROV_DEPTH
|
||||
DepthTextureRov[xy] = d;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if (PS_AUTOMATIC_LOD != 1) && (PS_MANUAL_LOD == 1)
|
||||
float manual_lod(float uv_w)
|
||||
{
|
||||
@@ -261,10 +341,21 @@ float manual_lod(float uv_w)
|
||||
#endif
|
||||
|
||||
#if PS_ANISOTROPIC_FILTERING > 1
|
||||
bool2 nan_or_inf(float2 xy)
|
||||
{
|
||||
// FXC (<=SM5.1) may optimise away isnan and isinf.
|
||||
// DXC (>=SM6.0) will preserve them.
|
||||
#ifdef __hlsl_dx_compiler
|
||||
return isinf(xy) | isnan(xy);
|
||||
#else
|
||||
return (asuint(xy) & 0x7f800000) == 0x7f800000;
|
||||
#endif
|
||||
}
|
||||
|
||||
float4 sample_c_af(float2 uv, float uv_w)
|
||||
{
|
||||
// HW sampler will reject bad UVs, match that here.
|
||||
uv = any(isnan(uv) | isinf(uv)) ? float2(0, 0) : uv;
|
||||
uv = any(nan_or_inf(uv)) ? float2(0, 0) : uv;
|
||||
|
||||
// Large floating point values risk NaN/Inf values.
|
||||
// Above this value floats lose decimal precision, so seems a resonable limit for UVs.
|
||||
@@ -281,7 +372,7 @@ float4 sample_c_af(float2 uv, float uv_w)
|
||||
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_inf_nan = any(isinf(dX) | isinf(dY) | isnan(dX) | isnan(dY));
|
||||
bool d_inf_nan = any(nan_or_inf(dX) | nan_or_inf(dY));
|
||||
|
||||
if (!(d_zero || d_par || d_per || d_inf_nan))
|
||||
{
|
||||
@@ -305,7 +396,7 @@ float4 sample_c_af(float2 uv, float uv_w)
|
||||
sqrt(F * (t + p) / (t * (q - t)))
|
||||
);
|
||||
|
||||
d_inf_nan = any(isinf(new_dX) | isinf(new_dY) | isnan(new_dX) | isnan(new_dY));
|
||||
d_inf_nan = any(nan_or_inf(new_dX) | nan_or_inf(new_dY));
|
||||
if (!d_inf_nan)
|
||||
{
|
||||
dX = new_dX;
|
||||
@@ -396,7 +487,7 @@ float4 sample_c_af(float2 uv, float uv_w)
|
||||
float4 sample_c(float2 uv, float uv_w, int2 xy)
|
||||
{
|
||||
#if PS_TEX_IS_FB == 1
|
||||
return RtTexture.Load(int3(int2(xy), 0));
|
||||
return RtLoad(xy);
|
||||
#elif PS_REGION_RECT == 1
|
||||
return Texture.Load(int3(int2(uv), 0));
|
||||
#else
|
||||
@@ -591,7 +682,7 @@ float4x4 sample_4p(uint4 u)
|
||||
uint fetch_raw_depth(int2 xy)
|
||||
{
|
||||
#if PS_TEX_IS_FB == 1
|
||||
float4 col = RtTexture.Load(int3(xy, 0));
|
||||
float4 col = RtLoad(xy);
|
||||
#else
|
||||
float4 col = Texture.Load(int3(xy, 0));
|
||||
#endif
|
||||
@@ -601,7 +692,7 @@ uint fetch_raw_depth(int2 xy)
|
||||
float4 fetch_raw_color(int2 xy)
|
||||
{
|
||||
#if PS_TEX_IS_FB == 1
|
||||
return RtTexture.Load(int3(xy, 0));
|
||||
return RtLoad(xy);
|
||||
#else
|
||||
return Texture.Load(int3(xy, 0));
|
||||
#endif
|
||||
@@ -610,7 +701,7 @@ float4 fetch_raw_color(int2 xy)
|
||||
float4 fetch_c(int2 uv)
|
||||
{
|
||||
#if PS_TEX_IS_FB == 1
|
||||
return RtTexture.Load(int3(uv, 0));
|
||||
return RtLoad(uv);
|
||||
#else
|
||||
return Texture.Load(int3(uv, 0));
|
||||
#endif
|
||||
@@ -696,7 +787,7 @@ float4 sample_depth(float2 st, float2 pos)
|
||||
}
|
||||
else if (PS_DEPTH_FMT == 1)
|
||||
{
|
||||
// Based on ps_convert_float32_rgba8 of convert
|
||||
// Based on ps_convert_depth32_rgba8 of convert
|
||||
|
||||
// Convert a FLOAT32 depth texture into a RGBA color texture
|
||||
uint d = uint(fetch_c(uv).r * exp2(32.0f));
|
||||
@@ -704,7 +795,7 @@ float4 sample_depth(float2 st, float2 pos)
|
||||
}
|
||||
else if (PS_DEPTH_FMT == 2)
|
||||
{
|
||||
// Based on ps_convert_float16_rgb5a1 of convert
|
||||
// Based on ps_convert_depth16_rgb5a1 of convert
|
||||
|
||||
// Convert a FLOAT32 (only 16 lsb) depth into a RGB5A1 color texture
|
||||
uint d = uint(fetch_c(uv).r * exp2(32.0f));
|
||||
@@ -1016,7 +1107,7 @@ void ps_fbmask(inout float4 C, float2 pos_xy)
|
||||
if (PS_FBMASK)
|
||||
{
|
||||
float multi = PS_COLCLIP_HW ? 65535.0f : 255.0f;
|
||||
float4 RT = trunc(RtTexture.Load(int3(pos_xy, 0)) * multi + 0.1f);
|
||||
float4 RT = trunc(RtLoad(int2(pos_xy)) * multi + 0.1f);
|
||||
C = (float4)(((uint4)C & ~FbMask) | ((uint4)RT & FbMask));
|
||||
}
|
||||
}
|
||||
@@ -1092,7 +1183,7 @@ void ps_blend(inout float4 Color, inout float4 As_rgba, float2 pos_xy)
|
||||
As_rgba.rgb = (float3)1.0f;
|
||||
}
|
||||
|
||||
float4 RT = SW_BLEND_NEEDS_RT ? RtTexture.Load(int3(pos_xy, 0)) : (float4)0.0f;
|
||||
float4 RT = SW_BLEND_NEEDS_RT ? RtLoad(int2(pos_xy)) : (float4)0.0f;
|
||||
|
||||
if (PS_SHUFFLE && SW_BLEND_NEEDS_RT)
|
||||
{
|
||||
@@ -1225,20 +1316,49 @@ void ps_blend(inout float4 Color, inout float4 As_rgba, float2 pos_xy)
|
||||
}
|
||||
}
|
||||
|
||||
PS_OUTPUT ps_main(PS_INPUT input)
|
||||
#if PS_ROV_EARLYDEPTHSTENCIL
|
||||
[earlydepthstencil]
|
||||
#endif
|
||||
|
||||
#if PS_ROV_COLOR || PS_ROV_DEPTH
|
||||
#define DISCARD rov_discard = true
|
||||
#else
|
||||
#define DISCARD discard
|
||||
#endif
|
||||
|
||||
#if (PS_RETURN_COLOR || PS_RETURN_DEPTH)
|
||||
PS_OUTPUT_REAL ps_main(PS_INPUT input)
|
||||
#else
|
||||
void ps_main(PS_INPUT input)
|
||||
#endif
|
||||
{
|
||||
// Must floor before depth testing.
|
||||
#if PS_ZFLOOR
|
||||
input.p.z = floor(input.p.z * exp2(32.0f)) * exp2(-32.0f);
|
||||
#endif
|
||||
|
||||
#if PS_ZTST == ZTST_GEQUAL
|
||||
if (input.p.z < DepthTexture.Load(int3(input.p.xy, 0)).r)
|
||||
discard;
|
||||
#elif PS_ZTST == ZTST_GREATER
|
||||
if (input.p.z <= DepthTexture.Load(int3(input.p.xy, 0)).r)
|
||||
discard;
|
||||
#if PS_ROV_COLOR
|
||||
rov_rt_value = RtTextureRov[input.p.xy];
|
||||
#endif
|
||||
|
||||
#if PS_ROV_DEPTH
|
||||
rov_depth_value = DepthTextureRov[input.p.xy];
|
||||
#endif
|
||||
|
||||
#if PS_ROV_COLOR || PS_ROV_DEPTH
|
||||
bool rov_discard = false;
|
||||
#endif
|
||||
|
||||
// Use ROV discard macro for since we cannot do
|
||||
// conditional discard based on value read from ROV.
|
||||
#if PS_ZTST == ZTST_GEQUAL
|
||||
if (input.p.z < DepthLoad(input.p.xy))
|
||||
DISCARD;
|
||||
#elif PS_ZTST == ZTST_GREATER
|
||||
if (input.p.z <= DepthLoad(input.p.xy))
|
||||
DISCARD;
|
||||
#endif
|
||||
|
||||
float4 C = ps_color(input);
|
||||
|
||||
#if PS_AA1
|
||||
@@ -1256,13 +1376,11 @@ PS_OUTPUT ps_main(PS_INPUT input)
|
||||
|
||||
bool atst_pass = atst(C);
|
||||
|
||||
#if PS_AFAIL == AFAIL_KEEP
|
||||
#if PS_ATST != PS_ATST_NONE && PS_AFAIL == AFAIL_KEEP
|
||||
if (!atst_pass)
|
||||
discard;
|
||||
#endif
|
||||
|
||||
PS_OUTPUT output;
|
||||
|
||||
if (PS_SCANMSK & 2)
|
||||
{
|
||||
// fail depth test on prohibited lines
|
||||
@@ -1273,7 +1391,7 @@ PS_OUTPUT ps_main(PS_INPUT input)
|
||||
float4 alpha_blend = (float4)0.0f;
|
||||
if (SW_AD_TO_HW)
|
||||
{
|
||||
float4 RT = PS_RTA_CORRECTION ? trunc(RtTexture.Load(int3(input.p.xy, 0)) * 128.0f + 0.1f) : trunc(RtTexture.Load(int3(input.p.xy, 0)) * 255.0f + 0.1f);
|
||||
float4 RT = PS_RTA_CORRECTION ? trunc(RtLoad(input.p.xy) * 128.0f + 0.1f) : trunc(RtLoad(input.p.xy) * 255.0f + 0.1f);
|
||||
alpha_blend = (float4)(RT.a / 128.0f);
|
||||
}
|
||||
else
|
||||
@@ -1297,9 +1415,9 @@ PS_OUTPUT ps_main(PS_INPUT input)
|
||||
|
||||
#if PS_WRITE_RG == 1
|
||||
// Pseudo 16 bits access.
|
||||
float rt_a = RtTexture.Load(int3(input.p.xy, 0)).g;
|
||||
float rt_a = RtLoad(input.p.xy).g;
|
||||
#else
|
||||
float rt_a = RtTexture.Load(int3(input.p.xy, 0)).a;
|
||||
float rt_a = RtLoad(input.p.xy).a;
|
||||
#endif
|
||||
|
||||
#if (PS_DATE & 3) == 1
|
||||
@@ -1318,9 +1436,8 @@ PS_OUTPUT ps_main(PS_INPUT input)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (bad)
|
||||
discard;
|
||||
|
||||
if (bad)
|
||||
discard;
|
||||
#endif
|
||||
|
||||
#if PS_DATE == 3
|
||||
@@ -1331,6 +1448,8 @@ PS_OUTPUT ps_main(PS_INPUT input)
|
||||
discard;
|
||||
#endif
|
||||
|
||||
PS_OUTPUT output;
|
||||
|
||||
// Get first primitive that will write a failling alpha value
|
||||
#if PS_DATE == 1
|
||||
// DATM == 0
|
||||
@@ -1416,32 +1535,32 @@ PS_OUTPUT ps_main(PS_INPUT input)
|
||||
alpha_blend.a = float(atst_pass);
|
||||
#endif
|
||||
|
||||
// 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;
|
||||
#if !PS_NO_COLOR1
|
||||
output.c1 = alpha_blend;
|
||||
#endif
|
||||
#endif // !PS_NO_COLOR
|
||||
|
||||
// Alpha test with feedback
|
||||
#if PS_AFAIL == AFAIL_FB_ONLY
|
||||
if (!atst_pass)
|
||||
input.p.z = DepthTexture.Load(int3(input.p.xy, 0)).r;
|
||||
input.p.z = DepthLoad(input.p.xy);
|
||||
#elif PS_AFAIL == AFAIL_ZB_ONLY
|
||||
if (!atst_pass)
|
||||
output.c0 = RtTexture.Load(int3(input.p.xy, 0));
|
||||
output.c0 = RtLoad(input.p.xy);
|
||||
#elif PS_AFAIL == AFAIL_RGB_ONLY || PS_AFAIL == AFAIL_RGB_ONLY_SW_Z
|
||||
if (!atst_pass)
|
||||
{
|
||||
output.c0.a = RtTexture.Load(int3(input.p.xy, 0)).a;
|
||||
output.c0.a = RtLoad(input.p.xy).a;
|
||||
#if PS_AFAIL == AFAIL_RGB_ONLY_SW_Z
|
||||
input.p.z = DepthTexture.Load(int3(input.p.xy, 0)).r;
|
||||
input.p.z = DepthLoad(input.p.xy);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // !PS_NO_COLOR
|
||||
|
||||
#endif // PS_DATE != 1/2
|
||||
|
||||
#if PS_ZCLAMP
|
||||
@@ -1450,18 +1569,47 @@ PS_OUTPUT ps_main(PS_INPUT input)
|
||||
|
||||
#if PS_AA1 == PS_AA1_TRIANGLE_SW_Z
|
||||
if (!bool(input.interior))
|
||||
input.p.z = DepthTexture.Load(int3(input.p.xy, 0)).r; // No depth update for triangle edges.
|
||||
input.p.z = DepthLoad(input.p.xy); // No depth update for triangle edges.
|
||||
#endif
|
||||
|
||||
#if ZWRITE
|
||||
#if SW_DEPTH && PS_NO_COLOR1 && DX12
|
||||
// Output color clone for feedback as well as real depth.
|
||||
output.depth_color = input.p.z;
|
||||
#endif
|
||||
output.depth = input.p.z;
|
||||
#if (PS_RETURN_COLOR || PS_RETURN_DEPTH)
|
||||
// Output struct with the actual system values output semantics.
|
||||
PS_OUTPUT_REAL output_real;
|
||||
#endif
|
||||
|
||||
return output;
|
||||
// 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
|
||||
#endif
|
||||
#elif PS_RETURN_COLOR_ROV
|
||||
output.c0 = (rov_discard | (FbMask == 0xFFu)) ? RtLoad(input.p.xy) : output.c0;
|
||||
|
||||
RtWrite(input.p.xy, output.c0);
|
||||
#endif
|
||||
|
||||
// Depth write back
|
||||
#if PS_RETURN_DEPTH
|
||||
output_real.depth = input.p.z;
|
||||
#if SW_DEPTH && PS_NO_COLOR1 && DX12
|
||||
// Output color clone for feedback.
|
||||
output_real.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);
|
||||
#endif
|
||||
|
||||
#if (PS_RETURN_COLOR || PS_RETURN_DEPTH)
|
||||
return output_real;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // PIXEL_SHADER
|
||||
@@ -1594,6 +1742,110 @@ VS_INPUT load_vertex(uint index)
|
||||
return vert;
|
||||
}
|
||||
|
||||
// Convert XY from NDC to GS pixel coordinates (i.e. 1.0 = 1 GS pixel).
|
||||
float2 get_xy_unscaled(float2 xy)
|
||||
{
|
||||
return round(xy / VertexScale) / 16.0f;
|
||||
}
|
||||
|
||||
// Get the XY deltas in GS pixel coordinates, using first vertex as the origin.
|
||||
float2x2 get_xy_deltas_unscaled(VS_OUTPUT v0, VS_OUTPUT v1, VS_OUTPUT v2)
|
||||
{
|
||||
float2 xy0 = get_xy_unscaled(v0.p.xy);
|
||||
float2 xy1 = get_xy_unscaled(v1.p.xy);
|
||||
float2 xy2 = get_xy_unscaled(v2.p.xy);
|
||||
return float2x2(xy1 - xy0, xy2 - xy0);
|
||||
}
|
||||
|
||||
// Get the AA1 outward expand direction to the edge formed by the first two vertices.
|
||||
// This is up or down for shallow (X dominant) edges, and right or left for steep (Y dominant) edges.
|
||||
// Similar expansion to line AA1 except instead of expanding on both sides of the line,
|
||||
// expand on on the side towards the outside of the triangle.
|
||||
float2 get_aa1_triangle_expand_dir(VS_OUTPUT v0, VS_OUTPUT v1, VS_OUTPUT v2)
|
||||
{
|
||||
float2x2 xy_deltas = get_xy_deltas_unscaled(v0, v1, v2);
|
||||
float2 line_delta = xy_deltas[0];
|
||||
float2 line_opposite = xy_deltas[1];
|
||||
|
||||
float2 line_normal = float2(line_delta.y, -line_delta.x);
|
||||
float2 line_expand = abs(line_delta.x) >= abs(line_delta.y) ? float2(0.0f, 1.0f) : float2(1.0f, 0.0f);
|
||||
|
||||
if ((dot(line_expand, line_normal) >= 0.0f) == (dot(line_opposite, line_normal) >= 0.0f))
|
||||
{
|
||||
// Expand direction point towards the interior so flip it.
|
||||
line_expand = -line_expand;
|
||||
}
|
||||
|
||||
return line_expand;
|
||||
}
|
||||
|
||||
float2x2 get_inverse(float2x2 mat, float det)
|
||||
{
|
||||
return float2x2(mat[1][1], -mat[0][1], -mat[1][0], mat[0][0]) * (1 / det);
|
||||
}
|
||||
|
||||
// Extrapolate triangle attributes from the first vertex along the given direction.
|
||||
// dp_mat is derived from the input vertices, it is passed in to avoid recomputing.
|
||||
void extrapolate_aa1_triangle_edge(inout VS_OUTPUT v0, VS_OUTPUT v1, VS_OUTPUT v2, float2x2 dp_mat, float2 dp)
|
||||
{
|
||||
// Get texture deltas
|
||||
#if VS_TME
|
||||
#if VS_FST
|
||||
float2x2 dt = float2x2(v1.ti.zw - v0.ti.zw, v2.ti.zw - v0.ti.zw);
|
||||
#else
|
||||
float2x2 dt = float2x2(v1.t.xy - v0.t.xy, v2.t.xy - v0.t.xy);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Get color delta if interpolating
|
||||
#if VS_IIP
|
||||
float2x4 dc = float2x4(v1.c - v0.c, v2.c - v0.c);
|
||||
#endif
|
||||
|
||||
float2 dz = float2(v1.p.z - v0.p.z, v2.p.z - v0.p.z); // Z deltas
|
||||
|
||||
float2 df = float2(v1.t.z - v0.t.z, v2.t.z - v0.t.z); // Fog deltas
|
||||
|
||||
float2 dq = float2(v1.t.w - v0.t.w, v2.t.w - v0.t.w); // Q deltas
|
||||
|
||||
// To prevent unstable extrapolation, do not extrapolate if the
|
||||
// minimum perpendicular length of the triangle is < 2 pixels.
|
||||
float dp_det = determinant(dp_mat); // Twice signed triangle area.
|
||||
float len0 = length(dp_mat[0]);
|
||||
float len1 = length(dp_mat[1]);
|
||||
float len2 = length(dp_mat[1] - dp_mat[0]);
|
||||
float min_perp_length = abs(dp_det) / max(max(len0, len1), len2);
|
||||
|
||||
// Get the position -> barycentric weight matrix
|
||||
float2x2 inv_dp_mat = get_inverse(dp_mat, dp_det);
|
||||
|
||||
float2 weights = min_perp_length < 2 ? 0 : mul(dp, inv_dp_mat);
|
||||
|
||||
v0.p.xy += dp * PointSize; // Extrapolate position
|
||||
|
||||
// Extrapolate texture coords
|
||||
#if VS_TME
|
||||
#if VS_FST
|
||||
v0.ti.zw += mul(weights, dt);
|
||||
v0.ti.xy = v0.ti.zw * TextureScale;
|
||||
#else
|
||||
v0.t.xy += mul(weights, dt);
|
||||
v0.ti.zw = v0.t.xy / TextureScale;
|
||||
v0.t.w += dot(weights, dq);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Extrapolate and clamp color
|
||||
#if VS_IIP
|
||||
v0.c += mul(weights, dc);
|
||||
v0.c = clamp(v0.c, 0, 255);
|
||||
#endif
|
||||
|
||||
v0.p.z += dot(weights, dz); // Extrapolate depth
|
||||
|
||||
v0.t.z += dot(weights, df); // Extrapolate fog
|
||||
}
|
||||
|
||||
VS_OUTPUT vs_main_expand(uint vid : SV_VertexID)
|
||||
{
|
||||
#if VS_EXPAND == VS_EXPAND_POINT
|
||||
@@ -1622,13 +1874,12 @@ VS_OUTPUT vs_main_expand(uint vid : SV_VertexID)
|
||||
|
||||
// Use bottom minus top for delta regardless of which vertex we are expanding.
|
||||
float2 line_delta = is_bottom ? (vtx.p.xy - other.p.xy) : (other.p.xy - vtx.p.xy);
|
||||
float2 line_vector = normalize(line_delta);
|
||||
float2 line_vector = normalize(line_delta / VertexScale);
|
||||
#if VS_EXPAND == VS_EXPAND_LINE
|
||||
float2 line_expand = float2(line_vector.y, -line_vector.x);
|
||||
#elif VS_EXPAND == VS_EXPAND_LINE_AA1
|
||||
// Expand in y direction for shallow lines and x direction for steep lines.
|
||||
line_delta /= VertexScale;
|
||||
float2 line_expand = abs(line_delta.x) >= abs(line_delta.y) ? float2(0.0f, 2.0f) : float2(2.0f, 0.0f);
|
||||
float2 line_expand = abs(line_vector.x) >= abs(line_vector.y) ? float2(0.0f, 2.0f) : float2(2.0f, 0.0f);
|
||||
#endif
|
||||
float2 line_width = (line_expand * PointSize) / 2;
|
||||
float2 offset = is_right ? line_width : -line_width;
|
||||
@@ -1674,10 +1925,14 @@ VS_OUTPUT vs_main_expand(uint vid : SV_VertexID)
|
||||
// - Vertices 3-8: First edge expanded (2 triangles).
|
||||
// - Vertices 9-14: Second edge expanded (2 triangles).
|
||||
// - Vertices 15-20: Third edge expanded (2 triangles).
|
||||
// - Vertices 21-26: First corner cap (2 triangles).
|
||||
// - Vertices 27-32: Second corner cap (2 triangles).
|
||||
// - Vertices 33-38: Third corner cap (2 triangles).
|
||||
|
||||
uint prim_id = vid / 21;
|
||||
uint prim_offset = vid - 21 * prim_id; // range: 0-20
|
||||
uint prim_id = vid / 39;
|
||||
uint prim_offset = vid - 39 * prim_id; // range: 0-38
|
||||
bool interior = prim_offset < 3;
|
||||
bool edge = 3 <= prim_offset && prim_offset < 21;
|
||||
|
||||
VS_OUTPUT vtx;
|
||||
if (interior)
|
||||
@@ -1686,7 +1941,7 @@ VS_OUTPUT vs_main_expand(uint vid : SV_VertexID)
|
||||
vtx.inv_cov = 0.0f; // Full coverage
|
||||
vtx.interior = 1;
|
||||
}
|
||||
else
|
||||
else if (edge)
|
||||
{
|
||||
// Vertex indices for this edge. We need all 3 for determining exterior/interior.
|
||||
uint prim_offset_edges = prim_offset - 3; // range: 0-17
|
||||
@@ -1695,39 +1950,67 @@ VS_OUTPUT vs_main_expand(uint vid : SV_VertexID)
|
||||
uint i2 = (i0 >= 1) ? i0 - 1 : i0 + 2;
|
||||
uint edge_offset = prim_offset_edges - 6 * i0; // range: 0-5
|
||||
|
||||
// Note: order of top/bottom, inside/outside order is arbitrary,
|
||||
// Note: order of top/bottom, inside/outside is arbitrary,
|
||||
// as long as it assembles into two triangles forming a quad.
|
||||
bool is_bottom = (2 <= edge_offset) && (edge_offset <= 4);
|
||||
bool is_outside = edge_offset & 1;
|
||||
|
||||
vtx = vs_main(load_vertex(load_index(3 * prim_id + i0)));
|
||||
VS_OUTPUT other = vs_main(load_vertex(load_index(3 * prim_id + i1)));
|
||||
vtx = vs_main(load_vertex(load_index(3 * prim_id + (is_bottom ? i1 : i0))));
|
||||
VS_OUTPUT other = vs_main(load_vertex(load_index(3 * prim_id + (is_bottom ? i0 : i1))));
|
||||
VS_OUTPUT opposite = vs_main(load_vertex(load_index(3 * prim_id + i2)));
|
||||
|
||||
// Similar expansion to line AA1 except instead of expanding on both sides of
|
||||
// the line we expand on on the side towards the outside of the triangle.
|
||||
float2 line_delta = vtx.p.xy - other.p.xy;
|
||||
float2 line_normal = normalize(float2(line_delta.y, -line_delta.x));
|
||||
float2 line_expand = abs(line_delta.x) >= abs(line_delta.y) ? float2(0.0f, 2.0f) : float2(2.0f, 0.0f);
|
||||
if ((dot(line_expand, line_normal) >= 0.0f) == (dot(opposite.p.xy - vtx.p.xy, line_normal) >= 0.0f))
|
||||
{
|
||||
// Expand direction point towards the interior so flip it.
|
||||
line_expand = -line_expand;
|
||||
}
|
||||
float2 line_width = (line_expand * PointSize) / 2;
|
||||
float2x2 pos_deltas = get_xy_deltas_unscaled(vtx, other, opposite);
|
||||
|
||||
if (is_bottom)
|
||||
vtx = other;
|
||||
if (is_outside)
|
||||
{
|
||||
vtx.p.xy += line_width;
|
||||
vtx.inv_cov = 1.0f; // No coverage
|
||||
}
|
||||
else
|
||||
{
|
||||
vtx.inv_cov = 0.0f; // Full coverage
|
||||
}
|
||||
float2 expand_dir = is_outside ? get_aa1_triangle_expand_dir(vtx, other, opposite) : 0;
|
||||
|
||||
// Do actual extrapolation, or no-op if expand_dir == 0.
|
||||
extrapolate_aa1_triangle_edge(vtx, other, opposite, pos_deltas, expand_dir);
|
||||
|
||||
vtx.inv_cov = is_outside ? 1.0f : 0.0f; // No coverage on outside, otherwise full.
|
||||
|
||||
vtx.interior = 0;
|
||||
}
|
||||
else // Corner cap
|
||||
{
|
||||
// Vertex indices for this cap. We need all 3 for determining exterior/interior.
|
||||
uint prim_offset_cap = prim_offset - 21; // range: 0-8
|
||||
uint i0 = prim_offset_cap / 6;
|
||||
uint i1 = (i0 >= 2) ? i0 - 2 : i0 + 1;
|
||||
uint i2 = (i0 >= 1) ? i0 - 1 : i0 + 2;
|
||||
uint cap_offset = prim_offset_cap - 6 * i0; // range: 0-5
|
||||
|
||||
bool is_near_corner = cap_offset == 0 || cap_offset == 3;
|
||||
bool is_far_corner = cap_offset == 2 || cap_offset == 5;
|
||||
bool is_first_tri = cap_offset < 3;
|
||||
|
||||
vtx = vs_main(load_vertex(load_index(3 * prim_id + i0)));
|
||||
VS_OUTPUT other = vs_main(load_vertex(load_index(3 * prim_id + (is_first_tri ? i1 : i2))));
|
||||
VS_OUTPUT opposite = vs_main(load_vertex(load_index(3 * prim_id + (is_first_tri ? i2 : i1))));
|
||||
|
||||
float2x2 pos_deltas = get_xy_deltas_unscaled(vtx, other, opposite);
|
||||
|
||||
// Get the edge expansion directions of both incident edges.
|
||||
float2 edge_expand_dir_0 = get_aa1_triangle_expand_dir(vtx, other, opposite);
|
||||
float2 edge_expand_dir_1 = get_aa1_triangle_expand_dir(vtx, opposite, other);
|
||||
|
||||
// Check if the corner is already filled by the expanded edges.
|
||||
// This happens if the expand directions are the same.
|
||||
// If so we output a degenerate triangle at this corner.
|
||||
bool corner_filled = all(edge_expand_dir_0 == edge_expand_dir_1);
|
||||
|
||||
// Nothing if corner is filled, otherwise opposite to the bisector of the corner angle.
|
||||
float2 far_corner_dir = corner_filled ? 0 : -normalize((pos_deltas[0] + pos_deltas[1]) / 2);
|
||||
|
||||
// Determine the expand direction.
|
||||
float2 expand_dir = is_near_corner ? 0 : // No extrapolation
|
||||
is_far_corner ? far_corner_dir : // Opposite to the angle bisector of corner
|
||||
edge_expand_dir_0; // Standard AA1 edge expansion
|
||||
|
||||
// Do the actual extrapolation (no-op if expand_dir == 0).
|
||||
extrapolate_aa1_triangle_edge(vtx, other, opposite, pos_deltas, expand_dir);
|
||||
|
||||
vtx.inv_cov = is_near_corner ? 0.0f : 1.0f; // Full coverage at near corner, otherwise none.
|
||||
|
||||
vtx.interior = 0;
|
||||
|
||||
#if !VS_IIP
|
||||
|
||||
@@ -3,21 +3,12 @@
|
||||
|
||||
//#version 420 // Keep it for editor detection
|
||||
|
||||
|
||||
#ifdef VERTEX_SHADER
|
||||
|
||||
layout(location = 0) in vec2 POSITION;
|
||||
layout(location = 1) in vec2 TEXCOORD0;
|
||||
layout(location = 7) in vec4 COLOR;
|
||||
|
||||
// FIXME set the interpolation (don't know what dx do)
|
||||
// flat means that there is no interpolation. The value given to the fragment shader is based on the provoking vertex conventions.
|
||||
//
|
||||
// noperspective means that there will be linear interpolation in window-space. This is usually not what you want, but it can have its uses.
|
||||
//
|
||||
// smooth, the default, means to do perspective-correct interpolation.
|
||||
//
|
||||
// The centroid qualifier only matters when multisampling. If this qualifier is not present, then the value is interpolated to the pixel's center, anywhere in the pixel, or to one of the pixel's samples. This sample may lie outside of the actual primitive being rendered, since a primitive can cover only part of a pixel's area. The centroid qualifier is used to prevent this; the interpolation point must fall within both the pixel's area and the primitive's area.
|
||||
out vec4 PSin_p;
|
||||
out vec2 PSin_t;
|
||||
out vec4 PSin_c;
|
||||
@@ -40,31 +31,125 @@ in vec4 PSin_c;
|
||||
|
||||
layout(binding = 0) uniform sampler2D TextureSampler;
|
||||
|
||||
// Give a different name so I remember there is a special case!
|
||||
#if defined(ps_convert_rgba8_16bits) || defined(ps_convert_float32_32bits)
|
||||
layout(location = 0) out uint SV_Target1;
|
||||
#elif defined(ps_convert_float32_depth_to_color)
|
||||
layout(location = 0) out float SV_Target0;
|
||||
#if HAS_INTEGER_OUTPUT
|
||||
layout(location = 0) out uint o_col0;
|
||||
#define OUTPUT o_col0
|
||||
#elif HAS_DEPTH_OUTPUT
|
||||
out float gl_FragDepth;
|
||||
#define OUTPUT gl_FragDepth
|
||||
#elif HAS_FLOAT32_OUTPUT
|
||||
layout(location = 0) out float o_col0;
|
||||
#define OUTPUT o_col0
|
||||
#elif HAS_STENCIL_OUTPUT
|
||||
#else
|
||||
layout(location = 0) out vec4 SV_Target0;
|
||||
layout(location = 0) out vec4 o_col0;
|
||||
#define OUTPUT o_col0
|
||||
#endif
|
||||
|
||||
#if HAS_FLOAT32_INPUT
|
||||
|
||||
float sample_c()
|
||||
{
|
||||
return texture(TextureSampler, PSin_t).r;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
vec4 sample_c()
|
||||
{
|
||||
return texture(TextureSampler, PSin_t);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
uint rgba8_to_uint(vec4 c)
|
||||
{
|
||||
uvec4 i = uvec4(c * 255.5f) & 0xFFu;
|
||||
return i.r | (i.g << 8) | (i.b << 16) | (i.a << 24);
|
||||
}
|
||||
|
||||
uint rgb5a1_to_uint(vec4 c)
|
||||
{
|
||||
uvec4 i = uvec4(c * 255.5f) & uvec4(0xF8u, 0xF8u, 0xF8u, 0x80u);
|
||||
return (i.r >> 3) | (i.g << 2) | (i.b << 7) | (i.a << 8);
|
||||
}
|
||||
|
||||
uint depth_to_uint(float d)
|
||||
{
|
||||
return uint(d * exp2(32.0f));
|
||||
}
|
||||
|
||||
vec4 uint_to_rgba8(uint i)
|
||||
{
|
||||
return vec4((i & 0xFFu), ((i >> 8) & 0xFFu), ((i >> 16) & 0xFFu), ((i >> 24) & 0xFFu)) / 255.0f;
|
||||
}
|
||||
|
||||
vec4 uint_to_rgb5a1(uint i)
|
||||
{
|
||||
return vec4(uvec4(i << 3, i >> 2, i >> 7, i >> 8) & uvec4(0xF8u, 0xF8u, 0xF8u, 0x80u)) / 255.0f;
|
||||
}
|
||||
|
||||
float uint_to_depth32(uint i)
|
||||
{
|
||||
return float(i) * exp2(-32.0f);
|
||||
}
|
||||
|
||||
float uint_to_depth24(uint i)
|
||||
{
|
||||
return float(i & 0xFFFFFFu) * exp2(-32.0f);
|
||||
}
|
||||
|
||||
float uint_to_depth16(uint i)
|
||||
{
|
||||
return float(i & 0xFFFFu) * exp2(-32.0f);
|
||||
}
|
||||
|
||||
float rgba8_to_depth32(vec4 val)
|
||||
{
|
||||
return uint_to_depth32(rgba8_to_uint(val));
|
||||
}
|
||||
|
||||
float rgba8_to_depth24(vec4 val)
|
||||
{
|
||||
return uint_to_depth24(rgba8_to_uint(val));
|
||||
}
|
||||
|
||||
float rgba8_to_depth16(vec4 val)
|
||||
{
|
||||
return uint_to_depth16(rgba8_to_uint(val));
|
||||
}
|
||||
|
||||
float rgb5a1_to_depth16(vec4 val)
|
||||
{
|
||||
return uint_to_depth16(rgb5a1_to_uint(val));
|
||||
}
|
||||
|
||||
vec4 depth32_to_rgba8(float d)
|
||||
{
|
||||
return uint_to_rgba8(depth_to_uint(d));
|
||||
}
|
||||
|
||||
vec4 depth16_to_rgb5a1(float d)
|
||||
{
|
||||
return uint_to_rgb5a1(depth_to_uint(d));
|
||||
}
|
||||
|
||||
float depth32_to_depth24(float d)
|
||||
{
|
||||
return uint_to_depth24(depth_to_uint(d));
|
||||
}
|
||||
|
||||
#ifdef ps_copy
|
||||
void ps_copy()
|
||||
{
|
||||
SV_Target0 = sample_c();
|
||||
OUTPUT = sample_c();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_depth_copy
|
||||
void ps_depth_copy()
|
||||
{
|
||||
gl_FragDepth = sample_c().r;
|
||||
OUTPUT = sample_c();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -83,126 +168,47 @@ void ps_downsample_copy()
|
||||
for (int xoff = 0; xoff < DownsampleFactor; xoff++)
|
||||
result += texelFetch(TextureSampler, coord + ivec2(xoff * StepMultiplier, yoff * StepMultiplier), 0);
|
||||
}
|
||||
SV_Target0 = result / Weight;
|
||||
o_col0 = result / Weight;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_convert_rgba8_16bits
|
||||
// Need to be careful with precision here, it can break games like Spider-Man 3 and Dogs Life
|
||||
void ps_convert_rgba8_16bits()
|
||||
#ifdef ps_convert_rgb5a1_16bits
|
||||
void ps_convert_rgb5a1_16bits()
|
||||
{
|
||||
highp uvec4 i = uvec4(sample_c() * vec4(255.5f, 255.5f, 255.5f, 255.5f));
|
||||
|
||||
SV_Target1 = ((i.x & 0x00F8u) >> 3) | ((i.y & 0x00F8u) << 2) | ((i.z & 0x00f8u) << 7) | ((i.w & 0x80u) << 8);
|
||||
// Need to be careful with precision here, it can break games like Spider-Man 3 and Dogs Life
|
||||
OUTPUT = rgb5a1_to_uint(sample_c());
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_convert_float32_32bits
|
||||
void ps_convert_float32_32bits()
|
||||
#ifdef ps_convert_depth32_32bits
|
||||
void ps_convert_depth32_32bits()
|
||||
{
|
||||
// Convert a GL_FLOAT32 depth texture into a 32 bits UINT texture
|
||||
SV_Target1 = uint(exp2(32.0f) * sample_c().r);
|
||||
OUTPUT = depth_to_uint(sample_c());
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_convert_float32_rgba8
|
||||
void ps_convert_float32_rgba8()
|
||||
#ifdef ps_convert_depth32_rgba8
|
||||
void ps_convert_depth32_rgba8()
|
||||
{
|
||||
// Convert a GL_FLOAT32 depth texture into a RGBA color texture
|
||||
uint d = uint(sample_c().r * exp2(32.0f));
|
||||
SV_Target0 = vec4(uvec4((d & 0xFFu), ((d >> 8) & 0xFFu), ((d >> 16) & 0xFFu), (d >> 24))) / vec4(255.0);
|
||||
OUTPUT = depth32_to_rgba8(sample_c());
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_convert_float16_rgb5a1
|
||||
void ps_convert_float16_rgb5a1()
|
||||
#ifdef ps_convert_depth16_rgb5a1
|
||||
void ps_convert_depth16_rgb5a1()
|
||||
{
|
||||
// Convert a GL_FLOAT32 (only 16 lsb) depth into a RGB5A1 color texture
|
||||
uint d = uint(sample_c().r * exp2(32.0f));
|
||||
SV_Target0 = vec4(uvec4(d << 3, d >> 2, d >> 7, d >> 8) & uvec4(0xf8, 0xf8, 0xf8, 0x80)) / 255.0f;
|
||||
OUTPUT = depth16_to_rgb5a1(sample_c());
|
||||
}
|
||||
#endif
|
||||
|
||||
float rgba8_to_depth32(vec4 unorm)
|
||||
{
|
||||
uvec4 c = uvec4(unorm * vec4(255.5f));
|
||||
return float(c.r | (c.g << 8) | (c.b << 16) | (c.a << 24)) * exp2(-32.0f);
|
||||
}
|
||||
|
||||
float rgba8_to_depth24(vec4 unorm)
|
||||
{
|
||||
uvec3 c = uvec3(unorm.rgb * vec3(255.5f));
|
||||
return float(c.r | (c.g << 8) | (c.b << 16)) * exp2(-32.0f);
|
||||
}
|
||||
|
||||
float rgba8_to_depth16(vec4 unorm)
|
||||
{
|
||||
uvec2 c = uvec2(unorm.rg * vec2(255.5f));
|
||||
return float(c.r | (c.g << 8)) * exp2(-32.0f);
|
||||
}
|
||||
|
||||
float rgb5a1_to_depth16(vec4 unorm)
|
||||
{
|
||||
uvec4 c = uvec4(unorm * vec4(255.5f));
|
||||
return float(((c.r & 0xF8u) >> 3) | ((c.g & 0xF8u) << 2) | ((c.b & 0xF8u) << 7) | ((c.a & 0x80u) << 8)) * exp2(-32.0f);
|
||||
}
|
||||
|
||||
#ifdef ps_convert_float32_depth_to_color
|
||||
void ps_convert_float32_depth_to_color()
|
||||
{
|
||||
SV_Target0 = sample_c().r;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_convert_float32_color_to_depth
|
||||
void ps_convert_float32_color_to_depth()
|
||||
{
|
||||
gl_FragDepth = sample_c().r;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_convert_float32_float24
|
||||
void ps_convert_float32_float24()
|
||||
#ifdef ps_convert_depth32_depth24
|
||||
void ps_convert_depth32_depth24()
|
||||
{
|
||||
// Truncates depth value to 24bits
|
||||
uint d = uint(sample_c().r * exp2(32.0f)) & 0xFFFFFFu;
|
||||
gl_FragDepth = float(d) * exp2(-32.0f);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_convert_rgba8_float32
|
||||
void ps_convert_rgba8_float32()
|
||||
{
|
||||
// Convert an RGBA texture into a float depth texture
|
||||
gl_FragDepth = rgba8_to_depth32(sample_c());
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_convert_rgba8_float24
|
||||
void ps_convert_rgba8_float24()
|
||||
{
|
||||
// Same as above but without the alpha channel (24 bits Z)
|
||||
|
||||
// Convert an RGBA texture into a float depth texture
|
||||
gl_FragDepth = rgba8_to_depth24(sample_c());
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_convert_rgba8_float16
|
||||
void ps_convert_rgba8_float16()
|
||||
{
|
||||
// Same as above but without the A/B channels (16 bits Z)
|
||||
|
||||
// Convert an RGBA texture into a float depth texture
|
||||
gl_FragDepth = rgba8_to_depth16(sample_c());
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_convert_rgb5a1_float16
|
||||
void ps_convert_rgb5a1_float16()
|
||||
{
|
||||
// Convert an RGB5A1 (saved as RGBA8) color to a 16 bit Z
|
||||
gl_FragDepth = rgb5a1_to_depth16(sample_c());
|
||||
OUTPUT = depth32_to_depth24(sample_c());
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -216,41 +222,55 @@ void ps_convert_rgb5a1_float16()
|
||||
float depthTR = CONVERT_FN(texelFetch(TextureSampler, coords.zy, 0)); \
|
||||
float depthBL = CONVERT_FN(texelFetch(TextureSampler, coords.xw, 0)); \
|
||||
float depthBR = CONVERT_FN(texelFetch(TextureSampler, coords.zw, 0)); \
|
||||
gl_FragDepth = mix(mix(depthTL, depthTR, mix_vals.x), mix(depthBL, depthBR, mix_vals.x), mix_vals.y);
|
||||
OUTPUT = mix(mix(depthTL, depthTR, mix_vals.x), mix(depthBL, depthBR, mix_vals.x), mix_vals.y);
|
||||
|
||||
#ifdef ps_convert_rgba8_float32_biln
|
||||
void ps_convert_rgba8_float32_biln()
|
||||
#ifdef ps_convert_rgba8_depth32
|
||||
void ps_convert_rgba8_depth32()
|
||||
{
|
||||
// Convert an RGBA texture into a float depth texture
|
||||
#if HAS_BILN
|
||||
SAMPLE_RGBA_DEPTH_BILN(rgba8_to_depth32);
|
||||
#else
|
||||
OUTPUT = rgba8_to_depth32(sample_c());
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_convert_rgba8_float24_biln
|
||||
void ps_convert_rgba8_float24_biln()
|
||||
#ifdef ps_convert_rgba8_depth24
|
||||
void ps_convert_rgba8_depth24()
|
||||
{
|
||||
// Same as above but without the alpha channel (24 bits Z)
|
||||
|
||||
// Convert an RGBA texture into a float depth texture
|
||||
#if HAS_BILN
|
||||
SAMPLE_RGBA_DEPTH_BILN(rgba8_to_depth24);
|
||||
#else
|
||||
OUTPUT = rgba8_to_depth24(sample_c());
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_convert_rgba8_float16_biln
|
||||
void ps_convert_rgba8_float16_biln()
|
||||
#ifdef ps_convert_rgba8_depth16
|
||||
void ps_convert_rgba8_depth16()
|
||||
{
|
||||
// Same as above but without the A/B channels (16 bits Z)
|
||||
|
||||
// Convert an RGBA texture into a float depth texture
|
||||
#if HAS_BILN
|
||||
SAMPLE_RGBA_DEPTH_BILN(rgba8_to_depth16);
|
||||
#else
|
||||
OUTPUT = rgba8_to_depth16(sample_c());
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_convert_rgb5a1_float16_biln
|
||||
void ps_convert_rgb5a1_float16_biln()
|
||||
#ifdef ps_convert_rgb5a1_depth16
|
||||
void ps_convert_rgb5a1_depth16()
|
||||
{
|
||||
// Convert an RGB5A1 (saved as RGBA8) color to a 16 bit Z
|
||||
#if HAS_BILN
|
||||
SAMPLE_RGBA_DEPTH_BILN(rgb5a1_to_depth16);
|
||||
#else
|
||||
OUTPUT = rgb5a1_to_depth16(sample_c());
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -360,7 +380,7 @@ void ps_convert_rgb5a1_8i()
|
||||
uint red = (denorm_c.r >> 3) & 0x1Fu;
|
||||
uint green = (denorm_c.g >> 3) & 0x1Fu;
|
||||
|
||||
SV_Target0 = vec4(float(((green << 5) | red) & 0xFFu) / 255.0f);
|
||||
o_col0 = vec4(float(((green << 5) | red) & 0xFFu) / 255.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -368,7 +388,7 @@ void ps_convert_rgb5a1_8i()
|
||||
uint blue = (denorm_c.b >> 3) & 0x1Fu;
|
||||
uint alpha = denorm_c.a & 0x80u;
|
||||
|
||||
SV_Target0 = vec4(float((alpha | (blue << 2) | (green >> 3)) & 0xFFu) / 255.0f);
|
||||
o_col0 = vec4(float((alpha | (blue << 2) | (green >> 3)) & 0xFFu) / 255.0f);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -417,7 +437,7 @@ void ps_convert_rgba_8i()
|
||||
vec4 pixel = texelFetch(TextureSampler, ivec2(coord), 0);
|
||||
vec2 sel0 = (pos.y & 2u) == 0u ? pixel.rb : pixel.ga;
|
||||
float sel1 = (pos.x & 8u) == 0u ? sel0.x : sel0.y;
|
||||
SV_Target0 = vec4(sel1);
|
||||
o_col0 = vec4(sel1);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -425,7 +445,7 @@ void ps_convert_rgba_8i()
|
||||
void ps_filter_transparency()
|
||||
{
|
||||
vec4 c = sample_c();
|
||||
SV_Target0 = vec4(c.rgb, 1.0);
|
||||
o_col0 = vec4(c.rgb, 1.0);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -473,7 +493,7 @@ void ps_datm0_rta_correction()
|
||||
void ps_rta_correction()
|
||||
{
|
||||
vec4 value = sample_c();
|
||||
SV_Target0 = vec4(value.rgb, value.a / (128.25f / 255.0f));
|
||||
o_col0 = vec4(value.rgb, value.a / (128.25f / 255.0f));
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -481,7 +501,7 @@ void ps_rta_correction()
|
||||
void ps_rta_decorrection()
|
||||
{
|
||||
vec4 value = sample_c();
|
||||
SV_Target0 = vec4(value.rgb, value.a * (128.25f / 255.0f));
|
||||
o_col0 = vec4(value.rgb, value.a * (128.25f / 255.0f));
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -489,7 +509,7 @@ void ps_rta_decorrection()
|
||||
void ps_colclip_init()
|
||||
{
|
||||
vec4 value = sample_c();
|
||||
SV_Target0 = vec4(round(value.rgb * 255.0f) / 65535.0f, value.a);
|
||||
o_col0 = vec4(round(value.rgb * 255.0f) / 65535.0f, value.a);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -497,7 +517,7 @@ void ps_colclip_init()
|
||||
void ps_colclip_resolve()
|
||||
{
|
||||
vec4 value = sample_c();
|
||||
SV_Target0 = vec4(vec3(uvec3(value.rgb * 65535.0f) & 255u) / 255.0f, value.a);
|
||||
o_col0 = vec4(vec3(uvec3(value.rgb * 65535.0f) & 255u) / 255.0f, value.a);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -512,7 +532,7 @@ void ps_convert_clut_4()
|
||||
uvec2 pos = uvec2(index % 8u, index / 8u);
|
||||
|
||||
ivec2 final = ivec2(floor(vec2(offset.xy + pos) * vec2(scale)));
|
||||
SV_Target0 = texelFetch(TextureSampler, final, 0);
|
||||
o_col0 = texelFetch(TextureSampler, final, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -532,7 +552,7 @@ void ps_convert_clut_8()
|
||||
pos.y = ((index / 32u) * 2u) + (subgroup % 2u);
|
||||
|
||||
ivec2 final = ivec2(floor(vec2(offset.xy + pos) * vec2(scale)));
|
||||
SV_Target0 = texelFetch(TextureSampler, final, 0);
|
||||
o_col0 = texelFetch(TextureSampler, final, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -587,31 +607,31 @@ void ps_yuv()
|
||||
break;
|
||||
}
|
||||
|
||||
SV_Target0 = o;
|
||||
o_col0 = o;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(ps_stencil_image_init_0) || defined(ps_stencil_image_init_1) || defined(ps_stencil_image_init_2) || defined(ps_stencil_image_init_3)
|
||||
#if defined(ps_primid_image_init_0) || defined(ps_primid_image_init_1) || defined(ps_primid_image_init_2) || defined(ps_primid_image_init_3)
|
||||
|
||||
void main()
|
||||
{
|
||||
SV_Target0 = vec4(0x7FFFFFFF);
|
||||
o_col0 = vec4(0x7FFFFFFF);
|
||||
|
||||
#ifdef ps_stencil_image_init_0
|
||||
#ifdef ps_primid_image_init_0
|
||||
if((127.5f / 255.0f) < sample_c().a) // < 0x80 pass (== 0x80 should not pass)
|
||||
SV_Target0 = vec4(-1);
|
||||
o_col0 = vec4(-1);
|
||||
#endif
|
||||
#ifdef ps_stencil_image_init_1
|
||||
#ifdef ps_primid_image_init_1
|
||||
if(sample_c().a < (127.5f / 255.0f)) // >= 0x80 pass
|
||||
SV_Target0 = vec4(-1);
|
||||
o_col0 = vec4(-1);
|
||||
#endif
|
||||
#ifdef ps_stencil_image_init_2
|
||||
#ifdef ps_primid_image_init_2
|
||||
if((254.5f / 255.0f) < sample_c().a) // < 0x80 pass (== 0x80 should not pass)
|
||||
SV_Target0 = vec4(-1);
|
||||
o_col0 = vec4(-1);
|
||||
#endif
|
||||
#ifdef ps_stencil_image_init_3
|
||||
#ifdef ps_primid_image_init_3
|
||||
if(sample_c().a < (254.5f / 255.0f)) // >= 0x80 pass
|
||||
SV_Target0 = vec4(-1);
|
||||
o_col0 = vec4(-1);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -54,12 +54,12 @@
|
||||
#define SW_AD_TO_HW (PS_BLEND_C == 1 && PS_A_MASKED)
|
||||
#define PS_PRIMID_INIT (PS_DATE == 1 || PS_DATE == 2)
|
||||
#define NEEDS_RT_EARLY (PS_TEX_IS_FB == 1 || PS_DATE >= 5)
|
||||
#define NEEDS_RT_FOR_AFAIL (PS_AFAIL == PS_ZB_ONLY || PS_AFAIL == AFAIL_RGB_ONLY || PS_AFAIL == AFAIL_RGB_ONLY_SW_Z)
|
||||
#define NEEDS_RT_FOR_AFAIL (PS_AFAIL == AFAIL_ZB_ONLY || PS_AFAIL == AFAIL_RGB_ONLY || PS_AFAIL == AFAIL_RGB_ONLY_SW_Z)
|
||||
#define NEEDS_DEPTH_FOR_AFAIL (PS_AFAIL == AFAIL_FB_ONLY || PS_AFAIL == AFAIL_RGB_ONLY_SW_Z)
|
||||
#define NEEDS_DEPTH_FOR_ZTST (PS_ZTST == ZTST_GEQUAL || PS_ZTST == ZTST_GREATER)
|
||||
#define NEEDS_DEPTH_FOR_AA1 (PS_AA1 == PS_AA1_TRIANGLE_SW_Z)
|
||||
|
||||
#define NEEDS_RT (NEEDS_RT_EARLY || NEEDS_RT_FOR_AFAIL || (!PS_PRIMID_INIT && (PS_FBMASK || SW_BLEND_NEEDS_RT || SW_AD_TO_HW)) || PS_COLOR_FEEDBACK)
|
||||
#define NEEDS_RT (NEEDS_RT_EARLY || NEEDS_RT_FOR_AFAIL || (!PS_PRIMID_INIT && (PS_FBMASK || SW_BLEND_NEEDS_RT || SW_AD_TO_HW)))
|
||||
#define NEEDS_TEX (PS_TFX != 4)
|
||||
#define SW_DEPTH (NEEDS_DEPTH_FOR_AFAIL || NEEDS_DEPTH_FOR_ZTST || NEEDS_DEPTH_FOR_AA1)
|
||||
#define ZWRITE (SW_DEPTH || PS_ZCLAMP || PS_ZFLOOR)
|
||||
@@ -121,7 +121,7 @@ in SHADER
|
||||
#if defined(GL_EXT_shader_framebuffer_fetch)
|
||||
#undef TARGET_0_QUALIFIER
|
||||
#define TARGET_0_QUALIFIER inout
|
||||
#define LAST_FRAG_COLOR SV_Target0
|
||||
#define LAST_FRAG_COLOR o_col0
|
||||
#elif defined(GL_ARM_shader_framebuffer_fetch)
|
||||
#define LAST_FRAG_COLOR gl_LastFragColorARM
|
||||
#endif
|
||||
@@ -129,20 +129,20 @@ in SHADER
|
||||
|
||||
#if !PS_NO_COLOR && !PS_NO_COLOR1
|
||||
// Same buffer but 2 colors for dual source blending
|
||||
layout(location = 0, index = 0) TARGET_0_QUALIFIER vec4 SV_Target0;
|
||||
layout(location = 0, index = 1) out vec4 SV_Target1;
|
||||
layout(location = 0, index = 0) TARGET_0_QUALIFIER vec4 o_col0;
|
||||
layout(location = 0, index = 1) out vec4 o_col1;
|
||||
#elif !PS_NO_COLOR
|
||||
layout(location = 0) TARGET_0_QUALIFIER vec4 SV_Target0;
|
||||
layout(location = 0) TARGET_0_QUALIFIER vec4 o_col0;
|
||||
#endif
|
||||
|
||||
// Depth feedback mode 2 is for depth as color.
|
||||
// Use FB fetch for the feedback if it's available.
|
||||
#if SW_DEPTH && PS_NO_COLOR1 && (DEPTH_FEEDBACK_SUPPORT == 2)
|
||||
#if HAS_FRAMEBUFFER_FETCH
|
||||
layout(location = 1) inout float SV_Target1;
|
||||
#else
|
||||
layout(location = 1) out float SV_Target1;
|
||||
#endif
|
||||
#if HAS_FRAMEBUFFER_FETCH
|
||||
layout(location = 1) inout float o_col1;
|
||||
#else
|
||||
layout(location = 1) out float o_col1;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if NEEDS_TEX
|
||||
@@ -180,14 +180,14 @@ vec4 sample_from_rt()
|
||||
#endif
|
||||
}
|
||||
|
||||
vec4 sample_from_depth()
|
||||
float sample_from_depth()
|
||||
{
|
||||
#if !SW_DEPTH
|
||||
return vec4(0.0);
|
||||
return 0.0f;
|
||||
#elif HAS_FRAMEBUFFER_FETCH && (DEPTH_FEEDBACK_SUPPORT == 2)
|
||||
return SV_Target1;
|
||||
return o_col1;
|
||||
#else
|
||||
return texelFetch(DepthSampler, ivec2(gl_FragCoord.xy), 0);
|
||||
return texelFetch(DepthSampler, ivec2(gl_FragCoord.xy), 0).r;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -621,13 +621,13 @@ vec4 sample_depth(vec2 st)
|
||||
|
||||
|
||||
#elif PS_DEPTH_FMT == 1
|
||||
// Based on ps_convert_float32_rgba8 of convert
|
||||
// Based on ps_convert_depth32_rgba8 of convert
|
||||
// Convert a GL_FLOAT32 depth texture into a RGBA color texture
|
||||
uint d = uint(fetch_c(uv).r * exp2(32.0f));
|
||||
t = vec4(uvec4((d & 0xFFu), ((d >> 8) & 0xFFu), ((d >> 16) & 0xFFu), (d >> 24)));
|
||||
|
||||
#elif PS_DEPTH_FMT == 2
|
||||
// Based on ps_convert_float16_rgb5a1 of convert
|
||||
// Based on ps_convert_depth16_rgb5a1 of convert
|
||||
// Convert a GL_FLOAT32 (only 16 lsb) depth into a RGB5A1 color texture
|
||||
uint d = uint(fetch_c(uv).r * exp2(32.0f));
|
||||
t = vec4(uvec4((d & 0x1Fu), ((d >> 5) & 0x1Fu), ((d >> 10) & 0x1Fu), (d >> 15) & 0x01u)) * vec4(8.0f, 8.0f, 8.0f, 128.0f);
|
||||
@@ -1193,10 +1193,10 @@ void ps_main()
|
||||
#endif
|
||||
|
||||
#if PS_ZTST == ZTST_GEQUAL
|
||||
if (input_z < sample_from_depth().r)
|
||||
if (input_z < sample_from_depth())
|
||||
discard;
|
||||
#elif PS_ZTST == ZTST_GREATER
|
||||
if (input_z <= sample_from_depth().r)
|
||||
if (input_z <= sample_from_depth())
|
||||
discard;
|
||||
#endif
|
||||
|
||||
@@ -1264,7 +1264,7 @@ void ps_main()
|
||||
|
||||
bool atst_pass = atst(C);
|
||||
|
||||
#if PS_AFAIL == AFAIL_KEEP
|
||||
#if PS_ATST != PS_ATST_NONE && PS_AFAIL == AFAIL_KEEP
|
||||
if (!atst_pass)
|
||||
discard;
|
||||
#endif
|
||||
@@ -1293,12 +1293,12 @@ void ps_main()
|
||||
#if PS_DATE == 1
|
||||
// DATM == 0
|
||||
// Pixel with alpha equal to 1 will failed (128-255)
|
||||
SV_Target0 = (C.a > 127.5f) ? vec4(gl_PrimitiveID) : vec4(0x7FFFFFFF);
|
||||
o_col0 = (C.a > 127.5f) ? vec4(gl_PrimitiveID) : vec4(0x7FFFFFFF);
|
||||
return;
|
||||
#elif PS_DATE == 2
|
||||
// DATM == 1
|
||||
// Pixel with alpha equal to 0 will failed (0-127)
|
||||
SV_Target0 = (C.a < 127.5f) ? vec4(gl_PrimitiveID) : vec4(0x7FFFFFFF);
|
||||
o_col0 = (C.a < 127.5f) ? vec4(gl_PrimitiveID) : vec4(0x7FFFFFFF);
|
||||
return;
|
||||
#endif
|
||||
|
||||
@@ -1372,7 +1372,7 @@ void ps_main()
|
||||
// Alpha test with feedback
|
||||
#if PS_AFAIL == AFAIL_FB_ONLY
|
||||
if (!atst_pass)
|
||||
input_z = sample_from_depth().r;
|
||||
input_z = sample_from_depth();
|
||||
#elif PS_AFAIL == AFAIL_ZB_ONLY
|
||||
if (!atst_pass)
|
||||
C = sample_from_rt();
|
||||
@@ -1381,17 +1381,17 @@ void ps_main()
|
||||
{
|
||||
C.a = sample_from_rt().a;
|
||||
#if PS_AFAIL == AFAIL_RGB_ONLY_SW_Z
|
||||
input_z = sample_from_depth().r;
|
||||
input_z = sample_from_depth();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
// Warning: do not write SV_Target0 until the end since the value might be needed for
|
||||
// Warning: do not write o_col0 until the end since the value might be needed for
|
||||
// FB fetch in sample_from_rt().
|
||||
SV_Target0 = C;
|
||||
o_col0 = C;
|
||||
|
||||
#if !PS_NO_COLOR1
|
||||
SV_Target1 = alpha_blend;
|
||||
o_col1 = alpha_blend;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -1401,16 +1401,17 @@ void ps_main()
|
||||
|
||||
#if PS_AA1 == PS_AA1_TRIANGLE_SW_Z
|
||||
if (!bool(PSin.interior))
|
||||
input_z = sample_from_depth().r; // No depth update for triangle edges.
|
||||
input_z = sample_from_depth(); // No depth update for triangle edges.
|
||||
#endif
|
||||
|
||||
// Writing back depth
|
||||
#if ZWRITE
|
||||
#if SW_DEPTH && PS_NO_COLOR1 && (DEPTH_FEEDBACK_SUPPORT == 2)
|
||||
// Depth as color write. For depth as color feedback we write to both
|
||||
// color copy and real depth to avoid having to copy back to real depth.
|
||||
// Warning: do not write SV_Target1 until the end since the value might
|
||||
// Warning: do not write o_col1 until the end since the value might
|
||||
// be needed for FB fetch in sample_from_depth().
|
||||
SV_Target1 = input_z;
|
||||
o_col1 = input_z;
|
||||
#endif
|
||||
// Standard depth write.
|
||||
gl_FragDepth = input_z;
|
||||
|
||||
@@ -83,7 +83,13 @@ void vs_main()
|
||||
// example: 133.0625 (133 + 1/16) should start from line 134, ceil(133.0625 - 0.05) still above 133
|
||||
gl_Position.xy = vec2(i_p) - vec2(0.05f, 0.05f);
|
||||
gl_Position.xy = gl_Position.xy * VertexScale - VertexOffset;
|
||||
gl_Position.z = float(z) * exp_min32;
|
||||
|
||||
#if HAS_CLIP_CONTROL
|
||||
gl_Position.z = float(z) * exp_min32;
|
||||
#else
|
||||
gl_Position.z = (float(z) * exp_min32) * 2.0f - 1.0f;
|
||||
#endif
|
||||
|
||||
gl_Position.w = 1.0f;
|
||||
|
||||
texture_coord();
|
||||
@@ -160,7 +166,13 @@ ProcessedVertex load_vertex(uint index)
|
||||
uint z = min(i_z, MaxDepth);
|
||||
vtx.p.xy = vec2(i_p) - vec2(0.05f, 0.05f);
|
||||
vtx.p.xy = vtx.p.xy * VertexScale - VertexOffset;
|
||||
vtx.p.z = float(z) * exp_min32;
|
||||
|
||||
#if HAS_CLIP_CONTROL
|
||||
vtx.p.z = float(z) * exp_min32;
|
||||
#else
|
||||
vtx.p.z = (float(z) * exp_min32) * 2.0f - 1.0f;
|
||||
#endif
|
||||
|
||||
vtx.p.w = 1.0f;
|
||||
|
||||
vec2 uv = vec2(i_uv) - TextureOffset;
|
||||
@@ -182,6 +194,110 @@ ProcessedVertex load_vertex(uint index)
|
||||
return vtx;
|
||||
}
|
||||
|
||||
// Convert XY from NDC to GS pixel coordinates (i.e. 1.0 = 1 GS pixel).
|
||||
vec2 get_xy_unscaled(vec2 xy)
|
||||
{
|
||||
return round(xy / VertexScale) / 16.0f;
|
||||
}
|
||||
|
||||
// Get the XY deltas in GS pixel coordinates, using first vertex as the origin.
|
||||
mat2 get_xy_deltas_unscaled(ProcessedVertex v0, ProcessedVertex v1, ProcessedVertex v2)
|
||||
{
|
||||
vec2 xy0 = get_xy_unscaled(v0.p.xy);
|
||||
vec2 xy1 = get_xy_unscaled(v1.p.xy);
|
||||
vec2 xy2 = get_xy_unscaled(v2.p.xy);
|
||||
return mat2(xy1 - xy0, xy2 - xy0);
|
||||
}
|
||||
|
||||
// Get the AA1 outward expand direction to the edge formed by the first two vertices.
|
||||
// This is up or down for shallow (X dominant) edges, and right or left for steep (Y dominant) edges.
|
||||
// Similar expansion to line AA1 except instead of expanding on both sides of the line,
|
||||
// expand on on the side towards the outside of the triangle.
|
||||
vec2 get_aa1_triangle_expand_dir(ProcessedVertex v0, ProcessedVertex v1, ProcessedVertex v2)
|
||||
{
|
||||
mat2 xy_deltas = get_xy_deltas_unscaled(v0, v1, v2);
|
||||
vec2 line_delta = xy_deltas[0];
|
||||
vec2 line_opposite = xy_deltas[1];
|
||||
|
||||
vec2 line_normal = vec2(line_delta.y, -line_delta.x);
|
||||
vec2 line_expand = abs(line_delta.x) >= abs(line_delta.y) ? vec2(0.0f, 1.0f) : vec2(1.0f, 0.0f);
|
||||
|
||||
if ((dot(line_expand, line_normal) >= 0.0f) == (dot(line_opposite, line_normal) >= 0.0f))
|
||||
{
|
||||
// Expand direction point towards the interior so flip it.
|
||||
line_expand = -line_expand;
|
||||
}
|
||||
|
||||
return line_expand;
|
||||
}
|
||||
|
||||
mat2 get_inverse(mat2 mat, float det)
|
||||
{
|
||||
return mat2(mat[1][1], -mat[0][1], -mat[1][0], mat[0][0]) * (1 / det);
|
||||
}
|
||||
|
||||
// Extrapolate triangle attributes from the first vertex along the given direction.
|
||||
// dp_mat is derived from the input vertices, it is passed in to avoid recomputing.
|
||||
void extrapolate_aa1_triangle_edge(inout ProcessedVertex v0, ProcessedVertex v1, ProcessedVertex v2, mat2 dp_mat, vec2 dp)
|
||||
{
|
||||
// Get texture deltas
|
||||
#if VS_TME
|
||||
#if VS_FST
|
||||
mat2 dt = mat2(v1.t_int.zw - v0.t_int.zw, v2.t_int.zw - v0.t_int.zw);
|
||||
#else
|
||||
mat2 dt = mat2(v1.t_float.xy - v0.t_float.xy, v2.t_float.xy - v0.t_float.xy);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Get color delta if interpolating
|
||||
#if VS_IIP
|
||||
mat2x4 dc = mat2x4(v1.c - v0.c, v2.c - v0.c);
|
||||
#endif
|
||||
|
||||
vec2 dz = vec2(v1.p.z - v0.p.z, v2.p.z - v0.p.z); // Z deltas
|
||||
|
||||
vec2 df = vec2(v1.t_float.z - v0.t_float.z, v2.t_float.z - v0.t_float.z); // Fog deltas
|
||||
|
||||
vec2 dq = vec2(v1.t_float.w - v0.t_float.w, v2.t_float.w - v0.t_float.w); // Q deltas
|
||||
|
||||
// To prevent unstable extrapolation, do not extrapolate if the
|
||||
// minimum perpendicular length of the triangle is < 2 pixels.
|
||||
float dp_det = determinant(dp_mat); // Twice signed triangle area.
|
||||
float len0 = length(dp_mat[0]);
|
||||
float len1 = length(dp_mat[1]);
|
||||
float len2 = length(dp_mat[1] - dp_mat[0]);
|
||||
float min_perp_length = abs(dp_det) / max(max(len0, len1), len2);
|
||||
|
||||
// Get the position -> barycentric weight matrix
|
||||
mat2 inv_dp_mat = get_inverse(dp_mat, dp_det);
|
||||
|
||||
vec2 weights = min_perp_length < 2 ? vec2(0) : inv_dp_mat * dp;
|
||||
|
||||
v0.p.xy += dp * PointSize; // Extrapolate position
|
||||
|
||||
// Extrapolate texture coords
|
||||
#if VS_TME
|
||||
#if VS_FST
|
||||
v0.t_int.zw += dt * weights;
|
||||
v0.t_int.xy = v0.t_int.zw * TextureScale;
|
||||
#else
|
||||
v0.t_float.xy += dt * weights;
|
||||
v0.t_int.zw = v0.t_float.xy / TextureScale;
|
||||
v0.t_float.w += dot(dq, weights);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Extrapolate and clamp color
|
||||
#if VS_IIP
|
||||
v0.c += dc * weights;
|
||||
v0.c = clamp(v0.c, vec4(0), vec4(255));
|
||||
#endif
|
||||
|
||||
v0.p.z += dot(dz, weights); // Extrapolate depth
|
||||
|
||||
v0.t_float.z += dot(df, weights); // Extrapolate fog
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
ProcessedVertex vtx;
|
||||
@@ -206,13 +322,12 @@ 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);
|
||||
vec2 line_vector = normalize(line_delta / VertexScale);
|
||||
#if VS_EXPAND == VS_EXPAND_LINE
|
||||
vec2 line_expand = vec2(line_vector.y, -line_vector.x);
|
||||
#elif VS_EXPAND == VS_EXPAND_LINE_AA1
|
||||
// Expand in y direction for shallow lines and x direction for steep lines.
|
||||
line_delta /= VertexScale;
|
||||
vec2 line_expand = abs(line_delta.x) >= abs(line_delta.y) ? vec2(0.0f, 2.0f) : vec2(2.0f, 0.0f);
|
||||
vec2 line_expand = abs(line_vector.x) >= abs(line_vector.y) ? vec2(0.0f, 2.0f) : vec2(2.0f, 0.0f);
|
||||
#endif
|
||||
vec2 line_width = (line_expand * PointSize) / 2;
|
||||
vec2 offset = is_right ? line_width : -line_width;
|
||||
@@ -254,10 +369,14 @@ void main()
|
||||
// - Vertices 3-8: First edge expanded (2 triangles).
|
||||
// - Vertices 9-14: Second edge expanded (2 triangles).
|
||||
// - Vertices 15-20: Third edge expanded (2 triangles).
|
||||
// - Vertices 21-26: First corner cap (2 triangles).
|
||||
// - Vertices 27-32: Second corner cap (2 triangles).
|
||||
// - Vertices 33-38: Third corner cap (2 triangles).
|
||||
|
||||
uint prim_id = vid / 21;
|
||||
uint prim_offset = vid - 21 * prim_id; // range: 0-20
|
||||
uint prim_id = vid / 39;
|
||||
uint prim_offset = vid - 39 * prim_id; // range: 0-38
|
||||
bool interior = prim_offset < 3;
|
||||
bool edge = 3 <= prim_offset && prim_offset < 21;
|
||||
|
||||
if (interior)
|
||||
{
|
||||
@@ -265,7 +384,7 @@ void main()
|
||||
VSout.inv_cov = 0.0f; // Full coverage
|
||||
VSout.interior = 1;
|
||||
}
|
||||
else
|
||||
else if (edge)
|
||||
{
|
||||
// Vertex indices for this edge. We need all 3 for determining exterior/interior.
|
||||
uint prim_offset_edges = prim_offset - 3; // range: 0-17
|
||||
@@ -274,39 +393,68 @@ void main()
|
||||
uint i2 = (i0 >= 1) ? i0 - 1 : i0 + 2;
|
||||
uint edge_offset = prim_offset_edges - 6 * i0; // range: 0-5
|
||||
|
||||
// Note: order of top/bottom, inside/outside order is arbitrary,
|
||||
// Note: order of top/bottom, inside/outside is arbitrary,
|
||||
// as long as it assembles into two triangles forming a quad.
|
||||
bool is_bottom = (2 <= edge_offset) && (edge_offset <= 4);
|
||||
bool is_outside = (edge_offset & 1) != 0;
|
||||
bool is_outside = (edge_offset & 1u) != 0;
|
||||
|
||||
vtx = load_vertex(load_index(3 * prim_id + i0));
|
||||
ProcessedVertex other = load_vertex(load_index(3 * prim_id + i1));
|
||||
vtx = load_vertex(load_index(3 * prim_id + (is_bottom ? i1 : i0)));
|
||||
ProcessedVertex other = load_vertex(load_index(3 * prim_id + (is_bottom ? i0 : i1)));
|
||||
ProcessedVertex opposite = load_vertex(load_index(3 * prim_id + i2));
|
||||
|
||||
// Similar expansion to line AA1 except instead of expanding on both sides of
|
||||
// the line we expand on on the side towards the outside of the triangle.
|
||||
vec2 line_delta = vtx.p.xy - other.p.xy;
|
||||
vec2 line_normal = normalize(vec2(line_delta.y, -line_delta.x));
|
||||
vec2 line_expand = abs(line_delta.x) >= abs(line_delta.y) ? vec2(0.0f, 2.0f) : vec2(2.0f, 0.0f);
|
||||
if ((dot(line_expand, line_normal) >= 0.0f) == (dot(opposite.p.xy - vtx.p.xy, line_normal) >= 0.0f))
|
||||
{
|
||||
// Expand direction point towards the interior so flip it.
|
||||
line_expand = -line_expand;
|
||||
}
|
||||
vec2 line_width = (line_expand * PointSize) / 2;
|
||||
mat2 pos_deltas = get_xy_deltas_unscaled(vtx, other, opposite);
|
||||
|
||||
if (is_bottom)
|
||||
vtx = other;
|
||||
if (is_outside)
|
||||
{
|
||||
vtx.p.xy += line_width;
|
||||
VSout.inv_cov = 1.0f; // No coverage
|
||||
}
|
||||
else
|
||||
{
|
||||
VSout.inv_cov = 0.0f; // Full coverage
|
||||
}
|
||||
vec2 expand_dir = is_outside ? get_aa1_triangle_expand_dir(vtx, other, opposite) : vec2(0);
|
||||
|
||||
// Do actual extrapolation, or no-op if expand_dir == 0.
|
||||
extrapolate_aa1_triangle_edge(vtx, other, opposite, pos_deltas, expand_dir);
|
||||
|
||||
VSout.inv_cov = is_outside ? 1.0f : 0.0f; // No coverage on outside, otherwise full.
|
||||
|
||||
VSout.interior = 0;
|
||||
}
|
||||
else // Corner cap
|
||||
{
|
||||
// Vertex indices for this cap. We need all 3 for determining exterior/interior.
|
||||
uint prim_offset_cap = prim_offset - 21; // range: 0-8
|
||||
uint i0 = prim_offset_cap / 6;
|
||||
uint i1 = (i0 >= 2) ? i0 - 2 : i0 + 1;
|
||||
uint i2 = (i0 >= 1) ? i0 - 1 : i0 + 2;
|
||||
uint cap_offset = prim_offset_cap - 6 * i0; // range: 0-5
|
||||
|
||||
bool is_near_corner = cap_offset == 0 || cap_offset == 3;
|
||||
bool is_far_corner = cap_offset == 2 || cap_offset == 5;
|
||||
bool is_first_tri = cap_offset < 3;
|
||||
|
||||
// First triangle is on the side of vertex i1 and second is on the side of vertex i2.
|
||||
vtx = load_vertex(load_index(3 * prim_id + i0));
|
||||
ProcessedVertex other = load_vertex(load_index(3 * prim_id + (is_first_tri ? i1 : i2)));
|
||||
ProcessedVertex opposite = load_vertex(load_index(3 * prim_id + (is_first_tri ? i2 : i1)));
|
||||
|
||||
mat2 pos_deltas = get_xy_deltas_unscaled(vtx, other, opposite);
|
||||
|
||||
// Get the edge expansion directions of both incident edges.
|
||||
vec2 edge_expand_dir_0 = get_aa1_triangle_expand_dir(vtx, other, opposite);
|
||||
vec2 edge_expand_dir_1 = get_aa1_triangle_expand_dir(vtx, opposite, other);
|
||||
|
||||
// Check if the corner is already filled by the expanded edges.
|
||||
// This happens if the expand directions are the same.
|
||||
// If so we output a degenerate triangle at this corner.
|
||||
bool corner_filled = all(equal(edge_expand_dir_0, edge_expand_dir_1));
|
||||
|
||||
// Nothing if corner is filled, otherwise opposite to the bisector of the corner angle.
|
||||
vec2 far_corner_dir = corner_filled ? vec2(0) : -normalize((pos_deltas[0] + pos_deltas[1]) / 2);
|
||||
|
||||
// Determine the expand direction.
|
||||
vec2 expand_dir = is_near_corner ? vec2(0) : // No extrapolation
|
||||
is_far_corner ? far_corner_dir : // Opposite to the angle bisector of corner
|
||||
edge_expand_dir_0; // Standard AA1 edge expansion
|
||||
|
||||
// Do the actual extrapolation (no-op if expand_dir == 0).
|
||||
extrapolate_aa1_triangle_edge(vtx, other, opposite, pos_deltas, expand_dir);
|
||||
|
||||
VSout.inv_cov = is_near_corner ? 0.0f : 1.0f; // Full coverage at near corner, otherwise none.
|
||||
|
||||
VSout.interior = 0;
|
||||
|
||||
#if !VS_IIP
|
||||
|
||||
@@ -20,44 +20,127 @@ void main()
|
||||
|
||||
layout(location = 0) in vec2 v_tex;
|
||||
|
||||
#if defined(ps_convert_rgba8_16bits) || defined(ps_convert_float32_32bits)
|
||||
layout(location = 0) out uint o_col0;
|
||||
#elif defined(ps_convert_float32_depth_to_color)
|
||||
layout(location = 0) out float o_col0;
|
||||
#elif !defined(ps_datm1) && \
|
||||
!defined(ps_datm0) && \
|
||||
!defined(ps_datm1_rta_correction) && \
|
||||
!defined(ps_datm0_rta_correction) && \
|
||||
!defined(ps_convert_rgba8_float32) && \
|
||||
!defined(ps_convert_rgba8_float24) && \
|
||||
!defined(ps_convert_rgba8_float16) && \
|
||||
!defined(ps_convert_rgb5a1_float16) && \
|
||||
!defined(ps_convert_rgba8_float32_biln) && \
|
||||
!defined(ps_convert_rgba8_float24_biln) && \
|
||||
!defined(ps_convert_rgba8_float16_biln) && \
|
||||
!defined(ps_convert_rgb5a1_float16_biln) && \
|
||||
!defined(ps_depth_copy)
|
||||
layout(location = 0) out vec4 o_col0;
|
||||
#if HAS_INTEGER_OUTPUT
|
||||
layout(location = 0) out uint o_col0;
|
||||
#define OUTPUT o_col0
|
||||
#elif HAS_DEPTH_OUTPUT
|
||||
out float gl_FragDepth;
|
||||
#define OUTPUT gl_FragDepth
|
||||
#elif HAS_FLOAT32_OUTPUT
|
||||
layout(location = 0) out float o_col0;
|
||||
#define OUTPUT o_col0
|
||||
#elif HAS_STENCIL_OUTPUT
|
||||
#else
|
||||
layout(location = 0) out vec4 o_col0;
|
||||
#define OUTPUT o_col0
|
||||
#endif
|
||||
|
||||
layout(set = 0, binding = 0) uniform sampler2D samp0;
|
||||
|
||||
#if HAS_FLOAT32_INPUT
|
||||
|
||||
float sample_c(vec2 uv)
|
||||
{
|
||||
return texture(samp0, uv).r;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
vec4 sample_c(vec2 uv)
|
||||
{
|
||||
return texture(samp0, uv);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
uint rgba8_to_uint(vec4 c)
|
||||
{
|
||||
uvec4 i = uvec4(c * 255.5f) & 0xFFu;
|
||||
return i.r | (i.g << 8) | (i.b << 16) | (i.a << 24);
|
||||
}
|
||||
|
||||
uint rgb5a1_to_uint(vec4 c)
|
||||
{
|
||||
uvec4 i = uvec4(c * 255.5f) & uvec4(0xF8u, 0xF8u, 0xF8u, 0x80u);
|
||||
return (i.r >> 3) | (i.g << 2) | (i.b << 7) | (i.a << 8);
|
||||
}
|
||||
|
||||
uint depth_to_uint(float d)
|
||||
{
|
||||
return uint(d * exp2(32.0f));
|
||||
}
|
||||
|
||||
vec4 uint_to_rgba8(uint i)
|
||||
{
|
||||
return vec4((i & 0xFFu), ((i >> 8) & 0xFFu), ((i >> 16) & 0xFFu), ((i >> 24) & 0xFFu)) / 255.0f;
|
||||
}
|
||||
|
||||
vec4 uint_to_rgb5a1(uint i)
|
||||
{
|
||||
return vec4(uvec4(i << 3, i >> 2, i >> 7, i >> 8) & uvec4(0xF8u, 0xF8u, 0xF8u, 0x80u)) / 255.0f;
|
||||
}
|
||||
|
||||
float uint_to_depth32(uint i)
|
||||
{
|
||||
return float(i) * exp2(-32.0f);
|
||||
}
|
||||
|
||||
float uint_to_depth24(uint i)
|
||||
{
|
||||
return float(i & 0xFFFFFFu) * exp2(-32.0f);
|
||||
}
|
||||
|
||||
float uint_to_depth16(uint i)
|
||||
{
|
||||
return float(i & 0xFFFFu) * exp2(-32.0f);
|
||||
}
|
||||
|
||||
float rgba8_to_depth32(vec4 val)
|
||||
{
|
||||
return uint_to_depth32(rgba8_to_uint(val));
|
||||
}
|
||||
|
||||
float rgba8_to_depth24(vec4 val)
|
||||
{
|
||||
return uint_to_depth24(rgba8_to_uint(val));
|
||||
}
|
||||
|
||||
float rgba8_to_depth16(vec4 val)
|
||||
{
|
||||
return uint_to_depth16(rgba8_to_uint(val));
|
||||
}
|
||||
|
||||
float rgb5a1_to_depth16(vec4 val)
|
||||
{
|
||||
return uint_to_depth16(rgb5a1_to_uint(val));
|
||||
}
|
||||
|
||||
vec4 depth32_to_rgba8(float d)
|
||||
{
|
||||
return uint_to_rgba8(depth_to_uint(d));
|
||||
}
|
||||
|
||||
vec4 depth16_to_rgb5a1(float d)
|
||||
{
|
||||
return uint_to_rgb5a1(depth_to_uint(d));
|
||||
}
|
||||
|
||||
float depth32_to_depth24(float d)
|
||||
{
|
||||
return uint_to_depth24(depth_to_uint(d));
|
||||
}
|
||||
|
||||
#ifdef ps_copy
|
||||
void ps_copy()
|
||||
{
|
||||
o_col0 = sample_c(v_tex);
|
||||
OUTPUT = sample_c(v_tex);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_depth_copy
|
||||
void ps_depth_copy()
|
||||
{
|
||||
gl_FragDepth = sample_c(v_tex).r;
|
||||
OUTPUT = sample_c(v_tex);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -82,7 +165,7 @@ void ps_downsample_copy()
|
||||
result += texelFetch(samp0, coord + ivec2(xoff * step_multiplier, yoff * step_multiplier), 0);
|
||||
}
|
||||
}
|
||||
o_col0 = result / Weight;
|
||||
OUTPUT = result / Weight;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -90,17 +173,15 @@ void ps_downsample_copy()
|
||||
void ps_filter_transparency()
|
||||
{
|
||||
vec4 c = sample_c(v_tex);
|
||||
o_col0 = vec4(c.rgb, 1.0);
|
||||
OUTPUT = vec4(c.rgb, 1.0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_convert_rgba8_16bits
|
||||
// Need to be careful with precision here, it can break games like Spider-Man 3 and Dogs Life
|
||||
void ps_convert_rgba8_16bits()
|
||||
#ifdef ps_convert_rgb5a1_16bits
|
||||
void ps_convert_rgb5a1_16bits()
|
||||
{
|
||||
uvec4 i = uvec4(sample_c(v_tex) * vec4(255.5f, 255.5f, 255.5f, 255.5f));
|
||||
|
||||
o_col0 = ((i.x & 0x00F8u) >> 3) | ((i.y & 0x00F8u) << 2) | ((i.z & 0x00f8u) << 7) | ((i.w & 0x80u) << 8);
|
||||
// Need to be careful with precision here, it can break games like Spider-Man 3 and Dogs Life
|
||||
OUTPUT = rgb5a1_to_uint(sample_c(v_tex));
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -140,7 +221,7 @@ void ps_datm0_rta_correction()
|
||||
void ps_rta_correction()
|
||||
{
|
||||
vec4 value = sample_c(v_tex);
|
||||
o_col0 = vec4(value.rgb, value.a / (128.25f / 255.0f));
|
||||
OUTPUT = vec4(value.rgb, value.a / (128.25f / 255.0f));
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -148,7 +229,7 @@ void ps_rta_correction()
|
||||
void ps_rta_decorrection()
|
||||
{
|
||||
vec4 value = sample_c(v_tex);
|
||||
o_col0 = vec4(value.rgb, value.a * (128.25f / 255.0f));
|
||||
OUTPUT = vec4(value.rgb, value.a * (128.25f / 255.0f));
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -156,7 +237,7 @@ void ps_rta_decorrection()
|
||||
void ps_colclip_init()
|
||||
{
|
||||
vec4 value = sample_c(v_tex);
|
||||
o_col0 = vec4(roundEven(value.rgb * 255.0f) / 65535.0f, value.a);
|
||||
OUTPUT = vec4(roundEven(value.rgb * 255.0f) / 65535.0f, value.a);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -164,116 +245,39 @@ void ps_colclip_init()
|
||||
void ps_colclip_resolve()
|
||||
{
|
||||
vec4 value = sample_c(v_tex);
|
||||
o_col0 = vec4(vec3(uvec3(value.rgb * 65535.5f) & 255u) / 255.0f, value.a);
|
||||
OUTPUT = vec4(vec3(uvec3(value.rgb * 65535.5f) & 255u) / 255.0f, value.a);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_convert_float32_depth_to_color
|
||||
void ps_convert_float32_depth_to_color()
|
||||
{
|
||||
o_col0 = sample_c(v_tex).r;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_convert_float32_color_to_depth
|
||||
void ps_convert_float32_color_to_depth()
|
||||
{
|
||||
gl_FragDepth = sample_c(v_tex).r;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_convert_float32_32bits
|
||||
void ps_convert_float32_32bits()
|
||||
#ifdef ps_convert_depth32_32bits
|
||||
void ps_convert_depth32_32bits()
|
||||
{
|
||||
// Convert a vec32 depth texture into a 32 bits UINT texture
|
||||
o_col0 = uint(exp2(32.0f) * sample_c(v_tex).r);
|
||||
OUTPUT = depth_to_uint(sample_c(v_tex));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_convert_float32_rgba8
|
||||
void ps_convert_float32_rgba8()
|
||||
#ifdef ps_convert_depth32_rgba8
|
||||
void ps_convert_depth32_rgba8()
|
||||
{
|
||||
// Convert a vec32 depth texture into a RGBA color texture
|
||||
uint d = uint(sample_c(v_tex).r * exp2(32.0f));
|
||||
o_col0 = vec4(uvec4((d & 0xFFu), ((d >> 8) & 0xFFu), ((d >> 16) & 0xFFu), (d >> 24))) / vec4(255.0);
|
||||
OUTPUT = depth32_to_rgba8(sample_c(v_tex));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_convert_float16_rgb5a1
|
||||
void ps_convert_float16_rgb5a1()
|
||||
#ifdef ps_convert_depth16_rgb5a1
|
||||
void ps_convert_depth16_rgb5a1()
|
||||
{
|
||||
// Convert a vec32 (only 16 lsb) depth into a RGB5A1 color texture
|
||||
uint d = uint(sample_c(v_tex).r * exp2(32.0f));
|
||||
o_col0 = vec4(uvec4(d << 3, d >> 2, d >> 7, d >> 8) & uvec4(0xf8, 0xf8, 0xf8, 0x80)) / 255.0f;
|
||||
OUTPUT = depth16_to_rgb5a1(sample_c(v_tex));
|
||||
}
|
||||
#endif
|
||||
|
||||
float rgba8_to_depth32(vec4 unorm)
|
||||
{
|
||||
uvec4 c = uvec4(unorm * vec4(255.5f));
|
||||
return float(c.r | (c.g << 8) | (c.b << 16) | (c.a << 24)) * exp2(-32.0f);
|
||||
}
|
||||
|
||||
float rgba8_to_depth24(vec4 unorm)
|
||||
{
|
||||
uvec3 c = uvec3(unorm.rgb * vec3(255.5f));
|
||||
return float(c.r | (c.g << 8) | (c.b << 16)) * exp2(-32.0f);
|
||||
}
|
||||
|
||||
float rgba8_to_depth16(vec4 unorm)
|
||||
{
|
||||
uvec2 c = uvec2(unorm.rg * vec2(255.5f));
|
||||
return float(c.r | (c.g << 8)) * exp2(-32.0f);
|
||||
}
|
||||
|
||||
float rgb5a1_to_depth16(vec4 unorm)
|
||||
{
|
||||
uvec4 c = uvec4(unorm * vec4(255.5f));
|
||||
return float(((c.r & 0xF8u) >> 3) | ((c.g & 0xF8u) << 2) | ((c.b & 0xF8u) << 7) | ((c.a & 0x80u) << 8)) * exp2(-32.0f);
|
||||
}
|
||||
|
||||
#ifdef ps_convert_float32_float24
|
||||
void ps_convert_float32_float24()
|
||||
#ifdef ps_convert_depth32_depth24
|
||||
void ps_convert_depth32_depth24()
|
||||
{
|
||||
// Truncates depth value to 24bits
|
||||
uint d = uint(sample_c(v_tex).r * exp2(32.0f)) & 0xFFFFFFu;
|
||||
gl_FragDepth = float(d) * exp2(-32.0f);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_convert_rgba8_float32
|
||||
void ps_convert_rgba8_float32()
|
||||
{
|
||||
// Convert an RGBA texture into a float depth texture
|
||||
gl_FragDepth = rgba8_to_depth32(sample_c(v_tex));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_convert_rgba8_float24
|
||||
void ps_convert_rgba8_float24()
|
||||
{
|
||||
// Same as above but without the alpha channel (24 bits Z)
|
||||
|
||||
// Convert an RGBA texture into a float depth texture
|
||||
gl_FragDepth = rgba8_to_depth24(sample_c(v_tex));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_convert_rgba8_float16
|
||||
void ps_convert_rgba8_float16()
|
||||
{
|
||||
// Same as above but without the A/B channels (16 bits Z)
|
||||
|
||||
// Convert an RGBA texture into a float depth texture
|
||||
gl_FragDepth = rgba8_to_depth16(sample_c(v_tex));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_convert_rgb5a1_float16
|
||||
void ps_convert_rgb5a1_float16()
|
||||
{
|
||||
// Convert an RGB5A1 (saved as RGBA8) color to a 16 bit Z
|
||||
gl_FragDepth = rgb5a1_to_depth16(sample_c(v_tex));
|
||||
OUTPUT = depth32_to_depth24(sample_c(v_tex));
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -287,41 +291,53 @@ void ps_convert_rgb5a1_float16()
|
||||
float depthTR = CONVERT_FN(texelFetch(samp0, coords.zy, 0)); \
|
||||
float depthBL = CONVERT_FN(texelFetch(samp0, coords.xw, 0)); \
|
||||
float depthBR = CONVERT_FN(texelFetch(samp0, coords.zw, 0)); \
|
||||
gl_FragDepth = mix(mix(depthTL, depthTR, mix_vals.x), mix(depthBL, depthBR, mix_vals.x), mix_vals.y);
|
||||
OUTPUT = mix(mix(depthTL, depthTR, mix_vals.x), mix(depthBL, depthBR, mix_vals.x), mix_vals.y);
|
||||
|
||||
#ifdef ps_convert_rgba8_float32_biln
|
||||
void ps_convert_rgba8_float32_biln()
|
||||
#ifdef ps_convert_rgba8_depth32
|
||||
void ps_convert_rgba8_depth32()
|
||||
{
|
||||
// Convert an RGBA texture into a float depth texture
|
||||
#if HAS_BILN
|
||||
SAMPLE_RGBA_DEPTH_BILN(rgba8_to_depth32);
|
||||
#else
|
||||
OUTPUT = rgba8_to_depth32(sample_c(v_tex));
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_convert_rgba8_float24_biln
|
||||
void ps_convert_rgba8_float24_biln()
|
||||
#ifdef ps_convert_rgba8_depth24
|
||||
void ps_convert_rgba8_depth24()
|
||||
{
|
||||
// Same as above but without the alpha channel (24 bits Z)
|
||||
|
||||
// Convert an RGBA texture into a float depth texture
|
||||
#if HAS_BILN
|
||||
SAMPLE_RGBA_DEPTH_BILN(rgba8_to_depth24);
|
||||
#else
|
||||
OUTPUT = rgba8_to_depth24(sample_c(v_tex));
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_convert_rgba8_float16_biln
|
||||
void ps_convert_rgba8_float16_biln()
|
||||
#ifdef ps_convert_rgba8_depth16
|
||||
void ps_convert_rgba8_depth16()
|
||||
{
|
||||
// Same as above but without the A/B channels (16 bits Z)
|
||||
|
||||
// Convert an RGBA texture into a float depth texture
|
||||
#if HAS_BILN
|
||||
SAMPLE_RGBA_DEPTH_BILN(rgba8_to_depth16);
|
||||
#else
|
||||
OUTPUT = rgba8_to_depth16(sample_c(v_tex));
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_convert_rgb5a1_float16_biln
|
||||
void ps_convert_rgb5a1_float16_biln()
|
||||
#ifdef ps_convert_rgb5a1_depth16
|
||||
void ps_convert_rgb5a1_depth16()
|
||||
{
|
||||
// Convert an RGB5A1 (saved as RGBA8) color to a 16 bit Z
|
||||
#if HAS_BILN
|
||||
SAMPLE_RGBA_DEPTH_BILN(rgb5a1_to_depth16);
|
||||
#else
|
||||
OUTPUT = rgb5a1_to_depth16(sample_c(v_tex));
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -608,25 +624,25 @@ void ps_yuv()
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(ps_stencil_image_init_0) || defined(ps_stencil_image_init_1) || defined(ps_stencil_image_init_2) || defined(ps_stencil_image_init_3)
|
||||
#if defined(ps_primid_image_init_0) || defined(ps_primid_image_init_1) || defined(ps_primid_image_init_2) || defined(ps_primid_image_init_3)
|
||||
|
||||
void main()
|
||||
{
|
||||
o_col0 = vec4(0x7FFFFFFF);
|
||||
|
||||
#ifdef ps_stencil_image_init_0
|
||||
#ifdef ps_primid_image_init_0
|
||||
if((127.5f / 255.0f) < sample_c(v_tex).a) // < 0x80 pass (== 0x80 should not pass)
|
||||
o_col0 = vec4(-1);
|
||||
#endif
|
||||
#ifdef ps_stencil_image_init_1
|
||||
#ifdef ps_primid_image_init_1
|
||||
if(sample_c(v_tex).a < (127.5f / 255.0f)) // >= 0x80 pass
|
||||
o_col0 = vec4(-1);
|
||||
#endif
|
||||
#ifdef ps_stencil_image_init_2
|
||||
#ifdef ps_primid_image_init_2
|
||||
if((254.5f / 255.0f) < sample_c(v_tex).a) // < 0x80 pass (== 0x80 should not pass)
|
||||
o_col0 = vec4(-1);
|
||||
#endif
|
||||
#ifdef ps_stencil_image_init_3
|
||||
#ifdef ps_primid_image_init_3
|
||||
if(sample_c(v_tex).a < (254.5f / 255.0f)) // >= 0x80 pass
|
||||
o_col0 = vec4(-1);
|
||||
#endif
|
||||
|
||||
@@ -189,6 +189,110 @@ ProcessedVertex load_vertex(uint index)
|
||||
return vtx;
|
||||
}
|
||||
|
||||
// Convert XY from NDC to GS pixel coordinates (i.e. 1.0 = 1 GS pixel).
|
||||
vec2 get_xy_unscaled(vec2 xy)
|
||||
{
|
||||
return round(xy / VertexScale) / 16.0f;
|
||||
}
|
||||
|
||||
// Get the XY deltas in GS pixel coordinates, using first vertex as the origin.
|
||||
mat2 get_xy_deltas_unscaled(ProcessedVertex v0, ProcessedVertex v1, ProcessedVertex v2)
|
||||
{
|
||||
vec2 xy0 = get_xy_unscaled(v0.p.xy);
|
||||
vec2 xy1 = get_xy_unscaled(v1.p.xy);
|
||||
vec2 xy2 = get_xy_unscaled(v2.p.xy);
|
||||
return mat2(xy1 - xy0, xy2 - xy0);
|
||||
}
|
||||
|
||||
// Get the AA1 outward expand direction to the edge formed by the first two vertices.
|
||||
// This is up or down for shallow (X dominant) edges, and right or left for steep (Y dominant) edges.
|
||||
// Similar expansion to line AA1 except instead of expanding on both sides of the line,
|
||||
// expand on on the side towards the outside of the triangle.
|
||||
vec2 get_aa1_triangle_expand_dir(ProcessedVertex v0, ProcessedVertex v1, ProcessedVertex v2)
|
||||
{
|
||||
mat2 xy_deltas = get_xy_deltas_unscaled(v0, v1, v2);
|
||||
vec2 line_delta = xy_deltas[0];
|
||||
vec2 line_opposite = xy_deltas[1];
|
||||
|
||||
vec2 line_normal = vec2(line_delta.y, -line_delta.x);
|
||||
vec2 line_expand = abs(line_delta.x) >= abs(line_delta.y) ? vec2(0.0f, 1.0f) : vec2(1.0f, 0.0f);
|
||||
|
||||
if ((dot(line_expand, line_normal) >= 0.0f) == (dot(line_opposite, line_normal) >= 0.0f))
|
||||
{
|
||||
// Expand direction point towards the interior so flip it.
|
||||
line_expand = -line_expand;
|
||||
}
|
||||
|
||||
return line_expand;
|
||||
}
|
||||
|
||||
mat2 get_inverse(mat2 mat, float det)
|
||||
{
|
||||
return mat2(mat[1][1], -mat[0][1], -mat[1][0], mat[0][0]) * (1 / det);
|
||||
}
|
||||
|
||||
// Extrapolate triangle attributes from the first vertex along the given direction.
|
||||
// dp_mat is derived from the input vertices, it is passed in to avoid recomputing.
|
||||
void extrapolate_aa1_triangle_edge(inout ProcessedVertex v0, ProcessedVertex v1, ProcessedVertex v2, mat2 dp_mat, vec2 dp)
|
||||
{
|
||||
// Get texture deltas
|
||||
#if VS_TME
|
||||
#if VS_FST
|
||||
mat2 dt = mat2(v1.ti.zw - v0.ti.zw, v2.ti.zw - v0.ti.zw);
|
||||
#else
|
||||
mat2 dt = mat2(v1.t.xy - v0.t.xy, v2.t.xy - v0.t.xy);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Get color delta if interpolating
|
||||
#if VS_IIP
|
||||
mat2x4 dc = mat2x4(v1.c - v0.c, v2.c - v0.c);
|
||||
#endif
|
||||
|
||||
vec2 dz = vec2(v1.p.z - v0.p.z, v2.p.z - v0.p.z); // Z deltas
|
||||
|
||||
vec2 df = vec2(v1.t.z - v0.t.z, v2.t.z - v0.t.z); // Fog deltas
|
||||
|
||||
vec2 dq = vec2(v1.t.w - v0.t.w, v2.t.w - v0.t.w); // Q deltas
|
||||
|
||||
// To prevent unstable extrapolation, do not extrapolate if the
|
||||
// minimum perpendicular length of the triangle is < 2 pixels.
|
||||
float dp_det = determinant(dp_mat); // Twice signed triangle area.
|
||||
float len0 = length(dp_mat[0]);
|
||||
float len1 = length(dp_mat[1]);
|
||||
float len2 = length(dp_mat[1] - dp_mat[0]);
|
||||
float min_perp_length = abs(dp_det) / max(max(len0, len1), len2);
|
||||
|
||||
// Get the position -> barycentric weight matrix
|
||||
mat2 inv_dp_mat = get_inverse(dp_mat, dp_det);
|
||||
|
||||
vec2 weights = min_perp_length < 2 ? vec2(0) : inv_dp_mat * dp;
|
||||
|
||||
v0.p.xy += dp * PointSize; // Extrapolate position
|
||||
|
||||
// Extrapolate texture coords
|
||||
#if VS_TME
|
||||
#if VS_FST
|
||||
v0.ti.zw += dt * weights;
|
||||
v0.ti.xy = v0.ti.zw * TextureScale;
|
||||
#else
|
||||
v0.t.xy += dt * weights;
|
||||
v0.ti.zw = v0.t.xy / TextureScale;
|
||||
v0.t.w += dot(dq, weights);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Extrapolate and clamp color
|
||||
#if VS_IIP
|
||||
v0.c += dc * weights;
|
||||
v0.c = clamp(v0.c, vec4(0), vec4(255));
|
||||
#endif
|
||||
|
||||
v0.p.z += dot(dz, weights); // Extrapolate depth
|
||||
|
||||
v0.t.z += dot(df, weights); // Extrapolate fog
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
ProcessedVertex vtx;
|
||||
@@ -214,13 +318,12 @@ 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);
|
||||
vec2 line_vector = normalize(line_delta / VertexScale);
|
||||
#if VS_EXPAND == VS_EXPAND_LINE
|
||||
vec2 line_expand = vec2(line_vector.y, -line_vector.x);
|
||||
#elif VS_EXPAND == VS_EXPAND_LINE_AA1
|
||||
// Expand in y direction for shallow lines and x direction for steep lines.
|
||||
line_delta /= VertexScale;
|
||||
vec2 line_expand = abs(line_delta.x) >= abs(line_delta.y) ? vec2(0.0f, 2.0f) : vec2(2.0f, 0.0f);
|
||||
vec2 line_expand = abs(line_vector.x) >= abs(line_vector.y) ? vec2(0.0f, 2.0f) : vec2(2.0f, 0.0f);
|
||||
#endif
|
||||
vec2 line_width = (line_expand * PointSize) / 2;
|
||||
vec2 offset = is_right ? line_width : -line_width;
|
||||
@@ -262,10 +365,14 @@ void main()
|
||||
// - Vertices 3-8: First edge expanded (2 triangles).
|
||||
// - Vertices 9-14: Second edge expanded (2 triangles).
|
||||
// - Vertices 15-20: Third edge expanded (2 triangles).
|
||||
// - Vertices 21-26: First corner cap (2 triangles).
|
||||
// - Vertices 27-32: Second corner cap (2 triangles).
|
||||
// - Vertices 33-38: Third corner cap (2 triangles).
|
||||
|
||||
uint prim_id = vid / 21;
|
||||
uint prim_offset = vid - 21 * prim_id; // range: 0-20
|
||||
uint prim_id = vid / 39;
|
||||
uint prim_offset = vid - 39 * prim_id; // range: 0-38
|
||||
bool interior = prim_offset < 3;
|
||||
bool edge = 3 <= prim_offset && prim_offset < 21;
|
||||
|
||||
if (interior)
|
||||
{
|
||||
@@ -273,7 +380,7 @@ void main()
|
||||
vsOut.inv_cov = 0.0f; // Full coverage
|
||||
vsOut.interior = 1;
|
||||
}
|
||||
else
|
||||
else if (edge)
|
||||
{
|
||||
// Vertex indices for this edge. We need all 3 for determining exterior/interior.
|
||||
uint prim_offset_edges = prim_offset - 3; // range: 0-17
|
||||
@@ -282,39 +389,68 @@ void main()
|
||||
uint i2 = (i0 >= 1) ? i0 - 1 : i0 + 2;
|
||||
uint edge_offset = prim_offset_edges - 6 * i0; // range: 0-5
|
||||
|
||||
// Note: order of top/bottom, inside/outside order is arbitrary,
|
||||
// Note: order of top/bottom, inside/outside is arbitrary,
|
||||
// as long as it assembles into two triangles forming a quad.
|
||||
bool is_bottom = (2 <= edge_offset) && (edge_offset <= 4);
|
||||
bool is_outside = (edge_offset & 1) != 0;
|
||||
|
||||
vtx = load_vertex(load_index(3 * prim_id + i0));
|
||||
ProcessedVertex other = load_vertex(load_index(3 * prim_id + i1));
|
||||
vtx = load_vertex(load_index(3 * prim_id + (is_bottom ? i1 : i0)));
|
||||
ProcessedVertex other = load_vertex(load_index(3 * prim_id + (is_bottom ? i0 : i1)));
|
||||
ProcessedVertex opposite = load_vertex(load_index(3 * prim_id + i2));
|
||||
|
||||
// Similar expansion to line AA1 except instead of expanding on both sides of
|
||||
// the line we expand on on the side towards the outside of the triangle.
|
||||
vec2 line_delta = vtx.p.xy - other.p.xy;
|
||||
vec2 line_normal = normalize(vec2(line_delta.y, -line_delta.x));
|
||||
vec2 line_expand = abs(line_delta.x) >= abs(line_delta.y) ? vec2(0.0f, 2.0f) : vec2(2.0f, 0.0f);
|
||||
if ((dot(line_expand, line_normal) >= 0.0f) == (dot(opposite.p.xy - vtx.p.xy, line_normal) >= 0.0f))
|
||||
{
|
||||
// Expand direction point towards the interior so flip it.
|
||||
line_expand = -line_expand;
|
||||
}
|
||||
vec2 line_width = (line_expand * PointSize) / 2;
|
||||
mat2 pos_deltas = get_xy_deltas_unscaled(vtx, other, opposite);
|
||||
|
||||
if (is_bottom)
|
||||
vtx = other;
|
||||
if (is_outside)
|
||||
{
|
||||
vtx.p.xy += line_width;
|
||||
vsOut.inv_cov = 1.0f; // No coverage
|
||||
}
|
||||
else
|
||||
{
|
||||
vsOut.inv_cov = 0.0f; // Full coverage
|
||||
}
|
||||
vec2 expand_dir = is_outside ? get_aa1_triangle_expand_dir(vtx, other, opposite) : vec2(0);
|
||||
|
||||
// Do actual extrapolation, or no-op if expand_dir == 0.
|
||||
extrapolate_aa1_triangle_edge(vtx, other, opposite, pos_deltas, expand_dir);
|
||||
|
||||
vsOut.inv_cov = is_outside ? 1.0f : 0.0f; // No coverage on outside, otherwise full.
|
||||
|
||||
vsOut.interior = 0;
|
||||
}
|
||||
else // Corner cap
|
||||
{
|
||||
// Vertex indices for this cap. We need all 3 for determining exterior/interior.
|
||||
uint prim_offset_cap = prim_offset - 21; // range: 0-8
|
||||
uint i0 = prim_offset_cap / 6;
|
||||
uint i1 = (i0 >= 2) ? i0 - 2 : i0 + 1;
|
||||
uint i2 = (i0 >= 1) ? i0 - 1 : i0 + 2;
|
||||
uint cap_offset = prim_offset_cap - 6 * i0; // range: 0-5
|
||||
|
||||
bool is_near_corner = cap_offset == 0 || cap_offset == 3;
|
||||
bool is_far_corner = cap_offset == 2 || cap_offset == 5;
|
||||
bool is_first_tri = cap_offset < 3;
|
||||
|
||||
// First triangle is on the side of vertex i1 and second is on the side of vertex i2.
|
||||
vtx = load_vertex(load_index(3 * prim_id + i0));
|
||||
ProcessedVertex other = load_vertex(load_index(3 * prim_id + (is_first_tri ? i1 : i2)));
|
||||
ProcessedVertex opposite = load_vertex(load_index(3 * prim_id + (is_first_tri ? i2 : i1)));
|
||||
|
||||
mat2 pos_deltas = get_xy_deltas_unscaled(vtx, other, opposite);
|
||||
|
||||
// Get the edge expansion directions of both incident edges.
|
||||
vec2 edge_expand_dir_0 = get_aa1_triangle_expand_dir(vtx, other, opposite);
|
||||
vec2 edge_expand_dir_1 = get_aa1_triangle_expand_dir(vtx, opposite, other);
|
||||
|
||||
// Check if the corner is already filled by the expanded edges.
|
||||
// This happens if the expand directions are the same.
|
||||
// If so we output a degenerate triangle at this corner.
|
||||
bool corner_filled = all(equal(edge_expand_dir_0, edge_expand_dir_1));
|
||||
|
||||
// Nothing if corner is filled, otherwise opposite to the bisector of the corner angle.
|
||||
vec2 far_corner_dir = corner_filled ? vec2(0) : -normalize((pos_deltas[0] + pos_deltas[1]) / 2);
|
||||
|
||||
// Determine the expand direction.
|
||||
vec2 expand_dir = is_near_corner ? vec2(0) : // No extrapolation
|
||||
is_far_corner ? far_corner_dir : // Opposite to the angle bisector of corner
|
||||
edge_expand_dir_0; // Standard AA1 edge expansion
|
||||
|
||||
// Do the actual extrapolation (no-op if expand_dir == 0).
|
||||
extrapolate_aa1_triangle_edge(vtx, other, opposite, pos_deltas, expand_dir);
|
||||
|
||||
vsOut.inv_cov = is_near_corner ? 0.0f : 1.0f; // Full coverage at near corner, otherwise none.
|
||||
|
||||
vsOut.interior = 0;
|
||||
|
||||
#if !VS_IIP
|
||||
@@ -386,6 +522,12 @@ void main()
|
||||
#define PS_AA1_TRIANGLE_SW_Z 3
|
||||
#endif
|
||||
|
||||
#ifndef PS_ROV_DEPTH_NONE
|
||||
#define PS_ROV_DEPTH_NONE 0
|
||||
#define PS_ROV_DEPTH_READ_WRITE 1
|
||||
#define PS_ROV_DEPTH_READ_ONLY 2
|
||||
#endif
|
||||
|
||||
#ifndef PS_FST
|
||||
#define PS_FST 0
|
||||
#define PS_WMS 0
|
||||
@@ -438,6 +580,8 @@ void main()
|
||||
#define PS_NO_COLOR1 0
|
||||
#define PS_DATE 0
|
||||
#define PS_TEX_IS_FB 0
|
||||
#define PS_ROV_COLOR 0
|
||||
#define PS_ROV_DEPTH 0
|
||||
#endif
|
||||
|
||||
#define SW_BLEND (PS_BLEND_A || PS_BLEND_B || PS_BLEND_D)
|
||||
@@ -450,7 +594,13 @@ void main()
|
||||
|
||||
#define PS_FEEDBACK_LOOP_IS_NEEDED_RT (PS_TEX_IS_FB == 1 || AFAIL_NEEDS_RT || PS_FBMASK || SW_BLEND_NEEDS_RT || SW_AD_TO_HW || (PS_DATE >= 5))
|
||||
#define PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH (AFAIL_NEEDS_DEPTH || ZTST_NEEDS_DEPTH || AA1_NEEDS_DEPTH)
|
||||
#define ZWRITE (PS_ZCLAMP || PS_ZFLOOR || PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH)
|
||||
#define ZWRITE (PS_ZCLAMP || PS_ZFLOOR || SW_DEPTH || PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH)
|
||||
|
||||
#define PS_RETURN_COLOR_ROV (!PS_NO_COLOR && PS_ROV_COLOR)
|
||||
#define PS_RETURN_COLOR (!PS_NO_COLOR && !PS_ROV_COLOR)
|
||||
#define PS_RETURN_DEPTH_ROV (PS_ROV_DEPTH == PS_ROV_DEPTH_READ_WRITE)
|
||||
#define PS_RETURN_DEPTH (ZWRITE && !PS_ROV_DEPTH)
|
||||
#define PS_ROV_EARLYDEPTHSTENCIL (PS_ROV_COLOR && !PS_ROV_DEPTH && !ZWRITE)
|
||||
|
||||
#define NEEDS_TEX (PS_TFX != 4)
|
||||
|
||||
@@ -489,11 +639,27 @@ layout(location = 0) in VSOutput
|
||||
flat uint interior; // 1 for triangle interior; 0 for edge;
|
||||
} vsIn;
|
||||
|
||||
#if !PS_NO_COLOR && !PS_NO_COLOR1
|
||||
layout(location = 0, index = 0) out vec4 o_col0;
|
||||
layout(location = 0, index = 1) out vec4 o_col1;
|
||||
#elif !PS_NO_COLOR
|
||||
layout(location = 0) out vec4 o_col0;
|
||||
#if PS_RETURN_COLOR
|
||||
#if !PS_NO_COLOR1
|
||||
layout(location = 0, index = 0) out vec4 o_col0;
|
||||
layout(location = 0, index = 1) out vec4 o_col1;
|
||||
#elif !PS_NO_COLOR
|
||||
layout(location = 0) out vec4 o_col0;
|
||||
#endif
|
||||
#elif PS_RETURN_COLOR_ROV
|
||||
vec4 o_col0;
|
||||
#endif
|
||||
|
||||
#if PS_ROV_COLOR
|
||||
layout(set = 1, binding = 5, rgba8) uniform restrict coherent image2D RtImageRov;
|
||||
vec4 rov_rt_value;
|
||||
vec4 sample_from_rt() { return rov_rt_value; }
|
||||
#endif
|
||||
|
||||
#if PS_ROV_DEPTH
|
||||
layout(set = 1, binding = 6, r32f) uniform restrict coherent image2D DepthImageRov;
|
||||
float rov_depth_value;
|
||||
float sample_from_depth() { return rov_depth_value; }
|
||||
#endif
|
||||
|
||||
#if NEEDS_TEX
|
||||
@@ -503,27 +669,27 @@ layout(set = 1, binding = 1) uniform texture2D Palette;
|
||||
|
||||
#if PS_FEEDBACK_LOOP_IS_NEEDED_RT || PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH
|
||||
#if defined(DISABLE_TEXTURE_BARRIER) || defined(HAS_FEEDBACK_LOOP_LAYOUT)
|
||||
#if PS_FEEDBACK_LOOP_IS_NEEDED_RT
|
||||
#if (PS_FEEDBACK_LOOP_IS_NEEDED_RT && !PS_ROV_COLOR)
|
||||
layout(set = 1, binding = 2) uniform texture2D RtSampler;
|
||||
vec4 sample_from_rt() { return texelFetch(RtSampler, ivec2(gl_FragCoord.xy), 0); }
|
||||
#endif
|
||||
#if PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH
|
||||
#if (PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH && !PS_ROV_DEPTH)
|
||||
layout(set = 1, binding = 4) uniform texture2D DepthSampler;
|
||||
vec4 sample_from_depth() { return texelFetch(DepthSampler, ivec2(gl_FragCoord.xy), 0); }
|
||||
float sample_from_depth() { return texelFetch(DepthSampler, ivec2(gl_FragCoord.xy), 0).r; }
|
||||
#endif
|
||||
#else
|
||||
// Must consider each case separately since the input attachment indices must be consecutive.
|
||||
#if PS_FEEDBACK_LOOP_IS_NEEDED_RT && PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH
|
||||
#if (PS_FEEDBACK_LOOP_IS_NEEDED_RT && !PS_ROV_COLOR) && (PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH && !PS_ROV_DEPTH)
|
||||
layout(input_attachment_index = 0, set = 1, binding = 2) uniform subpassInput RtSampler;
|
||||
layout(input_attachment_index = 1, set = 1, binding = 4) uniform subpassInput DepthSampler;
|
||||
vec4 sample_from_rt() { return subpassLoad(RtSampler); }
|
||||
vec4 sample_from_depth() { return subpassLoad(DepthSampler); }
|
||||
#elif PS_FEEDBACK_LOOP_IS_NEEDED_RT
|
||||
float sample_from_depth() { return subpassLoad(DepthSampler).r; }
|
||||
#elif (PS_FEEDBACK_LOOP_IS_NEEDED_RT && !PS_ROV_COLOR)
|
||||
layout(input_attachment_index = 0, set = 1, binding = 2) uniform subpassInput RtSampler;
|
||||
vec4 sample_from_rt() { return subpassLoad(RtSampler); }
|
||||
#elif PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH
|
||||
#elif (PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH && !PS_ROV_DEPTH)
|
||||
layout(input_attachment_index = 0, set = 1, binding = 4) uniform subpassInput DepthSampler;
|
||||
vec4 sample_from_depth() { return subpassLoad(DepthSampler); }
|
||||
float sample_from_depth() { return subpassLoad(DepthSampler).r; }
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
@@ -978,7 +1144,7 @@ vec4 sample_depth(vec2 st, ivec2 pos)
|
||||
}
|
||||
#elif (PS_DEPTH_FMT == 1)
|
||||
{
|
||||
// Based on ps_convert_float32_rgba8 of convert
|
||||
// Based on ps_convert_depth32_rgba8 of convert
|
||||
|
||||
// Convert a vec32 depth texture into a RGBA color texture
|
||||
uint d = uint(fetch_c(uv).r * exp2(32.0f));
|
||||
@@ -986,7 +1152,7 @@ vec4 sample_depth(vec2 st, ivec2 pos)
|
||||
}
|
||||
#elif (PS_DEPTH_FMT == 2)
|
||||
{
|
||||
// Based on ps_convert_float16_rgb5a1 of convert
|
||||
// Based on ps_convert_depth16_rgb5a1 of convert
|
||||
|
||||
// Convert a vec32 (only 16 lsb) depth into a RGB5A1 color texture
|
||||
uint d = uint(fetch_c(uv).r * exp2(32.0f));
|
||||
@@ -1468,7 +1634,7 @@ void ps_blend(inout vec4 Color, inout vec4 As_rgba)
|
||||
#elif PS_BLEND_MIX == 1
|
||||
Color.rgb = ((A - B) * C_clamped + D) - (124.0f/256.0f);
|
||||
#else
|
||||
Color.rgb = trunc((A - B) * C + D);
|
||||
Color.rgb = trunc((A - B) * C + D);
|
||||
#endif
|
||||
|
||||
#if PS_BLEND_HW == 1
|
||||
@@ -1542,6 +1708,20 @@ void ps_blend(inout vec4 Color, inout vec4 As_rgba)
|
||||
#endif
|
||||
}
|
||||
|
||||
#if PS_ROV_COLOR || PS_ROV_DEPTH
|
||||
layout(pixel_interlock_ordered) in;
|
||||
#endif
|
||||
|
||||
#if PS_ROV_EARLYDEPTHSTENCIL
|
||||
layout(early_fragment_tests) in;
|
||||
#endif
|
||||
|
||||
#if PS_ROV_COLOR || PS_ROV_DEPTH
|
||||
#define DISCARD rov_discard = true
|
||||
#else
|
||||
#define DISCARD discard
|
||||
#endif
|
||||
|
||||
void main()
|
||||
{
|
||||
float input_z = gl_FragCoord.z;
|
||||
@@ -1551,18 +1731,34 @@ void main()
|
||||
input_z = floor(input_z * exp2(32.0f)) * exp2(-32.0f);
|
||||
#endif
|
||||
|
||||
#if PS_ROV_COLOR || PS_ROV_DEPTH
|
||||
beginInvocationInterlockARB();
|
||||
#endif
|
||||
|
||||
#if PS_ROV_COLOR
|
||||
rov_rt_value = imageLoad(RtImageRov, ivec2(gl_FragCoord.xy));
|
||||
#endif
|
||||
|
||||
#if PS_ROV_DEPTH
|
||||
rov_depth_value = imageLoad(DepthImageRov, ivec2(gl_FragCoord.xy)).r;
|
||||
#endif
|
||||
|
||||
#if PS_ROV_COLOR || PS_ROV_DEPTH
|
||||
bool rov_discard = gl_HelperInvocation;
|
||||
#endif
|
||||
|
||||
#if PS_ZTST == ZTST_GEQUAL
|
||||
if (input_z < sample_from_depth().r)
|
||||
discard;
|
||||
if (input_z < sample_from_depth())
|
||||
DISCARD;
|
||||
#elif PS_ZTST == ZTST_GREATER
|
||||
if (input_z <= sample_from_depth().r)
|
||||
discard;
|
||||
if (input_z <= sample_from_depth())
|
||||
DISCARD;
|
||||
#endif
|
||||
|
||||
#if PS_SCANMSK & 2
|
||||
// fail depth test on prohibited lines
|
||||
if ((int(gl_FragCoord.y) & 1) == (PS_SCANMSK & 1))
|
||||
discard;
|
||||
DISCARD;
|
||||
#endif
|
||||
#if PS_DATE >= 5
|
||||
|
||||
@@ -1590,7 +1786,7 @@ void main()
|
||||
#endif
|
||||
|
||||
if (bad) {
|
||||
discard;
|
||||
DISCARD;
|
||||
}
|
||||
|
||||
#endif // PS_DATE >= 5
|
||||
@@ -1601,7 +1797,7 @@ void main()
|
||||
// the bad alpha value so we must keep it.
|
||||
|
||||
if (gl_PrimitiveID > stencil_ceil) {
|
||||
discard;
|
||||
DISCARD;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1622,9 +1818,10 @@ void main()
|
||||
|
||||
bool atst_pass = atst(C);
|
||||
|
||||
#if PS_AFAIL == AFAIL_KEEP
|
||||
if (!atst_pass)
|
||||
discard;
|
||||
#if PS_ATST != PS_ATST_NONE && PS_AFAIL == AFAIL_KEEP
|
||||
if (!atst_pass) {
|
||||
DISCARD;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SW_AD_TO_HW
|
||||
@@ -1716,6 +1913,7 @@ void main()
|
||||
alpha_blend.a = float(atst_pass);
|
||||
#endif
|
||||
|
||||
// Output color scaling
|
||||
#if !PS_NO_COLOR
|
||||
#if PS_RTA_CORRECTION
|
||||
o_col0.a = C.a / 128.0f;
|
||||
@@ -1734,7 +1932,7 @@ void main()
|
||||
// Alpha test with feedback
|
||||
#if PS_AFAIL == AFAIL_FB_ONLY
|
||||
if (!atst_pass)
|
||||
input_z = sample_from_depth().r;
|
||||
input_z = sample_from_depth();
|
||||
#elif PS_AFAIL == AFAIL_ZB_ONLY
|
||||
if (!atst_pass)
|
||||
o_col0 = sample_from_rt();
|
||||
@@ -1743,7 +1941,7 @@ void main()
|
||||
{
|
||||
o_col0.a = sample_from_rt().a;
|
||||
#if PS_AFAIL == AFAIL_RGB_ONLY_SW_Z
|
||||
input_z = sample_from_depth().r;
|
||||
input_z = sample_from_depth();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
@@ -1755,11 +1953,28 @@ void main()
|
||||
|
||||
#if PS_AA1 == PS_AA1_TRIANGLE_SW_Z
|
||||
if (!bool(vsIn.interior))
|
||||
input_z = sample_from_depth().r; // No depth update for triangle edges.
|
||||
input_z = sample_from_depth(); // No depth update for triangle edges.
|
||||
#endif
|
||||
|
||||
#if ZWRITE
|
||||
// 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);
|
||||
|
||||
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));
|
||||
#endif
|
||||
|
||||
#if PS_ROV_COLOR || PS_ROV_DEPTH
|
||||
endInvocationInterlockARB();
|
||||
#endif
|
||||
#endif // PS_DATE
|
||||
}
|
||||
|
||||
@@ -300,8 +300,8 @@ else()
|
||||
if(CMAKE_INTERPROCEDURAL_OPTIMIZATION)
|
||||
message(WARNING
|
||||
"The CMAKE_INTERPROCEDURAL_OPTIMIZATION option is enabled but the "
|
||||
"CMAKE_POSITION_INDEPENDENT_CODE option is disabled. This has been "
|
||||
"found to result in broken builds on certain platforms.")
|
||||
"POSITION_INDEPENDENT_CODE option is disabled. This has been found "
|
||||
"to result in broken builds on certain platforms.")
|
||||
endif()
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE OFF)
|
||||
|
||||
@@ -22,6 +22,9 @@ find_package(Freetype 2.10 REQUIRED) # 2.10 is the first with COLRv0 support, wh
|
||||
find_package(plutovg 1.1.0 REQUIRED)
|
||||
find_package(plutosvg 0.0.7 REQUIRED)
|
||||
find_package(ryml REQUIRED)
|
||||
if (WIN32)
|
||||
find_package(DirectX-Headers 1.618.1 REQUIRED)
|
||||
endif()
|
||||
|
||||
if(USE_VULKAN)
|
||||
find_package(Shaderc REQUIRED)
|
||||
|
||||
@@ -98,14 +98,25 @@ __ri void Log::WriteToConsole(LOGLEVEL level, ConsoleColors color, std::string_v
|
||||
|
||||
static constexpr size_t BUFFER_SIZE = 512;
|
||||
|
||||
#ifdef _WIN32
|
||||
constexpr bool supports_color_codes = true;
|
||||
#else
|
||||
const int fd = (level <= LOGLEVEL_WARNING) ? STDERR_FILENO : STDOUT_FILENO;
|
||||
const bool supports_color_codes = static_cast<bool>(isatty(fd));
|
||||
#endif
|
||||
|
||||
SmallStackString<BUFFER_SIZE> buffer;
|
||||
buffer.reserve(static_cast<u32>(32 + message.length()));
|
||||
buffer.append(s_ansi_color_codes[color]);
|
||||
if (supports_color_codes)
|
||||
buffer.append(s_ansi_color_codes[color]);
|
||||
|
||||
if (s_log_timestamps)
|
||||
buffer.append_format(TIMESTAMP_FORMAT_STRING, Log::GetCurrentMessageTime());
|
||||
|
||||
buffer.append(message);
|
||||
|
||||
if (supports_color_codes)
|
||||
buffer.append(s_ansi_color_codes[Color_Default]);
|
||||
buffer.append('\n');
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -134,7 +145,6 @@ cleanup:
|
||||
if (wmessage_buf != wbuf)
|
||||
std::free(wmessage_buf);
|
||||
#else
|
||||
const int fd = (level <= LOGLEVEL_WARNING) ? STDERR_FILENO : STDOUT_FILENO;
|
||||
write(fd, buffer.data(), buffer.length());
|
||||
#endif
|
||||
}
|
||||
@@ -157,7 +167,7 @@ void Log::SetConsoleOutputLevel(LOGLEVEL level)
|
||||
if (was_enabled == now_enabled)
|
||||
return;
|
||||
|
||||
// Worst that happens here is we write to a bad handle..
|
||||
// Worst that happens here is we write to a bad handle..
|
||||
|
||||
#if defined(_WIN32)
|
||||
static constexpr auto enable_virtual_terminal_processing = [](HANDLE hConsole) {
|
||||
|
||||
@@ -56,6 +56,11 @@ bool HTTPDownloaderWinHttp::Initialize(std::string user_agent)
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD timeout_ms = 15000;
|
||||
WinHttpSetOption(m_hSession, WINHTTP_OPTION_CONNECT_TIMEOUT, &timeout_ms, sizeof(timeout_ms));
|
||||
WinHttpSetOption(m_hSession, WINHTTP_OPTION_SEND_TIMEOUT, &timeout_ms, sizeof(timeout_ms));
|
||||
WinHttpSetOption(m_hSession, WINHTTP_OPTION_RECEIVE_TIMEOUT, &timeout_ms, sizeof(timeout_ms));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -290,10 +295,13 @@ bool HTTPDownloaderWinHttp::StartRequest(HTTPDownloader::Request* request)
|
||||
req->status_code = HTTP_STATUS_ERROR;
|
||||
req->state.store(Request::State::Complete);
|
||||
}
|
||||
else
|
||||
{
|
||||
DevCon.WriteLn("Started HTTP request for '%s'", req->url.c_str());
|
||||
req->state = Request::State::Started;
|
||||
req->start_time = Common::Timer::GetCurrentValue();
|
||||
}
|
||||
|
||||
DevCon.WriteLn("Started HTTP request for '%s'", req->url.c_str());
|
||||
req->state = Request::State::Started;
|
||||
req->start_time = Common::Timer::GetCurrentValue();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,8 +30,6 @@ namespace x86Emitter
|
||||
extern void EmitRex(SIMDInstructionInfo info, const xRegisterBase& reg1, const xRegisterBase& reg2);
|
||||
extern void EmitRex(SIMDInstructionInfo info, const xRegisterBase& reg1, const xIndirectVoid& sib);
|
||||
|
||||
extern void _xMovRtoR(const xRegisterInt& to, const xRegisterInt& from);
|
||||
|
||||
template <typename T>
|
||||
inline void xWrite(T val)
|
||||
{
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
namespace x86Emitter
|
||||
{
|
||||
|
||||
void _xMovRtoR(const xRegisterInt& to, const xRegisterInt& from)
|
||||
void xImpl_Mov::operator()(const xRegisterInt& to, const xRegisterInt& from) const
|
||||
{
|
||||
pxAssert(to.GetOperandSize() == from.GetOperandSize());
|
||||
|
||||
@@ -32,12 +32,6 @@ namespace x86Emitter
|
||||
xOpWrite(from.GetPrefix16(), from.Is8BitOp() ? 0x88 : 0x89, from, to);
|
||||
}
|
||||
|
||||
void xImpl_Mov::operator()(const xRegisterInt& to, const xRegisterInt& from) const
|
||||
{
|
||||
// FIXME WTF?
|
||||
_xMovRtoR(to, from);
|
||||
}
|
||||
|
||||
void xImpl_Mov::operator()(const xIndirectVoid& dest, const xRegisterInt& from) const
|
||||
{
|
||||
// mov eax has a special from when writing directly to a DISP32 address
|
||||
|
||||
@@ -1045,7 +1045,7 @@ const xRegister32
|
||||
}
|
||||
else if (displacement_size == 0)
|
||||
{
|
||||
_xMovRtoR(to, src.Index.MatchSizeTo(to));
|
||||
xMOV(to, src.Index.MatchSizeTo(to));
|
||||
return;
|
||||
}
|
||||
else if (!preserve_flags)
|
||||
@@ -1053,7 +1053,7 @@ const xRegister32
|
||||
// encode as MOV and ADD combo. Make sure to use the immediate on the
|
||||
// ADD since it can encode as an 8-bit sign-extended value.
|
||||
|
||||
_xMovRtoR(to, src.Index.MatchSizeTo(to));
|
||||
xMOV(to, src.Index.MatchSizeTo(to));
|
||||
xADD(to, src.Displacement);
|
||||
return;
|
||||
}
|
||||
@@ -1071,7 +1071,7 @@ const xRegister32
|
||||
// (this does not apply to older model P4s with the broken barrel shifter,
|
||||
// but we currently aren't optimizing for that target anyway).
|
||||
|
||||
_xMovRtoR(to, src.Index);
|
||||
xMOV(to, src.Index);
|
||||
xSHL(to, src.Scale);
|
||||
return;
|
||||
}
|
||||
@@ -1085,14 +1085,14 @@ const xRegister32
|
||||
if (src.Index == rsp)
|
||||
{
|
||||
// ESP is not encodable as an index (ix86 ignores it), thus:
|
||||
_xMovRtoR(to, src.Base.MatchSizeTo(to)); // will do the trick!
|
||||
xMOV(to, src.Base.MatchSizeTo(to)); // will do the trick!
|
||||
if (src.Displacement)
|
||||
xADD(to, src.Displacement);
|
||||
return;
|
||||
}
|
||||
else if (src.Displacement == 0)
|
||||
{
|
||||
_xMovRtoR(to, src.Base.MatchSizeTo(to));
|
||||
xMOV(to, src.Base.MatchSizeTo(to));
|
||||
xADD(to, src.Index.MatchSizeTo(to));
|
||||
return;
|
||||
}
|
||||
@@ -1102,7 +1102,7 @@ const xRegister32
|
||||
// special case handling of ESP as Index, which is replaceable with
|
||||
// a single MOV even when preserve_flags is set! :D
|
||||
|
||||
_xMovRtoR(to, src.Base.MatchSizeTo(to));
|
||||
xMOV(to, src.Base.MatchSizeTo(to));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<DepsRootDir Condition="'$(Platform)'=='x64'">$(SolutionDir)deps\</DepsRootDir>
|
||||
<DepsRootDir Condition="'$(Platform)'=='ARM64'">$(SolutionDir)deps-arm64\</DepsRootDir>
|
||||
<DepsBinDir>$(DepsRootDir)bin\</DepsBinDir>
|
||||
<DepsLibDir>$(DepsRootDir)lib\</DepsLibDir>
|
||||
<DepsIncludeDir>$(DepsRootDir)include\</DepsIncludeDir>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -1,5 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="DepsDir.props" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros">
|
||||
<SSEString />
|
||||
<SSEString Condition="$(Configuration.Contains(AVX2))">-avx2</SSEString>
|
||||
@@ -15,11 +18,6 @@
|
||||
<PropertyGroup>
|
||||
<OutDir>$(SolutionDir)build\lib-$(PlatformName)-$(Configuration)\</OutDir>
|
||||
<IntDir>$(SolutionDir)build\obj-$(ProjectName)-$(PlatformName)-$(Configuration)\</IntDir>
|
||||
<DepsRootDir Condition="'$(Platform)'=='x64'">$(SolutionDir)deps\</DepsRootDir>
|
||||
<DepsRootDir Condition="'$(Platform)'=='ARM64'">$(SolutionDir)deps-arm64\</DepsRootDir>
|
||||
<DepsBinDir>$(DepsRootDir)bin\</DepsBinDir>
|
||||
<DepsLibDir>$(DepsRootDir)lib\</DepsLibDir>
|
||||
<DepsIncludeDir>$(DepsRootDir)include\</DepsIncludeDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
|
||||
@@ -91,6 +91,9 @@ static double s_last_barriers = 0;
|
||||
static double s_last_copies = 0;
|
||||
static double s_last_uploads = 0;
|
||||
static double s_last_readbacks = 0;
|
||||
static double s_last_depth_copies_rov = 0;
|
||||
static double s_last_draws_rov = 0;
|
||||
static double s_last_barriers_rov = 0;
|
||||
static u64 s_total_internal_draws = 0;
|
||||
static u64 s_total_draws = 0;
|
||||
static u64 s_total_render_passes = 0;
|
||||
@@ -98,6 +101,9 @@ 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_draws_rov = 0;
|
||||
static u64 s_total_barriers_rov = 0;
|
||||
static u32 s_total_frames = 0;
|
||||
static u32 s_total_drawn_frames = 0;
|
||||
|
||||
@@ -216,6 +222,11 @@ bool Host::CopyTextToClipboard(const std::string_view text)
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string Host::GetTextFromClipboard()
|
||||
{
|
||||
return std::string();
|
||||
}
|
||||
|
||||
void Host::BeginTextInput()
|
||||
{
|
||||
// noop
|
||||
@@ -287,6 +298,9 @@ 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::DrawCallsROV, s_total_draws_rov, s_last_draws_rov);
|
||||
update_stat(GSPerfMon::BarriersROV, s_total_barriers_rov, s_last_barriers_rov);
|
||||
|
||||
const bool idle_frame = s_total_frames && (last_draws == s_total_internal_draws && last_uploads == s_total_uploads);
|
||||
|
||||
@@ -419,16 +433,6 @@ void Host::OnAchievementsRefreshed()
|
||||
// noop
|
||||
}
|
||||
|
||||
void Host::OnCoverDownloaderOpenRequested()
|
||||
{
|
||||
// noop
|
||||
}
|
||||
|
||||
void Host::OnCreateMemoryCardOpenRequested()
|
||||
{
|
||||
// noop
|
||||
}
|
||||
|
||||
bool Host::InBatchMode()
|
||||
{
|
||||
return false;
|
||||
@@ -913,6 +917,9 @@ 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@ 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)
|
||||
{
|
||||
Console.WriteLn(fmt::format("@HWSTAT@ Minimum Frame Time: {:.3f} ms ({:.3f} FPS)", PerformanceMetrics::GetMinimumFrameTime(), 1000.0f / PerformanceMetrics::GetMinimumFrameTime()));
|
||||
|
||||
@@ -567,7 +567,6 @@ bool NewParameterVariableDialog::parseUserInput()
|
||||
return;
|
||||
}
|
||||
|
||||
std::variant<ccc::RegisterStorage, ccc::StackStorage> storage;
|
||||
switch (storageType())
|
||||
{
|
||||
case GLOBAL_STORAGE:
|
||||
@@ -577,13 +576,13 @@ bool NewParameterVariableDialog::parseUserInput()
|
||||
}
|
||||
case REGISTER_STORAGE:
|
||||
{
|
||||
ccc::RegisterStorage& register_storage = storage.emplace<ccc::RegisterStorage>();
|
||||
ccc::RegisterStorage& register_storage = m_storage.emplace<ccc::RegisterStorage>();
|
||||
register_storage.dbx_register_number = m_ui.registerComboBox->currentIndex();
|
||||
break;
|
||||
}
|
||||
case STACK_STORAGE:
|
||||
{
|
||||
ccc::StackStorage& stack_storage = storage.emplace<ccc::StackStorage>();
|
||||
ccc::StackStorage& stack_storage = m_storage.emplace<ccc::StackStorage>();
|
||||
stack_storage.stack_pointer_offset = m_ui.stackPointerOffsetSpinBox->value();
|
||||
|
||||
// Convert to caller sp relative.
|
||||
|
||||
@@ -654,6 +654,9 @@ void GameListWidget::refreshGridCovers()
|
||||
|
||||
void GameListWidget::showGameList()
|
||||
{
|
||||
Host::SetBaseBoolSettingValue("UI", "GameListGridView", false);
|
||||
Host::CommitBaseSettingChanges();
|
||||
|
||||
if (m_ui.stack->currentIndex() == 0 || m_model->rowCount() == 0)
|
||||
{
|
||||
// We can click the toolbar multiple times, so keep it correct.
|
||||
@@ -661,8 +664,6 @@ void GameListWidget::showGameList()
|
||||
return;
|
||||
}
|
||||
|
||||
Host::SetBaseBoolSettingValue("UI", "GameListGridView", false);
|
||||
Host::CommitBaseSettingChanges();
|
||||
m_ui.stack->setCurrentIndex(0);
|
||||
setFocusProxy(m_ui.stack->currentWidget());
|
||||
resizeTableViewColumnsToFit();
|
||||
@@ -672,6 +673,9 @@ void GameListWidget::showGameList()
|
||||
|
||||
void GameListWidget::showGameGrid()
|
||||
{
|
||||
Host::SetBaseBoolSettingValue("UI", "GameListGridView", true);
|
||||
Host::CommitBaseSettingChanges();
|
||||
|
||||
if (m_ui.stack->currentIndex() == 1 || m_model->rowCount() == 0)
|
||||
{
|
||||
// We can click the toolbar multiple times, so keep it correct.
|
||||
@@ -679,8 +683,6 @@ void GameListWidget::showGameGrid()
|
||||
return;
|
||||
}
|
||||
|
||||
Host::SetBaseBoolSettingValue("UI", "GameListGridView", true);
|
||||
Host::CommitBaseSettingChanges();
|
||||
m_ui.stack->setCurrentIndex(1);
|
||||
setFocusProxy(m_ui.stack->currentWidget());
|
||||
updateToolbar();
|
||||
@@ -703,7 +705,7 @@ void GameListWidget::setShowCoverTitles(bool enabled)
|
||||
|
||||
void GameListWidget::updateToolbar()
|
||||
{
|
||||
const bool grid_view = isShowingGameGrid();
|
||||
const bool grid_view = Host::GetBaseBoolSettingValue("UI", "GameListGridView", false);
|
||||
{
|
||||
QSignalBlocker sb(m_ui.viewGameGrid);
|
||||
m_ui.viewGameGrid->setChecked(grid_view);
|
||||
|
||||
@@ -36,6 +36,9 @@
|
||||
#include "pcsx2/ImGui/FullscreenUI.h"
|
||||
#include "pcsx2/MTGS.h"
|
||||
#include "pcsx2/PerformanceMetrics.h"
|
||||
#include "pcsx2/VMManager.h"
|
||||
#include "pcsx2/ImGui/ImGuiOverlays.h"
|
||||
#include "pcsx2/SPU2/spu2.h"
|
||||
#include "pcsx2/Recording/InputRecording.h"
|
||||
#include "pcsx2/Recording/InputRecordingControls.h"
|
||||
#include "pcsx2/SaveState.h"
|
||||
@@ -54,8 +57,11 @@
|
||||
#include <QtWidgets/QInputDialog>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
#include <QtWidgets/QProgressBar>
|
||||
#include <QtWidgets/QSlider>
|
||||
#include <QtWidgets/QStyle>
|
||||
#include <QtWidgets/QStyleFactory>
|
||||
#include <QtWidgets/QWidgetAction>
|
||||
#include <QtWidgets/QHBoxLayout>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "common/RedtapeWindows.h"
|
||||
@@ -147,6 +153,14 @@ void MainWindow::initialize()
|
||||
switchToGameListView();
|
||||
updateWindowTitle();
|
||||
updateGameDependentActions();
|
||||
updateEmulationActions(false, s_vm_valid, false);
|
||||
updateStatusBarWidgetVisibility();
|
||||
|
||||
if (s_vm_paused)
|
||||
{
|
||||
m_ui.actionPause->setChecked(true);
|
||||
m_ui.actionToolbarPause->setChecked(true);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
registerForDeviceNotifications();
|
||||
@@ -185,6 +199,7 @@ void MainWindow::setupAdditionalUi()
|
||||
{
|
||||
makeIconsMasks(menuBar());
|
||||
updateAdvancedSettingsVisibility();
|
||||
setupStatusBarWidgets();
|
||||
|
||||
const bool toolbar_visible = Host::GetBaseBoolSettingValue("UI", "ShowToolbar", false);
|
||||
m_ui.actionViewToolbar->setChecked(toolbar_visible);
|
||||
@@ -195,58 +210,6 @@ void MainWindow::setupAdditionalUi()
|
||||
m_ui.toolBar->setMovable(!toolbars_locked);
|
||||
m_ui.toolBar->setContextMenuPolicy(Qt::PreventContextMenu);
|
||||
|
||||
const bool status_bar_visible = Host::GetBaseBoolSettingValue("UI", "ShowStatusBar", true);
|
||||
m_ui.actionViewStatusBar->setChecked(status_bar_visible);
|
||||
m_ui.statusBar->setVisible(status_bar_visible);
|
||||
|
||||
const bool show_game_grid = Host::GetBaseBoolSettingValue("UI", "GameListGridView", false);
|
||||
updateGameGridActions(show_game_grid);
|
||||
|
||||
m_game_list_widget = new GameListWidget(getContentParent());
|
||||
m_game_list_widget->initialize();
|
||||
m_ui.actionGridViewShowTitles->setChecked(m_game_list_widget->getShowGridCoverTitles());
|
||||
m_ui.mainContainer->addWidget(m_game_list_widget);
|
||||
|
||||
m_status_progress_widget = new QProgressBar(m_ui.statusBar);
|
||||
m_status_progress_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
|
||||
m_status_progress_widget->setFixedSize(140, 16);
|
||||
m_status_progress_widget->setMinimum(0);
|
||||
m_status_progress_widget->setMaximum(100);
|
||||
m_status_progress_widget->hide();
|
||||
|
||||
m_status_verbose_widget = new QLabel(m_ui.statusBar);
|
||||
m_status_verbose_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
m_status_verbose_widget->setFixedHeight(16);
|
||||
m_status_verbose_widget->hide();
|
||||
|
||||
m_status_renderer_widget = new QLabel(m_ui.statusBar);
|
||||
m_status_renderer_widget->setFixedHeight(16);
|
||||
m_status_renderer_widget->setFixedSize(65, 16);
|
||||
m_status_renderer_widget->hide();
|
||||
|
||||
m_status_resolution_widget = new QLabel(m_ui.statusBar);
|
||||
m_status_resolution_widget->setFixedHeight(16);
|
||||
m_status_resolution_widget->setFixedSize(75, 16);
|
||||
m_status_resolution_widget->hide();
|
||||
|
||||
m_status_fps_widget = new QLabel(m_ui.statusBar);
|
||||
m_status_fps_widget->setFixedHeight(16);
|
||||
m_status_fps_widget->setMinimumWidth(60);
|
||||
m_status_fps_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
|
||||
m_status_fps_widget->hide();
|
||||
|
||||
m_status_vps_widget = new QLabel(m_ui.statusBar);
|
||||
m_status_vps_widget->setFixedHeight(16);
|
||||
m_status_vps_widget->setMinimumWidth(60);
|
||||
m_status_vps_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
|
||||
m_status_vps_widget->hide();
|
||||
|
||||
m_status_speed_widget = new QLabel(m_ui.statusBar);
|
||||
m_status_speed_widget->setFixedHeight(16);
|
||||
m_status_speed_widget->setMinimumWidth(130);
|
||||
m_status_speed_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
|
||||
m_status_speed_widget->hide();
|
||||
|
||||
m_settings_toolbar_menu = new QMenu(m_ui.toolBar);
|
||||
m_settings_toolbar_menu->addAction(m_ui.actionSettings);
|
||||
m_settings_toolbar_menu->addAction(m_ui.actionViewGameProperties);
|
||||
@@ -257,6 +220,14 @@ void MainWindow::setupAdditionalUi()
|
||||
connect(action, &QAction::triggered, [scale]() { g_emu_thread->requestDisplaySize(static_cast<float>(scale)); });
|
||||
}
|
||||
|
||||
const bool show_game_grid = Host::GetBaseBoolSettingValue("UI", "GameListGridView", false);
|
||||
updateGameGridActions(show_game_grid);
|
||||
|
||||
m_game_list_widget = new GameListWidget(getContentParent());
|
||||
m_game_list_widget->initialize();
|
||||
m_ui.actionGridViewShowTitles->setChecked(m_game_list_widget->getShowGridCoverTitles());
|
||||
m_ui.mainContainer->addWidget(m_game_list_widget);
|
||||
|
||||
updateEmulationActions(false, false, false);
|
||||
updateDisplayRelatedActions(false, false, false);
|
||||
|
||||
@@ -292,6 +263,216 @@ void MainWindow::setupAdditionalUi()
|
||||
#endif
|
||||
}
|
||||
|
||||
void MainWindow::setupStatusBarWidgets()
|
||||
{
|
||||
const bool status_bar_visible = Host::GetBaseBoolSettingValue("UI", "ShowStatusBar", true);
|
||||
m_ui.actionViewStatusBar->setChecked(status_bar_visible);
|
||||
m_ui.statusBar->setVisible(status_bar_visible);
|
||||
|
||||
m_status_progress_widget = new QProgressBar(m_ui.statusBar);
|
||||
m_status_progress_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
|
||||
m_status_progress_widget->setFixedSize(140, 16);
|
||||
m_status_progress_widget->setMinimum(0);
|
||||
m_status_progress_widget->setMaximum(100);
|
||||
m_status_progress_widget->hide();
|
||||
|
||||
m_status_verbose_widget = new QLabel(m_ui.statusBar);
|
||||
m_status_verbose_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
m_status_verbose_widget->setContentsMargins(10, 0, 10, 0);
|
||||
m_status_verbose_widget->setFixedHeight(20);
|
||||
m_status_verbose_widget->setMinimumWidth(20);
|
||||
m_status_verbose_widget->hide();
|
||||
|
||||
m_status_renderer_widget = new QLabel(m_ui.statusBar);
|
||||
m_status_renderer_widget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
|
||||
m_status_renderer_widget->setContentsMargins(10, 0, 10, 0);
|
||||
m_status_renderer_widget->setAlignment(Qt::AlignCenter);
|
||||
m_status_renderer_widget->setFixedHeight(20);
|
||||
m_status_renderer_widget->hide();
|
||||
|
||||
m_status_resolution_widget = new QLabel(m_ui.statusBar);
|
||||
m_status_resolution_widget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
|
||||
m_status_resolution_widget->setContentsMargins(10, 0, 10, 0);
|
||||
m_status_resolution_widget->setAlignment(Qt::AlignCenter);
|
||||
m_status_resolution_widget->setFixedHeight(20);
|
||||
m_status_resolution_widget->hide();
|
||||
|
||||
m_status_volume_widget = new QToolButton(m_ui.statusBar);
|
||||
m_status_volume_widget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
|
||||
m_status_volume_widget->setContentsMargins(10, 0, 10, 0);
|
||||
m_status_volume_widget->setAutoRaise(true);
|
||||
m_status_volume_widget->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
||||
m_status_volume_widget->setIcon(QIcon::fromTheme(QStringLiteral("volume-up-line")));
|
||||
m_status_volume_widget->setPopupMode(QToolButton::InstantPopup);
|
||||
m_status_volume_widget->setFixedHeight(20);
|
||||
|
||||
m_status_volume_menu = new QMenu(m_status_volume_widget);
|
||||
|
||||
m_status_volume_per_game_action = new QAction(tr("Adjust Per-Game"), m_status_volume_menu);
|
||||
m_status_volume_per_game_action->setCheckable(true);
|
||||
|
||||
connect(m_status_volume_menu, &QMenu::aboutToShow, this, [this]() {
|
||||
auto lock = Host::GetSettingsLock();
|
||||
SettingsInterface* game_layer = Host::Internal::GetGameSettingsLayer();
|
||||
m_status_volume_per_game_action->setEnabled(game_layer != nullptr && QtHost::IsVMValid());
|
||||
m_status_volume_per_game_action->setChecked(game_layer &&
|
||||
(game_layer->ContainsValue("SPU2/Output", "StandardVolume") || game_layer->ContainsValue("SPU2/Output", "OutputMuted")));
|
||||
});
|
||||
|
||||
connect(m_status_volume_per_game_action, &QAction::triggered, this, [this](bool checked) {
|
||||
m_status_volume_slider_applied = true;
|
||||
applyStatusBarVolumeChanges(std::nullopt, false, checked);
|
||||
});
|
||||
|
||||
m_status_volume_toggle_mute_action = new QAction(tr("Toggle Mute"), m_status_volume_menu);
|
||||
m_status_volume_toggle_mute_action->setIcon(QIcon::fromTheme(QStringLiteral("volume-mute-line")));
|
||||
|
||||
connect(m_status_volume_toggle_mute_action, &QAction::triggered, this, [this]() {
|
||||
m_status_volume_slider_applied = true;
|
||||
applyStatusBarVolumeChanges(std::nullopt, true, std::nullopt);
|
||||
});
|
||||
|
||||
m_status_volume_menu->addAction(m_status_volume_per_game_action);
|
||||
m_status_volume_menu->addAction(m_status_volume_toggle_mute_action);
|
||||
m_status_volume_menu->addSeparator();
|
||||
|
||||
m_status_volume_slider = new QSlider(Qt::Horizontal, m_status_volume_menu);
|
||||
m_status_volume_slider->setRange(0, 100);
|
||||
m_status_volume_slider->setFixedWidth(120);
|
||||
m_status_volume_slider->setValue(Host::GetIntSettingValue("SPU2/Output", "StandardVolume", 100));
|
||||
connect(m_status_volume_slider, &QSlider::valueChanged, this, [this](int value) {
|
||||
Host::RunOnCPUThread([value]() {
|
||||
if (!VMManager::HasValidVM())
|
||||
return;
|
||||
EmuConfig.SPU2.StandardVolume = static_cast<u32>(value);
|
||||
SPU2::SetOutputVolume(static_cast<u32>(value));
|
||||
});
|
||||
if (m_status_volume_slider->isSliderDown())
|
||||
setStatusVolumeText(m_status_volume_muted ? tr("Volume: Muted") : tr("Volume: %1%").arg(value), value, m_status_volume_muted);
|
||||
});
|
||||
connect(m_status_volume_slider, &QSlider::sliderReleased, this, [this]() {
|
||||
m_status_volume_slider_applied = true;
|
||||
applyStatusBarVolumeChanges(m_status_volume_slider->value(), false);
|
||||
});
|
||||
connect(m_status_volume_menu, &QMenu::aboutToHide, this, [this]() {
|
||||
if (!m_status_volume_slider_applied)
|
||||
applyStatusBarVolumeChanges(m_status_volume_slider->value(), false);
|
||||
m_status_volume_slider_applied = false;
|
||||
});
|
||||
|
||||
QWidget* container = new QWidget(m_status_volume_menu);
|
||||
QHBoxLayout* layout = new QHBoxLayout(container);
|
||||
layout->setContentsMargins(8, 4, 8, 4);
|
||||
layout->addWidget(m_status_volume_slider);
|
||||
|
||||
QWidgetAction* slider_action = new QWidgetAction(m_status_volume_menu);
|
||||
slider_action->setDefaultWidget(container);
|
||||
m_status_volume_menu->addAction(slider_action);
|
||||
m_status_volume_widget->setMenu(m_status_volume_menu);
|
||||
m_status_volume_widget->hide();
|
||||
|
||||
m_status_speed_widget = new QToolButton(m_ui.statusBar);
|
||||
m_status_speed_widget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
|
||||
m_status_speed_widget->setContentsMargins(10, 0, 10, 0);
|
||||
m_status_speed_widget->setAutoRaise(true);
|
||||
m_status_speed_widget->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
||||
m_status_speed_widget->setPopupMode(QToolButton::InstantPopup);
|
||||
m_status_speed_widget->setFixedHeight(20);
|
||||
m_status_speed_widget->hide();
|
||||
|
||||
m_status_speed_menu = new QMenu(m_status_speed_widget);
|
||||
m_status_speed_menu->addAction(tr("Unlimited"), []() { Host::RunOnCPUThread([]() { VMManager::SetLimiterMode(LimiterModeType::Unlimited); }); });
|
||||
m_status_speed_menu->addAction(tr("Turbo"), []() { Host::RunOnCPUThread([]() { VMManager::SetLimiterMode(LimiterModeType::Turbo); }); });
|
||||
m_status_speed_menu->addAction(tr("Slow-Motion"), []() { Host::RunOnCPUThread([]() { VMManager::SetLimiterMode(LimiterModeType::Slomo); }); });
|
||||
m_status_speed_menu->addAction(tr("Normal"), []() { Host::RunOnCPUThread([]() { VMManager::SetLimiterMode(LimiterModeType::Nominal); }); });
|
||||
m_status_speed_widget->setMenu(m_status_speed_menu);
|
||||
m_status_speed_widget->hide();
|
||||
|
||||
m_status_gpu_widget = new QLabel(m_ui.statusBar);
|
||||
m_status_gpu_widget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
|
||||
m_status_gpu_widget->setContentsMargins(10, 0, 10, 0);
|
||||
m_status_gpu_widget->setAlignment(Qt::AlignCenter);
|
||||
m_status_gpu_widget->setFixedHeight(20);
|
||||
m_status_gpu_widget->hide();
|
||||
|
||||
m_status_fps_widget = new QLabel(m_ui.statusBar);
|
||||
m_status_fps_widget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
|
||||
m_status_fps_widget->setContentsMargins(10, 0, 10, 0);
|
||||
m_status_fps_widget->setAlignment(Qt::AlignCenter);
|
||||
m_status_fps_widget->setFixedHeight(20);
|
||||
m_status_fps_widget->hide();
|
||||
|
||||
m_status_vps_widget = new QLabel(m_ui.statusBar);
|
||||
m_status_vps_widget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
|
||||
m_status_vps_widget->setContentsMargins(10, 0, 10, 0);
|
||||
m_status_vps_widget->setAlignment(Qt::AlignCenter);
|
||||
m_status_vps_widget->setFixedHeight(20);
|
||||
m_status_vps_widget->hide();
|
||||
}
|
||||
|
||||
void MainWindow::applyStatusBarVolumeChanges(std::optional<int> volume, bool toggle_mute, std::optional<bool> override_per_game)
|
||||
{
|
||||
const bool use_per_game = override_per_game.value_or(m_status_volume_per_game_action->isChecked());
|
||||
Host::RunOnCPUThread([this, volume, toggle_mute, use_per_game]() {
|
||||
int target_vol = 100;
|
||||
bool target_mute = false;
|
||||
|
||||
{
|
||||
auto lock = Host::GetSettingsLock();
|
||||
SettingsInterface* per_game_settings = Host::Internal::GetGameSettingsLayer();
|
||||
SettingsInterface* global_settings = Host::Internal::GetBaseSettingsLayer();
|
||||
SettingsInterface* active_setting = (use_per_game && per_game_settings) ? per_game_settings : global_settings;
|
||||
|
||||
if (active_setting)
|
||||
{
|
||||
if (!active_setting->GetIntValue("SPU2/Output", "StandardVolume", &target_vol) && global_settings)
|
||||
global_settings->GetIntValue("SPU2/Output", "StandardVolume", &target_vol);
|
||||
if (!active_setting->GetBoolValue("SPU2/Output", "OutputMuted", &target_mute) && global_settings)
|
||||
global_settings->GetBoolValue("SPU2/Output", "OutputMuted", &target_mute);
|
||||
}
|
||||
|
||||
target_vol = volume.value_or(target_vol);
|
||||
if (toggle_mute)
|
||||
target_mute = !(VMManager::HasValidVM() ? SPU2::IsOutputMuted() : target_mute);
|
||||
|
||||
if (active_setting)
|
||||
{
|
||||
active_setting->SetIntValue("SPU2/Output", "StandardVolume", target_vol);
|
||||
active_setting->SetBoolValue("SPU2/Output", "OutputMuted", target_mute);
|
||||
}
|
||||
|
||||
if (per_game_settings && active_setting == per_game_settings)
|
||||
QtHost::SaveGameSettings(per_game_settings, true);
|
||||
else
|
||||
{
|
||||
if (per_game_settings)
|
||||
{
|
||||
const bool had_per_game_volume =
|
||||
per_game_settings->ContainsValue("SPU2/Output", "StandardVolume") ||
|
||||
per_game_settings->ContainsValue("SPU2/Output", "OutputMuted");
|
||||
per_game_settings->DeleteValue("SPU2/Output", "StandardVolume");
|
||||
per_game_settings->DeleteValue("SPU2/Output", "OutputMuted");
|
||||
if (had_per_game_volume)
|
||||
QtHost::SaveGameSettings(per_game_settings, true);
|
||||
}
|
||||
Host::CommitBaseSettingChanges();
|
||||
}
|
||||
}
|
||||
|
||||
if (VMManager::HasValidVM())
|
||||
{
|
||||
EmuConfig.SPU2.StandardVolume = static_cast<u32>(target_vol);
|
||||
EmuConfig.SPU2.OutputMuted = target_mute;
|
||||
SPU2::SetOutputMuted(target_mute);
|
||||
SPU2::SetOutputVolume(static_cast<u32>(target_vol));
|
||||
}
|
||||
|
||||
QTimer::singleShot(0, this, [this, target_vol, target_mute]() {
|
||||
setStatusVolumeText(target_mute ? tr("Volume: Muted") : tr("Volume: %1%").arg(target_vol), target_vol, target_mute);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void MainWindow::connectSignals()
|
||||
{
|
||||
connect(m_ui.actionStartFile, &QAction::triggered, this, &MainWindow::onStartFileActionTriggered);
|
||||
@@ -454,8 +635,6 @@ void MainWindow::connectVMThreadSignals(EmuThread* thread)
|
||||
connect(thread, &EmuThread::onCaptureStopped, this, &MainWindow::onCaptureStopped);
|
||||
connect(thread, &EmuThread::onAchievementsLoginRequested, this, &MainWindow::onAchievementsLoginRequested);
|
||||
connect(thread, &EmuThread::onAchievementsHardcoreModeChanged, this, &MainWindow::onAchievementsHardcoreModeChanged);
|
||||
connect(thread, &EmuThread::onCoverDownloaderOpenRequested, this, &MainWindow::onToolsCoverDownloaderTriggered);
|
||||
connect(thread, &EmuThread::onCreateMemoryCardOpenRequested, this, &MainWindow::onCreateMemoryCardOpenRequested);
|
||||
|
||||
connect(m_ui.actionReset, &QAction::triggered, this, &MainWindow::requestReset);
|
||||
connect(m_ui.actionPause, &QAction::toggled, thread, &EmuThread::setVMPaused);
|
||||
@@ -498,12 +677,11 @@ void MainWindow::createRendererSwitchMenu()
|
||||
QString::fromUtf8(Pcsx2Config::GSOptions::GetRendererName(renderer)), switch_renderer_group);
|
||||
action->setCheckable(true);
|
||||
action->setChecked(current_renderer == renderer);
|
||||
connect(action,
|
||||
&QAction::triggered, [renderer] {
|
||||
Host::SetBaseIntSettingValue("EmuCore/GS", "Renderer", static_cast<int>(renderer));
|
||||
Host::CommitBaseSettingChanges();
|
||||
g_emu_thread->applySettings();
|
||||
});
|
||||
connect(action, &QAction::triggered, [renderer] {
|
||||
Host::SetBaseIntSettingValue("EmuCore/GS", "Renderer", static_cast<int>(renderer));
|
||||
Host::CommitBaseSettingChanges();
|
||||
g_emu_thread->applySettings();
|
||||
});
|
||||
}
|
||||
|
||||
m_ui.menuDebugSwitchRenderer->addActions(switch_renderer_group->actions());
|
||||
@@ -550,6 +728,9 @@ void MainWindow::recreate()
|
||||
g_main_window->updateEmulationActions(false, s_vm_valid, false);
|
||||
g_main_window->onFullscreenUIStateChange(g_emu_thread->isRunningFullscreenUI());
|
||||
}
|
||||
|
||||
if (s_vm_valid)
|
||||
g_emu_thread->updatePerformanceMetrics(true);
|
||||
}
|
||||
|
||||
void MainWindow::recreateSettings()
|
||||
@@ -1021,13 +1202,15 @@ void MainWindow::updateDisplayRelatedActions(bool has_surface, bool render_to_ma
|
||||
void MainWindow::updateStatusBarWidgetVisibility()
|
||||
{
|
||||
auto Update = [this](QWidget* widget, bool visible, int stretch) {
|
||||
if (widget->isVisible() == visible)
|
||||
return;
|
||||
|
||||
if (widget->isVisible())
|
||||
{
|
||||
m_ui.statusBar->removeWidget(widget);
|
||||
widget->hide();
|
||||
}
|
||||
|
||||
if (visible)
|
||||
else
|
||||
{
|
||||
m_ui.statusBar->addPermanentWidget(widget, stretch);
|
||||
widget->show();
|
||||
@@ -1037,9 +1220,63 @@ void MainWindow::updateStatusBarWidgetVisibility()
|
||||
Update(m_status_verbose_widget, s_vm_valid, 1);
|
||||
Update(m_status_renderer_widget, s_vm_valid, 0);
|
||||
Update(m_status_resolution_widget, s_vm_valid, 0);
|
||||
Update(m_status_gpu_widget, s_vm_valid, 0);
|
||||
Update(m_status_fps_widget, s_vm_valid, 0);
|
||||
Update(m_status_vps_widget, s_vm_valid, 0);
|
||||
Update(m_status_speed_widget, s_vm_valid, 0);
|
||||
Update(m_status_volume_widget, s_vm_valid, 0);
|
||||
}
|
||||
|
||||
void MainWindow::setStatusVerboseText(const QString& text)
|
||||
{
|
||||
m_status_verbose_widget->setText(text);
|
||||
}
|
||||
|
||||
void MainWindow::setStatusRendererText(const QString& text)
|
||||
{
|
||||
m_status_renderer_widget->setText(text);
|
||||
}
|
||||
|
||||
void MainWindow::setStatusResolutionText(const QString& text)
|
||||
{
|
||||
m_status_resolution_widget->setText(text);
|
||||
}
|
||||
|
||||
void MainWindow::setStatusVolumeText(const QString& text, int volume, bool muted)
|
||||
{
|
||||
m_status_volume_widget->setText(text);
|
||||
if (muted != m_status_volume_muted)
|
||||
{
|
||||
m_status_volume_muted = muted;
|
||||
m_status_volume_widget->setIcon(QIcon::fromTheme(muted ? QStringLiteral("volume-mute-line") : QStringLiteral("volume-up-line")));
|
||||
m_status_volume_toggle_mute_action->setIcon(QIcon::fromTheme(muted ? QStringLiteral("volume-up-line") : QStringLiteral("volume-mute-line")));
|
||||
}
|
||||
|
||||
if (!muted && !m_status_volume_slider->isSliderDown())
|
||||
{
|
||||
QSignalBlocker blocker(m_status_volume_slider);
|
||||
m_status_volume_slider->setValue(volume);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::setStatusGPUText(const QString& text)
|
||||
{
|
||||
m_status_gpu_widget->setText(text);
|
||||
}
|
||||
|
||||
void MainWindow::setStatusFPSText(const QString& text)
|
||||
{
|
||||
m_status_fps_widget->setText(text);
|
||||
}
|
||||
|
||||
void MainWindow::setStatusVPSText(const QString& text)
|
||||
{
|
||||
m_status_vps_widget->setText(text);
|
||||
}
|
||||
|
||||
void MainWindow::setStatusSpeedText(const QString& text)
|
||||
{
|
||||
m_status_speed_widget->setText(text);
|
||||
}
|
||||
|
||||
void MainWindow::updateWindowTitle()
|
||||
@@ -2220,6 +2457,8 @@ void MainWindow::onVMStopped()
|
||||
m_last_fps_status = empty_string;
|
||||
m_status_renderer_widget->setText(empty_string);
|
||||
m_status_resolution_widget->setText(empty_string);
|
||||
m_status_volume_widget->setText(empty_string);
|
||||
m_status_gpu_widget->setText(empty_string);
|
||||
m_status_fps_widget->setText(empty_string);
|
||||
m_status_vps_widget->setText(empty_string);
|
||||
m_status_speed_widget->setText(empty_string);
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include <QtWidgets/QLabel>
|
||||
#include <QtWidgets/QMainWindow>
|
||||
#include <QtWidgets/QMenu>
|
||||
#include <QtWidgets/QSlider>
|
||||
#include <QtWidgets/QToolButton>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
|
||||
@@ -96,11 +98,13 @@ public:
|
||||
|
||||
/// Accessors for the status bar widgets, updated by the emulation thread.
|
||||
__fi QLabel* getStatusVerboseWidget() const { return m_status_verbose_widget; }
|
||||
__fi QToolButton* getStatusSpeedWidget() const { return m_status_speed_widget; }
|
||||
__fi QToolButton* getStatusVolumeWidget() const { return m_status_volume_widget; }
|
||||
__fi QLabel* getStatusRendererWidget() const { return m_status_renderer_widget; }
|
||||
__fi QLabel* getStatusResolutionWidget() const { return m_status_resolution_widget; }
|
||||
__fi QLabel* getStatusGPUWidget() const { return m_status_gpu_widget; }
|
||||
__fi QLabel* getStatusFPSWidget() const { return m_status_fps_widget; }
|
||||
__fi QLabel* getStatusVPSWidget() const { return m_status_vps_widget; }
|
||||
__fi QLabel* getStatusSpeedWidget() const { return m_status_speed_widget; }
|
||||
|
||||
/// Rescans a single file. NOTE: Happens on UI thread.
|
||||
void rescanFile(const std::string& path);
|
||||
@@ -125,6 +129,15 @@ public Q_SLOTS:
|
||||
void reportStateLoadError(const QString& message, std::optional<s32> slot, bool backup);
|
||||
void reportStateSaveError(const QString& message, std::optional<s32> slot);
|
||||
|
||||
void setStatusVerboseText(const QString& text);
|
||||
void setStatusRendererText(const QString& text);
|
||||
void setStatusResolutionText(const QString& text);
|
||||
void setStatusVolumeText(const QString& text, int volume, bool muted);
|
||||
void setStatusGPUText(const QString& text);
|
||||
void setStatusFPSText(const QString& text);
|
||||
void setStatusVPSText(const QString& text);
|
||||
void setStatusSpeedText(const QString& text);
|
||||
|
||||
void runOnUIThread(const std::function<void()>& func);
|
||||
void requestReset();
|
||||
bool requestShutdown(bool allow_confirm = true, bool allow_save_to_state = true, bool default_save_to_state = true);
|
||||
@@ -227,6 +240,8 @@ protected:
|
||||
|
||||
private:
|
||||
void setupAdditionalUi();
|
||||
void setupStatusBarWidgets();
|
||||
void applyStatusBarVolumeChanges(std::optional<int> volume, bool toggle_mute, std::optional<bool> override_per_game = std::nullopt);
|
||||
void connectSignals();
|
||||
void createRendererSwitchMenu();
|
||||
void recreate();
|
||||
@@ -309,14 +324,23 @@ private:
|
||||
QProgressBar* m_status_progress_widget = nullptr;
|
||||
QLabel* m_status_verbose_widget = nullptr;
|
||||
QLabel* m_status_renderer_widget = nullptr;
|
||||
QToolButton* m_status_volume_widget = nullptr;
|
||||
QMenu* m_status_volume_menu = nullptr;
|
||||
QAction* m_status_volume_per_game_action = nullptr;
|
||||
QAction* m_status_volume_toggle_mute_action = nullptr;
|
||||
QSlider* m_status_volume_slider = nullptr;
|
||||
QLabel* m_status_gpu_widget = nullptr;
|
||||
QLabel* m_status_fps_widget = nullptr;
|
||||
QLabel* m_status_vps_widget = nullptr;
|
||||
QLabel* m_status_speed_widget = nullptr;
|
||||
QToolButton* m_status_speed_widget = nullptr;
|
||||
QMenu* m_status_speed_menu = nullptr;
|
||||
QLabel* m_status_resolution_widget = nullptr;
|
||||
|
||||
QMenu* m_settings_toolbar_menu = nullptr;
|
||||
|
||||
bool m_display_created = false;
|
||||
bool m_status_volume_muted = false;
|
||||
bool m_status_volume_slider_applied = false;
|
||||
bool m_display_is_exclusive_fullscreen = false;
|
||||
bool m_relative_mouse_mode = false;
|
||||
bool m_hide_mouse_cursor = false;
|
||||
|
||||
@@ -77,6 +77,7 @@ namespace QtHost
|
||||
static void SaveSettings();
|
||||
static void HookSignals();
|
||||
static void RegisterTypes();
|
||||
static void InitializeClipboard();
|
||||
static bool RunSetupWizard();
|
||||
std::optional<bool> DownloadFile(QWidget* parent, const QString& title, std::string url, std::vector<u8>* data);
|
||||
} // namespace QtHost
|
||||
@@ -96,6 +97,8 @@ static bool s_run_setup_wizard = false;
|
||||
static bool s_cleanup_after_update = false;
|
||||
static bool s_boot_and_debug = false;
|
||||
static std::atomic_int s_vm_locked_with_dialog = 0;
|
||||
static std::string s_clipboard_cache;
|
||||
static std::mutex s_clipboard_cache_mutex;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// CPU Thread
|
||||
@@ -430,10 +433,16 @@ void EmuThread::run()
|
||||
void EmuThread::destroyVM()
|
||||
{
|
||||
m_last_speed = 0.0f;
|
||||
m_last_gpu_usage = 0.0f;
|
||||
m_last_game_fps = 0.0f;
|
||||
m_last_video_fps = 0.0f;
|
||||
m_last_internal_width = 0;
|
||||
m_last_internal_height = 0;
|
||||
m_last_upscale = 0.0f;
|
||||
m_last_volume = 0;
|
||||
m_last_muted = false;
|
||||
m_last_renderer = GSRendererType::Auto;
|
||||
m_last_limiter_mode = LimiterModeType::Nominal;
|
||||
m_was_paused_by_focus_loss = false;
|
||||
VMManager::Shutdown(m_save_state_on_shutdown);
|
||||
m_save_state_on_shutdown = false;
|
||||
@@ -967,8 +976,15 @@ void EmuThread::updatePerformanceMetrics(bool force)
|
||||
if (!g_main_window)
|
||||
return;
|
||||
|
||||
const s32 slot = SaveStateSelectorUI::GetCurrentSlot();
|
||||
u32 volume = 0;
|
||||
bool muted = false;
|
||||
|
||||
if (VMManager::HasValidVM())
|
||||
{
|
||||
volume = SPU2::GetOutputVolume();
|
||||
muted = SPU2::IsOutputMuted();
|
||||
|
||||
QString gs_stat;
|
||||
if (m_verbose_status)
|
||||
{
|
||||
@@ -977,9 +993,8 @@ void EmuThread::updatePerformanceMetrics(bool force)
|
||||
|
||||
if (THREAD_VU1)
|
||||
{
|
||||
gs_stat = tr("Slot: %1 | Volume: %2% | %3 | EE: %4% | VU: %5% | GS: %6%")
|
||||
.arg(SaveStateSelectorUI::GetCurrentSlot())
|
||||
.arg(SPU2::GetOutputVolume())
|
||||
gs_stat = tr("Slot: %1 | %2 | EE: %3% | VU: %4% | GS: %5%")
|
||||
.arg(slot)
|
||||
.arg(gs_stat_str.c_str())
|
||||
.arg(PerformanceMetrics::GetCPUThreadUsage(), 0, 'f', 0)
|
||||
.arg(PerformanceMetrics::GetVUThreadUsage(), 0, 'f', 0)
|
||||
@@ -987,48 +1002,81 @@ void EmuThread::updatePerformanceMetrics(bool force)
|
||||
}
|
||||
else
|
||||
{
|
||||
gs_stat = tr("Slot: %1 | Volume: %2% | %3 | EE: %4% | GS: %5%")
|
||||
.arg(SaveStateSelectorUI::GetCurrentSlot())
|
||||
.arg(SPU2::GetOutputVolume())
|
||||
gs_stat = tr("Slot: %1 | %2 | EE: %3% | GS: %4%")
|
||||
.arg(slot)
|
||||
.arg(gs_stat_str.c_str())
|
||||
.arg(PerformanceMetrics::GetCPUThreadUsage(), 0, 'f', 0)
|
||||
.arg(PerformanceMetrics::GetGSThreadUsage(), 0, 'f', 0);
|
||||
}
|
||||
}
|
||||
|
||||
QMetaObject::invokeMethod(g_main_window->getStatusVerboseWidget(), "setText", Qt::QueuedConnection, Q_ARG(const QString&, gs_stat));
|
||||
QMetaObject::invokeMethod(g_main_window, "setStatusVerboseText", Qt::QueuedConnection, Q_ARG(const QString&, gs_stat));
|
||||
}
|
||||
|
||||
const GSRendererType renderer = GSGetCurrentRenderer(); // Reading from GS thread, therefore racey, but it's just visual.
|
||||
const float upscale = EmuConfig.GS.UpscaleMultiplier;
|
||||
const float speed = std::round(PerformanceMetrics::GetSpeed());
|
||||
const LimiterModeType limiter_mode = VMManager::GetLimiterMode();
|
||||
const float gpu_usage = std::round(PerformanceMetrics::GetGPUUsage());
|
||||
const float gfps = std::round(PerformanceMetrics::GetInternalFPS());
|
||||
const float vfps = std::round(PerformanceMetrics::GetFPS());
|
||||
int iwidth, iheight;
|
||||
GSgetInternalResolution(&iwidth, &iheight);
|
||||
|
||||
if (iwidth != m_last_internal_width || iheight != m_last_internal_height || speed != m_last_speed || gfps != m_last_game_fps ||
|
||||
vfps != m_last_video_fps || renderer != m_last_renderer || force)
|
||||
if (iwidth != m_last_internal_width || iheight != m_last_internal_height || upscale != m_last_upscale ||
|
||||
speed != m_last_speed || gpu_usage != m_last_gpu_usage || gfps != m_last_game_fps || vfps != m_last_video_fps || renderer != m_last_renderer ||
|
||||
volume != m_last_volume || muted != m_last_muted || force)
|
||||
{
|
||||
|
||||
if (volume != m_last_volume || muted != m_last_muted || force)
|
||||
{
|
||||
QString vol_text = tr("Volume: %1%").arg(volume);
|
||||
if (muted)
|
||||
vol_text = tr("Volume: Muted");
|
||||
QMetaObject::invokeMethod(g_main_window, "setStatusVolumeText", Qt::QueuedConnection,
|
||||
Q_ARG(const QString&, vol_text), Q_ARG(int, static_cast<int>(volume)), Q_ARG(bool, muted));
|
||||
m_last_volume = volume;
|
||||
m_last_muted = muted;
|
||||
}
|
||||
|
||||
if (renderer != m_last_renderer || force)
|
||||
{
|
||||
QMetaObject::invokeMethod(g_main_window->getStatusRendererWidget(), "setText", Qt::QueuedConnection,
|
||||
Q_ARG(const QString&, QString::fromUtf8(Pcsx2Config::GSOptions::GetRendererName(renderer))));
|
||||
QString renderer_name = QString::fromUtf8(Pcsx2Config::GSOptions::GetRendererName(renderer));
|
||||
if (EmuConfig.GS.Renderer == GSRendererType::Auto && renderer != GSRendererType::Auto)
|
||||
renderer_name = tr("Auto (%1)").arg(renderer_name);
|
||||
|
||||
QMetaObject::invokeMethod(g_main_window, "setStatusRendererText", Qt::QueuedConnection,
|
||||
Q_ARG(const QString&, renderer_name));
|
||||
m_last_renderer = renderer;
|
||||
}
|
||||
|
||||
if (iwidth != m_last_internal_width || iheight != m_last_internal_height || force)
|
||||
if (iwidth != m_last_internal_width || iheight != m_last_internal_height || upscale != m_last_upscale || force)
|
||||
{
|
||||
QString text;
|
||||
if (iwidth == 0 || iheight == 0)
|
||||
text = tr("No Image");
|
||||
else
|
||||
text = tr("%1x%2").arg(iwidth).arg(iheight);
|
||||
text = tr("%1x%2 (%3x)").arg(iwidth).arg(iheight).arg(upscale, 0, 'g', 3);
|
||||
|
||||
QMetaObject::invokeMethod(
|
||||
g_main_window->getStatusResolutionWidget(), "setText", Qt::QueuedConnection, Q_ARG(const QString&, text));
|
||||
g_main_window, "setStatusResolutionText", Qt::QueuedConnection, Q_ARG(const QString&, text));
|
||||
|
||||
m_last_internal_width = iwidth;
|
||||
m_last_internal_height = iheight;
|
||||
m_last_upscale = upscale;
|
||||
}
|
||||
|
||||
if (gpu_usage != m_last_gpu_usage || force)
|
||||
{
|
||||
QString text;
|
||||
if (gpu_usage == 0)
|
||||
text = tr("GPU: N/A");
|
||||
else
|
||||
text = tr("GPU: %1%").arg(gpu_usage, 0, 'f', 0);
|
||||
|
||||
QMetaObject::invokeMethod(g_main_window, "setStatusGPUText", Qt::QueuedConnection,
|
||||
Q_ARG(const QString&, text));
|
||||
m_last_gpu_usage = gpu_usage;
|
||||
}
|
||||
|
||||
if (gfps != m_last_game_fps || force)
|
||||
@@ -1039,23 +1087,33 @@ void EmuThread::updatePerformanceMetrics(bool force)
|
||||
else
|
||||
text = tr("FPS: %1").arg(gfps, 0, 'f', 0);
|
||||
|
||||
QMetaObject::invokeMethod(g_main_window->getStatusFPSWidget(), "setText", Qt::QueuedConnection,
|
||||
QMetaObject::invokeMethod(g_main_window, "setStatusFPSText", Qt::QueuedConnection,
|
||||
Q_ARG(const QString&, text));
|
||||
m_last_game_fps = gfps;
|
||||
}
|
||||
|
||||
if (vfps != m_last_video_fps || force)
|
||||
{
|
||||
QMetaObject::invokeMethod(g_main_window->getStatusVPSWidget(), "setText", Qt::QueuedConnection,
|
||||
Q_ARG(const QString&, tr("VPS: %1 ").arg(vfps, 0, 'f', 0)));
|
||||
QMetaObject::invokeMethod(g_main_window, "setStatusVPSText", Qt::QueuedConnection,
|
||||
Q_ARG(const QString&, vfps == 0 ? tr("VPS: N/A") : tr("VPS: %1").arg(vfps, 0, 'f', 0)));
|
||||
m_last_video_fps = vfps;
|
||||
}
|
||||
|
||||
if (speed != m_last_speed || force)
|
||||
{
|
||||
QMetaObject::invokeMethod(g_main_window->getStatusSpeedWidget(), "setText", Qt::QueuedConnection,
|
||||
Q_ARG(const QString&, tr("Speed: %1% ").arg(speed, 0, 'f', 0)));
|
||||
m_last_speed = speed;
|
||||
}
|
||||
if (speed != m_last_speed || force)
|
||||
{
|
||||
QMetaObject::invokeMethod(g_main_window, "setStatusSpeedText", Qt::QueuedConnection,
|
||||
Q_ARG(const QString&, tr("Speed: %1%").arg(speed, 0, 'f', 0)));
|
||||
m_last_speed = speed;
|
||||
}
|
||||
|
||||
if (limiter_mode != m_last_limiter_mode || force)
|
||||
{
|
||||
QMetaObject::invokeMethod(g_main_window, [limiter_mode]() { g_main_window->getStatusSpeedWidget()->setIcon(QIcon::fromTheme(
|
||||
limiter_mode == LimiterModeType::Turbo ? QStringLiteral("fast-forward-line") :
|
||||
limiter_mode == LimiterModeType::Slomo ? QStringLiteral("slow-mo") :
|
||||
limiter_mode == LimiterModeType::Unlimited ? QStringLiteral("speed-line") :
|
||||
QStringLiteral("dashboard-line"))); }, Qt::QueuedConnection);
|
||||
m_last_limiter_mode = limiter_mode;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1131,16 +1189,6 @@ void Host::OnAchievementsHardcoreModeChanged(bool enabled)
|
||||
emit g_emu_thread->onAchievementsHardcoreModeChanged(enabled);
|
||||
}
|
||||
|
||||
void Host::OnCoverDownloaderOpenRequested()
|
||||
{
|
||||
emit g_emu_thread->onCoverDownloaderOpenRequested();
|
||||
}
|
||||
|
||||
void Host::OnCreateMemoryCardOpenRequested()
|
||||
{
|
||||
emit g_emu_thread->onCreateMemoryCardOpenRequested();
|
||||
}
|
||||
|
||||
bool Host::ShouldPreferHostFileSelector()
|
||||
{
|
||||
#ifdef __linux__
|
||||
@@ -1676,6 +1724,12 @@ bool Host::CopyTextToClipboard(const std::string_view text)
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string Host::GetTextFromClipboard()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(s_clipboard_cache_mutex);
|
||||
return s_clipboard_cache;
|
||||
}
|
||||
|
||||
void Host::BeginTextInput()
|
||||
{
|
||||
QInputMethod* method = qApp->inputMethod();
|
||||
@@ -2361,6 +2415,26 @@ void QtHost::RegisterTypes()
|
||||
qRegisterMetaType<Achievements::LoginRequestReason>();
|
||||
}
|
||||
|
||||
void QtHost::InitializeClipboard()
|
||||
{
|
||||
QClipboard* clipboard = QGuiApplication::clipboard();
|
||||
if (clipboard)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(s_clipboard_cache_mutex);
|
||||
s_clipboard_cache = clipboard->text().toStdString();
|
||||
}
|
||||
QObject::connect(clipboard, &QClipboard::dataChanged, []() {
|
||||
QClipboard* cb = QGuiApplication::clipboard();
|
||||
if (cb)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(s_clipboard_cache_mutex);
|
||||
s_clipboard_cache = cb->text().toStdString();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool QtHost::RunSetupWizard()
|
||||
{
|
||||
SetupWizardDialog dialog;
|
||||
@@ -2409,6 +2483,8 @@ int main(int argc, char* argv[])
|
||||
|
||||
PCSX2MainApplication app(argc, argv);
|
||||
|
||||
QtHost::InitializeClipboard();
|
||||
|
||||
#ifndef _WIN32
|
||||
if (!PerformEarlyHardwareChecks())
|
||||
return EXIT_FAILURE;
|
||||
|
||||
@@ -165,10 +165,6 @@ Q_SIGNALS:
|
||||
/// Called when hardcore mode is enabled or disabled.
|
||||
void onAchievementsHardcoreModeChanged(bool enabled);
|
||||
|
||||
/// Big Picture UI requests.
|
||||
void onCoverDownloaderOpenRequested();
|
||||
void onCreateMemoryCardOpenRequested();
|
||||
|
||||
/// Called when video capture starts/stops.
|
||||
void onCaptureStarted(const QString& filename);
|
||||
void onCaptureStopped();
|
||||
@@ -216,11 +212,16 @@ private:
|
||||
bool m_was_paused_by_focus_loss = false;
|
||||
|
||||
float m_last_speed = 0.0f;
|
||||
float m_last_gpu_usage = 0.0f;
|
||||
float m_last_game_fps = 0.0f;
|
||||
float m_last_video_fps = 0.0f;
|
||||
int m_last_internal_width = 0;
|
||||
int m_last_internal_height = 0;
|
||||
float m_last_upscale = 0.0f;
|
||||
u32 m_last_volume = 0;
|
||||
bool m_last_muted = false;
|
||||
GSRendererType m_last_renderer = GSRendererType::Auto;
|
||||
LimiterModeType m_last_limiter_mode = LimiterModeType::Nominal;
|
||||
};
|
||||
|
||||
extern EmuThread* g_emu_thread;
|
||||
|
||||
@@ -590,6 +590,12 @@ u32 QtUtils::KeyEventToCode(const QKeyEvent* ev)
|
||||
modifiers &= ~Qt::KeypadModifier;
|
||||
}
|
||||
}
|
||||
|
||||
// Undo Qt's swapping of Control and Command on macOS.
|
||||
if (key == Qt::Key_Control)
|
||||
key = Qt::Key_Meta;
|
||||
else if (key == Qt::Key_Meta)
|
||||
key = Qt::Key_Control;
|
||||
#endif
|
||||
|
||||
return static_cast<u32>(key) | (static_cast<u32>(modifiers) & static_cast<u32>(Qt::KeypadModifier));
|
||||
|
||||
@@ -180,7 +180,7 @@ namespace QtUtils
|
||||
}
|
||||
|
||||
wi.surface_refresh_rate = surface_refresh_rate.value();
|
||||
INFO_LOG("Surface refresh rate: {} hz", wi.surface_refresh_rate);
|
||||
INFO_LOG("Surface refresh rate: {} Hz", wi.surface_refresh_rate);
|
||||
|
||||
return wi;
|
||||
}
|
||||
|
||||
@@ -90,17 +90,15 @@ AchievementSettingsWidget::AchievementSettingsWidget(SettingsWindow* settings_di
|
||||
}
|
||||
else
|
||||
{
|
||||
// remove login and game info, not relevant for per-game
|
||||
// hide login and game info, not relevant for per-game
|
||||
m_ui.verticalLayout->removeWidget(m_ui.gameInfoBox);
|
||||
m_ui.gameInfoBox->deleteLater();
|
||||
m_ui.gameInfoBox = nullptr;
|
||||
m_ui.gameInfoBox->hide();
|
||||
m_ui.verticalLayout->removeWidget(m_ui.loginBox);
|
||||
m_ui.loginBox->deleteLater();
|
||||
m_ui.loginBox = nullptr;
|
||||
m_ui.loginBox->hide();
|
||||
|
||||
// sound effects
|
||||
m_ui.verticalLayout->removeWidget(m_ui.soundEffectsBox);
|
||||
m_ui.soundEffectsBox->deleteLater();
|
||||
m_ui.soundEffectsBox->hide();
|
||||
m_ui.soundEffectsBox = nullptr;
|
||||
}
|
||||
|
||||
@@ -121,9 +119,6 @@ void AchievementSettingsWidget::updateEnableState()
|
||||
const bool unlock = enabled && sound && dialog()->getEffectiveBoolValue("Achievements", "UnlockSound", true);
|
||||
const bool lbsound = enabled && sound && dialog()->getEffectiveBoolValue("Achievements", "LBSubmitSound", true);
|
||||
|
||||
m_ui.viewProfile->setEnabled(enabled);
|
||||
m_ui.loginButton->setEnabled(enabled);
|
||||
|
||||
m_ui.hardcoreMode->setEnabled(enabled);
|
||||
m_ui.achievementNotifications->setEnabled(enabled);
|
||||
m_ui.leaderboardNotifications->setEnabled(enabled);
|
||||
|
||||
@@ -11,6 +11,28 @@
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="rcheevosDisclaimer">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p align="justify">PCSX2 uses RetroAchievements as an achievement database and for tracking progress. To use achievements, please sign up for an account at <a href="https://retroachievements.org/">retroachievements.org</a>.</p><p align="justify">To view the achievement list in-game, press the hotkey for <span style=" font-weight:600;">Open Pause Menu</span> and select <span style=" font-weight:600;">Achievements</span> from the menu.</p></body></html></string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::TextFormat::RichText</enum>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>enable</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="loginBox">
|
||||
<property name="title">
|
||||
@@ -18,32 +40,21 @@
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="acccountLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="enable">
|
||||
<property name="text">
|
||||
<string>Enable Achievements</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="loginStatus">
|
||||
<property name="text">
|
||||
<string>Username:
|
||||
<widget class="QLabel" name="loginStatus">
|
||||
<property name="text">
|
||||
<string>Username:
|
||||
Login token generated at:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::TextInteractionFlag::TextBrowserInteraction</set>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>viewProfile</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::TextInteractionFlag::TextBrowserInteraction</set>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>viewProfile</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="achievementButtons">
|
||||
@@ -94,62 +105,47 @@ Login token generated at:</string>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="rcheevosDisclaimer">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p align="justify">PCSX2 uses RetroAchievements as an achievement database and for tracking progress. To use achievements, please sign up for an account at <a href="https://retroachievements.org/">retroachievements.org</a>.</p><p align="justify">To view the achievement list in-game, press the hotkey for <span style=" font-weight:600;">Open Pause Menu</span> and select <span style=" font-weight:600;">Achievements</span> from the menu.</p></body></html></string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::TextFormat::RichText</enum>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>enable</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="settingsBox">
|
||||
<property name="title">
|
||||
<string>Settings</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_5">
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="unofficialAchievements">
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="enable">
|
||||
<property name="text">
|
||||
<string>Test Unofficial Achievements</string>
|
||||
<string>Enable Achievements</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="spectatorMode">
|
||||
<property name="text">
|
||||
<string>Enable Spectator Mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="hardcoreMode">
|
||||
<property name="text">
|
||||
<string>Enable Hardcore Mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="spectatorMode">
|
||||
<property name="text">
|
||||
<string>Enable Spectator Mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="encoreMode">
|
||||
<property name="text">
|
||||
<string>Enable Encore Mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="unofficialAchievements">
|
||||
<property name="text">
|
||||
<string>Test Unofficial Achievements</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -537,9 +533,9 @@ Login token generated at:</string>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>enable</tabstop>
|
||||
<tabstop>viewProfile</tabstop>
|
||||
<tabstop>loginButton</tabstop>
|
||||
<tabstop>enable</tabstop>
|
||||
<tabstop>hardcoreMode</tabstop>
|
||||
<tabstop>spectatorMode</tabstop>
|
||||
<tabstop>encoreMode</tabstop>
|
||||
|
||||
@@ -474,7 +474,7 @@
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Deflate64</string>
|
||||
<string>Deflate</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
@@ -482,11 +482,6 @@
|
||||
<string>Zstandard</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>LZMA2</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <QtWidgets/QMessageBox>
|
||||
#include <QtWidgets/QScrollArea>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include "fmt/format.h"
|
||||
|
||||
#include "common/Console.h"
|
||||
@@ -14,6 +15,7 @@
|
||||
|
||||
#include "pcsx2/Host.h"
|
||||
#include "pcsx2/SIO/Pad/Pad.h"
|
||||
#include "pcsx2/SIO/Sio.h"
|
||||
|
||||
#include "Settings/ControllerBindingWidget.h"
|
||||
#include "Settings/ControllerSettingsWindow.h"
|
||||
@@ -41,7 +43,18 @@ ControllerBindingWidget::ControllerBindingWidget(QWidget* parent, ControllerSett
|
||||
, m_port_number(port)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
m_ui.groupBox->setTitle(tr("Controller Port %1").arg(port + 1));
|
||||
|
||||
const auto [pad_port, pad_slot] = sioConvertPadToPortAndSlot(port);
|
||||
const bool mtap_enabled = m_dialog->getBoolValue("Pad", (pad_port == 0) ? "MultitapPort1" : "MultitapPort2", false);
|
||||
if (mtap_enabled)
|
||||
{
|
||||
static constexpr const std::array<char, 4> s_mtap_slot_names = {{'A', 'B', 'C', 'D'}};
|
||||
m_ui.groupBox->setTitle(tr("Controller Port %1%2").arg(pad_port + 1).arg(s_mtap_slot_names[pad_slot]));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ui.groupBox->setTitle(tr("Controller Port %1").arg(pad_port + 1));
|
||||
}
|
||||
|
||||
populateControllerTypes();
|
||||
onTypeChanged();
|
||||
@@ -294,7 +307,20 @@ ControllerMacroWidget::ControllerMacroWidget(ControllerBindingWidget* parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
setWindowTitle(tr("Controller Port %1 Macros").arg(parent->getPortNumber() + 1u));
|
||||
|
||||
const u32 port = parent->getPortNumber();
|
||||
const auto [pad_port, pad_slot] = sioConvertPadToPortAndSlot(port);
|
||||
const bool mtap_enabled = parent->getDialog()->getBoolValue("Pad", (pad_port == 0) ? "MultitapPort1" : "MultitapPort2", false);
|
||||
if (mtap_enabled)
|
||||
{
|
||||
static constexpr const std::array<char, 4> s_mtap_slot_names = {{'A', 'B', 'C', 'D'}};
|
||||
setWindowTitle(tr("Controller Port %1%2 Macros").arg(pad_port + 1).arg(s_mtap_slot_names[pad_slot]));
|
||||
}
|
||||
else
|
||||
{
|
||||
setWindowTitle(tr("Controller Port %1 Macros").arg(pad_port + 1));
|
||||
}
|
||||
|
||||
createWidgets(parent);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ EmulationSettingsWidget::EmulationSettingsWidget(SettingsWindow* settings_dialog
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.vsync, "EmuCore/GS", "VsyncEnable", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.syncToHostRefreshRate, "EmuCore/GS", "SyncToHostRefreshRate", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.useVSyncForTiming, "EmuCore/GS", "UseVSyncForTiming", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.skipPresentingDuplicateFrames, "EmuCore/GS", "SkipDuplicateFrames", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.skipPresentingDuplicateFrames, "EmuCore/GS", "SkipDuplicateFrames", true);
|
||||
connect(m_ui.optimalFramePacing, &QCheckBox::checkStateChanged, this, &EmulationSettingsWidget::onOptimalFramePacingChanged);
|
||||
connect(m_ui.vsync, &QCheckBox::checkStateChanged, this, &EmulationSettingsWidget::updateUseVSyncForTimingEnabled);
|
||||
connect(m_ui.syncToHostRefreshRate, &QCheckBox::checkStateChanged, this, &EmulationSettingsWidget::updateUseVSyncForTimingEnabled);
|
||||
@@ -112,7 +112,7 @@ EmulationSettingsWidget::EmulationSettingsWidget(SettingsWindow* settings_dialog
|
||||
dialog()->registerWidgetHelp(m_ui.eeCycleRate, tr("EE Cycle Rate"), tr("100% (Normal Speed)"),
|
||||
tr("Higher values may increase internal framerate in games, but will increase CPU requirements substantially. "
|
||||
"Lower values will reduce the CPU load allowing lightweight games to run full speed on weaker CPUs."));
|
||||
dialog()->registerWidgetHelp(m_ui.eeCycleSkipping, tr("EE Cycle Skip"), tr("Disabled"),
|
||||
dialog()->registerWidgetHelp(m_ui.eeCycleSkipping, tr("EE Cycle Skipping"), tr("Disabled"),
|
||||
tr("Makes the emulated Emotion Engine skip cycles. "
|
||||
//: SOTC = Shadow of the Colossus. A game's title, should not be translated unless an official translation exists.
|
||||
"Helps a small subset of games like SOTC. Most of the time it's harmful to performance."));
|
||||
@@ -150,7 +150,7 @@ EmulationSettingsWidget::EmulationSettingsWidget(SettingsWindow* settings_dialog
|
||||
dialog()->registerWidgetHelp(m_ui.useVSyncForTiming, tr("Use Host VSync Timing"), tr("Unchecked"),
|
||||
tr("When synchronizing with the host refresh rate, this option disables PCSX2's internal frame timing and uses the host instead. "
|
||||
"Can result in smoother frame pacing, <strong>but at the cost of increased input latency</strong>."));
|
||||
dialog()->registerWidgetHelp(m_ui.skipPresentingDuplicateFrames, tr("Skip Presenting Duplicate Frames"), tr("Unchecked"),
|
||||
dialog()->registerWidgetHelp(m_ui.skipPresentingDuplicateFrames, tr("Skip Presenting Duplicate Frames"), tr("Checked"),
|
||||
tr("Detects when idle frames are being presented in 25/30fps games, and skips presenting those frames. The frame is still "
|
||||
"rendered, it just means the GPU has more time to complete it (this is NOT frame skipping). Can smooth out frame time "
|
||||
"fluctuations when the CPU/GPU are near maximum utilization, but makes frame pacing more inconsistent and can increase "
|
||||
|
||||
@@ -22,9 +22,9 @@ FolderSettingsWidget::FolderSettingsWidget(SettingsWindow* settings_dialog, QWid
|
||||
"Folders", "SaveStates", Path::Combine(EmuFolders::DataRoot, "sstates"));
|
||||
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.videoDumpingDirectory, m_ui.videoDumpingDirectoryBrowse, m_ui.videoDumpingDirectoryOpen, m_ui.videoDumpingDirectoryReset,
|
||||
"Folders", "Videos", Path::Combine(EmuFolders::DataRoot, "videos"));
|
||||
dialog()->registerWidgetHelp(m_ui.organizeSnapshotsByGame, tr("Organize Snapshots by Game"), tr("Unchecked"),
|
||||
dialog()->registerWidgetHelp(m_ui.organizeSnapshotsByGame, tr("Save Snapshots in Game-Specific Folders"), tr("Unchecked"),
|
||||
tr("Saves snapshots to per-game subfolders instead of a shared folder."));
|
||||
dialog()->registerWidgetHelp(m_ui.organizeVideoDumpByGame, tr("Organize Video Recordings by Game"), tr("Unchecked"),
|
||||
dialog()->registerWidgetHelp(m_ui.organizeVideoDumpByGame, tr("Save Video Recordings in Game-Specific Folders"), tr("Unchecked"),
|
||||
tr("Saves video recordings to per-game subfolders instead of a shared folder."));
|
||||
}
|
||||
|
||||
|
||||
@@ -294,7 +294,7 @@
|
||||
<item row="4" column="0">
|
||||
<widget class="QCheckBox" name="organizeVideoDumpByGame">
|
||||
<property name="text">
|
||||
<string>Save Video Recording in Game-Specific Folders</string>
|
||||
<string>Save Video Recordings in Game-Specific Folders</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -43,6 +43,11 @@
|
||||
<string>Accurate (Recommended)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Accurate Force Full (Can Reduce Readbacks)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Disable Readbacks (Synchronize GS Thread)</string>
|
||||
@@ -91,27 +96,6 @@
|
||||
</item>
|
||||
<item row="10" column="0" colspan="2">
|
||||
<layout class="QGridLayout" name="advancedLayout">
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="extendedUpscales">
|
||||
<property name="text">
|
||||
<string>Extended Upscaling Multipliers</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="disableMailboxPresentation">
|
||||
<property name="text">
|
||||
<string extracomment="Mailbox Presentation: a type of graphics-rendering technique that has not been exposed to the public that often, so chances are you will need to keep the word mailbox in English. It does not have anything to do with postal mailboxes or email inboxes/outboxes.">Disable Mailbox Presentation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="useBlitSwapChain">
|
||||
<property name="text">
|
||||
<string extracomment="Blit = a data operation. You might want to write it as-is, but fully uppercased. More information: https://en.wikipedia.org/wiki/Bit_blit \nSwap chain: see Microsoft's Terminology Portal.">Use Blit Swap Chain</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="spinCPUDuringReadbacks">
|
||||
<property name="text">
|
||||
@@ -119,13 +103,48 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="spinGPUDuringReadbacks">
|
||||
<property name="text">
|
||||
<string>Spin GPU During Readbacks</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="useBlitSwapChain">
|
||||
<property name="text">
|
||||
<string extracomment="Blit = a data operation. You might want to write it as-is, but fully uppercased. More information: https://en.wikipedia.org/wiki/Bit_blit \nSwap chain: see Microsoft's Terminology Portal.">Use Blit Swap Chain</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="rov">
|
||||
<property name="text">
|
||||
<string>Rasterizer Ordered View</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QCheckBox" name="disableMailboxPresentation">
|
||||
<property name="text">
|
||||
<string extracomment="Mailbox Presentation: a type of graphics-rendering technique that has not been exposed to the public that often, so chances are you will need to keep the word mailbox in English. It does not have anything to do with postal mailboxes or email inboxes/outboxes.">Disable Mailbox Presentation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="extendedUpscales">
|
||||
<property name="text">
|
||||
<string>Extended Upscaling Multipliers</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QCheckBox" name="rovBarriersVK">
|
||||
<property name="text">
|
||||
<string>ROV Barriers Vulkan</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
@@ -321,6 +340,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="useDebugBlend">
|
||||
<property name="text">
|
||||
<string>Use Debug Blend</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
@@ -346,11 +372,13 @@
|
||||
<tabstop>gsDumpCompression</tabstop>
|
||||
<tabstop>texturePreloading</tabstop>
|
||||
<tabstop>exclusiveFullscreenControl</tabstop>
|
||||
<tabstop>useBlitSwapChain</tabstop>
|
||||
<tabstop>rov</tabstop>
|
||||
<tabstop>extendedUpscales</tabstop>
|
||||
<tabstop>disableMailboxPresentation</tabstop>
|
||||
<tabstop>spinCPUDuringReadbacks</tabstop>
|
||||
<tabstop>spinGPUDuringReadbacks</tabstop>
|
||||
<tabstop>spinCPUDuringReadbacks</tabstop>
|
||||
<tabstop>useBlitSwapChain</tabstop>
|
||||
<tabstop>disableMailboxPresentation</tabstop>
|
||||
<tabstop>rovBarriersVK</tabstop>
|
||||
<tabstop>ntscFrameRate</tabstop>
|
||||
<tabstop>palFrameRate</tabstop>
|
||||
<tabstop>overrideTextureBarriers</tabstop>
|
||||
@@ -358,6 +386,7 @@
|
||||
<tabstop>disableFramebufferFetch</tabstop>
|
||||
<tabstop>disableShaderCache</tabstop>
|
||||
<tabstop>disableVertexShaderExpand</tabstop>
|
||||
<tabstop>useDebugBlend</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
||||
@@ -223,8 +223,10 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* settings_dialog,
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Advanced Settings
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_advanced.rov, "EmuCore/GS", "HWROV", true);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_advanced.useBlitSwapChain, "EmuCore/GS", "UseBlitSwapChain", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_advanced.useDebugDevice, "EmuCore/GS", "UseDebugDevice", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_advanced.useDebugBlend, "EmuCore/GS", "UseDebugBlend", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_advanced.disableMailboxPresentation, "EmuCore/GS", "DisableMailboxPresentation", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_advanced.extendedUpscales, "EmuCore/GS", "ExtendedUpscalingMultipliers", false);
|
||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_advanced.exclusiveFullscreenControl, "EmuCore/GS", "ExclusiveFullscreenControl", -1, -1);
|
||||
@@ -238,6 +240,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* settings_dialog,
|
||||
SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_advanced.palFrameRate, "EmuCore/GS", "FrameRatePAL", 50.00f);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_advanced.spinCPUDuringReadbacks, "EmuCore/GS", "HWSpinCPUForReadbacks", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_advanced.spinGPUDuringReadbacks, "EmuCore/GS", "HWSpinGPUForReadbacks", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_advanced.rovBarriersVK, "EmuCore/GS", "HWROVBarriersVK", false);
|
||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_advanced.texturePreloading, "EmuCore/GS", "texture_preloading", static_cast<int>(TexturePreloadingLevel::Off));
|
||||
|
||||
setTabVisible(m_advanced_tab, QtHost::ShouldShowAdvancedSettings());
|
||||
@@ -409,10 +412,10 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* settings_dialog,
|
||||
|
||||
// Display tab
|
||||
{
|
||||
dialog()->registerWidgetHelp(m_display.widescreenPatches, tr("Enable Widescreen Patches"), tr("Unchecked"),
|
||||
dialog()->registerWidgetHelp(m_display.widescreenPatches, tr("Apply Widescreen Patches"), tr("Unchecked"),
|
||||
tr("Automatically loads and applies widescreen patches on game start. Can cause issues."));
|
||||
|
||||
dialog()->registerWidgetHelp(m_display.noInterlacingPatches, tr("Enable No-Interlacing Patches"), tr("Unchecked"),
|
||||
dialog()->registerWidgetHelp(m_display.noInterlacingPatches, tr("Apply No-Interlacing Patches"), tr("Unchecked"),
|
||||
tr("Automatically loads and applies no-interlacing patches on game start. Can cause issues."));
|
||||
|
||||
dialog()->registerWidgetHelp(m_display.disableInterlaceOffset, tr("Disable Interlace Offset"), tr("Unchecked"),
|
||||
@@ -756,6 +759,12 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* settings_dialog,
|
||||
tr("Overrides the driver's heuristics for enabling exclusive fullscreen, or direct flip/scanout.<br>"
|
||||
"Disallowing exclusive fullscreen may enable smoother task switching and overlays, but increase input latency."));
|
||||
|
||||
dialog()->registerWidgetHelp(
|
||||
m_advanced.rov, tr("Rasterizer Ordered View"), tr("Checked"), tr("Enables Rasterizer Ordered View (ROV), which allows feedback loops to be executed with fewer draw calls. Can improve performance in feedback heavy games with higher accuracy settings."));
|
||||
|
||||
dialog()->registerWidgetHelp(m_advanced.rovBarriersVK, tr("ROV Barriers Vulkan"), tr("Unchecked"),
|
||||
tr("Forces extra barriers when using ROV with Vulkan to fix graphical issues present in some games and hardware configurations."));
|
||||
|
||||
dialog()->registerWidgetHelp(m_advanced.disableMailboxPresentation, tr("Disable Mailbox Presentation"), tr("Unchecked"),
|
||||
tr("Forces the use of FIFO over Mailbox presentation, i.e. double buffering instead of triple buffering. "
|
||||
"Usually results in worse frame pacing."));
|
||||
@@ -763,9 +772,12 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* settings_dialog,
|
||||
dialog()->registerWidgetHelp(m_advanced.extendedUpscales, tr("Extended Upscaling Multipliers"), tr("Unchecked"),
|
||||
tr("Displays additional, very high upscaling multipliers dependent on GPU and driver capability."));
|
||||
|
||||
dialog()->registerWidgetHelp(m_advanced.useDebugDevice, tr("Enable Debug Device"), tr("Unchecked"),
|
||||
dialog()->registerWidgetHelp(m_advanced.useDebugDevice, tr("Use Debug Device"), tr("Unchecked"),
|
||||
tr("Enables API-level validation of graphics commands."));
|
||||
|
||||
dialog()->registerWidgetHelp(m_advanced.useDebugBlend, tr("Use Debug Blend"), tr("Unchecked"),
|
||||
tr("Forces SW blending and disables several optimizations."));
|
||||
|
||||
dialog()->registerWidgetHelp(m_advanced.gsDownloadMode, tr("GS Download Mode"), tr("Accurate"),
|
||||
tr("Skips synchronizing with the GS thread and host GPU for GS downloads. "
|
||||
"Can result in a large speed boost on slower systems, at the cost of many broken graphical effects. "
|
||||
@@ -998,6 +1010,7 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
|
||||
const bool is_software = (type == GSRendererType::SW);
|
||||
const bool is_auto = (type == GSRendererType::Auto);
|
||||
const bool is_vk = (type == GSRendererType::VK);
|
||||
const bool is_ogl = (type == GSRendererType::OGL);
|
||||
const bool is_disable_barriers = (type == GSRendererType::Metal || type == GSRendererType::SW);
|
||||
const bool hw_fixes = (is_hardware && m_hw.enableHWFixes && m_hw.enableHWFixes->checkState() == Qt::Checked);
|
||||
|
||||
@@ -1040,6 +1053,9 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
|
||||
if (m_advanced.exclusiveFullscreenControl)
|
||||
m_advanced.exclusiveFullscreenControl->setEnabled(is_auto || is_vk);
|
||||
|
||||
if (m_advanced.rov)
|
||||
m_advanced.rov->setDisabled(!is_hardware || is_ogl);
|
||||
|
||||
// populate adapters
|
||||
std::vector<GSAdapterInfo> adapters = GSGetAdapterInfo(type);
|
||||
const GSAdapterInfo* current_adapter_info = nullptr;
|
||||
|
||||
@@ -201,7 +201,7 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsWindow* settings_dialog
|
||||
tr("Unchecked"), tr("Pauses the emulator when a controller with bindings is disconnected."));
|
||||
dialog()->registerWidgetHelp(m_ui.promptOnStateLoadSaveFailure, tr("Prompt On State Load/Save Failure"),
|
||||
tr("Checked"), tr("Displays a modal dialog when a save state load/save operation fails."));
|
||||
dialog()->registerWidgetHelp(m_ui.preferEnglishGameList, tr("Prefer English Titles"), tr("Unchecked"),
|
||||
dialog()->registerWidgetHelp(m_ui.preferEnglishGameList, tr("Prefer English Game Titles"), tr("Unchecked"),
|
||||
tr("For games with both a title in the game's native language and one in English, prefer the English title. Affects how game titles are displayed on the game list, window title and Discord Presence"));
|
||||
dialog()->registerWidgetHelp(m_ui.startFullscreen, tr("Start Fullscreen"), tr("Unchecked"),
|
||||
tr("Automatically switches to fullscreen mode when a game is started."));
|
||||
@@ -228,7 +228,7 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsWindow* settings_dialog
|
||||
m_ui.disableWindowResizing, tr("Disable Window Resizing"), tr("Unchecked"),
|
||||
tr("Prevents the main window from being resized."));
|
||||
dialog()->registerWidgetHelp(
|
||||
m_ui.startFullscreenUI, tr("Start Big Picture Mode"), tr("Unchecked"),
|
||||
m_ui.startFullscreenUI, tr("Start In Big Picture Mode"), tr("Unchecked"),
|
||||
tr("Automatically starts Big Picture Mode instead of the regular Qt interface when PCSX2 launches."));
|
||||
dialog()->registerWidgetHelp(
|
||||
m_ui.backgroundBrowse, tr("Game List Background"), tr("None"),
|
||||
|
||||
@@ -153,7 +153,7 @@
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="startFullscreenUI">
|
||||
<property name="text">
|
||||
<string>Start Big Picture UI</string>
|
||||
<string>Start In Big Picture Mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -89,7 +89,7 @@ ShortcutCreationDialog::ShortcutCreationDialog(QWidget* parent, const QString& t
|
||||
}
|
||||
|
||||
m_ui.iconPath->setText(Path::ToNativePath(icon_file.toStdString()).c_str());
|
||||
m_ui.iconPreview->setPixmap(pixmap.scaled(m_ui.iconPreview->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||||
m_ui.iconPreview->setPixmap(pixmap.scaled(m_ui.iconPreview->size(), Qt::KeepAspectRatio, Qt::FastTransformation));
|
||||
m_ui.resetIconButton->setEnabled(true);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -250,6 +250,7 @@ std::vector<std::pair<QString, QString>> QtHost::GetAvailableLanguageList()
|
||||
{QCoreApplication::translate("InterfaceSettingsWidget", "System Language [Default]"), QStringLiteral("system")},
|
||||
{QStringLiteral("Afrikaans (af-ZA)"), QStringLiteral("af-ZA")},
|
||||
{QStringLiteral("عربي (ar-SA)"), QStringLiteral("ar-SA")},
|
||||
{QStringLiteral("Azərbaycanca (az-AZ)"), QStringLiteral("az-AZ")},
|
||||
{QStringLiteral("Català (ca-ES)"), QStringLiteral("ca-ES")},
|
||||
{QStringLiteral("Čeština (cs-CZ)"), QStringLiteral("cs-CZ")},
|
||||
{QStringLiteral("Dansk (da-DK)"), QStringLiteral("da-DK")},
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10-10-4.477-10-10 4.477-10 10-10m0 2a8 8 0 1 0 0 16 8 8 0 0 0 0-16m3.833 3.337a.596.596 0 0 1 .763.067.59.59 0 0 1 .063.76q-3.27 4.569-3.598 4.897a1.5 1.5 0 0 1 -2.122-2.122q.56-.56 4.894-3.602m1.667 3.663a1 1 0 1 1 0 2 1 1 0 0 1 0-2m-11 0a1 1 0 1 1 0 2 1 1 0 0 1 0-2m2.318-3.596a1 1 0 1 1 -1.414 1.414 1 1 0 0 1 1.414-1.414m3.182-1.904a1 1 0 1 1 0 2 1 1 0 0 1 0-2"/></svg>
|
||||
|
After Width: | Height: | Size: 507 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path d="M22 4a1 1 0 0 0-1 1v5.666l-9.223-6.148a.5.5 0 0 0-.777.416v5.732L1.777 4.518A.5.5 0 0 0 1 4.934v14.132a.5.5 0 0 0 .777.416L11 13.333v5.733a.5.5 0 0 0 .777.416L21 13.333V19a1 1 0 1 0 2 0V5a1 1 0 0 0-1-1M3 7.737L9.394 12L3 16.263zm10 8.526V7.737L19.394 12z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 360 B |
@@ -0,0 +1 @@
|
||||
<svg height="512" viewBox="0 0 512 512" width="512" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round"><path d="m158.61169 32.411431 327.37685 189.011119-327.37685 189.01111z" stroke-width="43.2072" transform="matrix(.75450544 0 0 .77725456 100.32662 82.344678)"/><path d="m106.51372 100v309.27423" stroke-width="76.6719"/></g></svg>
|
||||
|
After Width: | Height: | Size: 393 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path d="m12 13.333l-9.223 6.149A.5.5 0 0 1 2 19.066V4.934a.5.5 0 0 1 .777-.416L12 10.667V4.934a.5.5 0 0 1 .777-.416l10.599 7.066a.5.5 0 0 1 0 .832l-10.599 7.066a.5.5 0 0 1-.777-.416zM10.394 12L4 7.737v8.526zM14 7.737v8.526L20.394 12z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 331 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" d="M10 7.22L6.603 10H3v4h3.603L10 16.78zM5.889 16H2a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1h3.889l5.294-4.332a.5.5 0 0 1 .817.387v15.89a.5.5 0 0 1-.817.387zm14.525-4l3.536 3.536l-1.415 1.414L19 13.414l-3.536 3.536l-1.414-1.414L17.586 12L14.05 8.465l1.414-1.415L19 10.586l3.535-3.536l1.415 1.415z"/></svg>
|
||||
|
After Width: | Height: | Size: 403 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path fill="#fff" d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2m0 2a8 8 0 1 0 0 16a8 8 0 0 0 0-16m3.833 3.337a.596.596 0 0 1 .763.067a.59.59 0 0 1 .063.76q-3.27 4.569-3.598 4.897a1.5 1.5 0 0 1-2.122-2.122q.56-.56 4.894-3.602M17.5 11a1 1 0 1 1 0 2a1 1 0 0 1 0-2m-11 0a1 1 0 1 1 0 2a1 1 0 0 1 0-2m2.318-3.596a1 1 0 1 1-1.414 1.414a1 1 0 0 1 1.414-1.414M12 5.5a1 1 0 1 1 0 2a1 1 0 0 1 0-2" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 510 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path fill="#fff" d="M22 4a1 1 0 0 0-1 1v5.666l-9.223-6.148a.5.5 0 0 0-.777.416v5.732L1.777 4.518A.5.5 0 0 0 1 4.934v14.132a.5.5 0 0 0 .777.416L11 13.333v5.733a.5.5 0 0 0 .777.416L21 13.333V19a1 1 0 1 0 2 0V5a1 1 0 0 0-1-1M3 7.737L9.394 12L3 16.263zm10 8.526V7.737L19.394 12z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 372 B |
@@ -0,0 +1 @@
|
||||
<svg height="512" viewBox="0 0 512 512" width="512" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round"><path d="m158.61169 32.411431 327.37685 189.011119-327.37685 189.01111z" stroke-width="43.2072" transform="matrix(.75450544 0 0 .77725456 100.32662 82.344678)"/><path d="m106.51372 100v309.27423" stroke-width="76.6719"/></g></svg>
|
||||
|
After Width: | Height: | Size: 393 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path fill="#fff" d="m12 13.333l-9.223 6.149A.5.5 0 0 1 2 19.066V4.934a.5.5 0 0 1 .777-.416L12 10.667V4.934a.5.5 0 0 1 .777-.416l10.599 7.066a.5.5 0 0 1 0 .832l-10.599 7.066a.5.5 0 0 1-.777-.416zM10.394 12L4 7.737v8.526zM14 7.737v8.526L20.394 12z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 343 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="#fff" d="M10 7.22L6.603 10H3v4h3.603L10 16.78zM5.889 16H2a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1h3.889l5.294-4.332a.5.5 0 0 1 .817.387v15.89a.5.5 0 0 1-.817.387zm14.525-4l3.536 3.536l-1.415 1.414L19 13.414l-3.536 3.536l-1.414-1.414L17.586 12L14.05 8.465l1.414-1.415L19 10.586l3.535-3.536l1.415 1.415z"/></svg>
|
||||
|
After Width: | Height: | Size: 395 B |
@@ -18,6 +18,7 @@
|
||||
<file>icons/black/svg/close-line.svg</file>
|
||||
<file>icons/black/svg/controller-line.svg</file>
|
||||
<file>icons/black/svg/controller-strike-line.svg</file>
|
||||
<file>icons/black/svg/dashboard-line.svg</file>
|
||||
<file>icons/black/svg/debug-step-into-line.svg</file>
|
||||
<file>icons/black/svg/debug-step-out-line.svg</file>
|
||||
<file>icons/black/svg/debug-step-over-line.svg</file>
|
||||
@@ -32,6 +33,7 @@
|
||||
<file>icons/black/svg/eject-line.svg</file>
|
||||
<file>icons/black/svg/emulation-line.svg</file>
|
||||
<file>icons/black/svg/eyetoy-line.svg</file>
|
||||
<file>icons/black/svg/fast-forward-line.svg</file>
|
||||
<file>icons/black/svg/file-add-line.svg</file>
|
||||
<file>icons/black/svg/file-line.svg</file>
|
||||
<file>icons/black/svg/file-list-line.svg</file>
|
||||
@@ -90,6 +92,8 @@
|
||||
<file>icons/black/svg/settings-3-line.svg</file>
|
||||
<file>icons/black/svg/shut-down-line.svg</file>
|
||||
<file>icons/black/svg/singstar-line.svg</file>
|
||||
<file>icons/black/svg/slow-mo.svg</file>
|
||||
<file>icons/black/svg/speed-line.svg</file>
|
||||
<file>icons/black/svg/tools-line.svg</file>
|
||||
<file>icons/black/svg/train-line.svg</file>
|
||||
<file>icons/black/svg/trance-vibrator-line.svg</file>
|
||||
@@ -97,6 +101,7 @@
|
||||
<file>icons/black/svg/trophy-line.svg</file>
|
||||
<file>icons/black/svg/tv-2-line.svg</file>
|
||||
<file>icons/black/svg/usb-fill.svg</file>
|
||||
<file>icons/black/svg/volume-mute-line.svg</file>
|
||||
<file>icons/black/svg/volume-up-line.svg</file>
|
||||
<file>icons/black/svg/warning-line.svg</file>
|
||||
<file>icons/black/svg/wheel-line.svg</file>
|
||||
@@ -123,6 +128,7 @@
|
||||
<file>icons/white/svg/close-line.svg</file>
|
||||
<file>icons/white/svg/controller-line.svg</file>
|
||||
<file>icons/white/svg/controller-strike-line.svg</file>
|
||||
<file>icons/white/svg/dashboard-line.svg</file>
|
||||
<file>icons/white/svg/debug-step-into-line.svg</file>
|
||||
<file>icons/white/svg/debug-step-out-line.svg</file>
|
||||
<file>icons/white/svg/debug-step-over-line.svg</file>
|
||||
@@ -137,6 +143,7 @@
|
||||
<file>icons/white/svg/eject-line.svg</file>
|
||||
<file>icons/white/svg/emulation-line.svg</file>
|
||||
<file>icons/white/svg/eyetoy-line.svg</file>
|
||||
<file>icons/white/svg/fast-forward-line.svg</file>
|
||||
<file>icons/white/svg/file-add-line.svg</file>
|
||||
<file>icons/white/svg/file-line.svg</file>
|
||||
<file>icons/white/svg/file-list-line.svg</file>
|
||||
@@ -198,6 +205,8 @@
|
||||
<file>icons/white/svg/settings-3-line.svg</file>
|
||||
<file>icons/white/svg/shut-down-line.svg</file>
|
||||
<file>icons/white/svg/singstar-line.svg</file>
|
||||
<file>icons/white/svg/slow-mo.svg</file>
|
||||
<file>icons/white/svg/speed-line.svg</file>
|
||||
<file>icons/white/svg/tools-line.svg</file>
|
||||
<file>icons/white/svg/train-line.svg</file>
|
||||
<file>icons/white/svg/trance-vibrator-line.svg</file>
|
||||
@@ -205,6 +214,7 @@
|
||||
<file>icons/white/svg/trophy-line.svg</file>
|
||||
<file>icons/white/svg/tv-2-line.svg</file>
|
||||
<file>icons/white/svg/usb-fill.svg</file>
|
||||
<file>icons/white/svg/volume-mute-line.svg</file>
|
||||
<file>icons/white/svg/volume-up-line.svg</file>
|
||||
<file>icons/white/svg/warning-line.svg</file>
|
||||
<file>icons/white/svg/wheel-line.svg</file>
|
||||
|
||||
@@ -183,6 +183,8 @@ namespace Achievements
|
||||
static void DrawLeaderboardEntry(const rc_client_leaderboard_entry_t& entry, bool is_self, float rank_column_width,
|
||||
float name_column_width, float time_column_width, float column_spacing);
|
||||
static void OpenSubset(const rc_client_subset_t* subset);
|
||||
static bool DrawSubsetSidebar(float sidebar_width, bool& sidebar_has_focus, bool leaderboards_only);
|
||||
static void DrawSubsetSidebarFooter(bool sidebar_has_focus);
|
||||
static void OpenLeaderboard(const rc_client_leaderboard_t* lboard);
|
||||
static void LeaderboardFetchNearbyCallback(
|
||||
int result, const char* error_message, rc_client_leaderboard_entry_list_t* list, rc_client_t* client, void* callback_userdata);
|
||||
@@ -237,6 +239,9 @@ namespace Achievements
|
||||
static float s_leaderboard_list_scroll = 0.0f;
|
||||
static bool s_restore_leaderboard_scroll = false;
|
||||
|
||||
static std::map<std::pair<u32, u32>, bool> s_achievement_buckets_collapsed;
|
||||
static std::map<std::pair<u32, u8>, bool> s_leaderboard_buckets_collapsed;
|
||||
|
||||
static std::vector<LeaderboardTrackerIndicator> s_active_leaderboard_trackers;
|
||||
static std::vector<AchievementChallengeIndicator> s_active_challenge_indicators;
|
||||
static std::optional<AchievementProgressIndicator> s_active_progress_indicator;
|
||||
@@ -745,11 +750,18 @@ void Achievements::ClientServerCall(
|
||||
{
|
||||
HTTPDownloader::Request::Callback hd_callback = [callback, callback_data](s32 status_code, const std::string& content_type, HTTPDownloader::Request::Data data)
|
||||
{
|
||||
const bool is_error = (status_code <= 0);
|
||||
const bool is_retryable =
|
||||
is_error && (status_code == HTTPDownloader::HTTP_STATUS_TIMEOUT);
|
||||
|
||||
rc_api_server_response_t rr;
|
||||
rr.http_status_code = (status_code <= 0) ? (status_code == HTTPDownloader::HTTP_STATUS_CANCELLED ?
|
||||
RC_API_SERVER_RESPONSE_CLIENT_ERROR :
|
||||
RC_API_SERVER_RESPONSE_RETRYABLE_CLIENT_ERROR)
|
||||
: status_code;
|
||||
rr.http_status_code =
|
||||
is_error
|
||||
? (is_retryable
|
||||
? RC_API_SERVER_RESPONSE_RETRYABLE_CLIENT_ERROR
|
||||
: RC_API_SERVER_RESPONSE_CLIENT_ERROR)
|
||||
: status_code;
|
||||
|
||||
rr.body_length = data.size();
|
||||
rr.body = reinterpret_cast<const char*>(data.data());
|
||||
|
||||
@@ -927,6 +939,17 @@ void Achievements::UpdateRichPresence(std::unique_lock<std::recursive_mutex>& lo
|
||||
lock.lock();
|
||||
}
|
||||
|
||||
void Achievements::PlayAchievementSound(bool is_specific_sound_enabled, const std::string& custom_sound_name, const std::string& default_sound_name)
|
||||
{
|
||||
if (!EmuConfig.Achievements.SoundEffects || !is_specific_sound_enabled)
|
||||
return;
|
||||
|
||||
if (custom_sound_name.empty() || !FileSystem::FileExists(custom_sound_name.c_str()))
|
||||
Common::PlaySoundAsync(Path::Combine(EmuFolders::Resources, default_sound_name).c_str());
|
||||
else
|
||||
Common::PlaySoundAsync(custom_sound_name.c_str());
|
||||
}
|
||||
|
||||
void Achievements::GameChanged(u32 disc_crc, u32 crc)
|
||||
{
|
||||
std::unique_lock lock(s_achievements_mutex);
|
||||
@@ -1144,13 +1167,7 @@ void Achievements::DisplayAchievementSummary()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (EmuConfig.Achievements.SoundEffects && EmuConfig.Achievements.InfoSound)
|
||||
Common::PlaySoundAsync(
|
||||
(EmuConfig.Achievements.InfoSoundName.empty()
|
||||
? Path::Combine(EmuFolders::Resources, DEFAULT_INFO_SOUND_NAME)
|
||||
: EmuConfig.Achievements.InfoSoundName).c_str()
|
||||
);
|
||||
Achievements::PlayAchievementSound(EmuConfig.Achievements.InfoSound, EmuConfig.Achievements.InfoSoundName, DEFAULT_INFO_SOUND_NAME);
|
||||
}
|
||||
|
||||
void Achievements::DisplayHardcoreDeferredMessage()
|
||||
@@ -1200,13 +1217,7 @@ void Achievements::HandleUnlockEvent(const rc_client_event_t* event)
|
||||
std::move(title), std::move(summary), std::move(badge_path));
|
||||
});
|
||||
}
|
||||
|
||||
if (EmuConfig.Achievements.SoundEffects && EmuConfig.Achievements.UnlockSound)
|
||||
Common::PlaySoundAsync(
|
||||
(EmuConfig.Achievements.UnlockSoundName.empty()
|
||||
? Path::Combine(EmuFolders::Resources, DEFAULT_UNLOCK_SOUND_NAME)
|
||||
: EmuConfig.Achievements.UnlockSoundName).c_str()
|
||||
);
|
||||
Achievements::PlayAchievementSound(EmuConfig.Achievements.UnlockSound, EmuConfig.Achievements.UnlockSoundName, DEFAULT_UNLOCK_SOUND_NAME);
|
||||
}
|
||||
|
||||
void Achievements::HandleGameCompleteEvent(const rc_client_event_t* event)
|
||||
@@ -1325,13 +1336,7 @@ void Achievements::HandleLeaderboardSubmittedEvent(const rc_client_event_t* even
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (EmuConfig.Achievements.SoundEffects && EmuConfig.Achievements.LBSubmitSound)
|
||||
Common::PlaySoundAsync(
|
||||
(EmuConfig.Achievements.LBSubmitSoundName.empty()
|
||||
? Path::Combine(EmuFolders::Resources, DEFAULT_LBSUBMIT_SOUND_NAME)
|
||||
: EmuConfig.Achievements.LBSubmitSoundName).c_str()
|
||||
);
|
||||
Achievements::PlayAchievementSound(EmuConfig.Achievements.LBSubmitSound, EmuConfig.Achievements.LBSubmitSoundName, DEFAULT_LBSUBMIT_SOUND_NAME);
|
||||
}
|
||||
|
||||
void Achievements::HandleLeaderboardScoreboardEvent(const rc_client_event_t* event)
|
||||
@@ -2025,6 +2030,9 @@ void Achievements::ClearUIState()
|
||||
rc_client_destroy_achievement_list(s_achievement_list);
|
||||
s_achievement_list = nullptr;
|
||||
}
|
||||
|
||||
s_achievement_buckets_collapsed.clear();
|
||||
s_leaderboard_buckets_collapsed.clear();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -2412,6 +2420,9 @@ bool Achievements::PrepareAchievementsWindow()
|
||||
CloseSubset();
|
||||
|
||||
s_subset_list = rc_client_create_subset_list(s_client);
|
||||
if (!s_subset_list)
|
||||
Console.Warning("Achievements: rc_client_create_subset_list() returned null");
|
||||
s_achievement_buckets_collapsed.clear();
|
||||
|
||||
if (s_achievement_list)
|
||||
rc_client_destroy_achievement_list(s_achievement_list);
|
||||
@@ -2426,6 +2437,131 @@ bool Achievements::PrepareAchievementsWindow()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Achievements::DrawSubsetSidebar(float sidebar_width, bool& sidebar_has_focus, bool leaderboards_only)
|
||||
{
|
||||
if (!s_subset_list)
|
||||
return false;
|
||||
|
||||
using ImGuiFullscreen::g_medium_font;
|
||||
using ImGuiFullscreen::LayoutScale;
|
||||
|
||||
static constexpr float alpha = 0.8f;
|
||||
const ImVec4 sidebar_bg(0.10f, 0.10f, 0.10f, alpha);
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, sidebar_bg);
|
||||
ImGui::PushStyleColor(ImGuiCol_NavCursor, IM_COL32(0, 0, 0, 0));
|
||||
|
||||
bool subset_selected = false;
|
||||
|
||||
if (ImGui::BeginChild("subset_sidebar", ImVec2(sidebar_width, 0.0f), 0, 0))
|
||||
{
|
||||
sidebar_has_focus = ImGui::IsWindowFocused();
|
||||
ImGuiFullscreen::BeginMenuButtons();
|
||||
|
||||
const float padding = LayoutScale(10.0f);
|
||||
const float spacing = LayoutScale(8.0f);
|
||||
const float icon_size = LayoutScale(28.0f);
|
||||
|
||||
for (u32 i = 0; i < s_subset_list->num_subsets; i++)
|
||||
{
|
||||
const rc_client_subset_t* subset = s_subset_list->subsets[i];
|
||||
if (leaderboards_only && subset->num_leaderboards == 0)
|
||||
continue;
|
||||
|
||||
TinyString id_str;
|
||||
id_str.format("sidebar_subset_{}", subset->id);
|
||||
|
||||
ImRect bb;
|
||||
bool visible, hovered;
|
||||
ImGuiFullscreen::MenuButtonFrame(id_str, true, ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY,
|
||||
&visible, &hovered, &bb.Min, &bb.Max, 0, 1.0f);
|
||||
|
||||
if (s_open_subset && s_open_subset->id == subset->id)
|
||||
{
|
||||
ImGui::GetWindowDrawList()->AddRectFilled(
|
||||
ImVec2(bb.Min.x, bb.Min.y), ImVec2(bb.Min.x + LayoutScale(4.0f), bb.Max.y), IM_COL32(52, 152, 219, 255));
|
||||
}
|
||||
|
||||
const ImVec2 icon_min(bb.Min.x + padding, bb.Min.y + (bb.Max.y - bb.Min.y - icon_size) * 0.5f);
|
||||
const ImVec2 icon_max(icon_min.x + icon_size, icon_min.y + icon_size);
|
||||
|
||||
std::string badge_path = GetSubsetBadgePath(subset);
|
||||
if (!badge_path.empty())
|
||||
{
|
||||
GSTexture* badge = ImGuiFullscreen::GetCachedTextureAsync(badge_path);
|
||||
if (badge)
|
||||
{
|
||||
ImGui::GetWindowDrawList()->AddImage(reinterpret_cast<ImTextureID>(badge->GetNativeHandle()),
|
||||
icon_min, icon_max, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
|
||||
}
|
||||
}
|
||||
|
||||
const ImRect text_bb(ImVec2(icon_max.x + spacing, bb.Min.y), ImVec2(bb.Max.x - padding, bb.Max.y));
|
||||
ImGui::PushFont(g_medium_font.first, g_medium_font.second);
|
||||
ImGuiFullscreen::RenderTextClippedWithShadow(
|
||||
text_bb.Min, text_bb.Max, subset->title, nullptr, nullptr, ImVec2(0.0f, 0.5f), &text_bb);
|
||||
ImGui::PopFont();
|
||||
|
||||
if (ImGui::IsItemClicked() || ImGui::IsItemActivated())
|
||||
{
|
||||
OpenSubset(subset);
|
||||
subset_selected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ImGuiFullscreen::EndMenuButtons();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::PopStyleColor(2);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f));
|
||||
ImGui::SameLine();
|
||||
ImGui::PopStyleVar();
|
||||
return subset_selected;
|
||||
}
|
||||
|
||||
void Achievements::DrawSubsetSidebarFooter(bool sidebar_has_focus)
|
||||
{
|
||||
if (ImGuiFullscreen::IsGamepadInputSource())
|
||||
{
|
||||
const bool circleOK = ImGui::GetIO().ConfigNavSwapGamepadButtons;
|
||||
const auto glyphs = ImGuiFullscreen::GetGamepadGlyphs();
|
||||
if (sidebar_has_focus)
|
||||
{
|
||||
ImGuiFullscreen::SetFullscreenFooterText(std::array{
|
||||
std::make_pair(glyphs.dpad_ud, TRANSLATE_SV("Achievements", "Navigate Subsets")),
|
||||
std::make_pair(glyphs.confirm(circleOK), TRANSLATE_SV("Achievements", "Select")),
|
||||
std::make_pair(glyphs.cancel(circleOK), TRANSLATE_SV("Achievements", "Back")),
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGuiFullscreen::SetFullscreenFooterText(std::array{
|
||||
std::make_pair(glyphs.dpad_ud, TRANSLATE_SV("Achievements", "Change Selection")),
|
||||
std::make_pair(glyphs.cancel(circleOK), TRANSLATE_SV("Achievements", "Subsets")),
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sidebar_has_focus)
|
||||
{
|
||||
ImGuiFullscreen::SetFullscreenFooterText(std::array{
|
||||
std::make_pair(ICON_PF_ARROW_UP ICON_PF_ARROW_DOWN, TRANSLATE_SV("Achievements", "Navigate Subsets")),
|
||||
std::make_pair(ICON_PF_ENTER, TRANSLATE_SV("Achievements", "Select")),
|
||||
std::make_pair(ICON_PF_ESC, TRANSLATE_SV("Achievements", "Back")),
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGuiFullscreen::SetFullscreenFooterText(std::array{
|
||||
std::make_pair(ICON_PF_ARROW_UP ICON_PF_ARROW_DOWN, TRANSLATE_SV("Achievements", "Change Selection")),
|
||||
std::make_pair(ICON_PF_ESC, TRANSLATE_SV("Achievements", "Subsets")),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Achievements::DrawAchievementsWindow()
|
||||
{
|
||||
using ImGuiFullscreen::g_large_font;
|
||||
@@ -2442,7 +2578,10 @@ void Achievements::DrawAchievementsWindow()
|
||||
Achievements::IdleUpdate();
|
||||
|
||||
const bool has_multiple_subsets = (s_subset_list && s_subset_list->num_subsets > 1);
|
||||
bool s_sidebar_has_focus = false;
|
||||
static bool sidebar_has_focus = false;
|
||||
static bool focus_sidebar = false;
|
||||
if (!has_multiple_subsets)
|
||||
sidebar_has_focus = false;
|
||||
|
||||
static constexpr float alpha = 0.8f;
|
||||
static constexpr float heading_alpha = 0.95f;
|
||||
@@ -2486,11 +2625,18 @@ void Achievements::DrawAchievementsWindow()
|
||||
SmallString text;
|
||||
ImVec2 text_size;
|
||||
|
||||
const bool wants_close = ImGuiFullscreen::WantsToCloseMenu();
|
||||
if (ImGuiFullscreen::FloatingButton(ICON_FA_SQUARE_XMARK, 10.0f, 10.0f, -1.0f, -1.0f, 1.0f, 0.0f, true, g_large_font) ||
|
||||
ImGuiFullscreen::WantsToCloseMenu())
|
||||
(wants_close && (!has_multiple_subsets || sidebar_has_focus)))
|
||||
{
|
||||
sidebar_has_focus = false;
|
||||
FullscreenUI::ReturnToPreviousWindow();
|
||||
}
|
||||
else if (has_multiple_subsets && !sidebar_has_focus && wants_close)
|
||||
{
|
||||
sidebar_has_focus = true;
|
||||
focus_sidebar = true;
|
||||
}
|
||||
|
||||
const ImRect title_bb(ImVec2(left, top), ImVec2(right, top + GetLineHeight(g_large_font)));
|
||||
text.assign(s_open_subset ? s_open_subset->title : s_game_title);
|
||||
@@ -2582,75 +2728,13 @@ void Achievements::DrawAchievementsWindow()
|
||||
{
|
||||
if (has_multiple_subsets)
|
||||
{
|
||||
const ImVec4 sidebar_bg(0.10f, 0.10f, 0.10f, alpha);
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, sidebar_bg);
|
||||
|
||||
if (ImGui::BeginChild("subset_sidebar", ImVec2(sidebar_width, 0.0f), ImGuiChildFlags_NavFlattened, 0))
|
||||
if (focus_sidebar)
|
||||
{
|
||||
ImGuiFullscreen::BeginMenuButtons();
|
||||
|
||||
for (u32 i = 0; i < s_subset_list->num_subsets; i++)
|
||||
{
|
||||
const rc_client_subset_t* subset = s_subset_list->subsets[i];
|
||||
TinyString id_str;
|
||||
id_str.format("sidebar_subset_{}", subset->id);
|
||||
|
||||
ImRect bb;
|
||||
bool visible, hovered;
|
||||
ImGuiFullscreen::MenuButtonFrame(id_str, true, ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY,
|
||||
&visible, &hovered, &bb.Min, &bb.Max, 0, 1.0f);
|
||||
|
||||
if (ImGui::IsItemHovered() || ImGui::IsItemFocused())
|
||||
s_sidebar_has_focus = true;
|
||||
|
||||
const float padding = LayoutScale(10.0f);
|
||||
const float spacing = LayoutScale(8.0f);
|
||||
const float icon_size = LayoutScale(28.0f);
|
||||
|
||||
if (s_open_subset && s_open_subset->id == subset->id)
|
||||
{
|
||||
const float line_width = LayoutScale(4.0f);
|
||||
const ImVec2 line_min(bb.Min.x, bb.Min.y);
|
||||
const ImVec2 line_max(bb.Min.x + line_width, bb.Max.y);
|
||||
ImGui::GetWindowDrawList()->AddRectFilled(line_min, line_max, IM_COL32(52, 152, 219, 255));
|
||||
}
|
||||
|
||||
const ImVec2 icon_min(bb.Min.x + padding, bb.Min.y + (bb.Max.y - bb.Min.y - icon_size) * 0.5f);
|
||||
const ImVec2 icon_max(icon_min.x + icon_size, icon_min.y + icon_size);
|
||||
|
||||
std::string badge_path = GetSubsetBadgePath(subset);
|
||||
if (!badge_path.empty())
|
||||
{
|
||||
GSTexture* badge = ImGuiFullscreen::GetCachedTextureAsync(badge_path);
|
||||
if (badge)
|
||||
{
|
||||
ImGui::GetWindowDrawList()->AddImage(reinterpret_cast<ImTextureID>(badge->GetNativeHandle()),
|
||||
icon_min, icon_max, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
|
||||
}
|
||||
}
|
||||
|
||||
const float text_x = icon_max.x + spacing;
|
||||
const ImRect text_bb(ImVec2(text_x, bb.Min.y), ImVec2(bb.Max.x - padding, bb.Max.y));
|
||||
|
||||
ImGui::PushFont(g_medium_font.first, g_medium_font.second);
|
||||
ImGuiFullscreen::RenderTextClippedWithShadow(
|
||||
text_bb.Min, text_bb.Max, subset->title, nullptr, nullptr, ImVec2(0.0f, 0.5f), &text_bb);
|
||||
ImGui::PopFont();
|
||||
|
||||
if (ImGui::IsItemClicked() || ImGui::IsItemActivated())
|
||||
OpenSubset(subset);
|
||||
}
|
||||
|
||||
ImGuiFullscreen::EndMenuButtons();
|
||||
ImGui::SetNextWindowFocus();
|
||||
focus_sidebar = false;
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
if (ImGui::IsItemHovered() || ImGui::IsItemFocused())
|
||||
s_sidebar_has_focus = true;
|
||||
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
ImGui::SameLine();
|
||||
if (DrawSubsetSidebar(sidebar_width, sidebar_has_focus, false))
|
||||
ImGui::SetNextWindowFocus();
|
||||
}
|
||||
|
||||
const float content_width = has_multiple_subsets ? (display_size.x - sidebar_width) : display_size.x;
|
||||
@@ -2660,12 +2744,9 @@ void Achievements::DrawAchievementsWindow()
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImGui::GetColorU32(ImGuiFullscreen::UIPrimaryColor));
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImGui::GetColorU32(ImGuiFullscreen::UISecondaryColor));
|
||||
|
||||
if (ImGui::BeginChild("achievements_content", ImVec2(content_width, 0.0f), ImGuiChildFlags_NavFlattened, 0))
|
||||
if (ImGui::BeginChild("achievements_content", ImVec2(content_width, 0.0f), 0, 0))
|
||||
{
|
||||
if (ImGui::IsWindowHovered() || ImGui::IsWindowFocused())
|
||||
s_sidebar_has_focus = false;
|
||||
|
||||
static std::map<std::pair<u32, u32>, bool> buckets_collapsed;
|
||||
static const char* bucket_names[NUM_RC_CLIENT_ACHIEVEMENT_BUCKETS] = {
|
||||
TRANSLATE_NOOP("Achievements", "Unknown"),
|
||||
TRANSLATE_NOOP("Achievements", "Locked"),
|
||||
@@ -2694,7 +2775,7 @@ void Achievements::DrawAchievementsWindow()
|
||||
|
||||
pxAssert(bucket.bucket_type < NUM_RC_CLIENT_ACHIEVEMENT_BUCKETS);
|
||||
|
||||
bool& bucket_collapsed = buckets_collapsed[std::make_pair(bucket.subset_id, bucket.bucket_type)];
|
||||
bool& bucket_collapsed = s_achievement_buckets_collapsed[std::make_pair(bucket.subset_id, bucket.bucket_type)];
|
||||
const char* translated_bucket_name = Host::TranslateToCString("Achievements", bucket_names[bucket.bucket_type]);
|
||||
|
||||
bucket_collapsed ^=
|
||||
@@ -2719,54 +2800,9 @@ void Achievements::DrawAchievementsWindow()
|
||||
ImGuiFullscreen::EndFullscreenWindow();
|
||||
|
||||
if (has_multiple_subsets)
|
||||
{
|
||||
if (ImGuiFullscreen::IsGamepadInputSource())
|
||||
{
|
||||
const bool circleOK = ImGui::GetIO().ConfigNavSwapGamepadButtons;
|
||||
const auto glyphs = ImGuiFullscreen::GetGamepadGlyphs();
|
||||
if (s_sidebar_has_focus)
|
||||
{
|
||||
ImGuiFullscreen::SetFullscreenFooterText(std::array{
|
||||
std::make_pair(glyphs.dpad_ud, TRANSLATE_SV("Achievements", "Navigate Subsets")),
|
||||
std::make_pair(glyphs.dpad_lr, TRANSLATE_SV("Achievements", "Back to List")),
|
||||
std::make_pair(glyphs.confirm(circleOK), TRANSLATE_SV("Achievements", "Select")),
|
||||
std::make_pair(glyphs.cancel(circleOK), TRANSLATE_SV("Achievements", "Back")),
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGuiFullscreen::SetFullscreenFooterText(std::array{
|
||||
std::make_pair(glyphs.dpad_lr, TRANSLATE_SV("Achievements", "Navigate Subsets")),
|
||||
std::make_pair(glyphs.dpad_ud, TRANSLATE_SV("Achievements", "Change Selection")),
|
||||
std::make_pair(glyphs.cancel(circleOK), TRANSLATE_SV("Achievements", "Back")),
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (s_sidebar_has_focus)
|
||||
{
|
||||
ImGuiFullscreen::SetFullscreenFooterText(std::array{
|
||||
std::make_pair(ICON_PF_ARROW_UP ICON_PF_ARROW_DOWN, TRANSLATE_SV("Achievements", "Navigate Subsets")),
|
||||
std::make_pair(ICON_PF_ARROW_LEFT ICON_PF_ARROW_RIGHT, TRANSLATE_SV("Achievements", "Back to List")),
|
||||
std::make_pair(ICON_PF_ENTER, TRANSLATE_SV("Achievements", "Select")),
|
||||
std::make_pair(ICON_PF_ESC, TRANSLATE_SV("Achievements", "Back")),
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGuiFullscreen::SetFullscreenFooterText(std::array{
|
||||
std::make_pair(ICON_PF_ARROW_LEFT ICON_PF_ARROW_RIGHT, TRANSLATE_SV("Achievements", "Navigate Subsets")),
|
||||
std::make_pair(ICON_PF_ARROW_UP ICON_PF_ARROW_DOWN, TRANSLATE_SV("Achievements", "Change Selection")),
|
||||
std::make_pair(ICON_PF_ESC, TRANSLATE_SV("Achievements", "Back")),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
DrawSubsetSidebarFooter(sidebar_has_focus);
|
||||
else
|
||||
{
|
||||
FullscreenUI::SetStandardSelectionFooterText(true);
|
||||
}
|
||||
}
|
||||
|
||||
void Achievements::DrawAchievement(const rc_client_achievement_t* cheevo)
|
||||
@@ -2982,9 +3018,21 @@ bool Achievements::PrepareLeaderboardsWindow()
|
||||
|
||||
s_achievement_badge_paths = {};
|
||||
CloseLeaderboard();
|
||||
|
||||
if (s_subset_list)
|
||||
{
|
||||
rc_client_destroy_subset_list(s_subset_list);
|
||||
s_subset_list = nullptr;
|
||||
}
|
||||
CloseSubset();
|
||||
s_subset_list = rc_client_create_subset_list(client);
|
||||
if (!s_subset_list)
|
||||
Console.Warning("Achievements: rc_client_create_subset_list() returned null");
|
||||
s_leaderboard_buckets_collapsed.clear();
|
||||
|
||||
if (s_leaderboard_list)
|
||||
rc_client_destroy_leaderboard_list(s_leaderboard_list);
|
||||
s_leaderboard_list = rc_client_create_leaderboard_list(client, RC_CLIENT_LEADERBOARD_LIST_GROUPING_NONE);
|
||||
s_leaderboard_list = rc_client_create_leaderboard_list(client, RC_CLIENT_LEADERBOARD_LIST_GROUPING_TRACKING);
|
||||
if (!s_leaderboard_list)
|
||||
{
|
||||
Console.Error("Achievements: rc_client_create_leaderboard_list() returned null");
|
||||
@@ -2997,6 +3045,18 @@ bool Achievements::PrepareLeaderboardsWindow()
|
||||
s_restore_leaderboard_scroll = false;
|
||||
s_leaderboard_list_scroll = 0.0f;
|
||||
|
||||
if (s_subset_list)
|
||||
{
|
||||
for (u32 i = 0; i < s_subset_list->num_subsets; i++)
|
||||
{
|
||||
if (s_subset_list->subsets[i]->num_leaderboards > 0)
|
||||
{
|
||||
OpenSubset(s_subset_list->subsets[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3020,6 +3080,14 @@ void Achievements::DrawLeaderboardsWindow()
|
||||
const bool is_leaderboard_open = (s_open_leaderboard != nullptr);
|
||||
bool close_leaderboard_on_exit = false;
|
||||
|
||||
const bool has_multiple_subsets = (s_subset_list && std::count_if(s_subset_list->subsets,
|
||||
s_subset_list->subsets + s_subset_list->num_subsets,
|
||||
[](const rc_client_subset_t* subset) { return subset->num_leaderboards > 0; }) > 1);
|
||||
static bool sidebar_has_focus = false;
|
||||
static bool focus_sidebar = false;
|
||||
if (!has_multiple_subsets || is_leaderboard_open)
|
||||
sidebar_has_focus = false;
|
||||
|
||||
ImRect bb;
|
||||
|
||||
const ImVec4 background(0.13f, 0.13f, 0.13f, alpha);
|
||||
@@ -3082,11 +3150,18 @@ void Achievements::DrawLeaderboardsWindow()
|
||||
|
||||
if (!is_leaderboard_open)
|
||||
{
|
||||
const bool wants_close = ImGuiFullscreen::WantsToCloseMenu();
|
||||
if (ImGuiFullscreen::FloatingButton(ICON_FA_SQUARE_XMARK, 10.0f, 10.0f, -1.0f, -1.0f, 1.0f, 0.0f, true, g_large_font) ||
|
||||
ImGuiFullscreen::WantsToCloseMenu())
|
||||
(wants_close && (!has_multiple_subsets || sidebar_has_focus)))
|
||||
{
|
||||
sidebar_has_focus = false;
|
||||
FullscreenUI::ReturnToPreviousWindow();
|
||||
}
|
||||
else if (has_multiple_subsets && !sidebar_has_focus && wants_close)
|
||||
{
|
||||
sidebar_has_focus = true;
|
||||
focus_sidebar = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3243,36 +3318,105 @@ void Achievements::DrawLeaderboardsWindow()
|
||||
|
||||
if (!is_leaderboard_open)
|
||||
{
|
||||
const float sidebar_width = has_multiple_subsets ? LayoutScale(200.0f) : 0.0f;
|
||||
|
||||
if (ImGuiFullscreen::BeginFullscreenWindow(
|
||||
ImVec2(0.0f, heading_height),
|
||||
ImVec2(display_size.x, display_size.y - heading_height - LayoutScale(ImGuiFullscreen::LAYOUT_FOOTER_HEIGHT)),
|
||||
"leaderboards", background, 0.0f, ImVec2(ImGuiFullscreen::LAYOUT_MENU_WINDOW_X_PADDING, 0.0f), 0))
|
||||
"leaderboards", background, 0.0f, ImVec2(0.0f, 0.0f), 0))
|
||||
{
|
||||
if (s_restore_leaderboard_scroll)
|
||||
if (has_multiple_subsets)
|
||||
{
|
||||
ImGui::SetScrollY(s_leaderboard_list_scroll);
|
||||
s_restore_leaderboard_scroll = false;
|
||||
if (focus_sidebar)
|
||||
{
|
||||
ImGui::SetNextWindowFocus();
|
||||
focus_sidebar = false;
|
||||
}
|
||||
if (DrawSubsetSidebar(sidebar_width, sidebar_has_focus, true))
|
||||
ImGui::SetNextWindowFocus();
|
||||
}
|
||||
|
||||
const bool had_pending_focus_request = s_restore_leaderboard_focus;
|
||||
const float content_width = has_multiple_subsets ? (display_size.x - sidebar_width) : display_size.x;
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(ImGuiFullscreen::LAYOUT_MENU_WINDOW_X_PADDING, 0.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, background);
|
||||
ImGui::PushStyleColor(ImGuiCol_Header, ImGui::GetColorU32(ImGuiFullscreen::UIPrimaryDarkColor));
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImGui::GetColorU32(ImGuiFullscreen::UIPrimaryColor));
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImGui::GetColorU32(ImGuiFullscreen::UISecondaryColor));
|
||||
|
||||
ImGuiFullscreen::BeginMenuButtons();
|
||||
|
||||
for (u32 bucket_index = 0; bucket_index < s_leaderboard_list->num_buckets; bucket_index++)
|
||||
if (ImGui::BeginChild("leaderboards_content", ImVec2(content_width, 0.0f), 0, 0))
|
||||
{
|
||||
const rc_client_leaderboard_bucket_t& bucket = s_leaderboard_list->buckets[bucket_index];
|
||||
for (u32 i = 0; i < bucket.num_leaderboards; i++)
|
||||
DrawLeaderboardListEntry(bucket.leaderboards[i]);
|
||||
|
||||
if (s_restore_leaderboard_scroll)
|
||||
{
|
||||
ImGui::SetScrollY(s_leaderboard_list_scroll);
|
||||
s_restore_leaderboard_scroll = false;
|
||||
}
|
||||
|
||||
const bool had_pending_focus_request = s_restore_leaderboard_focus;
|
||||
|
||||
ImGuiFullscreen::BeginMenuButtons();
|
||||
|
||||
static const char* lboard_bucket_names[NUM_RC_CLIENT_LEADERBOARD_BUCKETS] = {
|
||||
TRANSLATE_NOOP("Achievements", "Unknown"),
|
||||
TRANSLATE_NOOP("Achievements", "Inactive"),
|
||||
TRANSLATE_NOOP("Achievements", "Active"),
|
||||
TRANSLATE_NOOP("Achievements", "Unsupported"),
|
||||
TRANSLATE_NOOP("Achievements", "All"),
|
||||
};
|
||||
const bool show_bucket_headers = (s_leaderboard_list->num_buckets > 1);
|
||||
|
||||
for (u32 bucket_index = 0; bucket_index < s_leaderboard_list->num_buckets; bucket_index++)
|
||||
{
|
||||
const rc_client_leaderboard_bucket_t& bucket = s_leaderboard_list->buckets[bucket_index];
|
||||
if (s_open_subset && bucket.subset_id != 0 && bucket.subset_id != s_open_subset->id)
|
||||
continue;
|
||||
if (show_bucket_headers)
|
||||
{
|
||||
pxAssert(bucket.bucket_type < NUM_RC_CLIENT_LEADERBOARD_BUCKETS);
|
||||
const char* type_name = Host::TranslateToCString("Achievements", lboard_bucket_names[bucket.bucket_type]);
|
||||
|
||||
TinyString bucket_label;
|
||||
if (bucket.subset_id != 0 && !s_open_subset && s_subset_list)
|
||||
{
|
||||
auto subset_iter = std::find_if(s_subset_list->subsets, s_subset_list->subsets + s_subset_list->num_subsets,
|
||||
[&](const rc_client_subset_t* s) { return s->id == bucket.subset_id; });
|
||||
if (subset_iter != s_subset_list->subsets + s_subset_list->num_subsets)
|
||||
bucket_label.format("{} - {}", (*subset_iter)->title, type_name);
|
||||
else
|
||||
bucket_label.assign(type_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
bucket_label.assign(type_name);
|
||||
}
|
||||
|
||||
bool& bucket_collapsed = s_leaderboard_buckets_collapsed[std::make_pair(bucket.subset_id, bucket.bucket_type)];
|
||||
if (ImGuiFullscreen::MenuHeadingButton(
|
||||
bucket_label, bucket_collapsed ? ICON_FA_CHEVRON_DOWN : ICON_FA_CHEVRON_UP))
|
||||
bucket_collapsed = !bucket_collapsed;
|
||||
if (bucket_collapsed)
|
||||
continue;
|
||||
}
|
||||
for (u32 i = 0; i < bucket.num_leaderboards; i++)
|
||||
DrawLeaderboardListEntry(bucket.leaderboards[i]);
|
||||
}
|
||||
|
||||
ImGuiFullscreen::EndMenuButtons();
|
||||
|
||||
if (had_pending_focus_request && s_restore_leaderboard_focus)
|
||||
s_restore_leaderboard_focus = false;
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGuiFullscreen::EndMenuButtons();
|
||||
|
||||
if (had_pending_focus_request && s_restore_leaderboard_focus)
|
||||
s_restore_leaderboard_focus = false;
|
||||
ImGui::PopStyleColor(4);
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
ImGuiFullscreen::EndFullscreenWindow();
|
||||
|
||||
FullscreenUI::SetStandardSelectionFooterText(true);
|
||||
if (has_multiple_subsets)
|
||||
DrawSubsetSidebarFooter(sidebar_has_focus);
|
||||
else
|
||||
FullscreenUI::SetStandardSelectionFooterText(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -67,6 +67,9 @@ namespace Achievements
|
||||
/// Called when the system changes game, or is booting.
|
||||
void GameChanged(u32 disc_crc, u32 crc);
|
||||
|
||||
/// Play achievement related sounds effects
|
||||
void PlayAchievementSound(bool is_specific_sound_enabled, const std::string& custom_sound_name, const std::string& default_sound_name);
|
||||
|
||||
/// Re-enables hardcode mode if it is enabled in the settings.
|
||||
bool ResetHardcoreMode(bool is_booting);
|
||||
|
||||
|
||||
@@ -1338,7 +1338,7 @@ function(setup_main_executable target)
|
||||
endforeach()
|
||||
|
||||
get_target_property(WINDEPLOYQT_EXE Qt6::windeployqt IMPORTED_LOCATION)
|
||||
install(CODE "execute_process(COMMAND \"${WINDEPLOYQT_EXE}\" \"${CMAKE_SOURCE_DIR}/bin/$<TARGET_FILE_NAME:${target}>\" --plugindir \"${CMAKE_SOURCE_DIR}/bin/QtPlugins\" --pdb --no-compiler-runtime --no-system-d3d-compiler --no-system-dxc-compiler --no-translations COMMAND_ERROR_IS_FATAL ANY)")
|
||||
install(CODE "execute_process(COMMAND \"${WINDEPLOYQT_EXE}\" \"${CMAKE_SOURCE_DIR}/bin/$<TARGET_FILE_NAME:${target}>\" --plugindir \"${CMAKE_SOURCE_DIR}/bin/QtPlugins\" --pdb --no-compiler-runtime --no-system-d3d-compiler --no-system-dxc-compiler --no-translations --no-network COMMAND_ERROR_IS_FATAL ANY)")
|
||||
install(CODE "file(WRITE \"${CMAKE_SOURCE_DIR}/bin/qt.conf\" \"[Paths]\\nPlugins = ./QtPlugins\")")
|
||||
endif()
|
||||
|
||||
@@ -1438,7 +1438,8 @@ function(setup_main_executable target)
|
||||
endif()
|
||||
|
||||
if(ENABLE_SETCAP AND UNIX AND NOT APPLE)
|
||||
message(WARNING "Networking capabilities enabled, building will require temporary root elevation.")
|
||||
message(WARNING "Networking capabilities enabled on the main executable, building will require temporary root elevation.")
|
||||
message(WARNING "File capabilities can disable LD_PRELOAD-based integrations such as the Steam overlay and Steam Input.")
|
||||
add_custom_target(
|
||||
pcsx2-enable-setcap
|
||||
ALL
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
#include "common/Pcsx2Defs.h"
|
||||
|
||||
static const u32 BIAS = 2; // Bus is half of the actual ps2 speed
|
||||
static const u32 PS2CLK = 294912000; //hz /* 294.912 mhz */
|
||||
extern u32 PSXCLK; /* 36.864 Mhz */
|
||||
static const u32 PS2CLK = 294912000; //Hz /* 294.912 MHz */
|
||||
extern u32 PSXCLK; /* 36.864 MHz */
|
||||
|
||||
|
||||
#include "Memory.h"
|
||||
|
||||
@@ -384,9 +384,8 @@ enum class GSDumpCompressionMethod : u8
|
||||
enum class SavestateCompressionMethod : u8
|
||||
{
|
||||
Uncompressed = 0,
|
||||
Deflate64 = 1,
|
||||
Zstandard = 2,
|
||||
LZMA2 = 3
|
||||
Deflate = 1,
|
||||
Zstandard = 2
|
||||
};
|
||||
|
||||
enum class SavestateCompressionLevel : u8
|
||||
@@ -400,6 +399,7 @@ enum class SavestateCompressionLevel : u8
|
||||
enum class GSHardwareDownloadMode : u8
|
||||
{
|
||||
Enabled,
|
||||
EnabledForceFull,
|
||||
NoReadbacks,
|
||||
Unsynchronized,
|
||||
Disabled
|
||||
@@ -760,6 +760,7 @@ struct Pcsx2Config
|
||||
PCRTCOverscan : 1,
|
||||
IntegerScaling : 1,
|
||||
UseDebugDevice : 1,
|
||||
UseDebugBlend : 1,
|
||||
UseBlitSwapChain : 1,
|
||||
DisableShaderCache : 1,
|
||||
DisableFramebufferFetch : 1,
|
||||
@@ -791,8 +792,11 @@ struct Pcsx2Config
|
||||
PreloadFrameWithGSData : 1,
|
||||
Mipmap : 1,
|
||||
HWMipmap : 1,
|
||||
HWAccurateAlphaTest: 1,
|
||||
HWAccurateAlphaTest : 1,
|
||||
HWAA1 : 1,
|
||||
HWROV : 1,
|
||||
HWROVLogging : 1,
|
||||
HWROVBarriersVK : 1,
|
||||
ManualUserHacks : 1,
|
||||
UserHacks_AlignSpriteX : 1,
|
||||
UserHacks_CPUFBConversion : 1,
|
||||
|
||||
@@ -118,30 +118,12 @@ bool PCAPAdapter::recv(NetPacket* pkt)
|
||||
if (!blocking && NetAdapter::recv(pkt))
|
||||
return true;
|
||||
|
||||
pcap_pkthdr* header;
|
||||
const u_char* pkt_data;
|
||||
|
||||
// pcap bridged will pick up packets not intended for us, returning false on those packets will incur a 1ms wait.
|
||||
// This delays getting packets we need, so instead loop untill a valid packet, or no packet, is returned from pcap_next_ex.
|
||||
while (pcap_next_ex(hpcap, &header, &pkt_data) > 0)
|
||||
while (RecvPCAPPacket(pkt))
|
||||
{
|
||||
// 1518 is the largest Ethernet frame we can get using an MTU of 1500 (assuming no VLAN tagging).
|
||||
// This includes the FCS, which should be trimmed (PS2 SDK dosn't allow extra space for this).
|
||||
if (header->len > 1518)
|
||||
{
|
||||
Console.Error("DEV9: Dropped jumbo frame of size: %u", header->len);
|
||||
continue;
|
||||
}
|
||||
|
||||
pxAssert(header->len == header->caplen);
|
||||
|
||||
memcpy(pkt->buffer, pkt_data, header->len);
|
||||
pkt->size = static_cast<int>(header->len);
|
||||
|
||||
if (!switched)
|
||||
SetMACBridgedRecv(pkt);
|
||||
|
||||
if (VerifyPkt(pkt, header->len))
|
||||
if (VerifyPkt(pkt, pkt->size))
|
||||
{
|
||||
HandleFrameCheckSequence(pkt);
|
||||
|
||||
@@ -173,10 +155,39 @@ bool PCAPAdapter::send(NetPacket* pkt)
|
||||
if (!switched)
|
||||
SetMACBridgedSend(pkt);
|
||||
|
||||
if (pcap_sendpacket(hpcap, (u_char*)pkt->buffer, pkt->size))
|
||||
return false;
|
||||
else
|
||||
return SendPCAPPacket(pkt);
|
||||
}
|
||||
|
||||
bool PCAPAdapter::RecvPCAPPacket(NetPacket* pkt)
|
||||
{
|
||||
pcap_pkthdr* header;
|
||||
const u_char* pkt_data;
|
||||
|
||||
// pcap bridged will pick up packets not intended for us, returning false on those packets will incur a 1ms wait.
|
||||
// This delays getting packets we need, so instead loop untill a valid packet, or no packet, is returned from pcap_next_ex.
|
||||
while (pcap_next_ex(hpcap, &header, &pkt_data) > 0)
|
||||
{
|
||||
// 1518 is the largest Ethernet frame we can get using an MTU of 1500 (assuming no VLAN tagging).
|
||||
// This includes the FCS, which should be trimmed (PS2 SDK dosn't allow extra space for this).
|
||||
if (header->len > 1518)
|
||||
{
|
||||
Console.Error("DEV9: Dropped jumbo frame of size: %u", header->len);
|
||||
continue;
|
||||
}
|
||||
|
||||
pxAssert(header->len == header->caplen);
|
||||
|
||||
memcpy(pkt->buffer, pkt_data, header->len);
|
||||
pkt->size = static_cast<int>(header->len);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PCAPAdapter::SendPCAPPacket(const NetPacket* pkt)
|
||||
{
|
||||
return pcap_sendpacket(hpcap, reinterpret_cast<const u_char*>(pkt->buffer), pkt->size) == 0;
|
||||
}
|
||||
|
||||
void PCAPAdapter::reloadSettings()
|
||||
@@ -280,6 +291,12 @@ bool PCAPAdapter::InitPCAP(const std::string& adapter, bool promiscuous)
|
||||
)) == nullptr)
|
||||
{
|
||||
Console.Error("DEV9: %s", errbuf);
|
||||
#if defined(__linux__)
|
||||
Console.Error("DEV9: PCAP on Linux requires CAP_NET_RAW and CAP_NET_ADMIN capabilities.");
|
||||
Console.Error("DEV9: Flatpak: raw packet capture is not supported; use the Sockets backend instead.");
|
||||
Console.Error("DEV9: AppImage: extract the image first (--appimage-extract), then run setcap on the extracted binary.");
|
||||
Console.Error("DEV9: Note: applying file capabilities to the main PCSX2 executable breaks Steam overlay/Input.");
|
||||
#endif
|
||||
Console.Error("DEV9: Unable to open the adapter. %s is not supported by pcap", adapter.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -39,6 +39,9 @@ private:
|
||||
bool InitPCAP(const std::string& adapter, bool promiscuous);
|
||||
bool SetMACSwitchedFilter(PacketReader::MAC_Address mac);
|
||||
|
||||
bool RecvPCAPPacket(NetPacket* pkt);
|
||||
bool SendPCAPPacket(const NetPacket* pkt);
|
||||
|
||||
void SetMACBridgedRecv(NetPacket* pkt);
|
||||
void SetMACBridgedSend(NetPacket* pkt);
|
||||
|
||||
|
||||
@@ -161,7 +161,8 @@ static bool OpenGSDevice(GSRendererType renderer, bool clear_state_on_fail, bool
|
||||
return false;
|
||||
}
|
||||
|
||||
GSConfig.OsdShowGPU = GSConfig.OsdShowGPU && g_gs_device->SetGPUTimingEnabled(true);
|
||||
if (!g_gs_device->SetGPUTimingEnabled(true))
|
||||
GSConfig.OsdShowGPU = false;
|
||||
|
||||
Console.WriteLn(Color_StrongGreen, "%s Graphics Driver Info:", GSDevice::RenderAPIToString(new_api));
|
||||
Console.WriteLn(g_gs_device->GetDriverInfo());
|
||||
@@ -701,16 +702,36 @@ void GSgetStats(SmallStringBase& info)
|
||||
}
|
||||
else
|
||||
{
|
||||
info.format("{} HW | {} PRIM | {} DRW | {} DRWC | {} BAR | {} RP | {} RB | {} TC | {} TU",
|
||||
api_name,
|
||||
(int)pm.Get(GSPerfMon::Prim),
|
||||
(int)pm.Get(GSPerfMon::Draw),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::DrawCalls)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::Barriers)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::RenderPasses)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::Readbacks)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::TextureCopies)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::TextureUploads)));
|
||||
if (!GSConfig.HWROV)
|
||||
{
|
||||
info.format("{} HW | {} PRIM | {} DRW | {} DRWC | {} BAR | {} RP | {} RB | {} TC | {} TU",
|
||||
api_name,
|
||||
(int)pm.Get(GSPerfMon::Prim),
|
||||
(int)pm.Get(GSPerfMon::Draw),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::DrawCalls)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::Barriers)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::RenderPasses)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::Readbacks)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::TextureCopies)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::TextureUploads)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add ROV stats along standard stats.
|
||||
info.format("{} HW | {} PRIM | {} DRW | {}/{} DRWC | {}/{} BAR | {} RP | {} RB | {}/{} TC | {} TU",
|
||||
api_name,
|
||||
(int)pm.Get(GSPerfMon::Prim),
|
||||
(int)pm.Get(GSPerfMon::Draw),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::DrawCalls)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::DrawCallsROV)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::Barriers)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::BarriersROV)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::RenderPasses)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::Readbacks)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::TextureCopies)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::DepthCopiesROV)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::TextureUploads)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -858,9 +879,9 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
|
||||
g_gs_renderer->PurgeTextureCache(true, false, true);
|
||||
}
|
||||
|
||||
if (GSConfig.OsdShowGPU != old_config.OsdShowGPU)
|
||||
if (GSConfig.OsdShowGPU && !old_config.OsdShowGPU)
|
||||
{
|
||||
if (!g_gs_device->SetGPUTimingEnabled(GSConfig.OsdShowGPU))
|
||||
if (!g_gs_device->SetGPUTimingEnabled(true))
|
||||
GSConfig.OsdShowGPU = false;
|
||||
}
|
||||
}
|
||||
@@ -1305,12 +1326,12 @@ BEGIN_HOTKEY_LIST(g_gs_hotkeys){"Screenshot", TRANSLATE_NOOP("Hotkeys", "Graphic
|
||||
return;
|
||||
|
||||
static constexpr std::array<const char*, static_cast<u8>(AccBlendLevel::MaxCount)> s_blending_option_names = {{
|
||||
TRANSLATE_NOOP("Hotkeys", "Minimum"),
|
||||
TRANSLATE_NOOP("Hotkeys", "Basic"),
|
||||
TRANSLATE_NOOP("Hotkeys", "Medium"),
|
||||
TRANSLATE_NOOP("Hotkeys", "High"),
|
||||
TRANSLATE_NOOP("Hotkeys", "Full"),
|
||||
TRANSLATE_NOOP("Hotkeys", "Maximum"),
|
||||
TRANSLATE_NOOP("Hotkeys_BlendAcc", "Minimum"),
|
||||
TRANSLATE_NOOP("Hotkeys_BlendAcc", "Basic"),
|
||||
TRANSLATE_NOOP("Hotkeys_BlendAcc", "Medium"),
|
||||
TRANSLATE_NOOP("Hotkeys_BlendAcc", "High"),
|
||||
TRANSLATE_NOOP("Hotkeys_BlendAcc", "Full"),
|
||||
TRANSLATE_NOOP("Hotkeys_BlendAcc", "Maximum"),
|
||||
}};
|
||||
|
||||
const AccBlendLevel new_blend_mode = static_cast<AccBlendLevel>(
|
||||
|
||||
@@ -46,10 +46,15 @@
|
||||
#define GL_POP() g_gs_device->PopDebugGroup()
|
||||
#define GL_INS(...) g_gs_device->InsertDebugMessage(GSDevice::DebugMessageCategory::Message, __VA_ARGS__)
|
||||
#define GL_PERF(...) g_gs_device->InsertDebugMessage(GSDevice::DebugMessageCategory::Performance, __VA_ARGS__)
|
||||
#define GL_ROV(...) \
|
||||
if (GSConfig.HWROVLogging) \
|
||||
Console.Warning(__VA_ARGS__); \
|
||||
g_gs_device->InsertDebugMessage(GSDevice::DebugMessageCategory::Message, __VA_ARGS__);
|
||||
#else
|
||||
#define GL_PUSH_(...) (void)(0)
|
||||
#define GL_PUSH(...) (void)(0)
|
||||
#define GL_POP() (void)(0)
|
||||
#define GL_INS(...) (void)(0)
|
||||
#define GL_PERF(...) (void)(0)
|
||||
#define GL_ROV(...) (void)(0)
|
||||
#endif
|
||||
|
||||
@@ -23,6 +23,9 @@ public:
|
||||
SyncPoint,
|
||||
Barriers,
|
||||
RenderPasses,
|
||||
DepthCopiesROV, // Overlaps with regular texture copies.
|
||||
DrawCallsROV, // Overlaps with regular draw calls.
|
||||
BarriersROV, // Overlaps with regular barriers.
|
||||
CounterLast,
|
||||
|
||||
// Reused counters for HW.
|
||||
|
||||
@@ -203,8 +203,8 @@ enum GS_AFAIL
|
||||
|
||||
enum GS_ALPHA_BITS
|
||||
{
|
||||
ALPHA_ABC_CS = 0,
|
||||
ALPHA_ABC_CD = 1,
|
||||
ALPHA_ABD_CS = 0,
|
||||
ALPHA_ABD_CD = 1,
|
||||
ALPHA_C_AS = 0,
|
||||
ALPHA_C_AD = 1,
|
||||
ALPHA_C_FIX = 2,
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/Pcsx2Types.h"
|
||||
#include "common/Timer.h"
|
||||
|
||||
#include <atomic>
|
||||
|
||||
namespace GSShaderCompileIndicator
|
||||
{
|
||||
inline constexpr u64 RECENT_COMPILE_HOLD_NS = 1'500'000'000ULL;
|
||||
|
||||
inline std::atomic<u32> s_count{0};
|
||||
inline std::atomic<u64> s_time_ns{0};
|
||||
inline std::atomic<u64> s_last_time{0};
|
||||
|
||||
inline u64 GetRecentCompileHold()
|
||||
{
|
||||
static const u64 hold = static_cast<u64>(Common::Timer::ConvertNanosecondsToValue(static_cast<double>(RECENT_COMPILE_HOLD_NS)));
|
||||
return hold;
|
||||
}
|
||||
|
||||
inline void OnCompileDone(u64 duration_ns, u64 start_time)
|
||||
{
|
||||
const u64 now = Common::Timer::GetCurrentValue();
|
||||
const u64 last = s_last_time.load(std::memory_order_relaxed);
|
||||
if (last != 0 && start_time > last && (start_time - last) >= GetRecentCompileHold())
|
||||
{
|
||||
s_count.store(0, std::memory_order_relaxed);
|
||||
s_time_ns.store(0, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
s_count.fetch_add(1, std::memory_order_relaxed);
|
||||
s_time_ns.fetch_add(duration_ns, std::memory_order_relaxed);
|
||||
s_last_time.store(now, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
inline u32 GetCount()
|
||||
{
|
||||
return s_count.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
inline u32 GetTimeMs()
|
||||
{
|
||||
const u64 time_ns = s_time_ns.load(std::memory_order_relaxed);
|
||||
const u32 ms = static_cast<u32>(time_ns / 1000000);
|
||||
if (ms > 0)
|
||||
return ms;
|
||||
|
||||
return GetCount() > 0 ? 1u : 0u;
|
||||
}
|
||||
|
||||
inline bool IsVisible()
|
||||
{
|
||||
if (GetCount() == 0)
|
||||
return false;
|
||||
|
||||
const u64 last = s_last_time.load(std::memory_order_relaxed);
|
||||
if (last == 0)
|
||||
return false;
|
||||
|
||||
return (Common::Timer::GetCurrentValue() - last) < GetRecentCompileHold();
|
||||
}
|
||||
|
||||
inline float GetFadeAlpha()
|
||||
{
|
||||
const u64 last = s_last_time.load(std::memory_order_relaxed);
|
||||
if (last == 0)
|
||||
return 0.0f;
|
||||
|
||||
const u64 now = Common::Timer::GetCurrentValue();
|
||||
if (now <= last)
|
||||
return 1.0f;
|
||||
|
||||
const u64 hold = GetRecentCompileHold();
|
||||
const u64 elapsed = now - last;
|
||||
if (elapsed >= hold)
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f - static_cast<float>(elapsed) / static_cast<float>(hold);
|
||||
}
|
||||
|
||||
struct CompileTimer
|
||||
{
|
||||
Common::Timer timer;
|
||||
|
||||
CompileTimer() = default;
|
||||
|
||||
~CompileTimer()
|
||||
{
|
||||
OnCompileDone(static_cast<u64>(timer.GetTimeNanoseconds()), timer.GetStartValue());
|
||||
}
|
||||
|
||||
CompileTimer(const CompileTimer&) = delete;
|
||||
CompileTimer& operator=(const CompileTimer&) = delete;
|
||||
};
|
||||
} // namespace GSShaderCompileIndicator
|
||||
@@ -355,11 +355,15 @@ void GSState::ResetDrawBufferIdx()
|
||||
m_vertex = &m_vertex_buffers[m_current_buffer_idx];
|
||||
|
||||
if (m_index->tail == 0)
|
||||
{
|
||||
m_backed_up_ctx = -1;
|
||||
|
||||
m_dirty_gs_regs = 0;
|
||||
|
||||
//DevCon.Warning("New round of draws buffer %d vertex tail %d index tail %d TME %d TBP0 0x%x draw %d", m_current_buffer_idx, m_vertex->tail, m_index->tail, m_env.PRIM.TME, m_env.CTXT[m_env.PRIM.CTXT].TEX0.TBP0, s_n);
|
||||
m_dirty_gs_regs = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_dirty_gs_regs = m_env_buffers[m_current_buffer_idx].m_dirty_regs;
|
||||
temp_draw_rect = m_env_buffers[m_current_buffer_idx].draw_rect;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -385,7 +389,7 @@ void GSState::ResetDrawBuffers()
|
||||
// exclude_current is used if there is a flush for a reason other than the normal context change.
|
||||
void GSState::FlushBuffers(bool flush_base_only, bool use_flush_reason, GSFlushReason flush_reason)
|
||||
{
|
||||
const u32 current_idx = m_current_buffer_idx;
|
||||
const int current_idx = m_current_buffer_idx;
|
||||
bool restore_env = false;
|
||||
|
||||
if (m_used_buffers_idx > 0)
|
||||
@@ -398,8 +402,7 @@ void GSState::FlushBuffers(bool flush_base_only, bool use_flush_reason, GSFlushR
|
||||
else if (m_index_buffers[0].tail == 0)
|
||||
return;
|
||||
|
||||
int max_flushes = flush_base_only ? 1 : m_used_buffers_idx;
|
||||
//DevCon.Warning("Flushing %d draw buffers from draw %d", m_used_buffers_idx, s_n);
|
||||
const int max_flushes = flush_base_only ? 1 : m_used_buffers_idx;
|
||||
for (int i = 0; i < max_flushes; i++)
|
||||
{
|
||||
m_current_buffer_idx = i;
|
||||
@@ -431,7 +434,7 @@ void GSState::FlushBuffers(bool flush_base_only, bool use_flush_reason, GSFlushR
|
||||
}
|
||||
else if (restore_env)
|
||||
memcpy(&m_env, &m_temp_env, sizeof(m_env));
|
||||
//DevCon.Warning("Flushing position %d ABE is %d TME %d TEX0 TBP %x", i, m_prev_env.PRIM.ABE, m_prev_env.PRIM.TME, m_prev_env.CTXT[m_prev_env.PRIM.CTXT].TEX0.TBP0);
|
||||
|
||||
if (use_flush_reason && (i == current_idx || flush_reason == VSYNC))
|
||||
FlushDraw(flush_reason);
|
||||
else
|
||||
@@ -443,7 +446,6 @@ void GSState::FlushBuffers(bool flush_base_only, bool use_flush_reason, GSFlushR
|
||||
m_current_buffer_idx = current_idx;
|
||||
m_index = &m_index_buffers[m_current_buffer_idx];
|
||||
m_vertex = &m_vertex_buffers[m_current_buffer_idx];
|
||||
m_dirty_gs_regs = 0;
|
||||
|
||||
const int ctx = m_env_buffers[m_current_buffer_idx].m_backed_up_ctx;
|
||||
std::memcpy(&m_prev_env, &m_env_buffers[m_current_buffer_idx].m_env, 88);
|
||||
@@ -451,8 +453,6 @@ void GSState::FlushBuffers(bool flush_base_only, bool use_flush_reason, GSFlushR
|
||||
std::memcpy(&m_prev_env.CTXT[1], &m_env_buffers[m_current_buffer_idx].m_env.CTXT[1], 96);
|
||||
std::memcpy(&m_prev_env.CTXT[ctx].offset, &m_env_buffers[m_current_buffer_idx].m_env.CTXT[ctx].offset, sizeof(m_env_buffers[m_current_buffer_idx].m_env.CTXT[ctx].offset));
|
||||
std::memcpy(&m_prev_env.CTXT[ctx].scissor, &m_env_buffers[m_current_buffer_idx].m_env.CTXT[ctx].scissor, sizeof(m_env_buffers[m_current_buffer_idx].m_env.CTXT[ctx].scissor));
|
||||
//DevCon.Warning("Flush complete, draw now %d", s_n);
|
||||
//UpdateContext();
|
||||
}
|
||||
|
||||
void GSState::PushBuffer()
|
||||
@@ -460,7 +460,6 @@ void GSState::PushBuffer()
|
||||
// Just in case it tries to overflow.
|
||||
if (m_used_buffers_idx >= MAX_DRAW_BUFFERS)
|
||||
{
|
||||
//DevCon.Warning("Attempted to add a draw to buffer when full. Flushing");
|
||||
FlushBuffers(false, false);
|
||||
ResetDrawBufferIdx();
|
||||
return;
|
||||
@@ -487,15 +486,11 @@ void GSState::PushBuffer()
|
||||
{
|
||||
for (u32 i = 0; i < copy_amt; i++)
|
||||
{
|
||||
GSVector4i* RESTRICT vert_ptr = (GSVector4i*)&m_vertex->buff[m_vertex->head + i];
|
||||
GSVector4i v = vert_ptr[1];
|
||||
v = v.xxxx().u16to32().sub32(m_xyof);
|
||||
v = v.blend32<12>(v.sra32<4>());
|
||||
m_vertex->xy[i & 3] = v;
|
||||
m_vertex->xy_tail = std::min(copy_amt, 2U);
|
||||
m_vertex->xy[i & 3] = m_vertex_buffers[m_current_buffer_idx].xy[((m_vertex_buffers[m_current_buffer_idx].xy_tail - copy_amt) + i) & 3];
|
||||
m_vertex->xy_tail++;
|
||||
|
||||
if (i == 0)
|
||||
m_vertex->xyhead = v;
|
||||
m_vertex->xyhead = m_vertex_buffers[m_current_buffer_idx].xyhead;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -506,7 +501,6 @@ void GSState::PushBuffer()
|
||||
m_dirty_gs_regs = 0;
|
||||
m_used_buffers_idx++;
|
||||
m_recent_buffer_switch = true;
|
||||
//DevCon.Warning("Pushing new buffer %d vertex tail %d index tail %d TME %d TBP0 0x%x draw %d", m_current_buffer_idx, m_vertex->tail, m_index->tail, m_env.PRIM.TME, m_env.CTXT[m_env.PRIM.CTXT].TEX0.TBP0, s_n);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -520,10 +514,11 @@ bool GSState::CanBufferNewDraw()
|
||||
|
||||
// If the base draw isn't writing to the Z buffer, but following draws do, we can't use it.
|
||||
// Also the base draw needs to be solid, not an alpha blend.
|
||||
if (base_context.ZBUF.ZMSK || cur_context.FRAME.FBP != base_context.FRAME.FBP || cur_context.ZBUF.ZBP != base_context.ZBUF.ZBP || (m_env_buffers[0].m_env.PRIM.TME && base_context.TEX0.TFX > TFX_DECAL) ||
|
||||
if (base_context.ZBUF.ZMSK || cur_context.FRAME.FBP != base_context.FRAME.FBP || cur_context.ZBUF.ZBP != base_context.ZBUF.ZBP || (m_env_buffers[0].m_env.PRIM.TME &&
|
||||
(base_context.TEX0.TFX > TFX_DECAL || (m_env_buffers[0].m_env.PRIM.ABE && !base_context.TEX0.TCC && m_v.RGBAQ.A != 128))) ||
|
||||
((base_context.TEST.ATE && base_context.TEST.ATST > ATST_ALWAYS && base_context.TEST.AREF != 0) && (base_context.TEST.AFAIL & AFAIL_FB_ONLY) == AFAIL_KEEP))
|
||||
{
|
||||
//DevCon.Warning("Flushing, cannot buffer draw due to incompatible base");
|
||||
// Incompatible base.
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -542,6 +537,8 @@ bool GSState::CanBufferNewDraw()
|
||||
if (!std::memcmp(&m_env_buffers[i].m_env, &m_env, 88))
|
||||
{
|
||||
GSDrawingEnvironment& buffered_ctx = m_env_buffers[i].m_env;
|
||||
if (m_index_buffers[i].tail > m_index_buffers[0].tail)
|
||||
return false;
|
||||
|
||||
if (buffered_ctx.CTXT[ctx].SCISSOR.U64 ^ cur_context.SCISSOR.U64)
|
||||
continue;
|
||||
@@ -565,6 +562,9 @@ bool GSState::CanBufferNewDraw()
|
||||
continue;
|
||||
if (buffered_ctx.CTXT[ctx].TEX1.U32[0] ^ cur_context.TEX1.U32[0])
|
||||
continue;
|
||||
if (buffered_ctx.CTXT[ctx].TEX1.MXL != cur_context.TEX1.MXL)
|
||||
continue;
|
||||
|
||||
if (cur_context.TEX1.MXL)
|
||||
{
|
||||
if (buffered_ctx.CTXT[ctx].TEX1.U32[1] ^ cur_context.TEX1.U32[1])
|
||||
@@ -600,27 +600,32 @@ bool GSState::CanBufferNewDraw()
|
||||
if (i == 1 && !m_env_buffers[i].draw_rect.eq(m_env_buffers[0].draw_rect))
|
||||
{
|
||||
FlushWrite();
|
||||
|
||||
FlushBuffers(true, false);
|
||||
const bool base_only = m_env_buffers[i].draw_rect.rintersect(m_env_buffers[0].draw_rect).rempty();
|
||||
FlushBuffers(base_only, false);
|
||||
ResetDrawBufferIdx();
|
||||
i = -1;
|
||||
continue;
|
||||
if (!base_only)
|
||||
break;
|
||||
else
|
||||
{
|
||||
i = -1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/*if (i != (m_current_buffer_idx + 1) && i != 0)
|
||||
return false;*/
|
||||
// We found a matching draw
|
||||
//DevCon.Warning("Matching buffered draw detected in index %d, using", i);
|
||||
m_index = &m_index_buffers[i];
|
||||
m_vertex = &m_vertex_buffers[i];
|
||||
|
||||
const u32 copy_amt = m_vertex_buffers[m_current_buffer_idx].tail - m_vertex_buffers[m_current_buffer_idx].head;
|
||||
|
||||
m_recent_buffer_switch = m_vertex->tail == m_vertex->head;
|
||||
m_vertex->tail = m_index->buff[m_index->tail - 1] + 1;
|
||||
if (m_index->tail)
|
||||
m_vertex->tail = m_index->buff[m_index->tail - 1] + 1;
|
||||
else
|
||||
m_vertex->tail = 0;
|
||||
|
||||
if (copy_amt)
|
||||
memcpy(&m_vertex->buff[m_vertex->tail], &m_vertex_buffers[m_current_buffer_idx].buff[m_vertex_buffers[m_current_buffer_idx].head], sizeof(GSVertex) * copy_amt);
|
||||
@@ -641,18 +646,13 @@ bool GSState::CanBufferNewDraw()
|
||||
|
||||
if (copy_amt)
|
||||
{
|
||||
for (u32 j = 0; j < copy_amt; j++)
|
||||
for (u32 i = 0; i < copy_amt; i++)
|
||||
{
|
||||
GSVector4i* RESTRICT vert_ptr = (GSVector4i*)&m_vertex->buff[m_vertex->head + j];
|
||||
GSVector4i v = vert_ptr[1];
|
||||
v = v.xxxx().u16to32().sub32(m_xyof);
|
||||
v = v.blend32<12>(v.sra32<4>());
|
||||
m_vertex->xy[j & 3] = v;
|
||||
m_vertex->xy[m_vertex->xy_tail & 3] = m_vertex_buffers[m_current_buffer_idx].xy[((m_vertex_buffers[m_current_buffer_idx].xy_tail - copy_amt) + i) & 3];
|
||||
m_vertex->xy_tail++;
|
||||
|
||||
if (j == 0)
|
||||
m_vertex->xyhead = v;
|
||||
|
||||
m_vertex->xy_tail = copy_amt;
|
||||
if (i == 0)
|
||||
m_vertex->xyhead = m_vertex_buffers[m_current_buffer_idx].xyhead;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -663,7 +663,7 @@ bool GSState::CanBufferNewDraw()
|
||||
}
|
||||
|
||||
m_dirty_gs_regs = 0;
|
||||
//DevCon.Warning("Picking buffer %d vertex tail %d index tail %d TME %d TBP0 0x%x dirty %x draw %d", m_current_buffer_idx, m_vertex->tail, m_index->tail, m_env.PRIM.TME, m_env.CTXT[m_env.PRIM.CTXT].TEX0.TBP0, m_dirty_gs_regs, s_n);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -688,7 +688,6 @@ bool GSState::CanBufferNewDraw()
|
||||
return false;
|
||||
|
||||
PushBuffer();
|
||||
//DevCon.Warning("Buffering new draw! now buffering %d", m_used_buffers_idx);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -4234,7 +4233,7 @@ bool GSState::TrianglesAreQuads(bool shuffle_check)
|
||||
}
|
||||
|
||||
template<u32 primclass>
|
||||
GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlist, bool save_bbox, float bbox_scale)
|
||||
GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlist, bool save_bbox, float bbox_scale, u32* max_size)
|
||||
{
|
||||
const GSVector4i xyof = m_context->scissor.xyof.xyxy();
|
||||
|
||||
@@ -4282,7 +4281,14 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlis
|
||||
if (primclass == GS_TRIANGLE_CLASS && m_quad_check_valid && m_are_quads && !using_aa1)
|
||||
{
|
||||
// The triangles-are-quads check already ensures that there is no overlap.
|
||||
m_drawlist.push_back(m_index->tail / n);
|
||||
if (save_drawlist)
|
||||
{
|
||||
m_drawlist.push_back(m_index->tail / n);
|
||||
}
|
||||
else if (max_size)
|
||||
{
|
||||
*max_size = 1;
|
||||
}
|
||||
if (save_bbox)
|
||||
{
|
||||
const GSVector4i draw_area = GSVector4i(m_vt.m_min.p.upld(m_vt.m_max.p) * GSVector4(16.0f)) + xyof;
|
||||
@@ -4294,6 +4300,7 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlis
|
||||
PRIM_OVERLAP overlap = PRIM_OVERLAP_NO;
|
||||
bool check_quads = (primclass == GS_TRIANGLE_CLASS) && !using_aa1;
|
||||
|
||||
u32 drawlist_size = 0;
|
||||
u32 i = 0;
|
||||
u32 skip = 0; // Number of indices to skip if we have the bbox from the previous iteration.
|
||||
|
||||
@@ -4642,13 +4649,13 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlis
|
||||
};
|
||||
|
||||
// First check: see if the triangles are part of a triangle strip.
|
||||
if (!got_bbox && !using_aa1)
|
||||
if (!got_bbox && !using_aa1 && !GSConfig.UseDebugBlend)
|
||||
{
|
||||
got_bbox = CheckTriangleStrips(j, skip, bbox, saved_tristrip);
|
||||
}
|
||||
|
||||
// Second check: see if the triangles are part of triangle fan.
|
||||
if (!got_bbox && !using_aa1)
|
||||
if (!got_bbox && !using_aa1 && !GSConfig.UseDebugBlend)
|
||||
{
|
||||
got_bbox = CheckTriangleQuads.template operator()<1>(j, skip, bbox);
|
||||
}
|
||||
@@ -4691,6 +4698,17 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlis
|
||||
{
|
||||
m_drawlist.push_back((j - i) / n); // Prim count
|
||||
}
|
||||
else if (max_size)
|
||||
{
|
||||
// If the max size pointer is passed it means we just want to peek at
|
||||
// the drawlist size up to the given limit to avoid unecessary work.
|
||||
drawlist_size++;
|
||||
if (drawlist_size >= *max_size)
|
||||
{
|
||||
*max_size = drawlist_size;
|
||||
return drawlist_size > 0 ? PRIM_OVERLAP_YES : PRIM_OVERLAP_UNKNOW;
|
||||
}
|
||||
}
|
||||
else if (j < count)
|
||||
{
|
||||
return PRIM_OVERLAP_YES; // Early exit if not saving drawlist.
|
||||
@@ -4704,21 +4722,27 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlis
|
||||
all = bbox;
|
||||
i = j;
|
||||
}
|
||||
|
||||
if (max_size)
|
||||
{
|
||||
*max_size = drawlist_size;
|
||||
}
|
||||
|
||||
return overlap;
|
||||
}
|
||||
|
||||
GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlist(bool save_drawlist, bool save_bbox, float bbox_scale)
|
||||
GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlist(bool save_drawlist, bool save_bbox, float bbox_scale, u32* max_size)
|
||||
{
|
||||
switch (m_vt.m_primclass)
|
||||
{
|
||||
case GS_POINT_CLASS:
|
||||
return GetPrimitiveOverlapDrawlistImpl<GS_POINT_CLASS>(save_drawlist, save_bbox, bbox_scale);
|
||||
return GetPrimitiveOverlapDrawlistImpl<GS_POINT_CLASS>(save_drawlist, save_bbox, bbox_scale, max_size);
|
||||
case GS_LINE_CLASS:
|
||||
return GetPrimitiveOverlapDrawlistImpl<GS_LINE_CLASS>(save_drawlist, save_bbox, bbox_scale);
|
||||
return GetPrimitiveOverlapDrawlistImpl<GS_LINE_CLASS>(save_drawlist, save_bbox, bbox_scale, max_size);
|
||||
case GS_TRIANGLE_CLASS:
|
||||
return GetPrimitiveOverlapDrawlistImpl<GS_TRIANGLE_CLASS>(save_drawlist, save_bbox, bbox_scale);
|
||||
return GetPrimitiveOverlapDrawlistImpl<GS_TRIANGLE_CLASS>(save_drawlist, save_bbox, bbox_scale, max_size);
|
||||
case GS_SPRITE_CLASS:
|
||||
return GetPrimitiveOverlapDrawlistImpl<GS_SPRITE_CLASS>(save_drawlist, save_bbox, bbox_scale);
|
||||
return GetPrimitiveOverlapDrawlistImpl<GS_SPRITE_CLASS>(save_drawlist, save_bbox, bbox_scale, max_size);
|
||||
default:
|
||||
pxFail("Invalid primclass."); // Impossible.
|
||||
return PRIM_OVERLAP_UNKNOW;
|
||||
@@ -5753,10 +5777,10 @@ bool GSState::CheckOverlapVerts(u32 n)
|
||||
|
||||
if (m_index->tail > 0)
|
||||
{
|
||||
int matching_verts = 0;
|
||||
u32 matching_verts = 0;
|
||||
for (u32 i = 0; i < n; i++)
|
||||
{
|
||||
const int pos = m_index->buff[(m_index->tail - n) + i];
|
||||
const u32 pos = m_index->buff[(m_index->tail - n) + i];
|
||||
const GSVector2i prev_vert = GSVector2i(v[pos].XYZ.X - m_context->XYOFFSET.OFX, v[pos].XYZ.Y - m_context->XYOFFSET.OFY);
|
||||
|
||||
for (u32 j = 0; j < n; j++)
|
||||
@@ -6081,7 +6105,7 @@ __forceinline void GSState::VertexKick(u32 skip)
|
||||
|
||||
// Update rectangle for the current draw. Needs exclusive endpoints.
|
||||
const GSVector4i draw_rect = bbox.sra32<4>() + GSVector4i(0, 0, 1, 1);
|
||||
if (m_vertex->tail != n)
|
||||
if (m_index->tail != n)
|
||||
temp_draw_rect = temp_draw_rect.runion(draw_rect);
|
||||
else
|
||||
temp_draw_rect = draw_rect;
|
||||
|
||||
@@ -532,8 +532,10 @@ public:
|
||||
bool TrianglesAreQuadsImpl();
|
||||
bool TrianglesAreQuads(bool shuffle_check = false);
|
||||
template <u32 primclass>
|
||||
PRIM_OVERLAP GetPrimitiveOverlapDrawlistImpl(bool save_drawlist = false, bool save_bbox = false, float bbox_scale = 1.0f);
|
||||
PRIM_OVERLAP GetPrimitiveOverlapDrawlist(bool save_drawlist = false, bool save_bbox = false, float bbox_scale = 1.0f);
|
||||
PRIM_OVERLAP GetPrimitiveOverlapDrawlistImpl(bool save_drawlist = false, bool save_bbox = false,
|
||||
float bbox_scale = 1.0f, u32* max_size = nullptr);
|
||||
PRIM_OVERLAP GetPrimitiveOverlapDrawlist(bool save_drawlist = false, bool save_bbox = false,
|
||||
float bbox_scale = 1.0f, u32* max_size = nullptr);
|
||||
PRIM_OVERLAP PrimitiveOverlap(bool save_drawlist = false);
|
||||
bool SpriteDrawWithoutGaps();
|
||||
void CalculatePrimitiveCoversWithoutGaps();
|
||||
|
||||
@@ -22,29 +22,13 @@
|
||||
#include <ostream>
|
||||
#include <fstream>
|
||||
|
||||
int SetDATMShader(SetDATM datm)
|
||||
{
|
||||
switch (datm)
|
||||
{
|
||||
case SetDATM::DATM1_RTA_CORRECTION:
|
||||
return static_cast<int>(ShaderConvert::DATM_1_RTA_CORRECTION);
|
||||
case SetDATM::DATM0_RTA_CORRECTION:
|
||||
return static_cast<int>(ShaderConvert::DATM_0_RTA_CORRECTION);
|
||||
case SetDATM::DATM1:
|
||||
return static_cast<int>(ShaderConvert::DATM_1);
|
||||
case SetDATM::DATM0:
|
||||
default:
|
||||
return static_cast<int>(ShaderConvert::DATM_0);
|
||||
}
|
||||
}
|
||||
|
||||
const char* shaderName(ShaderConvert value)
|
||||
const char* ShaderEntryPoint(ShaderConvert value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
// clang-format off
|
||||
// clang-format off
|
||||
case ShaderConvert::COPY: return "ps_copy";
|
||||
case ShaderConvert::RGBA8_TO_16_BITS: return "ps_convert_rgba8_16bits";
|
||||
case ShaderConvert::RGB5A1_TO_16_BITS: return "ps_convert_rgb5a1_16bits";
|
||||
case ShaderConvert::DATM_1: return "ps_datm1";
|
||||
case ShaderConvert::DATM_0: return "ps_datm0";
|
||||
case ShaderConvert::DATM_1_RTA_CORRECTION: return "ps_datm1_rta_correction";
|
||||
@@ -54,22 +38,16 @@ const char* shaderName(ShaderConvert value)
|
||||
case ShaderConvert::RTA_CORRECTION: return "ps_rta_correction";
|
||||
case ShaderConvert::RTA_DECORRECTION: return "ps_rta_decorrection";
|
||||
case ShaderConvert::TRANSPARENCY_FILTER: return "ps_filter_transparency";
|
||||
case ShaderConvert::FLOAT32_TO_16_BITS: return "ps_convert_float32_32bits";
|
||||
case ShaderConvert::FLOAT32_TO_32_BITS: return "ps_convert_float32_32bits";
|
||||
case ShaderConvert::FLOAT32_TO_RGBA8: return "ps_convert_float32_rgba8";
|
||||
case ShaderConvert::FLOAT32_TO_RGB8: return "ps_convert_float32_rgba8";
|
||||
case ShaderConvert::FLOAT16_TO_RGB5A1: return "ps_convert_float16_rgb5a1";
|
||||
case ShaderConvert::RGBA8_TO_FLOAT32: return "ps_convert_rgba8_float32";
|
||||
case ShaderConvert::RGBA8_TO_FLOAT24: return "ps_convert_rgba8_float24";
|
||||
case ShaderConvert::RGBA8_TO_FLOAT16: return "ps_convert_rgba8_float16";
|
||||
case ShaderConvert::RGB5A1_TO_FLOAT16: return "ps_convert_rgb5a1_float16";
|
||||
case ShaderConvert::RGBA8_TO_FLOAT32_BILN: return "ps_convert_rgba8_float32_biln";
|
||||
case ShaderConvert::RGBA8_TO_FLOAT24_BILN: return "ps_convert_rgba8_float24_biln";
|
||||
case ShaderConvert::RGBA8_TO_FLOAT16_BILN: return "ps_convert_rgba8_float16_biln";
|
||||
case ShaderConvert::RGB5A1_TO_FLOAT16_BILN: return "ps_convert_rgb5a1_float16_biln";
|
||||
case ShaderConvert::FLOAT32_DEPTH_TO_COLOR: return "ps_convert_float32_depth_to_color";
|
||||
case ShaderConvert::FLOAT32_COLOR_TO_DEPTH: return "ps_convert_float32_color_to_depth";
|
||||
case ShaderConvert::FLOAT32_TO_FLOAT24: return "ps_convert_float32_float24";
|
||||
case ShaderConvert::DEPTH32_TO_16_BITS: return "ps_convert_depth32_32bits";
|
||||
case ShaderConvert::DEPTH32_TO_32_BITS: return "ps_convert_depth32_32bits";
|
||||
case ShaderConvert::DEPTH32_TO_RGBA8: return "ps_convert_depth32_rgba8";
|
||||
case ShaderConvert::DEPTH32_TO_RGB8: return "ps_convert_depth32_rgba8";
|
||||
case ShaderConvert::DEPTH16_TO_RGB5A1: return "ps_convert_depth16_rgb5a1";
|
||||
case ShaderConvert::RGBA8_TO_DEPTH32: return "ps_convert_rgba8_depth32";
|
||||
case ShaderConvert::RGBA8_TO_DEPTH24: return "ps_convert_rgba8_depth24";
|
||||
case ShaderConvert::RGBA8_TO_DEPTH16: return "ps_convert_rgba8_depth16";
|
||||
case ShaderConvert::RGB5A1_TO_DEPTH16: return "ps_convert_rgb5a1_depth16";
|
||||
case ShaderConvert::DEPTH32_TO_DEPTH24: return "ps_convert_depth32_depth24";
|
||||
case ShaderConvert::DEPTH_COPY: return "ps_depth_copy";
|
||||
case ShaderConvert::DOWNSAMPLE_COPY: return "ps_downsample_copy";
|
||||
case ShaderConvert::RGBA_TO_8I: return "ps_convert_rgba_8i";
|
||||
@@ -77,18 +55,18 @@ const char* shaderName(ShaderConvert value)
|
||||
case ShaderConvert::CLUT_4: return "ps_convert_clut_4";
|
||||
case ShaderConvert::CLUT_8: return "ps_convert_clut_8";
|
||||
case ShaderConvert::YUV: return "ps_yuv";
|
||||
// clang-format on
|
||||
// clang-format on
|
||||
default:
|
||||
pxAssert(0);
|
||||
return "ShaderConvertUnknownShader";
|
||||
}
|
||||
}
|
||||
|
||||
const char* shaderName(PresentShader value)
|
||||
const char* ShaderEntryPoint(PresentShader value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
// clang-format off
|
||||
// clang-format off
|
||||
case PresentShader::COPY: return "ps_copy";
|
||||
case PresentShader::SCANLINE: return "ps_filter_scanlines";
|
||||
case PresentShader::DIAGONAL_FILTER: return "ps_filter_diagonal";
|
||||
@@ -97,13 +75,53 @@ const char* shaderName(PresentShader value)
|
||||
case PresentShader::LOTTES_FILTER: return "ps_filter_lottes";
|
||||
case PresentShader::SUPERSAMPLE_4xRGSS: return "ps_4x_rgss";
|
||||
case PresentShader::SUPERSAMPLE_AUTO: return "ps_automagical_supersampling";
|
||||
// clang-format on
|
||||
// clang-format on
|
||||
default:
|
||||
pxAssert(0);
|
||||
return "DisplayShaderUnknownShader";
|
||||
}
|
||||
}
|
||||
|
||||
const char* ShaderConvertName(ShaderConvert shader)
|
||||
{
|
||||
#define ENTRY(x) case ShaderConvert::x: return #x
|
||||
switch (shader)
|
||||
{
|
||||
ENTRY(COPY);
|
||||
ENTRY(DEPTH_COPY);
|
||||
ENTRY(RGB5A1_TO_16_BITS);
|
||||
ENTRY(DATM_1);
|
||||
ENTRY(DATM_0);
|
||||
ENTRY(DATM_1_RTA_CORRECTION);
|
||||
ENTRY(DATM_0_RTA_CORRECTION);
|
||||
ENTRY(COLCLIP_INIT);
|
||||
ENTRY(COLCLIP_RESOLVE);
|
||||
ENTRY(RTA_CORRECTION);
|
||||
ENTRY(RTA_DECORRECTION);
|
||||
ENTRY(TRANSPARENCY_FILTER);
|
||||
ENTRY(DEPTH32_TO_16_BITS);
|
||||
ENTRY(DEPTH32_TO_32_BITS);
|
||||
ENTRY(DEPTH32_TO_RGBA8);
|
||||
ENTRY(DEPTH32_TO_RGB8);
|
||||
ENTRY(DEPTH16_TO_RGB5A1);
|
||||
ENTRY(RGBA8_TO_DEPTH32);
|
||||
ENTRY(RGBA8_TO_DEPTH24);
|
||||
ENTRY(RGBA8_TO_DEPTH16);
|
||||
ENTRY(RGB5A1_TO_DEPTH16);
|
||||
ENTRY(DEPTH32_TO_DEPTH24);
|
||||
ENTRY(DOWNSAMPLE_COPY);
|
||||
ENTRY(RGBA_TO_8I);
|
||||
ENTRY(RGB5A1_TO_8I);
|
||||
ENTRY(CLUT_4);
|
||||
ENTRY(CLUT_8);
|
||||
ENTRY(YUV);
|
||||
case ShaderConvert::Count: break;
|
||||
}
|
||||
#undef ENTRY
|
||||
pxAssert(false);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
|
||||
enum class TextureLabel
|
||||
@@ -297,7 +315,7 @@ bool GSDevice::GetRequestedExclusiveFullscreenMode(u32* width, u32* height, floa
|
||||
|
||||
std::string GSDevice::GetFullscreenModeString(u32 width, u32 height, float refresh_rate)
|
||||
{
|
||||
return StringUtil::StdStringFromFormat("%u x %u @ %f hz", width, height, refresh_rate);
|
||||
return StringUtil::StdStringFromFormat("%u x %u @ %f Hz", width, height, refresh_rate);
|
||||
}
|
||||
|
||||
void GSDevice::GenerateExpansionIndexBuffer(void* buffer)
|
||||
@@ -675,6 +693,12 @@ void GSDevice::Recycle(GSTexture* t)
|
||||
|
||||
t->SetLastFrameUsed(m_frame);
|
||||
|
||||
t->ClearUnorderedAccess();
|
||||
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
t->SetDebugName("");
|
||||
#endif
|
||||
|
||||
FastList<GSTexture*>& pool = m_pool[!t->IsTexture()];
|
||||
pool.push_front(t);
|
||||
m_pool_memory_usage += t->GetMemUsage();
|
||||
@@ -741,9 +765,33 @@ GSTexture* GSDevice::CreateRenderTarget(int w, int h, GSTexture::Format format,
|
||||
return FetchSurface(GSTexture::Type::RenderTarget, w, h, 1, format, clear, !prefer_reuse);
|
||||
}
|
||||
|
||||
GSTexture* GSDevice::CreateDepthStencil(int w, int h, GSTexture::Format format, bool clear, bool prefer_reuse)
|
||||
GSTexture* GSDevice::CreateRenderTarget(const GSVector2i& size, GSTexture::Format format, bool clear, bool prefer_reuse)
|
||||
{
|
||||
return FetchSurface(GSTexture::Type::DepthStencil, w, h, 1, format, clear, !prefer_reuse);
|
||||
return FetchSurface(GSTexture::Type::RenderTarget, size.x, size.y, 1, format, clear, !prefer_reuse);
|
||||
}
|
||||
|
||||
GSTexture* GSDevice::CreateDepthStencil(int w, int h, bool clear, bool prefer_reuse)
|
||||
{
|
||||
return FetchSurface(GSTexture::Type::DepthStencil, w, h, 1, GSTexture::Format::DepthStencil,
|
||||
clear, !prefer_reuse);
|
||||
}
|
||||
|
||||
GSTexture* GSDevice::CreateDepthStencil(const GSVector2i& size, bool clear, bool prefer_reuse)
|
||||
{
|
||||
return FetchSurface(GSTexture::Type::DepthStencil, size.x, size.y, 1, GSTexture::Format::DepthStencil,
|
||||
clear, !prefer_reuse);
|
||||
}
|
||||
|
||||
GSTexture* GSDevice::CreateDepthColor(int w, int h, bool clear, bool prefer_reuse)
|
||||
{
|
||||
return FetchSurface(GSTexture::Type::RenderTarget, w, h, 1, GSTexture::Format::DepthColor,
|
||||
clear, !prefer_reuse);
|
||||
}
|
||||
|
||||
GSTexture* GSDevice::CreateDepthColor(const GSVector2i& size, bool clear, bool prefer_reuse)
|
||||
{
|
||||
return FetchSurface(GSTexture::Type::RenderTarget, size.x, size.y, 1, GSTexture::Format::DepthColor,
|
||||
clear, !prefer_reuse);
|
||||
}
|
||||
|
||||
GSTexture* GSDevice::CreateTexture(int w, int h, int mipmap_levels, GSTexture::Format format, bool prefer_reuse /* = false */)
|
||||
@@ -753,60 +801,102 @@ GSTexture* GSDevice::CreateTexture(int w, int h, int mipmap_levels, GSTexture::F
|
||||
return FetchSurface(GSTexture::Type::Texture, w, h, levels, format, false, m_features.prefer_new_textures && !prefer_reuse);
|
||||
}
|
||||
|
||||
void GSDevice::DoStretchRectWithAssertions(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect,
|
||||
GSHWDrawConfig::ColorMaskSelector cms, ShaderConvert shader, bool linear)
|
||||
GSTexture* GSDevice::CreateTexture(const GSVector2i& size, int mipmap_levels, GSTexture::Format format, bool prefer_reuse /* = false */)
|
||||
{
|
||||
pxAssert((dTex && dTex->IsDepthStencil()) == HasDepthOutput(shader));
|
||||
pxAssert(linear ? SupportsBilinear(shader) : SupportsNearest(shader));
|
||||
GL_INS("StretchRect(%d) {%d,%d} %dx%d -> {%d,%d) %dx%d", shader, int(sRect.left), int(sRect.top),
|
||||
return CreateTexture(size.x, size.y, mipmap_levels, format, prefer_reuse);
|
||||
}
|
||||
|
||||
GSTexture* GSDevice::CreateCompatible(GSTexture* tex, bool clear, bool prefer_reuse)
|
||||
{
|
||||
return CreateCompatible(tex, tex->GetWidth(), tex->GetHeight(), clear, prefer_reuse);
|
||||
}
|
||||
|
||||
GSTexture* GSDevice::CreateCompatible(GSTexture* tex, const GSVector2i& size, bool clear, bool prefer_reuse)
|
||||
{
|
||||
return CreateCompatible(tex, size.x, size.y, clear, prefer_reuse);
|
||||
}
|
||||
|
||||
GSTexture* GSDevice::CreateCompatible(GSTexture* tex, int w, int h, bool clear, bool prefer_reuse)
|
||||
{
|
||||
return FetchSurface(tex->GetType(), w, h, 1, tex->GetFormat(), clear, !prefer_reuse);
|
||||
}
|
||||
|
||||
void GSDevice::DoStretchRectWithAssertions(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex,
|
||||
const GSVector4& dRect, ShaderConvertSelector shader, Filter filter)
|
||||
{
|
||||
pxAssert((dTex && dTex->IsDepthLike()) == shader.Float32Output());
|
||||
pxAssert(!(filter == Biln && shader.SupportsBilinear())); // Don't allow HW bilinear if SW bilinear is required.
|
||||
GL_INS("StretchRect(%s) {%d,%d} %dx%d -> {%d,%d) %dx%d", ShaderConvertName(shader.Shader()),
|
||||
int(sRect.left), int(sRect.top),
|
||||
int(sRect.right - sRect.left), int(sRect.bottom - sRect.top), int(dRect.left), int(dRect.top),
|
||||
int(dRect.right - dRect.left), int(dRect.bottom - dRect.top));
|
||||
DoStretchRect(sTex, sRect, dTex, dRect, cms, shader, linear);
|
||||
DoStretchRect(sTex, sRect, dTex, dRect, shader, filter);
|
||||
}
|
||||
|
||||
void GSDevice::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect,
|
||||
bool red, bool green, bool blue, bool alpha, ShaderConvert shader)
|
||||
ShaderConvertSelector shader, Filter filter)
|
||||
{
|
||||
GSHWDrawConfig::ColorMaskSelector cms;
|
||||
|
||||
cms.wr = red;
|
||||
cms.wg = green;
|
||||
cms.wb = blue;
|
||||
cms.wa = alpha;
|
||||
|
||||
pxAssert(HasVariableWriteMask(shader));
|
||||
GL_INS("ColorCopy Red:%d Green:%d Blue:%d Alpha:%d", cms.wr, cms.wg, cms.wb, cms.wa);
|
||||
|
||||
DoStretchRectWithAssertions(sTex, sRect, dTex, dRect, cms, shader, false);
|
||||
DoStretchRectWithAssertions(sTex, sRect, dTex, dRect, shader, filter);
|
||||
}
|
||||
|
||||
void GSDevice::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect,
|
||||
ShaderConvert shader, bool linear)
|
||||
void GSDevice::StretchRect(GSTexture* sTex, GSTexture* dTex, const GSVector4& dRect, ShaderConvertSelector shader, Filter filter)
|
||||
{
|
||||
DoStretchRectWithAssertions(sTex, sRect, dTex, dRect, GSHWDrawConfig::ColorMaskSelector(ShaderConvertWriteMask(shader)), shader, linear);
|
||||
StretchRect(sTex, GSVector4(0, 0, 1, 1), dTex, dRect, shader, filter);
|
||||
}
|
||||
|
||||
void GSDevice::StretchRect(GSTexture* sTex, GSTexture* dTex, const GSVector4& dRect, ShaderConvert shader, bool linear)
|
||||
void GSDevice::StretchRect(GSTexture* sTex, GSTexture* dTex, ShaderConvertSelector shader, Filter filter)
|
||||
{
|
||||
StretchRect(sTex, GSVector4(0, 0, 1, 1), dTex, dRect, shader, linear);
|
||||
StretchRect(sTex, dTex, GSVector4(dTex->GetRect()), shader, filter);
|
||||
}
|
||||
|
||||
void GSDevice::StretchRectAuto(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect,
|
||||
Filter filter, u32 src_bpp, u32 dst_bpp)
|
||||
{
|
||||
ShaderConvertSelector shader = GetConvertShader(sTex, dTex, src_bpp, dst_bpp);
|
||||
if (shader.SupportsBilinear() && filter == Biln)
|
||||
{
|
||||
// Bilinear is emulated in the shader.
|
||||
shader.SetFilter(Biln);
|
||||
filter = Nearest;
|
||||
}
|
||||
StretchRect(sTex, sRect, dTex, dRect, shader, filter);
|
||||
}
|
||||
|
||||
void GSDevice::StretchRectAuto(GSTexture* sTex, GSTexture* dTex, const GSVector4& dRect, Filter filter, u32 src_bpp, u32 dst_bpp)
|
||||
{
|
||||
StretchRectAuto(sTex, GSVector4(0, 0, 1, 1), dTex, dRect, filter, src_bpp, dst_bpp);
|
||||
}
|
||||
|
||||
void GSDevice::StretchRectAuto(GSTexture* sTex, GSTexture* dTex, Filter filter, u32 src_bpp, u32 dst_bpp)
|
||||
{
|
||||
StretchRectAuto(sTex, dTex, GSVector4(dTex->GetRect()), filter, src_bpp, dst_bpp);
|
||||
}
|
||||
|
||||
void GSDevice::StretchRectAutoMask(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect,
|
||||
bool red, bool green, bool blue, bool alpha, u32 src_bpp, u32 dst_bpp)
|
||||
{
|
||||
StretchRect(sTex, sRect, dTex, dRect, GetConvertShaderMask(sTex, dTex, src_bpp, dst_bpp, red, green, blue, alpha), Nearest);
|
||||
}
|
||||
|
||||
void GSDevice::StretchRectAutoMask(GSTexture* sTex, GSTexture* dTex, const GSVector4& dRect, bool red, bool green, bool blue, bool alpha, u32 src_bpp, u32 dst_bpp)
|
||||
{
|
||||
StretchRectAutoMask(sTex, GSVector4(0, 0, 1, 1), dTex, dRect, red, green, blue, alpha, src_bpp, dst_bpp);
|
||||
}
|
||||
|
||||
void GSDevice::StretchRectAutoMask(GSTexture* sTex, GSTexture* dTex, bool red, bool green, bool blue, bool alpha,
|
||||
u32 src_bpp, u32 dst_bpp)
|
||||
{
|
||||
StretchRectAutoMask(sTex, dTex, GSVector4(dTex->GetRect()), red, green, blue, alpha, src_bpp, dst_bpp);
|
||||
}
|
||||
|
||||
void GSDevice::DrawMultiStretchRects(
|
||||
const MultiStretchRect* rects, u32 num_rects, GSTexture* dTex, ShaderConvert shader)
|
||||
const MultiStretchRect* rects, u32 num_rects, GSTexture* dTex, ShaderConvertSelector shader)
|
||||
{
|
||||
for (u32 i = 0; i < num_rects; i++)
|
||||
{
|
||||
const MultiStretchRect& sr = rects[i];
|
||||
pxAssert(HasVariableWriteMask(shader) || rects[0].wmask.wrgba == 0xf);
|
||||
if (rects[0].wmask.wrgba != 0xf)
|
||||
{
|
||||
g_gs_device->StretchRect(sr.src, sr.src_rect, dTex, sr.dst_rect, rects[0].wmask.wr,
|
||||
rects[0].wmask.wg, rects[0].wmask.wb, rects[0].wmask.wa, shader);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_gs_device->StretchRect(sr.src, sr.src_rect, dTex, sr.dst_rect, shader, sr.linear);
|
||||
}
|
||||
g_gs_device->StretchRect(sr.src, sr.src_rect, dTex, sr.dst_rect,
|
||||
shader.SetMask(rects[0].wmask.wrgba).SetFilter(sr.filter), sr.filter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -814,7 +904,7 @@ void GSDevice::SortMultiStretchRects(MultiStretchRect* rects, u32 num_rects)
|
||||
{
|
||||
// Depending on num_rects, insertion sort may be better here.
|
||||
std::sort(rects, rects + num_rects, [](const MultiStretchRect& lhs, const MultiStretchRect& rhs) {
|
||||
return lhs.src < rhs.src || lhs.linear < rhs.linear;
|
||||
return lhs.src < rhs.src || lhs.filter < rhs.filter;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -840,7 +930,7 @@ void GSDevice::ClearCurrent()
|
||||
void GSDevice::Merge(GSTexture* sTex[3], GSVector4* sRect, GSVector4* dRect, const GSVector2i& fs, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, u32 c)
|
||||
{
|
||||
if (ResizeRenderTarget(&m_merge, fs.x, fs.y, false, false))
|
||||
DoMerge(sTex, sRect, m_merge, dRect, PMODE, EXTBUF, c, GSConfig.PCRTCOffsets);
|
||||
DoMerge(sTex, sRect, m_merge, dRect, PMODE, EXTBUF, c, BilnIf(GSConfig.PCRTCOffsets));
|
||||
|
||||
m_current = m_merge;
|
||||
}
|
||||
@@ -851,7 +941,7 @@ void GSDevice::Interlace(const GSVector2i& ds, int field, int mode, float yoffse
|
||||
float offset = yoffset * static_cast<float>(field);
|
||||
offset = GSConfig.DisableInterlaceOffset ? 0.0f : offset;
|
||||
|
||||
auto do_interlace = [this](GSTexture* sTex, GSTexture* dTex, ShaderInterlace shader, bool linear, float yoffset, int bufIdx) {
|
||||
auto do_interlace = [this](GSTexture* sTex, GSTexture* dTex, ShaderInterlace shader, Filter filter, float yoffset, int bufIdx) {
|
||||
const GSVector2i ds_i = dTex->GetSize();
|
||||
const GSVector2 ds = GSVector2(static_cast<float>(ds_i.x), static_cast<float>(ds_i.y));
|
||||
|
||||
@@ -872,28 +962,28 @@ void GSDevice::Interlace(const GSVector2i& ds, int field, int mode, float yoffse
|
||||
GSVector4(static_cast<float>(bufIdx), 1.0f / ds.y, ds.y, MAD_SENSITIVITY)
|
||||
};
|
||||
|
||||
GL_PUSH("DoInterlace %dx%d Shader:%d Linear:%d", ds_i.x, ds_i.y, static_cast<int>(shader), linear);
|
||||
DoInterlace(sTex, sRect, dTex, dRect, shader, linear, cb);
|
||||
GL_PUSH("DoInterlace %dx%d Shader:%d Filter:%d", ds_i.x, ds_i.y, static_cast<int>(shader), filter);
|
||||
DoInterlace(sTex, sRect, dTex, dRect, shader, filter, cb);
|
||||
};
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case 0: // Weave
|
||||
ResizeRenderTarget(&m_weavebob, ds.x, ds.y, true, false);
|
||||
do_interlace(m_merge, m_weavebob, ShaderInterlace::WEAVE, false, offset, field);
|
||||
do_interlace(m_merge, m_weavebob, ShaderInterlace::WEAVE, Nearest, offset, field);
|
||||
m_current = m_weavebob;
|
||||
break;
|
||||
case 1: // Bob
|
||||
// Field is reversed here as we are countering the bounce.
|
||||
ResizeRenderTarget(&m_weavebob, ds.x, ds.y, true, false);
|
||||
do_interlace(m_merge, m_weavebob, ShaderInterlace::BOB, true, yoffset * (1 - field), 0);
|
||||
do_interlace(m_merge, m_weavebob, ShaderInterlace::BOB, Biln, yoffset * (1 - field), 0);
|
||||
m_current = m_weavebob;
|
||||
break;
|
||||
case 2: // Blend
|
||||
ResizeRenderTarget(&m_weavebob, ds.x, ds.y, true, false);
|
||||
do_interlace(m_merge, m_weavebob, ShaderInterlace::WEAVE, false, offset, field);
|
||||
do_interlace(m_merge, m_weavebob, ShaderInterlace::WEAVE, Nearest, offset, field);
|
||||
ResizeRenderTarget(&m_blend, ds.x, ds.y, true, false);
|
||||
do_interlace(m_weavebob, m_blend, ShaderInterlace::BLEND, false, 0, 0);
|
||||
do_interlace(m_weavebob, m_blend, ShaderInterlace::BLEND, Biln, 0, 0);
|
||||
m_current = m_blend;
|
||||
break;
|
||||
case 3: // FastMAD Motion Adaptive Deinterlacing
|
||||
@@ -902,9 +992,9 @@ void GSDevice::Interlace(const GSVector2i& ds, int field, int mode, float yoffse
|
||||
bufIdx |= field;
|
||||
bufIdx &= 3;
|
||||
ResizeRenderTarget(&m_mad, ds.x, ds.y * 2.0f, true, false);
|
||||
do_interlace(m_merge, m_mad, ShaderInterlace::MAD_BUFFER, false, offset, bufIdx);
|
||||
do_interlace(m_merge, m_mad, ShaderInterlace::MAD_BUFFER, Nearest, offset, bufIdx);
|
||||
ResizeRenderTarget(&m_weavebob, ds.x, ds.y, true, false);
|
||||
do_interlace(m_mad, m_weavebob, ShaderInterlace::MAD_RECONSTRUCT, false, 0, bufIdx);
|
||||
do_interlace(m_mad, m_weavebob, ShaderInterlace::MAD_RECONSTRUCT, Nearest, 0, bufIdx);
|
||||
m_current = m_weavebob;
|
||||
break;
|
||||
default:
|
||||
@@ -961,7 +1051,7 @@ void GSDevice::Resize(int width, int height)
|
||||
{
|
||||
const GSVector4 sRect(0, 0, 1, 1);
|
||||
const GSVector4 dRect(0, 0, s.x, s.y);
|
||||
StretchRect(m_current, sRect, dTex, dRect, ShaderConvert::COPY, false);
|
||||
StretchRectAuto(m_current, sRect, dTex, dRect, Nearest);
|
||||
m_current = dTex;
|
||||
}
|
||||
}
|
||||
@@ -992,7 +1082,7 @@ bool GSDevice::ResizeRenderTarget(GSTexture** t, int w, int h, bool preserve_con
|
||||
{
|
||||
constexpr GSVector4 sRect = GSVector4::cxpr(0, 0, 1, 1);
|
||||
const GSVector4 dRect = GSVector4(orig_tex->GetRect());
|
||||
StretchRect(orig_tex, sRect, new_tex, dRect, ShaderConvert::COPY, true);
|
||||
StretchRect(orig_tex, sRect, new_tex, dRect, ShaderConvert::COPY, Biln);
|
||||
}
|
||||
|
||||
if (orig_tex)
|
||||
@@ -1012,10 +1102,10 @@ void GSDevice::BeginDSAsRT(GSTexture* ds, const GSVector4i& drawarea)
|
||||
// Create a temporary RT and copy the area needed for the draw.
|
||||
const int w = ds->GetWidth();
|
||||
const int h = ds->GetHeight();
|
||||
m_ds_as_rt = g_gs_device->CreateRenderTarget(w, h, GSTexture::Format::Float32, false, true);
|
||||
m_ds_as_rt = g_gs_device->CreateRenderTarget(w, h, GSTexture::Format::DepthColor, false, true);
|
||||
const GSVector4 dRect(drawarea);
|
||||
const GSVector4 sRect(dRect.x / w, dRect.y / h, dRect.z / w, dRect.w / h);
|
||||
StretchRect(ds, sRect, m_ds_as_rt, dRect, ShaderConvert::FLOAT32_DEPTH_TO_COLOR, false);
|
||||
StretchRectAuto(ds, sRect, m_ds_as_rt, dRect, Nearest);
|
||||
}
|
||||
|
||||
void GSDevice::EndDSAsRT()
|
||||
@@ -1414,9 +1504,9 @@ static const char* GetSetDATMName(SetDATM datm)
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
static const char* GetPSAA1Name(u32 aa1)
|
||||
static const char* GetPSAA1Name(GSHWDrawConfig::PS_AA1 aa1)
|
||||
{
|
||||
switch (static_cast<GSHWDrawConfig::PS_AA1>(aa1))
|
||||
switch (aa1)
|
||||
{
|
||||
case GSHWDrawConfig::PS_AA1::NONE: return "NONE";
|
||||
case GSHWDrawConfig::PS_AA1::LINE: return "LINE";
|
||||
@@ -1426,6 +1516,28 @@ static const char* GetPSAA1Name(u32 aa1)
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
static const char* GetTexHazardName(u32 tex_hazard)
|
||||
{
|
||||
switch (tex_hazard)
|
||||
{
|
||||
case GSHWDrawConfig::TEX_HAZARD_NONE: return "NONE";
|
||||
case GSHWDrawConfig::TEX_HAZARD_RT: return "RT";
|
||||
case GSHWDrawConfig::TEX_HAZARD_DEPTH: return "DEPTH";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
static const char* GetPSROVDepthname(GSHWDrawConfig::PS_ROV_DEPTH rov_depth)
|
||||
{
|
||||
switch (rov_depth)
|
||||
{
|
||||
case GSHWDrawConfig::PS_ROV_DEPTH::NONE: return "NONE";
|
||||
case GSHWDrawConfig::PS_ROV_DEPTH::READ_ONLY: return "READ_ONLY";
|
||||
case GSHWDrawConfig::PS_ROV_DEPTH::READ_WRITE: return "READ_WRITE";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
static void DumpPSSelector(DrawConfigWriter& out, const GSHWDrawConfig::PSSelector& ps)
|
||||
{
|
||||
out.WriteLn("aem_fmt: {}", ps.aem_fmt);
|
||||
@@ -1469,7 +1581,6 @@ static void DumpPSSelector(DrawConfigWriter& out, const GSHWDrawConfig::PSSelect
|
||||
out.WriteLn("no_color: {}", ps.no_color);
|
||||
out.WriteLn("no_color1: {}", ps.no_color1);
|
||||
out.WriteLn("channel: {} ({})", GetPSChannelName(static_cast<ChannelFetch>(ps.channel)), ps.channel);
|
||||
out.WriteLn("channel_fb: {}", ps.channel_fb);
|
||||
out.WriteLn("dither: {} ({})", GetPSDitherName(ps.dither), ps.dither);
|
||||
out.WriteLn("dither_adjust: {}", ps.dither_adjust);
|
||||
out.WriteLn("zclamp: {}", ps.zclamp);
|
||||
@@ -1482,9 +1593,13 @@ static void DumpPSSelector(DrawConfigWriter& out, const GSHWDrawConfig::PSSelect
|
||||
out.WriteLn("point_sampler: {}", ps.point_sampler);
|
||||
out.WriteLn("region_rect: {}", ps.region_rect);
|
||||
out.WriteLn("scanmsk: {} ({})", GSUtil::GetSCANMSKName(ps.scanmsk), ps.scanmsk);
|
||||
out.WriteLn("aa1: {} ({})", static_cast<u32>(ps.aa1), GetPSAA1Name(static_cast<u32>(ps.aa1)));
|
||||
out.WriteLn("aa1: {} ({})", GetPSAA1Name(ps.aa1), static_cast<u32>(ps.aa1));
|
||||
out.WriteLn("abe: {}", static_cast<u32>(ps.abe));
|
||||
out.WriteLn("sw_aniso: {}", ps.sw_aniso);
|
||||
out.WriteLn("rov_color: {}", ps.rov_color);
|
||||
out.WriteLn("rov_depth: {} ({})", GetPSROVDepthname(ps.rov_depth), static_cast<u32>(ps.rov_depth));
|
||||
out.WriteLn("ztst: {} ({})", GSUtil::GetZTSTName(ps.ztst), static_cast<u32>(ps.ztst));
|
||||
out.WriteLn("zfloor: {}", static_cast<u32>(ps.zfloor));
|
||||
}
|
||||
|
||||
static void DumpVSSelector(DrawConfigWriter& out, const GSHWDrawConfig::VSSelector& vs)
|
||||
@@ -1608,6 +1723,9 @@ static void DumpConfig(DrawConfigWriter& out, const GSHWDrawConfig& conf,
|
||||
out.WriteLn("topology: {} ({})", GetTopologyName(conf.topology), static_cast<u32>(conf.topology));
|
||||
out.WriteLn("require_one_barrier: {}", conf.require_one_barrier);
|
||||
out.WriteLn("require_full_barrier: {}", conf.require_full_barrier);
|
||||
DumpVector4(out, "drawarea", conf.drawarea);
|
||||
DumpVector4(out, "samplearea", conf.samplearea);
|
||||
out.WriteLn("tex_hazard: {}", GetTexHazardName(conf.tex_hazard));
|
||||
|
||||
out.WriteLn("destination_alpha: {} ({})", GetDestinationAlphaModeName(conf.destination_alpha), static_cast<u32>(conf.destination_alpha));
|
||||
out.WriteLn("datm: {} ({})", GetSetDATMName(conf.datm), static_cast<u32>(conf.datm));
|
||||
@@ -1680,6 +1798,75 @@ void GSHWDrawConfig::DumpConfig(const std::string& path, const GSHWDrawConfig& c
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr u32 NUM_REMAP_INPUTS = static_cast<u32>(ShaderConvert::Count) * 4;
|
||||
|
||||
static constexpr ShaderConvertSelector GetRemappedShader(u32 idx)
|
||||
{
|
||||
ShaderConvert convert = static_cast<ShaderConvert>(idx >> 2);
|
||||
bool depth_out = (idx >> 0) & 1;
|
||||
Filter filter = static_cast<Filter>((idx >> 1) & 1);
|
||||
return ShaderConvertSelector(convert, 0xf, depth_out, filter);
|
||||
}
|
||||
|
||||
static constexpr bool RemapIndexIsValid(u32 idx)
|
||||
{
|
||||
ShaderConvert convert = static_cast<ShaderConvert>(idx >> 2);
|
||||
bool depth_out = (idx >> 0) & 1;
|
||||
Filter filter = static_cast<Filter>((idx >> 1) & 1);
|
||||
if (HasVariableWriteMask(convert) && !depth_out && filter == Nearest)
|
||||
return false; // Handled as variable write mask
|
||||
if (depth_out && !HasFloat32Output(convert))
|
||||
return false;
|
||||
if (filter == Biln && !SupportsBilinear(convert))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static constexpr u32 CalcNumRemappedShaders()
|
||||
{
|
||||
u32 num = 0;
|
||||
for (u32 i = 0; i < NUM_REMAP_INPUTS; i++)
|
||||
num += RemapIndexIsValid(i);
|
||||
return num;
|
||||
}
|
||||
|
||||
static constexpr u32 NUM_REMAPPED_SHADERS = CalcNumRemappedShaders();
|
||||
static constexpr u32 NUM_TOTAL_SHADERS = NUM_REMAPPED_SHADERS +
|
||||
16 * ShaderConvertSelector::NUM_VARIABLE_WRITE_MASK_SHADERS;
|
||||
static_assert(NUM_REMAPPED_SHADERS <= 256); // We use u8 for the remap indices.
|
||||
|
||||
static constexpr std::array<u8, NUM_REMAP_INPUTS> GenRemapArray()
|
||||
{
|
||||
std::array<u8, NUM_REMAP_INPUTS> out{};
|
||||
u8 out_idx = 0;
|
||||
const u8 invalid = 0xff;
|
||||
for (u32 i = 0; i < NUM_REMAP_INPUTS; i++)
|
||||
out[i] = RemapIndexIsValid(i) ? out_idx++ : invalid;
|
||||
return out;
|
||||
}
|
||||
|
||||
static constexpr std::array<ShaderConvertSelector, NUM_TOTAL_SHADERS> GetPackedShaders()
|
||||
{
|
||||
std::array<ShaderConvertSelector, NUM_TOTAL_SHADERS> out{};
|
||||
u32 append_idx = 0;
|
||||
for (u32 i = 0; i < NUM_REMAP_INPUTS; i++)
|
||||
{
|
||||
if (RemapIndexIsValid(i))
|
||||
out[append_idx++] = GetRemappedShader(i);
|
||||
}
|
||||
for (u32 i = 0; i < 16; i++)
|
||||
out[append_idx++] = ShaderConvertSelector(ShaderConvert::COPY, i);
|
||||
for (u32 i = 0; i < 16; i++)
|
||||
out[append_idx++] = ShaderConvertSelector(ShaderConvert::RTA_CORRECTION, i);
|
||||
return out;
|
||||
}
|
||||
|
||||
constinit const u32 ShaderConvertSelector::NUM_REMAPPED_SHADERS = ::NUM_REMAPPED_SHADERS;
|
||||
constinit const u32 ShaderConvertSelector::NUM_TOTAL_SHADERS = ::NUM_TOTAL_SHADERS;
|
||||
constinit const std::array<u8, NUM_REMAP_INPUTS> ShaderConvertSelector::INDEX_REMAP = GenRemapArray();
|
||||
static constexpr auto PACKED_SHADERS = GetPackedShaders();
|
||||
const std::span<const ShaderConvertSelector> ShaderConvertSelector::SHADERS = PACKED_SHADERS;
|
||||
|
||||
// clang-format off
|
||||
|
||||
// Maps PS2 blend modes to our best approximation of them with PC hardware
|
||||
|
||||
@@ -13,11 +13,27 @@
|
||||
#include "GS/GSAlignedClass.h"
|
||||
#include "GS/GSExtra.h"
|
||||
#include <array>
|
||||
#include <span>
|
||||
|
||||
enum class Filter
|
||||
{
|
||||
Nearest = 0,
|
||||
Biln = 1,
|
||||
};
|
||||
|
||||
static inline constexpr Filter Nearest = Filter::Nearest;
|
||||
static inline constexpr Filter Biln = Filter::Biln;
|
||||
|
||||
static inline constexpr Filter BilnIf(bool biln)
|
||||
{
|
||||
return biln ? Biln : Nearest;
|
||||
}
|
||||
|
||||
enum class ShaderConvert
|
||||
{
|
||||
COPY = 0,
|
||||
RGBA8_TO_16_BITS,
|
||||
DEPTH_COPY,
|
||||
RGB5A1_TO_16_BITS,
|
||||
DATM_1,
|
||||
DATM_0,
|
||||
DATM_1_RTA_CORRECTION,
|
||||
@@ -27,23 +43,16 @@ enum class ShaderConvert
|
||||
RTA_CORRECTION,
|
||||
RTA_DECORRECTION,
|
||||
TRANSPARENCY_FILTER,
|
||||
FLOAT32_TO_16_BITS,
|
||||
FLOAT32_TO_32_BITS,
|
||||
FLOAT32_TO_RGBA8,
|
||||
FLOAT32_TO_RGB8,
|
||||
FLOAT16_TO_RGB5A1,
|
||||
RGBA8_TO_FLOAT32,
|
||||
RGBA8_TO_FLOAT24,
|
||||
RGBA8_TO_FLOAT16,
|
||||
RGB5A1_TO_FLOAT16,
|
||||
RGBA8_TO_FLOAT32_BILN,
|
||||
RGBA8_TO_FLOAT24_BILN,
|
||||
RGBA8_TO_FLOAT16_BILN,
|
||||
RGB5A1_TO_FLOAT16_BILN,
|
||||
FLOAT32_DEPTH_TO_COLOR,
|
||||
FLOAT32_COLOR_TO_DEPTH,
|
||||
FLOAT32_TO_FLOAT24,
|
||||
DEPTH_COPY,
|
||||
DEPTH32_TO_16_BITS,
|
||||
DEPTH32_TO_32_BITS,
|
||||
DEPTH32_TO_RGBA8,
|
||||
DEPTH32_TO_RGB8,
|
||||
DEPTH16_TO_RGB5A1,
|
||||
RGBA8_TO_DEPTH32,
|
||||
RGBA8_TO_DEPTH24,
|
||||
RGBA8_TO_DEPTH16,
|
||||
RGB5A1_TO_DEPTH16,
|
||||
DEPTH32_TO_DEPTH24,
|
||||
DOWNSAMPLE_COPY,
|
||||
RGBA_TO_8I,
|
||||
RGB5A1_TO_8I,
|
||||
@@ -53,6 +62,19 @@ enum class ShaderConvert
|
||||
Count
|
||||
};
|
||||
|
||||
enum class PresentShader
|
||||
{
|
||||
COPY = 0,
|
||||
SCANLINE,
|
||||
DIAGONAL_FILTER,
|
||||
TRIANGULAR_FILTER,
|
||||
COMPLEX_FILTER,
|
||||
LOTTES_FILTER,
|
||||
SUPERSAMPLE_4xRGSS,
|
||||
SUPERSAMPLE_AUTO,
|
||||
Count
|
||||
};
|
||||
|
||||
enum class SetDATM : u8
|
||||
{
|
||||
DATM0 = 0U,
|
||||
@@ -71,7 +93,7 @@ enum class ShaderInterlace
|
||||
Count
|
||||
};
|
||||
|
||||
static inline bool HasVariableWriteMask(ShaderConvert shader)
|
||||
static inline constexpr bool HasVariableWriteMask(ShaderConvert shader)
|
||||
{
|
||||
switch (shader)
|
||||
{
|
||||
@@ -83,37 +105,64 @@ static inline bool HasVariableWriteMask(ShaderConvert shader)
|
||||
}
|
||||
}
|
||||
|
||||
static inline int GetShaderIndexForMask(ShaderConvert shader, int mask)
|
||||
{
|
||||
pxAssert(HasVariableWriteMask(shader));
|
||||
int index = mask;
|
||||
if (shader == ShaderConvert::RTA_CORRECTION)
|
||||
index |= 1 << 4;
|
||||
return index;
|
||||
}
|
||||
|
||||
static inline bool HasDepthOutput(ShaderConvert shader)
|
||||
static inline constexpr bool HasColorOutput(ShaderConvert shader)
|
||||
{
|
||||
switch (shader)
|
||||
{
|
||||
case ShaderConvert::RGBA8_TO_FLOAT32:
|
||||
case ShaderConvert::RGBA8_TO_FLOAT24:
|
||||
case ShaderConvert::RGBA8_TO_FLOAT16:
|
||||
case ShaderConvert::RGB5A1_TO_FLOAT16:
|
||||
case ShaderConvert::RGBA8_TO_FLOAT32_BILN:
|
||||
case ShaderConvert::RGBA8_TO_FLOAT24_BILN:
|
||||
case ShaderConvert::RGBA8_TO_FLOAT16_BILN:
|
||||
case ShaderConvert::RGB5A1_TO_FLOAT16_BILN:
|
||||
case ShaderConvert::FLOAT32_COLOR_TO_DEPTH:
|
||||
case ShaderConvert::FLOAT32_TO_FLOAT24:
|
||||
case ShaderConvert::DEPTH_COPY:
|
||||
case ShaderConvert::COPY:
|
||||
case ShaderConvert::RTA_CORRECTION:
|
||||
case ShaderConvert::RTA_DECORRECTION:
|
||||
case ShaderConvert::TRANSPARENCY_FILTER:
|
||||
case ShaderConvert::DEPTH32_TO_RGBA8:
|
||||
case ShaderConvert::DEPTH32_TO_RGB8:
|
||||
case ShaderConvert::DEPTH16_TO_RGB5A1:
|
||||
case ShaderConvert::DOWNSAMPLE_COPY:
|
||||
case ShaderConvert::RGBA_TO_8I:
|
||||
case ShaderConvert::RGB5A1_TO_8I:
|
||||
case ShaderConvert::CLUT_4:
|
||||
case ShaderConvert::CLUT_8:
|
||||
case ShaderConvert::YUV:
|
||||
case ShaderConvert::COLCLIP_RESOLVE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool HasStencilOutput(ShaderConvert shader)
|
||||
static inline constexpr bool HasFloat32Output(ShaderConvert shader)
|
||||
{
|
||||
switch (shader)
|
||||
{
|
||||
case ShaderConvert::RGBA8_TO_DEPTH32:
|
||||
case ShaderConvert::RGBA8_TO_DEPTH24:
|
||||
case ShaderConvert::RGBA8_TO_DEPTH16:
|
||||
case ShaderConvert::RGB5A1_TO_DEPTH16:
|
||||
case ShaderConvert::DEPTH_COPY:
|
||||
case ShaderConvert::DEPTH32_TO_DEPTH24:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline constexpr bool HasFloat32Input(ShaderConvert shader)
|
||||
{
|
||||
switch (shader)
|
||||
{
|
||||
case ShaderConvert::DEPTH_COPY:
|
||||
case ShaderConvert::DEPTH32_TO_16_BITS:
|
||||
case ShaderConvert::DEPTH32_TO_32_BITS:
|
||||
case ShaderConvert::DEPTH32_TO_RGBA8:
|
||||
case ShaderConvert::DEPTH32_TO_RGB8:
|
||||
case ShaderConvert::DEPTH16_TO_RGB5A1:
|
||||
case ShaderConvert::DEPTH32_TO_DEPTH24:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline constexpr bool IsDATMConvertShader(ShaderConvert shader)
|
||||
{
|
||||
switch (shader)
|
||||
{
|
||||
@@ -127,63 +176,375 @@ static inline bool HasStencilOutput(ShaderConvert shader)
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool SupportsNearest(ShaderConvert shader)
|
||||
static inline constexpr bool HasStencilOutput(ShaderConvert shader)
|
||||
{
|
||||
return IsDATMConvertShader(shader);
|
||||
}
|
||||
|
||||
static inline constexpr int IntegerOutputBpp(ShaderConvert shader)
|
||||
{
|
||||
switch (shader)
|
||||
{
|
||||
case ShaderConvert::RGBA8_TO_FLOAT32_BILN:
|
||||
case ShaderConvert::RGBA8_TO_FLOAT24_BILN:
|
||||
case ShaderConvert::RGBA8_TO_FLOAT16_BILN:
|
||||
case ShaderConvert::RGB5A1_TO_FLOAT16_BILN:
|
||||
return false;
|
||||
case ShaderConvert::DEPTH32_TO_32_BITS:
|
||||
return 32;
|
||||
case ShaderConvert::DEPTH32_TO_16_BITS:
|
||||
case ShaderConvert::RGB5A1_TO_16_BITS:
|
||||
return 16;
|
||||
default:
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool SupportsBilinear(ShaderConvert shader)
|
||||
static inline constexpr bool HasColorClipOutput(ShaderConvert shader)
|
||||
{
|
||||
return (shader == ShaderConvert::COLCLIP_INIT);
|
||||
}
|
||||
|
||||
static inline constexpr bool SupportsBilinear(ShaderConvert shader)
|
||||
{
|
||||
switch (shader)
|
||||
{
|
||||
case ShaderConvert::RGBA8_TO_FLOAT32:
|
||||
case ShaderConvert::RGBA8_TO_FLOAT24:
|
||||
case ShaderConvert::RGBA8_TO_FLOAT16:
|
||||
case ShaderConvert::RGB5A1_TO_FLOAT16:
|
||||
return false;
|
||||
default:
|
||||
case ShaderConvert::RGBA8_TO_DEPTH32:
|
||||
case ShaderConvert::RGBA8_TO_DEPTH24:
|
||||
case ShaderConvert::RGBA8_TO_DEPTH16:
|
||||
case ShaderConvert::RGB5A1_TO_DEPTH16:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u32 ShaderConvertWriteMask(ShaderConvert shader)
|
||||
static inline constexpr u32 ShaderConvertWriteMask(ShaderConvert shader)
|
||||
{
|
||||
switch (shader)
|
||||
{
|
||||
case ShaderConvert::FLOAT32_TO_RGB8:
|
||||
case ShaderConvert::DEPTH32_TO_RGB8:
|
||||
return 0x7;
|
||||
default:
|
||||
return 0xf;
|
||||
}
|
||||
}
|
||||
|
||||
enum class PresentShader
|
||||
static inline constexpr int GetShaderIndexForMask(ShaderConvert shader, int mask)
|
||||
{
|
||||
COPY = 0,
|
||||
SCANLINE,
|
||||
DIAGONAL_FILTER,
|
||||
TRIANGULAR_FILTER,
|
||||
COMPLEX_FILTER,
|
||||
LOTTES_FILTER,
|
||||
SUPERSAMPLE_4xRGSS,
|
||||
SUPERSAMPLE_AUTO,
|
||||
Count
|
||||
pxAssert(HasVariableWriteMask(shader));
|
||||
int index = mask;
|
||||
if (shader == ShaderConvert::RTA_CORRECTION)
|
||||
index |= 1 << 4;
|
||||
return index;
|
||||
}
|
||||
|
||||
static inline constexpr ShaderConvert SetDATMShader(SetDATM datm)
|
||||
{
|
||||
switch (datm)
|
||||
{
|
||||
case SetDATM::DATM1_RTA_CORRECTION:
|
||||
return ShaderConvert::DATM_1_RTA_CORRECTION;
|
||||
case SetDATM::DATM0_RTA_CORRECTION:
|
||||
return ShaderConvert::DATM_0_RTA_CORRECTION;
|
||||
case SetDATM::DATM1:
|
||||
return ShaderConvert::DATM_1;
|
||||
case SetDATM::DATM0:
|
||||
default:
|
||||
return ShaderConvert::DATM_0;
|
||||
}
|
||||
}
|
||||
|
||||
const char* ShaderEntryPoint(ShaderConvert value);
|
||||
const char* ShaderEntryPoint(PresentShader value);
|
||||
const char* ShaderConvertName(ShaderConvert shader);
|
||||
|
||||
class ShaderConvertSelector
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
u32 shader : 8; // Main shader
|
||||
u32 mask : 8; // Variable color mask
|
||||
u32 depth_out : 1; // Depth texture output
|
||||
u32 filter : 1; // Shader filter (HW filter is specified separately)
|
||||
};
|
||||
|
||||
u32 key;
|
||||
} fields;
|
||||
|
||||
static_assert(sizeof(fields) == 4);
|
||||
|
||||
public:
|
||||
constexpr ShaderConvertSelector(ShaderConvert shader = ShaderConvert::COPY, u8 mask = 0xf,
|
||||
bool depth_out = false, Filter filter = Filter::Nearest)
|
||||
: fields { static_cast<u32>(shader) }
|
||||
{
|
||||
*this = SetMask(mask).SetDepthOutput(depth_out).SetFilter(filter);
|
||||
}
|
||||
|
||||
constexpr ShaderConvert Shader() const
|
||||
{
|
||||
return static_cast<ShaderConvert>(fields.shader);
|
||||
}
|
||||
|
||||
constexpr u8 Mask() const
|
||||
{
|
||||
return fields.mask;
|
||||
}
|
||||
|
||||
constexpr u8 DefaultMask() const
|
||||
{
|
||||
return ShaderConvertWriteMask(Shader());
|
||||
}
|
||||
|
||||
constexpr Filter GetFilter() const
|
||||
{
|
||||
return static_cast<Filter>(fields.filter);
|
||||
}
|
||||
|
||||
constexpr bool Biln() const
|
||||
{
|
||||
return GetFilter() == Filter::Biln;
|
||||
}
|
||||
|
||||
constexpr bool Nearest() const
|
||||
{
|
||||
return GetFilter() == Filter::Nearest;
|
||||
}
|
||||
|
||||
constexpr bool SupportsBilinear() const
|
||||
{
|
||||
return ::SupportsBilinear(Shader());
|
||||
}
|
||||
|
||||
constexpr bool ColorOutput() const
|
||||
{
|
||||
return HasColorOutput(Shader());
|
||||
}
|
||||
|
||||
constexpr bool DepthOutput() const
|
||||
{
|
||||
return fields.depth_out;
|
||||
}
|
||||
|
||||
constexpr bool StencilOutput() const
|
||||
{
|
||||
return HasStencilOutput(Shader());
|
||||
}
|
||||
|
||||
constexpr bool DATMConvertShader() const
|
||||
{
|
||||
return IsDATMConvertShader(Shader());
|
||||
}
|
||||
|
||||
constexpr bool Float32Output() const
|
||||
{
|
||||
return HasFloat32Output(Shader());
|
||||
}
|
||||
|
||||
constexpr bool Float32Input() const
|
||||
{
|
||||
return HasFloat32Input(Shader());
|
||||
}
|
||||
|
||||
constexpr int IntegerOutputBpp() const
|
||||
{
|
||||
return ::IntegerOutputBpp(Shader());
|
||||
}
|
||||
|
||||
constexpr bool VariableWriteMask() const
|
||||
{
|
||||
return HasVariableWriteMask(Shader());
|
||||
}
|
||||
|
||||
constexpr bool ColorClipOutput() const
|
||||
{
|
||||
return HasColorClipOutput(Shader());
|
||||
}
|
||||
|
||||
const char* Name() const
|
||||
{
|
||||
return ShaderConvertName(Shader());
|
||||
}
|
||||
|
||||
const char* EntryPoint() const
|
||||
{
|
||||
return ShaderEntryPoint(Shader());
|
||||
}
|
||||
|
||||
constexpr ShaderConvertSelector SetMask(u8 mask = 0xf) const
|
||||
{
|
||||
ShaderConvertSelector tmp = *this;
|
||||
tmp.fields.mask = VariableWriteMask() ? (mask & 0xf) : DefaultMask();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
constexpr ShaderConvertSelector SetMask(bool wr, bool wg, bool wb, bool wa) const
|
||||
{
|
||||
return SetMask((wr ? 1 : 0) | (wg ? 2 : 0) | (wb ? 4 : 0) | (wa ? 8 : 0));
|
||||
}
|
||||
|
||||
constexpr ShaderConvertSelector SetDepthOutput(bool depth_out) const
|
||||
{
|
||||
ShaderConvertSelector tmp = *this;
|
||||
tmp.fields.depth_out = Float32Output() && depth_out;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
constexpr ShaderConvertSelector SetFilter(Filter filter) const
|
||||
{
|
||||
ShaderConvertSelector tmp = *this;
|
||||
tmp.fields.filter = static_cast<u32>(SupportsBilinear() ? filter : Filter::Nearest);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
GSTexture::Format OutputFormat() const
|
||||
{
|
||||
if (DepthOutput())
|
||||
return GSTexture::Format::DepthStencil;
|
||||
else if (int bpp = IntegerOutputBpp())
|
||||
return bpp == 16 ? GSTexture::Format::UInt16 : GSTexture::Format::UInt32;
|
||||
else if (Float32Output())
|
||||
return GSTexture::Format::DepthColor;
|
||||
else if (ColorOutput())
|
||||
return GSTexture::Format::Color;
|
||||
else if (ColorClipOutput())
|
||||
return GSTexture::Format::ColorClip;
|
||||
else
|
||||
return GSTexture::Format::Invalid;
|
||||
}
|
||||
|
||||
private:
|
||||
// Helper variables for packing valid shaders into a contiguous range.
|
||||
static const std::span<const ShaderConvertSelector> SHADERS;
|
||||
static const std::array<u8, static_cast<u32>(ShaderConvert::Count) * 4> INDEX_REMAP;
|
||||
static const u32 NUM_REMAPPED_SHADERS;
|
||||
|
||||
public:
|
||||
static constexpr u32 NUM_VARIABLE_WRITE_MASK_SHADERS = 2;
|
||||
static const u32 NUM_TOTAL_SHADERS;
|
||||
|
||||
u32 Index() const
|
||||
{
|
||||
if (VariableWriteMask() && !fields.depth_out && Nearest())
|
||||
return GetShaderIndexForMask(Shader(), fields.mask) + NUM_REMAPPED_SHADERS;
|
||||
u32 remapped = INDEX_REMAP[(fields.depth_out << 0) +
|
||||
(fields.filter << 1) +
|
||||
(fields.shader << 2)];
|
||||
pxAssert(remapped < NUM_REMAPPED_SHADERS);
|
||||
return remapped;
|
||||
}
|
||||
|
||||
// Inverse of Index()
|
||||
static ShaderConvertSelector Get(u32 index)
|
||||
{
|
||||
return SHADERS[index];
|
||||
}
|
||||
};
|
||||
|
||||
/// Get the name of a shader
|
||||
/// (Can't put methods on an enum class)
|
||||
int SetDATMShader(SetDATM datm);
|
||||
const char* shaderName(ShaderConvert value);
|
||||
const char* shaderName(PresentShader value);
|
||||
static inline ShaderConvertSelector GetConvertShader(GSTexture::Format src, GSTexture::Format dst,
|
||||
u32 src_bpp = 32, u32 dst_bpp = 32, u8 mask = 0xf)
|
||||
{
|
||||
ShaderConvert shader = static_cast<ShaderConvert>(-1);
|
||||
switch (src)
|
||||
{
|
||||
case GSTexture::Format::Color:
|
||||
switch (dst)
|
||||
{
|
||||
case GSTexture::Format::Color:
|
||||
pxAssert(src_bpp == 32 && dst_bpp == 32);
|
||||
shader = ShaderConvert::COPY; // bpp is handled by mask
|
||||
break;
|
||||
case GSTexture::Format::DepthColor:
|
||||
case GSTexture::Format::DepthStencil:
|
||||
switch (dst_bpp)
|
||||
{
|
||||
case 32:
|
||||
shader = ShaderConvert::RGBA8_TO_DEPTH32;
|
||||
break;
|
||||
case 24:
|
||||
shader = ShaderConvert::RGBA8_TO_DEPTH24;
|
||||
break;
|
||||
case 16:
|
||||
pxAssert(src_bpp == 16 || src_bpp == 32);
|
||||
shader = src_bpp == 16 ? ShaderConvert::RGB5A1_TO_DEPTH16 :
|
||||
ShaderConvert::RGBA8_TO_DEPTH16;
|
||||
break;
|
||||
default:
|
||||
pxAssert(false);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pxAssert(false);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case GSTexture::Format::DepthColor:
|
||||
case GSTexture::Format::DepthStencil:
|
||||
switch (dst)
|
||||
{
|
||||
case GSTexture::Format::Color:
|
||||
switch (dst_bpp)
|
||||
{
|
||||
case 32:
|
||||
shader = ShaderConvert::DEPTH32_TO_RGBA8;
|
||||
break;
|
||||
case 24:
|
||||
shader = ShaderConvert::DEPTH32_TO_RGB8;
|
||||
break;
|
||||
case 16:
|
||||
pxAssert(src_bpp == 16);
|
||||
shader = ShaderConvert::DEPTH16_TO_RGB5A1;
|
||||
break;
|
||||
default:
|
||||
pxAssert(false);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case GSTexture::Format::DepthColor:
|
||||
case GSTexture::Format::DepthStencil:
|
||||
switch (dst_bpp)
|
||||
{
|
||||
case 32:
|
||||
pxAssert(src_bpp == 32);
|
||||
shader = ShaderConvert::DEPTH_COPY;
|
||||
break;
|
||||
case 24:
|
||||
pxAssert(src_bpp == 32);
|
||||
shader = ShaderConvert::DEPTH32_TO_DEPTH24;
|
||||
break;
|
||||
default:
|
||||
pxAssert(false);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pxAssert(false);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pxAssert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
return ShaderConvertSelector(shader, mask, dst == GSTexture::Format::DepthStencil);
|
||||
}
|
||||
|
||||
static inline ShaderConvertSelector GetConvertShader(const GSTexture* src, const GSTexture* dst, u32 src_bpp, u32 dst_bpp, u8 mask = 0xf)
|
||||
{
|
||||
return GetConvertShader(src->GetFormat(), dst->GetFormat(), src_bpp, dst_bpp, mask);
|
||||
}
|
||||
|
||||
static inline ShaderConvertSelector GetConvertShaderMask(GSTexture::Format src, GSTexture::Format dst,
|
||||
u32 src_bpp, u32 dst_bpp, bool red = true, bool green = true, bool blue = true, bool alpha = true)
|
||||
{
|
||||
const u8 mask = (red ? 1 : 0) | (green ? 2 : 0) | (blue ? 4 : 0) | (alpha ? 8 : 0);
|
||||
return GetConvertShader(src, dst, src_bpp, dst_bpp, mask);
|
||||
}
|
||||
|
||||
static inline ShaderConvertSelector GetConvertShaderMask(const GSTexture* src, const GSTexture* dst,
|
||||
u32 src_bpp, u32 dst_bpp, bool red = true, bool green = true, bool blue = true, bool alpha = true)
|
||||
{
|
||||
return GetConvertShaderMask(src->GetFormat(), dst->GetFormat(), src_bpp, dst_bpp, red, green, blue, alpha);
|
||||
}
|
||||
|
||||
enum ChannelFetch
|
||||
{
|
||||
@@ -305,6 +666,7 @@ struct alignas(16) GSHWDrawConfig
|
||||
using PS_ATST = GSShader::PS_ATST;
|
||||
using PS_AFAIL = GSShader::PS_AFAIL;
|
||||
using PS_AA1 = GSShader::PS_AA1;
|
||||
using PS_ROV_DEPTH = GSShader::PS_ROV_DEPTH;
|
||||
#pragma pack(push, 1)
|
||||
struct VSSelector
|
||||
{
|
||||
@@ -397,7 +759,6 @@ struct alignas(16) GSHWDrawConfig
|
||||
|
||||
// Others ways to fetch the texture
|
||||
u32 channel : 3;
|
||||
u32 channel_fb : 1;
|
||||
|
||||
// Dithering
|
||||
u32 dither : 2;
|
||||
@@ -426,12 +787,16 @@ struct alignas(16) GSHWDrawConfig
|
||||
|
||||
// Anisotropic filtering
|
||||
u32 sw_aniso : 5;
|
||||
|
||||
// ROVs
|
||||
u32 rov_color : 1;
|
||||
PS_ROV_DEPTH rov_depth : 2;
|
||||
};
|
||||
|
||||
struct
|
||||
{
|
||||
u64 key_lo;
|
||||
u32 key_hi;
|
||||
u64 key_hi;
|
||||
};
|
||||
};
|
||||
__fi PSSelector() : key_lo(0), key_hi(0) {}
|
||||
@@ -440,12 +805,27 @@ struct alignas(16) GSHWDrawConfig
|
||||
__fi bool operator!=(const PSSelector& rhs) const { return (key_lo != rhs.key_lo || key_hi != rhs.key_hi); }
|
||||
__fi bool operator<(const PSSelector& rhs) const { return (key_lo < rhs.key_lo || key_hi < rhs.key_hi); }
|
||||
|
||||
__fi bool IsSWBlending() const
|
||||
{
|
||||
return blend_a || blend_b || blend_d;
|
||||
}
|
||||
|
||||
__fi bool IsZTesting() const
|
||||
{
|
||||
return ztst == ZTST_GEQUAL || ztst == ZTST_GREATER;
|
||||
}
|
||||
|
||||
__fi bool IsAlphaTesting() const
|
||||
{
|
||||
return atst != PS_ATST::NONE;
|
||||
}
|
||||
|
||||
__fi bool IsFeedbackLoopRT() const
|
||||
{
|
||||
const u32 sw_blend_bits = blend_a | blend_b | blend_d;
|
||||
const bool sw_blend_needs_rt = (sw_blend_bits != 0 && ((sw_blend_bits | blend_c) & 1u)) || ((a_masked & blend_c) != 0);
|
||||
const bool afail_needs_rt = afail == PS_AFAIL::ZB_ONLY || afail == PS_AFAIL::RGB_ONLY || afail == PS_AFAIL::RGB_ONLY_SW_Z;
|
||||
return channel_fb || tex_is_fb || fbmask || (date >= 5) || sw_blend_needs_rt || afail_needs_rt;
|
||||
return tex_is_fb || fbmask || (date >= 5) || sw_blend_needs_rt || afail_needs_rt;
|
||||
}
|
||||
|
||||
__fi bool IsFeedbackLoopDepth() const
|
||||
@@ -456,6 +836,11 @@ struct alignas(16) GSHWDrawConfig
|
||||
return afail_needs_depth || ztst_needs_depth || aa1_needs_depth;
|
||||
}
|
||||
|
||||
__fi bool HasShaderDiscard() const
|
||||
{
|
||||
return (IsAlphaTesting() && afail == PS_AFAIL::KEEP) || scanmsk || date || IsZTesting();
|
||||
}
|
||||
|
||||
/// Disables color output from the pixel shader, this is done when all channels are masked.
|
||||
__fi void DisableColorOutput()
|
||||
{
|
||||
@@ -484,9 +869,39 @@ struct alignas(16) GSHWDrawConfig
|
||||
{
|
||||
aa1 = PS_AA1::TRIANGLE;
|
||||
}
|
||||
|
||||
if (rov_depth == PS_ROV_DEPTH::READ_WRITE)
|
||||
{
|
||||
rov_depth = PS_ROV_DEPTH::READ_ONLY;
|
||||
}
|
||||
}
|
||||
|
||||
__fi bool HasColorOutput() const
|
||||
{
|
||||
return !no_color;
|
||||
}
|
||||
|
||||
__fi bool HasDepthOutput() const
|
||||
{
|
||||
return zfloor || zclamp || IsFeedbackLoopDepth() || (rov_depth == PS_ROV_DEPTH::READ_WRITE);
|
||||
}
|
||||
|
||||
__fi bool HasColorROV() const
|
||||
{
|
||||
return rov_color != 0;
|
||||
}
|
||||
|
||||
__fi bool HasDepthROV() const
|
||||
{
|
||||
return rov_depth == PS_ROV_DEPTH::READ_ONLY || rov_depth == PS_ROV_DEPTH::READ_WRITE;
|
||||
}
|
||||
|
||||
__fi bool HasDepthROVWrite() const
|
||||
{
|
||||
return rov_depth == PS_ROV_DEPTH::READ_WRITE;
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(PSSelector) == 12, "PSSelector is 12 bytes");
|
||||
static_assert(sizeof(PSSelector) == 16, "PSSelector is 12 bytes");
|
||||
#pragma pack(pop)
|
||||
struct PSSelectorHash
|
||||
{
|
||||
@@ -811,19 +1226,20 @@ struct alignas(16) GSHWDrawConfig
|
||||
EarlyResolve = 4
|
||||
};
|
||||
|
||||
GSTexture* rt; ///< Render target
|
||||
GSTexture* ds; ///< Depth stencil
|
||||
GSTexture* tex; ///< Source texture
|
||||
GSTexture* pal; ///< Palette texture
|
||||
const GSVertex* verts;///< Vertices to draw
|
||||
const u16* indices; ///< Indices to draw
|
||||
u32 nverts; ///< Number of vertices
|
||||
u32 nindices; ///< Number of indices
|
||||
u32 indices_per_prim; ///< Number of indices that make up one primitive
|
||||
GSTexture* rt; ///< Render target
|
||||
GSTexture* ds; ///< Depth stencil
|
||||
GSTexture* tex; ///< Source texture
|
||||
GSTexture* pal; ///< Palette texture
|
||||
const GSVertex* verts; ///< Vertices to draw
|
||||
const u16* indices; ///< Indices to draw
|
||||
u32 nverts; ///< Number of vertices
|
||||
u32 nindices; ///< Number of indices
|
||||
u32 indices_per_prim; ///< Number of indices that make up one primitive
|
||||
const std::vector<size_t>* drawlist; ///< For reducing barriers on sprites
|
||||
const std::vector<GSVector4i>* drawlist_bbox; ///< For RT copy when barriers not available.
|
||||
GSVector4i scissor; ///< Scissor rect
|
||||
GSVector4i drawarea; ///< Area in the framebuffer which will be modified.
|
||||
GSVector4i samplearea; ///< Area in the texture which will be sampled.
|
||||
Topology topology; ///< Draw topology
|
||||
|
||||
alignas(8) PSSelector ps;
|
||||
@@ -837,6 +1253,13 @@ struct alignas(16) GSHWDrawConfig
|
||||
bool require_one_barrier; ///< Require texture barrier before draw (also used to requst an rt copy if texture barrier isn't supported)
|
||||
bool require_full_barrier; ///< Require texture barrier between all prims
|
||||
|
||||
enum : u32
|
||||
{
|
||||
TEX_HAZARD_NONE,
|
||||
TEX_HAZARD_RT,
|
||||
TEX_HAZARD_DEPTH,
|
||||
} tex_hazard;
|
||||
|
||||
AlphaTestMode alpha_test;
|
||||
|
||||
DestinationAlphaMode destination_alpha;
|
||||
@@ -877,6 +1300,21 @@ struct alignas(16) GSHWDrawConfig
|
||||
GIFRegFRAME colclip_frame;
|
||||
GSVector4i colclip_update_area; ///< Area in the framebuffer which colclip will modify;
|
||||
|
||||
__fi bool IsFeedbackLoopRT(const PSSelector& ps) const
|
||||
{
|
||||
return ps.IsFeedbackLoopRT() || (tex_hazard == TEX_HAZARD_RT);
|
||||
}
|
||||
|
||||
__fi bool IsFeedbackLoopDepth(const PSSelector& ps) const
|
||||
{
|
||||
return ps.IsFeedbackLoopDepth() || (tex_hazard == TEX_HAZARD_DEPTH);
|
||||
}
|
||||
|
||||
bool IsBlending()
|
||||
{
|
||||
return blend.enable || blend_multi_pass.enable || ps.IsSWBlending();
|
||||
}
|
||||
|
||||
// Dumping
|
||||
static void DumpConfig(const std::string& path, const GSHWDrawConfig& conf,
|
||||
bool ps = true, bool vs = true, bool bs = true, bool dss = true, bool ss = true, bool asp = true, bool bmp = true,
|
||||
@@ -894,7 +1332,7 @@ static inline u32 GetExpansionFactor(GSHWDrawConfig::VSExpand expand)
|
||||
case GSHWDrawConfig::VSExpand::Sprite:
|
||||
return 2;
|
||||
case GSHWDrawConfig::VSExpand::TriangleAA1:
|
||||
return 7;
|
||||
return 13;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
@@ -951,6 +1389,7 @@ public:
|
||||
bool test_and_sample_depth: 1; ///< Supports concurrently binding the depth-stencil buffer for sampling and depth testing.
|
||||
bool depth_feedback : 1; ///< Depth feedback loops can be done with DS directly (otherwise need to copy to separate RT). Implies `feedback_loops`.
|
||||
bool aa1 : 1; ///< Supports the GS AA1 feature.
|
||||
bool rov : 1; ///< Supports rasterizer ordered views for both depth and color.
|
||||
FeatureSupport()
|
||||
{
|
||||
memset(this, 0, sizeof(*this));
|
||||
@@ -964,7 +1403,7 @@ public:
|
||||
GSVector4 src_rect;
|
||||
GSVector4 dst_rect;
|
||||
GSTexture* src;
|
||||
bool linear;
|
||||
Filter filter;
|
||||
GSHWDrawConfig::ColorMaskSelector wmask; // 0xf for all channels by default
|
||||
};
|
||||
|
||||
@@ -1041,8 +1480,8 @@ protected:
|
||||
virtual GSTexture* CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format) = 0;
|
||||
GSTexture* FetchSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format, bool clear, bool prefer_unused_texture);
|
||||
|
||||
virtual void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, u32 c, const bool linear) = 0;
|
||||
virtual void DoInterlace(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderInterlace shader, bool linear, const InterlaceConstantBuffer& cb) = 0;
|
||||
virtual void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, u32 c, const Filter filter) = 0;
|
||||
virtual void DoInterlace(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderInterlace shader, Filter filter, const InterlaceConstantBuffer& cb) = 0;
|
||||
virtual void DoFXAA(GSTexture* sTex, GSTexture* dTex) = 0;
|
||||
virtual void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) = 0;
|
||||
|
||||
@@ -1058,9 +1497,14 @@ protected:
|
||||
protected:
|
||||
// Entry point to the renderer-specific StretchRect code.
|
||||
virtual void DoStretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect,
|
||||
GSHWDrawConfig::ColorMaskSelector cms, ShaderConvert shader, bool linear) = 0;
|
||||
void DoStretchRectWithAssertions(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, GSHWDrawConfig::ColorMaskSelector cms, ShaderConvert shader, bool linear);
|
||||
|
||||
ShaderConvertSelector shader, Filter filter) = 0;
|
||||
virtual void DoStretchRect(GSTexture* sTex, const GSVector4& sRect, const GSVector4& dRect,
|
||||
PresentShader shader, Filter filter)
|
||||
{
|
||||
pxFailRel("Not implemented");
|
||||
}
|
||||
void DoStretchRectWithAssertions(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect,
|
||||
ShaderConvertSelector shader, Filter filter);
|
||||
public:
|
||||
GSDevice();
|
||||
virtual ~GSDevice();
|
||||
@@ -1080,7 +1524,7 @@ public:
|
||||
/// Returns a string representing the specified API.
|
||||
static const char* RenderAPIToString(RenderAPI api);
|
||||
|
||||
/// Parses the configured fullscreen mode into its components (width * height @ refresh hz)
|
||||
/// Parses the configured fullscreen mode into its components (width * height @ refresh Hz)
|
||||
static bool GetRequestedExclusiveFullscreenMode(u32* width, u32* height, float* refresh_rate);
|
||||
|
||||
/// Converts a fullscreen mode to a string.
|
||||
@@ -1178,23 +1622,44 @@ public:
|
||||
virtual void InsertDebugMessage(DebugMessageCategory category, const char* fmt, ...) = 0;
|
||||
|
||||
GSTexture* CreateRenderTarget(int w, int h, GSTexture::Format format, bool clear = true, bool prefer_reuse = true);
|
||||
GSTexture* CreateDepthStencil(int w, int h, GSTexture::Format format, bool clear = true, bool prefer_reuse = true);
|
||||
GSTexture* CreateDepthStencil(int w, int h, bool clear = true, bool prefer_reuse = true);
|
||||
GSTexture* CreateDepthColor(int w, int h, bool clear = true, bool prefer_reuse = true);
|
||||
GSTexture* CreateTexture(int w, int h, int mipmap_levels, GSTexture::Format format, bool prefer_reuse = false);
|
||||
GSTexture* CreateRenderTarget(const GSVector2i& size, GSTexture::Format format, bool clear = true, bool prefer_reuse = true);
|
||||
GSTexture* CreateDepthStencil(const GSVector2i& size, bool clear = true, bool prefer_reuse = true);
|
||||
GSTexture* CreateDepthColor(const GSVector2i& size, bool clear = true, bool prefer_reuse = true);
|
||||
GSTexture* CreateTexture(const GSVector2i& size, int mipmap_levels, GSTexture::Format format, bool prefer_reuse = false);
|
||||
GSTexture* CreateCompatible(GSTexture* tex, bool clear = true, bool prefer_reuse = true);
|
||||
GSTexture* CreateCompatible(GSTexture* tex, const GSVector2i& size, bool clear = true, bool prefer_reuse = true);
|
||||
GSTexture* CreateCompatible(GSTexture* tex, int w, int h, bool clear = true, bool prefer_reuse = true);
|
||||
|
||||
virtual std::unique_ptr<GSDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GSTexture::Format format) = 0;
|
||||
|
||||
virtual void CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY) = 0;
|
||||
|
||||
void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, bool red, bool green, bool blue, bool alpha, ShaderConvert shader = ShaderConvert::COPY);
|
||||
void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderConvert shader = ShaderConvert::COPY, bool linear = true);
|
||||
void StretchRect(GSTexture* sTex, GSTexture* dTex, const GSVector4& dRect, ShaderConvert shader = ShaderConvert::COPY, bool linear = true);
|
||||
// StretchRect - all options
|
||||
void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderConvertSelector shader, Filter filter);
|
||||
void StretchRect(GSTexture* sTex, GSTexture* dTex, const GSVector4& dRect, ShaderConvertSelector shader, Filter filter);
|
||||
void StretchRect(GSTexture* sTex, GSTexture* dTex, ShaderConvertSelector shader, Filter filter);
|
||||
|
||||
// StretchRect - infer shader based on formats
|
||||
void StretchRectAuto(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, Filter filter,
|
||||
u32 src_bpp = 32, u32 dst_bpp = 32);
|
||||
void StretchRectAuto(GSTexture* sTex, GSTexture* dTex, const GSVector4& dRect, Filter filter,
|
||||
u32 src_bpp = 32, u32 dst_bpp = 32);
|
||||
void StretchRectAuto(GSTexture* sTex, GSTexture* dTex, Filter filter, u32 src_bpp = 32, u32 dst_bpp = 32);
|
||||
|
||||
// StretchRect - nearest filter, infer shader based on formats, specify channel mask
|
||||
void StretchRectAutoMask(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, bool red, bool green, bool blue, bool alpha, u32 src_bpp = 32, u32 dst_bpp = 32);
|
||||
void StretchRectAutoMask(GSTexture* sTex, GSTexture* dTex, const GSVector4& dRect, bool red, bool green, bool blue, bool alpha, u32 src_bpp = 32, u32 dst_bpp = 32);
|
||||
void StretchRectAutoMask(GSTexture* sTex, GSTexture* dTex, bool red, bool green, bool blue, bool alpha, u32 src_bpp = 32, u32 dst_bpp = 32);
|
||||
|
||||
/// Performs a screen blit for display. If dTex is null, it assumes you are writing to the system framebuffer/swap chain.
|
||||
virtual void PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, PresentShader shader, float shaderTime, bool linear) = 0;
|
||||
virtual void PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, PresentShader shader, float shaderTime, Filter filter) = 0;
|
||||
|
||||
/// Same as doing StretchRect for each item, except tries to batch together rectangles in as few draws as possible.
|
||||
/// The provided list should be sorted by texture, the implementations only check if it's the same as the last.
|
||||
virtual void DrawMultiStretchRects(const MultiStretchRect* rects, u32 num_rects, GSTexture* dTex, ShaderConvert shader = ShaderConvert::COPY);
|
||||
virtual void DrawMultiStretchRects(const MultiStretchRect* rects, u32 num_rects, GSTexture* dTex, ShaderConvertSelector shader = ShaderConvert::COPY);
|
||||
|
||||
/// Sorts a MultiStretchRect list for optimal batching.
|
||||
static void SortMultiStretchRects(MultiStretchRect* rects, u32 num_rects);
|
||||
|
||||
@@ -695,13 +695,12 @@ void GSRenderer::VSync(u32 field, bool registers_written, bool idle_frame)
|
||||
const float shader_time = static_cast<float>(Common::Timer::ConvertValueToSeconds(current_time - m_shader_time_start));
|
||||
|
||||
g_gs_device->PresentRect(current, src_uv, nullptr, draw_rect,
|
||||
s_tv_shader_indices[GSConfig.TVShader], shader_time, GSConfig.LinearPresent != GSPostBilinearMode::Off);
|
||||
s_tv_shader_indices[GSConfig.TVShader], shader_time, BilnIf(GSConfig.LinearPresent != GSPostBilinearMode::Off));
|
||||
}
|
||||
|
||||
EndPresentFrame();
|
||||
|
||||
if (GSConfig.OsdShowGPU || GSDumpReplayer::IsReplayingDump())
|
||||
PerformanceMetrics::OnGPUPresent(g_gs_device->GetAndResetAccumulatedGPUTime());
|
||||
PerformanceMetrics::OnGPUPresent(g_gs_device->GetAndResetAccumulatedGPUTime());
|
||||
}
|
||||
|
||||
PerformanceMetrics::Update(registers_written, fb_sprite_frame, false);
|
||||
@@ -818,7 +817,7 @@ void GSRenderer::VSync(u32 field, bool registers_written, bool idle_frame)
|
||||
GSTexture* temp = g_gs_device->CreateRenderTarget(size.x, size.y, GSTexture::Format::Color, false);
|
||||
if (temp)
|
||||
{
|
||||
g_gs_device->StretchRect(current, temp, GSVector4(0, 0, size.x, size.y));
|
||||
g_gs_device->StretchRect(current, temp, GSVector4(0, 0, size.x, size.y), ShaderConvert::COPY, Biln);
|
||||
GSCapture::DeliverVideoFrame(temp);
|
||||
g_gs_device->Recycle(temp);
|
||||
}
|
||||
@@ -972,7 +971,7 @@ void GSRenderer::PresentCurrentFrame()
|
||||
const float shader_time = static_cast<float>(Common::Timer::ConvertValueToSeconds(current_time - m_shader_time_start));
|
||||
|
||||
g_gs_device->PresentRect(current, src_uv, nullptr, draw_rect,
|
||||
s_tv_shader_indices[GSConfig.TVShader], shader_time, GSConfig.LinearPresent != GSPostBilinearMode::Off);
|
||||
s_tv_shader_indices[GSConfig.TVShader], shader_time, BilnIf(GSConfig.LinearPresent != GSPostBilinearMode::Off));
|
||||
}
|
||||
|
||||
EndPresentFrame();
|
||||
@@ -1086,7 +1085,7 @@ bool GSRenderer::SaveSnapshotToMemory(u32 window_width, u32 window_height, bool
|
||||
if (dl)
|
||||
{
|
||||
const GSVector4i rc(0, 0, draw_width, draw_height);
|
||||
g_gs_device->StretchRect(current, src_uv, rt, GSVector4(rc), ShaderConvert::TRANSPARENCY_FILTER);
|
||||
g_gs_device->StretchRect(current, src_uv, rt, GSVector4(rc), ShaderConvert::TRANSPARENCY_FILTER, Biln);
|
||||
dl->CopyFromTexture(rc, rt, rc, 0);
|
||||
dl->Flush();
|
||||
|
||||
|
||||
@@ -56,4 +56,11 @@ enum class PS_AA1 : uint32_t
|
||||
TRIANGLE_SW_Z = 3, ///< AA1 triangles with software Z discard
|
||||
};
|
||||
|
||||
enum class PS_ROV_DEPTH : uint32_t
|
||||
{
|
||||
NONE = 0,
|
||||
READ_WRITE = 1,
|
||||
READ_ONLY = 2,
|
||||
};
|
||||
|
||||
} // namespace GSShader
|
||||
|
||||