Compare commits

..

63 Commits

Author SHA1 Message Date
jasaaved 04438ace57 FullscreenUI: Fix analog requiring neutral position to reset
Before, the analog stick would have to reset to the neutral position before allowing the cursor to move again. This has been fixed, cursor moves now once it crosses 45 degrees towards a new axis.
2026-06-13 15:34:27 -04:00
jasaaved e16236e450 FullscreenUI: Cleanup formatting
Fix formatting in pre-existing code
2026-06-13 15:34:27 -04:00
jasaaved 3807517e02 FullscreenUI: Fix analog/dpad nav state shared across controllers
Per-controller state was stored in function-local statics, meaning one controller could corrupt the input state of another. Move to a per-controller map keyed by controller ID.
2026-06-13 15:34:27 -04:00
jasaaved ab8bd8e754 FullscreenUI: Add analog navigation for Xinput and Dinput 2026-06-13 15:34:27 -04:00
jasaaved 65d63085f8 FullscreenUI: Fix analog stick input bursting
Analog inputs are now read as single event once it passes the deadzone / threshold.
2026-06-13 15:34:27 -04:00
jasaaved 2c8ecc3ea7 FullscreenUI: Remove diagonal navigation
Removing diagonal navigation makes moving across titles feel smoother and works closer to Steam's big picture mode.
2026-06-13 15:34:27 -04:00
jasaaved 5fbc79c466 FullscreenUI: Add analog control 2026-06-13 15:34:27 -04:00
SternXD 8fe4183856 FullscreenUI: Fix volume sliders to match Qts max 200%
Signed-off-by: SternXD <stern@sidestore.io>
2026-06-13 15:33:29 -04:00
SternXD 0d52608f1d FullscreenUI: Add some missing settings from Qt
Signed-off-by: SternXD <stern@sidestore.io>
2026-06-13 15:33:29 -04:00
SternXD 5df3c54b84 FullscreenUI: Move Achievement page/login to settings file
I guess this was an oversight when I was splitting up FullscreenUI

Signed-off-by: SternXD <stern@sidestore.io>
2026-06-13 15:33:29 -04:00
SternXD b2db2e71b6 FullscreenUI: Correct default label for Division Rounding Mode
Signed-off-by: SternXD <stern@sidestore.io>
2026-06-13 15:33:29 -04:00
SternXD 7978370b39 Common/Emitter: Inline _xMovRtoR into xImpl_Mov operator
Signed-off-by: SternXD <stern@sidestore.io>
2026-06-13 09:34:53 -04:00
TheLastRar d187c61599 GS/DX12: Enable tight alignment 2026-06-12 18:08:03 -04:00
TheLastRar fa745a58b2 3rdparty: Update D3D12MA to v3.2.0 2026-06-12 18:08:03 -04:00
lightningterror 16592f3a6e GS/GL: Expand the fb optimization hit detection. 2026-06-12 22:29:47 +02:00
lightningterror 2b5d3510c3 GS/DX11: Expand the fb optimization hit detection. 2026-06-12 22:29:47 +02:00
TJnotJT 76f690d96e GS/DX11/GL: Fix sample area calculation for RT/DS sampling hazard. 2026-06-12 18:58:17 +02:00
PCSX2 Bot d5f75c9e42 [ci skip] Qt: Update Base Translation. 2026-06-10 02:26:28 +02:00
lightningterror 13d3491571 GS/GL: Add basic clip control fallback.
When GLAD_GL_ARB_clip_control is not supported then change
the vertex shader depth ranges to make sure it matches -1 1.
2026-06-10 01:35:46 +02:00
lightningterror 8a6d6818fb Revert "GS/GL: Discard stencil when not in use while using depth."
This reverts commit e0365c1d10.
2026-06-10 01:35:46 +02:00
Ty d7c1cbbc4e GHActions: Report flathub cronjob errors to discord 2026-06-09 18:35:41 -04:00
JordanTheToaster 5644ca09c4 GameDB: Dragon Quest VIII fixes 2026-06-09 22:39:03 +02:00
JordanTheToaster 863307d871 ImGuiOverlays: Add missing space to draw buffering 2026-06-09 22:39:03 +02:00
lightningterror 904a0942f0 GS/DX11: Destroy uav resources. 2026-06-09 09:22:07 +02:00
lightningterror 7f20ff60ea GS/DX11: Fallback to regular OMSetRenderTargets if no uav are active. 2026-06-09 09:22:07 +02:00
lightningterror 1ce246e05d GS/DX11: Reuse frame buffer optimizations for uav/rov. 2026-06-09 09:22:07 +02:00
PCSX2 Bot f79461b407 [ci skip] Qt: Update Base Translation. 2026-06-09 03:53:56 +02:00
PCSX2 Bot bbfe32e963 [ci skip] PAD: Update to latest controller database. 2026-06-09 03:53:42 +02:00
wxvu 06c67b0319 GameDB: Various fixes
GameDB: Various fixes

GameDB: Various fixes

GameDB: Various fixes
2026-06-07 15:25:08 -04:00
Adso Castro c68753d776 Qt: Add blank lines around the macro window title block 2026-06-07 15:24:54 -04:00
Adso Castro 15172303e4 Qt: Use multitap slot names for the first slot too
The binding widget and macro window titles keyed off sioPadIsMultitapSlot(),
which is only true for the multitap-exclusive slots (B/C/D), so the first slot
of each port (1A/2A) was shown without its slot letter while multitap was
enabled. Decide based on whether multitap is enabled for the port instead, so
all four slots are labelled consistently with the side list.
2026-06-07 15:24:54 -04:00
Adso Castro 9017ad626d Qt: Remove translator comments from controller binding titles 2026-06-07 15:24:54 -04:00
Adso Castro ef18567d9f Qt: Show multitap slot names in controller binding titles
The ControllerBindingWidget group box title and the ControllerMacroWidget
window title always read "Controller Port N", ignoring multitap slots. When
a multitap is enabled the side list already shows "Controller Port NA/B/C/D"
(handled in ControllerSettingsWindow), but the per-slot widget titles did not
follow suit, so every multitap slot looked like it referred to the base port.

Use sioPadIsMultitapSlot()/sioConvertPadToPortAndSlot() to render the same
"Controller Port NA" style labels in both titles, mirroring the existing
list-item logic.

Fixes #14529

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 15:24:54 -04:00
SternXD d0c2097cd4 Qt/ImGui: Use macOS keyboard behaviors
Signed-off-by: SternXD <stern@sidestore.io>
2026-06-07 15:02:57 -04:00
SternXD b5ed65ddd6 Qt/ImGui: Add clipboard support and fix modfier keys
Signed-off-by: SternXD <stern@sidestore.io>
2026-06-07 15:02:57 -04:00
Adso Castro 673917ae66 Qt: Fix game list view toggle with an empty game list
When the game list is empty the placeholder page is shown, and showGameList()/
showGameGrid() returned early before persisting the GameListGridView setting, so
toggling the view had no effect. updateToolbar() also derived the button state
from the current stack index, which is always the placeholder page when empty,
so the toolbar never reflected the selected mode.

Persist the preference before the early-out and base the toolbar state on the
saved setting, so switching views works (and is remembered for when games are
added) even while the list is empty.

Fixes #13220

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 14:54:17 -04:00
SternXD e31f57a02f GameList: Create covers directory if missing before downloading covers
Signed-off-by: SternXD <stern@sidestore.io>
2026-06-07 14:53:56 -04:00
SternXD ee57382d6e FullscreenUI: Implement cover download support
Signed-off-by: SternXD <stern@sidestore.io>
2026-06-07 14:53:56 -04:00
SternXD 6683f84da0 Tests: remove stubs that aren't needed
Signed-off-by: SternXD <stern@sidestore.io>
2026-06-07 14:53:56 -04:00
SternXD d2cb0aaf63 GSRunner: Remove noops that aren't needed
Signed-off-by: SternXD <stern@sidestore.io>
2026-06-07 14:53:56 -04:00
SternXD 51a5e10b4e Qt: Remove unneeded BPM handling code
Signed-off-by: SternXD <stern@sidestore.io>
2026-06-07 14:53:56 -04:00
refractionpcsx2 c2dfc10c18 GS/HW: Fix draw offsets in shuffles 2026-06-07 14:41:23 -04:00
refractionpcsx2 6628de5262 GS/HW: Only expand overlaps on page boundaries 2026-06-07 14:41:23 -04:00
refractionpcsx2 37f2837a55 GS/TC: Don't pre-downscale if native palette draw is enabled 2026-06-07 14:41:03 -04:00
refractionpcsx2 4b76157b9e GS/TC: Add new HW Readback option to force full download 2026-06-07 14:39:52 -04:00
refractionpcsx2 3b2962fe1c GS: Fix some more draw buffering edgecases 2026-06-07 14:21:48 -04:00
refractionpcsx2 20721b05fb GS: Fix temp rect handling 2026-06-07 14:21:48 -04:00
refractionpcsx2 75187d69c6 GS: Fix dirty reg handling 2026-06-07 14:21:48 -04:00
chaoticgd 3df128d9f9 PINE: Ignore SIGPIPE properly on macOS 2026-06-06 09:59:21 -04:00
PCSX2 Bot 426d77a932 [ci skip] Qt: Update Base Translation. 2026-06-04 02:56:46 +02:00
lightningterror b3b1ff0f50 GS/GL: Expand fb copy fallbacks.
Add nv extension GLAD_GL_NV_copy_image copy fallback.

Re add fbo copy fallback when no copy extension is supported and expand it for both color and depth.
2026-06-04 02:22:17 +02:00
KamFretoZ 88029a1411 Qt/FSUI: Enable ROV by default 2026-06-03 20:07:26 +02:00
JordanTheToaster 57e46f07fe Deps: Update SDL3 to v3.4.10 2026-06-03 05:38:57 +02:00
TJnotJT 254d955f89 GS/HW: Fix PS selector bits format in debug strings.
Fix the formatting since bit width went from 96 to 128.
2026-06-03 04:52:54 +02:00
TJnotJT e490685b42 GS/HW: Improve unordered access flag handling for RTs.
Only for DX11, which doesn't track resource state otherwise.
2026-06-03 03:26:24 +02:00
TJnotJT 7b0c39a20d GS/GL: Remove ROV support.
Buggy on multiple systems, even with various barrier hacks.
2026-06-03 03:26:24 +02:00
TJnotJT 059d631a6d QT/FSUI: Add ROV options to FSUI and QT.
Co-authored-by: SternXD
2026-06-03 03:26:24 +02:00
TJnotJT 7c255bd58d GS/VK: Fix for broken feedback transitions on Windows RDNA2.
Co-authored-by: TheLastRar
2026-06-03 03:26:24 +02:00
TJnotJT bbcd5ec25e GS/HW: ROV support. 2026-06-03 03:26:24 +02:00
chaoticgd 5369d6b38e CMake: Fix typo in BuildParameters.cmake 2026-06-02 07:14:55 -04:00
chaoticgd b3834427bc PINE: Don't test for a valid VM when checking emulator version 2026-06-01 23:55:19 +02:00
PCSX2 Bot d7002433a2 [ci skip] PAD: Update to latest controller database. 2026-06-01 23:54:08 +02:00
Dreadmoth e1291d5b7c Misc: Correct Hz symbol case 2026-06-01 09:51:12 -04:00
99 changed files with 5763 additions and 3142 deletions
+17
View File
@@ -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
@@ -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
@@ -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": [
@@ -47,7 +47,7 @@ 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
@@ -93,7 +93,7 @@ b2bf6c6845ac175ed7f819145483ba4676f617aaa6a5012c8efee63c8bbac413 qtimageformats
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
@@ -24,7 +24,7 @@ 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
@@ -69,7 +69,7 @@ b2bf6c6845ac175ed7f819145483ba4676f617aaa6a5012c8efee63c8bbac413 qtimageformats
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(",");
@@ -60,7 +60,7 @@ 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
@@ -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
@@ -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,6 +97,7 @@ 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" 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
@@ -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
@@ -507,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
+2
View File
@@ -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)
+3 -2
View File
@@ -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" />
+12 -3
View File
@@ -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.
+249 -57
View File
@@ -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 = {};
+55 -9
View File
@@ -1620,6 +1620,7 @@ SCAJ-20055:
vuClampMode: 3 # Stops car from falling through track.
gsHWFixes:
textureInsideRT: 1 # Fixes broken splitscreen mode.
halfPixelOffset: 4 # Fixes misaligned blur in replays.
SCAJ-20056:
name: "Bujingai"
region: "NTSC-Unk"
@@ -1971,6 +1972,8 @@ SCAJ-20110:
halfPixelOffset: 2 # Sharpens world in far distances, aligns some bloom better.
roundSprite: 1 # Fixes font artifacts.
nativeScaling: 2 # Fixes background DOF blur in battles.
cpuSpriteRenderBW: 1 # Fixes water and other sprite errors.
cpuSpriteRenderLevel: 2 # Needed for above.
SCAJ-20111:
name: "Crash Bandicoot 5"
region: "NTSC-Unk"
@@ -4898,7 +4901,7 @@ SCES-50360:
name: "Twisted Metal - Black"
region: "PAL-M5"
gsHWFixes:
recommendedBlendingLevel: 4 # Fixes menu text brightness.
minimumBlendingLevel: 4 # Fixes menu text brightness.
halfPixelOffset: 4 # Fixes misaligned blur during Darkside special.
SCES-50361:
name: "Jak and Daxter - The Precursor Legacy"
@@ -5422,7 +5425,7 @@ SCES-51480:
name: "Twisted Metal - Black Online"
region: "PAL-M5"
gsHWFixes:
recommendedBlendingLevel: 4 # Fixes menu text brightness.
minimumBlendingLevel: 4 # Fixes menu text brightness.
halfPixelOffset: 4 # Fixes misaligned blur during Darkside special.
SCES-51513:
name: "EyeToy - Play"
@@ -11114,7 +11117,7 @@ SCUS-97101:
region: "NTSC-U"
compat: 5
gsHWFixes:
recommendedBlendingLevel: 4 # Fixes menu text brightness.
minimumBlendingLevel: 4 # Fixes menu text brightness.
halfPixelOffset: 4 # Fixes misaligned blur during Darkside special.
SCUS-97102:
name: "Gran Turismo 3 - A-Spec"
@@ -11377,7 +11380,7 @@ SCUS-97164:
name: "Twisted Metal - Black [Demo]"
region: "NTSC-U"
gsHWFixes:
recommendedBlendingLevel: 4 # Fixes menu text brightness.
minimumBlendingLevel: 4 # Fixes menu text brightness.
halfPixelOffset: 4 # Fixes misaligned blur during Darkside special.
SCUS-97165:
name: "Official U.S. PlayStation Magazine Demo Disc 051"
@@ -11445,7 +11448,7 @@ SCUS-97179:
region: "NTSC-U"
compat: 5
gsHWFixes:
recommendedBlendingLevel: 4 # Fixes menu text brightness.
minimumBlendingLevel: 4 # Fixes menu text brightness.
halfPixelOffset: 4 # Fixes misaligned blur during Darkside special.
SCUS-97181:
name: "Official U.S. PlayStation Magazine Demo Disc 055"
@@ -11490,14 +11493,14 @@ SCUS-97195:
name: "Twisted Metal - Black Online"
region: "NTSC-U"
gsHWFixes:
recommendedBlendingLevel: 4 # Fixes menu text brightness.
minimumBlendingLevel: 4 # Fixes menu text brightness.
halfPixelOffset: 4 # Fixes misaligned blur during Darkside special.
SCUS-97196:
name: "Twisted Metal - Black Online"
region: "NTSC-U"
compat: 5
gsHWFixes:
recommendedBlendingLevel: 4 # Fixes menu text brightness.
minimumBlendingLevel: 4 # Fixes menu text brightness.
halfPixelOffset: 4 # Fixes misaligned blur during Darkside special.
SCUS-97197:
name: "War of the Monsters"
@@ -13177,7 +13180,8 @@ SCUS-97621:
clampModes:
vu1ClampMode: 3 # Fixes missing textures.
gsHWFixes:
recommendedBlendingLevel: 4 # Fixes menu text brightness and smoke effects.
minimumBlendingLevel: 4 # Fixes menu text brightness and smoke effects.
halfPixelOffset: 4 # Fixes misaligned blur during Darkside special.
gameFixes:
- VUSyncHack # Fixes black doors on vehicles.
patches:
@@ -14432,6 +14436,8 @@ SLED-53977:
halfPixelOffset: 2 # Sharpens world in far distances, aligns some bloom better.
roundSprite: 1 # Fixes font artifacts.
nativeScaling: 2 # Fixes background DOF blur in battles.
cpuSpriteRenderBW: 1 # Fixes water and other sprite errors.
cpuSpriteRenderLevel: 2 # Needed for above.
SLED-53980:
name: "Urban Chaos - Riot Response [Demo]"
region: "PAL-E"
@@ -14821,6 +14827,8 @@ SLES-50113:
name: "Ring of Red"
region: "PAL-M3"
compat: 5
gameFixes:
- SoftwareRendererFMVHack # Fixes FMV lines.
gsHWFixes:
alignSprite: 1 # Fixes vertical lines.
SLES-50114:
@@ -16220,6 +16228,7 @@ SLES-50731:
clampModes:
vuClampMode: 2 # White textures.
gsHWFixes:
drawBuffering: 1 # Reduces draws and improves performance.
recommendedBlendingLevel: 3 # Improves road quality.
halfPixelOffset: 2 # Fixes depth line and blur.
SLES-50735:
@@ -20026,6 +20035,8 @@ SLES-52275:
name: "Way of the Samurai 2"
region: "PAL-M3"
compat: 5
gsHWFixes:
roundSprite: 2 # Fixes UI lines.
SLES-52276:
name: "187 - Ride or Die"
region: "PAL-M5"
@@ -25380,6 +25391,8 @@ SLES-53974:
halfPixelOffset: 2 # Sharpens world in far distances, aligns some bloom better.
roundSprite: 1 # Fixes font artifacts.
nativeScaling: 2 # Fixes background DOF blur in battles.
cpuSpriteRenderBW: 1 # Fixes water and other sprite errors.
cpuSpriteRenderLevel: 2 # Needed for above.
SLES-53976:
name: "Evolution GT"
region: "PAL-M5"
@@ -35535,6 +35548,8 @@ SLPM-60122:
name-sort: "りんぐ おぶ れっど [たいけんばん]"
name-en: "Ring of Red [Trial]"
region: "NTSC-J"
gameFixes:
- SoftwareRendererFMVHack # Fixes FMV lines.
gsHWFixes:
alignSprite: 1 # Fixes vertical lines.
SLPM-60123:
@@ -37312,6 +37327,8 @@ SLPM-62013:
name-sort: "りんぐ おぶ れっど"
name-en: "Ring of Red"
region: "NTSC-J"
gameFixes:
- SoftwareRendererFMVHack # Fixes FMV lines.
gsHWFixes:
alignSprite: 1 # Fixes vertical lines.
SLPM-62015:
@@ -37669,6 +37686,8 @@ SLPM-62091:
name-sort: "りんぐ おぶ れっど [KONAMI The BEST]"
name-en: "Ring of Red [KONAMI The BEST]"
region: "NTSC-J"
gameFixes:
- SoftwareRendererFMVHack # Fixes FMV lines.
gsHWFixes:
alignSprite: 1 # Fixes vertical lines.
SLPM-62092:
@@ -39697,6 +39716,8 @@ SLPM-62490:
halfPixelOffset: 2 # Sharpens world in far distances, aligns some bloom better.
roundSprite: 1 # Fixes font artifacts.
nativeScaling: 2 # Fixes background DOF blur in battles.
cpuSpriteRenderBW: 1 # Fixes water and other sprite errors.
cpuSpriteRenderLevel: 2 # Needed for above.
SLPM-62491:
name: "ロックマン パワーバトルファイターズ"
name-sort: "ろっくまん ぱわーばとるふぁいたーず"
@@ -42375,7 +42396,9 @@ SLPM-65181:
clampModes:
vuClampMode: 2 # White textures.
gsHWFixes:
halfPixelOffset: 1 # Fixes blurriness.
drawBuffering: 1 # Reduces draws and improves performance.
recommendedBlendingLevel: 3 # Improves road quality.
halfPixelOffset: 2 # Fixes depth line and blur.
SLPM-65183:
name: "auto modellista モデムパック バージョン"
name-sort: "おーともでりすた もでむぱっく ばーじょん"
@@ -43566,6 +43589,8 @@ SLPM-65380:
name-sort: "さむらいどう2 WAY OF THE SAMURAI 2"
name-en: "Samurai Dou 2"
region: "NTSC-J"
gsHWFixes:
roundSprite: 2 # Fixes UI lines.
SLPM-65381:
name: "きまぐれストロベリーカフェ"
name-sort: "きまぐれすとろべりーかふぇ"
@@ -43877,6 +43902,7 @@ SLPM-65434:
vuClampMode: 3 # Stops car from falling through track.
gsHWFixes:
textureInsideRT: 1 # Fixes broken splitscreen mode.
halfPixelOffset: 4 # Fixes misaligned blur in replays.
SLPM-65435:
name: "北へ。~Diamond Dust"
name-sort: "きたへ Diamond Dust"
@@ -46487,6 +46513,8 @@ SLPM-65884:
name-sort: "りもーとこんとろーるだんでぃSF"
name-en: "Remote Control Dandy SF"
region: "NTSC-J"
gsHWFixes:
minimumBlendingLevel: 4 # Fixes health bar flickering in tutorial level.
SLPM-65885:
name: "RUMBLE ROSES"
name-sort: "らんぶるろーず"
@@ -46516,6 +46544,8 @@ SLPM-65888:
roundSprite: 1 # Fixes font artifacts.
nativeScaling: 2 # Fixes background DOF blur in battles.
PCRTCOverscan: 1 # Fixes offscreen image.
cpuSpriteRenderBW: 1 # Fixes water and other sprite errors.
cpuSpriteRenderLevel: 2 # Needed for above.
SLPM-65889:
name: "家族計画~心の絆~"
name-sort: "かぞくけいかく こころのきずな"
@@ -48885,6 +48915,8 @@ SLPM-66260:
name-sort: "りもーとこんとろーるだんでぃSF [KONAMI The BEST]"
name-en: "Remote Control Dandy SF [KONAMI The BEST]"
region: "NTSC-J"
gsHWFixes:
minimumBlendingLevel: 4 # Fixes health bar flickering in tutorial level.
SLPM-66261:
name: "OZ -オズ- [KONAMI The BEST]"
name-sort: "おず [KONAMI The BEST]"
@@ -50291,6 +50323,8 @@ SLPM-66481:
halfPixelOffset: 2 # Sharpens world in far distances, aligns some bloom better.
roundSprite: 1 # Fixes font artifacts.
nativeScaling: 2 # Fixes background DOF blur in battles.
cpuSpriteRenderBW: 1 # Fixes water and other sprite errors.
cpuSpriteRenderLevel: 2 # Needed for above.
SLPM-66482:
name: "ときめきメモリアル Girl's Side 2nd Kiss [リピート生産版]"
name-sort: "ときめきめもりある がーるずさいど 2nd Kiss [りぴーとせいさんばん]"
@@ -53693,6 +53727,7 @@ SLPM-67527:
clampModes:
vuClampMode: 2 # White textures.
gsHWFixes:
drawBuffering: 1 # Reduces draws and improves performance.
recommendedBlendingLevel: 3 # Improves road quality.
halfPixelOffset: 2 # Fixes depth line and blur.
SLPM-67528:
@@ -65066,6 +65101,8 @@ SLUS-20145:
name: "Ring of Red"
region: "NTSC-U"
compat: 5
gameFixes:
- SoftwareRendererFMVHack # Fixes FMV lines.
gsHWFixes:
alignSprite: 1 # Fixes vertical lines.
SLUS-20146:
@@ -66118,6 +66155,7 @@ SLUS-20362:
clampModes:
vuClampMode: 2 # White textures.
gsHWFixes:
drawBuffering: 1 # Reduces draws and improves performance.
recommendedBlendingLevel: 3 # Improves road quality.
halfPixelOffset: 2 # Fixes depth line and blur.
SLUS-20363:
@@ -69015,6 +69053,8 @@ SLUS-20893:
name: "Way of the Samurai 2"
region: "NTSC-U"
compat: 5
gsHWFixes:
roundSprite: 2 # Fixes UI lines.
SLUS-20894:
name: "Worms 3D"
region: "NTSC-U"
@@ -70950,6 +70990,8 @@ SLUS-21207:
halfPixelOffset: 2 # Sharpens world in far distances, aligns some bloom better.
roundSprite: 1 # Fixes font artifacts.
nativeScaling: 2 # Fixes background DOF blur in battles.
cpuSpriteRenderBW: 1 # Fixes water and other sprite errors.
cpuSpriteRenderLevel: 2 # Needed for above.
SLUS-21208:
name: "Tony Hawk's American Wasteland"
region: "NTSC-U"
@@ -71822,6 +71864,8 @@ SLUS-21327:
name: "Atelier Iris 2 - The Azoth of Destiny"
region: "NTSC-U"
compat: 5
gameFixes:
- SoftwareRendererFMVHack # Flickering in FMV.
gsHWFixes:
roundSprite: 1 # Fixes font artifacts for health and other sprites.
texturePreloading: 0 # Improves performance and prevents it disabling itself regardless.
@@ -76127,6 +76171,8 @@ SLUS-29157:
halfPixelOffset: 2 # Sharpens world in far distances, aligns some bloom better.
roundSprite: 1 # Fixes font artifacts.
nativeScaling: 2 # Fixes background DOF blur in battles.
cpuSpriteRenderBW: 1 # Fixes water and other sprite errors.
cpuSpriteRenderLevel: 2 # Needed for above.
SLUS-29159:
name: "One Piece - Grand Battle [Demo]"
region: "NTSC-U"
+14 -9
View File
@@ -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,leftshoulder:b6,lefttrigger:b7,back:b8,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,
@@ -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,7 +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,x:b3,y:b4,back:b10,guide:b12,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a2,righttrigger:a3,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,
@@ -1797,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,
+186 -49
View File
@@ -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)
{
@@ -407,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
@@ -602,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
@@ -612,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
@@ -621,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
@@ -1027,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));
}
}
@@ -1103,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)
{
@@ -1236,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
@@ -1267,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
@@ -1284,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
@@ -1308,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
@@ -1329,9 +1436,8 @@ PS_OUTPUT ps_main(PS_INPUT input)
#endif
#endif
if (bad)
discard;
if (bad)
discard;
#endif
#if PS_DATE == 3
@@ -1342,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
@@ -1427,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
@@ -1461,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
+28 -27
View File
@@ -59,7 +59,7 @@
#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
}
@@ -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;
+14 -2
View File
@@ -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;
+108 -29
View File
@@ -522,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
@@ -574,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)
@@ -586,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)
@@ -625,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
@@ -639,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
@@ -1604,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
@@ -1678,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;
@@ -1687,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
@@ -1726,7 +1786,7 @@ void main()
#endif
if (bad) {
discard;
DISCARD;
}
#endif // PS_DATE >= 5
@@ -1737,7 +1797,7 @@ void main()
// the bad alpha value so we must keep it.
if (gl_PrimitiveID > stencil_ceil) {
discard;
DISCARD;
}
#endif
@@ -1758,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
@@ -1852,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;
@@ -1870,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();
@@ -1879,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
@@ -1891,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
}
+2 -2
View File
@@ -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)
+3
View File
@@ -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)
-2
View File
@@ -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)
{
+1 -7
View File
@@ -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
+6 -6
View File
@@ -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;
}
}
+10
View File
@@ -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>
+3 -5
View File
@@ -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>
+17 -10
View File
@@ -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()));
+7 -5
View File
@@ -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);
-2
View File
@@ -454,8 +454,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);
+31 -10
View File
@@ -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
@@ -1131,16 +1134,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 +1669,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 +2360,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 +2428,8 @@ int main(int argc, char* argv[])
PCSX2MainApplication app(argc, argv);
QtHost::InitializeClipboard();
#ifndef _WIN32
if (!PerformEarlyHardwareChecks())
return EXIT_FAILURE;
-4
View File
@@ -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();
+6
View File
@@ -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));
+1 -1
View File
@@ -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;
}
+28 -2
View File
@@ -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);
}
@@ -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">
@@ -353,19 +372,21 @@
<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>
<tabstop>useDebugDevice</tabstop>
<tabstop>useDebugBlend</tabstop>
<tabstop>disableFramebufferFetch</tabstop>
<tabstop>disableShaderCache</tabstop>
<tabstop>disableVertexShaderExpand</tabstop>
<tabstop>useDebugBlend</tabstop>
</tabstops>
<resources/>
<connections/>
@@ -223,6 +223,7 @@ 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);
@@ -239,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());
@@ -757,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."));
@@ -1002,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);
@@ -1044,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;
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -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"
+4
View File
@@ -399,6 +399,7 @@ enum class SavestateCompressionLevel : u8
enum class GSHardwareDownloadMode : u8
{
Enabled,
EnabledForceFull,
NoReadbacks,
Unsynchronized,
Disabled
@@ -793,6 +794,9 @@ struct Pcsx2Config
HWMipmap : 1,
HWAccurateAlphaTest : 1,
HWAA1 : 1,
HWROV : 1,
HWROVLogging : 1,
HWROVBarriersVK : 1,
ManualUserHacks : 1,
UserHacks_AlignSpriteX : 1,
UserHacks_CPUFBConversion : 1,
+30 -10
View File
@@ -701,16 +701,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)));
}
}
}
+5
View File
@@ -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
+3
View File
@@ -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.
+2 -2
View File
@@ -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,
+65 -98
View File
@@ -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;
}
}
@@ -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;
@@ -502,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);
}
}
@@ -516,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;
}
@@ -538,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;
@@ -561,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])
@@ -596,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);
@@ -654,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;
}
}
@@ -679,7 +688,6 @@ bool GSState::CanBufferNewDraw()
return false;
PushBuffer();
//DevCon.Warning("Buffering new draw! now buffering %d", m_used_buffers_idx);
return true;
}
@@ -2607,7 +2615,6 @@ void GSState::FlushPrim()
m_quad_check_valid_shuffle = false;
m_drawlist.clear();
m_drawlist_bbox.clear();
m_drawlist_bbox_tex.clear();
if (GSConfig.ShouldDump(s_n, g_perfmon.GetFrame()))
{
@@ -4226,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();
@@ -4274,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;
@@ -4286,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.
@@ -4683,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.
@@ -4696,92 +4722,33 @@ 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;
}
}
template <u32 primclass, bool fst>
void GSState::GetPrimitiveOverlapDrawlistTextureBBoxImpl(float bbox_scale)
{
pxAssert(m_drawlist_bbox_tex.empty()); // Should only call this once per draw.
constexpr int n = GSUtil::GetClassVertexCount(primclass);
const GSVertex* RESTRICT v = m_vertex->buff;
const u16* RESTRICT index = m_index->buff;
const u32 count = m_index->tail;
for (u32 i = 0; i < count; i += n)
{
const float q = primclass == GS_SPRITE_CLASS ? v[index[i + 1]].RGBAQ.Q : v[index[i]].RGBAQ.Q;
GSVector4 bbox = GetTexCoordsImpl<fst>(v[index[i]], q);
for (u32 j = 1; j < n; j++)
{
const GSVector4 tex = GetTexCoordsImpl<fst>(v[index[i + j]]);
bbox = bbox.min(tex).xyzw(bbox.max(tex));
}
bbox = bbox * bbox_scale; // Account for upscaling.
bbox = bbox.floor().xyzw(bbox.ceil()); // Round.
bbox += GSVector4(-1.0f, -1.0f, 1.0f, 1.0f); // +1 on all sides for bilinear.
m_drawlist_bbox_tex.push_back(GSVector4i(bbox));
}
}
void GSState::GetPrimitiveOverlapDrawlistTextureBBox(float bbox_scale)
{
pxAssert(PRIM->TME);
switch (m_vt.m_primclass)
{
case GS_POINT_CLASS:
if (PRIM->FST)
GetPrimitiveOverlapDrawlistTextureBBoxImpl<GS_POINT_CLASS, true>(bbox_scale);
else
GetPrimitiveOverlapDrawlistTextureBBoxImpl<GS_POINT_CLASS, false>(bbox_scale);
break;
case GS_LINE_CLASS:
if (PRIM->FST)
GetPrimitiveOverlapDrawlistTextureBBoxImpl<GS_LINE_CLASS, true>(bbox_scale);
else
GetPrimitiveOverlapDrawlistTextureBBoxImpl<GS_LINE_CLASS, false>(bbox_scale);
break;
case GS_TRIANGLE_CLASS:
if (PRIM->FST)
GetPrimitiveOverlapDrawlistTextureBBoxImpl<GS_TRIANGLE_CLASS, true>(bbox_scale);
else
GetPrimitiveOverlapDrawlistTextureBBoxImpl<GS_TRIANGLE_CLASS, false>(bbox_scale);
break;
case GS_SPRITE_CLASS:
if (PRIM->FST)
GetPrimitiveOverlapDrawlistTextureBBoxImpl<GS_SPRITE_CLASS, true>(bbox_scale);
else
GetPrimitiveOverlapDrawlistTextureBBoxImpl<GS_SPRITE_CLASS, false>(bbox_scale);
break;
default:
pxAssert(false);
return;
}
}
GSState::PRIM_OVERLAP GSState::PrimitiveOverlap(bool save_drawlist)
{
// Assume that 1 triangle, 1 sprite, 1 line, or 3 points can't overlap.
@@ -6138,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;
+4 -6
View File
@@ -392,7 +392,6 @@ public:
PRIM_OVERLAP m_prim_overlap = PRIM_OVERLAP_UNKNOW;
std::vector<size_t> m_drawlist;
std::vector<GSVector4i> m_drawlist_bbox;
std::vector<GSVector4i> m_drawlist_bbox_tex;
struct GSPCRTCRegs
{
@@ -533,12 +532,11 @@ 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);
template <u32 primclass, bool fst>
void GetPrimitiveOverlapDrawlistTextureBBoxImpl(float bbox_scale = 1.0f);
void GetPrimitiveOverlapDrawlistTextureBBox(float bbox_scale = 1.0f);
bool SpriteDrawWithoutGaps();
void CalculatePrimitiveCoversWithoutGaps();
GIFRegTEX0 GetTex0Layer(u32 lod);
+39 -4
View File
@@ -315,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)
@@ -693,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();
@@ -776,6 +782,18 @@ GSTexture* GSDevice::CreateDepthStencil(const GSVector2i& size, bool clear, bool
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 */)
{
pxAssert(mipmap_levels != 0 && (mipmap_levels < 0 || mipmap_levels <= GetMipmapLevelsForSize(w, h)));
@@ -1486,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";
@@ -1509,6 +1527,17 @@ static const char* GetTexHazardName(u32 tex_hazard)
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);
@@ -1564,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)
@@ -1690,6 +1723,8 @@ 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));
+77 -15
View File
@@ -666,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
{
@@ -786,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) {}
@@ -800,6 +805,21 @@ 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;
@@ -816,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()
{
@@ -844,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
{
@@ -1171,18 +1226,17 @@ 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
const std::vector<size_t>* drawlist; ///< For reducing barriers on sprites
const std::vector<GSVector4i>* drawlist_bbox; ///< For RT copy when barriers not available.
const std::vector<GSVector4i>* drawlist_bbox_tex; ///< Additionally if we need to sample not from the same pixel of the RT.
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.
@@ -1255,6 +1309,11 @@ struct alignas(16) GSHWDrawConfig
{
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,
@@ -1330,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));
@@ -1464,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.
@@ -1563,9 +1623,11 @@ public:
GSTexture* CreateRenderTarget(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);
@@ -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
+14
View File
@@ -73,6 +73,13 @@ protected:
bool m_needs_mipmaps_generated = true;
ClearValue m_clear_value = {};
// For GL/DX11 since they don't track layouts.
// Used heuristically to decide whether to use ROV for a draw.
bool m_unordered_access = false;
#ifdef PCSX2_DEVBUILD
std::string m_debug_name;
#endif
public:
GSTexture();
virtual ~GSTexture();
@@ -87,6 +94,7 @@ public:
#ifdef PCSX2_DEVBUILD
virtual void SetDebugName(std::string_view name) = 0;
const std::string& GetDebugName() { return m_debug_name; }
#endif
bool Save(const std::string& fn);
@@ -173,6 +181,12 @@ public:
// Typical size of a RGBA texture
u32 GetMemUsage() const { return m_size.x * m_size.y * (m_format == Format::UNorm8 ? 1 : 4); }
// The unordered access flag is sticky, so once it's set we keep using ROV for the target
// until it's recycled/destroyed. Only used for DX11/GL, which don't track actual resource layout/state.
virtual bool IsUnorderedAccess() const { return m_unordered_access; }
void SetUnorderedAccess() { m_unordered_access = true; }
void ClearUnorderedAccess() { m_unordered_access = false; }
// Helper routines for formats/types
static bool IsCompressedFormat(Format format) { return (format >= Format::BC1 && format <= Format::BC7); }
};
+1 -1
View File
@@ -20,7 +20,7 @@
#include <array>
#include <d3d11.h>
#include <d3d12.h>
#include <directx/d3d12.h>
#include <d3dcompiler.h>
#include <fstream>
@@ -65,6 +65,7 @@ bool D3D11ShaderCache::Open(D3D_FEATURE_LEVEL feature_level, bool debug)
m_shader_model = D3D::ShaderModel::SM41;
break;
case D3D_FEATURE_LEVEL_11_0:
case D3D_FEATURE_LEVEL_11_1:
m_shader_model = D3D::ShaderModel::SM50;
break;
default:
+136 -31
View File
@@ -109,7 +109,8 @@ bool GSDevice11::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
wil::com_ptr_nothrow<IDXGIAdapter1> dxgi_adapter = D3D::GetAdapterByName(m_dxgi_factory.get(), GSConfig.Adapter);
static constexpr std::array<D3D_FEATURE_LEVEL, 2> requested_feature_levels = {{
static constexpr std::array<D3D_FEATURE_LEVEL, 3> requested_feature_levels = {{
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_0,
}};
@@ -592,11 +593,18 @@ bool GSDevice11::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
Host::OSD_WARNING_DURATION);
}
// 1x1 dummy texture.
m_null_texture = CreateSurface(GSTexture::Type::RenderTarget, 1, 1, 1, GSTexture::Format::Color);
if (!m_null_texture)
return false;
return true;
}
void GSDevice11::Destroy()
{
delete m_null_texture;
GSDevice::Destroy();
DestroySwapChain();
DestroyTimestampQueries();
@@ -641,6 +649,20 @@ void GSDevice11::Destroy()
}
m_state.current_ds = nullptr;
if (m_state.rt_uav)
{
m_state.rt_uav->Release();
m_state.rt_uav = nullptr;
}
m_state.current_rt_uav = nullptr;
if (m_state.ds_uav)
{
m_state.ds_uav->Release();
m_state.ds_uav = nullptr;
}
m_state.current_ds_uav = nullptr;
m_shader_cache.Close();
#ifdef REPORT_LEAKED_OBJECTS
@@ -683,6 +705,12 @@ void GSDevice11::SetFeatures(IDXGIAdapter1* adapter)
m_conservative_depth = (m_feature_level >= D3D_FEATURE_LEVEL_11_0);
m_rgba16_unorm_hw_blend = IsTextureFormatHWBlendable(m_dev.get(), DXGI_FORMAT_R16G16B16A16_UNORM);
m_uav_texture = (m_feature_level >= D3D_FEATURE_LEVEL_11_0);
D3D11_FEATURE_DATA_D3D11_OPTIONS2 options2{};
m_dev->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS2, &options2, sizeof(options2));
m_features.rov = m_uav_texture && options2.ROVsSupported;
// Let the user know if said features are available.
Console.WriteLnFmt("D3D11: DXTn Texture Compression: {}", m_features.dxt_textures ? "Supported" : "Not Supported");
Console.WriteLnFmt("D3D11: BC6/7 Texture Compression: {}", m_features.bptc_textures ? "Supported" : "Not Supported");
@@ -1322,6 +1350,8 @@ GSTexture* GSDevice11::CreateSurface(GSTexture::Type type, int width, int height
{
case GSTexture::Type::RenderTarget:
desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
if (m_uav_texture)
desc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS;
break;
case GSTexture::Type::DepthStencil:
desc.BindFlags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE;
@@ -1331,6 +1361,7 @@ GSTexture* GSDevice11::CreateSurface(GSTexture::Type type, int width, int height
desc.MiscFlags = (levels > 1 && !GSTexture::IsCompressedFormat(format)) ? D3D11_RESOURCE_MISC_GENERATE_MIPS : 0;
break;
case GSTexture::Type::RWTexture:
pxAssert(m_uav_texture);
desc.BindFlags = D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE;
break;
default:
@@ -1482,7 +1513,7 @@ void GSDevice11::DoStretchRect(GSTexture* sTex, const GSVector4& sRect, GSTextur
// ps
PSSetShaderResource(0, sTex);
PSSetShaderResource(TEXTURE_TEXTURE, sTex);
PSSetSamplerState(filter == Biln ? m_convert.ln.get() : m_convert.pt.get());
PSSetShader(ps, ps_cb);
@@ -1547,7 +1578,7 @@ void GSDevice11::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
// ps
PSSetShaderResource(0, sTex);
PSSetShaderResource(TEXTURE_TEXTURE, sTex);
PSSetSamplerState(filter == Biln ? m_convert.ln.get() : m_convert.pt.get());
PSSetShader(m_present.ps[static_cast<u32>(shader)].get(), m_present.ps_cb.get());
@@ -1698,7 +1729,7 @@ void GSDevice11::DoMultiStretchRects(const MultiStretchRect* rects, u32 num_rect
IASetIndexBuffer(m_ib.get());
CommitClear(rects[0].src);
PSSetShaderResource(0, rects[0].src);
PSSetShaderResource(TEXTURE_TEXTURE, rects[0].src);
PSSetSamplerState(rects[0].filter == Biln ? m_convert.ln.get() : m_convert.pt.get());
OMSetBlendState(m_convert.bs[rects[0].wmask.wrgba].get(), 0.0f);
@@ -1921,6 +1952,8 @@ void GSDevice11::SetupPS(const PSSelector& sel, const GSHWDrawConfig::PSConstant
sm.AddMacro("PS_AA1", static_cast<u32>(sel.aa1));
sm.AddMacro("PS_ABE", sel.abe);
sm.AddMacro("PS_ANISOTROPIC_FILTERING", sel.sw_aniso);
sm.AddMacro("PS_ROV_COLOR", sel.rov_color);
sm.AddMacro("PS_ROV_DEPTH", static_cast<u32>(sel.rov_depth));
wil::com_ptr_nothrow<ID3D11PixelShader> ps = m_shader_cache.GetPixelShader(m_dev.get(), m_tfx_source, sm.GetPtr(), "ps_main");
i = m_ps.try_emplace(sel, std::move(ps)).first;
@@ -2339,7 +2372,7 @@ void GSDevice11::SetupDATE(GSTexture* rt, GSTexture* ds, SetDATM datm, const GSV
// ps
PSSetShaderResource(0, rt);
PSSetShaderResource(TEXTURE_TEXTURE, rt);
PSSetSamplerState(m_convert.pt.get());
PSSetShader(GetConvertShader(SetDATMShader(datm)), nullptr);
@@ -2707,10 +2740,16 @@ void GSDevice11::OMSetBlendState(ID3D11BlendState* bs, u8 bf)
}
}
void GSDevice11::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i* scissor, ID3D11DepthStencilView* read_only_dsv)
void GSDevice11::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, GSTexture* rt_uav_tex, GSTexture* ds_uav_tex,
const GSVector4i* scissor, ID3D11DepthStencilView* read_only_dsv)
{
if (!(rt || rt_uav_tex) && ds_uav_tex)
rt_uav_tex = m_null_texture; // Fill in the first UAV slot with the null texture.
ID3D11RenderTargetView* rtv = nullptr;
ID3D11DepthStencilView* dsv = nullptr;
ID3D11UnorderedAccessView* rt_uav = nullptr;
ID3D11UnorderedAccessView* ds_uav = nullptr;
if (rt)
{
@@ -2722,9 +2761,22 @@ void GSDevice11::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector
CommitClear(ds);
dsv = read_only_dsv ? read_only_dsv : *static_cast<GSTexture11*>(ds);
}
if (rt_uav_tex)
{
CommitClear(rt_uav_tex);
rt_uav = *static_cast<GSTexture11*>(rt_uav_tex);
}
if (ds_uav_tex)
{
CommitClear(ds_uav_tex);
ds_uav = *static_cast<GSTexture11*>(ds_uav_tex);
}
const bool changed = (m_state.rtv != rtv || m_state.dsv != dsv);
g_perfmon.Put(GSPerfMon::RenderPasses, static_cast<double>(changed));
const bool changed = (m_state.rtv != rtv || m_state.dsv != dsv || m_state.rt_uav != rt_uav || m_state.ds_uav != ds_uav);
const bool no_uavs = m_state.rt_uav == nullptr && rt_uav == nullptr && m_state.ds_uav == nullptr && ds_uav == nullptr;
if (changed)
g_perfmon.Put(GSPerfMon::RenderPasses, 1.0);
if (m_state.rtv != rtv)
{
@@ -2744,15 +2796,54 @@ void GSDevice11::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector
m_state.dsv = dsv;
m_state.current_ds = ds;
}
if (m_state.rt_uav != rt_uav)
{
if (m_state.rt_uav)
m_state.rt_uav->Release();
if (rt_uav)
rt_uav->AddRef();
m_state.rt_uav = rt_uav;
m_state.current_rt_uav = rt_uav_tex;
}
if (m_state.ds_uav != ds_uav)
{
if (m_state.ds_uav)
m_state.ds_uav->Release();
if (ds_uav)
ds_uav->AddRef();
m_state.ds_uav = ds_uav;
m_state.current_ds_uav = ds_uav_tex;
}
PSUnbindConflictingSRVs(m_state.current_rt, read_only_dsv ? nullptr : m_state.current_ds);
PSUnbindConflictingSRVs(m_state.current_rt_uav, m_state.current_ds_uav);
if (changed)
m_ctx->OMSetRenderTargets(1, &rtv, dsv);
if (rt || ds)
{
const GSVector2i size = rt ? rt->GetSize() : ds->GetSize();
// OM targets and UAVs share the same namespace in DX11, so we need to pack them into contiguous slots.
const u32 num_rtvs = rt ? 1 : 0;
ID3D11RenderTargetView* rtvs[] = {rtv};
if (no_uavs)
m_ctx->OMSetRenderTargets(num_rtvs, rtvs, dsv);
else
{
u32 num_uavs = 0;
ID3D11UnorderedAccessView* uavs[2];
if (rt_uav)
uavs[num_uavs++] = rt_uav;
if (ds_uav)
uavs[num_uavs++] = ds_uav;
m_ctx->OMSetRenderTargetsAndUnorderedAccessViews(num_rtvs, rtvs, dsv, num_rtvs, num_uavs, uavs, nullptr);
}
}
if (rt || ds || rt_uav_tex || ds_uav_tex)
{
const GSVector2i size =
rt ? rt->GetSize() :
ds ? ds->GetSize() :
(rt_uav_tex && rt_uav_tex != m_null_texture) ? rt_uav_tex->GetSize() :
ds_uav_tex->GetSize();
SetViewport(size);
SetScissor(scissor ? *scissor : GSVector4i::loadh(size));
}
@@ -2815,6 +2906,10 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
{
const GSVector2i rtsize = (config.rt ? config.rt : config.ds)->GetSize();
GSTexture* colclip_rt = g_gs_device->GetColorClipTexture();
GSTexture* draw_rt = config.ps.HasColorROV() ? nullptr : config.rt;
GSTexture* draw_ds = config.ps.HasDepthROV() ? nullptr : config.ds;
GSTexture* draw_rt_rov = config.ps.HasColorROV() ? config.rt : nullptr;
GSTexture* draw_ds_rov = config.ps.HasDepthROV() ? config.ds : nullptr;
GSTexture* draw_rt_clone = nullptr;
GSTexture* draw_ds_clone = nullptr;
GSTexture* primid_texture = nullptr;
@@ -2865,6 +2960,8 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
const GSVector4 sRect = dRect / GSVector4(rtsize.x, rtsize.y).xyxy();
StretchRect(config.rt, sRect, colclip_rt, dRect, ShaderConvert::COLCLIP_INIT, Nearest);
}
draw_rt = colclip_rt ? colclip_rt : draw_rt;
}
// Destination Alpha Setup
@@ -2950,7 +3047,7 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
const OMBlendSelector blend(GSHWDrawConfig::ColorMaskSelector(1),
GSHWDrawConfig::BlendState(true, CONST_ONE, CONST_ONE, 3 /* MIN */, CONST_ONE, CONST_ZERO, false, 0));
SetupOM(dss, blend, 0);
OMSetRenderTargets(primid_texture, config.ds, &config.scissor, read_only_dsv);
OMSetRenderTargets(primid_texture, config.ds, nullptr, nullptr, &config.scissor, read_only_dsv);
SetRenderHWShaderResources(config, nullptr);
Draw(config);
@@ -2960,24 +3057,28 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
}
// Avoid changing framebuffer just to switch from rt+depth to rt and vice versa.
GSTexture* draw_rt = colclip_rt ? colclip_rt : config.rt;
GSTexture* draw_ds = config.ds;
// Make sure no tex is bound as both rtv and srv at the same time.
// All conflicts should've been taken care of by PSUnbindConflictingSRVs.
if (!draw_rt && draw_ds && m_state.rtv && m_state.current_rt && m_state.rtv == *static_cast<GSTexture11*>(m_state.current_rt) &&
m_state.current_ds == draw_ds && config.tex != m_state.current_rt && m_state.current_rt->GetSize() == draw_ds->GetSize())
if (!(draw_rt || draw_rt_rov) && draw_ds && m_state.current_rt && config.tex != m_state.current_rt && m_state.current_rt->GetSize() == draw_ds->GetSize())
{
draw_rt = m_state.current_rt;
}
else if (!draw_ds && draw_rt && m_state.dsv && m_state.current_ds && m_state.dsv == *static_cast<GSTexture11*>(m_state.current_ds) &&
m_state.current_rt == draw_rt && config.tex != m_state.current_ds && m_state.current_ds->GetSize() == draw_rt->GetSize())
else if (!(draw_ds || draw_ds_rov) && draw_rt && m_state.current_ds && config.tex != m_state.current_ds && m_state.current_ds->GetSize() == draw_rt->GetSize())
{
draw_ds = m_state.current_ds;
}
else if (!(draw_rt || draw_rt_rov) && draw_ds_rov && m_state.current_rt_uav && config.tex != m_state.current_rt_uav)
{
draw_rt_rov = m_state.current_rt_uav;
}
else if (!(draw_ds || draw_ds_rov) && draw_rt_rov && m_state.current_ds_uav && config.tex != m_state.current_ds_uav)
{
draw_ds_rov = m_state.current_ds_uav;
}
const bool rt_feedbackloop_pass1 = config.IsFeedbackLoopRT(config.ps);
const bool rt_feedbackloop_pass2 = config.IsFeedbackLoopRT(config.alpha_second_pass.ps);
if (draw_rt && (((config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy)) &&
if (draw_rt && !draw_rt_rov && (((config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy)) &&
(rt_feedbackloop_pass1 || rt_feedbackloop_pass2))))
{
// Requires a copy of the RT.
@@ -2989,7 +3090,7 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
const bool ds_feedbackloop_pass1 = config.IsFeedbackLoopDepth(config.ps);
const bool ds_feedbackloop_pass2 = config.IsFeedbackLoopDepth(config.alpha_second_pass.ps);
if (draw_ds && m_features.depth_feedback && (config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy)) &&
if (draw_ds && !draw_ds_rov && m_features.depth_feedback && (config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy)) &&
(ds_feedbackloop_pass1 || ds_feedbackloop_pass2))
{
// Requires a copy of the DS.
@@ -2999,7 +3100,7 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
Console.Warning("D3D11: Failed to allocate temp texture for DS copy.");
}
OMSetRenderTargets(draw_rt, draw_ds, &config.scissor, read_only_dsv);
OMSetRenderTargets(draw_rt, draw_ds, draw_rt_rov, draw_ds_rov, &config.scissor, read_only_dsv);
SetRenderHWShaderResources(config, primid_texture);
SetupOM(config.depth, OMBlendSelector(config.colormask, config.blend), config.blend.constant);
@@ -3008,7 +3109,7 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
m_ctx->ClearDepthStencilView(*static_cast<GSTexture11*>(draw_ds), D3D11_CLEAR_STENCIL, 0.0f, 1);
SendHWDraw(config, rt_feedbackloop_pass1 ? draw_rt_clone : nullptr, draw_rt, ds_feedbackloop_pass1 ? draw_ds_clone : nullptr, draw_ds,
config.require_one_barrier, config.require_full_barrier);
config.require_one_barrier, config.require_full_barrier, rtsize);
if (config.blend_multi_pass.enable)
{
@@ -3036,7 +3137,7 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
const bool one_barrier = config.alpha_second_pass.require_one_barrier && m_features.multidraw_fb_copy;
SetupOM(config.alpha_second_pass.depth, OMBlendSelector(config.alpha_second_pass.colormask, config.blend), config.blend.constant);
SendHWDraw(config, rt_feedbackloop_pass2 ? draw_rt_clone : nullptr, draw_rt, ds_feedbackloop_pass2 ? draw_ds_clone : nullptr, draw_ds,
one_barrier, config.alpha_second_pass.require_full_barrier);
one_barrier, config.alpha_second_pass.require_full_barrier, rtsize);
}
if (colclip_rt)
@@ -3110,7 +3211,7 @@ void GSDevice11::FeedbackCopyAndBind(const GSHWDrawConfig& config,
void GSDevice11::SendHWDraw(const GSHWDrawConfig& config,
GSTexture* draw_rt_clone, GSTexture* draw_rt, GSTexture* draw_ds_clone, GSTexture* draw_ds,
const bool one_barrier, const bool full_barrier)
const bool one_barrier, const bool full_barrier, GSVector2i rtsize)
{
#ifdef PCSX2_DEVBUILD
if ((one_barrier || full_barrier) && !(config.IsFeedbackLoopRT(config.ps) || config.IsFeedbackLoopDepth(config.ps))) [[unlikely]]
@@ -3125,15 +3226,16 @@ void GSDevice11::SendHWDraw(const GSHWDrawConfig& config,
pxAssert(config.drawlist && !config.drawlist->empty());
pxAssert(config.drawlist_bbox && static_cast<u32>(config.drawlist_bbox->size()) == draw_list_size);
if (config.tex_hazard != config.TEX_HAZARD_NONE)
FeedbackCopyAndBind(config, draw_rt, draw_rt_clone, draw_ds, draw_ds_clone, config.samplearea);
for (u32 n = 0, p = 0; n < draw_list_size; n++)
{
const u32 count = config.drawlist->at(n) * indices_per_prim;
const GSVector4i bbox = config.drawlist_bbox->at(n).rintersect(config.drawarea);
const GSVector4i bbox_tex = config.drawlist_bbox_tex ?
config.drawlist_bbox_tex->at(n).rintersect(config.samplearea) : GSVector4i::zero();
FeedbackCopyAndBind(config, draw_rt, draw_rt_clone, draw_ds, draw_ds_clone, bbox, bbox_tex);
FeedbackCopyAndBind(config, draw_rt, draw_rt_clone, draw_ds, draw_ds_clone, bbox);
Draw(config, p, count);
@@ -3149,6 +3251,9 @@ void GSDevice11::SendHWDraw(const GSHWDrawConfig& config,
}
Draw(config);
if (config.ps.HasColorROV() || config.ps.HasDepthROV())
g_perfmon.Put(GSPerfMon::DrawCallsROV, 1);
}
void GSDevice11::SetRenderHWShaderResources(const GSHWDrawConfig& config, GSTexture* primid_texture)
@@ -3156,13 +3261,13 @@ void GSDevice11::SetRenderHWShaderResources(const GSHWDrawConfig& config, GSText
if (config.tex && config.tex_hazard == GSHWDrawConfig::TEX_HAZARD_NONE)
{
CommitClear(config.tex);
PSSetShaderResource(0, config.tex);
PSSetShaderResource(TEXTURE_TEXTURE, config.tex);
}
if (config.pal)
{
CommitClear(config.pal);
PSSetShaderResource(1, config.pal);
PSSetShaderResource(TEXTURE_PALETTE, config.pal);
}
if (primid_texture)
PSSetShaderResource(3, primid_texture);
PSSetShaderResource(TEXTURE_PRIMID, primid_texture);
}
+15 -2
View File
@@ -83,6 +83,11 @@ public:
private:
enum : u32
{
TEXTURE_TEXTURE = 0,
TEXTURE_PALETTE = 1,
TEXTURE_RT = 2,
TEXTURE_PRIMID = 3,
TEXTURE_DEPTH = 4,
MAX_TEXTURES = 5,
MAX_SAMPLERS = 1,
VERTEX_BUFFER_SIZE = 32 * 1024 * 1024,
@@ -129,6 +134,8 @@ private:
wil::com_ptr_nothrow<ID3D11ShaderResourceView> m_expand_vb_srv;
wil::com_ptr_nothrow<ID3D11ShaderResourceView> m_expand_ib_vs_srv;
GSTexture* m_null_texture;
D3D_FEATURE_LEVEL m_feature_level = D3D_FEATURE_LEVEL_10_0;
u32 m_vb_pos = 0; // bytes
u32 m_ib_pos = 0; // indices/sizeof(u16)
@@ -141,6 +148,7 @@ private:
bool m_is_exclusive_fullscreen = false;
bool m_conservative_depth = false;
bool m_rgba16_unorm_hw_blend = false;
bool m_uav_texture = false;
struct
{
@@ -166,8 +174,12 @@ private:
u8 bf;
ID3D11RenderTargetView* rtv;
ID3D11DepthStencilView* dsv;
ID3D11UnorderedAccessView* rt_uav;
ID3D11UnorderedAccessView* ds_uav;
GSTexture* current_rt;
GSTexture* current_ds;
GSTexture* current_rt_uav;
GSTexture* current_ds_uav;
} m_state;
std::array<std::array<wil::com_ptr_nothrow<ID3D11Query>, 3>, NUM_TIMESTAMP_QUERIES> m_timestamp_queries = {};
@@ -371,7 +383,8 @@ public:
void OMSetDepthStencilState(ID3D11DepthStencilState* dss, u8 sref);
void OMSetBlendState(ID3D11BlendState* bs, u8 bf);
void OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i* scissor = nullptr, ID3D11DepthStencilView* read_only_dsv = nullptr);
void OMSetRenderTargets(GSTexture* rt, GSTexture* ds, GSTexture* rt_uav = nullptr, GSTexture* ds_uav = nullptr,
const GSVector4i* scissor = nullptr, ID3D11DepthStencilView* read_only_dsv = nullptr);
void SetViewport(const GSVector2i& viewport);
void SetScissor(const GSVector4i& scissor);
@@ -390,7 +403,7 @@ public:
const GSVector4i& copyarea, const GSVector4i& samplearea);
void SendHWDraw(const GSHWDrawConfig& config,
GSTexture* draw_rt_clone, GSTexture* draw_rt, GSTexture* draw_ds_clone, GSTexture* draw_ds,
const bool one_barrier, const bool full_barrier);
const bool one_barrier, const bool full_barrier, GSVector2i rtsize);
void SetRenderHWShaderResources(const GSHWDrawConfig& config, GSTexture* primid_texture);
void ClearSamplerCache() override;
+2
View File
@@ -102,6 +102,8 @@ void GSTexture11::SetDebugName(std::string_view name)
GSDevice11::SetD3DDebugObjectName(m_srv.get(), fmt::format("{} SRV", name));
if (m_rtv)
GSDevice11::SetD3DDebugObjectName(m_rtv.get(), fmt::format("{} RTV", name));
m_debug_name = name;
}
#endif
-1
View File
@@ -19,7 +19,6 @@ class GSTexture11 final : public GSTexture
wil::com_ptr_nothrow<ID3D11UnorderedAccessView> m_uav;
wil::com_ptr_nothrow<ID3D11DepthStencilView> m_read_only_dsv;
D3D11_TEXTURE2D_DESC m_desc;
public:
explicit GSTexture11(wil::com_ptr_nothrow<ID3D11Texture2D> texture, const D3D11_TEXTURE2D_DESC& desc,
GSTexture::Type type, GSTexture::Format format);
+25
View File
@@ -354,6 +354,31 @@ u32 D3D12::RootSignatureBuilder::AddDescriptorTable(
return index;
}
// Allows using ranges of non-contiguous shader registers in a single descriptor table.
u32 D3D12::RootSignatureBuilder::AddDescriptorTableMultiRange(u32 num_ranges,
D3D12_DESCRIPTOR_RANGE_TYPE* rt, u32* start_shader_reg, u32* num_shader_regs, D3D12_SHADER_VISIBILITY visibility)
{
const u32 index = m_desc.NumParameters++;
const u32 dr_index = m_num_descriptor_ranges;
m_num_descriptor_ranges += num_ranges;
for (u32 i = 0; i < num_ranges; i++)
{
m_descriptor_ranges[dr_index + i].RangeType = rt[i];
m_descriptor_ranges[dr_index + i].NumDescriptors = num_shader_regs[i];
m_descriptor_ranges[dr_index + i].BaseShaderRegister = start_shader_reg[i];
m_descriptor_ranges[dr_index + i].RegisterSpace = 0;
m_descriptor_ranges[dr_index + i].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
}
m_params[index].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
m_params[index].DescriptorTable.pDescriptorRanges = &m_descriptor_ranges[dr_index];
m_params[index].DescriptorTable.NumDescriptorRanges = num_ranges;
m_params[index].ShaderVisibility = visibility;
return index;
}
#ifdef PCSX2_DEVBUILD
#include "common/StringUtil.h"
+3 -1
View File
@@ -8,7 +8,7 @@
#include "common/RedtapeWilCom.h"
#include <array>
#include <d3d12.h>
#include <directx/d3d12.h>
#include <string_view>
class D3D12ShaderCache;
@@ -37,6 +37,8 @@ namespace D3D12
u32 AddSRVParameter(u32 shader_reg, D3D12_SHADER_VISIBILITY visibility);
u32 AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE rt, u32 start_shader_reg, u32 num_shader_regs,
D3D12_SHADER_VISIBILITY visibility);
u32 AddDescriptorTableMultiRange(u32 num_ranges, D3D12_DESCRIPTOR_RANGE_TYPE* rt, u32* start_shader_reg,
u32* num_shader_regs, D3D12_SHADER_VISIBILITY visibility);
private:
D3D12_ROOT_SIGNATURE_DESC m_desc{};
@@ -10,7 +10,7 @@
#include <bitset>
#include <cstring>
#include <d3d12.h>
#include <directx/d3d12.h>
#include <unordered_map>
#include <vector>
+1 -1
View File
@@ -10,7 +10,7 @@
#include "common/RedtapeWilCom.h"
#include <cstdio>
#include <d3d12.h>
#include <directx/d3d12.h>
#include <string_view>
#include <unordered_map>
#include <vector>
+1 -1
View File
@@ -8,7 +8,7 @@
#include "common/RedtapeWilCom.h"
#include "common/Assertions.h"
#include <d3d12.h>
#include <directx/d3d12.h>
#include <deque>
#include <utility>
+202 -85
View File
@@ -397,17 +397,6 @@ bool GSDevice12::CreateDescriptorHeaps()
return false;
}
// Allocate null SRV descriptor for unbound textures.
constexpr D3D12_SHADER_RESOURCE_VIEW_DESC null_srv_desc = {
DXGI_FORMAT_R8G8B8A8_UNORM, D3D12_SRV_DIMENSION_TEXTURE2D, D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING};
if (!m_descriptor_heap_manager.Allocate(&m_null_srv_descriptor))
{
pxFailRel("Failed to allocate null descriptor");
return false;
}
m_device->CreateShaderResourceView(nullptr, &null_srv_desc, m_null_srv_descriptor.cpu_handle);
return true;
}
@@ -1449,6 +1438,11 @@ bool GSDevice12::CheckFeatures(const u32& vendor_id)
m_enhanced_barriers = false;
}
D3D12_FEATURE_DATA_D3D12_OPTIONS options{};
m_device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS));
m_features.rov = options.ROVsSupported;
Console.WriteLnFmt("D3D12: Tight Alignment: {}", m_allocator->IsTightAlignmentSupported() ? "Supported" : "Not Supported");
return true;
}
@@ -1505,30 +1499,30 @@ void GSDevice12::Draw(const GSHWDrawConfig& config)
}
void GSDevice12::LookupNativeFormat(GSTexture::Format format, DXGI_FORMAT* d3d_format, DXGI_FORMAT* srv_format,
DXGI_FORMAT* rtv_format, DXGI_FORMAT* dsv_format) const
DXGI_FORMAT* rtv_format, DXGI_FORMAT* dsv_format, DXGI_FORMAT* uav_format) const
{
static constexpr std::array<std::array<DXGI_FORMAT, 4>, static_cast<int>(GSTexture::Format::Last) + 1>
static constexpr std::array<std::array<DXGI_FORMAT, 5>, static_cast<int>(GSTexture::Format::Last) + 1>
s_format_mapping = {{
{DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // Invalid
{DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM,
DXGI_FORMAT_UNKNOWN}, // Color
DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R8G8B8A8_UNORM}, // Color
{DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_FORMAT_R10G10B10A2_UNORM,
DXGI_FORMAT_UNKNOWN}, // ColorHQ
DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R10G10B10A2_UNORM}, // ColorHQ
{DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT,
DXGI_FORMAT_UNKNOWN}, // ColorHDR
DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R16G16B16A16_FLOAT}, // ColorHDR
{DXGI_FORMAT_R16G16B16A16_UNORM, DXGI_FORMAT_R16G16B16A16_UNORM, DXGI_FORMAT_R16G16B16A16_UNORM,
DXGI_FORMAT_UNKNOWN}, // ColorClip
DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R16G16B16A16_UNORM}, // ColorClip
{DXGI_FORMAT_D32_FLOAT_S8X24_UINT, DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS, DXGI_FORMAT_UNKNOWN,
DXGI_FORMAT_D32_FLOAT_S8X24_UINT}, // DepthStencil
{DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_UNKNOWN}, // DepthColor
{DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_UNKNOWN}, // UNorm8
{DXGI_FORMAT_R16_UINT, DXGI_FORMAT_R16_UINT, DXGI_FORMAT_R16_UINT, DXGI_FORMAT_UNKNOWN}, // UInt16
{DXGI_FORMAT_R32_UINT, DXGI_FORMAT_R32_UINT, DXGI_FORMAT_R32_UINT, DXGI_FORMAT_UNKNOWN}, // UInt32
{DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_UNKNOWN}, // Int32
{DXGI_FORMAT_BC1_UNORM, DXGI_FORMAT_BC1_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // BC1
{DXGI_FORMAT_BC2_UNORM, DXGI_FORMAT_BC2_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // BC2
{DXGI_FORMAT_BC3_UNORM, DXGI_FORMAT_BC3_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // BC3
{DXGI_FORMAT_BC7_UNORM, DXGI_FORMAT_BC7_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // BC7
DXGI_FORMAT_D32_FLOAT_S8X24_UINT, DXGI_FORMAT_R32_FLOAT}, // DepthStencil
{DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R32_FLOAT}, // DepthColor
{DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_A8_UNORM}, // UNorm8
{DXGI_FORMAT_R16_UINT, DXGI_FORMAT_R16_UINT, DXGI_FORMAT_R16_UINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R16_UINT}, // UInt16
{DXGI_FORMAT_R32_UINT, DXGI_FORMAT_R32_UINT, DXGI_FORMAT_R32_UINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R32_UINT}, // UInt32
{DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R32_FLOAT}, // Int32
{DXGI_FORMAT_BC1_UNORM, DXGI_FORMAT_BC1_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // BC1
{DXGI_FORMAT_BC2_UNORM, DXGI_FORMAT_BC2_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // BC2
{DXGI_FORMAT_BC3_UNORM, DXGI_FORMAT_BC3_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // BC3
{DXGI_FORMAT_BC7_UNORM, DXGI_FORMAT_BC7_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // BC7
}};
const auto& mapping = s_format_mapping[static_cast<int>(format)];
@@ -1540,14 +1534,17 @@ void GSDevice12::LookupNativeFormat(GSTexture::Format format, DXGI_FORMAT* d3d_f
*rtv_format = mapping[2];
if (dsv_format)
*dsv_format = mapping[3];
if (uav_format)
*uav_format = mapping[4];
}
GSTexture* GSDevice12::CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format)
{
DXGI_FORMAT dxgi_format, srv_format, rtv_format, dsv_format;
LookupNativeFormat(format, &dxgi_format, &srv_format, &rtv_format, &dsv_format);
DXGI_FORMAT dxgi_format, srv_format, rtv_format, dsv_format, uav_format;
LookupNativeFormat(format, &dxgi_format, &srv_format, &rtv_format, &dsv_format, &uav_format);
const DXGI_FORMAT uav_format = (type == GSTexture::Type::RWTexture) ? dxgi_format : DXGI_FORMAT_UNKNOWN;
if (type != GSTexture::Type::RWTexture && type != GSTexture::Type::RenderTarget)
uav_format = DXGI_FORMAT_UNKNOWN; // We don't need the UAV descriptor.
std::unique_ptr<GSTexture12> tex(GSTexture12::Create(type, format, width, height, levels,
dxgi_format, srv_format, rtv_format, dsv_format, uav_format));
@@ -2446,24 +2443,23 @@ void GSDevice12::VSSetIndexBuffer(const void* index, size_t count)
}
void GSDevice12::OMSetRenderTargets(GSTexture* rt, GSTexture* ds_as_rt, GSTexture* ds, const GSVector4i& scissor,
bool depth_read)
bool depth_read, const GSVector2i& viewport_size)
{
GSTexture12* d12Rt = static_cast<GSTexture12*>(rt);
GSTexture12* d12DsRt = static_cast<GSTexture12*>(ds_as_rt);
GSTexture12* d12Ds = static_cast<GSTexture12*>(ds);
pxAssert(d12Rt || d12DsRt || d12Ds);
if (m_current_render_target != d12Rt || m_current_depth_render_target != d12DsRt || m_current_depth_target != d12Ds ||
m_current_depth_read_only != depth_read)
// Check if framebuffer changed
if (m_current_render_target != d12Rt || m_current_depth_render_target != d12DsRt ||
m_current_depth_target != d12Ds || m_current_depth_read_only != depth_read)
{
// framebuffer change
EndRenderPass();
}
else if (InRenderPass())
{
// Framebuffer unchanged, but check for clears. Have to restart render pass, unlike Vulkan.
// We'll take care of issuing the actual clear there, because we have to start one anyway.
for (GSTexture12* tex : std::array{d12Rt, d12DsRt, d12Ds})
for (GSTexture12* tex : std::array{ d12Rt, d12DsRt, d12Ds })
{
if (tex && tex->GetState() != GSTexture::State::Dirty)
{
@@ -2491,7 +2487,8 @@ void GSDevice12::OMSetRenderTargets(GSTexture* rt, GSTexture* ds_as_rt, GSTextur
}
// This is used to set/initialize the framebuffer for tfx rendering.
const GSVector2i size = d12Rt ? d12Rt->GetSize() : (d12DsRt ? d12DsRt->GetSize() : d12Ds->GetSize());
const GSVector2i size = d12Rt ? d12Rt->GetSize() :
(d12DsRt ? d12DsRt->GetSize() : (d12Ds ? d12Ds->GetSize() : viewport_size));
const D3D12_VIEWPORT vp{0.0f, 0.0f, static_cast<float>(size.x), static_cast<float>(size.y), 0.0f, 1.0f};
SetViewport(vp);
@@ -2608,8 +2605,8 @@ GSDevice12::ComPtr<ID3DBlob> GSDevice12::GetUtilityPixelShader(const std::string
bool GSDevice12::CreateNullTexture()
{
m_null_texture =
GSTexture12::Create(GSTexture::Type::Texture, GSTexture::Format::Color, 1, 1, 1, DXGI_FORMAT_R8G8B8A8_UNORM,
DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN);
GSTexture12::Create(GSTexture::Type::RenderTarget, GSTexture::Format::Color, 1, 1, 1, DXGI_FORMAT_R8G8B8A8_UNORM,
DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R8G8B8A8_UNORM);
if (!m_null_texture)
return false;
@@ -2689,9 +2686,13 @@ bool GSDevice12::CreateRootSignatures()
rsb.AddCBVParameter(1, D3D12_SHADER_VISIBILITY_PIXEL);
rsb.AddSRVParameter(0, D3D12_SHADER_VISIBILITY_VERTEX);
rsb.AddSRVParameter(5, D3D12_SHADER_VISIBILITY_VERTEX);
rsb.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, 2, D3D12_SHADER_VISIBILITY_PIXEL); // Source / Palette
rsb.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, 2, D3D12_SHADER_VISIBILITY_PIXEL); // Source (t0) / Palette (t2)
rsb.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 0, NUM_TFX_SAMPLERS, D3D12_SHADER_VISIBILITY_PIXEL);
rsb.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 2, 3, D3D12_SHADER_VISIBILITY_PIXEL); // RT / PrimID / Depth
// RT (t2) / PrimID (t3) / Depth (t4) / RT UAV (u0) / Depth UAV (u1)
D3D12_DESCRIPTOR_RANGE_TYPE rt_types[2] = { D3D12_DESCRIPTOR_RANGE_TYPE_SRV, D3D12_DESCRIPTOR_RANGE_TYPE_UAV };
u32 rt_start_regs[2] = { 2, 0 };
u32 rt_num_regs[2] = { 3, 2 };
rsb.AddDescriptorTableMultiRange(2, rt_types, rt_start_regs, rt_num_regs, D3D12_SHADER_VISIBILITY_PIXEL);
rsb.Add32BitConstants(2, sizeof(m_vs_pc_cache) / sizeof(u32), D3D12_SHADER_VISIBILITY_VERTEX);
if (!(m_tfx_root_signature = rsb.Create()))
return false;
@@ -2728,7 +2729,7 @@ bool GSDevice12::CompileConvertPipelines()
GSTexture::Format format = shader.OutputFormat();
DXGI_FORMAT dxgi_format;
LookupNativeFormat(format, nullptr, nullptr, &dxgi_format, nullptr);
LookupNativeFormat(format, nullptr, nullptr, &dxgi_format, nullptr, nullptr);
if (shader.DATMConvertShader() || shader.DepthOutput())
{
@@ -3067,7 +3068,6 @@ void GSDevice12::DestroyResources()
m_shader_cache.Close();
m_descriptor_heap_manager.Free(&m_null_srv_descriptor);
m_timestamp_query_buffer.reset();
m_timestamp_query_allocation.reset();
m_sampler_heap_manager.Destroy();
@@ -3178,6 +3178,8 @@ const ID3DBlob* GSDevice12::GetTFXPixelShader(const GSHWDrawConfig::PSSelector&
sm.AddMacro("PS_AA1", static_cast<u32>(sel.aa1));
sm.AddMacro("PS_ABE", sel.abe);
sm.AddMacro("PS_ANISOTROPIC_FILTERING", sel.sw_aniso);
sm.AddMacro("PS_ROV_COLOR", sel.rov_color);
sm.AddMacro("PS_ROV_DEPTH", static_cast<u32>(sel.rov_depth));
ComPtr<ID3DBlob> ps(m_shader_cache.GetPixelShader(m_tfx_source, sm.GetPtr(), "ps_main"));
it = m_tfx_pixel_shaders.emplace(sel, std::move(ps)).first;
@@ -3216,11 +3218,11 @@ GSDevice12::ComPtr<ID3D12PipelineState> GSDevice12::CreateTFXPipeline(const Pipe
if (p.rt)
{
const GSTexture::Format format = IsDATEModePrimIDInit(p.ps.date) ?
GSTexture::Format::PrimID :
(p.ps.colclip_hw ? GSTexture::Format::ColorClip : GSTexture::Format::Color);
GSTexture::Format::PrimID :
(p.ps.colclip_hw ? GSTexture::Format::ColorClip : GSTexture::Format::Color);
DXGI_FORMAT native_format;
LookupNativeFormat(format, nullptr, nullptr, &native_format, nullptr);
LookupNativeFormat(format, nullptr, nullptr, &native_format, nullptr, nullptr);
gpb.SetRenderTarget(num_rts++, native_format);
}
if (p.ds_as_rt)
@@ -3305,7 +3307,7 @@ GSDevice12::ComPtr<ID3D12PipelineState> GSDevice12::CreateTFXPipeline(const Pipe
if (pipeline)
{
D3D12::SetObjectName(
pipeline.get(), TinyString::from_format("TFX Pipeline {:08X}/{:08X}{:016X}", p.vs.key, p.ps.key_hi, p.ps.key_lo));
pipeline.get(), TinyString::from_format("TFX Pipeline {:08X}/{:016X}_{:016X}", p.vs.key, p.ps.key_hi, p.ps.key_lo));
}
return pipeline;
@@ -3335,8 +3337,10 @@ bool GSDevice12::BindDrawPipeline(const PipelineSelector& p)
void GSDevice12::InitializeState()
{
for (u32 i = 0; i < NUM_TOTAL_TFX_TEXTURES; i++)
for (u32 i = 0; i < TEXTURE_RT_UAV; i++)
m_tfx_textures[i] = m_null_texture->GetSRVDescriptor();
for (u32 i = TEXTURE_RT_UAV; i < NUM_TOTAL_TFX_TEXTURES; i++)
m_tfx_textures[i] = m_null_texture->GetUAVDescriptor();
m_tfx_sampler_sel = GSHWDrawConfig::SamplerSelector::Point().key;
InvalidateCachedState();
@@ -3475,25 +3479,30 @@ void GSDevice12::SetStencilRef(u8 ref)
m_dirty_flags |= DIRTY_FLAG_STENCIL_REF;
}
void GSDevice12::PSSetShaderResource(int i, GSTexture* sr, bool check_state, bool feedback)
void GSDevice12::PSSetShaderResource(int i, GSTexture* sr, bool check_state, ResourceType type)
{
pxAssert(i < NUM_TFX_TEXTURES + NUM_TFX_RT_TEXTURES + NUM_TFX_UAV_TEXTURES);
D3D12DescriptorHandle handle;
if (sr)
{
const GSTexture12::ResourceState state = GetResourceState(type);
GSTexture12* dtex = static_cast<GSTexture12*>(sr);
if (check_state)
{
if (dtex->GetResourceState() != GSTexture12::ResourceState::PixelShaderResource && InRenderPass())
if (dtex->GetResourceState() != state && InRenderPass())
{
GL_INS("Ending render pass due to resource transition");
EndRenderPass();
}
dtex->CommitClear();
dtex->TransitionToState(GSTexture12::ResourceState::PixelShaderResource);
dtex->TransitionToState(state);
}
dtex->SetUseFenceCounter(GetCurrentFenceValue());
handle = (feedback && !m_enhanced_barriers) ? dtex->GetFBLDescriptor() : dtex->GetSRVDescriptor();
handle = GetResourceDescriptor(dtex, type);
}
else
{
@@ -3504,7 +3513,7 @@ void GSDevice12::PSSetShaderResource(int i, GSTexture* sr, bool check_state, boo
return;
m_tfx_textures[i] = handle;
m_dirty_flags |= (i < 2) ? DIRTY_FLAG_TFX_TEXTURES : DIRTY_FLAG_TFX_RT_TEXTURES;
m_dirty_flags |= (i < NUM_TFX_TEXTURES) ? DIRTY_FLAG_TFX_TEXTURES : DIRTY_FLAG_TFX_RT_TEXTURES;
}
void GSDevice12::PSSetSampler(GSHWDrawConfig::SamplerSelector sel)
@@ -3517,6 +3526,68 @@ void GSDevice12::PSSetSampler(GSHWDrawConfig::SamplerSelector sel)
m_dirty_flags |= DIRTY_FLAG_TFX_SAMPLERS;
}
void GSDevice12::PSSetUnorderedAccess(GSTexture* rt, GSTexture* ds, bool write_rt, bool write_ds)
{
GSTexture12* d12Rt = static_cast<GSTexture12*>(rt);
GSTexture12* d12Ds = static_cast<GSTexture12*>(ds);
GSTexture12* oldD12Rt = m_tfx_textures_uav[0] != m_null_texture.get() ? m_tfx_textures_uav[0] : nullptr;
GSTexture12* oldD12Ds = m_tfx_textures_uav[1] != m_null_texture.get() ? m_tfx_textures_uav[1] : nullptr;
if (!(d12Rt || d12Ds || oldD12Rt || oldD12Ds))
return;
pxAssert(!(d12Rt || d12Ds) || m_features.rov);
if (d12Rt)
{
PSSetShaderResource(TEXTURE_RT_UAV, d12Rt, true, ResourceType::UAV);
// Unbind conflicting RT texture
PSSetShaderResource(TEXTURE_RT, nullptr, false);
// Unbind conflicting source texture
if (m_tfx_textures[TEXTURE_TEXTURE] == d12Rt->GetSRVDescriptor() ||
m_tfx_textures[TEXTURE_TEXTURE] == d12Rt->GetFBLDescriptor())
{
PSSetShaderResource(TEXTURE_TEXTURE, nullptr, false);
}
if (write_rt)
{
d12Rt->SetState(GSTexture::State::Dirty);
}
}
else
{
// Unbind to avoid conflicts with OM targets.
PSSetShaderResource(TEXTURE_RT_UAV, nullptr, false);
}
if (d12Ds)
{
PSSetShaderResource(TEXTURE_DEPTH_UAV, d12Ds, true, ResourceType::UAV);
// Unbind conflicting depth texture
PSSetShaderResource(TEXTURE_DEPTH, nullptr, false);
// Unbind conflicting source texture
if (m_tfx_textures[TEXTURE_TEXTURE] == d12Ds->GetSRVDescriptor())
{
PSSetShaderResource(TEXTURE_TEXTURE, nullptr, false);
}
if (write_ds)
{
d12Ds->SetState(GSTexture::State::Dirty);
}
}
else
{
// Unbind to avoid conflicts with OM targets.
PSSetShaderResource(TEXTURE_DEPTH_UAV, nullptr, false);
}
}
void GSDevice12::SetUtilityRootSignature()
{
if (m_current_root_signature == RootSignature::Utility)
@@ -3578,7 +3649,8 @@ void GSDevice12::SetUtilityPushConstants(const void* data, u32 size)
void GSDevice12::UnbindTexture(GSTexture12* tex)
{
for (u32 i = 0; i < NUM_TOTAL_TFX_TEXTURES; i++)
// Source / palette
for (u32 i = 0; i <= TEXTURE_PALETTE; i++)
{
if (m_tfx_textures[i] == tex->GetSRVDescriptor() || m_tfx_textures[i] == tex->GetFBLDescriptor())
{
@@ -3586,6 +3658,28 @@ void GSDevice12::UnbindTexture(GSTexture12* tex)
m_dirty_flags |= DIRTY_FLAG_TFX_TEXTURES;
}
}
// RT / primid / depth
for (u32 i = TEXTURE_RT; i <= TEXTURE_DEPTH; i++)
{
if (m_tfx_textures[i] == tex->GetSRVDescriptor() || m_tfx_textures[i] == tex->GetFBLDescriptor())
{
m_tfx_textures[i] = m_null_texture->GetSRVDescriptor();
m_dirty_flags |= DIRTY_FLAG_TFX_RT_TEXTURES;
}
}
// RT UAV / depth UAV
for (u32 i = TEXTURE_RT_UAV; i <= TEXTURE_DEPTH_UAV; i++)
{
if (m_tfx_textures_uav[i - TEXTURE_RT_UAV] == tex)
{
m_tfx_textures_uav[i - TEXTURE_RT_UAV] = nullptr;
m_tfx_textures[i] = m_null_texture->GetUAVDescriptor();
m_dirty_flags |= DIRTY_FLAG_TFX_RT_TEXTURES;
}
}
if (m_current_render_target == tex)
{
EndRenderPass();
@@ -3714,7 +3808,7 @@ void GSDevice12::BeginRenderPass(D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE color_b
if (color_begin == D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR)
{
LookupNativeFormat(m_current_render_target->GetFormat(), nullptr,
&rt[num_rts].BeginningAccess.Clear.ClearValue.Format, nullptr, nullptr);
&rt[num_rts].BeginningAccess.Clear.ClearValue.Format, nullptr, nullptr, nullptr);
GSVector4::store<false>(rt[num_rts].BeginningAccess.Clear.ClearValue.Color, clear_color);
}
num_rts++;
@@ -3737,7 +3831,7 @@ void GSDevice12::BeginRenderPass(D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE color_b
if (depth_begin == D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR)
{
LookupNativeFormat(m_current_depth_target->GetFormat(), nullptr, nullptr, nullptr,
&ds.DepthBeginningAccess.Clear.ClearValue.Format);
&ds.DepthBeginningAccess.Clear.ClearValue.Format, nullptr);
ds.DepthBeginningAccess.Clear.ClearValue.DepthStencil.Depth = clear_depth;
}
ds.StencilEndingAccess.Type = stencil_end;
@@ -3745,7 +3839,7 @@ void GSDevice12::BeginRenderPass(D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE color_b
if (stencil_begin == D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR)
{
LookupNativeFormat(m_current_depth_target->GetFormat(), nullptr, nullptr, nullptr,
&ds.StencilBeginningAccess.Clear.ClearValue.Format);
&ds.StencilBeginningAccess.Clear.ClearValue.Format, nullptr);
ds.StencilBeginningAccess.Clear.ClearValue.DepthStencil.Stencil = clear_stencil;
}
}
@@ -3847,6 +3941,10 @@ __ri void GSDevice12::ApplyBaseState(u32 flags, ID3D12GraphicsCommandList* cmdli
{
cmdlist->OMSetRenderTargets(0, nullptr, FALSE, m_current_depth_read_only ? &m_current_depth_target->GetReadDepthViewDescriptor().cpu_handle : &m_current_depth_target->GetWriteDescriptor().cpu_handle);
}
else
{
cmdlist->OMSetRenderTargets(0, nullptr, FALSE, nullptr);
}
}
}
@@ -3925,7 +4023,7 @@ bool GSDevice12::ApplyTFXState(bool already_execed)
if (flags & DIRTY_FLAG_TFX_RT_TEXTURES)
{
if (!GetTextureGroupDescriptors(&m_tfx_rt_textures_handle_gpu, m_tfx_textures.data() + 2, 3))
if (!GetTextureGroupDescriptors(&m_tfx_rt_textures_handle_gpu, m_tfx_textures.data() + 2, NUM_TFX_RT_TEXTURES + NUM_TFX_UAV_TEXTURES))
{
ExecuteCommandListAndRestartRenderPass(false, "Ran out of TFX RT descriptor descriptor groups");
return ApplyTFXState(true);
@@ -4161,10 +4259,14 @@ void GSDevice12::FeedbackBarrier(const GSTexture12* texture)
void GSDevice12::RenderHW(GSHWDrawConfig& config)
{
GSTexture12* colclip_rt = static_cast<GSTexture12*>(g_gs_device->GetColorClipTexture());
GSTexture12* draw_rt = static_cast<GSTexture12*>(config.rt);
GSTexture12* draw_ds = static_cast<GSTexture12*>(config.ds);
GSTexture12* draw_rt = config.ps.HasColorROV() ? nullptr : static_cast<GSTexture12*>(config.rt);
GSTexture12* draw_ds = config.ps.HasDepthROV() ? nullptr : static_cast<GSTexture12*>(config.ds);
GSTexture12* draw_rt_rov = config.ps.HasColorROV() ? static_cast<GSTexture12*>(config.rt) : nullptr;
GSTexture12* draw_ds_rov = config.ps.HasDepthROV() ? static_cast<GSTexture12*>(config.ds) : nullptr;
GSTexture12* draw_rt_clone = nullptr;
const bool feedback = draw_rt && (config.require_one_barrier || (config.require_full_barrier && m_features.texture_barrier) || (config.tex && config.tex == config.rt));
// Align the render area to 128x128, hopefully avoiding render pass restarts for small render area changes (e.g. Ratchet and Clank).
const GSVector2i rtsize(config.rt ? config.rt->GetSize() : config.ds->GetSize());
@@ -4200,6 +4302,9 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
Recycle(colclip_rt);
g_gs_device->SetColorClipTexture(nullptr);
colclip_rt = nullptr;
draw_rt = config.ps.HasColorROV() ? nullptr : static_cast<GSTexture12*>(config.rt);
}
else
{
@@ -4314,18 +4419,18 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
{
// avoid restarting the render pass just to switch from rt+depth to rt and vice versa
// keep the depth even if doing colclip hw draws, because the next draw will probably re-enable depth
if (!draw_rt && m_current_render_target && config.tex != m_current_render_target &&
m_current_render_target->GetSize() == draw_ds->GetSize())
if (!(draw_rt || draw_rt_rov) && m_current_render_target && config.tex != m_current_render_target &&
draw_ds && m_current_render_target->GetSize() == draw_ds->GetSize())
{
draw_rt = m_current_render_target;
m_pipeline_selector.rt = true;
}
else if (!draw_ds && m_current_depth_target && config.tex != m_current_depth_target &&
m_current_depth_target->GetSize() == draw_rt->GetSize())
{
draw_ds = m_current_depth_target;
m_pipeline_selector.ds = true;
}
}
else if (!(draw_ds || draw_ds_rov) && m_current_depth_target && config.tex != m_current_depth_target &&
draw_rt && m_current_depth_target->GetSize() == draw_rt->GetSize())
{
draw_ds = m_current_depth_target;
m_pipeline_selector.ds = true;
}
GSTexture12* draw_ds_as_rt = static_cast<GSTexture12*>(m_ds_as_rt);
@@ -4377,10 +4482,13 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
else
Console.Warning("D3D12: Failed to allocate temp texture for RT copy.");
}
PSSetUnorderedAccess(draw_rt_rov, draw_ds_rov, config.ps.HasColorOutput(), config.ps.HasDepthROVWrite());
// For depth testing and sampling, use a read only dsv, otherwise use a write dsv
OMSetRenderTargets(draw_rt, draw_ds_as_rt, draw_ds, config.scissor,
config.tex && (config.tex == config.ds || config.ps.IsFeedbackLoopDepth()) && !config.depth.zwe);
config.tex && (config.tex == draw_ds || config.ps.IsFeedbackLoopDepth()) && !config.depth.zwe && !config.ps.HasDepthROV(),
config.rt ? config.rt->GetSize() : config.ds->GetSize());
// DX12 equivalent of vkCmdClearAttachments for StencilOne
if (config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::StencilOne)
@@ -4435,14 +4543,15 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
// Restore original scissor, not sure if needed since the render pass has already been started. But to be safe.
OMSetRenderTargets(draw_rt, nullptr, draw_ds, config.scissor);
}
// VB/IB upload, if we did DATE setup and it's not colclip hw this has already been done
SetPrimitiveTopology(s_primitive_topology_mapping[static_cast<u8>(config.topology)]);
if (!date_image || colclip_rt)
UploadHWDrawVerticesAndIndices(config);
// now we can do the actual draw
SendHWDraw(pipe, config, draw_rt, draw_ds_as_rt, feedback_rt, feedback_depth,
config.require_one_barrier, config.require_full_barrier);
SendHWDraw(pipe, config, draw_rt, draw_ds_as_rt, draw_rt_rov, draw_ds_rov,
feedback_rt, feedback_depth, config.require_one_barrier, config.require_full_barrier);
// blend second pass
if (config.blend_multi_pass.enable)
@@ -4472,8 +4581,9 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
pipe.cms = config.alpha_second_pass.colormask;
pipe.dss = config.alpha_second_pass.depth;
pipe.bs = config.blend;
SendHWDraw(pipe, config, draw_rt, draw_ds_as_rt, feedback_rt, feedback_depth,
config.alpha_second_pass.require_one_barrier, config.alpha_second_pass.require_full_barrier);
SendHWDraw(pipe, config, draw_rt, draw_ds_as_rt, draw_rt_rov, draw_ds_rov,
feedback_rt, feedback_depth, config.alpha_second_pass.require_one_barrier,
config.alpha_second_pass.require_full_barrier);
}
if (date_image)
@@ -4516,9 +4626,13 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
}
void GSDevice12::SendHWDraw(const PipelineSelector& pipe, const GSHWDrawConfig& config, GSTexture12* draw_rt,
GSTexture12* draw_ds, const bool feedback_rt, const bool feedback_depth, const bool one_barrier,
const bool full_barrier)
GSTexture12* draw_ds, GSTexture12* draw_rt_rov, GSTexture12* draw_ds_rov,
const bool feedback_rt, const bool feedback_depth,
const bool one_barrier, const bool full_barrier)
{
// Should not be mixing ROVs with barriers.
pxAssert(!(draw_rt_rov || draw_ds_rov) || !(one_barrier || full_barrier));
const int n_barriers = static_cast<int>(feedback_rt) + static_cast<int>(feedback_depth);
if (!m_features.texture_barrier) [[unlikely]]
@@ -4535,11 +4649,11 @@ void GSDevice12::SendHWDraw(const PipelineSelector& pipe, const GSHWDrawConfig&
Console.Warning("D3D12: Possible unnecessary barrier detected.");
#endif
if ((one_barrier || full_barrier) && feedback_rt)
PSSetShaderResource(2, draw_rt, false, true);
PSSetShaderResource(2, draw_rt, false, ResourceType::FBL);
if (config.tex_hazard == GSHWDrawConfig::TEX_HAZARD_RT)
PSSetShaderResource(0, draw_rt, false, true);
PSSetShaderResource(0, draw_rt, false, ResourceType::FBL);
if ((one_barrier || full_barrier) && feedback_depth)
PSSetShaderResource(4, draw_ds, false, true);
PSSetShaderResource(4, draw_ds, false, ResourceType::FBL);
if (full_barrier)
{
@@ -4580,6 +4694,9 @@ void GSDevice12::SendHWDraw(const PipelineSelector& pipe, const GSHWDrawConfig&
if (BindDrawPipeline(pipe))
Draw(config);
if (config.ps.HasColorROV() || config.ps.HasDepthROV())
g_perfmon.Put(GSPerfMon::DrawCallsROV, 1);
}
void GSDevice12::UpdateHWPipelineSelector(GSHWDrawConfig& config)
@@ -4587,14 +4704,14 @@ void GSDevice12::UpdateHWPipelineSelector(GSHWDrawConfig& config)
m_pipeline_selector.vs.key = config.vs.key;
m_pipeline_selector.ps.key_hi = config.ps.key_hi;
m_pipeline_selector.ps.key_lo = config.ps.key_lo;
m_pipeline_selector.dss.key = config.depth.key;
m_pipeline_selector.bs.key = config.blend.key;
m_pipeline_selector.dss.key = config.ps.HasDepthROV() ? GSHWDrawConfig::DepthStencilSelector::NoDepth().key : config.depth.key;
m_pipeline_selector.bs.key = config.ps.HasColorROV() ? GSHWDrawConfig::BlendState().key : config.blend.key;
m_pipeline_selector.bs.constant = 0; // don't dupe states with different alpha values
m_pipeline_selector.cms.key = config.colormask.key;
m_pipeline_selector.cms.key = config.ps.HasColorROV() ? GSHWDrawConfig::ColorMaskSelector().key : config.colormask.key;
m_pipeline_selector.topology = static_cast<u32>(config.topology);
m_pipeline_selector.rt = config.rt != nullptr;
m_pipeline_selector.ds = config.ds != nullptr;
m_pipeline_selector.ds_as_rt = m_ds_as_rt != nullptr;
m_pipeline_selector.rt = config.rt != nullptr && !config.ps.HasColorROV();
m_pipeline_selector.ds = config.ds != nullptr && !config.ps.HasDepthROV();
m_pipeline_selector.ds_as_rt = m_ds_as_rt != nullptr && !config.ps.HasDepthROV();
}
void GSDevice12::UploadHWDrawVerticesAndIndices(GSHWDrawConfig& config)
+52 -12
View File
@@ -56,6 +56,44 @@ public:
D3D12_RESOURCE_DESC desc;
};
enum class ResourceType
{
SRV, // Shader resource view
FBL, // Feedback loop
UAV, // Unordered access
};
D3D12DescriptorHandle GetResourceDescriptor(GSTexture12* tex, ResourceType type) const
{
switch (type)
{
case ResourceType::SRV:
return tex->GetSRVDescriptor();
case ResourceType::FBL:
return m_enhanced_barriers ? tex->GetSRVDescriptor() : tex->GetFBLDescriptor();
case ResourceType::UAV:
return tex->GetUAVDescriptor();
default:
pxFailRel("Impossible.");
return D3D12DescriptorHandle{ 0, 0 };
}
}
static constexpr GSTexture12::ResourceState GetResourceState(ResourceType type)
{
switch (type)
{
case ResourceType::SRV:
case ResourceType::FBL:
return GSTexture12::ResourceState::PixelShaderResource;
case ResourceType::UAV:
return GSTexture12::ResourceState::PixelShaderUAV;
default:
pxFailRel("Impossible.");
return static_cast<GSTexture12::ResourceState>(-1);
}
}
__fi IDXGIAdapter1* GetAdapter() const { return m_adapter.get(); }
__fi ID3D12Device* GetDevice() const { return m_device.get(); }
__fi ID3D12CommandQueue* GetCommandQueue() const { return m_command_queue.get(); }
@@ -96,7 +134,6 @@ public:
D3D12DescriptorHeapManager& GetRTVHeapManager() { return m_rtv_heap_manager; }
D3D12DescriptorHeapManager& GetDSVHeapManager() { return m_dsv_heap_manager; }
D3D12DescriptorHeapManager& GetSamplerHeapManager() { return m_sampler_heap_manager; }
const D3D12DescriptorHandle& GetNullSRVDescriptor() const { return m_null_srv_descriptor; }
D3D12StreamBuffer& GetTextureStreamBuffer() { return m_texture_stream_buffer; }
// Root signature access.
@@ -198,7 +235,6 @@ private:
D3D12DescriptorHeapManager m_rtv_heap_manager;
D3D12DescriptorHeapManager m_dsv_heap_manager;
D3D12DescriptorHeapManager m_sampler_heap_manager;
D3D12DescriptorHandle m_null_srv_descriptor;
D3D_FEATURE_LEVEL m_feature_level = D3D_FEATURE_LEVEL_11_0;
@@ -224,14 +260,13 @@ public:
GSHWDrawConfig::VSSelector vs;
GSHWDrawConfig::DepthStencilSelector dss;
GSHWDrawConfig::ColorMaskSelector cms;
u8 pad;
__fi bool operator==(const PipelineSelector& p) const { return BitEqual(*this, p); }
__fi bool operator!=(const PipelineSelector& p) const { return !BitEqual(*this, p); }
__fi PipelineSelector() { std::memset(this, 0, sizeof(*this)); }
};
static_assert(sizeof(PipelineSelector) == 24, "Pipeline selector is 24 bytes");
static_assert(sizeof(PipelineSelector) == 32, "Pipeline selector is 32 bytes");
struct PipelineSelectorHash
{
@@ -282,11 +317,14 @@ public:
TEXTURE_RT = 2,
TEXTURE_PRIMID = 3,
TEXTURE_DEPTH = 4,
TEXTURE_RT_UAV = 5,
TEXTURE_DEPTH_UAV = 6,
NUM_TFX_CONSTANT_BUFFERS = 2,
NUM_TFX_TEXTURES = 2,
NUM_TFX_RT_TEXTURES = 3,
NUM_TOTAL_TFX_TEXTURES = NUM_TFX_TEXTURES + NUM_TFX_RT_TEXTURES,
NUM_TFX_UAV_TEXTURES = 2,
NUM_TOTAL_TFX_TEXTURES = NUM_TFX_TEXTURES + NUM_TFX_RT_TEXTURES + NUM_TFX_UAV_TEXTURES,
NUM_TFX_SAMPLERS = 1,
NUM_UTILITY_TEXTURES = 1,
NUM_UTILITY_SAMPLERS = 1,
@@ -385,7 +423,7 @@ private:
std::string m_tfx_source;
void LookupNativeFormat(GSTexture::Format format, DXGI_FORMAT* d3d_format, DXGI_FORMAT* srv_format,
DXGI_FORMAT* rtv_format, DXGI_FORMAT* dsv_format) const;
DXGI_FORMAT* rtv_format, DXGI_FORMAT* dsv_format, DXGI_FORMAT* uav_format) const;
u32 GetSwapChainBufferCount() const;
bool CreateSwapChain();
@@ -513,11 +551,12 @@ public:
void IASetIndexBuffer(const void* index, size_t count);
void VSSetIndexBuffer(const void* index, size_t count);
void PSSetShaderResource(int i, GSTexture* sr, bool check_state, bool feedback = false);
void PSSetShaderResource(int i, GSTexture* sr, bool check_state, ResourceType type = ResourceType::SRV);
void PSSetSampler(GSHWDrawConfig::SamplerSelector sel);
void PSSetUnorderedAccess(GSTexture* rt, GSTexture* ds, bool write_rt, bool write_ds);
void OMSetRenderTargets(GSTexture* rt, GSTexture* ds, GSTexture* ds_as_rt, const GSVector4i& scissor,
bool depth_read = false);
bool depth_read = false, const GSVector2i& viewport_size = {});
void SetVSConstantBuffer(const GSHWDrawConfig::VSConstantBuffer& cb);
void SetPSConstantBuffer(const GSHWDrawConfig::PSConstantBuffer& cb);
@@ -526,8 +565,8 @@ public:
void RenderHW(GSHWDrawConfig& config) override;
void SendHWDraw(const PipelineSelector& pipe, const GSHWDrawConfig& config, GSTexture12* draw_rt,
GSTexture12* draw_ds, const bool feedback_rt, const bool feedback_depth, const bool one_barrier,
const bool full_barrier);
GSTexture12* draw_ds, GSTexture12* draw_rt_rov, GSTexture12* draw_ds_rov,
const bool feedback_rt, const bool feedback_depth, const bool one_barrier, const bool full_barrier);
void UpdateHWPipelineSelector(GSHWDrawConfig& config);
void UploadHWDrawVerticesAndIndices(GSHWDrawConfig& config);
@@ -593,8 +632,8 @@ private:
DIRTY_FLAG_PS_CONSTANT_BUFFER_BINDING = (1 << 6),
DIRTY_FLAG_VS_VERTEX_BUFFER_BINDING = (1 << 7),
DIRTY_FLAG_VS_INDEX_BUFFER_BINDING = (1 << 8),
DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE = (1 << 9),
DIRTY_FLAG_SAMPLERS_DESCRIPTOR_TABLE = (1 << 10),
DIRTY_FLAG_SAMPLERS_DESCRIPTOR_TABLE = (1 << 9),
DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE = (1 << 10),
DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE_2 = (1 << 11),
DIRTY_FLAG_VS_PUSH_CONSTANTS = (1 << 12),
@@ -657,6 +696,7 @@ private:
std::array<D3D12_GPU_VIRTUAL_ADDRESS, NUM_TFX_CONSTANT_BUFFERS> m_tfx_constant_buffers{};
std::array<D3D12DescriptorHandle, NUM_TOTAL_TFX_TEXTURES> m_tfx_textures{};
std::array<GSTexture12*, NUM_TFX_UAV_TEXTURES> m_tfx_textures_uav{};
D3D12DescriptorHandle m_tfx_sampler;
u32 m_tfx_sampler_sel = 0;
D3D12DescriptorHandle m_tfx_textures_handle_gpu;
+36 -22
View File
@@ -110,6 +110,10 @@ void GSTexture12::Destroy(bool defer)
}
m_write_descriptor_type = WriteDescriptorType::None;
#ifdef PCSX2_DEVBUILD
m_debug_name.clear();
#endif
}
// For use with non-simultaneous textures only.
@@ -203,6 +207,7 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
desc.desc1.Flags = (levels > 1 && !IsCompressedFormat(format)) ? D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET :
D3D12_RESOURCE_FLAG_NONE;
state = ResourceState::CopyDst;
pxAssert(uav_format == DXGI_FORMAT_UNKNOWN);
}
break;
@@ -217,6 +222,10 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
desc.desc1.Layout = D3D12_TEXTURE_LAYOUT_64KB_UNDEFINED_SWIZZLE;
optimized_clear_value.Format = rtv_format;
state = ResourceState::RenderTarget;
if (uav_format != DXGI_FORMAT_UNKNOWN)
{
desc.desc1.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
}
}
break;
@@ -227,6 +236,7 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
desc.desc1.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
optimized_clear_value.Format = dsv_format;
state = ResourceState::DepthWriteStencil;
pxAssert(uav_format == DXGI_FORMAT_UNKNOWN);
}
break;
@@ -235,6 +245,8 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
pxAssert(levels == 1);
allocationDesc.Flags |= D3D12MA::ALLOCATION_FLAG_COMMITTED;
state = ResourceState::PixelShaderResource;
pxAssert(uav_format != DXGI_FORMAT_UNKNOWN);
desc.desc1.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
}
break;
@@ -242,9 +254,6 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
return {};
}
if (uav_format != DXGI_FORMAT_UNKNOWN)
desc.desc1.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
wil::com_ptr_nothrow<ID3D12Resource> resource;
wil::com_ptr_nothrow<ID3D12Resource> resource_fbl;
wil::com_ptr_nothrow<D3D12MA::Allocation> allocation;
@@ -330,6 +339,12 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
dev->GetDescriptorHeapManager().Free(&srv_descriptor);
return {};
}
if (uav_format != DXGI_FORMAT_UNKNOWN && !CreateUAVDescriptor(resource.get(), uav_format, &uav_descriptor))
{
dev->GetRTVHeapManager().Free(&write_descriptor);
dev->GetDescriptorHeapManager().Free(&srv_descriptor);
return {};
}
}
break;
@@ -350,26 +365,15 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
}
break;
default:
break;
}
if (uav_format != DXGI_FORMAT_UNKNOWN && !CreateUAVDescriptor(resource.get(), dsv_format, &uav_descriptor))
{
switch (write_descriptor_type)
case Type::RWTexture:
{
case WriteDescriptorType::RTV:
dev->GetRTVHeapManager().Free(&write_descriptor);
break;
case WriteDescriptorType::DSV:
dev->GetDSVHeapManager().Free(&ro_dsv_descriptor);
dev->GetDSVHeapManager().Free(&write_descriptor);
break;
default:
break;
if (uav_format != DXGI_FORMAT_UNKNOWN && !CreateUAVDescriptor(resource.get(), uav_format, &uav_descriptor))
{
dev->GetDescriptorHeapManager().Free(&srv_descriptor);
return {};
}
}
dev->GetDescriptorHeapManager().Free(&srv_descriptor);
return {};
break;
}
// Feedback descriptor used with legacy barriers
@@ -442,7 +446,7 @@ std::unique_ptr<GSTexture12> GSTexture12::Adopt(wil::com_ptr_nothrow<ID3D12Resou
if (uav_format != DXGI_FORMAT_UNKNOWN)
{
if (!CreateUAVDescriptor(resource.get(), srv_format, &uav_descriptor))
if (!CreateUAVDescriptor(resource.get(), uav_format, &uav_descriptor))
{
switch (write_descriptor_type)
{
@@ -796,6 +800,8 @@ void GSTexture12::SetDebugName(std::string_view name)
return;
D3D12::SetObjectName(m_resource.get(), name);
m_debug_name = name;
}
#endif
@@ -1137,6 +1143,14 @@ void GSTexture12::TransitionSubresourceToState(const D3D12CommandList& cmdlist,
cmdlist.list4->ResourceBarrier(num_barriers, barriers);
}
// Count as a UAV barrier if we transition to/from UAV.
if (IsRenderTargetOrDepthStencil() &&
(before_state == ResourceState::PixelShaderUAV || after_state == ResourceState::PixelShaderUAV))
{
g_perfmon.Put(GSPerfMon::Barriers, 1);
g_perfmon.Put(GSPerfMon::BarriersROV, 1);
}
}
void GSTexture12::CommitClear()
+2
View File
@@ -56,6 +56,8 @@ public:
__fi ID3D12Resource* GetResource() const { return m_resource.get(); }
__fi ID3D12Resource* GetFBLResource() const { return m_resource_fbl.get(); }
virtual bool IsUnorderedAccess() const override { return GetResourceState() == ResourceState::PixelShaderUAV; }
void* GetNativeHandle() const override;
bool Update(const GSVector4i& r, const void* data, int pitch, int layer = 0) override;
+415 -20
View File
@@ -1358,10 +1358,10 @@ GSVector4i GSRendererHW::ComputeBoundingBoxRT(const GSVector2i& rtsize, float rt
return GSVector4i(box * GSVector4(rtscale)).rintersect(GSVector4i(0, 0, rtsize.x, rtsize.y));
}
GSVector4i GSRendererHW::ComputeBoundingBoxTex(const GSVector2i& texsize, const GSVector4i& region, float texscale)
GSVector4i GSRendererHW::ComputeBoundingBoxTex(const GSVector2i& texsize, const GSVector4i& coverage, const GSVector4i& region, float texscale)
{
const GSVector4 offset = GSVector4(region.xyxy()) + GSVector4(-1.0f, -1.0f, 1.0f, 1.0f); // Region offset + round value
const GSVector4 box = m_vt.m_min.t.upld(m_vt.m_max.t) + offset;
const GSVector4 box = GSVector4(coverage) + offset;
return GSVector4i(box * GSVector4(texscale)).rintersect(GSVector4i(0, 0, texsize.x, texsize.y));
}
@@ -6206,16 +6206,6 @@ void GSRendererHW::DetermineBarriers(GSTextureCache::Target* rt, GSTextureCache:
ComputeDrawlistGetSize(rt->m_scale);
m_conf.drawlist = &m_drawlist;
m_conf.drawlist_bbox = &m_drawlist_bbox;
if (m_conf.tex_hazard != GSHWDrawConfig::TEX_HAZARD_NONE)
{
GetPrimitiveOverlapDrawlistTextureBBox(tex->GetScale());
m_conf.drawlist_bbox_tex = &m_drawlist_bbox_tex;
}
else
{
m_conf.drawlist_bbox_tex = nullptr;
}
}
}
@@ -6825,6 +6815,13 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, DATEOptio
m_conf.ps.blend_d = 2;
}
// Save in case needed for ROV setup.
m_optimized_blend.A = m_conf.ps.blend_a;
m_optimized_blend.B = m_conf.ps.blend_b;
m_optimized_blend.C = m_conf.ps.blend_c;
m_optimized_blend.D = m_conf.ps.blend_d;
m_optimized_blend.FIX = AFIX;
// TODO: blend_ad_alpha_masked, as well as other blend cases can be optimized on dx11/dx12/gl to use
// blend multipass more which might be faster, vk likely won't benefit as barriers are already fast.
@@ -7478,6 +7475,396 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, DATEOptio
}
}
// In certain cases using a ROV with depth or color will force the other one
// because of how color and depth interact via testing.
__fi void GSRendererHW::GetForcedROVUsage(bool& rov_color, bool& rov_depth)
{
// Cannot force if both are already enabled or disabled.
if (rov_color == rov_depth)
return;
// If depth and color have feedback and one uses ROV, the other must also.
// We currently don't have a way of using barriers in one and ROV in the other.
if ((m_conf.ps.IsFeedbackLoopRT() && rov_depth) || (m_conf.ps.IsFeedbackLoopDepth() && rov_color))
{
GL_INS("ROV: Feedback compatibility forces color and depth ROV");
rov_color = true;
rov_depth = true;
}
// If we use color ROV with discard or the pixel shader writes to depth,
// we cannot use early depth stencil, so must use depth ROV with feedback.
// Same applies in reverse for depth ROV forcing color ROV with feedback.
const bool color_write = (m_conf.colormask.wrgba != 0);
const bool depth_test = m_cached_ctx.DepthRead();
// Separate flag for DATE since they are many methods and the interaction with ROV is not clear.
const bool date = m_cached_ctx.TEST.DATE;
if (m_conf.ds && rov_color && (m_conf.ps.HasShaderDiscard() || m_conf.ps.HasDepthOutput() || date))
{
GL_INS("ROV: Color ROV with shader discard/depth write forces depth ROV");
rov_depth = true;
}
else if (m_conf.rt && color_write && rov_depth && (m_conf.ps.HasShaderDiscard() || depth_test || date))
{
GL_INS("ROV: Depth ROV with shader discard forces color ROV");
rov_color = true;
}
}
void GSRendererHW::DetermineROVUsage(GSTextureCache::Target* rt, GSTextureCache::Target* ds)
{
const GSDevice::FeatureSupport& features = g_gs_device->Features();
if (!(GSConfig.HWROV && features.rov))
return;
GL_PUSH("HW: ROV Setup");
if (features.framebuffer_fetch)
{
GL_INS("ROV: Disabled because have FB-fetch");
return;
}
if (rt && rt->m_texture == m_conf.tex && !m_conf.ps.tex_is_fb)
{
GL_INS("ROV: Disabled because tex is RT and not sampling from current pixel");
return;
}
if (ds && ds->m_texture == m_conf.tex)
{
GL_INS("ROV: Disabled because tex is depth");
return;
}
const bool color_write = rt && m_conf.colormask.wrgba != 0;
const bool depth_write = ds && m_cached_ctx.DepthWrite();
const u32 colormask = GSUtil::GetChannelMask(m_cached_ctx.FRAME.PSM) & m_conf.colormask.wrgba;
const bool colormask_needs_rt = colormask != 0xF;
const u32 ate = m_cached_ctx.TEST.ATE;
const u32 atst = m_cached_ctx.TEST.ATST;
const u32 afail = m_cached_ctx.TEST.AFAIL;
const bool afail_needs_rt = ate && ((afail == AFAIL_ZB_ONLY) || (afail == AFAIL_RGB_ONLY));
const bool afail_needs_depth = ate && ((afail == AFAIL_FB_ONLY) || (afail == AFAIL_RGB_ONLY));
const bool blend = m_conf.IsBlending();
const bool blend_needs_rt = blend &&
(m_optimized_blend.A == ALPHA_ABD_CD || m_optimized_blend.B == ALPHA_ABD_CD ||
m_optimized_blend.C == ALPHA_C_AD || m_optimized_blend.D == ALPHA_ABD_CD);
const bool two_pass_alpha = GSHWDrawConfig::HasAlphaTestSecondPass(m_conf.alpha_test);
const bool date = m_conf.destination_alpha != GSHWDrawConfig::DestinationAlphaMode::Off;
const bool ztst = m_cached_ctx.DepthRead();
const bool full_barrier = m_conf.require_full_barrier;
// Heuristically determine what ROVs would be needed to eliminate passes based on the current config.
const bool multipass_color = (full_barrier && m_conf.ps.IsFeedbackLoopRT()) ||
two_pass_alpha ||
m_conf.blend_multi_pass.enable;
const bool multipass_depth = (full_barrier && m_conf.ps.IsFeedbackLoopDepth()) || two_pass_alpha;
// If already ROV, just continue the usage.
const bool color_is_rov = rt && rt->m_texture->IsUnorderedAccess();
const bool depth_is_rov = ds && ds->m_texture->IsDepthColor();
bool use_rov_color = (color_write && multipass_color) || color_is_rov;
bool use_rov_depth = (depth_write && multipass_depth) || depth_is_rov;
// In certain cases, ROV in color or depth will force ROV in the other for correctness.
GetForcedROVUsage(use_rov_color, use_rov_depth);
// Get the number of barriers that would be used with the current config.
u32 barriers = 1;
if (full_barrier)
{
if (m_drawlist.size() > 0)
{
barriers = static_cast<u32>(m_drawlist.size()); // Already computed
}
else
{
barriers = 2; // Tells drawlist computation to stop after reaching 2.
GetPrimitiveOverlapDrawlist(false, false, 1.0f, &barriers);
}
}
const u32 multiplier = m_conf.alpha_second_pass.enable ? 2 : 1; // Alpha second pass doubles the barriers.
barriers *= multiplier;
// Heuristic: only activate ROV if we save at least one draw call by doing so.
const bool activate = (use_rov_color != color_is_rov || use_rov_depth != depth_is_rov) && barriers >= 2;
if (!color_is_rov && !depth_is_rov && !activate)
{
// Not enough barriers or no feedback to activate, and ROV is not already active.
GL_ROV("No ROV usage: Draw=%05lld | C=%016p | D=%016p | BAR=%d.",
s_n, rt ? rt->m_texture : nullptr, ds ? ds->m_texture : nullptr, barriers);
return;
}
if (activate)
{
GL_ROV("ROV activated: Draw=%05lld | C=%016p | D=%016p | BAR=%d | C=[%d=>%d] | D=[%d=>%d].",
s_n, rt ? rt->m_texture : nullptr, ds ? ds->m_texture : nullptr, barriers,
color_is_rov, use_rov_color, depth_is_rov, use_rov_depth);
}
else
{
GL_ROV("ROV continued: Draw=%05lld | C=%016p | D=%016p | BAR=%d | C=%d | D=%d.",
s_n, rt ? rt->m_texture : nullptr, ds ? ds->m_texture : nullptr, barriers,
color_is_rov, depth_is_rov);
}
GL_INS("ROV: Color ROV %s / depth ROV %s",
use_rov_color ? "enabled" : "disabled", use_rov_depth ? "enabled" : "disabled");
// Do the actual pipeline config.
ConfigureROV(use_rov_color, use_rov_depth);
}
void GSRendererHW::ConfigureROV(bool color_rov, bool depth_rov)
{
// Do the actual config for depth.
if (depth_rov)
{
m_conf.depth = GSHWDrawConfig::DepthStencilSelector::NoDepth(); // Disable real depth.
const bool depth_write = m_cached_ctx.DepthWrite();
GL_INS("ROV: Using %s depth ROV", depth_write ? "read/write" : "read-only");
ConfigureDepthFeedback(true);
m_conf.ps.rov_depth = depth_write ? GSHWDrawConfig::PS_ROV_DEPTH::READ_WRITE : GSHWDrawConfig::PS_ROV_DEPTH::READ_ONLY;
}
// Do the actual config for color.
if (color_rov)
{
// FbMask setup
if (m_conf.colormask.wrgba != 0)
{
const GSVector4i fbmask = GSVector4i(m_conf.colormask.wr ? 0 : 0xFF, m_conf.colormask.wg ? 0 : 0xFF,
m_conf.colormask.wb ? 0 : 0xFF, m_conf.colormask.wa ? 0 : 0xFF);
if (!m_conf.ps.fbmask)
{
// Don't enable FB mask emulation, just use the mask for ROV.
m_conf.cb_ps.FbMask = fbmask;
}
else
{
m_conf.cb_ps.FbMask |= fbmask;
}
GL_INS("ROV: FbMask={ R=%x, G=%x, B=%x, A=%x }",
m_conf.cb_ps.FbMask.r, m_conf.cb_ps.FbMask.g, m_conf.cb_ps.FbMask.b, m_conf.cb_ps.FbMask.a);
}
else
{
m_conf.ps.no_color = true;
}
// Blend setup
if (m_conf.IsBlending())
{
GL_INS("ROV: Using SW blend%s", m_conf.blend.enable ? " and disabling HW" : "");
m_conf.ps.blend_a = m_optimized_blend.A;
m_conf.ps.blend_b = m_optimized_blend.B;
m_conf.ps.blend_c = m_optimized_blend.C;
m_conf.ps.blend_d = m_optimized_blend.D;
if (m_conf.ps.blend_c == ALPHA_C_FIX)
m_conf.cb_ps.TA_MaxDepth_Af.a = m_optimized_blend.FIX / 128.0f;
// Disable HW or mixed blend or multipass blend
m_conf.blend = {};
m_conf.ps.blend_hw = false;
m_conf.ps.blend_mix = false;
m_conf.blend_multi_pass = {};
if (!m_conf.ps.no_color1)
{
// We should never need dual source with SW blend
GL_INS("ROV: Disabling dual source blending");
m_conf.ps.no_color1 = true;
}
// Only needed with HW blend.
m_conf.ps.round_inv = false;
m_conf.ps.a_masked = false;
}
// Dither setup
if (m_conf.ps.dither)
{
// Only needed with HW blend.
m_conf.ps.dither_adjust = false;
}
// Destination alpha test setup
if (m_conf.destination_alpha != GSHWDrawConfig::DestinationAlphaMode::Off)
{
GL_INS("ROV: Using DATE Full%s",
(m_conf.destination_alpha != GSHWDrawConfig::DestinationAlphaMode::Full) ? " and replace current method" : "");
if (m_conf.destination_alpha != GSHWDrawConfig::DestinationAlphaMode::Full)
{
m_conf.destination_alpha = GSHWDrawConfig::DestinationAlphaMode::Full;
m_conf.depth.date = false; // Don't use stencil with ROV
m_conf.depth.date_one = false; // Don't use stencil with ROV
m_conf.ps.date = 5 + m_cached_ctx.TEST.DATM; // Shader discard DATM.
m_conf.datm = static_cast<SetDATM>(0); // Not needed
}
}
// Colclip setup
if (m_conf.ps.colclip_hw)
{
// Remove HW colclip texture if needed
GL_INS("ROV: Replacing colclip HW with SW");
const bool has_colclip_texture = g_gs_device->GetColorClipTexture() != nullptr;
m_conf.ps.colclip_hw = 0;
m_conf.ps.colclip = true;
m_conf.colclip_mode = has_colclip_texture ? GSHWDrawConfig::ColClipMode::EarlyResolve : GSHWDrawConfig::ColClipMode::NoModify;
}
// PABE setup
const bool PABE = m_draw_env->PABE.PABE && GetAlphaMinMax().min < 128;
if (m_conf.IsBlending() && PABE && !m_conf.ps.pabe)
{
GL_INS("ROV: Enabling PABE");
m_conf.ps.pabe = true;
}
// Alpha test setup. KEEP will already be fine.
if (m_cached_ctx.TEST.ATE && m_conf.alpha_test != GSHWDrawConfig::AlphaTestMode::KEEP)
{
GL_INS("ROV: Using SW feedback alpha test%s", m_conf.alpha_second_pass.enable ?
" and disabling alpha second pass" : "");
m_conf.alpha_test = GSHWDrawConfig::AlphaTestMode::FEEDBACK;
GSHWDrawConfig::PS_ATST ps_atst;
float ps_aref;
GetAlphaTestConfigPS(m_cached_ctx.TEST.ATST, m_cached_ctx.TEST.AREF, false, ps_atst, ps_aref);
m_conf.ps.atst = ps_atst;
m_conf.ps.afail = static_cast<GSHWDrawConfig::PS_AFAIL>(m_cached_ctx.TEST.AFAIL);
if (m_cached_ctx.DepthWrite() && m_cached_ctx.TEST.AFAIL == AFAIL_RGB_ONLY)
{
pxAssert(depth_rov); // Should have enabled depth ROV for depth feedback loop.
m_conf.ps.afail = PS_AFAIL::RGB_ONLY_SW_Z;
}
m_conf.cb_ps.FogColor_AREF.a = ps_aref;
GL_INS("ROV: Using ATST=%d, AFAIL=%d, AREF=%.2f", ps_atst, static_cast<u32>(m_conf.ps.afail), ps_aref);
if (m_conf.alpha_second_pass.enable)
{
m_conf.alpha_second_pass = {};
}
}
m_conf.ps.rov_color = true;
}
// Remove regular barriers.
if (color_rov || depth_rov)
{
m_conf.require_full_barrier = false;
m_conf.require_one_barrier = false;
}
}
void GSRendererHW::SetUnorderedAccessFlag(GSTextureCache::Target* rt)
{
// Set flag for ROV activation heuristic. Only used by DX11.
// Only needed for RT, as we use a different method for depth.
if (rt)
{
if (m_conf.ps.HasColorROV())
rt->m_texture->SetUnorderedAccess();
else
rt->m_texture->ClearUnorderedAccess();
}
}
static void CopyDepthTextureROV(GSTexture* src, GSTexture* dst)
{
switch (src->GetState())
{
case GSTexture::State::Cleared:
g_gs_device->ClearDepth(dst, src->GetClearDepth());
break;
case GSTexture::State::Invalidated:
g_gs_device->InvalidateRenderTarget(dst);
break;
case GSTexture::State::Dirty:
g_gs_device->StretchRectAuto(src, dst, Nearest);
break;
}
// These stats are counted both as part of ROV and non-ROV stats.
g_perfmon.Put(GSPerfMon::DepthCopiesROV, 1.0);
g_perfmon.Put(GSPerfMon::DrawCallsROV, 1.0);
};
void GSRendererHW::ConvertDepthFormatROV(GSTextureCache::Target* ds)
{
if (!ds)
return;
GSTexture* ds_tex_old = m_conf.ds;
GSTexture* ds_tex_new = nullptr;
// Convert depth to depth color or vice versa if needed.
bool depth_to_color;
if (m_conf.ps.HasDepthROV() && !ds_tex_old->IsDepthColor())
{
depth_to_color = true;
ds_tex_new = g_gs_device->CreateDepthColor(ds_tex_old->GetSize(), false, true);
}
else if (!m_conf.ps.HasDepthROV() && ds_tex_old->IsDepthColor())
{
depth_to_color = false;
ds_tex_new = g_gs_device->CreateDepthStencil(ds->m_texture->GetSize(), false, true);
}
else
{
return;
}
GL_PUSH("HW: Convert %s for ROV.", depth_to_color ? "DepthStencil -> DepthColor" : "DepthColor -> DepthStencil");
CopyDepthTextureROV(ds_tex_old, ds_tex_new);
#if PCSX2_DEVBUILD
ds_tex_new->SetDebugName(ds->m_texture->GetDebugName());
#endif
// Fix up the texture cache.
if (ds->m_texture == ds_tex_old)
{
GL_CACHE("HW: Replaced texture for DS @ 0x%04x", ds->m_TEX0.TBP0);
ds->m_texture = ds_tex_new;
}
else
{
// Must be the temporary Z.
pxAssert(g_texture_cache->GetTemporaryZ() == ds_tex_old);
GL_CACHE("HW: Replaced texture for temporary Z @ 0x%04x", g_texture_cache->GetTemporaryZInfo().ZBP);
g_texture_cache->SetTemporaryZ(ds_tex_new);
}
g_gs_device->Recycle(ds_tex_old);
m_conf.ds = ds_tex_new;
}
__ri static constexpr bool IsRedundantClamp(u8 clamp, u32 clamp_min, u32 clamp_max, u32 tsize)
{
// Don't shader sample when the clamp/repeat is configured to the texture size.
@@ -8075,8 +8462,10 @@ __ri void GSRendererHW::HandleTextureHazards(const GSTextureCache::Target* rt, c
const int clamp_horizontal_page_offset = m_cached_ctx.CLAMP.WMS == CLAMP_REGION_REPEAT ? (m_cached_ctx.CLAMP.MAXU / GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].pgs.x) : 0;
const int clamp_vertical_page_offset = m_cached_ctx.CLAMP.WMT == CLAMP_REGION_REPEAT ? (m_cached_ctx.CLAMP.MAXV / GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].pgs.y) : 0;
const int page_offset = ((m_cached_ctx.TEX0.TBP0 - src_target->m_TEX0.TBP0) >> 5) + clamp_horizontal_page_offset + clamp_vertical_page_offset;
const int horizontal_offset = ((page_offset % src_target->m_TEX0.TBW) * GSLocalMemory::m_psm[src_target->m_TEX0.PSM].pgs.x);
const int vertical_offset = ((page_offset / src_target->m_TEX0.TBW) * GSLocalMemory::m_psm[src_target->m_TEX0.PSM].pgs.y);
const GSVector2i draw_offset = GSVector2i((static_cast<int>(m_vt.m_min.t.x) / GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].pgs.x) * GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].pgs.x,
(static_cast<int>(m_vt.m_min.t.y) / GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].pgs.y) * GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].pgs.y);
const int horizontal_offset = ((page_offset % src_target->m_TEX0.TBW) * GSLocalMemory::m_psm[src_target->m_TEX0.PSM].pgs.x) + draw_offset.x;
const int vertical_offset = ((page_offset / src_target->m_TEX0.TBW) * GSLocalMemory::m_psm[src_target->m_TEX0.PSM].pgs.y) + draw_offset.y;
if (HandleBarrierHazard(false) || (rt != tex->m_from_target && ds != tex->m_from_target))
{
@@ -8780,14 +9169,14 @@ void GSRendererHW::EmulateAlphaTestSecondPass()
}
// Setup barriers and/or SW depth testing for depth feedback.
void GSRendererHW::ConfigureDepthFeedback()
void GSRendererHW::ConfigureDepthFeedback(bool rov_depth)
{
if (m_conf.ps.IsFeedbackLoopDepth())
if (m_conf.ps.IsFeedbackLoopDepth() || rov_depth)
{
const GSDevice::FeatureSupport& features = g_gs_device->Features();
// We need barriers for the feedback.
if (features.feedback_loops())
// We need barriers for the feedback (except with ROV).
if (features.feedback_loops() && !rov_depth)
{
m_conf.require_one_barrier |= (m_prim_overlap == PRIM_OVERLAP_NO);
m_conf.require_full_barrier |= (m_prim_overlap != PRIM_OVERLAP_NO);
@@ -9037,7 +9426,13 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
m_conf.ps.rta_correction = rt->m_rt_alpha_scale;
}
// Call before computing the full drawlist in case ROV is used and we don't need it.
DetermineROVUsage(rt, ds);
ConvertDepthFormatROV(ds);
SetUnorderedAccessFlag(rt);
// Barriers must be determined before indices are modified via HandleProvokingVertexFirst/SetupIA.
// This also computes the drawlist if needed.
DetermineBarriers(rt, tex);
// Perform second pass setup here once barriers are determined.
@@ -9057,7 +9452,7 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
const GSVector4i tex_region = tex ? tex->GetRegionRect() : GSVector4i::zero();
m_conf.samplearea = m_channel_shuffle ? scissor :
GSVector4i::loadh(texsize).rintersect(ComputeBoundingBoxTex(texsize, tex_region, texscale));
GSVector4i::loadh(texsize).rintersect(ComputeBoundingBoxTex(texsize, tmm.coverage, tex_region, texscale));
m_conf.scissor = (date_options.enabled && !date_options.barrier) ? m_conf.drawarea : scissor;
@@ -9065,7 +9460,7 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
SetupIA(rtscale, vs_scale_x, vs_scale_y, m_channel_shuffle_width != 0, no_rt);
if (m_conf.ds && m_conf.ps.IsFeedbackLoopDepth() && !g_gs_device->Features().depth_feedback)
if (m_conf.ds && m_conf.ps.IsFeedbackLoopDepth() && !g_gs_device->Features().depth_feedback && !m_conf.ps.HasDepthROV())
{
GL_PUSH("HW: Creating temporary R32 RT for depth feedback");
+10 -2
View File
@@ -236,7 +236,7 @@ private:
static void GetAlphaTestConfigPS(const u32 atst, const u8 aref, const bool invert_test, PS_ATST& ps_atst_out, float& aref_out);
void EmulateAlphaTest(DATEOptions& date_options);
void EmulateAlphaTestSecondPass();
void ConfigureDepthFeedback();
void ConfigureDepthFeedback(bool rov_depth = false);
void CalculateAlphaRange(GSTextureCache::Target* rt, GSTextureCache::Target* ds, DATEOptions& date_options,
int& blend_alpha_min, int& blend_alpha_max, int& rt_new_alpha_min, int& rt_new_alpha_max);
@@ -253,6 +253,12 @@ private:
const GSVector2i& unscaled_size, float& vs_scale_x, float& vs_scale_y);
void DetermineBarriers(GSTextureCache::Target* rt, GSTextureCache::Source* tex);
void GetForcedROVUsage(bool& color_cov, bool& depth_rov); // Whether having color or depth with the current config forces the other.
void DetermineROVUsage(GSTextureCache::Target* rt, GSTextureCache::Target* ds); // Heuristics to determine whether to enable/disable ROV
void ConfigureROV(bool color_rov, bool depth_rov); // Actual config for ROV
void SetUnorderedAccessFlag(GSTextureCache::Target* rt); // Set flag for DX11 ROV heuristic to work.
void ConvertDepthFormatROV(GSTextureCache::Target* ds); // Convert between depth and depth color if needed.
void SetTCOffset();
bool NextDrawColClip() const;
bool IsPossibleChannelShuffle() const;
@@ -330,6 +336,8 @@ private:
GSVector2i m_lod = {}; // Min & Max level of detail
GIFRegALPHA m_optimized_blend = {}; // Save for ROV setup
GSHWDrawConfig m_conf = {};
HWCachedCtx m_cached_ctx;
@@ -356,7 +364,7 @@ public:
void ExpandLineIndices();
GSVector4 RealignTargetTextureCoordinate(const GSTextureCache::Source* tex);
GSVector4i ComputeBoundingBoxRT(const GSVector2i& rtsize, float rtscale);
GSVector4i ComputeBoundingBoxTex(const GSVector2i& texsize, const GSVector4i& region, float texscale);
GSVector4i ComputeBoundingBoxTex(const GSVector2i& texsize, const GSVector4i& coverage, const GSVector4i& region, float texscale);
void MergeSprite(GSTextureCache::Source* tex);
float GetTextureScaleFactor() override;
GSVector2i GetValidSize(const GSTextureCache::Source* tex = nullptr, const bool is_shuffle = false);
+6 -8
View File
@@ -2586,7 +2586,7 @@ GSTextureCache::Target* GSTextureCache::LookupDrawTarget(GIFRegTEX0 TEX0, const
i++;
continue;
}
const GSLocalMemory::psm_t& s_psm = GSLocalMemory::m_psm[TEX0.PSM];
const u32 widthpage_offset = (std::abs(static_cast<int>(bp - t->m_TEX0.TBP0)) >> 5) % std::max(t->m_TEX0.TBW, 1U);
const bool is_aligned_ok = widthpage_offset == 0 || ((min_rect.width() <= static_cast<int>((t->m_TEX0.TBW - widthpage_offset) * 64) && (t->m_TEX0.TBW == TEX0.TBW || TEX0.TBW == 1)) && bp >= t->m_TEX0.TBP0);
const bool no_target_or_newer = (!new_dst || ((GSState::s_n - new_dst->m_last_draw) < (GSState::s_n - t->m_last_draw)));
@@ -2597,11 +2597,9 @@ GSTextureCache::Target* GSTextureCache::LookupDrawTarget(GIFRegTEX0 TEX0, const
const bool was_used_last_draw = t->m_last_draw == (GSState::s_n - 1);
// if it's a shuffle, some games tend to offset back by a page, such as Tomb Raider, for no disernable reason, but it then causes problems.
// This can also happen horizontally (Catwoman moves everything one page left with shuffles), but this is too messy to deal with right now.
const bool overlaps = t->Overlaps(bp, TEX0.TBW, TEX0.PSM, min_rect) || (is_shuffle && src && GSLocalMemory::m_psm[src->m_TEX0.PSM].bpp == 8 && t->Overlaps(bp, TEX0.TBW, TEX0.PSM, min_rect + GSVector4i(0, 0, 0, 32)));
const bool overlaps = t->Overlaps(bp, TEX0.TBW, TEX0.PSM, min_rect) || (is_shuffle && src && GSLocalMemory::m_psm[src->m_TEX0.PSM].bpp == 8 && t->Overlaps(bp, TEX0.TBW, TEX0.PSM, min_rect + GSVector4i(0, 0, 0, s_psm.pgs.y - (min_rect.w & (s_psm.pgs.y - 1)))));
if (source_match || (no_target_or_newer && is_aligned_ok && width_match && overlaps && (is_shuffle || ds_offset || is_double_buffer || was_used_last_draw)))
{
const GSLocalMemory::psm_t& s_psm = GSLocalMemory::m_psm[TEX0.PSM];
// If it overlaps but the target is huge and the Z isn't offset, we need to split the buffer, so let's shrink this one down.
// 896 is just 448 * 2,just gives the buffer chance to be larger than normal, in case they do something like 640x640, or something ridiculous.
if (!is_shuffle && (ds && offset == 0 && (t->m_valid.w >= 896) && ((((t->m_end_block + 1) - t->m_TEX0.TBP0) >> 1) + t->m_TEX0.TBP0) <= bp))
@@ -4893,7 +4891,7 @@ void GSTextureCache::InvalidateLocalMem(const GSOffset& off, const GSVector4i& r
// Could be reading Z24/32 back as CT32 (Gundam Battle Assault 3)
if (GSLocalMemory::m_psm[psm].bpp >= 16)
{
if (GSConfig.HWDownloadMode != GSHardwareDownloadMode::Enabled)
if (GSConfig.HWDownloadMode > GSHardwareDownloadMode::EnabledForceFull)
{
DevCon.Error("TC: Skipping depth readback of %ux%u @ %u,%u", r.width(), r.height(), r.left, r.top);
return;
@@ -4926,7 +4924,7 @@ void GSTextureCache::InvalidateLocalMem(const GSOffset& off, const GSVector4i& r
const bool swizzle_match = GSLocalMemory::m_psm[psm].depth == GSLocalMemory::m_psm[t->m_TEX0.PSM].depth;
// Calculate the rect offset if the BP doesn't match.
GSVector4i targetr = {};
if (full_flush || t->readbacks_since_draw > 1)
if (full_flush || GSConfig.HWDownloadMode == GSHardwareDownloadMode::EnabledForceFull || t->readbacks_since_draw > 1)
{
targetr = t->m_drawn_since_read;
}
@@ -5163,7 +5161,7 @@ void GSTextureCache::InvalidateLocalMem(const GSOffset& off, const GSVector4i& r
}
}
if (GSConfig.HWDownloadMode != GSHardwareDownloadMode::Enabled)
if (GSConfig.HWDownloadMode > GSHardwareDownloadMode::EnabledForceFull)
{
DevCon.Error("TC: Skipping depth readback of %ux%u @ %u,%u", targetr.width(), targetr.height(), targetr.left, targetr.top);
continue;
@@ -6426,7 +6424,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
}
const u32 destination_tbw = (dst->m_TEX0.TBP0 == TEX0.TBP0) ? (std::max<u32>(TEX0.TBW, 1u) * 64) : std::max<u32>(dst->m_TEX0.TBW, 1u) * 128;
if (dst->GetScale() > 1.0f)
if (!GSConfig.UserHacks_NativePaletteDraw && dst->GetScale() > 1.0f)
{
GSTexture* tmpTex = use_texture ?
g_gs_device->CreateTexture(dst->m_unscaled_size, 1, GSTexture::Format::Color, PreferReusedLabelledTexture()) :
+1 -1
View File
@@ -101,7 +101,7 @@ struct PipelineSelectorMTL
}
};
static_assert(sizeof(PipelineSelectorMTL) == 24);
static_assert(sizeof(PipelineSelectorMTL) == 28);
template <>
struct std::hash<PipelineSelectorMTL>
+1 -1
View File
@@ -2005,7 +2005,7 @@ void GSDeviceMTL::MRESetHWPipelineState(GSHWDrawConfig::VSSelector vssel, GSHWDr
MTLRenderPipelineColorAttachmentDescriptor* color1 = [[pdesc colorAttachments] objectAtIndexedSubscript:1];
[color1 setPixelFormat:MTLPixelFormatR32Float];
}
NSString* pname = [NSString stringWithFormat:@"HW Render %x.%x.%llx.%x", vssel_mtl.key, pssel.key_hi, pssel.key_lo, extras.fullkey];
NSString* pname = [NSString stringWithFormat:@"HW Render %x.%x.%llx.%llx", vssel_mtl.key, pssel.key_hi, pssel.key_lo, extras.fullkey];
auto pipeline = MakePipeline(pdesc, vs, ps, pname);
[m_current_render.encoder setRenderPipelineState:pipeline];
-2
View File
@@ -45,7 +45,6 @@ namespace GLState
u32 draw_buffers;
GLuint tex_unit[8];
GLuint64 tex_handle[8];
u32 UpdateDrawBuffers()
{
@@ -90,6 +89,5 @@ namespace GLState
draw_buffers = 0;
std::fill(std::begin(tex_unit), std::end(tex_unit), 0);
std::fill(std::begin(tex_handle), std::end(tex_handle), 0);
}
} // namespace GLState
-1
View File
@@ -49,7 +49,6 @@ namespace GLState
extern bool ds_written; // Depth Stencil written
extern GLuint tex_unit[8]; // shader input texture
extern GLuint64 tex_handle[8]; // shader input texture
extern u32 UpdateDrawBuffers();
extern void Clear();
+88 -50
View File
@@ -87,6 +87,12 @@ namespace Emulate_DSA
glTexSubImage2D(GL_TEXTURE_2D, level, xoffset, yoffset, width, height, format, type, pixels);
}
static void GLAPIENTRY CopyTextureSubImage(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height)
{
BindTextureUnit(7, texture);
glCopyTexSubImage2D(GL_TEXTURE_2D, level, xoffset, yoffset, x, y, width, height);
}
static void GLAPIENTRY CompressedTextureSubImage(GLuint texture, GLint level, GLint xoffset, GLint yoffset,
GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data)
{
@@ -126,6 +132,7 @@ namespace Emulate_DSA
glCreateTextures = CreateTexture;
glTextureStorage2D = TextureStorage;
glTextureSubImage2D = TextureSubImage;
glCopyTextureSubImage2D = CopyTextureSubImage;
glCompressedTextureSubImage2D = CompressedTextureSubImage;
glGetTextureImage = GetTexureImage;
glTextureParameteri = TextureParameteri;
@@ -619,7 +626,8 @@ bool GSDeviceOGL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
// This extension allow FS depth to range from -1 to 1. So
// gl_position.z could range from [0, 1]
// Change depth convention
glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
if (GLAD_GL_ARB_clip_control)
glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
// ****************************************************************
// HW renderer shader
@@ -650,7 +658,7 @@ bool GSDeviceOGL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
// Basic to ensure structures are correctly packed
static_assert(sizeof(VSSelector) == 1, "Wrong VSSelector size");
static_assert(sizeof(PSSelector) == 12, "Wrong PSSelector size");
static_assert(sizeof(PSSelector) == 16, "Wrong PSSelector size");
static_assert(sizeof(PSSamplerSelector) == 1, "Wrong PSSamplerSelector size");
static_assert(sizeof(OMDepthStencilSelector) == 1, "Wrong OMDepthStencilSelector size");
static_assert(sizeof(OMColorMaskSelector) == 1, "Wrong OMColorMaskSelector size");
@@ -769,17 +777,16 @@ bool GSDeviceOGL::CheckFeatures()
return false;
}
if (!GLAD_GL_VERSION_4_3 && !GLAD_GL_ARB_copy_image && !GLAD_GL_EXT_copy_image)
if (!GLAD_GL_VERSION_4_3 && !GLAD_GL_ARB_copy_image && !GLAD_GL_EXT_copy_image && !GLAD_GL_NV_copy_image)
{
Host::ReportFormattedErrorAsync(
"GS", "GL_ARB_copy_image is not supported, this is required for the OpenGL renderer.");
return false;
Host::AddOSDMessage(
"GL_ARB_copy_image is not supported, copies will be slower.", Host::OSD_ERROR_DURATION);
}
if (!GLAD_GL_VERSION_4_5 && !GLAD_GL_ARB_clip_control)
{
Host::ReportFormattedErrorAsync(
"GS", "GL_ARB_clip_control is not supported, this is required for the OpenGL renderer.");
return false;
Host::AddOSDMessage(
"GL_ARB_clip_control is not supported, depth will be less accurate.", Host::OSD_ERROR_DURATION);
}
if (!GLAD_GL_ARB_viewport_array)
@@ -901,6 +908,11 @@ bool GSDeviceOGL::CheckFeatures()
m_features.aa1 = GSConfig.HWAA1 && m_features.vs_expand && m_features.feedback_loops();
if (GSConfig.HWROV)
{
Console.Warning("GL: ROV is not implemented for GL and will be disabled.");
}
return true;
}
@@ -1332,8 +1344,9 @@ void GSDeviceOGL::CommitClear(GSTexture* t, bool use_write_fbo)
if (use_write_fbo)
{
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER,
(t->GetType() == GSTexture::Type::RenderTarget) ? GL_COLOR_ATTACHMENT0 :
(m_features.framebuffer_fetch ? GL_DEPTH_ATTACHMENT : GL_DEPTH_STENCIL_ATTACHMENT),
(t->GetType() == GSTexture::Type::RenderTarget) ?
GL_COLOR_ATTACHMENT0 :
(m_features.framebuffer_fetch ? GL_DEPTH_ATTACHMENT : GL_DEPTH_STENCIL_ATTACHMENT),
GL_TEXTURE_2D, 0, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, GLState::fbo);
}
@@ -1439,9 +1452,9 @@ std::string GSDeviceOGL::GenGlslHeader(const std::string_view entry, GLenum type
{
std::string header;
// Intel's GL driver doesn't like the readonly qualifier with 3.3 GLSL.
if (m_features.vs_expand && GLAD_GL_VERSION_4_3)
{
// Intel's GL driver doesn't like the readonly qualifier with 3.3 GLSL.
header = "#version 430 core\n";
}
else
@@ -1485,6 +1498,11 @@ std::string GSDeviceOGL::GenGlslHeader(const std::string_view entry, GLenum type
header += "#define DEPTH_FEEDBACK_SUPPORT 2\n"; // Depth as RT
}
if (GLAD_GL_ARB_clip_control)
header += "#define HAS_CLIP_CONTROL 1\n";
else
header += "#define HAS_CLIP_CONTROL 0\n";
// Allow to puts several shader in 1 files
switch (type)
{
@@ -1522,7 +1540,7 @@ std::string GSDeviceOGL::GetVSSource(VSSelector sel)
std::string macro = fmt::format("#define VS_FST {}\n", static_cast<u32>(sel.fst))
+ fmt::format("#define VS_IIP {}\n", static_cast<u32>(sel.iip))
+ fmt::format("#define VS_POINT_SIZE {}\n", static_cast<u32>(sel.point_size))
+ fmt::format("#define VS_EXPAND {}\n", static_cast<int>(sel.expand));
+ fmt::format("#define VS_EXPAND {}\n", static_cast<int>(sel.expand));
std::string src = GenGlslHeader("vs_main", GL_VERTEX_SHADER, macro);
src += m_shader_tfx_vgs;
@@ -1531,7 +1549,7 @@ std::string GSDeviceOGL::GetVSSource(VSSelector sel)
std::string GSDeviceOGL::GetPSSource(const PSSelector& sel)
{
DevCon.WriteLn("GL: Compiling new pixel shader with selector 0x%" PRIX64 "%08X", sel.key_hi, sel.key_lo);
DevCon.WriteLn("GL: Compiling new pixel shader with selector 0x%016" PRIX64 "_%016" PRIX64, sel.key_hi, sel.key_lo);
std::string macro = fmt::format("#define PS_FST {}\n", sel.fst)
+ fmt::format("#define PS_WMS {}\n", sel.wms)
@@ -1592,7 +1610,9 @@ std::string GSDeviceOGL::GetPSSource(const PSSelector& sel)
+ fmt::format("#define PS_ZTST {}\n", sel.ztst)
+ fmt::format("#define PS_AA1 {}\n", static_cast<u32>(sel.aa1))
+ fmt::format("#define PS_ABE {}\n", sel.abe)
+ fmt::format("#define PS_ANISOTROPIC_FILTERING {}", sel.sw_aniso)
+ fmt::format("#define PS_ANISOTROPIC_FILTERING {}\n", sel.sw_aniso)
+ fmt::format("#define PS_ROV_COLOR {}\n", 0)
+ fmt::format("#define PS_ROV_DEPTH {}\n", 0)
;
std::string src = GenGlslHeader("ps_main", GL_FRAGMENT_SHADER, macro);
@@ -1619,7 +1639,7 @@ void GSDeviceOGL::BlitRect(GSTexture* sTex, const GSVector4i& r, const GSVector2
OMSetDepthStencilState(m_convert.dss);
OMSetBlendState();
OMSetColorMaskState();
PSSetShaderResource(0, sTex);
PSSetShaderResource(TEXTURE_TEXTURE, sTex);
PSSetSamplerState(filter == Biln ? m_convert.ln : m_convert.pt);
DrawStretchRect(float_r / (GSVector4(sTex->GetSize()).xyxy()), float_r, dsize);
@@ -1668,6 +1688,30 @@ void GSDeviceOGL::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r
glCopyImageSubDataEXT(sid, GL_TEXTURE_2D, 0, r.x, r.y, 0, did, GL_TEXTURE_2D,
0, destX, destY, 0, r.width(), r.height(), 1);
}
else if (GLAD_GL_NV_copy_image)
{
glCopyImageSubDataNV(sid, GL_TEXTURE_2D, 0, r.x, r.y, 0, did, GL_TEXTURE_2D,
0, destX, destY, 0, r.width(), r.height(), 1);
}
else
{
const bool draw_in_depth = sTex->IsDepthStencil();
const GLenum attachment = draw_in_depth ? GL_DEPTH_STENCIL_ATTACHMENT : GL_COLOR_ATTACHMENT0;
// Bind attachments.
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo_read);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, attachment, GL_TEXTURE_2D, sid, 0);
glReadBuffer(draw_in_depth ? GL_NONE : GL_COLOR_ATTACHMENT0);
// Do copy.
glCopyTextureSubImage2D(did, 0, destX, destY, r.x, r.y, r.width(), r.height());
// Unbind attachments.
if (draw_in_depth)
glReadBuffer(GL_COLOR_ATTACHMENT0);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, attachment, GL_TEXTURE_2D, 0, 0);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
}
dTex->SetState(GSTexture::State::Dirty);
}
@@ -1722,7 +1766,7 @@ void GSDeviceOGL::DoStretchRect(GSTexture* sTex, const GSVector4& sRect, GSTextu
// Texture
// ************************************
PSSetShaderResource(0, sTex);
PSSetShaderResource(TEXTURE_TEXTURE, sTex);
PSSetSamplerState(filter == Biln ? m_convert.ln : m_convert.pt);
// ************************************
@@ -1757,7 +1801,7 @@ void GSDeviceOGL::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture
OMSetBlendState(false);
OMSetColorMaskState();
PSSetShaderResource(0, sTex);
PSSetShaderResource(TEXTURE_TEXTURE, sTex);
PSSetSamplerState(filter == Biln ? m_convert.ln : m_convert.pt);
// Flip y axis only when we render in the backbuffer
@@ -1784,7 +1828,7 @@ void GSDeviceOGL::UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX,
OMSetColorMaskState();
OMSetRenderTargets(dTex, nullptr, nullptr);
PSSetShaderResource(0, sTex);
PSSetShaderResource(TEXTURE_TEXTURE, sTex);
PSSetSamplerState(m_convert.pt);
const GSVector4 dRect(0, 0, dSize, 1);
@@ -1808,7 +1852,7 @@ void GSDeviceOGL::ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 off
OMSetColorMaskState();
OMSetRenderTargets(dTex, nullptr, nullptr);
PSSetShaderResource(0, sTex);
PSSetShaderResource(TEXTURE_TEXTURE, sTex);
PSSetSamplerState(m_convert.pt);
const GSVector4 dRect(0, 0, dTex->GetWidth(), dTex->GetHeight());
@@ -1832,7 +1876,7 @@ void GSDeviceOGL::FilteredDownsampleTexture(GSTexture* sTex, GSTexture* dTex, u3
OMSetColorMaskState();
OMSetRenderTargets(dTex, nullptr, nullptr);
PSSetShaderResource(0, sTex);
PSSetShaderResource(TEXTURE_TEXTURE, sTex);
PSSetSamplerState(m_convert.pt);
//const GSVector4 dRect = GSVector4(dTex->GetRect());
@@ -1958,7 +2002,7 @@ void GSDeviceOGL::DoMultiStretchRects(const MultiStretchRect* rects, u32 num_rec
m_vertex_stream_buffer->Unmap(vcount * sizeof(GSVertexPT1));
m_index_stream_buffer->Unmap(icount * sizeof(u16));
PSSetShaderResource(0, rects[0].src);
PSSetShaderResource(TEXTURE_TEXTURE, rects[0].src);
PSSetSamplerState(rects[0].filter == Biln ? m_convert.ln : m_convert.pt);
OMSetColorMaskState(rects[0].wmask);
DrawIndexedPrimitive();
@@ -2149,7 +2193,7 @@ void GSDeviceOGL::SetupDATE(GSTexture* rt, GSTexture* ds, SetDATM datm, const GS
// Texture
PSSetShaderResource(0, rt);
PSSetShaderResource(TEXTURE_TEXTURE, rt);
PSSetSamplerState(m_convert.pt);
DrawPrimitive();
@@ -2308,7 +2352,7 @@ bool GSDeviceOGL::DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, con
prog.Uniform4uiv(1, &constants[4]);
prog.Uniform2iv(2, reinterpret_cast<const s32*>(&constants[8]));
PSSetShaderResource(0, sTex);
PSSetShaderResource(TEXTURE_TEXTURE, sTex);
glBindImageTexture(0, static_cast<GSTextureOGL*>(dTex)->GetID(), 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8);
static const int threadGroupWorkRegionDim = 16;
@@ -2706,6 +2750,9 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
const GSVector2i rtsize = (config.rt ? config.rt : config.ds)->GetSize();
GSTexture* colclip_rt = g_gs_device->GetColorClipTexture();
GSTexture* draw_rt = config.rt;
GSTexture* draw_ds = config.ds;
GSTexture* draw_ds_as_rt = m_ds_as_rt;
GSTexture* draw_rt_clone = nullptr;
GSTexture* draw_ds_clone = nullptr;
GSTexture* primid_texture = nullptr;
@@ -2762,6 +2809,8 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
const GSVector4 sRect = dRect / GSVector4(rtsize.x, rtsize.y).xyxy();
StretchRect(config.rt, sRect, colclip_rt, dRect, ShaderConvert::COLCLIP_INIT, Nearest);
}
draw_rt = colclip_rt ? colclip_rt : config.rt;
}
// Destination Alpha Setup
@@ -2820,13 +2869,13 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
IASetPrimitiveTopology(topology);
if (config.tex && (m_features.texture_barrier || config.tex_hazard == GSHWDrawConfig::TEX_HAZARD_NONE))
PSSetShaderResource(0, config.tex);
PSSetShaderResource(TEXTURE_TEXTURE, config.tex);
if (config.pal)
PSSetShaderResource(1, config.pal);
PSSetShaderResource(TEXTURE_PALETTE, config.pal);
if (m_features.texture_barrier && (config.require_one_barrier || config.require_full_barrier))
PSSetShaderResource(2, colclip_rt ? colclip_rt : config.rt);
PSSetShaderResource(TEXTURE_RT, colclip_rt ? colclip_rt : config.rt);
if (m_features.texture_barrier && (config.require_one_barrier || config.require_full_barrier) && config.ps.IsFeedbackLoopDepth())
PSSetShaderResource(4, m_features.depth_feedback ? config.ds : m_ds_as_rt);
PSSetShaderResource(TEXTURE_DEPTH, m_features.depth_feedback ? config.ds : m_ds_as_rt);
SetupSampler(config.sampler);
@@ -2896,10 +2945,10 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
psel.ps.date = 3;
config.alpha_second_pass.ps.date = 3;
SetupPipeline(psel);
PSSetShaderResource(3, primid_texture);
PSSetShaderResource(TEXTURE_PRIMID, primid_texture);
}
if (m_ds_as_rt)
if (draw_ds_as_rt)
{
// We must clear the blend equation of any dual source blending factors or
// it may interact badly with MRTs, even if blending is disabled.
@@ -2918,28 +2967,22 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
OMSetBlendState();
}
GSTexture* draw_rt = colclip_rt ? colclip_rt : config.rt;
GSTexture* draw_ds_as_rt = m_ds_as_rt;
GSTexture* draw_ds = config.ds;
// Clear texture binding when it's bound to RT or DS.
if (!config.tex && ((draw_rt && static_cast<GSTextureOGL*>(draw_rt)->GetID() == GLState::tex_unit[0]) ||
(draw_ds && static_cast<GSTextureOGL*>(draw_ds)->GetID() == GLState::tex_unit[0])))
PSSetShaderResource(0, nullptr);
PSSetShaderResource(TEXTURE_TEXTURE, nullptr);
// Avoid changing framebuffer just to switch from rt+depth to rt and vice versa.
bool fb_optimization_needs_barrier = false;
if (!draw_rt && GLState::rt && GLState::ds == draw_ds && config.tex != GLState::rt &&
GLState::rt->GetSize() == draw_ds->GetSize() && !draw_ds_as_rt)
if (!(draw_rt || draw_ds_as_rt) && draw_ds && GLState::rt && GLState::rt->GetSize() == draw_ds->GetSize())
{
draw_rt = GLState::rt;
fb_optimization_needs_barrier = !GLState::rt_written;
fb_optimization_needs_barrier = !GLState::rt_written && GLState::ds == draw_ds;
}
else if (!draw_ds && GLState::ds && GLState::rt == draw_rt && config.tex != GLState::ds &&
GLState::ds->GetSize() == draw_rt->GetSize() && !draw_ds_as_rt)
else if (!(draw_ds || draw_ds_as_rt) && draw_rt && GLState::ds && GLState::ds->GetSize() == draw_rt->GetSize())
{
draw_ds = GLState::ds;
fb_optimization_needs_barrier = !GLState::ds_written;
fb_optimization_needs_barrier = !GLState::ds_written && GLState::rt == draw_rt;
}
// Be careful of the rt already being bound and the blend using the RT without a barrier.
@@ -2985,12 +3028,6 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
constexpr GLint clear_color = 1;
glClearBufferiv(GL_STENCIL, 0, &clear_color);
}
else if (draw_ds && !(config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::Stencil ||
config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::StencilOne))
{
const GLenum attachments[] = {GL_STENCIL_ATTACHMENT};
glInvalidateFramebuffer(GL_DRAW_FRAMEBUFFER, std::size(attachments), attachments);
}
SendHWDraw(config, rt_feedbackloop_pass1 ? draw_rt_clone : nullptr, draw_rt, ds_feedbackloop_pass1 ? draw_ds_clone : nullptr, draw_ds,
config.require_one_barrier, config.require_full_barrier);
@@ -3133,6 +3170,9 @@ void GSDeviceOGL::SendHWDraw(const GSHWDrawConfig& config,
else
pxAssert(config.drawlist_bbox && static_cast<u32>(config.drawlist_bbox->size()) == draw_list_size);
if (!m_features.texture_barrier && config.tex_hazard != config.TEX_HAZARD_NONE)
FeedbackCopyAndBind(config, draw_rt, draw_rt_clone, draw_ds, draw_ds_clone, config.samplearea);
for (u32 n = 0, p = 0; n < draw_list_size; n++)
{
const u32 count = config.drawlist->at(n) * indices_per_prim;
@@ -3144,9 +3184,7 @@ void GSDeviceOGL::SendHWDraw(const GSHWDrawConfig& config,
else
{
const GSVector4i bbox = config.drawlist_bbox->at(n).rintersect(config.drawarea);
const GSVector4i bbox_tex = config.drawlist_bbox_tex ?
config.drawlist_bbox_tex->at(n).rintersect(config.samplearea) : GSVector4i::zero();
FeedbackCopyAndBind(config, draw_rt, draw_rt_clone, draw_ds, draw_ds_clone, bbox, bbox_tex);
FeedbackCopyAndBind(config, draw_rt, draw_rt_clone, draw_ds, draw_ds_clone, bbox);
}
Draw(config, p, count);
+10 -1
View File
@@ -115,6 +115,15 @@ public:
using OMDepthStencilSelector = GSHWDrawConfig::DepthStencilSelector;
using OMColorMaskSelector = GSHWDrawConfig::ColorMaskSelector;
enum TextureUnit : u32
{
TEXTURE_TEXTURE,
TEXTURE_PALETTE,
TEXTURE_RT,
TEXTURE_PRIMID,
TEXTURE_DEPTH,
};
struct alignas(16) ProgramSelector
{
PSSelector ps;
@@ -124,7 +133,7 @@ public:
__fi bool operator==(const ProgramSelector& p) const { return BitEqual(*this, p); }
__fi bool operator!=(const ProgramSelector& p) const { return !BitEqual(*this, p); }
};
static_assert(sizeof(ProgramSelector) == 16, "Program selector is 16 bytes");
static_assert(sizeof(ProgramSelector) == 32, "Program selector is 32 bytes");
struct ProgramSelectorHash
{
+16 -15
View File
@@ -30,26 +30,25 @@ GSTextureOGL::GSTextureOGL(Type type, int width, int height, int levels, Format
m_type = type;
m_texture_id = 0;
m_mipmap_levels = 1;
int gl_fmt = 0;
// Bunch of constant parameter
switch (m_format)
{
// 1 Channel integer
case Format::PrimID:
gl_fmt = GL_R32F;
m_gl_format = GL_R32F;
m_int_format = GL_RED;
m_int_type = GL_INT;
m_int_shift = 2;
break;
case Format::UInt32:
gl_fmt = GL_R32UI;
m_gl_format = GL_R32UI;
m_int_format = GL_RED_INTEGER;
m_int_type = GL_UNSIGNED_INT;
m_int_shift = 2;
break;
case Format::UInt16:
gl_fmt = GL_R16UI;
m_gl_format = GL_R16UI;
m_int_format = GL_RED_INTEGER;
m_int_type = GL_UNSIGNED_SHORT;
m_int_shift = 1;
@@ -57,7 +56,7 @@ GSTextureOGL::GSTextureOGL(Type type, int width, int height, int levels, Format
// 1 Channel normalized
case Format::UNorm8:
gl_fmt = GL_R8;
m_gl_format = GL_R8;
m_int_format = GL_RED;
m_int_type = GL_UNSIGNED_BYTE;
m_int_shift = 0;
@@ -65,7 +64,7 @@ GSTextureOGL::GSTextureOGL(Type type, int width, int height, int levels, Format
// 1 channel float
case Format::DepthColor:
gl_fmt = GL_R32F;
m_gl_format = GL_R32F;
m_int_format = GL_RED;
m_int_type = GL_FLOAT;
m_int_shift = 2;
@@ -75,7 +74,7 @@ GSTextureOGL::GSTextureOGL(Type type, int width, int height, int levels, Format
case Format::Color:
case Format::ColorHQ:
case Format::ColorHDR:
gl_fmt = GL_RGBA8;
m_gl_format = GL_RGBA8;
m_int_format = GL_RGBA;
m_int_type = GL_UNSIGNED_BYTE;
m_int_shift = 2;
@@ -83,7 +82,7 @@ GSTextureOGL::GSTextureOGL(Type type, int width, int height, int levels, Format
// 4 channel float
case Format::ColorClip:
gl_fmt = GL_RGBA16;
m_gl_format = GL_RGBA16;
m_int_format = GL_RGBA;
m_int_type = GL_UNSIGNED_SHORT;
m_int_shift = 3;
@@ -94,14 +93,14 @@ GSTextureOGL::GSTextureOGL(Type type, int width, int height, int levels, Format
{
if (!g_gs_device->Features().framebuffer_fetch)
{
gl_fmt = GL_DEPTH32F_STENCIL8;
m_gl_format = GL_DEPTH32F_STENCIL8;
m_int_format = GL_DEPTH_STENCIL;
m_int_type = GL_FLOAT_32_UNSIGNED_INT_24_8_REV;
m_int_shift = 3; // 4 bytes for depth + 4 bytes for stencil by texels
}
else
{
gl_fmt = GL_DEPTH_COMPONENT32F;
m_gl_format = GL_DEPTH_COMPONENT32F;
m_int_format = GL_DEPTH_COMPONENT;
m_int_type = GL_FLOAT;
m_int_shift = 2;
@@ -110,28 +109,28 @@ GSTextureOGL::GSTextureOGL(Type type, int width, int height, int levels, Format
break;
case Format::BC1:
gl_fmt = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
m_gl_format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
m_int_format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
m_int_type = GL_UNSIGNED_BYTE;
m_int_shift = 1;
break;
case Format::BC2:
gl_fmt = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
m_gl_format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
m_int_format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
m_int_type = GL_UNSIGNED_BYTE;
m_int_shift = 1;
break;
case Format::BC3:
gl_fmt = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
m_gl_format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
m_int_format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
m_int_type = GL_UNSIGNED_BYTE;
m_int_shift = 1;
break;
case Format::BC7:
gl_fmt = GL_COMPRESSED_RGBA_BPTC_UNORM_ARB;
m_gl_format = GL_COMPRESSED_RGBA_BPTC_UNORM_ARB;
m_int_format = GL_COMPRESSED_RGBA_BPTC_UNORM_ARB;
m_int_type = GL_UNSIGNED_BYTE;
m_int_shift = 1;
@@ -157,7 +156,7 @@ GSTextureOGL::GSTextureOGL(Type type, int width, int height, int levels, Format
glTextureParameteri(m_texture_id, GL_TEXTURE_SWIZZLE_A, GL_RED);
}
glTextureStorage2D(m_texture_id, m_mipmap_levels, gl_fmt, m_size.x, m_size.y);
glTextureStorage2D(m_texture_id, m_mipmap_levels, m_gl_format, m_size.x, m_size.y);
}
GSTextureOGL::~GSTextureOGL()
@@ -335,6 +334,8 @@ void GSTextureOGL::SetDebugName(std::string_view name)
if (glObjectLabel)
glObjectLabel(GL_TEXTURE, m_texture_id, static_cast<GLsizei>(name.length()), static_cast<const GLchar*>(name.data()));
m_debug_name = name;
}
#endif
+3
View File
@@ -22,6 +22,7 @@ private:
u32 m_map_offset = 0;
// internal opengl format/type/alignment
GLenum m_gl_format = 0;
GLenum m_int_format = 0;
GLenum m_int_type = 0;
u32 m_int_shift = 0;
@@ -33,6 +34,7 @@ public:
__fi GLenum GetIntFormat() const { return m_int_format; }
__fi GLenum GetIntType() const { return m_int_type; }
__fi u32 GetIntShift() const { return m_int_shift; }
__fi u32 GetGLFormat() const { return m_gl_format; }
void* GetNativeHandle() const override;
@@ -55,6 +57,7 @@ public:
}
__fi u32 GetID() { return m_texture_id; }
__fi u32 GetGLFormat() { return m_gl_format; }
};
class GSDownloadTextureOGL final : public GSDownloadTexture
+221 -39
View File
@@ -444,6 +444,8 @@ bool GSDeviceVK::SelectDeviceExtensions(ExtensionList* extension_list, bool enab
enable_surface && SupportsExtension(VK_EXT_FULL_SCREEN_EXCLUSIVE_EXTENSION_NAME, false);
#endif
m_optional_extensions.vk_ext_fragment_shader_interlock = SupportsExtension(VK_EXT_FRAGMENT_SHADER_INTERLOCK_EXTENSION_NAME, false);
return true;
}
@@ -459,6 +461,7 @@ bool GSDeviceVK::SelectDeviceFeatures()
m_device_features.fragmentStoresAndAtomics = available_features.fragmentStoresAndAtomics;
m_device_features.textureCompressionBC = available_features.textureCompressionBC;
m_device_features.geometryShader = available_features.geometryShader;
m_device_features.fragmentStoresAndAtomics = available_features.fragmentStoresAndAtomics;
return true;
}
@@ -628,6 +631,8 @@ bool GSDeviceVK::CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer
// VK_EXT_swapchain_maintenance1 types/enums are aliases of VK_KHR_swapchain_maintenance1 types/enums.
VkPhysicalDeviceSwapchainMaintenance1FeaturesKHR swapchain_maintenance1_feature = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SWAPCHAIN_MAINTENANCE_1_FEATURES_KHR};
VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT fragment_shader_interlock_ext_feature = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_INTERLOCK_FEATURES_EXT};
if (m_optional_extensions.vk_ext_provoking_vertex)
{
@@ -654,6 +659,11 @@ bool GSDeviceVK::CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer
swapchain_maintenance1_feature.swapchainMaintenance1 = VK_TRUE;
Vulkan::AddPointerToChain(&device_info, &swapchain_maintenance1_feature);
}
if (m_optional_extensions.vk_ext_fragment_shader_interlock)
{
fragment_shader_interlock_ext_feature.fragmentShaderPixelInterlock = VK_TRUE;
Vulkan::AddPointerToChain(&device_info, &fragment_shader_interlock_ext_feature);
}
VkResult res = vkCreateDevice(m_physical_device, &device_info, nullptr, &m_device);
if (res != VK_SUCCESS)
@@ -724,6 +734,8 @@ bool GSDeviceVK::ProcessDeviceExtensions()
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SWAPCHAIN_MAINTENANCE_1_FEATURES_KHR, nullptr, VK_TRUE};
VkPhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT attachment_feedback_loop_feature = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ATTACHMENT_FEEDBACK_LOOP_LAYOUT_FEATURES_EXT};
VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT fragment_shader_interlock_ext_feature = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_INTERLOCK_FEATURES_EXT };
// add in optional feature structs
if (m_optional_extensions.vk_ext_provoking_vertex)
@@ -736,6 +748,8 @@ bool GSDeviceVK::ProcessDeviceExtensions()
Vulkan::AddPointerToChain(&features2, &attachment_feedback_loop_feature);
if (m_optional_extensions.vk_swapchain_maintenance1)
Vulkan::AddPointerToChain(&features2, &swapchain_maintenance1_feature);
if (m_optional_extensions.vk_ext_fragment_shader_interlock)
Vulkan::AddPointerToChain(&features2, &fragment_shader_interlock_ext_feature);
// query
vkGetPhysicalDeviceFeatures2(m_physical_device, &features2);
@@ -813,6 +827,9 @@ bool GSDeviceVK::ProcessDeviceExtensions()
m_optional_extensions.vk_swapchain_maintenance1 &=
(swapchain_maintenance1_feature.swapchainMaintenance1 == VK_TRUE);
m_optional_extensions.vk_ext_fragment_shader_interlock &=
(fragment_shader_interlock_ext_feature.fragmentShaderPixelInterlock == VK_TRUE);
Console.WriteLn(
"VK_EXT_provoking_vertex is %s", m_optional_extensions.vk_ext_provoking_vertex ? "supported" : "NOT supported");
Console.WriteLn(
@@ -830,6 +847,8 @@ bool GSDeviceVK::ProcessDeviceExtensions()
m_optional_extensions.vk_khr_driver_properties ? "supported" : "NOT supported");
Console.WriteLn("VK_EXT_attachment_feedback_loop_layout is %s",
m_optional_extensions.vk_ext_attachment_feedback_loop_layout ? "supported" : "NOT supported");
Console.WriteLn("VK_EXT_fragment_shader_interlock is %s",
m_optional_extensions.vk_ext_fragment_shader_interlock ? "supported" : "NOT supported");
return true;
}
@@ -2100,6 +2119,12 @@ bool GSDeviceVK::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
return false;
}
if ((m_null_framebuffer = GSTextureVK::CreateNullFramebuffer()) == VK_NULL_HANDLE)
{
Host::ReportErrorAsync("GS", "Failed to create dummy framebuffer");
return false;
}
{
std::optional<std::string> shader = ReadShaderSource("shaders/vulkan/tfx.glsl");
if (!shader.has_value())
@@ -2743,6 +2768,9 @@ bool GSDeviceVK::CheckFeatures()
m_max_texture_size = m_device_properties.limits.maxImageDimension2D;
m_features.rov = m_optional_extensions.vk_ext_fragment_shader_interlock &&
m_device_features.fragmentStoresAndAtomics;
return true;
}
@@ -2997,7 +3025,7 @@ void GSDeviceVK::DoMultiStretchRects(
const MultiStretchRect* rects, u32 num_rects, GSTextureVK* dTex, ShaderConvertSelector shader)
{
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
// Set up vertices first.
const u32 vertex_reserve_size = num_rects * 4 * sizeof(GSVertexPT1);
const u32 index_reserve_size = num_rects * 6 * sizeof(u16);
@@ -3487,14 +3515,15 @@ void GSDeviceVK::VSSetIndexBuffer(const void* index, size_t count)
}
void GSDeviceVK::OMSetRenderTargets(
GSTexture* rt, GSTexture* ds, const GSVector4i& scissor, FeedbackLoopFlag feedback_loop)
GSTexture* rt, GSTexture* ds, const GSVector4i& scissor, FeedbackLoopFlag feedback_loop,
const GSVector2i& viewport_size)
{
GSTextureVK* vkRt = static_cast<GSTextureVK*>(rt);
GSTextureVK* vkDs = static_cast<GSTextureVK*>(ds);
pxAssert(vkRt || vkDs);
if (m_current_render_target != vkRt || m_current_depth_target != vkDs ||
m_current_framebuffer_feedback_loop != feedback_loop)
m_current_framebuffer_feedback_loop != feedback_loop ||
m_current_framebuffer == VK_NULL_HANDLE)
{
// framebuffer change or feedback loop enabled/disabled
EndRenderPass();
@@ -3506,12 +3535,16 @@ void GSDeviceVK::OMSetRenderTargets(
(feedback_loop & FeedbackLoopFlag_ReadAndWriteRT) != 0,
(feedback_loop & (FeedbackLoopFlag_ReadAndWriteDepth | FeedbackLoopFlag_ReadDepth)) != 0);
}
else
else if (vkDs)
{
pxAssert(!(feedback_loop & FeedbackLoopFlag_ReadAndWriteRT));
m_current_framebuffer = vkDs->GetLinkedFramebuffer(
nullptr, false, (feedback_loop & (FeedbackLoopFlag_ReadAndWriteDepth | FeedbackLoopFlag_ReadDepth)) != 0);
}
else
{
m_current_framebuffer = m_null_framebuffer;
}
}
else if (InRenderPass())
{
@@ -3656,7 +3689,7 @@ void GSDeviceVK::OMSetRenderTargets(
}
// This is used to set/initialize the framebuffer for tfx rendering.
const GSVector2i size = vkRt ? vkRt->GetSize() : vkDs->GetSize();
const GSVector2i size = (vkRt ? vkRt->GetSize() : (vkDs ? vkDs->GetSize() : viewport_size));
const VkViewport vp{0.0f, 0.0f, static_cast<float>(size.x), static_cast<float>(size.y), 0.0f, 1.0f};
SetViewport(vp);
@@ -3732,6 +3765,11 @@ static void AddShaderHeader(std::stringstream& ss)
ss << "#define DISABLE_TEXTURE_BARRIER 1\n";
if (features.texture_barrier && dev->UseFeedbackLoopLayout())
ss << "#define HAS_FEEDBACK_LOOP_LAYOUT 1\n";
if (features.rov)
{
ss << "#extension GL_ARB_fragment_shader_interlock : require\n";
ss << "#extension GL_ARB_shader_image_load_store : require\n";
}
}
static void AddShaderStageMacro(std::stringstream& ss, bool vs, bool gs, bool fs)
@@ -3905,6 +3943,8 @@ bool GSDeviceVK::CreatePipelineLayouts()
(m_features.texture_barrier && !UseFeedbackLoopLayout()) ? VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT :
VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
1, VK_SHADER_STAGE_FRAGMENT_BIT);
dslb.AddBinding(TFX_TEXTURE_RT_ROV, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
dslb.AddBinding(TFX_TEXTURE_DEPTH_ROV, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
if ((m_tfx_texture_ds_layout = dslb.Create(dev)) == VK_NULL_HANDLE)
return false;
Vulkan::SetObjectName(dev, m_tfx_texture_ds_layout, "TFX texture descriptor layout");
@@ -4739,6 +4779,11 @@ void GSDeviceVK::DestroyResources()
if (m_utility_ds_layout != VK_NULL_HANDLE)
vkDestroyDescriptorSetLayout(m_device, m_utility_ds_layout, nullptr);
if (m_null_framebuffer != VK_NULL_HANDLE)
{
vkDestroyFramebuffer(m_device, m_null_framebuffer, nullptr);
}
if (m_null_texture)
{
m_null_texture->Destroy(false);
@@ -4870,12 +4915,13 @@ VkShaderModule GSDeviceVK::GetTFXFragmentShader(const GSHWDrawConfig::PSSelector
AddMacro(ss, "PS_AA1", static_cast<u32>(sel.aa1));
AddMacro(ss, "PS_ABE", sel.abe);
AddMacro(ss, "PS_ANISOTROPIC_FILTERING", sel.sw_aniso);
AddMacro(ss, "PS_ROV_COLOR", sel.rov_color);
AddMacro(ss, "PS_ROV_DEPTH", static_cast<u32>(sel.rov_depth));
ss << m_tfx_source;
VkShaderModule mod = g_vulkan_shader_cache->GetFragmentShader(ss.str());
if (mod)
Vulkan::SetObjectName(m_device, mod, "TFX Fragment %" PRIX64 "%08X", sel.key_hi, sel.key_lo);
Vulkan::SetObjectName(m_device, mod, "TFX Fragment %016" PRIX64 "_%016" PRIX64, sel.key_hi, sel.key_lo);
m_tfx_fragment_shaders.emplace(sel, mod);
return mod;
@@ -5002,7 +5048,7 @@ VkPipeline GSDeviceVK::CreateTFXPipeline(const PipelineSelector& p)
if (pipeline)
{
Vulkan::SetObjectName(
m_device, pipeline, "TFX Pipeline %08X/%" PRIX64 "%08X", p.vs.key, p.ps.key_hi, p.ps.key_lo);
m_device, pipeline, "TFX Pipeline %08X/%016" PRIX64 "_%016" PRIX64, p.vs.key, p.ps.key_hi, p.ps.key_lo);
}
return pipeline;
@@ -5223,7 +5269,95 @@ void GSDeviceVK::SetLineWidth(float width)
m_dirty_flags |= DIRTY_FLAG_LINE_WIDTH;
}
void GSDeviceVK::PSSetShaderResource(int i, GSTexture* sr, bool check_state)
void GSDeviceVK::PSSetUnorderedAccess(GSTexture* rt, GSTexture* ds, bool write_rt, bool write_ds)
{
GSTextureVK* vkRt = static_cast<GSTextureVK*>(rt);
GSTextureVK* vkDs = static_cast<GSTextureVK*>(ds);
GSTextureVK* oldVkRt = m_tfx_textures[TFX_TEXTURE_RT_ROV] != m_null_texture.get() ?
m_tfx_textures[TFX_TEXTURE_RT_ROV] : nullptr;
GSTextureVK* oldVkDs = m_tfx_textures[TFX_TEXTURE_DEPTH_ROV] != m_null_texture.get() ?
m_tfx_textures[TFX_TEXTURE_DEPTH_ROV] : nullptr;
if (!(vkRt || vkDs || oldVkRt || oldVkDs))
return;
pxAssert(!(vkRt || vkDs) || m_features.rov);
if (vkRt)
{
PSSetShaderResource(TFX_TEXTURE_RT_ROV, vkRt, true, ResourceType::UAV);
// Unbind conflicting RT texture
PSSetShaderResource(TFX_TEXTURE_RT, nullptr, false);
// Unbind conflicting source texture
if (m_tfx_textures[TFX_TEXTURE_TEXTURE] == vkRt)
{
PSSetShaderResource(TFX_TEXTURE_TEXTURE, nullptr, false);
}
if (write_rt)
{
vkRt->SetState(GSTexture::State::Dirty);
}
}
else
{
// Unbind to avoid conflicts with OM targets.
PSSetShaderResource(TFX_TEXTURE_RT_ROV, nullptr, false);
}
if (vkDs)
{
PSSetShaderResource(TFX_TEXTURE_DEPTH_ROV, vkDs, true, ResourceType::UAV);
// Unbind conflicting depth texture
PSSetShaderResource(TFX_TEXTURE_DEPTH, nullptr, false);
// Unbind conflicting source texture
if (m_tfx_textures[TFX_TEXTURE_TEXTURE] == vkDs)
{
PSSetShaderResource(TFX_TEXTURE_TEXTURE, nullptr, false);
}
if (write_ds)
{
vkDs->SetState(GSTexture::State::Dirty);
}
}
else
{
// Unbind to avoid conflicts with OM targets.
PSSetShaderResource(TFX_TEXTURE_DEPTH_ROV, nullptr, false);
}
if (GSConfig.HWROVBarriersVK)
{
// This is to fix issues with some systems that seem to require barriers even with FSI.
// Adds a barriers to the UAVs before every draw. Can result in a large performance hit.
if ((vkRt || vkDs) && InRenderPass())
{
GL_INS("Ending render pass due to UAV barrier");
EndRenderPass();
}
for (GSTextureVK* tex : std::array{ vkRt, vkDs })
{
if (tex)
{
if (InRenderPass())
{
GL_INS("Ending render pass due to UAV barrier");
EndRenderPass();
}
// The subresources version does the barrier even when the before/after layout
// are the same since we want a memory barrier here, not just a layout change.
tex->TransitionSubresourcesToLayout(GetCurrentCommandBuffer(), 0, 1, tex->GetLayout(), tex->GetLayout());
}
}
}
}
void GSDeviceVK::PSSetShaderResource(int i, GSTexture* sr, bool check_state, ResourceType type)
{
GSTextureVK* vkTex = static_cast<GSTextureVK*>(sr);
bool update_layout = false;
@@ -5231,15 +5365,20 @@ void GSDeviceVK::PSSetShaderResource(int i, GSTexture* sr, bool check_state)
{
if (check_state)
{
if (vkTex->GetLayout() != GSTextureVK::Layout::ShaderReadOnly && InRenderPass())
{
GL_INS("Ending render pass due to resource transition");
EndRenderPass();
update_layout = true;
}
const GSTextureVK::Layout layout = GetResourceLayout(type);
vkTex->CommitClear();
vkTex->TransitionToLayout(GSTextureVK::Layout::ShaderReadOnly);
if (vkTex->GetLayout() != layout)
{
update_layout = true;
if (InRenderPass())
{
GL_INS("Ending render pass due to resource transition");
EndRenderPass();
}
vkTex->TransitionToLayout(layout);
}
}
vkTex->SetUseFenceCounter(GetCurrentFenceCounter());
}
@@ -5567,6 +5706,16 @@ bool GSDeviceVK::ApplyTFXState(bool already_execed)
m_tfx_textures[TFX_TEXTURE_DEPTH]->GetVkLayout());
}
}
if (flags & DIRTY_FLAG_TFX_TEXTURE_RT_ROV)
{
dsub.AddImageDescriptorWrite(VK_NULL_HANDLE, TFX_TEXTURE_RT_ROV, m_tfx_textures[TFX_TEXTURE_RT_ROV]->GetView(),
m_tfx_textures[TFX_TEXTURE_RT_ROV]->GetVkLayout(), true);
}
if (flags & DIRTY_FLAG_TFX_TEXTURE_DEPTH_ROV)
{
dsub.AddImageDescriptorWrite(VK_NULL_HANDLE, TFX_TEXTURE_DEPTH_ROV, m_tfx_textures[TFX_TEXTURE_DEPTH_ROV]->GetView(),
m_tfx_textures[TFX_TEXTURE_DEPTH_ROV]->GetVkLayout(), true);
}
dsub.PushUpdate(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, m_tfx_pipeline_layout, TFX_DESCRIPTOR_SET_TEXTURES);
}
@@ -5744,8 +5893,10 @@ GSTextureVK* GSDeviceVK::SetupPrimitiveTrackingDATE(GSHWDrawConfig& config)
void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
{
const GSVector2i rtsize(config.rt ? config.rt->GetSize() : config.ds->GetSize());
GSTextureVK* draw_rt = static_cast<GSTextureVK*>(config.rt);
GSTextureVK* draw_ds = static_cast<GSTextureVK*>(config.ds);
GSTextureVK* draw_rt = config.ps.HasColorROV() ? nullptr : static_cast<GSTextureVK*>(config.rt);
GSTextureVK* draw_ds = config.ps.HasDepthROV() ? nullptr : static_cast<GSTextureVK*>(config.ds);
GSTextureVK* draw_rt_rov = config.ps.HasColorROV() ? static_cast<GSTextureVK*>(config.rt) : nullptr;
GSTextureVK* draw_ds_rov = config.ps.HasDepthROV() ? static_cast<GSTextureVK*>(config.ds) : nullptr;
GSTextureVK* draw_rt_clone = nullptr;
GSTextureVK* colclip_rt = static_cast<GSTextureVK*>(g_gs_device->GetColorClipTexture());
@@ -5756,11 +5907,11 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
// bind textures before checking the render pass, in case we need to transition them
if (config.tex)
{
PSSetShaderResource(0, config.tex, config.tex != config.rt && config.tex != config.ds);
PSSetShaderResource(TFX_TEXTURE_TEXTURE, config.tex, config.tex != config.rt && config.tex != config.ds);
PSSetSampler(config.sampler);
}
if (config.pal)
PSSetShaderResource(1, config.pal, true);
PSSetShaderResource(TFX_TEXTURE_PALETTE, config.pal, true);
if (config.blend.constant_enable)
SetBlendConstants(config.blend.constant);
@@ -5839,6 +5990,7 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
g_gs_device->SetColorClipTexture(nullptr);
colclip_rt = nullptr;
draw_rt = config.ps.HasColorROV() ? nullptr : static_cast<GSTextureVK*>(config.rt);
}
else
{
@@ -5927,25 +6079,31 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
// colclip hw requires blitting.
EndRenderPass();
}
else if (InRenderPass() && (m_current_render_target == draw_rt || m_current_depth_target == draw_ds))
else if (InRenderPass() &&
((draw_rt && m_current_render_target == draw_rt) ||
(draw_ds && m_current_depth_target == draw_ds)))
{
// avoid restarting the render pass just to switch from rt+depth to rt and vice versa
// keep the depth even if doing colclip hw draws, because the next draw will probably re-enable depth
if (!draw_rt && m_current_render_target && config.tex != m_current_render_target &&
m_current_render_target->GetSize() == draw_ds->GetSize())
// keep the depth even if doing colclip hw draws, because the next draw will probably re-enable depth.
if (!(draw_rt || draw_rt_rov) && m_current_render_target && config.tex != m_current_render_target &&
draw_ds && m_current_render_target->GetSize() == draw_ds->GetSize())
{
draw_rt = m_current_render_target;
m_pipeline_selector.rt = true;
}
else if (!draw_ds && m_current_depth_target && config.tex != m_current_depth_target &&
m_current_depth_target->GetSize() == draw_rt->GetSize())
else if (!(draw_ds || draw_ds_rov) && m_current_depth_target && config.tex != m_current_depth_target &&
draw_rt && m_current_depth_target->GetSize() == draw_rt->GetSize())
{
draw_ds = m_current_depth_target;
m_pipeline_selector.ds = true;
}
// Prefer keeping feedback loop enabled, that way we're not constantly restarting render passes
pipe.feedback_loop_flags |= m_current_framebuffer_feedback_loop;
if (draw_rt)
pipe.feedback_loop_flags |= m_current_framebuffer_feedback_loop & FeedbackLoopFlag_ReadAndWriteRT;
if (draw_ds)
pipe.feedback_loop_flags |= (m_current_framebuffer_feedback_loop &
(FeedbackLoopFlag_ReadAndWriteDepth | FeedbackLoopFlag_ReadDepth));
}
if (draw_rt && ((config.require_one_barrier && (config.IsFeedbackLoopRT(config.ps) || config.IsFeedbackLoopRT(config.alpha_second_pass.ps)))) && !m_features.texture_barrier)
@@ -5992,25 +6150,46 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
Console.Warning("VK: Failed to allocate temp texture for RT copy.");
}
OMSetRenderTargets(draw_rt, draw_ds, config.scissor, static_cast<FeedbackLoopFlag>(pipe.feedback_loop_flags));
if (pipe.IsRTFeedbackLoop())
if (pipe.IsRTFeedbackLoop() && draw_rt)
{
pxAssertMsg(m_features.texture_barrier, "Texture barriers enabled");
PSSetShaderResource(TFX_TEXTURE_RT, draw_rt, false);
if (m_tfx_textures[TFX_TEXTURE_RT_ROV] == draw_rt)
{
// Unbind to avoid conflicts.
PSSetShaderResource(TFX_TEXTURE_RT_ROV, nullptr, false);
m_dirty_flags |= static_cast<u32>(DIRTY_FLAG_TFX_TEXTURE_RT_ROV);
}
}
else if (!draw_rt_clone)
{
PSSetShaderResource(TFX_TEXTURE_RT, nullptr, false); // Unbind to avoid layout conflicts
// Unbind to avoid conflicts with framebuffer
PSSetShaderResource(TFX_TEXTURE_RT, nullptr, false);
}
if (pipe.IsDepthFeedbackLoop())
if (pipe.IsDepthFeedbackLoop() && draw_ds)
{
pxAssertMsg(m_features.texture_barrier, "Texture barriers enabled");
PSSetShaderResource(TFX_TEXTURE_DEPTH, draw_ds, false);
if (m_tfx_textures[TFX_TEXTURE_DEPTH_ROV] == draw_ds)
{
// Unbind to avoid conflicts.
PSSetShaderResource(TFX_TEXTURE_DEPTH_ROV, nullptr, false);
m_dirty_flags |= static_cast<u32>(DIRTY_FLAG_TFX_TEXTURE_DEPTH_ROV);
}
}
else
{
PSSetShaderResource(TFX_TEXTURE_DEPTH, nullptr, false); // Unbind to avoid layout conflicts
// Unbind to avoid conflicts with framebuffer
PSSetShaderResource(TFX_TEXTURE_DEPTH, nullptr, false);
}
PSSetUnorderedAccess(draw_rt_rov, draw_ds_rov, config.ps.HasColorOutput(), config.ps.HasDepthROVWrite());
OMSetRenderTargets(draw_rt, draw_ds, config.scissor, static_cast<FeedbackLoopFlag>(pipe.feedback_loop_flags), rtsize);
// Begin render pass if new target or out of the area.
if (!InRenderPass())
{
@@ -6188,13 +6367,13 @@ void GSDeviceVK::UpdateHWPipelineSelector(GSHWDrawConfig& config, PipelineSelect
pipe.vs.key = config.vs.key;
pipe.ps.key_hi = config.ps.key_hi;
pipe.ps.key_lo = config.ps.key_lo;
pipe.dss.key = config.depth.key;
pipe.bs.key = config.blend.key;
pipe.dss.key = config.ps.HasDepthROV() ? GSHWDrawConfig::DepthStencilSelector::NoDepth().key : config.depth.key;
pipe.bs.key = config.ps.HasColorROV() ? GSHWDrawConfig::BlendState().key : config.blend.key;
pipe.bs.constant = 0; // don't dupe states with different alpha values
pipe.cms.key = config.colormask.key;
pipe.cms.key = config.ps.HasColorROV() ? GSHWDrawConfig::ColorMaskSelector().key : config.colormask.key;
pipe.topology = static_cast<u32>(config.topology);
pipe.rt = config.rt != nullptr;
pipe.ds = config.ds != nullptr;
pipe.rt = config.rt != nullptr && !config.ps.HasColorROV();
pipe.ds = config.ds != nullptr && !config.ps.HasDepthROV();
pipe.line_width = config.line_expand;
pipe.feedback_loop_flags = FeedbackLoopFlag_None;
if (m_features.texture_barrier && (config.require_one_barrier || config.require_full_barrier))
@@ -6205,7 +6384,7 @@ void GSDeviceVK::UpdateHWPipelineSelector(GSHWDrawConfig& config, PipelineSelect
if (config.IsFeedbackLoopDepth(config.ps))
pipe.feedback_loop_flags |= FeedbackLoopFlag_ReadAndWriteDepth;
}
if (!(pipe.feedback_loop_flags & FeedbackLoopFlag_ReadAndWriteDepth))
if (pipe.ds && !(pipe.feedback_loop_flags & FeedbackLoopFlag_ReadAndWriteDepth))
{
pipe.feedback_loop_flags |= (config.tex && config.tex == config.ds) ? FeedbackLoopFlag_ReadDepth : FeedbackLoopFlag_None;
}
@@ -6339,4 +6518,7 @@ void GSDeviceVK::SendHWDraw(const GSHWDrawConfig& config, GSTextureVK* draw_rt,
}
Draw(config);
if (config.ps.HasColorROV() || config.ps.HasDepthROV())
g_perfmon.Put(GSPerfMon::DrawCallsROV, 1);
}
+46 -19
View File
@@ -46,6 +46,7 @@ public:
bool vk_khr_driver_properties : 1;
bool vk_khr_shader_non_semantic_info : 1;
bool vk_ext_attachment_feedback_loop_layout : 1;
bool vk_ext_fragment_shader_interlock : 1;
};
// Global state accessors
@@ -61,8 +62,8 @@ public:
// The interaction between raster order attachment access and fbfetch is unclear.
__fi bool UseFeedbackLoopLayout() const
{
return (m_optional_extensions.vk_ext_attachment_feedback_loop_layout &&
!m_optional_extensions.vk_ext_rasterization_order_attachment_access);
return m_optional_extensions.vk_ext_attachment_feedback_loop_layout &&
!m_optional_extensions.vk_ext_rasterization_order_attachment_access;
}
// Helpers for getting constants
@@ -301,6 +302,25 @@ public:
FeedbackLoopFlag_ReadAndWriteDepth = 4,
};
enum class ResourceType
{
SRV, // Shader resource view (read only)
UAV, // Unordered access (read/write)
};
static constexpr GSTextureVK::Layout GetResourceLayout(ResourceType type)
{
switch (type)
{
default:
pxFailRel("Impossible.");
case ResourceType::SRV:
return GSTextureVK::Layout::ShaderReadOnly;
case ResourceType::UAV:
return GSTextureVK::Layout::ReadWriteImage;
}
}
struct alignas(8) PipelineSelector
{
GSHWDrawConfig::PSSelector ps;
@@ -334,7 +354,7 @@ public:
__fi bool IsDepthFeedbackLoop() const { return ((feedback_loop_flags & FeedbackLoopFlag_ReadAndWriteDepth) != 0); }
__fi bool IsTestingAndSamplingDepth() const { return ((feedback_loop_flags & (FeedbackLoopFlag_ReadDepth | FeedbackLoopFlag_ReadAndWriteDepth)) != 0); }
};
static_assert(sizeof(PipelineSelector) == 24, "Pipeline selector is 24 bytes");
static_assert(sizeof(PipelineSelector) == 32, "Pipeline selector is 32 bytes");
struct PipelineSelectorHash
{
@@ -368,6 +388,8 @@ public:
TFX_TEXTURE_RT,
TFX_TEXTURE_PRIMID,
TFX_TEXTURE_DEPTH,
TFX_TEXTURE_RT_ROV,
TFX_TEXTURE_DEPTH_ROV,
NUM_TFX_TEXTURES
};
@@ -586,11 +608,12 @@ public:
void IASetIndexBuffer(const void* index, size_t count);
void VSSetIndexBuffer(const void* index, size_t count);
void PSSetShaderResource(int i, GSTexture* sr, bool check_state);
void PSSetUnorderedAccess(GSTexture* rt, GSTexture* ds, bool write_rt, bool write_ds);
void PSSetShaderResource(int i, GSTexture* sr, bool check_state, ResourceType type = ResourceType::SRV);
void PSSetSampler(GSHWDrawConfig::SamplerSelector sel);
void OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i& scissor,
FeedbackLoopFlag feedback_loop = FeedbackLoopFlag_None);
FeedbackLoopFlag feedback_loop = FeedbackLoopFlag_None, const GSVector2i& viewport_size = {});
void SetVSConstantBuffer(const GSHWDrawConfig::VSConstantBuffer& cb);
void SetPSConstantBuffer(const GSHWDrawConfig::PSConstantBuffer& cb);
@@ -654,28 +677,31 @@ public:
private:
enum DIRTY_FLAG : u32
{
DIRTY_FLAG_TFX_TEXTURE_0 = (1 << 0), // 0, 1, 2, 3, 4
DIRTY_FLAG_TFX_UBO = (1 << 5),
DIRTY_FLAG_UTILITY_TEXTURE = (1 << 6),
DIRTY_FLAG_BLEND_CONSTANTS = (1 << 7),
DIRTY_FLAG_LINE_WIDTH = (1 << 8),
DIRTY_FLAG_INDEX_BUFFER = (1 << 9),
DIRTY_FLAG_VIEWPORT = (1 << 10),
DIRTY_FLAG_SCISSOR = (1 << 11),
DIRTY_FLAG_PIPELINE = (1 << 12),
DIRTY_FLAG_VS_CONSTANT_BUFFER = (1 << 13),
DIRTY_FLAG_PS_CONSTANT_BUFFER = (1 << 14),
DIRTY_FLAG_VS_PUSH_CONSTANTS = (1 << 15),
DIRTY_FLAG_TFX_TEXTURE_0 = (1 << 0), // 0, 1, 2, 3, 4, 5, 6
DIRTY_FLAG_TFX_UBO = (1 << 7),
DIRTY_FLAG_UTILITY_TEXTURE = (1 << 8),
DIRTY_FLAG_BLEND_CONSTANTS = (1 << 9),
DIRTY_FLAG_LINE_WIDTH = (1 << 10),
DIRTY_FLAG_INDEX_BUFFER = (1 << 11),
DIRTY_FLAG_VIEWPORT = (1 << 12),
DIRTY_FLAG_SCISSOR = (1 << 13),
DIRTY_FLAG_PIPELINE = (1 << 14),
DIRTY_FLAG_VS_CONSTANT_BUFFER = (1 << 15),
DIRTY_FLAG_PS_CONSTANT_BUFFER = (1 << 16),
DIRTY_FLAG_VS_PUSH_CONSTANTS = (1 << 17),
DIRTY_FLAG_TFX_TEXTURE_TEX = (DIRTY_FLAG_TFX_TEXTURE_0 << 0),
DIRTY_FLAG_TFX_TEXTURE_PALETTE = (DIRTY_FLAG_TFX_TEXTURE_0 << 1),
DIRTY_FLAG_TFX_TEXTURE_RT = (DIRTY_FLAG_TFX_TEXTURE_0 << 2),
DIRTY_FLAG_TFX_TEXTURE_PRIMID = (DIRTY_FLAG_TFX_TEXTURE_0 << 3),
DIRTY_FLAG_TFX_TEXTURE_DEPTH = (DIRTY_FLAG_TFX_TEXTURE_0 << 4),
DIRTY_FLAG_TFX_TEXTURE_RT_ROV = (DIRTY_FLAG_TFX_TEXTURE_0 << 5),
DIRTY_FLAG_TFX_TEXTURE_DEPTH_ROV = (DIRTY_FLAG_TFX_TEXTURE_0 << 6),
DIRTY_FLAG_TFX_TEXTURES = DIRTY_FLAG_TFX_TEXTURE_TEX | DIRTY_FLAG_TFX_TEXTURE_PALETTE |
DIRTY_FLAG_TFX_TEXTURE_RT | DIRTY_FLAG_TFX_TEXTURE_PRIMID |
DIRTY_FLAG_TFX_TEXTURE_DEPTH,
DIRTY_FLAG_TFX_TEXTURE_DEPTH | DIRTY_FLAG_TFX_TEXTURE_RT_ROV |
DIRTY_FLAG_TFX_TEXTURE_DEPTH_ROV,
DIRTY_BASE_STATE = DIRTY_FLAG_INDEX_BUFFER | DIRTY_FLAG_PIPELINE | DIRTY_FLAG_VIEWPORT | DIRTY_FLAG_SCISSOR |
DIRTY_FLAG_BLEND_CONSTANTS | DIRTY_FLAG_LINE_WIDTH,
@@ -716,7 +742,7 @@ private:
float m_current_line_width = 1.0f;
u8 m_blend_constant_color = 0;
std::array<const GSTextureVK*, NUM_TFX_TEXTURES> m_tfx_textures{};
std::array<GSTextureVK*, NUM_TFX_TEXTURES> m_tfx_textures{};
VkSampler m_tfx_sampler = VK_NULL_HANDLE;
u32 m_tfx_sampler_sel = 0;
VkDescriptorSet m_tfx_ubo_descriptor_set = VK_NULL_HANDLE;
@@ -732,6 +758,7 @@ private:
VkPipeline m_current_pipeline = VK_NULL_HANDLE;
std::unique_ptr<GSTextureVK> m_null_texture;
VkFramebuffer m_null_framebuffer;
// current pipeline selector - we save this in the struct to avoid re-zeroing it every draw
PipelineSelector m_pipeline_selector = {};
+53 -3
View File
@@ -11,6 +11,24 @@
#include "common/Console.h"
#include "common/BitUtils.h"
VkFramebuffer GSTextureVK::CreateNullFramebuffer()
{
const VkRenderPass rp = GSDeviceVK::GetInstance()->GetRenderPass(
VK_FORMAT_UNDEFINED,
VK_FORMAT_UNDEFINED,
VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_ATTACHMENT_LOAD_OP_DONT_CARE,
VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, false, false);
if (!rp)
return VK_NULL_HANDLE;
Vulkan::FramebufferBuilder fbb;
fbb.SetSize(16384, 16384, 1);
fbb.SetRenderPass(rp);
return fbb.Create(GSDeviceVK::GetInstance()->GetDevice());
}
static constexpr const VkComponentMapping s_identity_swizzle{VK_COMPONENT_SWIZZLE_IDENTITY,
VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY};
@@ -101,7 +119,7 @@ std::unique_ptr<GSTextureVK> GSTextureVK::Create(Type type, Format format, int w
pxAssert(levels == 1);
ici.usage =
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT |
(GSDeviceVK::GetInstance()->UseFeedbackLoopLayout() ? VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT
: VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT);
}
@@ -239,6 +257,10 @@ void GSTextureVK::Destroy(bool defer)
m_image = VK_NULL_HANDLE;
m_allocation = VK_NULL_HANDLE;
}
#ifdef PCSX2_DEVBUILD
m_debug_name.clear();
#endif
}
VkImageLayout GSTextureVK::GetVkLayout() const
@@ -523,6 +545,8 @@ void GSTextureVK::SetDebugName(std::string_view name)
Vulkan::SetObjectName(GSDeviceVK::GetInstance()->GetDevice(), m_image, "%.*s", static_cast<int>(name.size()), name.data());
Vulkan::SetObjectName(GSDeviceVK::GetInstance()->GetDevice(), m_view, "%.*s", static_cast<int>(name.size()), name.data());
m_debug_name = name;
}
#endif
@@ -547,13 +571,17 @@ void GSTextureVK::CommitClear(VkCommandBuffer cmdbuf)
const VkImageSubresourceRange srr = {VK_IMAGE_ASPECT_DEPTH_BIT, 0u, 1u, 0u, 1u};
vkCmdClearDepthStencilImage(cmdbuf, m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &cv, 1, &srr);
}
else
else if (IsRenderTarget())
{
alignas(16) VkClearColorValue cv;
GSVector4::store<true>(cv.float32, GetClearForFormat());
const VkImageSubresourceRange srr = {VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u};
vkCmdClearColorImage(cmdbuf, m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &cv, 1, &srr);
}
else
{
pxFailRel("Illegal texture type for clear.");
}
SetState(GSTexture::State::Dirty);
}
@@ -581,8 +609,22 @@ void GSTextureVK::TransitionToLayout(VkCommandBuffer command_buffer, Layout new_
void GSTextureVK::TransitionSubresourcesToLayout(
VkCommandBuffer command_buffer, int start_level, int num_levels, Layout old_layout, Layout new_layout)
{
// Windows RDNA2 drivers don't always correctly transition the layout(?) when ROV is involved.
// ReadWriteImage -> Feedback transitions are broken.
// ReadWriteImage -> Read only layout -> Feedback transitions are broken.
// ReadWriteImage -> General layout -> Feedback transitions are broken.
// ReadWriteImage -> Write only layout -> Feedback transitions works fine.
// Not every broken transition gives broken rendering, the Shadow of the colossus eagle dump is fine after the 1st frame.
// Transition to a write only layout using an extra barrier, then to feedback fixes this issue.
if (old_layout == Layout::ReadWriteImage && new_layout != Layout::ColorAttachment && new_layout != Layout::ReadWriteImage)
{
GL_INS("VK: Doing extra transition for broken RDNA2 feedback transitions");
TransitionSubresourcesToLayout(command_buffer, 0, num_levels, old_layout, Layout::ColorAttachment);
old_layout = Layout::ColorAttachment;
}
VkImageAspectFlags aspect;
if (m_type == Type::DepthStencil)
if (IsDepthStencil())
{
aspect = g_gs_device->Features().stencil_buffer ? (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT) :
VK_IMAGE_ASPECT_DEPTH_BIT;
@@ -754,6 +796,14 @@ void GSTextureVK::TransitionSubresourcesToLayout(
break;
}
vkCmdPipelineBarrier(command_buffer, srcStageMask, dstStageMask, 0, 0, nullptr, 0, nullptr, 1, &barrier);
// Count as a UAV barrier if we transition to/from UAV.
if (IsRenderTargetOrDepthStencil() &&
(old_layout == Layout::ReadWriteImage || new_layout == Layout::ReadWriteImage))
{
g_perfmon.Put(GSPerfMon::Barriers, 1);
g_perfmon.Put(GSPerfMon::BarriersROV, 1);
}
}
VkFramebuffer GSTextureVK::GetFramebuffer(bool feedback_loop)
+4
View File
@@ -42,6 +42,8 @@ public:
__fi VkImage GetImage() const { return m_image; }
__fi VkImageView GetView() const { return m_view; }
__fi Layout GetLayout() const { return m_layout; }
virtual bool IsUnorderedAccess() const override { return GetLayout() == Layout::ReadWriteImage; }
__fi VkFormat GetVkFormat() const { return m_vk_format; }
VkImageLayout GetVkLayout() const;
@@ -70,6 +72,8 @@ public:
void TransitionSubresourcesToLayout(
VkCommandBuffer command_buffer, int start_level, int num_levels, Layout old_layout, Layout new_layout);
static VkFramebuffer CreateNullFramebuffer();
/// Framebuffers are lazily allocated.
VkFramebuffer GetFramebuffer(bool feedback_loop);
+2 -2
View File
@@ -733,7 +733,7 @@ void Vulkan::DescriptorSetUpdateBuilder::PushUpdate(
}
void Vulkan::DescriptorSetUpdateBuilder::AddImageDescriptorWrite(VkDescriptorSet set, u32 binding, VkImageView view,
VkImageLayout layout /*= VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL*/)
VkImageLayout layout /*= VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL*/, bool storage_image /*= false*/)
{
pxAssert(m_num_writes < MAX_WRITES && m_num_image_infos < MAX_IMAGE_INFOS);
@@ -747,7 +747,7 @@ void Vulkan::DescriptorSetUpdateBuilder::AddImageDescriptorWrite(VkDescriptorSet
dw.dstSet = set;
dw.dstBinding = binding;
dw.descriptorCount = 1;
dw.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
dw.descriptorType = storage_image ? VK_DESCRIPTOR_TYPE_STORAGE_IMAGE : VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
dw.pImageInfo = &ii;
}
+1 -1
View File
@@ -241,7 +241,7 @@ namespace Vulkan
bool clear = true);
void AddImageDescriptorWrite(VkDescriptorSet set, u32 binding, VkImageView view,
VkImageLayout layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
VkImageLayout layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, bool storage_image = false);
void AddSamplerDescriptorWrite(VkDescriptorSet set, u32 binding, VkSampler sampler);
void AddSamplerDescriptorWrites(VkDescriptorSet set, u32 binding, const VkSampler* samplers, u32 num_samplers);
void AddCombinedImageSamplerDescriptorWrite(VkDescriptorSet set, u32 binding, VkImageView view,
+6
View File
@@ -1357,6 +1357,12 @@ bool GameList::DownloadCovers(const std::vector<std::string>& url_templates, boo
return false;
}
if (!FileSystem::CreateDirectoryPath(EmuFolders::Covers.c_str(), false))
{
progress->DisplayError(fmt::format("Failed to create covers directory: {}", EmuFolders::Covers).c_str());
return false;
}
std::vector<std::pair<std::string, std::string>> download_urls;
{
std::unique_lock lock(s_mutex);
+3
View File
@@ -75,6 +75,9 @@ namespace Host
/// Copies the provided text to the host's clipboard, if present.
bool CopyTextToClipboard(const std::string_view text);
/// Returns the current text from the host's clipboard, if present.
std::string GetTextFromClipboard();
/// Requests settings reset. Can be called from any thread, will call back and apply on the CPU thread.
bool RequestResetSettings(bool folders, bool core, bool controllers, bool hotkeys, bool ui);
File diff suppressed because it is too large Load Diff
+18
View File
@@ -9,6 +9,8 @@
#include "common/Timer.h"
#include "Input/InputManager.h"
#include <thread>
#define TR_CONTEXT "FullscreenUI"
template <size_t L>
@@ -215,6 +217,10 @@ namespace FullscreenUI
void CopyTextToClipboard(std::string title, const std::string_view text);
void DrawAboutWindow();
void OpenAboutWindow();
void DrawCoverDownloaderWindow();
void OpenCoverDownloaderWindow();
void CloseCoverDownloaderWindow();
void CoverDownloaderThreadFunc(const std::vector<std::string>& urls);
void GetStandardSelectionFooterText(SmallStringBase& dest, bool back_instead_of_cancel);
void ApplyLayoutSettings(const SettingsInterface* bsi = nullptr);
@@ -239,6 +245,18 @@ namespace FullscreenUI
inline char s_achievements_login_password[256] = {};
inline Achievements::LoginRequestReason s_achievements_login_reason = Achievements::LoginRequestReason::UserInitiated;
// cover downloader dialog state
inline bool s_cover_downloader_open = false;
inline std::array<char, 4096> s_cover_downloader_urls_buffer = {};
inline bool s_cover_downloader_use_title_filenames = false;
inline bool s_cover_downloader_downloading = false;
inline std::unique_ptr<std::thread> s_cover_downloader_thread;
inline std::mutex s_cover_downloader_mutex;
inline std::string s_cover_downloader_status;
inline bool s_cover_downloader_has_error = false;
inline s32 s_cover_downloader_progress_max = 0;
inline s32 s_cover_downloader_progress_value = 0;
// local copies of the currently-running game
inline std::string s_current_game_title;
inline std::string s_current_game_subtitle;
+587 -3
View File
@@ -26,6 +26,7 @@
#endif
#include "common/Console.h"
#include "common/Error.h"
#include "common/FileSystem.h"
#include "common/Path.h"
#include "common/SettingsInterface.h"
@@ -2488,6 +2489,10 @@ void FullscreenUI::DrawBIOSSettingsPage()
MenuHeading(FSUI_CSTR("Fast Boot Options"));
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_FORWARD_FAST, "Fast Boot"), FSUI_CSTR("Skips the intro screen, and bypasses region checks."),
"EmuCore", "EnableFastBoot", true);
const bool fast_boot_enabled = GetEffectiveBoolSetting(bsi, "EmuCore", "EnableFastBoot", true);
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_FORWARD, "Fast Forward Boot"),
FSUI_CSTR("Removes emulation speed throttle until the game starts to reduce startup time."),
"EmuCore", "EnableFastBootFastForward", false, fast_boot_enabled);
EndMenuButtons();
}
@@ -2862,6 +2867,7 @@ void FullscreenUI::DrawGraphicsSettingsPage(SettingsInterface* bsi, bool show_ad
};
static constexpr const char* s_hw_download[] = {
FSUI_NSTR("Accurate (Recommended)"),
FSUI_NSTR("Accurate Force Full (Can Reduce Readbacks)"),
FSUI_NSTR("Disable Readbacks (Synchronize GS Thread)"),
FSUI_NSTR("Unsynchronized (Non-Deterministic)"),
FSUI_NSTR("Disabled (Ignore Transfers)"),
@@ -2973,6 +2979,9 @@ void FullscreenUI::DrawGraphicsSettingsPage(SettingsInterface* bsi, bool show_ad
FSUI_CSTR("Selects the algorithm used to convert the PS2's interlaced output to progressive for display."), "EmuCore/GS",
"deinterlace_mode", static_cast<int>(GSInterlaceMode::Automatic), s_deinterlacing_options, std::size(s_deinterlacing_options),
true);
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_TV, "Disable Interlace Offset"),
FSUI_CSTR("Disables interlacing offset which may reduce blurring in some situations."), "EmuCore/GS",
"disable_interlace_offset", false);
DrawIntListSetting(bsi, FSUI_ICONSTR(ICON_FA_ARROWS_UP_DOWN_LEFT_RIGHT, "Screenshot Size"), FSUI_CSTR("Determines the resolution at which screenshots will be saved."),
"EmuCore/GS", "ScreenshotSize", static_cast<int>(GSScreenshotSize::WindowResolution), s_screenshot_sizes,
std::size(s_screenshot_sizes), true);
@@ -3168,6 +3177,9 @@ void FullscreenUI::DrawGraphicsSettingsPage(SettingsInterface* bsi, bool show_ad
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_PALETTE, "GPU Palette Conversion"),
FSUI_CSTR("When enabled GPU converts colormap-textures, otherwise the CPU will. It is a trade-off between GPU and CPU."),
"EmuCore/GS", "paltex", false, manual_hw_fixes);
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_LAYER_GROUP, "Draw Buffering"),
FSUI_CSTR("Attempts to reduce draw calls in games which do heavy context switching for blending purposes."),
"EmuCore/GS", "UserHacks_DrawBuffering", false, manual_hw_fixes);
MenuHeading(FSUI_CSTR("Upscaling Fixes"));
DrawIntListSetting(bsi, FSUI_ICONSTR(ICON_FA_ARROWS_UP_DOWN_LEFT_RIGHT, "Half Pixel Offset"), FSUI_CSTR("Adjusts vertices relative to upscaling."), "EmuCore/GS",
@@ -3283,6 +3295,13 @@ void FullscreenUI::DrawGraphicsSettingsPage(SettingsInterface* bsi, bool show_ad
if (show_advanced_settings)
{
MenuHeading(FSUI_CSTR("Advanced"));
if (is_hardware && effective_renderer != GSRendererType::OGL)
{
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_LAYER_GROUP, "Rasterizer Ordered View"),
FSUI_CSTR("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."),
"EmuCore/GS", "HWROV", true);
}
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_FORWARD, "Skip Presenting Duplicate Frames"),
FSUI_CSTR("Skips displaying frames that don't change in 25/30fps games. Can improve speed, but increase input lag/make frame pacing "
"worse."),
@@ -3315,6 +3334,9 @@ void FullscreenUI::DrawGraphicsSettingsPage(SettingsInterface* bsi, bool show_ad
"EmuCore/GS", "DisableShaderCache", false);
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_BAN, "Disable Vertex Shader Expand"), FSUI_CSTR("Falls back to the CPU for expanding sprites/lines."),
"EmuCore/GS", "DisableVertexShaderExpand", false);
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_ROAD_BARRIER, "ROV Barriers Vulkan"),
FSUI_CSTR("Forces extra barriers when using ROV with Vulkan to fix graphical issues present in some games and hardware configurations."),
"EmuCore/GS", "HWROVBarriersVK", false);
DrawIntListSetting(bsi, FSUI_ICONSTR(ICON_FA_DOWNLOAD, "Texture Preloading"),
FSUI_CSTR(
"Uploads full textures to the GPU on use, rather than only the utilized regions. Can improve performance in some games."),
@@ -3476,10 +3498,10 @@ void FullscreenUI::DrawAudioSettingsPage()
DrawIntRangeSetting(bsi, FSUI_ICONSTR(ICON_FA_VOLUME_HIGH, "Standard Volume"),
FSUI_CSTR("Controls the volume of the audio played on the host at normal speed."), "SPU2/Output", "StandardVolume", 100,
0, 100, "%d%%");
0, 200, "%d%%");
DrawIntRangeSetting(bsi, FSUI_ICONSTR(ICON_FA_FORWARD_FAST, "Fast Forward Volume"),
FSUI_CSTR("Controls the volume of the audio played on the host when fast forwarding."), "SPU2/Output",
"FastForwardVolume", 100, 0, 100, "%d%%");
"FastForwardVolume", 100, 0, 200, "%d%%");
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_VOLUME_XMARK, "Mute All Sound"),
FSUI_CSTR("Prevents the emulator from producing any audible sound."), "SPU2/Output", "OutputMuted",
false);
@@ -3533,6 +3555,31 @@ void FullscreenUI::DrawMemoryCardSettingsPage()
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_MAGNIFYING_GLASS, "Folder Memory Card Filter"),
FSUI_CSTR("Simulates a larger memory card by filtering saves only to the current game."), "EmuCore", "McdFolderAutoManage", true);
if (MenuButton(FSUI_ICONSTR(ICON_FA_ARROWS_ROTATE, "Swap Memory Cards"), FSUI_CSTR("Swaps the selected memory cards in Slot 1 and Slot 2.")))
{
SettingsInterface* ebsi = GetEditingSettingsInterface();
std::string card1;
if (!ebsi->GetStringValue("MemoryCards", "Slot1_Filename", &card1))
card1 = Host::GetBaseStringSettingValue("MemoryCards", "Slot1_Filename", FileMcd_GetDefaultName(0).c_str());
std::string card2;
if (!ebsi->GetStringValue("MemoryCards", "Slot2_Filename", &card2))
card2 = Host::GetBaseStringSettingValue("MemoryCards", "Slot2_Filename", FileMcd_GetDefaultName(1).c_str());
if (card1.empty() || card2.empty())
{
ShowToast(std::string(), FSUI_STR("Both slots must have a card selected to swap."));
}
else
{
ebsi->SetStringValue("MemoryCards", "Slot1_Filename", card2.c_str());
ebsi->SetStringValue("MemoryCards", "Slot2_Filename", card1.c_str());
SetSettingsChanged(ebsi);
ShowToast(std::string(), FSUI_STR("Swapped Slot 1 and Slot 2 memory cards."));
}
}
for (u32 port = 0; port < NUM_MEMORY_CARD_PORTS; port++)
{
SmallString str;
@@ -4285,6 +4332,512 @@ void FullscreenUI::DoCreateMemoryCard(std::string name, MemoryCardType type, Mem
ShowToast(std::string(), fmt::format(FSUI_FSTR("Memory Card '{}' created."), name));
}
void FullscreenUI::DrawAchievementsLoginWindow()
{
if (s_achievements_login_open && !ImGui::IsPopupOpen("RetroAchievements"))
ImGui::OpenPopup("RetroAchievements");
const float dialog_width = std::clamp(LayoutScale(420.0f), 300.0f, ImGui::GetIO().DisplaySize.x);
ImGui::SetNextWindowSizeConstraints(ImVec2(dialog_width, 0.0f), ImVec2(dialog_width, ImGui::GetIO().DisplaySize.y));
ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(ImGuiFullscreen::LAYOUT_WINDOW_ROUNDING));
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, LayoutScale(24.0f, 24.0f));
ImGui::PushStyleColor(ImGuiCol_PopupBg, ImVec4(0.13f, 0.13f, 0.13f, 0.95f));
if (ImGui::BeginPopupModal("RetroAchievements", &s_achievements_login_open, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize))
{
const float content_width = ImGui::GetContentRegionAvail().x;
ImGui::PushFont(g_large_font.first, g_large_font.second);
const float icon_height = LayoutScale(24.0f);
const float icon_width = icon_height * (500.0f / 275.0f);
GSTexture* ra_icon = GetCachedSvgTextureAsync("icons/ra-icon.svg", ImVec2(icon_width, icon_height));
const float title_width = ImGui::CalcTextSize("RetroAchievements").x;
const float header_width = (ra_icon ? icon_width + LayoutScale(10.0f) : 0.0f) + title_width;
const float header_start = (content_width - header_width) * 0.5f;
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + header_start);
if (ra_icon)
{
ImGui::Image(reinterpret_cast<ImTextureID>(ra_icon->GetNativeHandle()),
ImVec2(icon_width, icon_height));
ImGui::SameLine();
}
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(1.0f));
ImGui::TextUnformatted(FSUI_CSTR("RetroAchievements"));
ImGui::PopFont();
ImGui::Spacing();
ImGui::Spacing();
ImGui::PushTextWrapPos(content_width);
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.8f, 0.8f, 0.8f, 1.0f));
ImGui::TextWrapped("%s", FSUI_CSTR("Please enter your user name and password for retroachievements.org below.\n\nYour password will not be saved in PCSX2, an access token will be generated and used instead."));
ImGui::PopStyleColor();
ImGui::PopTextWrapPos();
ImGui::Spacing();
ImGui::Spacing();
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, LayoutScale(ImGuiFullscreen::LAYOUT_FRAME_ROUNDING));
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, LayoutScale(12.0f, 10.0f));
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, LayoutScale(1.0f));
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.2f, 0.2f, 0.2f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(0.25f, 0.25f, 0.25f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(0.3f, 0.3f, 0.3f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.4f, 0.4f, 0.4f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
if (s_achievements_login_logging_in || s_achievements_login_show_dismiss)
ImGui::BeginDisabled();
ImGui::SetNextItemWidth(content_width);
ImGui::InputTextWithHint("##username", FSUI_CSTR("Username"), s_achievements_login_username, sizeof(s_achievements_login_username));
ImGui::Spacing();
ImGui::SetNextItemWidth(content_width);
ImGui::InputTextWithHint("##password", FSUI_CSTR("Password"), s_achievements_login_password, sizeof(s_achievements_login_password), ImGuiInputTextFlags_Password);
ImGui::PopStyleColor(5);
ImGui::PopStyleVar(3);
if (s_achievements_login_logging_in || s_achievements_login_show_dismiss)
ImGui::EndDisabled();
ImGui::Spacing();
ImGui::Spacing();
if (s_achievements_login_logging_in)
{
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.7f, 0.7f, 0.7f, 1.0f));
const float status_width = ImGui::CalcTextSize("Logging in...").x;
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (content_width - status_width) * 0.5f);
ImGui::TextUnformatted(FSUI_CSTR("Logging in..."));
ImGui::PopStyleColor();
ImGui::Spacing();
}
else if (s_achievements_login_show_dismiss)
{
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.35f, 0.9f, 0.35f, 1.0f));
const TinyString username = Host::GetBaseTinyStringSettingValue("Achievements", "Username", "");
const SmallString success_text = SmallString::from_format(
FSUI_FSTR("Successfully logged in as {}."), username.empty() ? "Unknown" : username.view());
const float status_width = ImGui::CalcTextSize(success_text.c_str()).x;
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (content_width - status_width) * 0.5f);
ImGui::TextUnformatted(success_text.c_str());
ImGui::PopStyleColor();
ImGui::Spacing();
}
const float button_height = std::max(LayoutScale(36.0f), 28.0f);
const float button_spacing = LayoutScale(12.0f);
const float button_width = (content_width - button_spacing) * 0.5f;
auto CloseLoginPopup = []() {
ImGui::CloseCurrentPopup();
s_achievements_login_open = false;
s_achievements_login_logging_in = false;
s_achievements_login_show_dismiss = false;
s_achievements_login_username[0] = '\0';
s_achievements_login_password[0] = '\0';
};
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, LayoutScale(ImGuiFullscreen::LAYOUT_FRAME_ROUNDING));
const bool can_login = !s_achievements_login_show_dismiss && !s_achievements_login_logging_in &&
strlen(s_achievements_login_username) > 0 &&
strlen(s_achievements_login_password) > 0;
if (s_achievements_login_show_dismiss)
{
// keep dialog open and let user explicitly dismiss.
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.2f, 0.5f, 0.9f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.3f, 0.6f, 1.0f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.1f, 0.4f, 0.8f, 1.0f));
if (ImGui::Button(FSUI_CSTR("Dismiss"), ImVec2(button_width, button_height)) && !s_achievements_login_logging_in)
CloseLoginPopup();
ImGui::PopStyleColor(3);
ImGui::SameLine(0, button_spacing);
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.3f, 0.3f, 0.3f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.3f, 0.3f, 0.3f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.3f, 0.3f, 0.3f, 1.0f));
ImGui::BeginDisabled();
ImGui::Button(FSUI_CSTR("Login"), ImVec2(button_width, button_height));
ImGui::EndDisabled();
ImGui::PopStyleColor(3);
ImGui::PopStyleVar();
ImGui::EndPopup();
ImGui::PopStyleColor();
ImGui::PopStyleVar(2);
return;
}
if (can_login)
{
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.2f, 0.5f, 0.9f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.3f, 0.6f, 1.0f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.1f, 0.4f, 0.8f, 1.0f));
}
else
{
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.3f, 0.3f, 0.3f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.3f, 0.3f, 0.3f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.3f, 0.3f, 0.3f, 1.0f));
}
if (ImGui::Button(FSUI_CSTR("Login"), ImVec2(button_width, button_height)) && can_login)
{
s_achievements_login_logging_in = true;
s_achievements_login_show_dismiss = false;
Host::RunOnCPUThread([username = std::string(s_achievements_login_username),
password = std::string(s_achievements_login_password)]() {
Error error;
const bool result = Achievements::Login(username.c_str(), password.c_str(), &error);
s_achievements_login_logging_in = false;
if (!result)
{
ShowToast(std::string(), fmt::format(FSUI_FSTR("Login failed.\nError: {}\n\nPlease check your username and password, and try again."),
error.GetDescription()));
return;
}
s_achievements_login_password[0] = '\0';
if (s_achievements_login_reason == Achievements::LoginRequestReason::UserInitiated)
{
if (!Host::GetBaseBoolSettingValue("Achievements", "Enabled", false))
{
OpenConfirmMessageDialog(FSUI_STR("Enable Achievements"),
FSUI_STR("Achievement tracking is not currently enabled. Your login will have no effect until "
"after tracking is enabled.\n\nDo you want to enable tracking now?"),
[](bool result) {
if (result)
{
Host::SetBaseBoolSettingValue("Achievements", "Enabled", true);
Host::CommitBaseSettingChanges();
VMManager::ApplySettings();
}
});
}
if (!Host::GetBaseBoolSettingValue("Achievements", "ChallengeMode", false))
{
OpenConfirmMessageDialog(FSUI_STR("Enable Hardcore Mode"),
FSUI_STR("Hardcore mode is not currently enabled. Enabling hardcore mode allows you to set times, scores, and "
"participate in game-specific leaderboards.\n\nHowever, hardcore mode also prevents the usage of save "
"states, cheats and slowdown functionality.\n\nDo you want to enable hardcore mode?"),
[](bool result) {
if (result)
{
Host::SetBaseBoolSettingValue("Achievements", "ChallengeMode", true);
Host::CommitBaseSettingChanges();
VMManager::ApplySettings();
bool has_active_game;
{
auto lock = Achievements::GetLock();
has_active_game = Achievements::HasActiveGame();
}
if (has_active_game)
{
OpenConfirmMessageDialog(FSUI_STR("Reset System"),
FSUI_STR("Hardcore mode will not be enabled until the system is reset. Do you want to reset the system now?"),
[](bool reset) {
if (reset && VMManager::HasValidVM())
RequestReset();
});
}
}
});
}
}
s_achievements_login_show_dismiss = true;
});
}
ImGui::PopStyleColor(3);
ImGui::SameLine(0, button_spacing);
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.4f, 0.4f, 0.4f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.5f, 0.5f, 0.5f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.3f, 0.3f, 0.3f, 1.0f));
if (ImGui::Button(FSUI_CSTR("Cancel"), ImVec2(button_width, button_height)) && !s_achievements_login_logging_in)
{
if (s_achievements_login_reason == Achievements::LoginRequestReason::TokenInvalid)
{
if (VMManager::HasValidVM() && !Achievements::HasActiveGame())
Achievements::DisableHardcoreMode();
}
CloseLoginPopup();
}
ImGui::PopStyleColor(3);
ImGui::PopStyleVar();
ImGui::EndPopup();
}
ImGui::PopStyleColor();
ImGui::PopStyleVar(2);
}
void FullscreenUI::DrawAchievementsSettingsPage(std::unique_lock<std::mutex>& settings_lock)
{
#ifdef ENABLE_RAINTEGRATION
if (Achievements::IsUsingRAIntegration())
{
BeginMenuButtons();
ActiveButton(FSUI_ICONSTR(ICON_FA_BAN, "RAIntegration is being used instead of the built-in achievements implementation."), false,
false, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY);
EndMenuButtons();
return;
}
#endif
SettingsInterface* bsi = GetEditingSettingsInterface();
bool check_challenge_state = false;
BeginMenuButtons();
MenuHeading(FSUI_CSTR("Settings"));
check_challenge_state = DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_TROPHY, "Enable Achievements"),
FSUI_CSTR("When enabled and logged in, PCSX2 will scan for achievements on startup."), "Achievements", "Enabled", false);
const bool enabled = bsi->GetBoolValue("Achievements", "Enabled", false);
check_challenge_state |= DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_PF_DUMBELL, "Hardcore Mode"),
FSUI_CSTR(
"\"Challenge\" mode for achievements, including leaderboard tracking. Disables save state, cheats, and slowdown functions."),
"Achievements", "ChallengeMode", false, enabled);
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_BELL, "Achievement Notifications"),
FSUI_CSTR("Displays popup messages on events such as achievement unlocks and leaderboard submissions."), "Achievements",
"Notifications", true, enabled);
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_LIST_OL, "Leaderboard Notifications"),
FSUI_CSTR("Displays popup messages when starting, submitting, or failing a leaderboard challenge."), "Achievements",
"LeaderboardNotifications", true, enabled);
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_HEADPHONES, "Sound Effects"),
FSUI_CSTR("Plays sound effects for events such as achievement unlocks and leaderboard submissions."), "Achievements",
"SoundEffects", true, enabled);
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_PF_HEARTBEAT_ALT, "Enable In-Game Overlays"),
FSUI_CSTR("Shows icons in the screen when a challenge/primed achievement is active."), "Achievements",
"Overlays", true, enabled);
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_PF_HEARTBEAT_ALT, "Enable In-Game Leaderboard Overlays"),
FSUI_CSTR("Shows icons in the screen when leaderboard tracking is active."), "Achievements",
"LBOverlays", true, enabled);
if (enabled)
{
const char* alignment_options[] = {
TRANSLATE_NOOP("FullscreenUI", "Top Left"),
TRANSLATE_NOOP("FullscreenUI", "Top Center"),
TRANSLATE_NOOP("FullscreenUI", "Top Right"),
TRANSLATE_NOOP("FullscreenUI", "Center Left"),
TRANSLATE_NOOP("FullscreenUI", "Center"),
TRANSLATE_NOOP("FullscreenUI", "Center Right"),
TRANSLATE_NOOP("FullscreenUI", "Bottom Left"),
TRANSLATE_NOOP("FullscreenUI", "Bottom Center"),
TRANSLATE_NOOP("FullscreenUI", "Bottom Right")};
DrawIntListSetting(bsi, FSUI_ICONSTR(ICON_FA_ALIGN_CENTER, "Overlay Position"),
FSUI_CSTR("Determines where achievement/leaderboard overlays are positioned on the screen."), "Achievements", "OverlayPosition",
8, alignment_options, std::size(alignment_options), true, 0, enabled);
const bool notifications_enabled = GetEffectiveBoolSetting(bsi, "Achievements", "Notifications", true) ||
GetEffectiveBoolSetting(bsi, "Achievements", "LeaderboardNotifications", true);
if (notifications_enabled)
{
DrawIntListSetting(bsi, FSUI_ICONSTR(ICON_FA_BELL, "Notification Position"),
FSUI_CSTR("Determines where achievement/leaderboard notification popups are positioned on the screen."), "Achievements", "NotificationPosition",
2, alignment_options, std::size(alignment_options), true, 0, enabled);
const bool achievement_notifications_enabled = enabled && GetEffectiveBoolSetting(bsi, "Achievements", "Notifications", true);
DrawIntRangeSetting(bsi, FSUI_ICONSTR(ICON_FA_CLOCK, "Unlock Notification Duration"),
FSUI_CSTR("Determines the display duration for achievement unlock popups."),
"Achievements", "NotificationsDuration", 5, 3, 30, "%d seconds", achievement_notifications_enabled);
const bool leaderboard_notifications_enabled = enabled && GetEffectiveBoolSetting(bsi, "Achievements", "LeaderboardNotifications", true);
DrawIntRangeSetting(bsi, FSUI_ICONSTR(ICON_FA_CLOCK, "Leaderboard Notification Duration"),
FSUI_CSTR("Determines the display duration for leaderboard popups."),
"Achievements", "LeaderboardsDuration", 10, 3, 30, "%d seconds", leaderboard_notifications_enabled);
}
}
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_LOCK, "Encore Mode"),
FSUI_CSTR("When enabled, each session will behave as if no achievements have been unlocked."), "Achievements", "EncoreMode", false,
enabled);
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_EYE, "Spectator Mode"),
FSUI_CSTR("When enabled, PCSX2 will assume all achievements are locked and not send any unlock notifications to the server."),
"Achievements", "SpectatorMode", false, enabled);
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_MEDAL, "Test Unofficial Achievements"),
FSUI_CSTR(
"When enabled, PCSX2 will list achievements from unofficial sets. These achievements are not tracked by RetroAchievements."),
"Achievements", "UnofficialTestMode", false, enabled);
// Check for challenge mode just being enabled.
if (check_challenge_state && enabled && bsi->GetBoolValue("Achievements", "ChallengeMode", false) && VMManager::HasValidVM())
{
// don't bother prompting if the game doesn't have achievements
auto lock = Achievements::GetLock();
if (Achievements::HasActiveGame() && Achievements::HasAchievementsOrLeaderboards())
{
ImGuiFullscreen::OpenConfirmMessageDialog(FSUI_STR("Reset System"),
FSUI_STR("Hardcore mode will not be enabled until the system is reset. Do you want to reset the system now?"), [](bool reset) {
if (!VMManager::HasValidVM())
return;
if (reset)
RequestReset();
});
}
}
if (!IsEditingGameSettings(bsi))
{
MenuHeading(FSUI_CSTR("Sound Effects"));
const auto draw_sound_setting = [bsi](const char* title, const char* key, const char* default_filename, const char* selector_title) {
const std::string default_path = Path::Combine(EmuFolders::Resources, default_filename);
const std::optional<SmallString> custom_path = bsi->GetOptionalSmallStringValue("Achievements", key, std::nullopt);
const char* value = custom_path.has_value() ? custom_path->c_str() : default_path.c_str();
if (!MenuButton(title, value))
return;
ImGuiFullscreen::ChoiceDialogOptions options;
options.emplace_back(FSUI_ICONSTR(ICON_FA_FILE, "Select File"), false);
options.emplace_back(FSUI_ICONSTR(ICON_FA_VOLUME_HIGH, "Preview"), false);
options.emplace_back(FSUI_ICONSTR(ICON_FA_ROTATE_RIGHT, "Reset to Default"), false);
OpenChoiceDialog(title, false, std::move(options),
[bsi, key = std::string(key), selector_title = std::string(selector_title), default_path = std::move(default_path)](
s32 index, const std::string&, bool) {
if (index == 0)
{
auto callback = [bsi, key = key](const std::string& path) {
if (!path.empty())
{
bsi->SetStringValue("Achievements", key.c_str(), path.c_str());
SetSettingsChanged(bsi);
}
CloseFileSelector();
};
OpenFileSelector(selector_title.c_str(), false, std::move(callback), GetAudioFileFilters());
}
else if (index == 1)
{
const TinyString preview_path = bsi->GetTinyStringValue("Achievements", key.c_str(), default_path.c_str());
if (!Common::PlaySoundAsync(preview_path.c_str()))
{
ShowToast(std::string(),
fmt::format(FSUI_FSTR("Failed to preview sound:\n{}"),
preview_path.empty() ? FSUI_STR("No file selected.") : preview_path.c_str()));
}
}
else if (index == 2)
{
if (bsi->ContainsValue("Achievements", key.c_str()))
{
bsi->DeleteValue("Achievements", key.c_str());
SetSettingsChanged(bsi);
ShowToast(std::string(), FSUI_STR("Sound reset to default."));
}
else
{
ShowToast(std::string(), FSUI_STR("Sound is already using default."));
}
}
CloseChoiceDialog();
});
};
draw_sound_setting(FSUI_ICONSTR(ICON_FA_MUSIC, "Notification Sound"), "InfoSoundName", "sounds/achievements/message.wav",
FSUI_ICONSTR(ICON_FA_FOLDER_OPEN, "Select Notification Sound"));
draw_sound_setting(FSUI_ICONSTR(ICON_FA_MUSIC, "Unlock Sound"), "UnlockSoundName", "sounds/achievements/unlock.wav",
FSUI_ICONSTR(ICON_FA_FOLDER_OPEN, "Select Unlock Sound"));
draw_sound_setting(FSUI_ICONSTR(ICON_FA_MUSIC, "Leaderboard Submit Sound"), "LBSubmitSoundName",
"sounds/achievements/lbsubmit.wav", FSUI_ICONSTR(ICON_FA_FOLDER_OPEN, "Select Leaderboard Submit Sound"));
MenuHeading(FSUI_CSTR("Account"));
SettingsInterface* secrets_si = Host::Internal::GetSecretsSettingsLayer();
const TinyString username = bsi->GetTinyStringValue("Achievements", "Username", "");
const bool has_token = (secrets_si && secrets_si->ContainsValue("Achievements", "Token"));
if (has_token)
{
ImGui::PushStyleColor(ImGuiCol_TextDisabled, ImGui::GetStyle().Colors[ImGuiCol_Text]);
ActiveButton(SmallString::from_format(
fmt::runtime(FSUI_ICONSTR(ICON_FA_USER, "Username: {}")), username.empty() ? "Unknown" : username.view()),
false, false, ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY);
ActiveButton(SmallString::from_format(fmt::runtime(FSUI_ICONSTR(ICON_FA_CLOCK, "Login token generated on {}")),
TimeToPrintableString(static_cast<time_t>(
StringUtil::FromChars<u64>(bsi->GetTinyStringValue("Achievements", "LoginTimestamp", "0")).value_or(0)))),
false, false, ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY);
ImGui::PopStyleColor();
if (MenuButton(FSUI_ICONSTR(ICON_FA_KEY, "Logout"), FSUI_CSTR("Logs out of RetroAchievements.")))
{
Host::RunOnCPUThread([]() { Achievements::Logout(); });
}
}
else
{
ActiveButton(FSUI_ICONSTR(ICON_FA_USER, "Not Logged In"), false, false, ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY);
if (MenuButton(FSUI_ICONSTR(ICON_FA_KEY, "Login"), FSUI_CSTR("Logs in to RetroAchievements.")))
{
s_achievements_login_reason = Achievements::LoginRequestReason::UserInitiated;
s_achievements_login_show_dismiss = false;
s_achievements_login_open = true;
}
}
MenuHeading(FSUI_CSTR("Current Game"));
if (Achievements::HasActiveGame())
{
const auto lock = Achievements::GetLock();
ImGui::PushStyleColor(ImGuiCol_TextDisabled, ImGui::GetStyle().Colors[ImGuiCol_Text]);
ActiveButton(SmallString::from_format(fmt::runtime(FSUI_ICONSTR(ICON_FA_BOOKMARK, "Game: {0} ({1})")), Achievements::GetGameID(),
Achievements::GetGameTitle()),
false, false, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY);
const std::string& rich_presence_string = Achievements::GetRichPresenceString();
if (!rich_presence_string.empty())
{
ActiveButton(
SmallString::from_format(ICON_FA_MAP "{}", rich_presence_string), false, false, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY);
}
else
{
ActiveButton(FSUI_ICONSTR(ICON_FA_MAP, "Rich presence inactive or unsupported."), false, false,
LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY);
}
ImGui::PopStyleColor();
}
else
{
ActiveButton(FSUI_ICONSTR(ICON_FA_BAN, "Game not loaded or no RetroAchievements available."), false, false,
LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY);
}
}
EndMenuButtons();
}
void FullscreenUI::ResetControllerSettings()
{
OpenConfirmMessageDialog(FSUI_ICONSTR(u8"🔥", "Reset Controller Settings"),
@@ -4851,6 +5404,12 @@ void FullscreenUI::DrawFoldersSettingsPage()
DrawFolderSetting(bsi, FSUI_ICONSTR(ICON_FA_SHIRT, "Texture Replacements Directory"), "Folders", "Textures", EmuFolders::Textures);
DrawFolderSetting(bsi, FSUI_ICONSTR(ICON_FA_VIDEO, "Video Dumping Directory"), "Folders", "Videos", EmuFolders::Videos);
MenuHeading(FSUI_CSTR("Organization"));
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_FOLDER_OPEN, "Save Snapshots in Game-Specific Folders"),
FSUI_CSTR("Saves snapshots to per-game subfolders instead of a shared folder."), "EmuCore/GS", "OrganizeScreenshotsByGame", false);
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_FOLDER_OPEN, "Save Video Recordings in Game-Specific Folders"),
FSUI_CSTR("Saves video recordings to per-game subfolders instead of a shared folder."), "EmuCore/GS", "OrganizeVideoCaptureByGame", false);
EndMenuButtons();
}
@@ -4863,6 +5422,13 @@ void FullscreenUI::DrawAdvancedSettingsPage()
FSUI_NSTR("Chop/Zero (Default)"),
};
static constexpr const char* ee_div_rounding_mode_settings[] = {
FSUI_NSTR("Nearest (Default)"),
FSUI_NSTR("Negative"),
FSUI_NSTR("Positive"),
FSUI_NSTR("Chop/Zero"),
};
SettingsInterface* bsi = GetEditingSettingsInterface();
const bool show_advanced_settings = ShouldShowAdvancedSettings(bsi);
@@ -4922,7 +5488,7 @@ void FullscreenUI::DrawAdvancedSettingsPage()
DrawIntListSetting(bsi, FSUI_ICONSTR(ICON_FA_DIVIDE, "Division Rounding Mode"),
FSUI_CSTR("Determines how the results of floating-point division is rounded. Some games need specific settings."),
"EmuCore/CPU", "FPUDiv.Roundmode", static_cast<int>(FPRoundMode::Nearest),
ee_rounding_mode_settings, std::size(ee_rounding_mode_settings), true);
ee_div_rounding_mode_settings, std::size(ee_div_rounding_mode_settings), true);
DrawClampingModeSetting(bsi, FSUI_ICONSTR(ICON_FA_ARROW_TURN_DOWN, "Clamping Mode"),
FSUI_CSTR("Determines how out-of-range floating point numbers are handled. Some games need specific settings."), -1);
@@ -4938,6 +5504,12 @@ void FullscreenUI::DrawAdvancedSettingsPage()
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_MEMORY, "Enable Fast Memory Access"),
FSUI_CSTR("Uses backpatching to avoid register flushing on every memory access."), "EmuCore/CPU/Recompiler", "EnableFastmem",
true);
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_PAUSE, "Pause On TLB Miss"),
FSUI_CSTR("Pauses the virtual machine when a TLB miss occurs, instead of ignoring it and continuing."),
"EmuCore/CPU/Recompiler", "PauseOnTLBMiss", false);
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_MEMORY, "Enable Extended RAM (Dev Console)"),
FSUI_CSTR("Exposes additional memory to the virtual machine, expanding the EE and IOP memory to 128MB and 8MB respectively."),
"EmuCore/CPU", "ExtraMemory", false);
MenuHeading(FSUI_CSTR("Vector Units"));
DrawIntListSetting(bsi, FSUI_ICONSTR(ICON_FA_ARROW_TREND_DOWN, "VU0 Rounding Mode"),
@@ -4980,6 +5552,13 @@ void FullscreenUI::DrawAdvancedSettingsPage()
"UseDebugDevice", false);
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_BUG, "Use Debug Blend"), FSUI_CSTR("Forces SW blending and disables several optimizations."), "EmuCore/GS", "UseDebugBlend", false);
MenuHeading(FSUI_CSTR("PINE Settings"));
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_NETWORK_WIRED, "Enable PINE"),
FSUI_CSTR("Enables the PINE Inter-Process Communication system, allowing external programs to interact with the emulator."), "EmuCore", "EnablePINE", false);
const bool pine_enabled = GetEffectiveBoolSetting(bsi, "EmuCore", "EnablePINE", false);
DrawIntSpinBoxSetting(bsi, FSUI_ICONSTR(ICON_FA_NETWORK_WIRED, "PINE Slot"),
FSUI_CSTR("The network port slot used for PINE IPC connections."), "EmuCore", "PINESlot", 28011, 1024, 65535, 1, "%d", pine_enabled);
}
EndMenuButtons();
@@ -5320,6 +5899,7 @@ TRANSLATE_NOOP("FullscreenUI", "Adjusts gamma. 50 is normal.");
TRANSLATE_NOOP("FullscreenUI", "Adjusts saturation. 50 is normal.");
TRANSLATE_NOOP("FullscreenUI", "Applies a shader which replicates the visual effects of different styles of television set.");
TRANSLATE_NOOP("FullscreenUI", "Advanced");
TRANSLATE_NOOP("FullscreenUI", "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.");
TRANSLATE_NOOP("FullscreenUI", "Skips displaying frames that don't change in 25/30fps games. Can improve speed, but increase input lag/make frame pacing worse.");
TRANSLATE_NOOP("FullscreenUI", "Forces the use of FIFO over Mailbox presentation, i.e. double buffering instead of triple buffering. Usually results in worse frame pacing.");
TRANSLATE_NOOP("FullscreenUI", "Displays additional, very high upscaling multipliers dependent on GPU and driver capability.");
@@ -5330,6 +5910,7 @@ TRANSLATE_NOOP("FullscreenUI", "Sets the compression algorithm for GS dumps.");
TRANSLATE_NOOP("FullscreenUI", "Prevents the usage of framebuffer fetch when supported by host GPU.");
TRANSLATE_NOOP("FullscreenUI", "Prevents the loading and saving of shaders/pipelines to disk.");
TRANSLATE_NOOP("FullscreenUI", "Falls back to the CPU for expanding sprites/lines.");
TRANSLATE_NOOP("FullscreenUI", "Forces extra barriers when using ROV with Vulkan to fix graphical issues present in some games and hardware configurations.");
TRANSLATE_NOOP("FullscreenUI", "Uploads full textures to the GPU on use, rather than only the utilized regions. Can improve performance in some games.");
TRANSLATE_NOOP("FullscreenUI", "Determines what frame rate NTSC games run at.");
TRANSLATE_NOOP("FullscreenUI", "Determines what frame rate PAL games run at.");
@@ -5648,6 +6229,7 @@ TRANSLATE_NOOP("FullscreenUI", "Full (Hash Cache)");
TRANSLATE_NOOP("FullscreenUI", "Force Disabled");
TRANSLATE_NOOP("FullscreenUI", "Force Enabled");
TRANSLATE_NOOP("FullscreenUI", "Accurate (Recommended)");
TRANSLATE_NOOP("FullscreenUI", "Accurate Force Full (Can Reduce Readbacks)");
TRANSLATE_NOOP("FullscreenUI", "Disable Readbacks (Synchronize GS Thread)");
TRANSLATE_NOOP("FullscreenUI", "Unsynchronized (Non-Deterministic)");
TRANSLATE_NOOP("FullscreenUI", "Disabled (Ignore Transfers)");
@@ -5859,6 +6441,7 @@ TRANSLATE_NOOP("FullscreenUI", "Shade Boost Contrast");
TRANSLATE_NOOP("FullscreenUI", "Shade Boost Gamma");
TRANSLATE_NOOP("FullscreenUI", "Shade Boost Saturation");
TRANSLATE_NOOP("FullscreenUI", "TV Shaders");
TRANSLATE_NOOP("FullscreenUI", "Rasterizer Ordered View");
TRANSLATE_NOOP("FullscreenUI", "Skip Presenting Duplicate Frames");
TRANSLATE_NOOP("FullscreenUI", "Disable Mailbox Presentation");
TRANSLATE_NOOP("FullscreenUI", "Extended Upscaling Multipliers");
@@ -5869,6 +6452,7 @@ TRANSLATE_NOOP("FullscreenUI", "GS Dump Compression");
TRANSLATE_NOOP("FullscreenUI", "Disable Framebuffer Fetch");
TRANSLATE_NOOP("FullscreenUI", "Disable Shader Cache");
TRANSLATE_NOOP("FullscreenUI", "Disable Vertex Shader Expand");
TRANSLATE_NOOP("FullscreenUI", "ROV Barriers Vulkan");
TRANSLATE_NOOP("FullscreenUI", "Texture Preloading");
TRANSLATE_NOOP("FullscreenUI", "NTSC Frame Rate");
TRANSLATE_NOOP("FullscreenUI", "PAL Frame Rate");
+189 -29
View File
@@ -100,6 +100,22 @@ static std::atomic_bool s_imgui_wants_text{false};
static bool s_gamepad_swap_noth_west = false;
struct ControllerNavState
{
bool dpad_h_held = false;
bool dpad_v_held = false;
struct AxisState
{
float x = 0.0f, y = 0.0f;
bool x_neg_active = false, x_pos_active = false;
bool y_neg_active = false, y_pos_active = false;
};
AxisState left_stick;
AxisState right_stick;
};
static std::unordered_map<u32, ControllerNavState> s_controller_nav_states;
// mapping of host key -> imgui key
static std::unordered_map<u32, ImGuiKey> s_imgui_key_map;
@@ -147,6 +163,21 @@ bool ImGuiManager::Initialize()
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures | ImGuiBackendFlags_HasGamepad;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard | ImGuiConfigFlags_NavEnableGamepad;
io.KeyRepeatDelay = 0.5f;
#ifdef __APPLE__
// For macOS we should use the standard macOS text editing shortcuts
io.ConfigMacOSXBehaviors = true;
#endif
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
platform_io.Platform_GetClipboardTextFn = [](ImGuiContext* ctx) -> const char* {
static thread_local std::string s_clipboard_text;
s_clipboard_text = Host::GetTextFromClipboard();
return s_clipboard_text.c_str();
};
platform_io.Platform_SetClipboardTextFn = [](ImGuiContext* ctx, const char* text) {
if (text)
Host::CopyTextToClipboard(text);
};
g.ConfigNavWindowingKeyNext = ImGuiKey_None;
g.ConfigNavWindowingKeyPrev = ImGuiKey_None;
@@ -398,10 +429,10 @@ void ImGuiManager::SetKeyMap()
static constexpr KeyMapping mapping[] = {{ImGuiKey_LeftArrow, "Left"}, {ImGuiKey_RightArrow, "Right"}, {ImGuiKey_UpArrow, "Up"},
{ImGuiKey_DownArrow, "Down"}, {ImGuiKey_PageUp, "PageUp"}, {ImGuiKey_PageDown, "PageDown"}, {ImGuiKey_Home, "Home"},
{ImGuiKey_End, "End"}, {ImGuiKey_Insert, "Insert"}, {ImGuiKey_Delete, "Delete"}, {ImGuiKey_Backspace, "Backspace"},
{ImGuiKey_Space, "Space"}, {ImGuiKey_Enter, "Return"}, {ImGuiKey_Escape, "Escape"}, {ImGuiKey_LeftCtrl, "LeftCtrl", "Ctrl"},
{ImGuiKey_LeftShift, "LeftShift", "Shift"}, {ImGuiKey_LeftAlt, "LeftAlt", "Alt"}, {ImGuiKey_LeftSuper, "LeftSuper", "Super"},
{ImGuiKey_Space, "Space"}, {ImGuiKey_Enter, "Return"}, {ImGuiKey_Escape, "Escape"}, {ImGuiKey_LeftCtrl, "LeftCtrl", "Control"},
{ImGuiKey_LeftShift, "LeftShift", "Shift"}, {ImGuiKey_LeftAlt, "LeftAlt", "Alt"}, {ImGuiKey_LeftSuper, "Meta", "Super_L"},
{ImGuiKey_RightCtrl, "RightCtrl"}, {ImGuiKey_RightShift, "RightShift"}, {ImGuiKey_RightAlt, "RightAlt"},
{ImGuiKey_RightSuper, "RightSuper"}, {ImGuiKey_Menu, "Menu"}, {ImGuiKey_0, "0"}, {ImGuiKey_1, "1"}, {ImGuiKey_2, "2"},
{ImGuiKey_RightSuper, "Super_R"}, {ImGuiKey_Menu, "Menu"}, {ImGuiKey_0, "0"}, {ImGuiKey_1, "1"}, {ImGuiKey_2, "2"},
{ImGuiKey_3, "3"}, {ImGuiKey_4, "4"}, {ImGuiKey_5, "5"}, {ImGuiKey_6, "6"}, {ImGuiKey_7, "7"}, {ImGuiKey_8, "8"}, {ImGuiKey_9, "9"},
{ImGuiKey_A, "A"}, {ImGuiKey_B, "B"}, {ImGuiKey_C, "C"}, {ImGuiKey_D, "D"}, {ImGuiKey_E, "E"}, {ImGuiKey_F, "F"}, {ImGuiKey_G, "G"},
{ImGuiKey_H, "H"}, {ImGuiKey_I, "I"}, {ImGuiKey_J, "J"}, {ImGuiKey_K, "K"}, {ImGuiKey_L, "L"}, {ImGuiKey_M, "M"}, {ImGuiKey_N, "N"},
@@ -509,7 +540,15 @@ static u32 GetFontIndex(const ImGuiManager::FontInfo& font)
{
if (!font.face_name)
return 0; // No face name selected
#define RET_IF_ERR(x) do { if ((x) != FT_Err_Ok) [[unlikely]] { assert(0); return 0; } } while (0)
#define RET_IF_ERR(x) \
do \
{ \
if ((x) != FT_Err_Ok) [[unlikely]] \
{ \
assert(0); \
return 0; \
} \
} while (0)
if (!s_ft_lib)
RET_IF_ERR(FT_Init_FreeType(&s_ft_lib));
u32 face_idx = 0;
@@ -882,7 +921,7 @@ void ImGuiManager::DrawOSDMessages(Common::Timer::Value current_time)
const float padding = std::ceil(8.0f * scale);
const float rounding = std::ceil(5.0f * scale);
const float max_width = s_window_width - (margin + padding) * 2.0f;
float position_y = margin;
switch (GSConfig.OsdMessagesPos)
{
@@ -891,20 +930,20 @@ void ImGuiManager::DrawOSDMessages(Common::Timer::Value current_time)
case OsdOverlayPos::TopRight:
position_y = margin;
break;
case OsdOverlayPos::CenterLeft:
case OsdOverlayPos::Center:
case OsdOverlayPos::CenterRight:
position_y = s_window_height * 0.5f;
break;
case OsdOverlayPos::BottomLeft:
case OsdOverlayPos::BottomCenter:
case OsdOverlayPos::BottomRight:
// For bottom positions, start from the bottom and let messages stack upward
position_y = s_window_height - margin;
break;
case OsdOverlayPos::None:
default:
position_y = margin;
@@ -976,16 +1015,16 @@ void ImGuiManager::DrawOSDMessages(Common::Timer::Value current_time)
const ImVec2 text_size(
font->CalcTextSizeA(font_size, max_width, max_width, msg.text.c_str(), msg.text.c_str() + msg.text.length()));
const ImVec2 size(text_size.x + padding * 2.0f, text_size.y + padding * 2.0f);
// For bottom positions, adjust actual_y to try to account for message height
float final_y = actual_y;
if (GSConfig.OsdMessagesPos == OsdOverlayPos::BottomLeft ||
GSConfig.OsdMessagesPos == OsdOverlayPos::BottomCenter ||
GSConfig.OsdMessagesPos == OsdOverlayPos::BottomRight)
if (GSConfig.OsdMessagesPos == OsdOverlayPos::BottomLeft ||
GSConfig.OsdMessagesPos == OsdOverlayPos::BottomCenter ||
GSConfig.OsdMessagesPos == OsdOverlayPos::BottomRight)
{
final_y = actual_y - size.y;
}
const ImVec2 base_pos = CalculateOSDPosition(GSConfig.OsdMessagesPos, margin, size, s_window_width, s_window_height);
const ImVec2 pos(base_pos.x, final_y);
const ImVec4 text_rect(pos.x + padding, pos.y + padding, pos.x + size.x - padding, pos.y + size.y - padding);
@@ -995,11 +1034,11 @@ void ImGuiManager::DrawOSDMessages(Common::Timer::Value current_time)
dl->AddRect(pos, ImVec2(pos.x + size.x, pos.y + size.y), IM_COL32(0x48, 0x48, 0x48, opacity), rounding);
dl->AddText(font, font_size, ImVec2(text_rect.x, text_rect.y), IM_COL32(0xff, 0xff, 0xff, opacity), msg.text.c_str(),
msg.text.c_str() + msg.text.length(), max_width, &text_rect);
// Stack direction depends on the position upward for bottom positions, downward for others
if (GSConfig.OsdMessagesPos == OsdOverlayPos::BottomLeft ||
GSConfig.OsdMessagesPos == OsdOverlayPos::BottomCenter ||
GSConfig.OsdMessagesPos == OsdOverlayPos::BottomRight)
if (GSConfig.OsdMessagesPos == OsdOverlayPos::BottomLeft ||
GSConfig.OsdMessagesPos == OsdOverlayPos::BottomCenter ||
GSConfig.OsdMessagesPos == OsdOverlayPos::BottomRight)
{
position_y -= (size.y + spacing); // Stack upward for bottom positions
}
@@ -1122,19 +1161,50 @@ bool ImGuiManager::ProcessPointerAxisEvent(InputBindingKey key, float value)
return s_imgui_wants_mouse.load(std::memory_order_acquire);
}
static ImGuiKey GetModifierForKey(ImGuiKey key)
{
static constexpr std::pair<ImGuiKey, ImGuiKey> modifier_map[] = {
{ImGuiKey_LeftCtrl, ImGuiMod_Ctrl},
{ImGuiKey_RightCtrl, ImGuiMod_Ctrl},
{ImGuiKey_LeftShift, ImGuiMod_Shift},
{ImGuiKey_RightShift, ImGuiMod_Shift},
{ImGuiKey_LeftAlt, ImGuiMod_Alt},
{ImGuiKey_RightAlt, ImGuiMod_Alt},
{ImGuiKey_LeftSuper, ImGuiMod_Super},
{ImGuiKey_RightSuper, ImGuiMod_Super},
};
for (const auto& [k, mod] : modifier_map)
{
if (key == k)
return mod;
}
return ImGuiKey_None;
}
bool ImGuiManager::ProcessHostKeyEvent(InputBindingKey key, float value)
{
decltype(s_imgui_key_map)::iterator iter;
if (!ImGui::GetCurrentContext() || (iter = s_imgui_key_map.find(key.data)) == s_imgui_key_map.end())
if (!ImGui::GetCurrentContext())
return false;
const auto iter = s_imgui_key_map.find(key.data);
if (iter == s_imgui_key_map.end())
return false;
// still update state anyway
MTGS::RunOnGSThread([imkey = iter->second, down = (value != 0.0f)]() { ImGui::GetIO().AddKeyEvent(imkey, down); });
MTGS::RunOnGSThread([imkey = iter->second, down = (value != 0.0f)]() {
ImGuiIO& io = ImGui::GetIO();
io.AddKeyEvent(imkey, down);
if (const ImGuiKey mod = GetModifierForKey(imkey); mod != ImGuiKey_None)
io.AddKeyEvent(mod, down);
});
return s_imgui_wants_keyboard.load(std::memory_order_acquire);
}
bool ImGuiManager::ProcessGenericInputEvent(GenericInputBinding key, InputLayout layout, float value)
bool ImGuiManager::ProcessGenericInputEvent(GenericInputBinding key, InputLayout layout, float value, u32 controller_id)
{
static constexpr ImGuiKey key_map[] = {
ImGuiKey_None, // Unknown,
@@ -1142,15 +1212,15 @@ bool ImGuiManager::ProcessGenericInputEvent(GenericInputBinding key, InputLayout
ImGuiKey_GamepadDpadRight, // DPadRight
ImGuiKey_GamepadDpadLeft, // DPadLeft
ImGuiKey_GamepadDpadDown, // DPadDown
ImGuiKey_None, // LeftStickUp
ImGuiKey_None, // LeftStickRight
ImGuiKey_None, // LeftStickDown
ImGuiKey_None, // LeftStickLeft
ImGuiKey_GamepadDpadUp, // LeftStickUp
ImGuiKey_GamepadDpadRight, // LeftStickRight
ImGuiKey_GamepadDpadDown, // LeftStickDown
ImGuiKey_GamepadDpadLeft, // LeftStickLeft
ImGuiKey_GamepadL3, // L3
ImGuiKey_None, // RightStickUp
ImGuiKey_None, // RightStickRight
ImGuiKey_None, // RightStickDown
ImGuiKey_None, // RightStickLeft
ImGuiKey_GamepadDpadUp, // RightStickUp
ImGuiKey_GamepadDpadRight, // RightStickRight
ImGuiKey_GamepadDpadDown, // RightStickDown
ImGuiKey_GamepadDpadLeft, // RightStickLeft
ImGuiKey_GamepadR3, // R3
ImGuiKey_GamepadFaceUp, // Triangle
ImGuiKey_GamepadFaceRight, // Circle
@@ -1171,6 +1241,21 @@ bool ImGuiManager::ProcessGenericInputEvent(GenericInputBinding key, InputLayout
if (static_cast<u32>(key) >= std::size(key_map) || key_map[static_cast<u32>(key)] == ImGuiKey_None)
return false;
// Ignore diagonal D-pad input — neither direction fires when both axes are held simultaneously.
const bool is_dpad_h = (key == GenericInputBinding::DPadLeft || key == GenericInputBinding::DPadRight);
const bool is_dpad_v = (key == GenericInputBinding::DPadUp || key == GenericInputBinding::DPadDown);
if (is_dpad_h || is_dpad_v)
{
ControllerNavState& nav = s_controller_nav_states[controller_id];
if (is_dpad_h)
nav.dpad_h_held = (value > 0.0f);
else
nav.dpad_v_held = (value > 0.0f);
if (nav.dpad_h_held && nav.dpad_v_held)
return false;
}
MTGS::RunOnGSThread(
[key = key_map[static_cast<u32>(key)], value, layout]() mutable {
if (s_gamepad_swap_noth_west)
@@ -1188,6 +1273,81 @@ bool ImGuiManager::ProcessGenericInputEvent(GenericInputBinding key, InputLayout
return s_imgui_wants_keyboard.load(std::memory_order_acquire);
}
void ImGuiManager::ProcessGenericAxisEvent(GenericInputBinding negative_key, GenericInputBinding positive_key, InputLayout layout, float value, u32 controller_id)
{
// Hysteresis prevents wobble: activate at 0.5, release at 0.2.
static constexpr float ACTIVATE_THRESHOLD = 0.5f;
static constexpr float RELEASE_THRESHOLD = 0.2f;
// Ignore diagonal input and track binary state per direction to suppress duplicate events.
using AxisState = ControllerNavState::AxisState;
ControllerNavState& nav = s_controller_nav_states[controller_id];
AxisState* state = nullptr;
bool is_x_axis = false;
if (negative_key == GenericInputBinding::LeftStickLeft)
{
state = &nav.left_stick;
state->x = value;
is_x_axis = true;
}
else if (negative_key == GenericInputBinding::LeftStickUp)
{
state = &nav.left_stick;
state->y = value;
is_x_axis = false;
}
else if (negative_key == GenericInputBinding::RightStickLeft)
{
state = &nav.right_stick;
state->x = value;
is_x_axis = true;
}
else if (negative_key == GenericInputBinding::RightStickUp)
{
state = &nav.right_stick;
state->y = value;
is_x_axis = false;
}
float suppressed_value = value;
if (state)
{
const float other = is_x_axis ? state->y : state->x;
if (std::abs(other) > std::abs(value))
suppressed_value = 0.0f;
}
// Treat as binary like the D-pad: either fully pressed or released, with a deadzone.
bool* neg_active_ptr = state ? (is_x_axis ? &state->x_neg_active : &state->y_neg_active) : nullptr;
bool* pos_active_ptr = state ? (is_x_axis ? &state->x_pos_active : &state->y_pos_active) : nullptr;
if (negative_key != GenericInputBinding::Unknown)
{
const bool currently_active = neg_active_ptr ? *neg_active_ptr : false;
const float threshold = currently_active ? RELEASE_THRESHOLD : ACTIVATE_THRESHOLD;
const bool active = suppressed_value < -threshold;
if (!neg_active_ptr || active != currently_active)
{
if (neg_active_ptr)
*neg_active_ptr = active;
ProcessGenericInputEvent(negative_key, layout, active ? 1.0f : 0.0f, controller_id);
}
}
if (positive_key != GenericInputBinding::Unknown)
{
const bool currently_active = pos_active_ptr ? *pos_active_ptr : false;
const float threshold = currently_active ? RELEASE_THRESHOLD : ACTIVATE_THRESHOLD;
const bool active = suppressed_value > threshold;
if (!pos_active_ptr || active != currently_active)
{
if (pos_active_ptr)
*pos_active_ptr = active;
ProcessGenericInputEvent(positive_key, layout, active ? 1.0f : 0.0f, controller_id);
}
}
}
void ImGuiManager::SwapGamepadNorthWest(bool value)
{
s_gamepad_swap_noth_west = value;
+4 -1
View File
@@ -105,7 +105,10 @@ namespace ImGuiManager
bool ProcessHostKeyEvent(InputBindingKey key, float value);
/// Called on the CPU thread when any input event fires. Allows imgui to take over controller navigation.
bool ProcessGenericInputEvent(GenericInputBinding key, InputLayout layout, float value);
bool ProcessGenericInputEvent(GenericInputBinding key, InputLayout layout, float value, u32 controller_id = 0);
/// Called on the CPU thread for a bidirectional analog axis event.
void ProcessGenericAxisEvent(GenericInputBinding negative_key, GenericInputBinding positive_key, InputLayout layout, float value, u32 controller_id = 0);
/// Called to swap North/West gamepad buttons within ImGui
void SwapGamepadNorthWest(bool value);
+7 -1
View File
@@ -916,6 +916,12 @@ __ri void ImGuiManager::DrawSettingsOverlay(float scale, float margin, float spa
if (GSConfig.HWAA1)
APPEND("AA1 ");
if (GSConfig.HWROV)
APPEND("ROV ");
if (GSConfig.HWROVBarriersVK)
APPEND("RBVK ");
// deliberately test global and print local here for auto values
if (EmuConfig.GS.TextureFiltering != BiFiltering::PS2)
APPEND("BF={} ", static_cast<unsigned>(GSConfig.TextureFiltering));
@@ -974,7 +980,7 @@ __ri void ImGuiManager::DrawSettingsOverlay(float scale, float margin, float spa
if (GSConfig.UserHacks_EstimateTextureRegion)
APPEND("ETR ");
if (GSConfig.UserHacks_DrawBuffering)
APPEND("DRWB");
APPEND("DRWB ");
if (GSConfig.HWSpinGPUForReadbacks)
APPEND("RBSG ");
if (GSConfig.HWSpinCPUForReadbacks)
+68 -10
View File
@@ -114,7 +114,8 @@ namespace InputManager
static void GenerateRelativeMouseEvents();
static bool DoEventHook(InputBindingKey key, float value);
static bool PreprocessEvent(InputBindingKey key, float value, GenericInputBinding generic_key);
static bool PreprocessEvent(InputBindingKey key, float value, GenericInputBinding generic_key,
GenericInputBinding axis_neg_key, GenericInputBinding axis_pos_key);
static bool ProcessEvent(InputBindingKey key, float value, bool skip_button_handlers);
template <typename T>
@@ -132,6 +133,13 @@ static BindingMap s_binding_map;
static VibrationBindingArray s_pad_vibration_array;
static std::mutex s_binding_map_write_lock;
// Reverse lookups for controller navigation; user bindings take priority over static defaults.
using ControllerButtonGenericMap = std::unordered_map<InputBindingKey, GenericInputBinding, InputBindingKeyHash>;
static ControllerButtonGenericMap s_controller_button_generic_map;
using ControllerAxisGenericMap = std::unordered_map<InputBindingKey, std::array<GenericInputBinding, 2>, InputBindingKeyHash>;
static ControllerAxisGenericMap s_controller_axis_generic_map;
// Hooks/intercepting (for setting bindings)
static std::mutex m_event_intercept_mutex;
static InputInterceptHook::Callback m_event_intercept_callback;
@@ -637,7 +645,7 @@ void InputManager::AddBindings(const std::vector<std::string>& bindings, const I
// INISettingsInterface, can just update directly
si.SetStringList(section, key, new_bindings);
si.Save();
}
}
else
{
// LayeredSettingsInterface, Need to find which layer our binding came from
@@ -905,6 +913,32 @@ void InputManager::AddPadBindings(SettingsInterface& si, u32 pad_index, bool is_
Pad::SetControllerState(pad_index, bind_index, ApplySingleBindingScale(sensitivity, deadzone, value));
}},
bi.bind_type, si, section.c_str(), bi.name, is_profile);
// Build reverse maps for controller navigation; user bindings take priority over static defaults.
if (bi.generic_mapping != GenericInputBinding::Unknown)
{
for (const std::string& binding_str : bindings)
{
InputBindingKey bkey;
InputSource* bsrc;
if (!ParseBindingAndGetSource(binding_str, &bkey, &bsrc))
continue;
if (bkey.source_subtype == InputSubclass::ControllerButton)
{
s_controller_button_generic_map.emplace(bkey.MaskDirection(), bi.generic_mapping);
}
else if (bkey.source_subtype == InputSubclass::ControllerAxis)
{
auto& entry = s_controller_axis_generic_map[bkey.MaskDirection()];
// Negate modifier = negative half of axis (e.g. "-Axis0" → LeftStickLeft)
if (bkey.modifier == InputModifier::Negate)
entry[0] = bi.generic_mapping;
else
entry[1] = bi.generic_mapping;
}
}
}
}
}
break;
@@ -1071,13 +1105,14 @@ bool InputManager::IsAxisHandler(const InputEventHandler& handler)
return std::holds_alternative<InputAxisEventHandler>(handler);
}
bool InputManager::InvokeEvents(InputBindingKey key, float value, GenericInputBinding generic_key)
bool InputManager::InvokeEvents(InputBindingKey key, float value, GenericInputBinding generic_key,
GenericInputBinding axis_neg_key, GenericInputBinding axis_pos_key)
{
if (DoEventHook(key, value))
return true;
// If imgui ate the event, don't fire our handlers.
const bool skip_button_handlers = PreprocessEvent(key, value, generic_key);
const bool skip_button_handlers = PreprocessEvent(key, value, generic_key, axis_neg_key, axis_pos_key);
return ProcessEvent(key, value, skip_button_handlers);
}
@@ -1258,7 +1293,8 @@ void InputManager::ClearBindStateFromSource(InputBindingKey key)
} while (matched);
}
bool InputManager::PreprocessEvent(InputBindingKey key, float value, GenericInputBinding generic_key)
bool InputManager::PreprocessEvent(InputBindingKey key, float value, GenericInputBinding generic_key,
GenericInputBinding axis_neg_key, GenericInputBinding axis_pos_key)
{
// does imgui want the event?
if (key.source_type == InputSourceType::Keyboard)
@@ -1274,11 +1310,31 @@ bool InputManager::PreprocessEvent(InputBindingKey key, float value, GenericInpu
if (ImGuiManager::ProcessPointerButtonEvent(key, value))
return true;
}
else if (generic_key != GenericInputBinding::Unknown)
else if (key.source_subtype == InputSubclass::ControllerButton)
{
InputLayout layout = s_input_sources[static_cast<u32>(InputSourceType::SDL)]->GetControllerLayout(key.source_index);
if (ImGuiManager::ProcessGenericInputEvent(generic_key, layout, value) && value != 0.0f)
return true;
// User binding takes priority; fall back to the generic_key passed by the source (static table).
const auto it = s_controller_button_generic_map.find(key.MaskDirection());
const GenericInputBinding resolved = (it != s_controller_button_generic_map.end()) ? it->second : generic_key;
if (resolved != GenericInputBinding::Unknown)
{
const u32 controller_id = (static_cast<u32>(key.source_type) << 8) | key.source_index;
const InputLayout layout = s_input_sources[static_cast<u32>(InputSourceType::SDL)]->GetControllerLayout(key.source_index);
if (ImGuiManager::ProcessGenericInputEvent(resolved, layout, value, controller_id) && value != 0.0f)
return true;
}
}
else if (key.source_subtype == InputSubclass::ControllerAxis)
{
// User binding takes priority; fall back to the neg/pos keys passed by the source (static table).
const auto it = s_controller_axis_generic_map.find(key.MaskDirection());
const GenericInputBinding neg = (it != s_controller_axis_generic_map.end()) ? it->second[0] : axis_neg_key;
const GenericInputBinding pos = (it != s_controller_axis_generic_map.end()) ? it->second[1] : axis_pos_key;
if (neg != GenericInputBinding::Unknown || pos != GenericInputBinding::Unknown)
{
const u32 controller_id = (static_cast<u32>(key.source_type) << 8) | key.source_index;
const InputLayout layout = s_input_sources[static_cast<u32>(InputSourceType::SDL)]->GetControllerLayout(key.source_index);
ImGuiManager::ProcessGenericAxisEvent(neg, pos, layout, value, controller_id);
}
}
return false;
@@ -1557,6 +1613,8 @@ void InputManager::ReloadBindings(SettingsInterface& si, SettingsInterface& bind
s_pad_vibration_array.clear();
s_keyboard_event_callbacks.clear();
s_pointer_move_callbacks.clear();
s_controller_button_generic_map.clear();
s_controller_axis_generic_map.clear();
// Hotkeys use the base configuration, except if the custom hotkeys option is enabled.
AddHotkeyBindings(hotkey_binding_si, is_hotkey_profile);
@@ -1571,7 +1629,7 @@ void InputManager::ReloadBindings(SettingsInterface& si, SettingsInterface& bind
for (u32 axis = 0; axis <= static_cast<u32>(InputPointerAxis::Y); axis++)
{
s_pointer_axis_speed[axis] = si.GetFloatValue("Pad", fmt::format("Pointer{}Speed", s_pointer_axis_setting_names[axis]).c_str(), 40.0f) /
ui_ctrl_range * pointer_sensitivity;
ui_ctrl_range * pointer_sensitivity;
s_pointer_axis_dead_zone[axis] = std::min(
si.GetFloatValue("Pad", fmt::format("Pointer{}DeadZone", s_pointer_axis_setting_names[axis]).c_str(), 20.0f) / ui_ctrl_range, 1.0f);
s_pointer_axis_range[axis] = 1.0f - s_pointer_axis_dead_zone[axis];
+3 -1
View File
@@ -271,7 +271,9 @@ namespace InputManager
/// Updates internal state for any binds for this key, and fires callbacks as needed.
/// Returns true if anything was bound to this key, otherwise false.
bool InvokeEvents(InputBindingKey key, float value, GenericInputBinding generic_key = GenericInputBinding::Unknown);
bool InvokeEvents(InputBindingKey key, float value, GenericInputBinding generic_key = GenericInputBinding::Unknown,
GenericInputBinding axis_neg_key = GenericInputBinding::Unknown,
GenericInputBinding axis_pos_key = GenericInputBinding::Unknown);
/// Clears internal state for any binds with a matching source/index.
void ClearBindStateFromSource(InputBindingKey key);
+17 -5
View File
@@ -1489,7 +1489,19 @@ bool SDLInputSource::HandleGamepadAxisEvent(const SDL_GamepadAxisEvent* ev)
return false;
const InputBindingKey key(MakeGenericControllerAxisKey(InputSourceType::SDL, it->player_id, ev->axis));
InputManager::InvokeEvents(key, NormalizeS16(ev->value));
const float value = NormalizeS16(ev->value);
if (ev->axis < std::size(s_sdl_generic_binding_axis_mapping))
{
InputManager::InvokeEvents(key, value, GenericInputBinding::Unknown,
s_sdl_generic_binding_axis_mapping[ev->axis][0],
s_sdl_generic_binding_axis_mapping[ev->axis][1]);
}
else
{
InputManager::InvokeEvents(key, value);
}
return true;
}
@@ -1501,8 +1513,8 @@ bool SDLInputSource::HandleGamepadButtonEvent(const SDL_GamepadButtonEvent* ev)
const InputBindingKey key(MakeGenericControllerButtonKey(InputSourceType::SDL, it->player_id, ev->button));
const GenericInputBinding generic_key = (ev->button < std::size(s_sdl_generic_binding_button_mapping)) ?
s_sdl_generic_binding_button_mapping[ev->button] :
GenericInputBinding::Unknown;
s_sdl_generic_binding_button_mapping[ev->button] :
GenericInputBinding::Unknown;
InputManager::InvokeEvents(key, static_cast<float>(ev->down), generic_key);
return true;
}
@@ -1765,6 +1777,6 @@ bool SDLInputSource::IsControllerSixaxis(const ControllerData& cd)
// This differing layout also isn't mapped correctly in SDL, I think due to how L2/R2 are exposed.
// Also see SetHints regarding reading the pressure sense from DsHidMini's SDF mode.
return type == SDL_GAMEPAD_TYPE_PS3 &&
SDL_GetNumJoystickAxes(cd.joystick) == 16 &&
SDL_GetNumJoystickButtons(cd.joystick) == 11;
SDL_GetNumJoystickAxes(cd.joystick) == 16 &&
SDL_GetNumJoystickButtons(cd.joystick) == 11;
}
+10 -3
View File
@@ -513,8 +513,11 @@ void XInputSource::CheckForStateChanges(u32 index, const XINPUT_STATE& new_state
#define CHECK_AXIS(field, axis, min_value, max_value) \
if (ogp.field != ngp.field) \
{ \
InputManager::InvokeEvents(MakeGenericControllerAxisKey(InputSourceType::XInput, index, axis), \
static_cast<float>(ngp.field) / ((ngp.field < 0) ? min_value : max_value)); \
const float value = static_cast<float>(ngp.field) / ((ngp.field < 0) ? min_value : max_value); \
InputManager::InvokeEvents(MakeGenericControllerAxisKey(InputSourceType::XInput, index, axis), value, \
GenericInputBinding::Unknown, \
s_xinput_generic_binding_axis_mapping[axis][0], \
s_xinput_generic_binding_axis_mapping[axis][1]); \
}
// Y axes is inverted in XInput when compared to SDL.
@@ -560,7 +563,11 @@ void XInputSource::CheckForStateChangesSCP(u32 index, const SCP_EXTN& new_state)
#define CHECK_AXIS(field, mult) \
if (ogp.field != ngp.field) \
{ \
InputManager::InvokeEvents(MakeGenericControllerAxisKey(InputSourceType::XInput, index, axis), ngp.field* mult); \
const float value = ngp.field * mult; \
InputManager::InvokeEvents(MakeGenericControllerAxisKey(InputSourceType::XInput, index, axis), value, \
GenericInputBinding::Unknown, \
s_xinput_generic_binding_axis_mapping[axis][0], \
s_xinput_generic_binding_axis_mapping[axis][1]); \
} \
axis++;
+18 -20
View File
@@ -384,11 +384,22 @@ std::vector<u8>& PINEServer::MakeFailIPC(std::vector<u8>& ret_buffer, uint32_t s
bool PINEServer::AcceptClient()
{
s_msgsock = accept(s_sock, 0, 0);
if (s_msgsock >= 0)
if (s_msgsock < 0)
{
// Gross C-style cast, but SOCKET is a handle on Windows.
Console.WriteLn("PINE: New client with FD %d connected.", (int)s_msgsock);
return true;
// everything else is non recoverable in our scope
// we also mark as recoverable socket errors where it would block a
// non blocking socket, even though our socket is blocking, in case
// we ever have to implement a non blocking socket.
#ifdef _WIN32
const int errno_w = WSAGetLastError();
if (!(errno_w == WSAECONNRESET || errno_w == WSAEINTR || errno_w == WSAEINPROGRESS || errno_w == WSAEMFILE || errno_w == WSAEWOULDBLOCK) && s_sock != INVALID_SOCKET)
Console.Error("PINE: accept() returned error %d", errno_w);
#else
if (!(errno == ECONNABORTED || errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) && s_sock >= 0)
Console.Error("PINE: accept() returned error %d", errno);
#endif
return false;
}
#ifdef __APPLE__
@@ -396,20 +407,9 @@ bool PINEServer::AcceptClient()
setsockopt(s_msgsock, SOL_SOCKET, SO_NOSIGPIPE, &nosigpipe, sizeof(nosigpipe));
#endif
// everything else is non recoverable in our scope
// we also mark as recoverable socket errors where it would block a
// non blocking socket, even though our socket is blocking, in case
// we ever have to implement a non blocking socket.
#ifdef _WIN32
const int errno_w = WSAGetLastError();
if (!(errno_w == WSAECONNRESET || errno_w == WSAEINTR || errno_w == WSAEINPROGRESS || errno_w == WSAEMFILE || errno_w == WSAEWOULDBLOCK) && s_sock != INVALID_SOCKET)
Console.Error("PINE: accept() returned error %d", errno_w);
#else
if (!(errno == ECONNABORTED || errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) && s_sock >= 0)
Console.Error("PINE: accept() returned error %d", errno);
#endif
return false;
// Gross C-style cast, but SOCKET is a handle on Windows.
Console.WriteLn("PINE: New client with FD %d connected.", (int)s_msgsock);
return true;
}
void PINEServer::MainLoop()
@@ -629,8 +629,6 @@ PINEServer::IPCBuffer PINEServer::ParseCommand(std::span<u8> buf, std::vector<u8
}
case MsgVersion:
{
if (!VMManager::HasValidVM())
goto error;
u32 size = strlen(BuildVersion::GitRev) + 7;
if (!SafetyChecks(buf_cnt, 0, ret_cnt, size + 4, buf_size)) [[unlikely]]
goto error;
+6
View File
@@ -755,6 +755,9 @@ Pcsx2Config::GSOptions::GSOptions()
HWAccurateAlphaTest = false;
HWAA1 = false;
UseDebugBlend = false;
HWROV = true;
HWROVLogging = false;
HWROVBarriersVK = false;
ManualUserHacks = false;
UserHacks_AlignSpriteX = false;
@@ -1043,6 +1046,9 @@ void Pcsx2Config::GSOptions::LoadSave(SettingsWrapper& wrap)
SettingsWrapBitBool(HWAccurateAlphaTest);
SettingsWrapBitBool(HWAA1);
SettingsWrapBitBool(UseDebugBlend);
SettingsWrapBitBool(HWROV);
SettingsWrapBitBool(HWROVLogging);
SettingsWrapBitBool(HWROVBarriersVK);
SettingsWrapIntEnumEx(AccurateBlendingUnit, "accurate_blending_unit");
SettingsWrapIntEnumEx(TextureFiltering, "filter");
SettingsWrapIntEnumEx(TexturePreloading, "texture_preloading");
+1 -1
View File
@@ -3,4 +3,4 @@
/// Version number for GS and other shaders. Increment whenever any of the contents of the
/// shaders change, to invalidate the cache.
static constexpr u32 SHADER_CACHE_VERSION = 98; // Last changed in PR 14477
static constexpr u32 SHADER_CACHE_VERSION = 101; // Last changed in PR 14547
+1 -1
View File
@@ -20,7 +20,7 @@
#include <stdexcept>
#include <string>
#define PSXCLK 36864000 /* 36.864 Mhz */
#define PSXCLK 36864000 /* 36.864 MHz */
namespace USB
{
+1 -1
View File
@@ -3252,7 +3252,7 @@ void VMManager::WarnAboutUnsafeSettings()
append(ICON_FA_PAINTBRUSH,
TRANSLATE_SV("VMManager", "Blending Accuracy is below Basic, this may break effects in some games."));
}
if (EmuConfig.GS.HWDownloadMode != GSHardwareDownloadMode::Enabled)
if (EmuConfig.GS.HWDownloadMode > GSHardwareDownloadMode::EnabledForceFull)
{
append(ICON_FA_DOWNLOAD,
TRANSLATE_SV("VMManager", "Hardware Download Mode is not set to Accurate, this may break rendering in some games."));
+1 -1
View File
@@ -62,7 +62,7 @@
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>PrecompiledHeader.h</PrecompiledHeaderFile>
<ForcedIncludeFiles>PrecompiledHeader.h;%(ForcedIncludeFiles)</ForcedIncludeFiles>
<PreprocessorDefinitions>C4_NO_DEBUG_BREAK;ST_NO_EXCEPTION_HANDLING;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>C4_NO_DEBUG_BREAK;ST_NO_EXCEPTION_HANDLING;D3D12MA_USING_DIRECTX_HEADERS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(Platform)'!='ARM64'">ENABLE_RAINTEGRATION;ENABLE_OPENGL;ENABLE_VULKAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(Platform)'=='x64'">XBYAK_NO_EXCEPTION;ZYCORE_STATIC_BUILD;ZYDIS_STATIC_BUILD;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(Platform)'=='ARM64'">VIXL_INCLUDE_TARGET_AARCH64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+5 -8
View File
@@ -65,6 +65,11 @@ bool Host::CopyTextToClipboard(const std::string_view text)
return false;
}
std::string Host::GetTextFromClipboard()
{
return std::string();
}
void Host::BeginTextInput()
{
}
@@ -246,14 +251,6 @@ void Host::OnAchievementsHardcoreModeChanged(bool enabled)
{
}
void Host::OnCoverDownloaderOpenRequested()
{
}
void Host::OnCreateMemoryCardOpenRequested()
{
}
bool Host::LocaleCircleConfirm()
{
return false;