Compare commits

...

153 Commits

Author SHA1 Message Date
lightningterror 1f0f3b92b3 GS/DX12: Expand the fb optimization hit detection. 2026-07-02 11:21:49 +02:00
JordanTheToaster fe3ce7f578 GameDB: Various Gran Turismo fixes 2026-07-01 11:40:37 +02:00
PCSX2 Bot 51c995168a [ci skip] PAD: Update to latest controller database. 2026-06-29 18:57:33 +02:00
PCSX2 Bot f83e4ff4bf [ci skip] Qt: Update Base Translation. 2026-06-28 20:26:52 -04:00
chaoticgd b67e793283 PINE: Disallow access to pages with custom VTLB handlers 2026-06-28 16:46:13 -04:00
chaoticgd d87ec54a5f VTLB: Rewrite vtlb_ramRead function template
Previously the declaration didn't match the definition, so you couldn't
actually call it.
2026-06-28 16:46:13 -04:00
TJnotJT a6b1040caf GS: Update target TEX0 on a move if buffer width is increased.
Co-authored-by: refractionpcsx2
2026-06-28 21:51:10 +02:00
TheLastRar 9650c4b79a GS/HW: Tweak AF shader 2026-06-28 21:46:04 +02:00
wxvu 3f8eb5adf0 GameDB: Various fixes 2026-06-28 13:58:29 -04:00
TJnotJT 99a78f9a55 GS: Add GPU pipeline stats to OSD.
Adds vertex shader and pixel shader invocations.
2026-06-28 12:39:56 -04:00
TJnotJT c3ace64205 GS/HW: Change prefer_unused_texture parameter to prefer_reuse in GSDevice::FetchSurface().
prefer_reuse is the opposite of prefer_unused_texture, so all corresponding arguments are inverted.
2026-06-28 12:13:38 -04:00
TJnotJT 3b2c1ec6e3 GS/DX12: Fix DX12 renderpass restart optimization rebase error. 2026-06-28 12:13:38 -04:00
TJnotJT dba432639e GS/VK: Detect max supported framebuffer dimensions for null framebuffer. 2026-06-28 12:13:38 -04:00
TJnotJT 166c4491c2 GS/HW: ROV activation revisions.
Force color ROV if depth ROV is used.
Only force color/depth ROV if the draw actually uses both color/depth (fixes a bug with temporary DS for stencil being used as a ROV).
Simplify the ROV activation heuristic a bit (removed unused variables, simplify barrier counting, etc.)
2026-06-28 12:13:38 -04:00
TJnotJT d1cd5a62ed GS/HW: ROV revisions.
Use GSTexture::FeedbackTarget RTs by default (not GSTexture::ShaderWriteTarget).
Do ROV type conversion for both color and depth (if needed).
Make sure the formats for ROV actually support UAV/storage image.
Change 'PSSetUnorderedAccess' to 'PSSetROVs' for clarity.
DX12: Use enum names instead of raw constants in PSSetShaderResource().
Some string format cleanup to GL_*() macros.
VK: Do not enable ROV if FB fetch is available.
Other misc cosmetic changes such as whitespace, etc.

Co-authored-by: TellowKrinkle
2026-06-28 12:13:38 -04:00
TJnotJT b11a406519 GS: Change GSTexture::Type to GSTexture::Usage
Usage fits more cleanly with API flags.
Also separates out feedback and write usage from render target.

Co-authored-by: TellowKrinkle
2026-06-28 12:13:38 -04:00
TJnotJT c7042e1877 GS/HW: Use CreateCompatible() in texture cache to propagate correct formats.
Co-authored-by: TellowKrinkle
2026-06-28 12:13:38 -04:00
Devloon 896e0b82c2 Qt: show full game titles in grid mode.
Added UI element with new icon to toggle showing full game titles by wrapping around to the next line.
2026-06-28 11:55:18 -04:00
chaoticgd 7856757087 AudioStream: Don't compare uninitialized padding 2026-06-27 16:51:29 -04:00
chaoticgd b0ce5d7582 Debugger: Fix some uninitialized variables 2026-06-27 16:51:29 -04:00
Ziemas e23416aa92 IR3000A: Use SmallString with Zydis formatter 2026-06-27 14:26:08 -04:00
Ziemas 9a9cfca706 IR5900: Use SmallString with Zydis formatter 2026-06-27 14:26:08 -04:00
Ziemas 59feb88ef7 IR5900: fix recMemcheck to not cache loaded reg
This functions calls out of the JIT, so it will get stomped on by
something else.
2026-06-27 14:26:08 -04:00
Ziemas 247cda0e26 IR3000A: DUMP_BLOCKS: Symbolize text ptr relative 2026-06-27 14:26:08 -04:00
Ziemas 9f3233224e IR5900: DUMP_BLOCKS: Symbolize text ptr relative 2026-06-27 14:26:08 -04:00
PCSX2 Bot ef14238b4e [ci skip] Qt: Update Base Translation. 2026-06-27 09:02:47 +02:00
lightningterror 13b7345189 GS/HW: Fix bbox when using aa1.
Suggested by TJ.
2026-06-27 09:02:11 +02:00
JordanTheToaster 48ca476a2f GameSummary: Redirect to redump.info 2026-06-26 10:35:51 +02:00
JordanTheToaster 57ef8e7c25 GameDB: Various fixes 2026-06-26 10:35:51 +02:00
JordanTheToaster d09ce46700 GameDB: Add AA1 rec to the DB 2026-06-26 10:35:51 +02:00
JordanTheToaster 3afc1c2663 GameDB: Add AAT rec to the DB 2026-06-26 10:35:51 +02:00
JordanTheToaster 5821762870 ImGuiOverlay: Add ROV to GPU status 2026-06-26 10:35:51 +02:00
JordanTheToaster 86b7a19f19 GS/DX11/DX12: Report ROV support in log 2026-06-26 10:35:51 +02:00
PCSX2 Bot cc85cef17c [ci skip] Qt: Update Base Translation. 2026-06-25 20:54:38 -04:00
SternXD 5f08f523c2 Qt: Add sorting to memory card list 2026-06-25 21:04:04 +02:00
KamFretoZ b612959682 Qt/GameListBackground: Dont keep re-encoding frames 2026-06-25 21:03:07 +02:00
KamFretoZ aea83d5ece Qt/GameListBackground: Make focus detection less agressive
So that custom background can still play when child window is focused
2026-06-25 21:03:07 +02:00
KamFretoZ 07aa5786e5 Qt/GameListBackground: Small fixes
fix possibly wrong DPR calculation on HiDPI display and add processBackgroundFrame() to showEvent
2026-06-25 21:03:07 +02:00
KamFretoZ 5bd70aff90 Qt/GameListBackground: Make highlight semi-transparent
and a semi-transparent backdrop to "No Game Detected" screen
2026-06-25 21:03:07 +02:00
KamFretoZ 22b8d52311 Qt/GameListBackground: Auto adjust gamelist type icon color 2026-06-25 21:03:07 +02:00
KamFretoZ ee6c7e6286 Qt/GameListBackground: Auto adjust gamelist text color 2026-06-25 21:03:07 +02:00
lightningterror 66ea4c4967 GS/DX11: Backport ds as rt from GL for depth feedback.
Doing gpu depth copies is quite slower on dx11, so let's
use the ds as rt behavior from GL which would allow us
to treat them as rt copies instead which are faster.
2026-06-25 20:56:58 +02:00
lightningterror a6769818fd GS/HW: DATE setup changes.
DATE one can run on complex alpha test if there's no overlap.
Make sure DATE one doesn't run if complex alpha test is enabled and there's overlap.
Enable DATE barrier on complex alpha test if there's overlap.
2026-06-24 16:02:45 +02:00
refractionpcsx2 95535215aa GS/TC: Use ValidOverlaps check for exact Tex is RT matches 2026-06-23 03:13:35 +02:00
TJnotJT eadb46c5ac GS/DX12: Remove unnecessary UAV state and simplify texture unbinding. 2026-06-23 02:59:46 +02:00
PCSX2 Bot b2b7943dbe [ci skip] PAD: Update to latest controller database. 2026-06-22 14:48:03 -04:00
dependabot[bot] a0a261db50 Bump actions/checkout in the ci-deps group across 1 directory
Bumps the ci-deps group with 1 update in the / directory: [actions/checkout](https://github.com/actions/checkout).


Updates `actions/checkout` from 6 to 7
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: ci-deps
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-22 10:26:36 -04:00
lightningterror 8e73dea7e2 GS/GL: Fix recent aa1 shader regressions.
Line was accidentally removed and macro typo.
2026-06-21 01:17:52 +02:00
TJnotJT e2a3670d65 GS/HW: Adjust line AA1 blur with upscaling. 2026-06-20 22:09:33 +02:00
jasaaved 342db51523 FullscreenUI: Fix controller navigation breaking after Big Picture Mode window resized
If Big Picture mode's window is resized too greatly, the window's NavRectRel are stale and controller navigation breaks. This also happens when toggling fullscreen.

Now we force a nav reinit when resizing.
2026-06-19 23:40:42 +02:00
chaoticgd 414f1660ed Memory: Fix typo in SysMemory::DumpMemoryMap 2026-06-19 16:43:10 -04:00
PCSX2 Bot cd9530eec3 [ci skip] Qt: Update Base Translation. 2026-06-16 02:41:59 +02:00
PCSX2 Bot 53675cf5ce [ci skip] PAD: Update to latest controller database. 2026-06-15 19:42:12 +02:00
Bobby Smith e0566546c6 OSD: Include "Show GPU Debug Info" when using the "De/Select All" buttons 2026-06-15 18:41:24 +02:00
TJnotJT 6c4a8efe25 QT/FSUI: Set ROV default off and move the settings to Graphics>Rendering.
ROV may have negative performance impacts on some games/systems so allow users to opt in. Also it needs further revision/refinement.
2026-06-15 18:31:08 +02:00
lightningterror a8db6587cb Qt: Disable VK Rov barrier toggle on other renderers. 2026-06-15 18:28:55 +02:00
PCSX2 Bot 1d8fb3d407 [ci skip] Qt: Update Base Translation. 2026-06-13 20:29:29 -04:00
Oleg Tolmatcev 2d09d53805 Misc: clean up COM code 2026-06-13 15:40:44 -04:00
SternXD f5310f0ca4 Achievements: Group leaderboards by subset and tracking state 2026-06-13 15:35:03 -04:00
KamFretoZ 972fcfa0f0 Qt: Fix Formatting 2026-06-13 15:34:46 -04:00
KamFretoZ b0582f6ea3 Qt/OSD: Fix GPU stats not updating when Show GPU Usage settings arent enabled 2026-06-13 15:34:46 -04:00
KamFretoZ 8b6c28b374 Qt/Shortcut: Use Nearest-Neighbor scaling for icon preview
No need to get fancy
2026-06-13 15:34:46 -04:00
KamFretoZ 0a3c27765d Achievements: Simplify sound effects playback 2026-06-13 15:34:46 -04:00
KamFretoZ 040a8b8fc6 Qt: Fix Status Bar visibility when Languange Changes 2026-06-13 15:34:46 -04:00
KamFretoZ 9f67f72d3c Qt: Revamp the status bar 2026-06-13 15:34:46 -04:00
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
chaoticgd e1fd632ba3 CI: Add a debug build to the Linux build matrix 2026-06-01 09:26:11 -04:00
refractionpcsx2 fbaba52bc2 GS/HW: Fix bad assert causing debug build failure 2026-06-01 12:08:28 +01:00
PCSX2 Bot 224e82f33e [ci skip] Qt: Update Base Translation. 2026-06-01 05:27:17 +02:00
TJnotJT 8df49d7676 GS/HW: Fix a number of compiler warnings.
Missing default case in switch statement.
Signed/unsigned implict conversion.
Unused variables.
Hidden virtual overloaded function.
2026-05-31 22:38:31 +02:00
JordanTheToaster 88d8c561db Settings: Enable Skip Duplicate Frames by default 2026-05-31 15:11:02 -04:00
Tom Pritchard 78697466e0 Cli: Fix gamearg processing when using fast boot 2026-05-31 15:10:35 -04:00
chaoticgd 5e150cd3eb SaveState: Fix compression settings 2026-05-31 15:10:17 -04:00
chaoticgd 23dd23b468 CMake: Don't ship Qt Network on Windows 2026-05-31 15:10:06 -04:00
KamFretoZ 7d47dab728 Translation: Add Azərbaycanca 2026-05-31 15:02:29 -04:00
TJnotJT 74f2f73ce6 GS/HW: Use an enum instead of boolean to specify nearest/bilinear filtering. 2026-05-31 18:43:01 +02:00
TJnotJT 1e2f60a9b8 GS/HW: Ignore HW bilinear flag for shaders that do SW bilinear.
Co-authored-by: TellowKrinkle
2026-05-31 18:43:01 +02:00
TJnotJT bf892e9ece GS/HW: Treat depth input as R32 color input in convert shaders.
Co-authored-by: TellowKrinkle
2026-05-31 18:43:01 +02:00
TJnotJT a44fe80caa GS/HW: Refactor shader copy and texture formats.
Make it easier to add new depth formats, and code cleanup.

Co-authored-by: TellowKrinkle
2026-05-31 18:43:01 +02:00
chaoticgd 093287f6bf Console: Improve logic for outputting color codes 2026-05-31 15:35:22 +02:00
lightningterror e0365c1d10 GS/GL: Discard stencil when not in use while using depth.
Backport from DX12, clear the stencil when it's not used.
2026-05-30 22:02:09 +02:00
Ty 6cb2086dfc GHActions: Don't automatically undraft stable releases 2026-05-30 17:11:07 +02:00
TJnotJT 3f4b424200 GS/TC: Refactor/cleanup preload function. 2026-05-30 10:47:47 -04:00
refractionpcsx2 051fdd55b0 GS: Fix xy_tail buffers when draw buffering is enabled 2026-05-30 15:40:58 +02:00
refractionpcsx2 29f4318ef8 GS/TC: Fix combine targets scale and valid areas 2026-05-30 15:40:44 +02:00
lightningterror 30d7fed0e5 GS/DX12: Discard stencil when not in use while using depth.
Because the same texture is shared and used for both depth and stencil we want to discard when the stencil is not used instead of no access as that gives a validation error.
2026-05-30 15:02:22 +02:00
refractionpcsx2 f0177a879c HW/TC: Only make 8bit target sources temporary if they have MIP levels 2026-05-30 15:00:54 +02:00
refractionpcsx2 88af1e54a4 GS/HW: Downscale target before 8bit conversion 2026-05-30 15:00:54 +02:00
PCSX2 Bot d3ba0542f2 [ci skip] Qt: Update Base Translation. 2026-05-30 14:53:01 +02:00
TJnotJT 4d25bdcabb GS/DX11/GL: Adapt full barrier multidraw FB copy for sampling RT/DS hazards. 2026-05-30 14:52:42 +02:00
TJnotJT 02c5f8f648 GS/HW: Fix offset draw vertex trace/draw rect. 2026-05-29 23:27:33 +02:00
182 changed files with 39804 additions and 7396 deletions
@@ -11,7 +11,7 @@ jobs:
name: "Update Base Translation"
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Update Base Translation
run: ./.github/workflows/scripts/common/update_base_translation.sh
@@ -9,7 +9,7 @@ jobs:
if: github.repository == 'PCSX2/pcsx2'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Get Latest DB and Prepare DB File
run: |
+1 -1
View File
@@ -18,7 +18,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v6
uses: actions/checkout@v7
- name: Install Packages
run: |
+18 -1
View File
@@ -51,7 +51,7 @@ jobs:
steps:
- name: Checkout Repository
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
set-safe-directory: ${{ env.GITHUB_WORKSPACE }}
# 10 here, since the odds of having 10 untagged commits in a row should be slim to none
@@ -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@v7
- 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
+12
View File
@@ -20,6 +20,18 @@ jobs:
cmakeflags: ""
buildAppImage: true
secrets: inherit
build_linux_debug:
name: "Debug"
if: github.repository != 'PCSX2/pcsx2' || github.event_name == 'pull_request'
uses: ./.github/workflows/linux_build_qt.yml
with:
jobName: "Debug Build"
artifactPrefixName: "PCSX2-linux-Qt-x64-debug"
compiler: clang
cmakeflags: ""
buildAppImage: true
cmakeBuildType: Debug
secrets: inherit
build_linux_flatpak:
name: "Flatpak"
if: github.repository != 'PCSX2/pcsx2' || github.event_name == 'pull_request'
+6 -2
View File
@@ -39,6 +39,10 @@ on:
required: false
type: boolean
default: false
cmakeBuildType:
required: false
type: string
default: Release
jobs:
build_linux:
@@ -55,7 +59,7 @@ jobs:
steps:
- name: Checkout Repository
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
fetch-depth: 0
@@ -129,7 +133,7 @@ jobs:
ADDITIONAL_CMAKE_ARGS: ${{ inputs.cmakeflags }}
run: |
cmake -B build -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_BUILD_TYPE="${{ inputs.cmakeBuildType }}" \
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON \
-DCMAKE_PREFIX_PATH="$HOME/deps" \
-DCMAKE_C_COMPILER=clang-17 \
+1 -1
View File
@@ -52,7 +52,7 @@ jobs:
steps:
- name: Checkout Repository
uses: actions/checkout@v6
uses: actions/checkout@v7
# actions/checkout elides tags, fetch them primarily for releases
- name: Fetch Tags
+3 -2
View File
@@ -35,7 +35,7 @@ jobs:
outputs:
new_tag: ${{ steps.tag_version.outputs.new_tag }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
# Docs - https://github.com/mathieudutour/github-tag-action
- name: Bump Version and Push Tag
@@ -176,7 +176,7 @@ jobs:
name: "Upload Artifacts"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Prepare Artifact Folder
run: mkdir ./ci-artifacts/
@@ -209,6 +209,7 @@ jobs:
gh release upload "${TAG_VAL}" ${{ github.WORKSPACE }}/ci-artifacts/out/* --repo PCSX2/pcsx2 --clobber
- name: Publish Release
if: github.event_name != 'workflow_dispatch' || inputs.is_prerelease == 'true'
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
@@ -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
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
runs-on: windows-2025-vs2026
steps:
- name: Checkout Repository
uses: actions/checkout@v6
uses: actions/checkout@v7
- name: Verify VS Project Files
run: .github\workflows\scripts\windows\validate-vs-filters.ps1
+1 -1
View File
@@ -56,7 +56,7 @@ jobs:
steps:
- name: Checkout Repository
uses: actions/checkout@v6
uses: actions/checkout@v7
- name: Configure MSBuild Clang Version
if: inputs.configuration != 'CMake'
+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 = {};
File diff suppressed because it is too large Load Diff
+23 -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,
@@ -939,6 +941,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000503200000110000045010000,Atari VCS Classic,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b3,start:b2,platform:Mac OS X,
03000000503200000110000047010000,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b3,start:b2,platform:Mac OS X,
03000000503200000210000047010000,Atari VCS Modern Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a2,righty:a3,start:b8,x:b2,y:b3,platform:Mac OS X,
03000000e30500008106000000010000,Austgame GameCube Adapter,a:b0,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:+a4,leftx:a0,lefty:a1,misc3:b5,misc4:b6,rightshoulder:b4,righttrigger:-a4,rightx:a2,righty:a3,start:b7,x:b1,y:b3,platform:Mac OS X,
030000008a3500000102000000010000,Backbone One,a:b0,b:b1,back:b16,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b17,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b15,x:b2,y:b3,platform:Mac OS X,
030000008a3500000201000000010000,Backbone One,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:Mac OS X,
030000008a3500000202000000010000,Backbone One,a:b0,b:b1,back:b16,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b17,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b15,x:b2,y:b3,platform:Mac OS X,
@@ -1116,6 +1119,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,
@@ -1249,12 +1253,16 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000c82d00001230000011010000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000c82d00001330000011010000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
05000000c82d00001230000000010000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000c82d00001260000011010000,8BitDo Ultimate 2,a:b0,b:b1,x:b3,y:b4,back:b10,guide:b12,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,paddle1:b17,paddle2:b16,paddle3:b2,paddle4:b5,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4,platform:Linux,
03000000c82d00000b31000014010000,8BitDo Ultimate 2,a:b0,b:b1,x:b2,y:b3,back:b6,guide:b8,start:b7,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,
05000000c82d00001260000001000000,8BitDo Ultimate 2,a:b0,b:b1,x:b3,y:b4,back:b10,guide:b12,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,paddle1:b17,paddle2:b16,paddle3:b2,paddle4:b5,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4,platform:Linux,
03000000c82d00000a31000014010000,8BitDo Ultimate 2C,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000c82d00001d30000011010000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
05000000c82d00001b30000001000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000c82d00001530000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000c82d00001630000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000c82d00001730000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
06000000c82d00000320000007010000,8BitDo Ultimate Wired Controller for Xbox,a:b0,b:b1,x:b2,y:b3,back:b6,guide:b8,start:b7,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,
03000000c82d00000121000011010000,8BitDo Xbox One SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
05000000c82d00000121000000010000,8BitDo Xbox One SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
05000000a00500003232000001000000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,
@@ -1276,6 +1284,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000020500000913000010010000,Anbernic RG P01,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,
03000000373500000710000010010000,Anbernic RG P01,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,
05000000373500004610000001000000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
19000000010000000100000000010000,Anbernic RG28XX Controller,a:b4,b:b3,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b7,misc1:b1,rightshoulder:b8,leftstick:b12,rightstick:b13,start:b10,x:b5,y:b6,platform:Linux,
03000000190e00000110000010010000,Aquaplus Piece,a:b1,b:b0,back:b3,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b2,platform:Linux,
03000000790000003018000011010000,Arcade Fightstick F300,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,
03000000a30c00002700000011010000,Astro City Mini,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,
@@ -1348,6 +1357,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
05000000ac0500002d0200001b010000,GameSir G4s,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b33,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,
03000000ac0500007a05000011010000,GameSir G5,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:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b16,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000373500009710000001020000,GameSir Kaleid Flux,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000373500005810000001020000,GameSir K1,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000ac0500001a06000011010000,GameSir T3 2.02,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,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,
03000000373500000410000044010000,GameSir T4 Kaleid,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,
03000000bc2000005656000011010000,GameSir T4w,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,
@@ -1446,6 +1456,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000242f00008a00000011010000,JYS Adapter,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Linux,
03000000314100001935000000010000,LeadJoy Xeno Plus,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:a4,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000006f0e00000103000000020000,Logic3 Controller,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,
030000006f0e000001f5000010010000,Logic3 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,
030000006d040000d1ca000000000000,Logitech Chillstream,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:a3,start:b9,x:b0,y:b3,platform:Linux,
030000006d040000d1ca000011010000,Logitech Chillstream,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:a3,start:b9,x:b0,y:b3,platform:Linux,
030000006d04000016c2000010010000,Logitech Dual Action,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:a3,start:b9,x:b0,y:b3,platform:Linux,
@@ -1536,10 +1547,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,
@@ -1618,6 +1629,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000d62000000520000050010000,PowerA Xbox Series Controller,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,
03000000d62000000b20000001010000,PowerA Xbox Series Controller,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,
03000000d62000000f20000001010000,PowerA Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b7,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,
03000000d62000006520000002010000,PowerA Xbox Series Controller,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,
030000006d040000d2ca000011010000,Precision Controller,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:a3,start:b9,x:b0,y:b3,platform:Linux,
03000000250900000017000010010000,PS SS N64 Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b5,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2~,righty:a3,start:b8,platform:Linux,
03000000ff1100004133000010010000,PS2 Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,
@@ -1751,7 +1763,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 +1810,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,
+8
View File
@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-az" viewBox="0 0 640 480">
<path fill="#3f9c35" d="M.1 0h640v480H.1z"/>
<path fill="#ed2939" d="M.1 0h640v320H.1z"/>
<path fill="#00b9e4" d="M.1 0h640v160H.1z"/>
<circle cx="304" cy="240" r="72" fill="#fff"/>
<circle cx="320" cy="240" r="60" fill="#ed2939"/>
<path fill="#fff" d="m384 200 7.7 21.5 20.6-9.8-9.8 20.7L424 240l-21.5 7.7 9.8 20.6-20.6-9.8L384 280l-7.7-21.5-20.6 9.8 9.8-20.6L344 240l21.5-7.7-9.8-20.6 20.6 9.8z"/>
</svg>

After

Width:  |  Height:  |  Size: 501 B

+242 -138
View File
@@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#if defined(VERTEX_SHADER)
struct VS_INPUT
{
float4 p : POSITION;
@@ -15,6 +17,21 @@ struct VS_OUTPUT
float4 c : COLOR;
};
VS_OUTPUT vs_main(VS_INPUT input)
{
VS_OUTPUT output;
output.p = input.p;
output.t = input.t;
output.c = input.c;
return output;
}
#endif // VERTEX_SHADER
#if defined(PIXEL_SHADER)
cbuffer cb0 : register(b0)
{
float4 BGColor;
@@ -33,11 +50,22 @@ static const float3x3 rgb2yuv =
Texture2D Texture;
SamplerState TextureSampler;
#if HAS_FLOAT32_INPUT
float sample_c(float2 uv)
{
return Texture.Sample(TextureSampler, uv).r;
}
#else
float4 sample_c(float2 uv)
{
return Texture.Sample(TextureSampler, uv);
}
#endif
struct PS_INPUT
{
float4 p : SV_Position;
@@ -45,36 +73,117 @@ struct PS_INPUT
float4 c : COLOR;
};
#if HAS_INTEGER_OUTPUT
#define OUTPUT_TYPE uint
#define OUTPUT_SV SV_Target
#elif HAS_DEPTH_OUTPUT
#define OUTPUT_TYPE float
#define OUTPUT_SV SV_Depth
#elif HAS_FLOAT32_OUTPUT
#define OUTPUT_TYPE float
#define OUTPUT_SV SV_Target
#else
#define OUTPUT_TYPE float4
#define OUTPUT_SV SV_Target
#endif
struct PS_OUTPUT
{
float4 c : SV_Target0;
OUTPUT_TYPE o : OUTPUT_SV;
};
VS_OUTPUT vs_main(VS_INPUT input)
uint rgba8_to_uint(float4 c)
{
VS_OUTPUT output;
output.p = input.p;
output.t = input.t;
output.c = input.c;
return output;
uint4 i = uint4(c * 255.5f) & 0xFFu;
return i.r | (i.g << 8) | (i.b << 16) | (i.a << 24);
}
PS_OUTPUT ps_copy(PS_INPUT input)
uint rgb5a1_to_uint(float4 c)
{
PS_OUTPUT output;
output.c = sample_c(input.t);
return output;
uint4 i = uint4(c * 255.5f) & uint4(0xF8u, 0xF8u, 0xF8u, 0x80u);
return (i.r >> 3) | (i.g << 2) | (i.b << 7) | (i.a << 8);
}
float ps_depth_copy(PS_INPUT input) : SV_Depth
uint depth_to_uint(float d)
{
return sample_c(input.t).r;
return uint(d * exp2(32.0f));
}
float4 uint_to_rgba8(uint i)
{
return float4((i & 0xFFu), ((i >> 8) & 0xFFu), ((i >> 16) & 0xFFu), ((i >> 24) & 0xFFu)) / 255.0f;
}
float4 uint_to_rgb5a1(uint i)
{
return float4(uint4(i << 3, i >> 2, i >> 7, i >> 8) & uint4(0xF8u, 0xF8u, 0xF8u, 0x80u)) / 255.0f;
}
float uint_to_depth32(uint i)
{
return float(i) * exp2(-32.0f);
}
float uint_to_depth24(uint i)
{
return float(i & 0xFFFFFFu) * exp2(-32.0f);
}
float uint_to_depth16(uint i)
{
return float(i & 0xFFFFu) * exp2(-32.0f);
}
float rgba8_to_depth32(float4 val)
{
return uint_to_depth32(rgba8_to_uint(val));
}
float rgba8_to_depth24(float4 val)
{
return uint_to_depth24(rgba8_to_uint(val));
}
float rgba8_to_depth16(float4 val)
{
return uint_to_depth16(rgba8_to_uint(val));
}
float rgb5a1_to_depth16(float4 val)
{
return uint_to_depth16(rgb5a1_to_uint(val));
}
float4 depth32_to_rgba8(float d)
{
return uint_to_rgba8(depth_to_uint(d));
}
float4 depth16_to_rgb5a1(float d)
{
return uint_to_rgb5a1(depth_to_uint(d));
}
float depth32_to_depth24(float d)
{
return uint_to_depth24(depth_to_uint(d));
}
#if defined(__ps_copy__)
OUTPUT_TYPE ps_copy(PS_INPUT input) : OUTPUT_SV
{
return sample_c(input.t);
}
#endif
#if defined(__ps_depth_copy__)
OUTPUT_TYPE ps_depth_copy(PS_INPUT input) : OUTPUT_SV
{
return sample_c(input.t);
}
#endif
#if defined(__ps_downsample_copy__)
PS_OUTPUT ps_downsample_copy(PS_INPUT input)
{
int DownsampleFactor = DOFFSET;
@@ -85,179 +194,134 @@ PS_OUTPUT ps_downsample_copy(PS_INPUT input)
int2 coord = max(int2(input.p.xy) * DownsampleFactor, ClampMin);
PS_OUTPUT output;
output.c = (float4)0;
output.o = (float4)0;
for (int yoff = 0; yoff < DownsampleFactor; yoff++)
{
for (int xoff = 0; xoff < DownsampleFactor; xoff++)
output.c += Texture.Load(int3(coord + int2(xoff * step_multiplier, yoff * step_multiplier), 0));
output.o += Texture.Load(int3(coord + int2(xoff * step_multiplier, yoff * step_multiplier), 0));
}
output.c /= Weight;
output.o /= Weight;
return output;
}
#endif
#if defined(__ps_filter_transparency__)
PS_OUTPUT ps_filter_transparency(PS_INPUT input)
{
PS_OUTPUT output;
float4 c = sample_c(input.t);
output.c = float4(c.rgb, 1.0);
output.o = float4(c.rgb, 1.0);
return output;
}
#endif
// Need to be careful with precision here, it can break games like Spider-Man 3 and Dogs Life
uint ps_convert_rgba8_16bits(PS_INPUT input) : SV_Target0
#if defined(__ps_convert_rgb5a1_16bits__)
OUTPUT_TYPE ps_convert_rgb5a1_16bits(PS_INPUT input) : OUTPUT_SV
{
uint4 i = sample_c(input.t) * float4(255.5f, 255.5f, 255.5f, 255.5f);
return ((i.x & 0x00F8u) >> 3) | ((i.y & 0x00F8u) << 2) | ((i.z & 0x00f8u) << 7) | ((i.w & 0x80u) << 8);
// Need to be careful with precision here, it can break games like Spider-Man 3 and Dogs Life
return rgb5a1_to_uint(sample_c(input.t));
}
#endif
#if defined(__ps_datm1__)
void ps_datm1(PS_INPUT input)
{
clip(sample_c(input.t).a - 127.5f / 255); // >= 0x80 pass
}
#endif
#if defined(__ps_datm0__)
void ps_datm0(PS_INPUT input)
{
clip(127.5f / 255 - sample_c(input.t).a); // < 0x80 pass (== 0x80 should not pass)
}
#endif
#if defined(__ps_datm1_rta_correction__)
void ps_datm1_rta_correction(PS_INPUT input)
{
clip(sample_c(input.t).a - 254.5f / 255); // >= 0x80 pass
}
#endif
#if defined(__ps_datm0_rta_correction__)
void ps_datm0_rta_correction(PS_INPUT input)
{
clip(254.5f / 255 - sample_c(input.t).a); // < 0x80 pass (== 0x80 should not pass)
}
#endif
#if defined(__ps_rta_correction__)
PS_OUTPUT ps_rta_correction(PS_INPUT input)
{
PS_OUTPUT output;
float4 value = sample_c(input.t);
output.c = float4(value.rgb, value.a / (128.25f / 255.0f));
output.o = float4(value.rgb, value.a / (128.25f / 255.0f));
return output;
}
#endif
#if defined(__ps_rta_decorrection__)
PS_OUTPUT ps_rta_decorrection(PS_INPUT input)
{
PS_OUTPUT output;
float4 value = sample_c(input.t);
output.c = float4(value.rgb, value.a * (128.25f / 255.0f));
output.o = float4(value.rgb, value.a * (128.25f / 255.0f));
return output;
}
#endif
#if defined(__ps_colclip_init__)
PS_OUTPUT ps_colclip_init(PS_INPUT input)
{
PS_OUTPUT output;
float4 value = sample_c(input.t);
output.c = float4(round(value.rgb * 255) / 65535, value.a);
output.o = float4(round(value.rgb * 255) / 65535, value.a);
return output;
}
#endif
#if defined(__ps_colclip_resolve__)
PS_OUTPUT ps_colclip_resolve(PS_INPUT input)
{
PS_OUTPUT output;
float4 value = sample_c(input.t);
output.c = float4(float3(uint3(value.rgb * 65535.5) & 255) / 255, value.a);
output.o = float4(float3(uint3(value.rgb * 65535.5) & 255) / 255, value.a);
return output;
}
#endif
uint ps_convert_float32_32bits(PS_INPUT input) : SV_Target0
#if defined(__ps_convert_depth32_32bits__)
OUTPUT_TYPE ps_convert_depth32_32bits(PS_INPUT input) : OUTPUT_SV
{
// Convert a FLOAT32 depth texture into a 32 bits UINT texture
return uint(exp2(32.0f) * sample_c(input.t).r);
// Convert a depth texture into a 32 bits UINT texture
return depth_to_uint(sample_c(input.t));
}
#endif
PS_OUTPUT ps_convert_float32_rgba8(PS_INPUT input)
#if defined(__ps_convert_depth32_rgba8__)
OUTPUT_TYPE ps_convert_depth32_rgba8(PS_INPUT input) : OUTPUT_SV
{
PS_OUTPUT output;
// Convert a FLOAT32 depth texture into a RGBA color texture
uint d = uint(sample_c(input.t).r * exp2(32.0f));
output.c = float4(uint4((d & 0xFFu), ((d >> 8) & 0xFFu), ((d >> 16) & 0xFFu), (d >> 24))) / 255.0f;
return output;
// Convert a depth texture into a RGBA color texture
return depth32_to_rgba8(sample_c(input.t));
}
#endif
PS_OUTPUT ps_convert_float16_rgb5a1(PS_INPUT input)
#if defined(__ps_convert_depth16_rgb5a1__)
OUTPUT_TYPE ps_convert_depth16_rgb5a1(PS_INPUT input) : OUTPUT_SV
{
PS_OUTPUT output;
// Convert a FLOAT32 (only 16 lsb) depth into a RGB5A1 color texture
uint d = uint(sample_c(input.t).r * exp2(32.0f));
output.c = float4(uint4(d << 3, d >> 2, d >> 7, d >> 8) & uint4(0xf8, 0xf8, 0xf8, 0x80)) / 255.0f;
return output;
// Convert depth (only 16 lsb) into a RGB5A1 color texture
return depth16_to_rgb5a1(sample_c(input.t));
}
#endif
float rgba8_to_depth32(float4 val)
{
uint4 c = uint4(val * 255.5f);
return float(c.r | (c.g << 8) | (c.b << 16) | (c.a << 24)) * exp2(-32.0f);
}
float rgba8_to_depth24(float4 val)
{
uint3 c = uint3(val.rgb * 255.5f);
return float(c.r | (c.g << 8) | (c.b << 16)) * exp2(-32.0f);
}
float rgba8_to_depth16(float4 val)
{
uint2 c = uint2(val.rg * 255.5f);
return float(c.r | (c.g << 8)) * exp2(-32.0f);
}
float rgb5a1_to_depth16(float4 val)
{
uint4 c = uint4(val * 255.5f);
return float(((c.r & 0xF8u) >> 3) | ((c.g & 0xF8u) << 2) | ((c.b & 0xF8u) << 7) | ((c.a & 0x80u) << 8)) * exp2(-32.0f);
}
float ps_convert_float32_depth_to_color(PS_INPUT input) : SV_Target0
{
return sample_c(input.t).r;
}
float ps_convert_float32_color_to_depth(PS_INPUT input) : SV_Depth
{
return sample_c(input.t).r;
}
float ps_convert_float32_float24(PS_INPUT input) : SV_Depth
#if defined(__ps_convert_depth32_depth24__)
OUTPUT_TYPE ps_convert_depth32_depth24(PS_INPUT input) : OUTPUT_SV
{
// Truncates depth value to 24bits
uint d = uint(sample_c(input.t).r * exp2(32.0f)) & 0xFFFFFFu;
return float(d) * exp2(-32.0f);
}
float ps_convert_rgba8_float32(PS_INPUT input) : SV_Depth
{
// Convert an RGBA texture into a float depth texture
return rgba8_to_depth32(sample_c(input.t));
}
float ps_convert_rgba8_float24(PS_INPUT input) : SV_Depth
{
// Same as above but without the alpha channel (24 bits Z)
// Convert an RGBA texture into a float depth texture
return rgba8_to_depth24(sample_c(input.t));
}
float ps_convert_rgba8_float16(PS_INPUT input) : SV_Depth
{
// Same as above but without the A/B channels (16 bits Z)
// Convert an RGBA texture into a float depth texture
return rgba8_to_depth16(sample_c(input.t));
}
float ps_convert_rgb5a1_float16(PS_INPUT input) : SV_Depth
{
// Convert an RGB5A1 (saved as RGBA8) color to a 16 bit Z
return rgb5a1_to_depth16(sample_c(input.t));
return depth32_to_depth24(sample_c(input.t));
}
#endif
#define SAMPLE_RGBA_DEPTH_BILN(CONVERT_FN) \
uint width, height; \
@@ -272,34 +336,57 @@ float ps_convert_rgb5a1_float16(PS_INPUT input) : SV_Depth
float depthBR = CONVERT_FN(Texture.Load(int3(coords.zw, 0))); \
return lerp(lerp(depthTL, depthTR, mix_vals.x), lerp(depthBL, depthBR, mix_vals.x), mix_vals.y);
float ps_convert_rgba8_float32_biln(PS_INPUT input) : SV_Depth
#if defined(__ps_convert_rgba8_depth32__)
OUTPUT_TYPE ps_convert_rgba8_depth32(PS_INPUT input) : OUTPUT_SV
{
// Convert an RGBA texture into a float depth texture
#if HAS_BILN
SAMPLE_RGBA_DEPTH_BILN(rgba8_to_depth32);
#else
return rgba8_to_depth32(sample_c(input.t));
#endif
}
#endif
float ps_convert_rgba8_float24_biln(PS_INPUT input) : SV_Depth
#if defined(__ps_convert_rgba8_depth24__)
OUTPUT_TYPE ps_convert_rgba8_depth24(PS_INPUT input) : OUTPUT_SV
{
// Same as above but without the alpha channel (24 bits Z)
// Convert an RGBA texture into a float depth texture
#if HAS_BILN
SAMPLE_RGBA_DEPTH_BILN(rgba8_to_depth24);
#else
return rgba8_to_depth24(sample_c(input.t));
#endif
}
#endif
float ps_convert_rgba8_float16_biln(PS_INPUT input) : SV_Depth
#if defined(__ps_convert_rgba8_depth16__)
OUTPUT_TYPE ps_convert_rgba8_depth16(PS_INPUT input) : OUTPUT_SV
{
// Same as above but without the A/B channels (16 bits Z)
// Convert an RGBA texture into a float depth texture
#if HAS_BILN
SAMPLE_RGBA_DEPTH_BILN(rgba8_to_depth16);
#else
return rgba8_to_depth16(sample_c(input.t));
#endif
}
#endif
float ps_convert_rgb5a1_float16_biln(PS_INPUT input) : SV_Depth
#if defined(__ps_convert_rgb5a1_depth16__)
OUTPUT_TYPE ps_convert_rgb5a1_depth16(PS_INPUT input) : OUTPUT_SV
{
// Convert an RGB5A1 (saved as RGBA8) color to a 16 bit Z
#if HAS_BILN
SAMPLE_RGBA_DEPTH_BILN(rgb5a1_to_depth16);
#else
return rgb5a1_to_depth16(sample_c(input.t));
#endif
}
#endif
#if defined(__ps_convert_rgb5a1_8i__)
PS_OUTPUT ps_convert_rgb5a1_8i(PS_INPUT input)
{
PS_OUTPUT output;
@@ -406,7 +493,7 @@ PS_OUTPUT ps_convert_rgb5a1_8i(PS_INPUT input)
uint red = (denorm_c.r >> 3) & 0x1Fu;
uint green = (denorm_c.g >> 3) & 0x1Fu;
output.c = (float4)(((float)(((green << 5) | red) & 0xFFu)) / 255.0f);
output.o = (float4)(((float)(((green << 5) | red) & 0xFFu)) / 255.0f);
}
else
{
@@ -414,11 +501,13 @@ PS_OUTPUT ps_convert_rgb5a1_8i(PS_INPUT input)
uint blue = (denorm_c.b >> 3) & 0x1Fu;
uint alpha = denorm_c.a & 0x80u;
output.c = (float4)(((float)((alpha | (blue << 2) | (green >> 3)) & 0xFFu)) / 255.0f);
output.o = (float4)(((float)((alpha | (blue << 2) | (green >> 3)) & 0xFFu)) / 255.0f);
}
return output;
}
#endif
#if defined(__ps_convert_rgba_8i__)
PS_OUTPUT ps_convert_rgba_8i(PS_INPUT input)
{
PS_OUTPUT output;
@@ -462,10 +551,12 @@ PS_OUTPUT ps_convert_rgba_8i(PS_INPUT input)
float4 pixel = Texture.Load(int3(int2(coord), 0));
float2 sel0 = (pos.y & 2u) == 0u ? pixel.rb : pixel.ga;
float sel1 = (pos.x & 8u) == 0u ? sel0.x : sel0.y;
output.c = (float4)(sel1); // Divide by something here?
output.o = (float4)(sel1); // Divide by something here?
return output;
}
#endif
#if defined(__ps_convert_clut_4__)
PS_OUTPUT ps_convert_clut_4(PS_INPUT input)
{
// Borrowing the YUV constant buffer.
@@ -478,10 +569,12 @@ PS_OUTPUT ps_convert_clut_4(PS_INPUT input)
int2 final = int2(floor(float2(offset + pos) * scale));
PS_OUTPUT output;
output.c = Texture.Load(int3(final, 0), 0);
output.o = Texture.Load(int3(final, 0), 0);
return output;
}
#endif
#if defined(__ps_convert_clut_8__)
PS_OUTPUT ps_convert_clut_8(PS_INPUT input)
{
float scale = BGColor.x;
@@ -497,10 +590,12 @@ PS_OUTPUT ps_convert_clut_8(PS_INPUT input)
int2 final = int2(floor(float2(offset + pos) * scale));
PS_OUTPUT output;
output.c = Texture.Load(int3(final, 0), 0);
output.o = Texture.Load(int3(final, 0), 0);
return output;
}
#endif
#if defined(__ps_yuv__)
PS_OUTPUT ps_yuv(PS_INPUT input)
{
PS_OUTPUT output;
@@ -515,41 +610,43 @@ PS_OUTPUT ps_yuv(PS_INPUT input)
switch (EMODA)
{
case 0:
output.c.a = i.a;
output.o.a = i.a;
break;
case 1:
output.c.a = Y;
output.o.a = Y;
break;
case 2:
output.c.a = Y / 2.0f;
output.o.a = Y / 2.0f;
break;
case 3:
default:
output.c.a = 0.0f;
output.o.a = 0.0f;
break;
}
switch (EMODC)
{
case 0:
output.c.rgb = i.rgb;
output.o.rgb = i.rgb;
break;
case 1:
output.c.rgb = float3(Y, Y, Y);
output.o.rgb = float3(Y, Y, Y);
break;
case 2:
output.c.rgb = float3(Y, Cb, Cr);
output.o.rgb = float3(Y, Cb, Cr);
break;
case 3:
default:
output.c.rgb = float3(i.a, i.a, i.a);
output.o.rgb = float3(i.a, i.a, i.a);
break;
}
return output;
}
#endif
float ps_stencil_image_init_0(PS_INPUT input) : SV_Target
#if defined(__ps_primid_image_init_0__)
float ps_primid_image_init_0(PS_INPUT input) : SV_Target
{
float c;
if ((127.5f / 255.0f) < sample_c(input.t).a) // < 0x80 pass (== 0x80 should not pass)
@@ -558,8 +655,10 @@ float ps_stencil_image_init_0(PS_INPUT input) : SV_Target
c = float(0x7FFFFFFF);
return c;
}
#endif
float ps_stencil_image_init_1(PS_INPUT input) : SV_Target
#if defined(__ps_primid_image_init_1__)
float ps_primid_image_init_1(PS_INPUT input) : SV_Target
{
float c;
if (sample_c(input.t).a < (127.5f / 255.0f)) // >= 0x80 pass
@@ -568,9 +667,10 @@ float ps_stencil_image_init_1(PS_INPUT input) : SV_Target
c = float(0x7FFFFFFF);
return c;
}
#endif
float ps_stencil_image_init_2(PS_INPUT input)
: SV_Target
#if defined(__ps_primid_image_init_2__)
float ps_primid_image_init_2(PS_INPUT input) : SV_Target
{
float c;
if ((254.5f / 255.0f) < sample_c(input.t).a) // < 0x80 pass (== 0x80 should not pass)
@@ -579,9 +679,10 @@ float ps_stencil_image_init_2(PS_INPUT input)
c = float(0x7FFFFFFF);
return c;
}
#endif
float ps_stencil_image_init_3(PS_INPUT input)
: SV_Target
#if defined(__ps_primid_image_init_3__)
float ps_primid_image_init_3(PS_INPUT input) : SV_Target
{
float c;
if (sample_c(input.t).a < (254.5f / 255.0f)) // >= 0x80 pass
@@ -590,3 +691,6 @@ float ps_stencil_image_init_3(PS_INPUT input)
c = float(0x7FFFFFFF);
return c;
}
#endif
#endif // PIXEL_SHADER
+249 -104
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,24 +192,29 @@ 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 SW_DEPTH && PS_NO_COLOR1 && PS_DEPTH_FEEDBACK_SUPPORT == 2
#if NUM_RTS > 0
float depth_color : SV_Target1;
#else
@@ -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
@@ -242,8 +290,46 @@ cbuffer cb1
float4x4 DitherMatrix;
float ScaledScaleFactor;
float RcpScaleFactor;
float _pad0_cb1;
float _pad1_cb1;
float LineCovScale;
float _pad2_cb1;
float _pad3_cb1;
float _pad4_cb1;
};
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)
{
@@ -275,23 +361,28 @@ bool2 nan_or_inf(float2 xy)
float4 sample_c_af(float2 uv, float uv_w)
{
// HW sampler will reject bad UVs, match that here.
uv = any(nan_or_inf(uv)) ? float2(0, 0) : uv;
uv = any(nan_or_inf(uv)) ? float2(0.0f, 0.0f) : uv;
// Large floating point values risk NaN/Inf values.
// Above this value floats lose decimal precision, so seems a resonable limit for UVs.
uv = clamp(uv, -8388608.0f, 8388608.0f);
// Below taken from https://microsoft.github.io/DirectX-Specs/d3d/archive/D3D11_3_FunctionalSpec.htm#7.18.11%20LOD%20Calculations
// And https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_filter_anisotropic.txt
// With guidance from https://pema.dev/2025/05/09/mipmaps-too-much-detail/
float2 sz;
Texture.GetDimensions(sz.x, sz.y);
float2 dX = ddx(uv) * sz;
float2 dY = ddy(uv) * sz;
float length_x = length(dX);
float length_y = length(dY);
// Calculate Ellipse Transform
bool d_zero = length(dX) == 0 || length(dY) == 0;
bool d_par = (dX.x * dY.y - dY.x * dX.y) == 0;
bool d_per = dot(dX, dY) == 0;
bool d_zero = length_x < 0.001f || length_y < 0.001f;
float f = (dX.x * dY.y - dX.y * dY.x);
bool d_par = f < 0.001f;
bool d_per = dot(dX, dY) < 0.001f;
bool d_inf_nan = any(nan_or_inf(dX) | nan_or_inf(dY));
if (!(d_zero || d_par || d_per || d_inf_nan))
@@ -299,42 +390,49 @@ float4 sample_c_af(float2 uv, float uv_w)
float A = dX.y * dX.y + dY.y * dY.y;
float B = -2 * (dX.x * dX.y + dY.x * dY.y);
float C = dX.x * dX.x + dY.x * dY.x;
float f = (dX.x * dY.y - dY.x * dX.y);
float F = f * f;
float p = A - C;
float q = A + C;
float t = sqrt(p * p + B * B);
float sqrt_num_plus = sqrt(F * (t + p));
float sqrt_num_minus = sqrt(F * (t - p));
float inv_sqrt_denom_plus = rsqrt(t * (q + t));
float inv_sqrt_denom_minus = rsqrt(t * (q - t));
float signB = sign(B);
float2 new_dX = float2(
sqrt(F * (t + p) / (t * (q + t))),
sqrt(F * (t - p) / (t * (q + t))) * sign(B)
sqrt_num_plus * inv_sqrt_denom_plus,
sqrt_num_minus * inv_sqrt_denom_plus * signB
);
float2 new_dY = float2(
sqrt(F * (t - p) / (t * (q - t))) * -sign(B),
sqrt(F * (t + p) / (t * (q - t)))
sqrt_num_minus * inv_sqrt_denom_minus * -signB,
sqrt_num_plus * inv_sqrt_denom_minus
);
d_inf_nan = any(nan_or_inf(new_dX) | nan_or_inf(new_dY));
if (!d_inf_nan)
{
dX = new_dX;
dY = new_dY;
length_x = length(dX);
length_y = length(dY);
}
}
// Compute AF values
float squared_length_x = dX.x * dX.x + dX.y * dX.y;
float squared_length_y = dY.x * dY.x + dY.y * dY.y;
float determinant = abs(dX.x * dY.y - dX.y * dY.x);
bool is_major_x = squared_length_x > squared_length_y;
float squared_length_major = is_major_x ? squared_length_x : squared_length_y;
float length_major = sqrt(squared_length_major);
bool is_major_x = length_x > length_y;
float length_major = is_major_x ? length_x : length_y;
float length_minor = is_major_x ? length_y : length_x;
float aniso_ratio;
float length_lod;
float2 aniso_line;
if (length_major <= 1.0f)
{
// A zero length_major would result in NaN Lod and break sampling.
@@ -342,57 +440,44 @@ float4 sample_c_af(float2 uv, float uv_w)
// Perform isotropic filtering instead.
aniso_ratio = 1.0f;
length_lod = length_major;
aniso_line = float2(0, 0);
aniso_line = float2(0.0f, 0.0f);
}
else
{
float norm_major = 1.0f / length_major;
float2 aniso_line_dir = float2(
(is_major_x ? dX.x : dY.x) * norm_major,
(is_major_x ? dX.y : dY.y) * norm_major
);
aniso_ratio = squared_length_major / determinant;
float2 aniso_line_dir = is_major_x ? dX : dY;
// Calculate the minor length of the ellipse for Lod, while also clamping the ratio of anisotropy.
if (aniso_ratio > PS_ANISOTROPIC_FILTERING)
{
// ratio is clamped - Lod is based on ratio (preserves area)
aniso_ratio = PS_ANISOTROPIC_FILTERING;
length_lod = length_major / PS_ANISOTROPIC_FILTERING;
}
else
{
// ratio not clamped - Lod is based on area
length_lod = determinant / length_major;
}
aniso_ratio = min(length_major / length_minor, PS_ANISOTROPIC_FILTERING);
length_lod = length_major / aniso_ratio;
// clamp to top Lod
if (length_lod < 1.0f)
aniso_ratio = max(1.0f, aniso_ratio * length_lod);
aniso_ratio = round(aniso_ratio);
aniso_line = aniso_line_dir * 0.5f * length_major * (1.0f / sz);
aniso_line = aniso_line_dir * 0.5f * (1.0f / sz);
}
#if PS_AUTOMATIC_LOD == 1
float lod = log2(length_lod);
#elif PS_MANUAL_LOD == 1
float lod = manual_lod(uv_w);
#else
float lod = 0; // No Lod
float lod = 0.0f; // No Lod
#endif
float4 colour;
if (aniso_ratio == 1.0f)
colour = Texture.SampleLevel(TextureSampler, uv, lod);
else
{
float4 num = float4(0, 0, 0, 0);
for (int i = 0; i < aniso_ratio; i++)
{
float2 d = -aniso_line + (0.5f + i) * (2.0f * aniso_line) / aniso_ratio;
float4 num = float4(0.0f, 0.0f, 0.0f, 0.0f);
float2 segment = (2.0f * aniso_line) / aniso_ratio;
int aniso_ratio_i = (int)aniso_ratio;
for (int i = 0; i < aniso_ratio_i; i++)
{
float2 d = -aniso_line + (0.5f + i) * segment;
float2 uv_sample = uv + d;
float4 sample_colour = Texture.SampleLevel(TextureSampler, uv_sample, lod);
num += sample_colour;
@@ -407,7 +492,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 +687,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 +697,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 +706,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
@@ -707,7 +792,7 @@ float4 sample_depth(float2 st, float2 pos)
}
else if (PS_DEPTH_FMT == 1)
{
// Based on ps_convert_float32_rgba8 of convert
// Based on ps_convert_depth32_rgba8 of convert
// Convert a FLOAT32 depth texture into a RGBA color texture
uint d = uint(fetch_c(uv).r * exp2(32.0f));
@@ -715,7 +800,7 @@ float4 sample_depth(float2 st, float2 pos)
}
else if (PS_DEPTH_FMT == 2)
{
// Based on ps_convert_float16_rgb5a1 of convert
// Based on ps_convert_depth16_rgb5a1 of convert
// Convert a FLOAT32 (only 16 lsb) depth into a RGB5A1 color texture
uint d = uint(fetch_c(uv).r * exp2(32.0f));
@@ -1027,7 +1112,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 +1188,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,24 +1321,58 @@ 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
float cov = clamp(1.0f - abs(input.inv_cov), 0.0f, 1.0f);
#if PS_AA1 == PS_AA1_LINE
// Blur only outer part of the line by scaling coverage.
float cov = clamp(LineCovScale * (1.0f - abs(input.inv_cov)), 0.0f, 1.0f);
#else
float cov = clamp(1.0f - abs(input.inv_cov), 0.0f, 1.0f);
#endif
#if PS_ABE
if (floor(C.a) == 128.0f) // The coverage is only used if the fragment alpha is 128.
C.a = 128.0f * cov;
@@ -1267,13 +1386,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 +1401,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 +1425,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 +1446,8 @@ PS_OUTPUT ps_main(PS_INPUT input)
#endif
#endif
if (bad)
discard;
if (bad)
discard;
#endif
#if PS_DATE == 3
@@ -1342,6 +1458,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 +1545,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 +1579,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 && PS_DEPTH_FEEDBACK_SUPPORT == 2
// 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
@@ -1495,7 +1642,7 @@ cbuffer cb0
float2 TextureOffset;
float2 PointSize;
uint MaxDepth;
uint _cb0_pad0;
float LineAA1Width;
};
#ifdef DX12
@@ -1738,11 +1885,9 @@ VS_OUTPUT vs_main_expand(uint vid : SV_VertexID)
// Use bottom minus top for delta regardless of which vertex we are expanding.
float2 line_delta = is_bottom ? (vtx.p.xy - other.p.xy) : (other.p.xy - vtx.p.xy);
float2 line_vector = normalize(line_delta / VertexScale);
#if VS_EXPAND == VS_EXPAND_LINE
float2 line_expand = float2(line_vector.y, -line_vector.x);
#elif VS_EXPAND == VS_EXPAND_LINE_AA1
// Expand in y direction for shallow lines and x direction for steep lines.
float2 line_expand = abs(line_vector.x) >= abs(line_vector.y) ? float2(0.0f, 2.0f) : float2(2.0f, 0.0f);
#if VS_EXPAND == VS_EXPAND_LINE_AA1
line_expand *= 2.0f * LineAA1Width;
#endif
float2 line_width = (line_expand * PointSize) / 2;
float2 offset = is_right ? line_width : -line_width;
+166 -159
View File
@@ -3,21 +3,12 @@
//#version 420 // Keep it for editor detection
#ifdef VERTEX_SHADER
layout(location = 0) in vec2 POSITION;
layout(location = 1) in vec2 TEXCOORD0;
layout(location = 7) in vec4 COLOR;
// FIXME set the interpolation (don't know what dx do)
// flat means that there is no interpolation. The value given to the fragment shader is based on the provoking vertex conventions.
//
// noperspective means that there will be linear interpolation in window-space. This is usually not what you want, but it can have its uses.
//
// smooth, the default, means to do perspective-correct interpolation.
//
// The centroid qualifier only matters when multisampling. If this qualifier is not present, then the value is interpolated to the pixel's center, anywhere in the pixel, or to one of the pixel's samples. This sample may lie outside of the actual primitive being rendered, since a primitive can cover only part of a pixel's area. The centroid qualifier is used to prevent this; the interpolation point must fall within both the pixel's area and the primitive's area.
out vec4 PSin_p;
out vec2 PSin_t;
out vec4 PSin_c;
@@ -40,44 +31,125 @@ in vec4 PSin_c;
layout(binding = 0) uniform sampler2D TextureSampler;
// Give a different name so I remember there is a special case!
#if defined(ps_convert_rgba8_16bits) || defined(ps_convert_float32_32bits)
layout(location = 0) out uint SV_Target1;
#elif defined(ps_convert_float32_depth_to_color)
layout(location = 0) out float SV_Target0;
#elif !defined(ps_datm1) && \
!defined(ps_datm0) && \
!defined(ps_datm1_rta_correction) && \
!defined(ps_datm0_rta_correction) && \
!defined(ps_convert_rgba8_float32) && \
!defined(ps_convert_rgba8_float24) && \
!defined(ps_convert_rgba8_float16) && \
!defined(ps_convert_rgb5a1_float16) && \
!defined(ps_convert_rgba8_float32_biln) && \
!defined(ps_convert_rgba8_float24_biln) && \
!defined(ps_convert_rgba8_float16_biln) && \
!defined(ps_convert_rgb5a1_float16_biln) && \
!defined(ps_convert_float32_float24) && \
!defined(ps_depth_copy)
layout(location = 0) out vec4 SV_Target0;
#if HAS_INTEGER_OUTPUT
layout(location = 0) out uint o_col0;
#define OUTPUT o_col0
#elif HAS_DEPTH_OUTPUT
out float gl_FragDepth;
#define OUTPUT gl_FragDepth
#elif HAS_FLOAT32_OUTPUT
layout(location = 0) out float o_col0;
#define OUTPUT o_col0
#elif HAS_STENCIL_OUTPUT
#else
layout(location = 0) out vec4 o_col0;
#define OUTPUT o_col0
#endif
#if HAS_FLOAT32_INPUT
float sample_c()
{
return texture(TextureSampler, PSin_t).r;
}
#else
vec4 sample_c()
{
return texture(TextureSampler, PSin_t);
}
#endif
uint rgba8_to_uint(vec4 c)
{
uvec4 i = uvec4(c * 255.5f) & 0xFFu;
return i.r | (i.g << 8) | (i.b << 16) | (i.a << 24);
}
uint rgb5a1_to_uint(vec4 c)
{
uvec4 i = uvec4(c * 255.5f) & uvec4(0xF8u, 0xF8u, 0xF8u, 0x80u);
return (i.r >> 3) | (i.g << 2) | (i.b << 7) | (i.a << 8);
}
uint depth_to_uint(float d)
{
return uint(d * exp2(32.0f));
}
vec4 uint_to_rgba8(uint i)
{
return vec4((i & 0xFFu), ((i >> 8) & 0xFFu), ((i >> 16) & 0xFFu), ((i >> 24) & 0xFFu)) / 255.0f;
}
vec4 uint_to_rgb5a1(uint i)
{
return vec4(uvec4(i << 3, i >> 2, i >> 7, i >> 8) & uvec4(0xF8u, 0xF8u, 0xF8u, 0x80u)) / 255.0f;
}
float uint_to_depth32(uint i)
{
return float(i) * exp2(-32.0f);
}
float uint_to_depth24(uint i)
{
return float(i & 0xFFFFFFu) * exp2(-32.0f);
}
float uint_to_depth16(uint i)
{
return float(i & 0xFFFFu) * exp2(-32.0f);
}
float rgba8_to_depth32(vec4 val)
{
return uint_to_depth32(rgba8_to_uint(val));
}
float rgba8_to_depth24(vec4 val)
{
return uint_to_depth24(rgba8_to_uint(val));
}
float rgba8_to_depth16(vec4 val)
{
return uint_to_depth16(rgba8_to_uint(val));
}
float rgb5a1_to_depth16(vec4 val)
{
return uint_to_depth16(rgb5a1_to_uint(val));
}
vec4 depth32_to_rgba8(float d)
{
return uint_to_rgba8(depth_to_uint(d));
}
vec4 depth16_to_rgb5a1(float d)
{
return uint_to_rgb5a1(depth_to_uint(d));
}
float depth32_to_depth24(float d)
{
return uint_to_depth24(depth_to_uint(d));
}
#ifdef ps_copy
void ps_copy()
{
SV_Target0 = sample_c();
OUTPUT = sample_c();
}
#endif
#ifdef ps_depth_copy
void ps_depth_copy()
{
gl_FragDepth = sample_c().r;
OUTPUT = sample_c();
}
#endif
@@ -96,126 +168,47 @@ void ps_downsample_copy()
for (int xoff = 0; xoff < DownsampleFactor; xoff++)
result += texelFetch(TextureSampler, coord + ivec2(xoff * StepMultiplier, yoff * StepMultiplier), 0);
}
SV_Target0 = result / Weight;
o_col0 = result / Weight;
}
#endif
#ifdef ps_convert_rgba8_16bits
// Need to be careful with precision here, it can break games like Spider-Man 3 and Dogs Life
void ps_convert_rgba8_16bits()
#ifdef ps_convert_rgb5a1_16bits
void ps_convert_rgb5a1_16bits()
{
highp uvec4 i = uvec4(sample_c() * vec4(255.5f, 255.5f, 255.5f, 255.5f));
SV_Target1 = ((i.x & 0x00F8u) >> 3) | ((i.y & 0x00F8u) << 2) | ((i.z & 0x00f8u) << 7) | ((i.w & 0x80u) << 8);
// Need to be careful with precision here, it can break games like Spider-Man 3 and Dogs Life
OUTPUT = rgb5a1_to_uint(sample_c());
}
#endif
#ifdef ps_convert_float32_32bits
void ps_convert_float32_32bits()
#ifdef ps_convert_depth32_32bits
void ps_convert_depth32_32bits()
{
// Convert a GL_FLOAT32 depth texture into a 32 bits UINT texture
SV_Target1 = uint(exp2(32.0f) * sample_c().r);
OUTPUT = depth_to_uint(sample_c());
}
#endif
#ifdef ps_convert_float32_rgba8
void ps_convert_float32_rgba8()
#ifdef ps_convert_depth32_rgba8
void ps_convert_depth32_rgba8()
{
// Convert a GL_FLOAT32 depth texture into a RGBA color texture
uint d = uint(sample_c().r * exp2(32.0f));
SV_Target0 = vec4(uvec4((d & 0xFFu), ((d >> 8) & 0xFFu), ((d >> 16) & 0xFFu), (d >> 24))) / vec4(255.0);
OUTPUT = depth32_to_rgba8(sample_c());
}
#endif
#ifdef ps_convert_float16_rgb5a1
void ps_convert_float16_rgb5a1()
#ifdef ps_convert_depth16_rgb5a1
void ps_convert_depth16_rgb5a1()
{
// Convert a GL_FLOAT32 (only 16 lsb) depth into a RGB5A1 color texture
uint d = uint(sample_c().r * exp2(32.0f));
SV_Target0 = vec4(uvec4(d << 3, d >> 2, d >> 7, d >> 8) & uvec4(0xf8, 0xf8, 0xf8, 0x80)) / 255.0f;
OUTPUT = depth16_to_rgb5a1(sample_c());
}
#endif
float rgba8_to_depth32(vec4 unorm)
{
uvec4 c = uvec4(unorm * vec4(255.5f));
return float(c.r | (c.g << 8) | (c.b << 16) | (c.a << 24)) * exp2(-32.0f);
}
float rgba8_to_depth24(vec4 unorm)
{
uvec3 c = uvec3(unorm.rgb * vec3(255.5f));
return float(c.r | (c.g << 8) | (c.b << 16)) * exp2(-32.0f);
}
float rgba8_to_depth16(vec4 unorm)
{
uvec2 c = uvec2(unorm.rg * vec2(255.5f));
return float(c.r | (c.g << 8)) * exp2(-32.0f);
}
float rgb5a1_to_depth16(vec4 unorm)
{
uvec4 c = uvec4(unorm * vec4(255.5f));
return float(((c.r & 0xF8u) >> 3) | ((c.g & 0xF8u) << 2) | ((c.b & 0xF8u) << 7) | ((c.a & 0x80u) << 8)) * exp2(-32.0f);
}
#ifdef ps_convert_float32_depth_to_color
void ps_convert_float32_depth_to_color()
{
SV_Target0 = sample_c().r;
}
#endif
#ifdef ps_convert_float32_color_to_depth
void ps_convert_float32_color_to_depth()
{
gl_FragDepth = sample_c().r;
}
#endif
#ifdef ps_convert_float32_float24
void ps_convert_float32_float24()
#ifdef ps_convert_depth32_depth24
void ps_convert_depth32_depth24()
{
// Truncates depth value to 24bits
uint d = uint(sample_c().r * exp2(32.0f)) & 0xFFFFFFu;
gl_FragDepth = float(d) * exp2(-32.0f);
}
#endif
#ifdef ps_convert_rgba8_float32
void ps_convert_rgba8_float32()
{
// Convert an RGBA texture into a float depth texture
gl_FragDepth = rgba8_to_depth32(sample_c());
}
#endif
#ifdef ps_convert_rgba8_float24
void ps_convert_rgba8_float24()
{
// Same as above but without the alpha channel (24 bits Z)
// Convert an RGBA texture into a float depth texture
gl_FragDepth = rgba8_to_depth24(sample_c());
}
#endif
#ifdef ps_convert_rgba8_float16
void ps_convert_rgba8_float16()
{
// Same as above but without the A/B channels (16 bits Z)
// Convert an RGBA texture into a float depth texture
gl_FragDepth = rgba8_to_depth16(sample_c());
}
#endif
#ifdef ps_convert_rgb5a1_float16
void ps_convert_rgb5a1_float16()
{
// Convert an RGB5A1 (saved as RGBA8) color to a 16 bit Z
gl_FragDepth = rgb5a1_to_depth16(sample_c());
OUTPUT = depth32_to_depth24(sample_c());
}
#endif
@@ -229,41 +222,55 @@ void ps_convert_rgb5a1_float16()
float depthTR = CONVERT_FN(texelFetch(TextureSampler, coords.zy, 0)); \
float depthBL = CONVERT_FN(texelFetch(TextureSampler, coords.xw, 0)); \
float depthBR = CONVERT_FN(texelFetch(TextureSampler, coords.zw, 0)); \
gl_FragDepth = mix(mix(depthTL, depthTR, mix_vals.x), mix(depthBL, depthBR, mix_vals.x), mix_vals.y);
OUTPUT = mix(mix(depthTL, depthTR, mix_vals.x), mix(depthBL, depthBR, mix_vals.x), mix_vals.y);
#ifdef ps_convert_rgba8_float32_biln
void ps_convert_rgba8_float32_biln()
#ifdef ps_convert_rgba8_depth32
void ps_convert_rgba8_depth32()
{
// Convert an RGBA texture into a float depth texture
#if HAS_BILN
SAMPLE_RGBA_DEPTH_BILN(rgba8_to_depth32);
#else
OUTPUT = rgba8_to_depth32(sample_c());
#endif
}
#endif
#ifdef ps_convert_rgba8_float24_biln
void ps_convert_rgba8_float24_biln()
#ifdef ps_convert_rgba8_depth24
void ps_convert_rgba8_depth24()
{
// Same as above but without the alpha channel (24 bits Z)
// Convert an RGBA texture into a float depth texture
#if HAS_BILN
SAMPLE_RGBA_DEPTH_BILN(rgba8_to_depth24);
#else
OUTPUT = rgba8_to_depth24(sample_c());
#endif
}
#endif
#ifdef ps_convert_rgba8_float16_biln
void ps_convert_rgba8_float16_biln()
#ifdef ps_convert_rgba8_depth16
void ps_convert_rgba8_depth16()
{
// Same as above but without the A/B channels (16 bits Z)
// Convert an RGBA texture into a float depth texture
#if HAS_BILN
SAMPLE_RGBA_DEPTH_BILN(rgba8_to_depth16);
#else
OUTPUT = rgba8_to_depth16(sample_c());
#endif
}
#endif
#ifdef ps_convert_rgb5a1_float16_biln
void ps_convert_rgb5a1_float16_biln()
#ifdef ps_convert_rgb5a1_depth16
void ps_convert_rgb5a1_depth16()
{
// Convert an RGB5A1 (saved as RGBA8) color to a 16 bit Z
#if HAS_BILN
SAMPLE_RGBA_DEPTH_BILN(rgb5a1_to_depth16);
#else
OUTPUT = rgb5a1_to_depth16(sample_c());
#endif
}
#endif
@@ -373,7 +380,7 @@ void ps_convert_rgb5a1_8i()
uint red = (denorm_c.r >> 3) & 0x1Fu;
uint green = (denorm_c.g >> 3) & 0x1Fu;
SV_Target0 = vec4(float(((green << 5) | red) & 0xFFu) / 255.0f);
o_col0 = vec4(float(((green << 5) | red) & 0xFFu) / 255.0f);
}
else
{
@@ -381,7 +388,7 @@ void ps_convert_rgb5a1_8i()
uint blue = (denorm_c.b >> 3) & 0x1Fu;
uint alpha = denorm_c.a & 0x80u;
SV_Target0 = vec4(float((alpha | (blue << 2) | (green >> 3)) & 0xFFu) / 255.0f);
o_col0 = vec4(float((alpha | (blue << 2) | (green >> 3)) & 0xFFu) / 255.0f);
}
}
#endif
@@ -430,7 +437,7 @@ void ps_convert_rgba_8i()
vec4 pixel = texelFetch(TextureSampler, ivec2(coord), 0);
vec2 sel0 = (pos.y & 2u) == 0u ? pixel.rb : pixel.ga;
float sel1 = (pos.x & 8u) == 0u ? sel0.x : sel0.y;
SV_Target0 = vec4(sel1);
o_col0 = vec4(sel1);
}
#endif
@@ -438,7 +445,7 @@ void ps_convert_rgba_8i()
void ps_filter_transparency()
{
vec4 c = sample_c();
SV_Target0 = vec4(c.rgb, 1.0);
o_col0 = vec4(c.rgb, 1.0);
}
#endif
@@ -486,7 +493,7 @@ void ps_datm0_rta_correction()
void ps_rta_correction()
{
vec4 value = sample_c();
SV_Target0 = vec4(value.rgb, value.a / (128.25f / 255.0f));
o_col0 = vec4(value.rgb, value.a / (128.25f / 255.0f));
}
#endif
@@ -494,7 +501,7 @@ void ps_rta_correction()
void ps_rta_decorrection()
{
vec4 value = sample_c();
SV_Target0 = vec4(value.rgb, value.a * (128.25f / 255.0f));
o_col0 = vec4(value.rgb, value.a * (128.25f / 255.0f));
}
#endif
@@ -502,7 +509,7 @@ void ps_rta_decorrection()
void ps_colclip_init()
{
vec4 value = sample_c();
SV_Target0 = vec4(round(value.rgb * 255.0f) / 65535.0f, value.a);
o_col0 = vec4(round(value.rgb * 255.0f) / 65535.0f, value.a);
}
#endif
@@ -510,7 +517,7 @@ void ps_colclip_init()
void ps_colclip_resolve()
{
vec4 value = sample_c();
SV_Target0 = vec4(vec3(uvec3(value.rgb * 65535.0f) & 255u) / 255.0f, value.a);
o_col0 = vec4(vec3(uvec3(value.rgb * 65535.0f) & 255u) / 255.0f, value.a);
}
#endif
@@ -525,7 +532,7 @@ void ps_convert_clut_4()
uvec2 pos = uvec2(index % 8u, index / 8u);
ivec2 final = ivec2(floor(vec2(offset.xy + pos) * vec2(scale)));
SV_Target0 = texelFetch(TextureSampler, final, 0);
o_col0 = texelFetch(TextureSampler, final, 0);
}
#endif
@@ -545,7 +552,7 @@ void ps_convert_clut_8()
pos.y = ((index / 32u) * 2u) + (subgroup % 2u);
ivec2 final = ivec2(floor(vec2(offset.xy + pos) * vec2(scale)));
SV_Target0 = texelFetch(TextureSampler, final, 0);
o_col0 = texelFetch(TextureSampler, final, 0);
}
#endif
@@ -600,31 +607,31 @@ void ps_yuv()
break;
}
SV_Target0 = o;
o_col0 = o;
}
#endif
#if defined(ps_stencil_image_init_0) || defined(ps_stencil_image_init_1) || defined(ps_stencil_image_init_2) || defined(ps_stencil_image_init_3)
#if defined(ps_primid_image_init_0) || defined(ps_primid_image_init_1) || defined(ps_primid_image_init_2) || defined(ps_primid_image_init_3)
void main()
{
SV_Target0 = vec4(0x7FFFFFFF);
o_col0 = vec4(0x7FFFFFFF);
#ifdef ps_stencil_image_init_0
#ifdef ps_primid_image_init_0
if((127.5f / 255.0f) < sample_c().a) // < 0x80 pass (== 0x80 should not pass)
SV_Target0 = vec4(-1);
o_col0 = vec4(-1);
#endif
#ifdef ps_stencil_image_init_1
#ifdef ps_primid_image_init_1
if(sample_c().a < (127.5f / 255.0f)) // >= 0x80 pass
SV_Target0 = vec4(-1);
o_col0 = vec4(-1);
#endif
#ifdef ps_stencil_image_init_2
#ifdef ps_primid_image_init_2
if((254.5f / 255.0f) < sample_c().a) // < 0x80 pass (== 0x80 should not pass)
SV_Target0 = vec4(-1);
o_col0 = vec4(-1);
#endif
#ifdef ps_stencil_image_init_3
#ifdef ps_primid_image_init_3
if(sample_c().a < (254.5f / 255.0f)) // >= 0x80 pass
SV_Target0 = vec4(-1);
o_col0 = vec4(-1);
#endif
}
#endif
+86 -75
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)
@@ -93,6 +93,13 @@ layout(std140, binding = 0) uniform cb21
float ScaledScaleFactor;
float RcpScaleFactor;
float _pad0_cb1;
float _pad1_cb1;
float LineCovScale;
float _pad2_cb1;
float _pad3_cb1;
float _pad4_cb1;
};
in SHADER
@@ -121,7 +128,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 +136,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 +187,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
}
@@ -213,22 +220,27 @@ float manual_lod(float uv_w)
vec4 sample_c_af(vec2 uv, float uv_w)
{
// HW sampler will reject bad UVs, match that here.
uv = (any(isnan(uv)) || any(isinf(uv))) ? vec2(0, 0) : uv;
uv = (any(isnan(uv)) || any(isinf(uv))) ? vec2(0.0f, 0.0f) : uv;
// Large floating point values risk NaN/Inf values.
// Above this value floats lose decimal precision, so seems a resonable limit for UVs.
uv = clamp(uv, -8388608.0f, 8388608.0f);
// Below taken from https://microsoft.github.io/DirectX-Specs/d3d/archive/D3D11_3_FunctionalSpec.htm#7.18.11%20LOD%20Calculations
// And https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_filter_anisotropic.txt
// With guidance from https://pema.dev/2025/05/09/mipmaps-too-much-detail/
vec2 sz = textureSize(TextureSampler, 0);
vec2 dX = dFdx(uv) * sz;
vec2 dY = dFdy(uv) * sz;
float length_x = length(dX);
float length_y = length(dY);
// Calculate Ellipse Transform
bool d_zero = length(dX) == 0 || length(dY) == 0;
bool d_par = (dX.x * dY.y - dY.x * dX.y) == 0;
bool d_per = dot(dX, dY) == 0;
bool d_zero = length_x < 0.001f || length_y < 0.001f;
float f = (dX.x * dY.y - dX.y * dY.x);
bool d_par = f < 0.001f;
bool d_per = dot(dX, dY) < 0.001f;
bool d_inf_nan = any(isinf(dX)) || any(isinf(dY)) || any(isnan(dX)) || any(isnan(dY));
if (!(d_zero || d_par || d_per || d_inf_nan))
@@ -236,38 +248,46 @@ vec4 sample_c_af(vec2 uv, float uv_w)
float A = dX.y * dX.y + dY.y * dY.y;
float B = -2 * (dX.x * dX.y + dY.x * dY.y);
float C = dX.x * dX.x + dY.x * dY.x;
float f = (dX.x * dY.y - dY.x * dX.y);
float F = f * f;
float p = A - C;
float q = A + C;
float t = sqrt(p * p + B * B);
float signB = sign(B);
float denom_plus = t * (q + t);
float denom_minus = t * (q - t);
float sqrtA = sqrt(F * (t + p));
float sqrtB = sqrt(F * (t - p));
float inv_sqrt_denom_plus = inversesqrt(denom_plus);
float inv_sqrt_denom_minus = inversesqrt(denom_minus);
vec2 new_dX = vec2(
sqrt(F * (t + p) / (t * (q + t))),
sqrt(F * (t - p) / (t * (q + t))) * sign(B)
sqrtA * inv_sqrt_denom_plus,
sqrtB * inv_sqrt_denom_plus * signB
);
vec2 new_dY = vec2(
sqrt(F * (t - p) / (t * (q - t))) * -sign(B),
sqrt(F * (t + p) / (t * (q - t)))
sqrtB * inv_sqrt_denom_minus * -signB,
sqrtA * inv_sqrt_denom_minus
);
d_inf_nan = any(isinf(new_dX)) || any(isinf(new_dY)) || any(isnan(new_dX)) || any(isnan(new_dY));
if (!d_inf_nan)
{
dX = new_dX;
dY = new_dY;
length_x = length(dX);
length_y = length(dY);
}
}
// Compute AF values
float squared_length_x = dX.x * dX.x + dX.y * dX.y;
float squared_length_y = dY.x * dY.x + dY.y * dY.y;
float determinant = abs(dX.x * dY.y - dX.y * dY.x);
bool is_major_x = squared_length_x > squared_length_y;
float squared_length_major = is_major_x ? squared_length_x : squared_length_y;
float length_major = sqrt(squared_length_major);
bool is_major_x = length_x > length_y;
float length_major = is_major_x ? length_x : length_y;
float length_minor = is_major_x ? length_y : length_x;
float aniso_ratio;
float length_lod;
@@ -279,57 +299,42 @@ vec4 sample_c_af(vec2 uv, float uv_w)
// Perform isotropic filtering instead.
aniso_ratio = 1.0f;
length_lod = length_major;
aniso_line = vec2(0, 0);
aniso_line = vec2(0.0f, 0.0f);
}
else
{
float norm_major = 1.0f / length_major;
vec2 aniso_line_dir = vec2(
(is_major_x ? dX.x : dY.x) * norm_major,
(is_major_x ? dX.y : dY.y) * norm_major
);
aniso_ratio = squared_length_major / determinant;
vec2 aniso_line_dir = is_major_x ? dX : dY;
// Calculate the minor length of the ellipse for Lod, while also clamping the ratio of anisotropy.
if (aniso_ratio > PS_ANISOTROPIC_FILTERING)
{
// ratio is clamped - Lod is based on ratio (preserves area)
aniso_ratio = PS_ANISOTROPIC_FILTERING;
length_lod = length_major / PS_ANISOTROPIC_FILTERING;
}
else
{
// ratio not clamped - Lod is based on area
length_lod = determinant / length_major;
}
aniso_ratio = min(length_major / length_minor, PS_ANISOTROPIC_FILTERING);
length_lod = length_major / aniso_ratio;
// clamp to top Lod
if (length_lod < 1.0f)
aniso_ratio = max(1.0f, aniso_ratio * length_lod);
aniso_ratio = round(aniso_ratio);
aniso_line = aniso_line_dir * 0.5f * length_major * (1.0f / sz);
aniso_line = aniso_line_dir * 0.5f * (1.0f / sz);
}
#if PS_AUTOMATIC_LOD == 1
float lod = log2(length_lod);
#elif PS_MANUAL_LOD == 1
float lod = manual_lod(uv_w);
#else
float lod = 0; // No Lod
float lod = 0.0f; // No Lod
#endif
vec4 colour;
if (aniso_ratio == 1.0f)
colour = textureLod(TextureSampler, uv, lod);
else
{
vec4 num = vec4(0, 0, 0, 0);
vec4 num = vec4(0.0f, 0.0f, 0.0f, 0.0f);
vec2 segment = (2.0f * aniso_line) / aniso_ratio;
for (int i = 0; i < aniso_ratio; i++)
{
vec2 d = -aniso_line + (0.5f + i) * (2.0f * aniso_line) / aniso_ratio;
{
vec2 d = -aniso_line + (0.5f + i) * segment;
vec2 uv_sample = uv + d;
vec4 sample_colour = textureLod(TextureSampler, uv_sample, lod);
num += sample_colour;
@@ -621,13 +626,13 @@ vec4 sample_depth(vec2 st)
#elif PS_DEPTH_FMT == 1
// Based on ps_convert_float32_rgba8 of convert
// Based on ps_convert_depth32_rgba8 of convert
// Convert a GL_FLOAT32 depth texture into a RGBA color texture
uint d = uint(fetch_c(uv).r * exp2(32.0f));
t = vec4(uvec4((d & 0xFFu), ((d >> 8) & 0xFFu), ((d >> 16) & 0xFFu), (d >> 24)));
#elif PS_DEPTH_FMT == 2
// Based on ps_convert_float16_rgb5a1 of convert
// Based on ps_convert_depth16_rgb5a1 of convert
// Convert a GL_FLOAT32 (only 16 lsb) depth into a RGB5A1 color texture
uint d = uint(fetch_c(uv).r * exp2(32.0f));
t = vec4(uvec4((d & 0x1Fu), ((d >> 5) & 0x1Fu), ((d >> 10) & 0x1Fu), (d >> 15) & 0x01u)) * vec4(8.0f, 8.0f, 8.0f, 128.0f);
@@ -1193,10 +1198,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
@@ -1250,7 +1255,12 @@ void ps_main()
vec4 C = ps_color();
#if PS_AA1
float cov = clamp(1.0f - abs(PSin.inv_cov), 0.0f, 1.0f);
#if PS_AA1 == PS_AA1_LINE
// Blur only outer part of the line by scaling coverage.
float cov = clamp(LineCovScale * (1.0f - abs(PSin.inv_cov)), 0.0f, 1.0f);
#else
float cov = clamp(1.0f - abs(PSin.inv_cov), 0.0f, 1.0f);
#endif
#if PS_ABE
if (floor(C.a) == 128.0f) // The coverage is only used if the fragment alpha is 128.
C.a = 128.0f * cov;
@@ -1264,7 +1274,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 +1303,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 +1382,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 +1391,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 +1411,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;
+18 -7
View File
@@ -12,8 +12,9 @@ layout(std140, binding = 1) uniform cb20
vec2 TextureOffset;
vec2 PointSize;
uint MaxDepth;
uint pad_cb20;
float LineAA1Width;
};
#ifdef VERTEX_SHADER
@@ -83,7 +84,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 +167,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;
@@ -311,11 +324,9 @@ void main()
// Use bottom minus top for delta regardless of which vertex we are expanding.
vec2 line_delta = is_bottom ? (vtx.p.xy - other.p.xy) : (other.p.xy - vtx.p.xy);
vec2 line_vector = normalize(line_delta / VertexScale);
#if VS_EXPAND == VS_EXPAND_LINE
vec2 line_expand = vec2(line_vector.y, -line_vector.x);
#elif VS_EXPAND == VS_EXPAND_LINE_AA1
// Expand in y direction for shallow lines and x direction for steep lines.
vec2 line_expand = abs(line_vector.x) >= abs(line_vector.y) ? vec2(0.0f, 2.0f) : vec2(2.0f, 0.0f);
#if VS_EXPAND == VS_EXPAND_LINE_AA1
line_expand *= 2.0f * LineAA1Width;
#endif
vec2 line_width = (line_expand * PointSize) / 2;
vec2 offset = is_right ? line_width : -line_width;
+155 -140
View File
@@ -20,45 +20,127 @@ void main()
layout(location = 0) in vec2 v_tex;
#if defined(ps_convert_rgba8_16bits) || defined(ps_convert_float32_32bits)
layout(location = 0) out uint o_col0;
#elif defined(ps_convert_float32_depth_to_color)
layout(location = 0) out float o_col0;
#elif !defined(ps_datm1) && \
!defined(ps_datm0) && \
!defined(ps_datm1_rta_correction) && \
!defined(ps_datm0_rta_correction) && \
!defined(ps_convert_rgba8_float32) && \
!defined(ps_convert_rgba8_float24) && \
!defined(ps_convert_rgba8_float16) && \
!defined(ps_convert_rgb5a1_float16) && \
!defined(ps_convert_rgba8_float32_biln) && \
!defined(ps_convert_rgba8_float24_biln) && \
!defined(ps_convert_rgba8_float16_biln) && \
!defined(ps_convert_rgb5a1_float16_biln) && \
!defined(ps_convert_float32_float24) && \
!defined(ps_depth_copy)
layout(location = 0) out vec4 o_col0;
#if HAS_INTEGER_OUTPUT
layout(location = 0) out uint o_col0;
#define OUTPUT o_col0
#elif HAS_DEPTH_OUTPUT
out float gl_FragDepth;
#define OUTPUT gl_FragDepth
#elif HAS_FLOAT32_OUTPUT
layout(location = 0) out float o_col0;
#define OUTPUT o_col0
#elif HAS_STENCIL_OUTPUT
#else
layout(location = 0) out vec4 o_col0;
#define OUTPUT o_col0
#endif
layout(set = 0, binding = 0) uniform sampler2D samp0;
#if HAS_FLOAT32_INPUT
float sample_c(vec2 uv)
{
return texture(samp0, uv).r;
}
#else
vec4 sample_c(vec2 uv)
{
return texture(samp0, uv);
}
#endif
uint rgba8_to_uint(vec4 c)
{
uvec4 i = uvec4(c * 255.5f) & 0xFFu;
return i.r | (i.g << 8) | (i.b << 16) | (i.a << 24);
}
uint rgb5a1_to_uint(vec4 c)
{
uvec4 i = uvec4(c * 255.5f) & uvec4(0xF8u, 0xF8u, 0xF8u, 0x80u);
return (i.r >> 3) | (i.g << 2) | (i.b << 7) | (i.a << 8);
}
uint depth_to_uint(float d)
{
return uint(d * exp2(32.0f));
}
vec4 uint_to_rgba8(uint i)
{
return vec4((i & 0xFFu), ((i >> 8) & 0xFFu), ((i >> 16) & 0xFFu), ((i >> 24) & 0xFFu)) / 255.0f;
}
vec4 uint_to_rgb5a1(uint i)
{
return vec4(uvec4(i << 3, i >> 2, i >> 7, i >> 8) & uvec4(0xF8u, 0xF8u, 0xF8u, 0x80u)) / 255.0f;
}
float uint_to_depth32(uint i)
{
return float(i) * exp2(-32.0f);
}
float uint_to_depth24(uint i)
{
return float(i & 0xFFFFFFu) * exp2(-32.0f);
}
float uint_to_depth16(uint i)
{
return float(i & 0xFFFFu) * exp2(-32.0f);
}
float rgba8_to_depth32(vec4 val)
{
return uint_to_depth32(rgba8_to_uint(val));
}
float rgba8_to_depth24(vec4 val)
{
return uint_to_depth24(rgba8_to_uint(val));
}
float rgba8_to_depth16(vec4 val)
{
return uint_to_depth16(rgba8_to_uint(val));
}
float rgb5a1_to_depth16(vec4 val)
{
return uint_to_depth16(rgb5a1_to_uint(val));
}
vec4 depth32_to_rgba8(float d)
{
return uint_to_rgba8(depth_to_uint(d));
}
vec4 depth16_to_rgb5a1(float d)
{
return uint_to_rgb5a1(depth_to_uint(d));
}
float depth32_to_depth24(float d)
{
return uint_to_depth24(depth_to_uint(d));
}
#ifdef ps_copy
void ps_copy()
{
o_col0 = sample_c(v_tex);
OUTPUT = sample_c(v_tex);
}
#endif
#ifdef ps_depth_copy
void ps_depth_copy()
{
gl_FragDepth = sample_c(v_tex).r;
OUTPUT = sample_c(v_tex);
}
#endif
@@ -83,7 +165,7 @@ void ps_downsample_copy()
result += texelFetch(samp0, coord + ivec2(xoff * step_multiplier, yoff * step_multiplier), 0);
}
}
o_col0 = result / Weight;
OUTPUT = result / Weight;
}
#endif
@@ -91,17 +173,15 @@ void ps_downsample_copy()
void ps_filter_transparency()
{
vec4 c = sample_c(v_tex);
o_col0 = vec4(c.rgb, 1.0);
OUTPUT = vec4(c.rgb, 1.0);
}
#endif
#ifdef ps_convert_rgba8_16bits
// Need to be careful with precision here, it can break games like Spider-Man 3 and Dogs Life
void ps_convert_rgba8_16bits()
#ifdef ps_convert_rgb5a1_16bits
void ps_convert_rgb5a1_16bits()
{
uvec4 i = uvec4(sample_c(v_tex) * vec4(255.5f, 255.5f, 255.5f, 255.5f));
o_col0 = ((i.x & 0x00F8u) >> 3) | ((i.y & 0x00F8u) << 2) | ((i.z & 0x00f8u) << 7) | ((i.w & 0x80u) << 8);
// Need to be careful with precision here, it can break games like Spider-Man 3 and Dogs Life
OUTPUT = rgb5a1_to_uint(sample_c(v_tex));
}
#endif
@@ -141,7 +221,7 @@ void ps_datm0_rta_correction()
void ps_rta_correction()
{
vec4 value = sample_c(v_tex);
o_col0 = vec4(value.rgb, value.a / (128.25f / 255.0f));
OUTPUT = vec4(value.rgb, value.a / (128.25f / 255.0f));
}
#endif
@@ -149,7 +229,7 @@ void ps_rta_correction()
void ps_rta_decorrection()
{
vec4 value = sample_c(v_tex);
o_col0 = vec4(value.rgb, value.a * (128.25f / 255.0f));
OUTPUT = vec4(value.rgb, value.a * (128.25f / 255.0f));
}
#endif
@@ -157,7 +237,7 @@ void ps_rta_decorrection()
void ps_colclip_init()
{
vec4 value = sample_c(v_tex);
o_col0 = vec4(roundEven(value.rgb * 255.0f) / 65535.0f, value.a);
OUTPUT = vec4(roundEven(value.rgb * 255.0f) / 65535.0f, value.a);
}
#endif
@@ -165,116 +245,39 @@ void ps_colclip_init()
void ps_colclip_resolve()
{
vec4 value = sample_c(v_tex);
o_col0 = vec4(vec3(uvec3(value.rgb * 65535.5f) & 255u) / 255.0f, value.a);
OUTPUT = vec4(vec3(uvec3(value.rgb * 65535.5f) & 255u) / 255.0f, value.a);
}
#endif
#ifdef ps_convert_float32_depth_to_color
void ps_convert_float32_depth_to_color()
{
o_col0 = sample_c(v_tex).r;
}
#endif
#ifdef ps_convert_float32_color_to_depth
void ps_convert_float32_color_to_depth()
{
gl_FragDepth = sample_c(v_tex).r;
}
#endif
#ifdef ps_convert_float32_32bits
void ps_convert_float32_32bits()
#ifdef ps_convert_depth32_32bits
void ps_convert_depth32_32bits()
{
// Convert a vec32 depth texture into a 32 bits UINT texture
o_col0 = uint(exp2(32.0f) * sample_c(v_tex).r);
OUTPUT = depth_to_uint(sample_c(v_tex));
}
#endif
#ifdef ps_convert_float32_rgba8
void ps_convert_float32_rgba8()
#ifdef ps_convert_depth32_rgba8
void ps_convert_depth32_rgba8()
{
// Convert a vec32 depth texture into a RGBA color texture
uint d = uint(sample_c(v_tex).r * exp2(32.0f));
o_col0 = vec4(uvec4((d & 0xFFu), ((d >> 8) & 0xFFu), ((d >> 16) & 0xFFu), (d >> 24))) / vec4(255.0);
OUTPUT = depth32_to_rgba8(sample_c(v_tex));
}
#endif
#ifdef ps_convert_float16_rgb5a1
void ps_convert_float16_rgb5a1()
#ifdef ps_convert_depth16_rgb5a1
void ps_convert_depth16_rgb5a1()
{
// Convert a vec32 (only 16 lsb) depth into a RGB5A1 color texture
uint d = uint(sample_c(v_tex).r * exp2(32.0f));
o_col0 = vec4(uvec4(d << 3, d >> 2, d >> 7, d >> 8) & uvec4(0xf8, 0xf8, 0xf8, 0x80)) / 255.0f;
OUTPUT = depth16_to_rgb5a1(sample_c(v_tex));
}
#endif
float rgba8_to_depth32(vec4 unorm)
{
uvec4 c = uvec4(unorm * vec4(255.5f));
return float(c.r | (c.g << 8) | (c.b << 16) | (c.a << 24)) * exp2(-32.0f);
}
float rgba8_to_depth24(vec4 unorm)
{
uvec3 c = uvec3(unorm.rgb * vec3(255.5f));
return float(c.r | (c.g << 8) | (c.b << 16)) * exp2(-32.0f);
}
float rgba8_to_depth16(vec4 unorm)
{
uvec2 c = uvec2(unorm.rg * vec2(255.5f));
return float(c.r | (c.g << 8)) * exp2(-32.0f);
}
float rgb5a1_to_depth16(vec4 unorm)
{
uvec4 c = uvec4(unorm * vec4(255.5f));
return float(((c.r & 0xF8u) >> 3) | ((c.g & 0xF8u) << 2) | ((c.b & 0xF8u) << 7) | ((c.a & 0x80u) << 8)) * exp2(-32.0f);
}
#ifdef ps_convert_float32_float24
void ps_convert_float32_float24()
#ifdef ps_convert_depth32_depth24
void ps_convert_depth32_depth24()
{
// Truncates depth value to 24bits
uint d = uint(sample_c(v_tex).r * exp2(32.0f)) & 0xFFFFFFu;
gl_FragDepth = float(d) * exp2(-32.0f);
}
#endif
#ifdef ps_convert_rgba8_float32
void ps_convert_rgba8_float32()
{
// Convert an RGBA texture into a float depth texture
gl_FragDepth = rgba8_to_depth32(sample_c(v_tex));
}
#endif
#ifdef ps_convert_rgba8_float24
void ps_convert_rgba8_float24()
{
// Same as above but without the alpha channel (24 bits Z)
// Convert an RGBA texture into a float depth texture
gl_FragDepth = rgba8_to_depth24(sample_c(v_tex));
}
#endif
#ifdef ps_convert_rgba8_float16
void ps_convert_rgba8_float16()
{
// Same as above but without the A/B channels (16 bits Z)
// Convert an RGBA texture into a float depth texture
gl_FragDepth = rgba8_to_depth16(sample_c(v_tex));
}
#endif
#ifdef ps_convert_rgb5a1_float16
void ps_convert_rgb5a1_float16()
{
// Convert an RGB5A1 (saved as RGBA8) color to a 16 bit Z
gl_FragDepth = rgb5a1_to_depth16(sample_c(v_tex));
OUTPUT = depth32_to_depth24(sample_c(v_tex));
}
#endif
@@ -288,41 +291,53 @@ void ps_convert_rgb5a1_float16()
float depthTR = CONVERT_FN(texelFetch(samp0, coords.zy, 0)); \
float depthBL = CONVERT_FN(texelFetch(samp0, coords.xw, 0)); \
float depthBR = CONVERT_FN(texelFetch(samp0, coords.zw, 0)); \
gl_FragDepth = mix(mix(depthTL, depthTR, mix_vals.x), mix(depthBL, depthBR, mix_vals.x), mix_vals.y);
OUTPUT = mix(mix(depthTL, depthTR, mix_vals.x), mix(depthBL, depthBR, mix_vals.x), mix_vals.y);
#ifdef ps_convert_rgba8_float32_biln
void ps_convert_rgba8_float32_biln()
#ifdef ps_convert_rgba8_depth32
void ps_convert_rgba8_depth32()
{
// Convert an RGBA texture into a float depth texture
#if HAS_BILN
SAMPLE_RGBA_DEPTH_BILN(rgba8_to_depth32);
#else
OUTPUT = rgba8_to_depth32(sample_c(v_tex));
#endif
}
#endif
#ifdef ps_convert_rgba8_float24_biln
void ps_convert_rgba8_float24_biln()
#ifdef ps_convert_rgba8_depth24
void ps_convert_rgba8_depth24()
{
// Same as above but without the alpha channel (24 bits Z)
// Convert an RGBA texture into a float depth texture
#if HAS_BILN
SAMPLE_RGBA_DEPTH_BILN(rgba8_to_depth24);
#else
OUTPUT = rgba8_to_depth24(sample_c(v_tex));
#endif
}
#endif
#ifdef ps_convert_rgba8_float16_biln
void ps_convert_rgba8_float16_biln()
#ifdef ps_convert_rgba8_depth16
void ps_convert_rgba8_depth16()
{
// Same as above but without the A/B channels (16 bits Z)
// Convert an RGBA texture into a float depth texture
#if HAS_BILN
SAMPLE_RGBA_DEPTH_BILN(rgba8_to_depth16);
#else
OUTPUT = rgba8_to_depth16(sample_c(v_tex));
#endif
}
#endif
#ifdef ps_convert_rgb5a1_float16_biln
void ps_convert_rgb5a1_float16_biln()
#ifdef ps_convert_rgb5a1_depth16
void ps_convert_rgb5a1_depth16()
{
// Convert an RGB5A1 (saved as RGBA8) color to a 16 bit Z
#if HAS_BILN
SAMPLE_RGBA_DEPTH_BILN(rgb5a1_to_depth16);
#else
OUTPUT = rgb5a1_to_depth16(sample_c(v_tex));
#endif
}
#endif
@@ -609,25 +624,25 @@ void ps_yuv()
}
#endif
#if defined(ps_stencil_image_init_0) || defined(ps_stencil_image_init_1) || defined(ps_stencil_image_init_2) || defined(ps_stencil_image_init_3)
#if defined(ps_primid_image_init_0) || defined(ps_primid_image_init_1) || defined(ps_primid_image_init_2) || defined(ps_primid_image_init_3)
void main()
{
o_col0 = vec4(0x7FFFFFFF);
#ifdef ps_stencil_image_init_0
#ifdef ps_primid_image_init_0
if((127.5f / 255.0f) < sample_c(v_tex).a) // < 0x80 pass (== 0x80 should not pass)
o_col0 = vec4(-1);
#endif
#ifdef ps_stencil_image_init_1
#ifdef ps_primid_image_init_1
if(sample_c(v_tex).a < (127.5f / 255.0f)) // >= 0x80 pass
o_col0 = vec4(-1);
#endif
#ifdef ps_stencil_image_init_2
#ifdef ps_primid_image_init_2
if((254.5f / 255.0f) < sample_c(v_tex).a) // < 0x80 pass (== 0x80 should not pass)
o_col0 = vec4(-1);
#endif
#ifdef ps_stencil_image_init_3
#ifdef ps_primid_image_init_3
if(sample_c(v_tex).a < (254.5f / 255.0f)) // >= 0x80 pass
o_col0 = vec4(-1);
#endif
+168 -81
View File
@@ -24,7 +24,7 @@ layout(std140, set = 0, binding = 0) uniform cb0
vec2 TextureOffset;
vec2 PointSize;
uint MaxDepth;
uint pad_cb0;
float LineAA1Width;
};
layout(location = 0) out VSOutput
@@ -319,11 +319,9 @@ void main()
// Use bottom minus top for delta regardless of which vertex we are expanding.
vec2 line_delta = is_bottom ? (vtx.p.xy - other.p.xy) : (other.p.xy - vtx.p.xy);
vec2 line_vector = normalize(line_delta / VertexScale);
#if VS_EXPAND == VS_EXPAND_LINE
vec2 line_expand = vec2(line_vector.y, -line_vector.x);
#elif VS_EXPAND == VS_EXPAND_LINE_AA1
// Expand in y direction for shallow lines and x direction for steep lines.
vec2 line_expand = abs(line_vector.x) >= abs(line_vector.y) ? vec2(0.0f, 2.0f) : vec2(2.0f, 0.0f);
#if VS_EXPAND == VS_EXPAND_LINE_AA1
line_expand *= 2.0f * LineAA1Width;
#endif
vec2 line_width = (line_expand * PointSize) / 2;
vec2 offset = is_right ? line_width : -line_width;
@@ -522,6 +520,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 +578,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 +592,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)
@@ -610,6 +622,12 @@ layout(std140, set = 0, binding = 1) uniform cb1
mat4 DitherMatrix;
float ScaledScaleFactor;
float RcpScaleFactor;
float _pad0_cb1;
float _pad1_cb1;
float LineCovScale;
float _pad2_cb1;
float _pad3_cb1;
float _pad4_cb1;
};
layout(location = 0) in VSOutput
@@ -625,11 +643,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 +673,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
@@ -694,22 +728,27 @@ float manual_lod(float uv_w)
vec4 sample_c_af(vec2 uv, float uv_w)
{
// HW sampler will reject bad UVs, match that here.
uv = (any(isnan(uv)) || any(isinf(uv))) ? vec2(0, 0) : uv;
uv = (any(isnan(uv)) || any(isinf(uv))) ? vec2(0.0f, 0.0f) : uv;
// Large floating point values risk NaN/Inf values.
// Above this value floats lose decimal precision, so seems a resonable limit for UVs.
uv = clamp(uv, -8388608.0f, 8388608.0f);
// Below taken from https://microsoft.github.io/DirectX-Specs/d3d/archive/D3D11_3_FunctionalSpec.htm#7.18.11%20LOD%20Calculations
// And https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_filter_anisotropic.txt
// With guidance from https://pema.dev/2025/05/09/mipmaps-too-much-detail/
vec2 sz = textureSize(Texture, 0);
vec2 dX = dFdx(uv) * sz;
vec2 dY = dFdy(uv) * sz;
float length_x = length(dX);
float length_y = length(dY);
// Calculate Ellipse Transform
bool d_zero = length(dX) == 0 || length(dY) == 0;
bool d_par = (dX.x * dY.y - dY.x * dX.y) == 0;
bool d_per = dot(dX, dY) == 0;
bool d_zero = length_x < 0.001f || length_y < 0.001f;
float f = (dX.x * dY.y - dX.y * dY.x);
bool d_par = f < 0.001f;
bool d_per = dot(dX, dY) < 0.001f;
bool d_inf_nan = any(isinf(dX)) || any(isinf(dY)) || any(isnan(dX)) || any(isnan(dY));
if (!(d_zero || d_par || d_per || d_inf_nan))
@@ -724,31 +763,40 @@ vec4 sample_c_af(vec2 uv, float uv_w)
float q = A + C;
float t = sqrt(p * p + B * B);
float signB = sign(B);
float denom_plus = t * (q + t);
float denom_minus = t * (q - t);
float sqrtA = sqrt(F * (t + p));
float sqrtB = sqrt(F * (t - p));
float inv_sqrt_denom_plus = inversesqrt(denom_plus);
float inv_sqrt_denom_minus = inversesqrt(denom_minus);
vec2 new_dX = vec2(
sqrt(F * (t + p) / (t * (q + t))),
sqrt(F * (t - p) / (t * (q + t))) * sign(B)
sqrtA * inv_sqrt_denom_plus,
sqrtB * inv_sqrt_denom_plus * signB
);
vec2 new_dY = vec2(
sqrt(F * (t - p) / (t * (q - t))) * -sign(B),
sqrt(F * (t + p) / (t * (q - t)))
sqrtB * inv_sqrt_denom_minus * -signB,
sqrtA * inv_sqrt_denom_minus
);
d_inf_nan = any(isinf(new_dX)) || any(isinf(new_dY)) || any(isnan(new_dX)) || any(isnan(new_dY));
if (!d_inf_nan)
{
dX = new_dX;
dY = new_dY;
length_x = length(dX);
length_y = length(dY);
}
}
// Compute AF values
float squared_length_x = dX.x * dX.x + dX.y * dX.y;
float squared_length_y = dY.x * dY.x + dY.y * dY.y;
float determinant = abs(dX.x * dY.y - dX.y * dY.x);
bool is_major_x = squared_length_x > squared_length_y;
float squared_length_major = is_major_x ? squared_length_x : squared_length_y;
float length_major = sqrt(squared_length_major);
bool is_major_x = length_x > length_y;
float length_major = is_major_x ? length_x : length_y;
float length_minor = is_major_x ? length_y : length_x;
float aniso_ratio;
float length_lod;
@@ -760,57 +808,42 @@ vec4 sample_c_af(vec2 uv, float uv_w)
// Perform isotropic filtering instead.
aniso_ratio = 1.0f;
length_lod = length_major;
aniso_line = vec2(0, 0);
aniso_line = vec2(0.0f, 0.0f);
}
else
{
float norm_major = 1.0f / length_major;
vec2 aniso_line_dir = vec2(
(is_major_x ? dX.x : dY.x) * norm_major,
(is_major_x ? dX.y : dY.y) * norm_major
);
aniso_ratio = squared_length_major / determinant;
vec2 aniso_line_dir = is_major_x ? dX : dY;
// Calculate the minor length of the ellipse for Lod, while also clamping the ratio of anisotropy.
if (aniso_ratio > PS_ANISOTROPIC_FILTERING)
{
// ratio is clamped - Lod is based on ratio (preserves area)
aniso_ratio = PS_ANISOTROPIC_FILTERING;
length_lod = length_major / PS_ANISOTROPIC_FILTERING;
}
else
{
// ratio not clamped - Lod is based on area
length_lod = determinant / length_major;
}
aniso_ratio = min(length_major / length_minor, PS_ANISOTROPIC_FILTERING);
length_lod = length_major / aniso_ratio;
// clamp to top Lod
if (length_lod < 1.0f)
aniso_ratio = max(1.0f, aniso_ratio * length_lod);
aniso_ratio = round(aniso_ratio);
aniso_line = aniso_line_dir * 0.5f * length_major * (1.0f / sz);
aniso_line = aniso_line_dir * 0.5f * (1.0f / sz);
}
#if PS_AUTOMATIC_LOD == 1
float lod = log2(length_lod);
#elif PS_MANUAL_LOD == 1
float lod = manual_lod(uv_w);
#else
float lod = 0; // No Lod
float lod = 0.0f; // No Lod
#endif
vec4 colour;
if (aniso_ratio == 1.0f)
colour = textureLod(Texture, uv, lod);
else
{
vec4 num = vec4(0, 0, 0, 0);
vec4 num = vec4(0.0f, 0.0f, 0.0f, 0.0f);
vec2 segment = (2.0f * aniso_line) / aniso_ratio;
for (int i = 0; i < aniso_ratio; i++)
{
vec2 d = -aniso_line + (0.5f + i) * (2.0f * aniso_line) / aniso_ratio;
{
vec2 d = -aniso_line + (0.5f + i) * segment;
vec2 uv_sample = uv + d;
vec4 sample_colour = textureLod(Texture, uv_sample, lod);
num += sample_colour;
@@ -1114,7 +1147,7 @@ vec4 sample_depth(vec2 st, ivec2 pos)
}
#elif (PS_DEPTH_FMT == 1)
{
// Based on ps_convert_float32_rgba8 of convert
// Based on ps_convert_depth32_rgba8 of convert
// Convert a vec32 depth texture into a RGBA color texture
uint d = uint(fetch_c(uv).r * exp2(32.0f));
@@ -1122,7 +1155,7 @@ vec4 sample_depth(vec2 st, ivec2 pos)
}
#elif (PS_DEPTH_FMT == 2)
{
// Based on ps_convert_float16_rgb5a1 of convert
// Based on ps_convert_depth16_rgb5a1 of convert
// Convert a vec32 (only 16 lsb) depth into a RGB5A1 color texture
uint d = uint(fetch_c(uv).r * exp2(32.0f));
@@ -1604,7 +1637,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 +1711,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 +1734,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 +1789,7 @@ void main()
#endif
if (bad) {
discard;
DISCARD;
}
#endif // PS_DATE >= 5
@@ -1737,14 +1800,19 @@ void main()
// the bad alpha value so we must keep it.
if (gl_PrimitiveID > stencil_ceil) {
discard;
DISCARD;
}
#endif
vec4 C = ps_color();
#if PS_AA1
float cov = clamp(1.0f - abs(vsIn.inv_cov), 0.0f, 1.0f);
#if PS_AA1 == PS_AA1_LINE
// Blur only outer part of the line by scaling coverage.
float cov = clamp(LineCovScale * (1.0f - abs(vsIn.inv_cov)), 0.0f, 1.0f);
#else
float cov = clamp(1.0f - abs(vsIn.inv_cov), 0.0f, 1.0f);
#endif
#if PS_ABE
if (floor(C.a) == 128.0f) // The coverage is only used if the fragment alpha is 128.
C.a = 128.0f * cov;
@@ -1758,9 +1826,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 +1921,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 +1940,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 +1949,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 +1961,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)
+13 -3
View File
@@ -98,14 +98,25 @@ __ri void Log::WriteToConsole(LOGLEVEL level, ConsoleColors color, std::string_v
static constexpr size_t BUFFER_SIZE = 512;
#ifdef _WIN32
constexpr bool supports_color_codes = true;
#else
const int fd = (level <= LOGLEVEL_WARNING) ? STDERR_FILENO : STDOUT_FILENO;
const bool supports_color_codes = static_cast<bool>(isatty(fd));
#endif
SmallStackString<BUFFER_SIZE> buffer;
buffer.reserve(static_cast<u32>(32 + message.length()));
buffer.append(s_ansi_color_codes[color]);
if (supports_color_codes)
buffer.append(s_ansi_color_codes[color]);
if (s_log_timestamps)
buffer.append_format(TIMESTAMP_FORMAT_STRING, Log::GetCurrentMessageTime());
buffer.append(message);
if (supports_color_codes)
buffer.append(s_ansi_color_codes[Color_Default]);
buffer.append('\n');
#ifdef _WIN32
@@ -134,7 +145,6 @@ cleanup:
if (wmessage_buf != wbuf)
std::free(wmessage_buf);
#else
const int fd = (level <= LOGLEVEL_WARNING) ? STDERR_FILENO : STDOUT_FILENO;
write(fd, buffer.data(), buffer.length());
#endif
}
@@ -157,7 +167,7 @@ void Log::SetConsoleOutputLevel(LOGLEVEL level)
if (was_enabled == now_enabled)
return;
// Worst that happens here is we write to a bad handle..
// Worst that happens here is we write to a bad handle..
#if defined(_WIN32)
static constexpr auto enable_virtual_terminal_processing = [](HANDLE hConsole) {
+1
View File
@@ -163,6 +163,7 @@
<ClInclude Include="WindowInfo.h" />
<ClInclude Include="YAML.h" />
<ClInclude Include="Threading.h" />
<ClInclude Include="EnumOps.h" />
<ClInclude Include="emitter\implement\avx.h" />
<ClInclude Include="emitter\implement\bmi.h" />
<ClInclude Include="emitter\instructions.h" />
-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_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::TextureCopiesROV, s_total_copies_rov, s_last_depth_copies_rov);
update_stat(GSPerfMon::DrawCallsROV, s_total_draws_rov, s_last_draws_rov);
update_stat(GSPerfMon::BarriersROV, s_total_barriers_rov, s_last_barriers_rov);
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@ Copies (ROV): {} (avg {})", s_total_copies_rov, static_cast<u64>(std::ceil(s_total_copies_rov / static_cast<double>(s_total_drawn_frames)))));
Console.WriteLn(fmt::format("@HWSTAT@ Draws Calls (ROV): {} (avg {})", s_total_draws_rov, static_cast<u64>(std::ceil(s_total_draws_rov / static_cast<double>(s_total_drawn_frames)))));
Console.WriteLn(fmt::format("@HWSTAT@ Barriers (ROV): {} (avg {})", s_total_barriers_rov, static_cast<u64>(std::ceil(s_total_barriers_rov / static_cast<double>(s_total_drawn_frames)))));
if (s_perf_enable)
{
Console.WriteLn(fmt::format("@HWSTAT@ Minimum Frame Time: {:.3f} ms ({:.3f} FPS)", PerformanceMetrics::GetMinimumFrameTime(), 1000.0f / PerformanceMetrics::GetMinimumFrameTime()));
+9 -9
View File
@@ -50,15 +50,15 @@ private:
QWidget* parent;
MemoryViewType displayType = MemoryViewType::BYTE;
bool littleEndian = true;
u32 rowCount;
u32 rowVisible;
s32 rowHeight;
u32 rowCount = 0;
u32 rowVisible = 0;
s32 rowHeight = 0;
// Stuff used for selection handling
// This gets set every paint and depends on the window size / current display mode (1byte,2byte,etc)
s32 valuexAxis; // Where the hexadecimal view begins
s32 textXAxis; // Where the text view begins
s32 row1YAxis; // Where the first row starts
s32 valuexAxis = 0; // Where the hexadecimal view begins
s32 textXAxis = 0; // Where the text view begins
s32 row1YAxis = 0; // Where the first row starts
s32 segmentXAxis[16]; // Where the segments begin
bool selectedText = false; // Whether the user has clicked on text or hex
@@ -89,9 +89,9 @@ public:
{
}
u32 startAddress;
u32 selectedAddress;
s32 selectedIndex;
u32 startAddress = 0;
u32 selectedAddress = 0;
s32 selectedIndex = 0;
void UpdateStartAddress(u32 start);
void UpdateSelectedAddress(u32 selected, bool page = false);
+29 -4
View File
@@ -73,9 +73,10 @@ const char* GameListModel::getColumnName(const Column col)
return s_column_names[static_cast<int>(col)];
}
GameListModel::GameListModel(const float cover_scale, const bool show_cover_titles, const qreal dpr, QObject* parent /* = nullptr */)
GameListModel::GameListModel(const float cover_scale, const bool show_cover_titles, bool show_full_cover_titles, const qreal dpr, QObject* parent /* = nullptr */)
: QAbstractTableModel(parent)
, m_show_titles_for_covers(show_cover_titles)
, m_show_full_titles_for_covers(show_full_cover_titles)
, m_dpr{dpr}
{
loadSettings();
@@ -315,9 +316,33 @@ QVariant GameListModel::data(const QModelIndex& index, const int role) const
switch (index.column())
{
case Column_Cover:
return QSize(static_cast<int>(static_cast<float>(SIZE_HINT_WIDTH) * m_cover_scale),
static_cast<int>(static_cast<float>(m_show_titles_for_covers ? SIZE_HINT_HEIGHT_TITLES : SIZE_HINT_HEIGHT) * m_cover_scale));
{
const int cover_width = static_cast<int>(static_cast<float>(SIZE_HINT_WIDTH) * m_cover_scale);
const int cover_height = static_cast<int>(static_cast<float>(SIZE_HINT_HEIGHT) * m_cover_scale);
if (m_show_full_titles_for_covers && m_show_titles_for_covers)
{
// Find height needed for the longest title at the current font/width
const int text_width = static_cast<int>(static_cast<float>(COVER_ART_WIDTH) * m_cover_scale);
QFont font;
font.setPointSizeF(20.0f * m_cover_scale);
const QFontMetrics fm(font);
int max_text_height = fm.height();
const auto lock = GameList::GetLock();
const u32 count = GameList::GetEntryCount();
for (u32 i = 0; i < count; i++)
{
const GameList::Entry* entry = GameList::GetEntryByIndex(i);
if (!entry) continue;
const QString title = QString::fromStdString(entry->GetTitle(m_prefer_english_titles));
const QRect bound = fm.boundingRect(QRect(0, 0, text_width, 0),
Qt::TextWordWrap | Qt::AlignHCenter, title);
max_text_height = std::max(max_text_height, bound.height());
}
return QSize(cover_width, cover_height + max_text_height);
}
return QSize(cover_width, static_cast<int>(static_cast<float>(m_show_titles_for_covers ? SIZE_HINT_HEIGHT_TITLES : SIZE_HINT_HEIGHT) * m_cover_scale));
}
default:
return QVariant();
}
@@ -354,7 +379,7 @@ bool GameListModel::titlesLessThan(const int left_row, const int right_row) cons
const GameList::Entry* left = GameList::GetEntryByIndex(left_row);
const GameList::Entry* right = GameList::GetEntryByIndex(right_row);
return QtHost::LocaleSensitiveCompare(QString::fromStdString(left->GetTitleSort(m_prefer_english_titles)),
QString::fromStdString(right->GetTitleSort(m_prefer_english_titles))) < 0;
QString::fromStdString(right->GetTitleSort(m_prefer_english_titles))) < 0;
}
bool GameListModel::lessThan(const QModelIndex& left_index, const QModelIndex& right_index, const int column) const
+5 -1
View File
@@ -43,7 +43,7 @@ public:
static QIcon getIconForType(GameList::EntryType type);
static QIcon getIconForRegion(GameList::Region region);
GameListModel(float cover_scale, bool show_cover_titles, qreal dpr, QObject* parent = nullptr);
GameListModel(float cover_scale, bool show_cover_titles, bool show_full_cover_titles, qreal dpr, QObject* parent = nullptr);
~GameListModel();
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
@@ -63,6 +63,9 @@ public:
bool getShowCoverTitles() const { return m_show_titles_for_covers; }
void setShowCoverTitles(bool enabled) { m_show_titles_for_covers = enabled; }
bool getShowFullCoverTitles() const { return m_show_full_titles_for_covers; }
void setShowFullCoverTitles(bool enabled) { m_show_full_titles_for_covers = enabled; }
float getCoverScale() const { return m_cover_scale; }
void setCoverScale(float scale);
int getCoverArtWidth() const;
@@ -89,6 +92,7 @@ private:
float m_cover_scale = 0.0f;
std::atomic<u32> m_cover_scale_counter{0};
bool m_show_titles_for_covers = false;
bool m_show_full_titles_for_covers = false;
bool m_prefer_english_titles = false;
std::array<QString, Column_Count> m_column_display_names;
+133 -32
View File
@@ -22,7 +22,10 @@
#include <QtCore/QSortFilterProxyModel>
#include <QtCore/QDir>
#include <QtCore/QString>
#include <QtGui/QColor>
#include <QtGui/QImage>
#include <QtGui/QPainter>
#include <QtGui/QPalette>
#include <QtGui/QPixmap>
#include <QtGui/QPixmapCache>
#include <QtGui/QWheelEvent>
@@ -191,6 +194,29 @@ namespace
painter->drawPixmap(rect.topLeft() + icon_top_left, highlighted_icon);
}
// Recolor the icon based on the custom background color
else if (index.column() == GameListModel::Column_Type)
{
// Fetch pixmap from cache or construct a new one.
const QColor color = option.palette.color(QPalette::Text);
const QString key = QString::fromStdString(fmt::format("type-{:016X}-{:08X}", icon.cacheKey(), color.rgba()));
QPixmap tinted_icon;
if (!QPixmapCache::find(key, &tinted_icon))
{
QImage img = icon.toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied);
QPainter tinted_painter(&img);
tinted_painter.setCompositionMode(QPainter::CompositionMode_SourceAtop);
tinted_painter.fillRect(0, 0, img.width(), img.height(), color);
tinted_painter.end();
tinted_icon = QPixmap(QPixmap::fromImage(img));
QPixmapCache::insert(key, tinted_icon);
}
painter->drawPixmap(rect.topLeft() + icon_top_left, tinted_icon);
}
else
{
painter->drawPixmap(rect.topLeft() + icon_top_left, icon);
@@ -213,7 +239,8 @@ void GameListWidget::initialize()
{
const float cover_scale = Host::GetBaseFloatSettingValue("UI", "GameListCoverArtScale", 0.45f);
const bool show_cover_titles = Host::GetBaseBoolSettingValue("UI", "GameListShowCoverTitles", true);
m_model = new GameListModel(cover_scale, show_cover_titles, devicePixelRatioF(), this);
const bool show_full_cover_titles = Host::GetBaseBoolSettingValue("UI", "GameListShowFullCoverTitles", true);
m_model = new GameListModel(cover_scale, show_cover_titles, show_full_cover_titles, devicePixelRatioF(), this);
m_model->updateCacheSize(width(), height());
m_sort_model = new GameListSortModel(m_model);
@@ -242,6 +269,7 @@ void GameListWidget::initialize()
connect(m_ui.viewGameGrid, &QPushButton::clicked, this, &GameListWidget::showGameGrid);
connect(m_ui.gridScale, &QSlider::valueChanged, this, &GameListWidget::gridIntScale);
connect(m_ui.viewGridTitles, &QPushButton::toggled, this, &GameListWidget::setShowCoverTitles);
connect(m_ui.viewFullGridTitles, &QPushButton::toggled, this, &GameListWidget::setShowFullCoverTitles);
connect(m_ui.filterType, &QComboBox::currentIndexChanged, this, [this](int index) {
m_sort_model->setFilterType((index == 0) ? GameList::EntryType::Count : static_cast<GameList::EntryType>(index - 1));
});
@@ -322,6 +350,7 @@ void GameListWidget::initialize()
m_list_view->setFrameStyle(QFrame::NoFrame);
m_list_view->setVerticalScrollMode(QAbstractItemView::ScrollMode::ScrollPerPixel);
m_list_view->verticalScrollBar()->setSingleStep(15);
m_list_view->setWordWrap(show_full_cover_titles);
onCoverScaleChanged();
connect(m_list_view->selectionModel(), &QItemSelectionModel::currentChanged, this,
@@ -381,8 +410,14 @@ void GameListWidget::setCustomBackground()
delete m_background_movie;
m_background_movie = nullptr;
}
// Cache all frames for small images so loops don't keep re-decoding
else if (const s64 file_size = FileSystem::GetPathFileSize(path.c_str()); file_size > 0 && file_size < 64 * 1024 * 1024)
m_background_movie->setCacheMode(QMovie::CacheAll);
}
// Invalidate frame cache so the next animated frame triggers full reprocessing
m_background_last_size = QSize();
// If there is no valid background then reset fallback to default UI state
if (!m_background_movie)
{
@@ -391,6 +426,11 @@ void GameListWidget::setCustomBackground()
m_table_view->viewport()->setAutoFillBackground(true);
m_list_view->viewport()->setAutoFillBackground(true);
m_ui.stack->setPalette(QPalette());
m_background_text_color = QColor();
m_empty_widget->setPalette(QPalette());
m_empty_widget->setAutoFillBackground(false);
m_ui.stack->update();
m_table_view->setAlternatingRowColors(true);
return;
@@ -415,45 +455,79 @@ void GameListWidget::setCustomBackground()
m_background_opacity = Host::GetBaseFloatSettingValue("UI", "GameListBackgroundOpacity", 100.0f);
// Selected Custom background is valid, connect the signals and start animation in gamelist
connect(m_background_movie, &QMovie::frameChanged, this, &GameListWidget::processBackgroundFrames, Qt::UniqueConnection);
connect(m_background_movie, &QMovie::frameChanged, this, &GameListWidget::processBackgroundFrames);
m_ui.stack->setAutoFillBackground(false);
m_table_view->viewport()->setAutoFillBackground(false);
m_list_view->viewport()->setAutoFillBackground(false);
updateCustomBackgroundState(true);
m_background_movie->start();
updateCustomBackgroundState();
m_table_view->setAlternatingRowColors(false);
processBackgroundFrames();
}
void GameListWidget::updateCustomBackgroundState(const bool force_start)
void GameListWidget::updateCustomBackgroundState()
{
if (m_background_movie && m_background_movie->isValid())
{
if ((isVisible() && (isActiveWindow() || force_start)) && qGuiApp->applicationState() == Qt::ApplicationActive)
m_background_movie->setPaused(false);
else
m_background_movie->setPaused(true);
}
m_background_movie->setPaused(!(isVisible() && qGuiApp->applicationState() == Qt::ApplicationActive));
}
void GameListWidget::processBackgroundFrames()
{
if (m_background_movie && m_background_movie->isValid() && isVisible())
{
const int widget_width = m_ui.stack->width();
const int widget_height = m_ui.stack->height();
if (!m_background_movie || !m_background_movie->isValid() || !isVisible())
return;
if (widget_width <= 0 || widget_height <= 0)
return;
const QSize widget_size(m_ui.stack->width(), m_ui.stack->height());
if (widget_size.isEmpty())
return;
QPixmap pm = m_background_movie->currentPixmap();
const qreal dpr = devicePixelRatioF();
const int frame_number = m_background_movie->currentFrameNumber();
const qreal dpr = devicePixelRatioF();
QtUtils::resizeAndScalePixmap(&pm, widget_width, widget_height, dpr, m_background_scaling, m_background_opacity);
if (frame_number == m_background_last_frame && widget_size == m_background_last_size && qFuzzyCompare(dpr, m_background_last_dpr))
return;
m_background_pixmap = std::move(pm);
m_ui.stack->update();
}
QPixmap pm = m_background_movie->currentPixmap();
updateBackgroundTextColor(pm);
QtUtils::resizeAndScalePixmap(&pm, widget_size.width(), widget_size.height(), dpr, m_background_scaling, m_background_opacity);
m_background_pixmap = std::move(pm);
m_background_last_frame = frame_number;
m_background_last_size = widget_size;
m_background_last_dpr = dpr;
m_ui.stack->update();
}
void GameListWidget::updateBackgroundTextColor(const QPixmap& frame)
{
if (frame.isNull())
return;
const QImage sampled = frame.scaled(32, 32, Qt::IgnoreAspectRatio, Qt::FastTransformation).toImage();
const QColor average = sampled.scaled(1, 1, Qt::IgnoreAspectRatio, Qt::SmoothTransformation).pixelColor(0, 0);
const QColor base = qApp->palette().color(QPalette::Base);
const qreal coverage = average.alphaF() * std::clamp(m_background_opacity / 100.0f, 0.0f, 1.0f);
const qreal brightness = qGray(average.rgb()) * coverage + qGray(base.rgb()) * (1.0 - coverage);
const QColor text_color = (brightness > 127.5) ? Qt::black : Qt::white;
if (m_background_text_color == text_color)
return;
m_background_text_color = text_color;
QColor highlight_color = qApp->palette().color(QPalette::Highlight);
highlight_color.setAlpha(128);
const QColor empty_backdrop_color = (text_color == Qt::black) ? QColor(255, 255, 255, 128) : QColor(0, 0, 0, 128);
QPalette palette;
palette.setColor(QPalette::Text, text_color);
palette.setColor(QPalette::WindowText, text_color);
palette.setColor(QPalette::Highlight, highlight_color);
m_ui.stack->setPalette(palette);
QPalette empty_palette;
empty_palette.setColor(QPalette::Window, empty_backdrop_color);
m_empty_widget->setPalette(empty_palette);
m_empty_widget->setAutoFillBackground(true);
}
bool GameListWidget::isShowingGameList() const
@@ -471,6 +545,11 @@ bool GameListWidget::getShowGridCoverTitles() const
return m_model->getShowCoverTitles();
}
bool GameListWidget::getShowGridFullCoverTitles() const
{
return m_model->getShowFullCoverTitles();
}
void GameListWidget::refresh(bool invalidate_cache, bool popup_on_error)
{
cancelRefresh();
@@ -499,6 +578,8 @@ void GameListWidget::cancelRefresh()
void GameListWidget::reloadThemeSpecificImages()
{
m_model->reloadThemeSpecificImages();
m_background_last_size = QSize();
processBackgroundFrames();
}
void GameListWidget::onRefreshProgress(const QString& status, int current, int total)
@@ -654,6 +735,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 +745,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 +754,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 +764,6 @@ void GameListWidget::showGameGrid()
return;
}
Host::SetBaseBoolSettingValue("UI", "GameListGridView", true);
Host::CommitBaseSettingChanges();
m_ui.stack->setCurrentIndex(1);
setFocusProxy(m_ui.stack->currentWidget());
updateToolbar();
@@ -701,9 +784,24 @@ void GameListWidget::setShowCoverTitles(bool enabled)
emit layoutChange();
}
void GameListWidget::setShowFullCoverTitles(bool enabled)
{
if (m_model->getShowFullCoverTitles() == enabled)
return;
Host::SetBaseBoolSettingValue("UI", "GameListShowFullCoverTitles", enabled);
Host::CommitBaseSettingChanges();
m_model->setShowFullCoverTitles(enabled);
m_list_view->setWordWrap(enabled);
if (isShowingGameGrid())
m_model->refresh();
updateToolbar();
emit layoutChange();
}
void GameListWidget::updateToolbar()
{
const bool grid_view = isShowingGameGrid();
const bool grid_view = Host::GetBaseBoolSettingValue("UI", "GameListGridView", false);
{
QSignalBlocker sb(m_ui.viewGameGrid);
m_ui.viewGameGrid->setChecked(grid_view);
@@ -716,12 +814,17 @@ void GameListWidget::updateToolbar()
QSignalBlocker sb(m_ui.viewGridTitles);
m_ui.viewGridTitles->setChecked(m_model->getShowCoverTitles());
}
{
QSignalBlocker sb(m_ui.viewFullGridTitles);
m_ui.viewFullGridTitles->setChecked(m_model->getShowFullCoverTitles());
}
{
QSignalBlocker sb(m_ui.gridScale);
m_ui.gridScale->setValue(static_cast<int>(m_model->getCoverScale() * 100.0f));
}
m_ui.viewGridTitles->setEnabled(grid_view);
m_ui.viewFullGridTitles->setEnabled(grid_view && m_model->getShowCoverTitles());
m_ui.gridScale->setEnabled(grid_view);
}
@@ -729,6 +832,7 @@ void GameListWidget::showEvent(QShowEvent* event)
{
QWidget::showEvent(event);
updateCustomBackgroundState();
processBackgroundFrames();
}
void GameListWidget::hideEvent(QHideEvent* event)
@@ -765,11 +869,8 @@ bool GameListWidget::eventFilter(QObject* watched, QEvent* event)
if (!m_background_pixmap.isNull())
{
QPainter painter(m_ui.stack);
const auto* paint_event = static_cast<QPaintEvent*>(event);
painter.save();
painter.setClipRect(paint_event->rect());
painter.drawTiledPixmap(m_ui.stack->rect(), m_background_pixmap);
painter.restore();
painter.setClipRect(static_cast<QPaintEvent*>(event)->rect());
painter.drawPixmap(0, 0, m_background_pixmap);
return true;
}
}
+9 -1
View File
@@ -9,6 +9,7 @@
#include "pcsx2/GameList.h"
#include <QtGui/QColor>
#include <QtGui/QMovie>
#include <QtGui/QPixmap>
#include <QtWidgets/QListView>
@@ -53,12 +54,14 @@ public:
void cancelRefresh();
void reloadThemeSpecificImages();
void setCustomBackground();
void updateCustomBackgroundState(const bool force_start = false);
void updateCustomBackgroundState();
void processBackgroundFrames();
void updateBackgroundTextColor(const QPixmap& frame);
bool isShowingGameList() const;
bool isShowingGameGrid() const;
bool getShowGridCoverTitles() const;
bool getShowGridFullCoverTitles() const;
std::optional<GameList::Entry> getSelectedEntry() const;
@@ -93,6 +96,7 @@ public Q_SLOTS:
void showGameList();
void showGameGrid();
void setShowCoverTitles(bool enabled);
void setShowFullCoverTitles(bool enabled);
void gridZoomIn();
void gridZoomOut();
void gridIntScale(int int_scale);
@@ -128,6 +132,10 @@ private:
QMovie* m_background_movie = nullptr;
QPixmap m_background_pixmap;
QColor m_background_text_color;
QtUtils::ScalingMode m_background_scaling = QtUtils::ScalingMode::Fit;
float m_background_opacity = 100.0f;
int m_background_last_frame = -1;
QSize m_background_last_size;
qreal m_background_last_dpr = 0.0;
};
+26
View File
@@ -120,6 +120,31 @@
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="viewFullGridTitles">
<property name="minimumSize">
<size>
<width>32</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Show Full Titles</string>
</property>
<property name="text">
<string>Show Full Titles</string>
</property>
<property name="icon">
<iconset theme="price-tag-2-line"/>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="gridScale">
<property name="minimumSize">
@@ -218,6 +243,7 @@
<tabstop>viewGameList</tabstop>
<tabstop>viewGameGrid</tabstop>
<tabstop>viewGridTitles</tabstop>
<tabstop>viewFullGridTitles</tabstop>
<tabstop>gridScale</tabstop>
<tabstop>filterType</tabstop>
<tabstop>filterRegion</tabstop>
+309 -64
View File
@@ -36,6 +36,9 @@
#include "pcsx2/ImGui/FullscreenUI.h"
#include "pcsx2/MTGS.h"
#include "pcsx2/PerformanceMetrics.h"
#include "pcsx2/VMManager.h"
#include "pcsx2/ImGui/ImGuiOverlays.h"
#include "pcsx2/SPU2/spu2.h"
#include "pcsx2/Recording/InputRecording.h"
#include "pcsx2/Recording/InputRecordingControls.h"
#include "pcsx2/SaveState.h"
@@ -54,8 +57,11 @@
#include <QtWidgets/QInputDialog>
#include <QtWidgets/QMessageBox>
#include <QtWidgets/QProgressBar>
#include <QtWidgets/QSlider>
#include <QtWidgets/QStyle>
#include <QtWidgets/QStyleFactory>
#include <QtWidgets/QWidgetAction>
#include <QtWidgets/QHBoxLayout>
#ifdef _WIN32
#include "common/RedtapeWindows.h"
@@ -147,6 +153,14 @@ void MainWindow::initialize()
switchToGameListView();
updateWindowTitle();
updateGameDependentActions();
updateEmulationActions(false, s_vm_valid, false);
updateStatusBarWidgetVisibility();
if (s_vm_paused)
{
m_ui.actionPause->setChecked(true);
m_ui.actionToolbarPause->setChecked(true);
}
#ifdef _WIN32
registerForDeviceNotifications();
@@ -185,6 +199,7 @@ void MainWindow::setupAdditionalUi()
{
makeIconsMasks(menuBar());
updateAdvancedSettingsVisibility();
setupStatusBarWidgets();
const bool toolbar_visible = Host::GetBaseBoolSettingValue("UI", "ShowToolbar", false);
m_ui.actionViewToolbar->setChecked(toolbar_visible);
@@ -195,58 +210,6 @@ void MainWindow::setupAdditionalUi()
m_ui.toolBar->setMovable(!toolbars_locked);
m_ui.toolBar->setContextMenuPolicy(Qt::PreventContextMenu);
const bool status_bar_visible = Host::GetBaseBoolSettingValue("UI", "ShowStatusBar", true);
m_ui.actionViewStatusBar->setChecked(status_bar_visible);
m_ui.statusBar->setVisible(status_bar_visible);
const bool show_game_grid = Host::GetBaseBoolSettingValue("UI", "GameListGridView", false);
updateGameGridActions(show_game_grid);
m_game_list_widget = new GameListWidget(getContentParent());
m_game_list_widget->initialize();
m_ui.actionGridViewShowTitles->setChecked(m_game_list_widget->getShowGridCoverTitles());
m_ui.mainContainer->addWidget(m_game_list_widget);
m_status_progress_widget = new QProgressBar(m_ui.statusBar);
m_status_progress_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
m_status_progress_widget->setFixedSize(140, 16);
m_status_progress_widget->setMinimum(0);
m_status_progress_widget->setMaximum(100);
m_status_progress_widget->hide();
m_status_verbose_widget = new QLabel(m_ui.statusBar);
m_status_verbose_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
m_status_verbose_widget->setFixedHeight(16);
m_status_verbose_widget->hide();
m_status_renderer_widget = new QLabel(m_ui.statusBar);
m_status_renderer_widget->setFixedHeight(16);
m_status_renderer_widget->setFixedSize(65, 16);
m_status_renderer_widget->hide();
m_status_resolution_widget = new QLabel(m_ui.statusBar);
m_status_resolution_widget->setFixedHeight(16);
m_status_resolution_widget->setFixedSize(75, 16);
m_status_resolution_widget->hide();
m_status_fps_widget = new QLabel(m_ui.statusBar);
m_status_fps_widget->setFixedHeight(16);
m_status_fps_widget->setMinimumWidth(60);
m_status_fps_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
m_status_fps_widget->hide();
m_status_vps_widget = new QLabel(m_ui.statusBar);
m_status_vps_widget->setFixedHeight(16);
m_status_vps_widget->setMinimumWidth(60);
m_status_vps_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
m_status_vps_widget->hide();
m_status_speed_widget = new QLabel(m_ui.statusBar);
m_status_speed_widget->setFixedHeight(16);
m_status_speed_widget->setMinimumWidth(130);
m_status_speed_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
m_status_speed_widget->hide();
m_settings_toolbar_menu = new QMenu(m_ui.toolBar);
m_settings_toolbar_menu->addAction(m_ui.actionSettings);
m_settings_toolbar_menu->addAction(m_ui.actionViewGameProperties);
@@ -257,6 +220,16 @@ void MainWindow::setupAdditionalUi()
connect(action, &QAction::triggered, [scale]() { g_emu_thread->requestDisplaySize(static_cast<float>(scale)); });
}
const bool show_game_grid = Host::GetBaseBoolSettingValue("UI", "GameListGridView", false);
const bool show_grid_cover_titles = Host::GetBaseBoolSettingValue("UI", "ShowGridCoverTitles", false);
updateGameGridActions(show_game_grid, show_grid_cover_titles);
m_game_list_widget = new GameListWidget(getContentParent());
m_game_list_widget->initialize();
m_ui.actionGridViewShowTitles->setChecked(m_game_list_widget->getShowGridCoverTitles());
m_ui.actionGridViewShowFullTitles->setChecked(m_game_list_widget->getShowGridFullCoverTitles());
m_ui.mainContainer->addWidget(m_game_list_widget);
updateEmulationActions(false, false, false);
updateDisplayRelatedActions(false, false, false);
@@ -292,6 +265,216 @@ void MainWindow::setupAdditionalUi()
#endif
}
void MainWindow::setupStatusBarWidgets()
{
const bool status_bar_visible = Host::GetBaseBoolSettingValue("UI", "ShowStatusBar", true);
m_ui.actionViewStatusBar->setChecked(status_bar_visible);
m_ui.statusBar->setVisible(status_bar_visible);
m_status_progress_widget = new QProgressBar(m_ui.statusBar);
m_status_progress_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
m_status_progress_widget->setFixedSize(140, 16);
m_status_progress_widget->setMinimum(0);
m_status_progress_widget->setMaximum(100);
m_status_progress_widget->hide();
m_status_verbose_widget = new QLabel(m_ui.statusBar);
m_status_verbose_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
m_status_verbose_widget->setContentsMargins(10, 0, 10, 0);
m_status_verbose_widget->setFixedHeight(20);
m_status_verbose_widget->setMinimumWidth(20);
m_status_verbose_widget->hide();
m_status_renderer_widget = new QLabel(m_ui.statusBar);
m_status_renderer_widget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
m_status_renderer_widget->setContentsMargins(10, 0, 10, 0);
m_status_renderer_widget->setAlignment(Qt::AlignCenter);
m_status_renderer_widget->setFixedHeight(20);
m_status_renderer_widget->hide();
m_status_resolution_widget = new QLabel(m_ui.statusBar);
m_status_resolution_widget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
m_status_resolution_widget->setContentsMargins(10, 0, 10, 0);
m_status_resolution_widget->setAlignment(Qt::AlignCenter);
m_status_resolution_widget->setFixedHeight(20);
m_status_resolution_widget->hide();
m_status_volume_widget = new QToolButton(m_ui.statusBar);
m_status_volume_widget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
m_status_volume_widget->setContentsMargins(10, 0, 10, 0);
m_status_volume_widget->setAutoRaise(true);
m_status_volume_widget->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
m_status_volume_widget->setIcon(QIcon::fromTheme(QStringLiteral("volume-up-line")));
m_status_volume_widget->setPopupMode(QToolButton::InstantPopup);
m_status_volume_widget->setFixedHeight(20);
m_status_volume_menu = new QMenu(m_status_volume_widget);
m_status_volume_per_game_action = new QAction(tr("Adjust Per-Game"), m_status_volume_menu);
m_status_volume_per_game_action->setCheckable(true);
connect(m_status_volume_menu, &QMenu::aboutToShow, this, [this]() {
auto lock = Host::GetSettingsLock();
SettingsInterface* game_layer = Host::Internal::GetGameSettingsLayer();
m_status_volume_per_game_action->setEnabled(game_layer != nullptr && QtHost::IsVMValid());
m_status_volume_per_game_action->setChecked(game_layer &&
(game_layer->ContainsValue("SPU2/Output", "StandardVolume") || game_layer->ContainsValue("SPU2/Output", "OutputMuted")));
});
connect(m_status_volume_per_game_action, &QAction::triggered, this, [this](bool checked) {
m_status_volume_slider_applied = true;
applyStatusBarVolumeChanges(std::nullopt, false, checked);
});
m_status_volume_toggle_mute_action = new QAction(tr("Toggle Mute"), m_status_volume_menu);
m_status_volume_toggle_mute_action->setIcon(QIcon::fromTheme(QStringLiteral("volume-mute-line")));
connect(m_status_volume_toggle_mute_action, &QAction::triggered, this, [this]() {
m_status_volume_slider_applied = true;
applyStatusBarVolumeChanges(std::nullopt, true, std::nullopt);
});
m_status_volume_menu->addAction(m_status_volume_per_game_action);
m_status_volume_menu->addAction(m_status_volume_toggle_mute_action);
m_status_volume_menu->addSeparator();
m_status_volume_slider = new QSlider(Qt::Horizontal, m_status_volume_menu);
m_status_volume_slider->setRange(0, 100);
m_status_volume_slider->setFixedWidth(120);
m_status_volume_slider->setValue(Host::GetIntSettingValue("SPU2/Output", "StandardVolume", 100));
connect(m_status_volume_slider, &QSlider::valueChanged, this, [this](int value) {
Host::RunOnCPUThread([value]() {
if (!VMManager::HasValidVM())
return;
EmuConfig.SPU2.StandardVolume = static_cast<u32>(value);
SPU2::SetOutputVolume(static_cast<u32>(value));
});
if (m_status_volume_slider->isSliderDown())
setStatusVolumeText(m_status_volume_muted ? tr("Volume: Muted") : tr("Volume: %1%").arg(value), value, m_status_volume_muted);
});
connect(m_status_volume_slider, &QSlider::sliderReleased, this, [this]() {
m_status_volume_slider_applied = true;
applyStatusBarVolumeChanges(m_status_volume_slider->value(), false);
});
connect(m_status_volume_menu, &QMenu::aboutToHide, this, [this]() {
if (!m_status_volume_slider_applied)
applyStatusBarVolumeChanges(m_status_volume_slider->value(), false);
m_status_volume_slider_applied = false;
});
QWidget* container = new QWidget(m_status_volume_menu);
QHBoxLayout* layout = new QHBoxLayout(container);
layout->setContentsMargins(8, 4, 8, 4);
layout->addWidget(m_status_volume_slider);
QWidgetAction* slider_action = new QWidgetAction(m_status_volume_menu);
slider_action->setDefaultWidget(container);
m_status_volume_menu->addAction(slider_action);
m_status_volume_widget->setMenu(m_status_volume_menu);
m_status_volume_widget->hide();
m_status_speed_widget = new QToolButton(m_ui.statusBar);
m_status_speed_widget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
m_status_speed_widget->setContentsMargins(10, 0, 10, 0);
m_status_speed_widget->setAutoRaise(true);
m_status_speed_widget->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
m_status_speed_widget->setPopupMode(QToolButton::InstantPopup);
m_status_speed_widget->setFixedHeight(20);
m_status_speed_widget->hide();
m_status_speed_menu = new QMenu(m_status_speed_widget);
m_status_speed_menu->addAction(tr("Unlimited"), []() { Host::RunOnCPUThread([]() { VMManager::SetLimiterMode(LimiterModeType::Unlimited); }); });
m_status_speed_menu->addAction(tr("Turbo"), []() { Host::RunOnCPUThread([]() { VMManager::SetLimiterMode(LimiterModeType::Turbo); }); });
m_status_speed_menu->addAction(tr("Slow-Motion"), []() { Host::RunOnCPUThread([]() { VMManager::SetLimiterMode(LimiterModeType::Slomo); }); });
m_status_speed_menu->addAction(tr("Normal"), []() { Host::RunOnCPUThread([]() { VMManager::SetLimiterMode(LimiterModeType::Nominal); }); });
m_status_speed_widget->setMenu(m_status_speed_menu);
m_status_speed_widget->hide();
m_status_gpu_widget = new QLabel(m_ui.statusBar);
m_status_gpu_widget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
m_status_gpu_widget->setContentsMargins(10, 0, 10, 0);
m_status_gpu_widget->setAlignment(Qt::AlignCenter);
m_status_gpu_widget->setFixedHeight(20);
m_status_gpu_widget->hide();
m_status_fps_widget = new QLabel(m_ui.statusBar);
m_status_fps_widget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
m_status_fps_widget->setContentsMargins(10, 0, 10, 0);
m_status_fps_widget->setAlignment(Qt::AlignCenter);
m_status_fps_widget->setFixedHeight(20);
m_status_fps_widget->hide();
m_status_vps_widget = new QLabel(m_ui.statusBar);
m_status_vps_widget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
m_status_vps_widget->setContentsMargins(10, 0, 10, 0);
m_status_vps_widget->setAlignment(Qt::AlignCenter);
m_status_vps_widget->setFixedHeight(20);
m_status_vps_widget->hide();
}
void MainWindow::applyStatusBarVolumeChanges(std::optional<int> volume, bool toggle_mute, std::optional<bool> override_per_game)
{
const bool use_per_game = override_per_game.value_or(m_status_volume_per_game_action->isChecked());
Host::RunOnCPUThread([this, volume, toggle_mute, use_per_game]() {
int target_vol = 100;
bool target_mute = false;
{
auto lock = Host::GetSettingsLock();
SettingsInterface* per_game_settings = Host::Internal::GetGameSettingsLayer();
SettingsInterface* global_settings = Host::Internal::GetBaseSettingsLayer();
SettingsInterface* active_setting = (use_per_game && per_game_settings) ? per_game_settings : global_settings;
if (active_setting)
{
if (!active_setting->GetIntValue("SPU2/Output", "StandardVolume", &target_vol) && global_settings)
global_settings->GetIntValue("SPU2/Output", "StandardVolume", &target_vol);
if (!active_setting->GetBoolValue("SPU2/Output", "OutputMuted", &target_mute) && global_settings)
global_settings->GetBoolValue("SPU2/Output", "OutputMuted", &target_mute);
}
target_vol = volume.value_or(target_vol);
if (toggle_mute)
target_mute = !(VMManager::HasValidVM() ? SPU2::IsOutputMuted() : target_mute);
if (active_setting)
{
active_setting->SetIntValue("SPU2/Output", "StandardVolume", target_vol);
active_setting->SetBoolValue("SPU2/Output", "OutputMuted", target_mute);
}
if (per_game_settings && active_setting == per_game_settings)
QtHost::SaveGameSettings(per_game_settings, true);
else
{
if (per_game_settings)
{
const bool had_per_game_volume =
per_game_settings->ContainsValue("SPU2/Output", "StandardVolume") ||
per_game_settings->ContainsValue("SPU2/Output", "OutputMuted");
per_game_settings->DeleteValue("SPU2/Output", "StandardVolume");
per_game_settings->DeleteValue("SPU2/Output", "OutputMuted");
if (had_per_game_volume)
QtHost::SaveGameSettings(per_game_settings, true);
}
Host::CommitBaseSettingChanges();
}
}
if (VMManager::HasValidVM())
{
EmuConfig.SPU2.StandardVolume = static_cast<u32>(target_vol);
EmuConfig.SPU2.OutputMuted = target_mute;
SPU2::SetOutputMuted(target_mute);
SPU2::SetOutputVolume(static_cast<u32>(target_vol));
}
QTimer::singleShot(0, this, [this, target_vol, target_mute]() {
setStatusVolumeText(target_mute ? tr("Volume: Muted") : tr("Volume: %1%").arg(target_vol), target_vol, target_mute);
});
});
}
void MainWindow::connectSignals()
{
connect(m_ui.actionStartFile, &QAction::triggered, this, &MainWindow::onStartFileActionTriggered);
@@ -362,6 +545,7 @@ void MainWindow::connectSignals()
connect(m_ui.actionOpenDataDirectory, &QAction::triggered, this, &MainWindow::onToolsOpenDataDirectoryTriggered);
connect(m_ui.actionCoverDownloader, &QAction::triggered, this, &MainWindow::onToolsCoverDownloaderTriggered);
connect(m_ui.actionGridViewShowTitles, &QAction::triggered, m_game_list_widget, &GameListWidget::setShowCoverTitles);
connect(m_ui.actionGridViewShowFullTitles, &QAction::triggered, m_game_list_widget, &GameListWidget::setShowFullCoverTitles);
connect(m_ui.actionGridViewZoomIn, &QAction::triggered, m_game_list_widget, [this]() {
if (isShowingGameList())
m_game_list_widget->gridZoomIn();
@@ -373,8 +557,10 @@ void MainWindow::connectSignals()
connect(m_ui.actionGridViewRefreshCovers, &QAction::triggered, m_game_list_widget, &GameListWidget::refreshGridCovers);
connect(m_game_list_widget, &GameListWidget::layoutChange, this, [this]() {
QSignalBlocker sb(m_ui.actionGridViewShowTitles);
QSignalBlocker sb2(m_ui.actionGridViewShowFullTitles);
m_ui.actionGridViewShowTitles->setChecked(m_game_list_widget->getShowGridCoverTitles());
updateGameGridActions(m_game_list_widget->isShowingGameGrid());
m_ui.actionGridViewShowFullTitles->setChecked(m_game_list_widget->getShowGridFullCoverTitles());
updateGameGridActions(m_game_list_widget->isShowingGameGrid(), m_game_list_widget->getShowGridCoverTitles());
});
SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.actionViewStatusBarVerbose, "UI", "VerboseStatusBar", false);
@@ -454,8 +640,6 @@ void MainWindow::connectVMThreadSignals(EmuThread* thread)
connect(thread, &EmuThread::onCaptureStopped, this, &MainWindow::onCaptureStopped);
connect(thread, &EmuThread::onAchievementsLoginRequested, this, &MainWindow::onAchievementsLoginRequested);
connect(thread, &EmuThread::onAchievementsHardcoreModeChanged, this, &MainWindow::onAchievementsHardcoreModeChanged);
connect(thread, &EmuThread::onCoverDownloaderOpenRequested, this, &MainWindow::onToolsCoverDownloaderTriggered);
connect(thread, &EmuThread::onCreateMemoryCardOpenRequested, this, &MainWindow::onCreateMemoryCardOpenRequested);
connect(m_ui.actionReset, &QAction::triggered, this, &MainWindow::requestReset);
connect(m_ui.actionPause, &QAction::toggled, thread, &EmuThread::setVMPaused);
@@ -498,12 +682,11 @@ void MainWindow::createRendererSwitchMenu()
QString::fromUtf8(Pcsx2Config::GSOptions::GetRendererName(renderer)), switch_renderer_group);
action->setCheckable(true);
action->setChecked(current_renderer == renderer);
connect(action,
&QAction::triggered, [renderer] {
Host::SetBaseIntSettingValue("EmuCore/GS", "Renderer", static_cast<int>(renderer));
Host::CommitBaseSettingChanges();
g_emu_thread->applySettings();
});
connect(action, &QAction::triggered, [renderer] {
Host::SetBaseIntSettingValue("EmuCore/GS", "Renderer", static_cast<int>(renderer));
Host::CommitBaseSettingChanges();
g_emu_thread->applySettings();
});
}
m_ui.menuDebugSwitchRenderer->addActions(switch_renderer_group->actions());
@@ -550,6 +733,9 @@ void MainWindow::recreate()
g_main_window->updateEmulationActions(false, s_vm_valid, false);
g_main_window->onFullscreenUIStateChange(g_emu_thread->isRunningFullscreenUI());
}
if (s_vm_valid)
g_emu_thread->updatePerformanceMetrics(true);
}
void MainWindow::recreateSettings()
@@ -1021,13 +1207,15 @@ void MainWindow::updateDisplayRelatedActions(bool has_surface, bool render_to_ma
void MainWindow::updateStatusBarWidgetVisibility()
{
auto Update = [this](QWidget* widget, bool visible, int stretch) {
if (widget->isVisible() == visible)
return;
if (widget->isVisible())
{
m_ui.statusBar->removeWidget(widget);
widget->hide();
}
if (visible)
else
{
m_ui.statusBar->addPermanentWidget(widget, stretch);
widget->show();
@@ -1037,9 +1225,63 @@ void MainWindow::updateStatusBarWidgetVisibility()
Update(m_status_verbose_widget, s_vm_valid, 1);
Update(m_status_renderer_widget, s_vm_valid, 0);
Update(m_status_resolution_widget, s_vm_valid, 0);
Update(m_status_gpu_widget, s_vm_valid, 0);
Update(m_status_fps_widget, s_vm_valid, 0);
Update(m_status_vps_widget, s_vm_valid, 0);
Update(m_status_speed_widget, s_vm_valid, 0);
Update(m_status_volume_widget, s_vm_valid, 0);
}
void MainWindow::setStatusVerboseText(const QString& text)
{
m_status_verbose_widget->setText(text);
}
void MainWindow::setStatusRendererText(const QString& text)
{
m_status_renderer_widget->setText(text);
}
void MainWindow::setStatusResolutionText(const QString& text)
{
m_status_resolution_widget->setText(text);
}
void MainWindow::setStatusVolumeText(const QString& text, int volume, bool muted)
{
m_status_volume_widget->setText(text);
if (muted != m_status_volume_muted)
{
m_status_volume_muted = muted;
m_status_volume_widget->setIcon(QIcon::fromTheme(muted ? QStringLiteral("volume-mute-line") : QStringLiteral("volume-up-line")));
m_status_volume_toggle_mute_action->setIcon(QIcon::fromTheme(muted ? QStringLiteral("volume-up-line") : QStringLiteral("volume-mute-line")));
}
if (!muted && !m_status_volume_slider->isSliderDown())
{
QSignalBlocker blocker(m_status_volume_slider);
m_status_volume_slider->setValue(volume);
}
}
void MainWindow::setStatusGPUText(const QString& text)
{
m_status_gpu_widget->setText(text);
}
void MainWindow::setStatusFPSText(const QString& text)
{
m_status_fps_widget->setText(text);
}
void MainWindow::setStatusVPSText(const QString& text)
{
m_status_vps_widget->setText(text);
}
void MainWindow::setStatusSpeedText(const QString& text)
{
m_status_speed_widget->setText(text);
}
void MainWindow::updateWindowTitle()
@@ -2220,6 +2462,8 @@ void MainWindow::onVMStopped()
m_last_fps_status = empty_string;
m_status_renderer_widget->setText(empty_string);
m_status_resolution_widget->setText(empty_string);
m_status_volume_widget->setText(empty_string);
m_status_gpu_widget->setText(empty_string);
m_status_fps_widget->setText(empty_string);
m_status_vps_widget->setText(empty_string);
m_status_speed_widget->setText(empty_string);
@@ -3428,9 +3672,10 @@ void MainWindow::updateGameDependentActions()
m_ui.actionReloadPatches->setEnabled(s_vm_valid);
}
void MainWindow::updateGameGridActions(const bool show_game_grid)
void MainWindow::updateGameGridActions(const bool show_game_grid, const bool show_grid_cover_titles)
{
m_ui.actionGridViewShowTitles->setEnabled(show_game_grid);
m_ui.actionGridViewShowFullTitles->setEnabled(show_game_grid && show_grid_cover_titles);
m_ui.actionGridViewZoomIn->setEnabled(show_game_grid);
m_ui.actionGridViewZoomOut->setEnabled(show_game_grid);
m_ui.actionGridViewRefreshCovers->setEnabled(show_game_grid);
+27 -3
View File
@@ -8,6 +8,8 @@
#include <QtWidgets/QLabel>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QMenu>
#include <QtWidgets/QSlider>
#include <QtWidgets/QToolButton>
#include <functional>
#include <optional>
@@ -96,11 +98,13 @@ public:
/// Accessors for the status bar widgets, updated by the emulation thread.
__fi QLabel* getStatusVerboseWidget() const { return m_status_verbose_widget; }
__fi QToolButton* getStatusSpeedWidget() const { return m_status_speed_widget; }
__fi QToolButton* getStatusVolumeWidget() const { return m_status_volume_widget; }
__fi QLabel* getStatusRendererWidget() const { return m_status_renderer_widget; }
__fi QLabel* getStatusResolutionWidget() const { return m_status_resolution_widget; }
__fi QLabel* getStatusGPUWidget() const { return m_status_gpu_widget; }
__fi QLabel* getStatusFPSWidget() const { return m_status_fps_widget; }
__fi QLabel* getStatusVPSWidget() const { return m_status_vps_widget; }
__fi QLabel* getStatusSpeedWidget() const { return m_status_speed_widget; }
/// Rescans a single file. NOTE: Happens on UI thread.
void rescanFile(const std::string& path);
@@ -125,6 +129,15 @@ public Q_SLOTS:
void reportStateLoadError(const QString& message, std::optional<s32> slot, bool backup);
void reportStateSaveError(const QString& message, std::optional<s32> slot);
void setStatusVerboseText(const QString& text);
void setStatusRendererText(const QString& text);
void setStatusResolutionText(const QString& text);
void setStatusVolumeText(const QString& text, int volume, bool muted);
void setStatusGPUText(const QString& text);
void setStatusFPSText(const QString& text);
void setStatusVPSText(const QString& text);
void setStatusSpeedText(const QString& text);
void runOnUIThread(const std::function<void()>& func);
void requestReset();
bool requestShutdown(bool allow_confirm = true, bool allow_save_to_state = true, bool default_save_to_state = true);
@@ -227,6 +240,8 @@ protected:
private:
void setupAdditionalUi();
void setupStatusBarWidgets();
void applyStatusBarVolumeChanges(std::optional<int> volume, bool toggle_mute, std::optional<bool> override_per_game = std::nullopt);
void connectSignals();
void createRendererSwitchMenu();
void recreate();
@@ -242,7 +257,7 @@ private:
void updateEmulationActions(bool starting, bool running, bool stopping);
void updateDisplayRelatedActions(bool has_surface, bool render_to_main, bool fullscreen);
void updateGameDependentActions();
void updateGameGridActions(const bool show_game_grid);
void updateGameGridActions(const bool show_game_grid, const bool show_grid_cover_titles);
void updateStatusBarWidgetVisibility();
void updateAdvancedSettingsVisibility();
void updateWindowTitle();
@@ -309,14 +324,23 @@ private:
QProgressBar* m_status_progress_widget = nullptr;
QLabel* m_status_verbose_widget = nullptr;
QLabel* m_status_renderer_widget = nullptr;
QToolButton* m_status_volume_widget = nullptr;
QMenu* m_status_volume_menu = nullptr;
QAction* m_status_volume_per_game_action = nullptr;
QAction* m_status_volume_toggle_mute_action = nullptr;
QSlider* m_status_volume_slider = nullptr;
QLabel* m_status_gpu_widget = nullptr;
QLabel* m_status_fps_widget = nullptr;
QLabel* m_status_vps_widget = nullptr;
QLabel* m_status_speed_widget = nullptr;
QToolButton* m_status_speed_widget = nullptr;
QMenu* m_status_speed_menu = nullptr;
QLabel* m_status_resolution_widget = nullptr;
QMenu* m_settings_toolbar_menu = nullptr;
bool m_display_created = false;
bool m_status_volume_muted = false;
bool m_status_volume_slider_applied = false;
bool m_display_is_exclusive_fullscreen = false;
bool m_relative_mouse_mode = false;
bool m_hide_mouse_cursor = false;
+15
View File
@@ -175,6 +175,7 @@
<addaction name="menuWindowSize"/>
<addaction name="separator"/>
<addaction name="actionGridViewShowTitles"/>
<addaction name="actionGridViewShowFullTitles"/>
<addaction name="actionGridViewZoomIn"/>
<addaction name="actionGridViewZoomOut"/>
<addaction name="actionGridViewRefreshCovers"/>
@@ -808,6 +809,20 @@
<string>Show Titl&amp;es (Grid View)</string>
</property>
</action>
<action name="actionGridViewShowFullTitles">
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<property name="icon">
<iconset theme="price-tag-2-line"/>
</property>
<property name="text">
<string>Show F&amp;ull Titles (Grid View)</string>
</property>
</action>
<action name="actionGridViewZoomIn">
<property name="icon">
<iconset theme="zoom-in-line"/>
+109 -33
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
@@ -430,10 +433,16 @@ void EmuThread::run()
void EmuThread::destroyVM()
{
m_last_speed = 0.0f;
m_last_gpu_usage = 0.0f;
m_last_game_fps = 0.0f;
m_last_video_fps = 0.0f;
m_last_internal_width = 0;
m_last_internal_height = 0;
m_last_upscale = 0.0f;
m_last_volume = 0;
m_last_muted = false;
m_last_renderer = GSRendererType::Auto;
m_last_limiter_mode = LimiterModeType::Nominal;
m_was_paused_by_focus_loss = false;
VMManager::Shutdown(m_save_state_on_shutdown);
m_save_state_on_shutdown = false;
@@ -967,8 +976,15 @@ void EmuThread::updatePerformanceMetrics(bool force)
if (!g_main_window)
return;
const s32 slot = SaveStateSelectorUI::GetCurrentSlot();
u32 volume = 0;
bool muted = false;
if (VMManager::HasValidVM())
{
volume = SPU2::GetOutputVolume();
muted = SPU2::IsOutputMuted();
QString gs_stat;
if (m_verbose_status)
{
@@ -977,9 +993,8 @@ void EmuThread::updatePerformanceMetrics(bool force)
if (THREAD_VU1)
{
gs_stat = tr("Slot: %1 | Volume: %2% | %3 | EE: %4% | VU: %5% | GS: %6%")
.arg(SaveStateSelectorUI::GetCurrentSlot())
.arg(SPU2::GetOutputVolume())
gs_stat = tr("Slot: %1 | %2 | EE: %3% | VU: %4% | GS: %5%")
.arg(slot)
.arg(gs_stat_str.c_str())
.arg(PerformanceMetrics::GetCPUThreadUsage(), 0, 'f', 0)
.arg(PerformanceMetrics::GetVUThreadUsage(), 0, 'f', 0)
@@ -987,48 +1002,81 @@ void EmuThread::updatePerformanceMetrics(bool force)
}
else
{
gs_stat = tr("Slot: %1 | Volume: %2% | %3 | EE: %4% | GS: %5%")
.arg(SaveStateSelectorUI::GetCurrentSlot())
.arg(SPU2::GetOutputVolume())
gs_stat = tr("Slot: %1 | %2 | EE: %3% | GS: %4%")
.arg(slot)
.arg(gs_stat_str.c_str())
.arg(PerformanceMetrics::GetCPUThreadUsage(), 0, 'f', 0)
.arg(PerformanceMetrics::GetGSThreadUsage(), 0, 'f', 0);
}
}
QMetaObject::invokeMethod(g_main_window->getStatusVerboseWidget(), "setText", Qt::QueuedConnection, Q_ARG(const QString&, gs_stat));
QMetaObject::invokeMethod(g_main_window, "setStatusVerboseText", Qt::QueuedConnection, Q_ARG(const QString&, gs_stat));
}
const GSRendererType renderer = GSGetCurrentRenderer(); // Reading from GS thread, therefore racey, but it's just visual.
const float upscale = EmuConfig.GS.UpscaleMultiplier;
const float speed = std::round(PerformanceMetrics::GetSpeed());
const LimiterModeType limiter_mode = VMManager::GetLimiterMode();
const float gpu_usage = std::round(PerformanceMetrics::GetGPUUsage());
const float gfps = std::round(PerformanceMetrics::GetInternalFPS());
const float vfps = std::round(PerformanceMetrics::GetFPS());
int iwidth, iheight;
GSgetInternalResolution(&iwidth, &iheight);
if (iwidth != m_last_internal_width || iheight != m_last_internal_height || speed != m_last_speed || gfps != m_last_game_fps ||
vfps != m_last_video_fps || renderer != m_last_renderer || force)
if (iwidth != m_last_internal_width || iheight != m_last_internal_height || upscale != m_last_upscale ||
speed != m_last_speed || gpu_usage != m_last_gpu_usage || gfps != m_last_game_fps || vfps != m_last_video_fps || renderer != m_last_renderer ||
volume != m_last_volume || muted != m_last_muted || force)
{
if (volume != m_last_volume || muted != m_last_muted || force)
{
QString vol_text = tr("Volume: %1%").arg(volume);
if (muted)
vol_text = tr("Volume: Muted");
QMetaObject::invokeMethod(g_main_window, "setStatusVolumeText", Qt::QueuedConnection,
Q_ARG(const QString&, vol_text), Q_ARG(int, static_cast<int>(volume)), Q_ARG(bool, muted));
m_last_volume = volume;
m_last_muted = muted;
}
if (renderer != m_last_renderer || force)
{
QMetaObject::invokeMethod(g_main_window->getStatusRendererWidget(), "setText", Qt::QueuedConnection,
Q_ARG(const QString&, QString::fromUtf8(Pcsx2Config::GSOptions::GetRendererName(renderer))));
QString renderer_name = QString::fromUtf8(Pcsx2Config::GSOptions::GetRendererName(renderer));
if (EmuConfig.GS.Renderer == GSRendererType::Auto && renderer != GSRendererType::Auto)
renderer_name = tr("Auto (%1)").arg(renderer_name);
QMetaObject::invokeMethod(g_main_window, "setStatusRendererText", Qt::QueuedConnection,
Q_ARG(const QString&, renderer_name));
m_last_renderer = renderer;
}
if (iwidth != m_last_internal_width || iheight != m_last_internal_height || force)
if (iwidth != m_last_internal_width || iheight != m_last_internal_height || upscale != m_last_upscale || force)
{
QString text;
if (iwidth == 0 || iheight == 0)
text = tr("No Image");
else
text = tr("%1x%2").arg(iwidth).arg(iheight);
text = tr("%1x%2 (%3x)").arg(iwidth).arg(iheight).arg(upscale, 0, 'g', 3);
QMetaObject::invokeMethod(
g_main_window->getStatusResolutionWidget(), "setText", Qt::QueuedConnection, Q_ARG(const QString&, text));
g_main_window, "setStatusResolutionText", Qt::QueuedConnection, Q_ARG(const QString&, text));
m_last_internal_width = iwidth;
m_last_internal_height = iheight;
m_last_upscale = upscale;
}
if (gpu_usage != m_last_gpu_usage || force)
{
QString text;
if (gpu_usage == 0)
text = tr("GPU: N/A");
else
text = tr("GPU: %1%").arg(gpu_usage, 0, 'f', 0);
QMetaObject::invokeMethod(g_main_window, "setStatusGPUText", Qt::QueuedConnection,
Q_ARG(const QString&, text));
m_last_gpu_usage = gpu_usage;
}
if (gfps != m_last_game_fps || force)
@@ -1039,23 +1087,33 @@ void EmuThread::updatePerformanceMetrics(bool force)
else
text = tr("FPS: %1").arg(gfps, 0, 'f', 0);
QMetaObject::invokeMethod(g_main_window->getStatusFPSWidget(), "setText", Qt::QueuedConnection,
QMetaObject::invokeMethod(g_main_window, "setStatusFPSText", Qt::QueuedConnection,
Q_ARG(const QString&, text));
m_last_game_fps = gfps;
}
if (vfps != m_last_video_fps || force)
{
QMetaObject::invokeMethod(g_main_window->getStatusVPSWidget(), "setText", Qt::QueuedConnection,
Q_ARG(const QString&, tr("VPS: %1 ").arg(vfps, 0, 'f', 0)));
QMetaObject::invokeMethod(g_main_window, "setStatusVPSText", Qt::QueuedConnection,
Q_ARG(const QString&, vfps == 0 ? tr("VPS: N/A") : tr("VPS: %1").arg(vfps, 0, 'f', 0)));
m_last_video_fps = vfps;
}
if (speed != m_last_speed || force)
{
QMetaObject::invokeMethod(g_main_window->getStatusSpeedWidget(), "setText", Qt::QueuedConnection,
Q_ARG(const QString&, tr("Speed: %1% ").arg(speed, 0, 'f', 0)));
m_last_speed = speed;
}
if (speed != m_last_speed || force)
{
QMetaObject::invokeMethod(g_main_window, "setStatusSpeedText", Qt::QueuedConnection,
Q_ARG(const QString&, tr("Speed: %1%").arg(speed, 0, 'f', 0)));
m_last_speed = speed;
}
if (limiter_mode != m_last_limiter_mode || force)
{
QMetaObject::invokeMethod(g_main_window, [limiter_mode]() { g_main_window->getStatusSpeedWidget()->setIcon(QIcon::fromTheme(
limiter_mode == LimiterModeType::Turbo ? QStringLiteral("fast-forward-line") :
limiter_mode == LimiterModeType::Slomo ? QStringLiteral("slow-mo") :
limiter_mode == LimiterModeType::Unlimited ? QStringLiteral("speed-line") :
QStringLiteral("dashboard-line"))); }, Qt::QueuedConnection);
m_last_limiter_mode = limiter_mode;
}
}
}
@@ -1131,16 +1189,6 @@ void Host::OnAchievementsHardcoreModeChanged(bool enabled)
emit g_emu_thread->onAchievementsHardcoreModeChanged(enabled);
}
void Host::OnCoverDownloaderOpenRequested()
{
emit g_emu_thread->onCoverDownloaderOpenRequested();
}
void Host::OnCreateMemoryCardOpenRequested()
{
emit g_emu_thread->onCreateMemoryCardOpenRequested();
}
bool Host::ShouldPreferHostFileSelector()
{
#ifdef __linux__
@@ -1676,6 +1724,12 @@ bool Host::CopyTextToClipboard(const std::string_view text)
return true;
}
std::string Host::GetTextFromClipboard()
{
std::lock_guard<std::mutex> lock(s_clipboard_cache_mutex);
return s_clipboard_cache;
}
void Host::BeginTextInput()
{
QInputMethod* method = qApp->inputMethod();
@@ -2361,6 +2415,26 @@ void QtHost::RegisterTypes()
qRegisterMetaType<Achievements::LoginRequestReason>();
}
void QtHost::InitializeClipboard()
{
QClipboard* clipboard = QGuiApplication::clipboard();
if (clipboard)
{
{
std::lock_guard<std::mutex> lock(s_clipboard_cache_mutex);
s_clipboard_cache = clipboard->text().toStdString();
}
QObject::connect(clipboard, &QClipboard::dataChanged, []() {
QClipboard* cb = QGuiApplication::clipboard();
if (cb)
{
std::lock_guard<std::mutex> lock(s_clipboard_cache_mutex);
s_clipboard_cache = cb->text().toStdString();
}
});
}
}
bool QtHost::RunSetupWizard()
{
SetupWizardDialog dialog;
@@ -2409,6 +2483,8 @@ int main(int argc, char* argv[])
PCSX2MainApplication app(argc, argv);
QtHost::InitializeClipboard();
#ifndef _WIN32
if (!PerformEarlyHardwareChecks())
return EXIT_FAILURE;
+5 -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();
@@ -216,11 +212,16 @@ private:
bool m_was_paused_by_focus_loss = false;
float m_last_speed = 0.0f;
float m_last_gpu_usage = 0.0f;
float m_last_game_fps = 0.0f;
float m_last_video_fps = 0.0f;
int m_last_internal_width = 0;
int m_last_internal_height = 0;
float m_last_upscale = 0.0f;
u32 m_last_volume = 0;
bool m_last_muted = false;
GSRendererType m_last_renderer = GSRendererType::Auto;
LimiterModeType m_last_limiter_mode = LimiterModeType::Nominal;
};
extern EmuThread* g_emu_thread;
+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 -2
View File
@@ -156,7 +156,7 @@ namespace QtUtils
if (pm->width() == dpr_expected_width &&
pm->height() == dpr_expected_height &&
pm->devicePixelRatio() == dpr &&
qFuzzyCompare(pm->devicePixelRatio(), dpr) &&
opacity == 100.0f)
{
switch (scaling_mode)
@@ -249,7 +249,6 @@ namespace QtUtils
QPixmap tileSource = pm->scaled(tileWidth, tileHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation);
tileSource.setDevicePixelRatio(dpr);
QBrush tileBrush(tileSource);
tileBrush.setTextureImage(tileSource.toImage());
painter.fillRect(painterRect, tileBrush);
}
break;
+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;
}
+1 -6
View File
@@ -474,7 +474,7 @@
</item>
<item>
<property name="text">
<string>Deflate64</string>
<string>Deflate</string>
</property>
</item>
<item>
@@ -482,11 +482,6 @@
<string>Zstandard</string>
</property>
</item>
<item>
<property name="text">
<string>LZMA2</string>
</property>
</item>
</widget>
</item>
</layout>
+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);
}
@@ -32,7 +32,7 @@ EmulationSettingsWidget::EmulationSettingsWidget(SettingsWindow* settings_dialog
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.vsync, "EmuCore/GS", "VsyncEnable", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.syncToHostRefreshRate, "EmuCore/GS", "SyncToHostRefreshRate", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.useVSyncForTiming, "EmuCore/GS", "UseVSyncForTiming", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.skipPresentingDuplicateFrames, "EmuCore/GS", "SkipDuplicateFrames", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.skipPresentingDuplicateFrames, "EmuCore/GS", "SkipDuplicateFrames", true);
connect(m_ui.optimalFramePacing, &QCheckBox::checkStateChanged, this, &EmulationSettingsWidget::onOptimalFramePacingChanged);
connect(m_ui.vsync, &QCheckBox::checkStateChanged, this, &EmulationSettingsWidget::updateUseVSyncForTimingEnabled);
connect(m_ui.syncToHostRefreshRate, &QCheckBox::checkStateChanged, this, &EmulationSettingsWidget::updateUseVSyncForTimingEnabled);
@@ -150,7 +150,7 @@ EmulationSettingsWidget::EmulationSettingsWidget(SettingsWindow* settings_dialog
dialog()->registerWidgetHelp(m_ui.useVSyncForTiming, tr("Use Host VSync Timing"), tr("Unchecked"),
tr("When synchronizing with the host refresh rate, this option disables PCSX2's internal frame timing and uses the host instead. "
"Can result in smoother frame pacing, <strong>but at the cost of increased input latency</strong>."));
dialog()->registerWidgetHelp(m_ui.skipPresentingDuplicateFrames, tr("Skip Presenting Duplicate Frames"), tr("Unchecked"),
dialog()->registerWidgetHelp(m_ui.skipPresentingDuplicateFrames, tr("Skip Presenting Duplicate Frames"), tr("Checked"),
tr("Detects when idle frames are being presented in 25/30fps games, and skips presenting those frames. The frame is still "
"rendered, it just means the GPU has more time to complete it (this is NOT frame skipping). Can smooth out frame time "
"fluctuations when the CPU/GPU are near maximum utilization, but makes frame pacing more inconsistent and can increase "
+1 -1
View File
@@ -364,7 +364,7 @@ void GameSummaryWidget::onSearchHashClicked()
if (m_redump_search_keyword.empty())
return;
QtUtils::OpenURL(this, fmt::format("http://redump.org/discs/quicksearch/{}", m_redump_search_keyword).c_str());
QtUtils::OpenURL(this, fmt::format("https://redump.info/discs?q={}", m_redump_search_keyword).c_str());
}
void GameSummaryWidget::onCheckWikiClicked(const std::string& serial)
+1 -1
View File
@@ -552,7 +552,7 @@
<bool>false</bool>
</property>
<property name="text">
<string>Search on Redump.org...</string>
<string>Search on Redump.info...</string>
</property>
</widget>
</item>
@@ -43,6 +43,11 @@
<string>Accurate (Recommended)</string>
</property>
</item>
<item>
<property name="text">
<string>Accurate Force Full (Can Reduce Readbacks)</string>
</property>
</item>
<item>
<property name="text">
<string>Disable Readbacks (Synchronize GS Thread)</string>
@@ -91,27 +96,6 @@
</item>
<item row="10" column="0" colspan="2">
<layout class="QGridLayout" name="advancedLayout">
<item row="1" column="1">
<widget class="QCheckBox" name="extendedUpscales">
<property name="text">
<string>Extended Upscaling Multipliers</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="disableMailboxPresentation">
<property name="text">
<string extracomment="Mailbox Presentation: a type of graphics-rendering technique that has not been exposed to the public that often, so chances are you will need to keep the word mailbox in English. It does not have anything to do with postal mailboxes or email inboxes/outboxes.">Disable Mailbox Presentation</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="useBlitSwapChain">
<property name="text">
<string extracomment="Blit = a data operation. You might want to write it as-is, but fully uppercased. More information: https://en.wikipedia.org/wiki/Bit_blit \nSwap chain: see Microsoft's Terminology Portal.">Use Blit Swap Chain</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="spinCPUDuringReadbacks">
<property name="text">
@@ -119,13 +103,41 @@
</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="rovBarriersVK">
<property name="text">
<string>ROV Barriers Vulkan</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>
</layout>
</item>
<item row="6" column="0">
@@ -353,19 +365,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/>
@@ -72,6 +72,13 @@
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="rov">
<property name="text">
<string>Rasterizer Ordered View</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="enableHWFixes">
<property name="text">
<string>Manual Hardware Renderer Fixes</string>
@@ -113,6 +113,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* settings_dialog,
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_hw.mipmapping, "EmuCore/GS", "hw_mipmap", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_hw.accurateAlphaTest, "EmuCore/GS", "HWAccurateAlphaTest", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_hw.hwAA1, "EmuCore/GS", "HWAA1", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_hw.rov, "EmuCore/GS", "HWROV", false);
SettingWidgetBinder::BindWidgetToIntSetting(
sif, m_hw.blending, "EmuCore/GS", "accurate_blending_unit", static_cast<int>(AccBlendLevel::Basic));
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_hw.enableHWFixes, "EmuCore/GS", "UserHacks", 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());
@@ -497,6 +499,9 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* settings_dialog,
dialog()->registerWidgetHelp(
m_hw.hwAA1, tr("AA1"), tr("Unchecked"), tr("Enables AA1 (PS2 antialiasing), which some games require to render correctly. This may result in a heavy performance penalty."));
dialog()->registerWidgetHelp(
m_hw.rov, tr("Rasterizer Ordered View"), tr("Unchecked"), 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_hw.textureFiltering, tr("Texture Filtering"), tr("Bilinear (PS2)"),
tr("Changes what filtering algorithm is used to map textures to surfaces.<br> "
@@ -757,6 +762,9 @@ 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.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,12 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
if (m_advanced.exclusiveFullscreenControl)
m_advanced.exclusiveFullscreenControl->setEnabled(is_auto || is_vk);
if (m_hw.rov)
m_hw.rov->setDisabled(!is_hardware || is_ogl);
if (m_advanced.rovBarriersVK)
m_advanced.rovBarriersVK->setDisabled(!is_hardware || !is_vk);
// populate adapters
std::vector<GSAdapterInfo> adapters = GSGetAdapterInfo(type);
const GSAdapterInfo* current_adapter_info = nullptr;
+62 -1
View File
@@ -27,6 +27,35 @@ static std::string getSlotFilenameKey(u32 slot)
return StringUtil::StdStringFromFormat("Slot%u_Filename", slot + 1);
}
namespace
{
enum MemoryCardListColumn : int
{
Name,
Type,
Formatted,
LastModified,
};
class MemoryCardListItem : public QTreeWidgetItem
{
public:
using QTreeWidgetItem::QTreeWidgetItem;
bool operator<(const QTreeWidgetItem& other) const override
{
const QTreeWidget* tree = treeWidget();
const int column = tree ? tree->sortColumn() : 0;
if (column == MemoryCardListColumn::LastModified)
{
return data(MemoryCardListColumn::LastModified, Qt::UserRole).toLongLong() <
other.data(MemoryCardListColumn::LastModified, Qt::UserRole).toLongLong();
}
return text(column).localeAwareCompare(other.text(column)) < 0;
}
};
} // namespace
MemoryCardSettingsWidget::MemoryCardSettingsWidget(SettingsWindow* settings_dialog, QWidget* parent)
: SettingsWidget(settings_dialog, parent)
{
@@ -56,6 +85,11 @@ MemoryCardSettingsWidget::MemoryCardSettingsWidget(SettingsWindow* settings_dial
connect(m_ui.convertCard, &QPushButton::clicked, this, &MemoryCardSettingsWidget::convertCard);
connect(m_ui.deleteCard, &QPushButton::clicked, this, &MemoryCardSettingsWidget::deleteCard);
loadCardListSortState();
m_ui.cardList->setSortingEnabled(true);
connect(m_ui.cardList->header(), &QHeaderView::sortIndicatorChanged, this,
&MemoryCardSettingsWidget::saveCardListSortState);
refresh();
}
@@ -340,6 +374,24 @@ void MemoryCardSettingsWidget::swapCards()
refresh();
}
void MemoryCardSettingsWidget::saveCardListSortState()
{
Host::SetBaseIntSettingValue("MemoryCardListView", "SortColumn",
m_ui.cardList->header()->sortIndicatorSection());
Host::SetBaseBoolSettingValue("MemoryCardListView", "SortDescending",
m_ui.cardList->header()->sortIndicatorOrder() == Qt::DescendingOrder);
Host::CommitBaseSettingChanges();
}
void MemoryCardSettingsWidget::loadCardListSortState()
{
int sort_column = Host::GetBaseIntSettingValue("MemoryCardListView", "SortColumn", 0);
if (sort_column < 0 || sort_column > MemoryCardListColumn::LastModified)
sort_column = MemoryCardListColumn::Name;
const bool sort_descending = Host::GetBaseBoolSettingValue("MemoryCardListView", "SortDescending", false);
m_ui.cardList->sortByColumn(sort_column, sort_descending ? Qt::DescendingOrder : Qt::AscendingOrder);
}
static QString getSizeSummary(const AvailableMcdInfo& mcd)
{
if (mcd.type == MemoryCardType::File)
@@ -421,12 +473,17 @@ void MemoryCardListWidget::mouseMoveEvent(QMouseEvent* event)
void MemoryCardListWidget::refresh(SettingsWindow* dialog)
{
// We don't need to sort while it's being repopulated.
setSortingEnabled(false);
clear();
// we can't use the in use flag here anyway, because the config may not be in line with per game settings.
const std::vector<AvailableMcdInfo> mcds(FileMcd_GetAvailableCards(true));
if (mcds.empty())
{
setSortingEnabled(true);
return;
}
std::array<std::string, MemoryCardSettingsWidget::MAX_SLOTS> currentCards;
for (u32 i = 0; i < static_cast<u32>(currentCards.size()); i++)
@@ -439,7 +496,7 @@ void MemoryCardListWidget::refresh(SettingsWindow* dialog)
for (const AvailableMcdInfo& mcd : mcds)
{
QTreeWidgetItem* item = new QTreeWidgetItem();
MemoryCardListItem* item = new MemoryCardListItem();
const QDateTime mtime(QDateTime::fromSecsSinceEpoch(static_cast<qint64>(mcd.modified_time)));
const bool inUse = (std::find(currentCards.begin(), currentCards.end(), mcd.name) != currentCards.end());
@@ -452,9 +509,13 @@ void MemoryCardListWidget::refresh(SettingsWindow* dialog)
// store formatted metadata
item->setData(0, Qt::UserRole, mcd.formatted);
item->setData(MemoryCardListColumn::LastModified, Qt::UserRole,
static_cast<qint64>(mcd.modified_time));
addTopLevelItem(item);
}
setSortingEnabled(true);
}
MemoryCardSlotWidget::MemoryCardSlotWidget(QWidget* parent)
@@ -96,6 +96,9 @@ private:
void createCard();
void saveCardListSortState();
void loadCardListSortState();
QString getSelectedCard() const;
bool isSelectedCardFormatted() const;
void updateCardActions();
+22 -29
View File
@@ -62,6 +62,7 @@ OSDSettingsWidget::OSDSettingsWidget(SettingsWindow* settings_dialog, QWidget* p
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showUsageCPU, "EmuCore/GS", "OsdShowCPU", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showUsageGPU, "EmuCore/GS", "OsdShowGPU", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showDebugGPU, "EmuCore/GS", "OsdShowGPUDebug", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showStatsGPU, "EmuCore/GS", "OsdShowGPUStats", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showStatusIndicators, "EmuCore/GS", "OsdShowIndicators", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showFrameTimes, "EmuCore/GS", "OsdShowFrameTimes", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showHardwareInfo, "EmuCore/GS", "OsdShowHardwareInfo", false);
@@ -77,7 +78,7 @@ OSDSettingsWidget::OSDSettingsWidget(SettingsWindow* settings_dialog, QWidget* p
#ifndef _WIN32
// Currently DX12 only
m_ui.showDebugGPU->deleteLater();
m_ui.showDebugGPU->setVisible(false);
#endif
connect(m_ui.showSettings, &QCheckBox::checkStateChanged, this, &OSDSettingsWidget::onOsdShowSettingsToggled);
@@ -127,6 +128,8 @@ OSDSettingsWidget::OSDSettingsWidget(SettingsWindow* settings_dialog, QWidget* p
dialog()->registerWidgetHelp(m_ui.showDebugGPU, tr("Show GPU Debug Info"),
tr("Unchecked"), tr("Shows debug information about the renderer."));
#endif
dialog()->registerWidgetHelp(m_ui.showDebugGPU, tr("Show GPU Pipeline Statistics"),
tr("Unchecked"), tr("Shows GPU vertex shader and pixels shader invocations."));
dialog()->registerWidgetHelp(m_ui.showFrameTimes, tr("Show Frame Times"), tr("Unchecked"),
tr("Displays a graph showing the average frametimes."));
@@ -243,6 +246,7 @@ void OSDSettingsWidget::onPerformancePosChanged()
m_ui.showGSStats->setEnabled(enabled);
m_ui.showUsageCPU->setEnabled(enabled);
m_ui.showUsageGPU->setEnabled(enabled);
m_ui.showStatsGPU->setEnabled(enabled);
m_ui.showStatusIndicators->setEnabled(enabled);
m_ui.showFrameTimes->setEnabled(enabled);
m_ui.showHardwareInfo->setEnabled(enabled);
@@ -255,33 +259,7 @@ void OSDSettingsWidget::onOsdShowSettingsToggled()
m_ui.showPatches->setEnabled(enabled);
}
void OSDSettingsWidget::onSelectAllClicked()
{
const QList<QCheckBox*> checkboxes = {
m_ui.showSpeedPercentages,
m_ui.showFPS,
m_ui.showVPS,
m_ui.showResolution,
m_ui.showGSStats,
m_ui.showUsageCPU,
m_ui.showUsageGPU,
m_ui.showStatusIndicators,
m_ui.showFrameTimes,
m_ui.showHardwareInfo,
m_ui.showVersion,
m_ui.showSettings,
m_ui.showPatches,
m_ui.showInputs,
m_ui.showVideoCapture,
m_ui.showInputRec,
m_ui.showTextureReplacements,
m_ui.warnAboutUnsafeSettings};
for (QCheckBox* checkbox : checkboxes)
checkbox->setChecked(true);
}
void OSDSettingsWidget::onDeselectAllClicked()
void OSDSettingsWidget::setAllCheckboxes(bool checked)
{
const QList<QCheckBox*> checkboxes = {
m_ui.showSpeedPercentages,
@@ -291,6 +269,7 @@ void OSDSettingsWidget::onDeselectAllClicked()
m_ui.showGSStats,
m_ui.showUsageCPU,
m_ui.showUsageGPU,
m_ui.showStatsGPU,
m_ui.showFrameTimes,
m_ui.showHardwareInfo,
m_ui.showVersion,
@@ -300,7 +279,11 @@ void OSDSettingsWidget::onDeselectAllClicked()
m_ui.showTextureReplacements};
for (QCheckBox* checkbox : checkboxes)
checkbox->setChecked(false);
checkbox->setChecked(checked);
#ifdef _WIN32
m_ui.showDebugGPU->setChecked(checked);
#endif
// Keep these checked
m_ui.showStatusIndicators->setChecked(true);
@@ -309,4 +292,14 @@ void OSDSettingsWidget::onDeselectAllClicked()
m_ui.warnAboutUnsafeSettings->setChecked(true);
}
void OSDSettingsWidget::onSelectAllClicked()
{
setAllCheckboxes(true);
}
void OSDSettingsWidget::onDeselectAllClicked()
{
setAllCheckboxes(false);
}
#include "moc_OSDSettingsWidget.cpp"
+1
View File
@@ -26,6 +26,7 @@ private Q_SLOTS:
void onClearOsdFontPathClicked();
void onSelectAllClicked();
void onDeselectAllClicked();
void setAllCheckboxes(bool checked);
private:
void loadOsdFontPathSetting();
+8
View File
@@ -492,6 +492,13 @@
</property>
</widget>
</item>
<item row="11" column="0">
<widget class="QCheckBox" name="showStatsGPU">
<property name="text">
<string>Show GPU Pipeline Statistics</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
@@ -530,6 +537,7 @@
<tabstop>showGSStats</tabstop>
<tabstop>showUsageCPU</tabstop>
<tabstop>showUsageGPU</tabstop>
<tabstop>showStatsGPU</tabstop>
<tabstop>showStatusIndicators</tabstop>
<tabstop>showFrameTimes</tabstop>
</tabstops>
+1 -1
View File
@@ -89,7 +89,7 @@ ShortcutCreationDialog::ShortcutCreationDialog(QWidget* parent, const QString& t
}
m_ui.iconPath->setText(Path::ToNativePath(icon_file.toStdString()).c_str());
m_ui.iconPreview->setPixmap(pixmap.scaled(m_ui.iconPreview->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
m_ui.iconPreview->setPixmap(pixmap.scaled(m_ui.iconPreview->size(), Qt::KeepAspectRatio, Qt::FastTransformation));
m_ui.resetIconButton->setEnabled(true);
}
});
+1
View File
@@ -250,6 +250,7 @@ std::vector<std::pair<QString, QString>> QtHost::GetAvailableLanguageList()
{QCoreApplication::translate("InterfaceSettingsWidget", "System Language [Default]"), QStringLiteral("system")},
{QStringLiteral("Afrikaans (af-ZA)"), QStringLiteral("af-ZA")},
{QStringLiteral("عربي (ar-SA)"), QStringLiteral("ar-SA")},
{QStringLiteral("Azərbaycanca (az-AZ)"), QStringLiteral("az-AZ")},
{QStringLiteral("Català (ca-ES)"), QStringLiteral("ca-ES")},
{QStringLiteral("Čeština (cs-CZ)"), QStringLiteral("cs-CZ")},
{QStringLiteral("Dansk (da-DK)"), QStringLiteral("da-DK")},
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1 @@
<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10-10-4.477-10-10 4.477-10 10-10m0 2a8 8 0 1 0 0 16 8 8 0 0 0 0-16m3.833 3.337a.596.596 0 0 1 .763.067.59.59 0 0 1 .063.76q-3.27 4.569-3.598 4.897a1.5 1.5 0 0 1 -2.122-2.122q.56-.56 4.894-3.602m1.667 3.663a1 1 0 1 1 0 2 1 1 0 0 1 0-2m-11 0a1 1 0 1 1 0 2 1 1 0 0 1 0-2m2.318-3.596a1 1 0 1 1 -1.414 1.414 1 1 0 0 1 1.414-1.414m3.182-1.904a1 1 0 1 1 0 2 1 1 0 0 1 0-2"/></svg>

After

Width:  |  Height:  |  Size: 507 B

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M22 4a1 1 0 0 0-1 1v5.666l-9.223-6.148a.5.5 0 0 0-.777.416v5.732L1.777 4.518A.5.5 0 0 0 1 4.934v14.132a.5.5 0 0 0 .777.416L11 13.333v5.733a.5.5 0 0 0 .777.416L21 13.333V19a1 1 0 1 0 2 0V5a1 1 0 0 0-1-1M3 7.737L9.394 12L3 16.263zm10 8.526V7.737L19.394 12z" />
</svg>

After

Width:  |  Height:  |  Size: 360 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="rgba(0,0,0,1)"><path d="M3.00488 6.99972L11.4502 1.36952C11.7861 1.14559 12.2237 1.14559 12.5596 1.36952L21.0049 6.99972V20.9997C21.0049 21.552 20.5572 21.9997 20.0049 21.9997H4.00488C3.4526 21.9997 3.00488 21.552 3.00488 20.9997V6.99972ZM5.00488 8.07009V19.9997H19.0049V8.07009L12.0049 3.40342L5.00488 8.07009ZM8.00488 15.9997H16.0049V17.9997H8.00488V15.9997ZM8.00488 12.9997H16.0049V14.9997H8.00488V12.9997ZM12.0049 10.9997C10.9003 10.9997 10.0049 10.1043 10.0049 8.99972C10.0049 7.89515 10.9003 6.99972 12.0049 6.99972C13.1095 6.99972 14.0049 7.89515 14.0049 8.99972C14.0049 10.1043 13.1095 10.9997 12.0049 10.9997Z"></path></svg>

After

Width:  |  Height:  |  Size: 699 B

@@ -0,0 +1 @@
<svg height="512" viewBox="0 0 512 512" width="512" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round"><path d="m158.61169 32.411431 327.37685 189.011119-327.37685 189.01111z" stroke-width="43.2072" transform="matrix(.75450544 0 0 .77725456 100.32662 82.344678)"/><path d="m106.51372 100v309.27423" stroke-width="76.6719"/></g></svg>

After

Width:  |  Height:  |  Size: 393 B

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="m12 13.333l-9.223 6.149A.5.5 0 0 1 2 19.066V4.934a.5.5 0 0 1 .777-.416L12 10.667V4.934a.5.5 0 0 1 .777-.416l10.599 7.066a.5.5 0 0 1 0 .832l-10.599 7.066a.5.5 0 0 1-.777-.416zM10.394 12L4 7.737v8.526zM14 7.737v8.526L20.394 12z" />
</svg>

After

Width:  |  Height:  |  Size: 331 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" d="M10 7.22L6.603 10H3v4h3.603L10 16.78zM5.889 16H2a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1h3.889l5.294-4.332a.5.5 0 0 1 .817.387v15.89a.5.5 0 0 1-.817.387zm14.525-4l3.536 3.536l-1.415 1.414L19 13.414l-3.536 3.536l-1.414-1.414L17.586 12L14.05 8.465l1.414-1.415L19 10.586l3.535-3.536l1.415 1.415z"/></svg>

After

Width:  |  Height:  |  Size: 403 B

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path fill="#fff" d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2m0 2a8 8 0 1 0 0 16a8 8 0 0 0 0-16m3.833 3.337a.596.596 0 0 1 .763.067a.59.59 0 0 1 .063.76q-3.27 4.569-3.598 4.897a1.5 1.5 0 0 1-2.122-2.122q.56-.56 4.894-3.602M17.5 11a1 1 0 1 1 0 2a1 1 0 0 1 0-2m-11 0a1 1 0 1 1 0 2a1 1 0 0 1 0-2m2.318-3.596a1 1 0 1 1-1.414 1.414a1 1 0 0 1 1.414-1.414M12 5.5a1 1 0 1 1 0 2a1 1 0 0 1 0-2" />
</svg>

After

Width:  |  Height:  |  Size: 510 B

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path fill="#fff" d="M22 4a1 1 0 0 0-1 1v5.666l-9.223-6.148a.5.5 0 0 0-.777.416v5.732L1.777 4.518A.5.5 0 0 0 1 4.934v14.132a.5.5 0 0 0 .777.416L11 13.333v5.733a.5.5 0 0 0 .777.416L21 13.333V19a1 1 0 1 0 2 0V5a1 1 0 0 0-1-1M3 7.737L9.394 12L3 16.263zm10 8.526V7.737L19.394 12z" />
</svg>

After

Width:  |  Height:  |  Size: 372 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#fff"><path d="M3.00488 6.99972L11.4502 1.36952C11.7861 1.14559 12.2237 1.14559 12.5596 1.36952L21.0049 6.99972V20.9997C21.0049 21.552 20.5572 21.9997 20.0049 21.9997H4.00488C3.4526 21.9997 3.00488 21.552 3.00488 20.9997V6.99972ZM5.00488 8.07009V19.9997H19.0049V8.07009L12.0049 3.40342L5.00488 8.07009ZM8.00488 15.9997H16.0049V17.9997H8.00488V15.9997ZM8.00488 12.9997H16.0049V14.9997H8.00488V12.9997ZM12.0049 10.9997C10.9003 10.9997 10.0049 10.1043 10.0049 8.99972C10.0049 7.89515 10.9003 6.99972 12.0049 6.99972C13.1095 6.99972 14.0049 7.89515 14.0049 8.99972C14.0049 10.1043 13.1095 10.9997 12.0049 10.9997Z"></path></svg>

After

Width:  |  Height:  |  Size: 691 B

@@ -0,0 +1 @@
<svg height="512" viewBox="0 0 512 512" width="512" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round"><path d="m158.61169 32.411431 327.37685 189.011119-327.37685 189.01111z" stroke-width="43.2072" transform="matrix(.75450544 0 0 .77725456 100.32662 82.344678)"/><path d="m106.51372 100v309.27423" stroke-width="76.6719"/></g></svg>

After

Width:  |  Height:  |  Size: 393 B

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path fill="#fff" d="m12 13.333l-9.223 6.149A.5.5 0 0 1 2 19.066V4.934a.5.5 0 0 1 .777-.416L12 10.667V4.934a.5.5 0 0 1 .777-.416l10.599 7.066a.5.5 0 0 1 0 .832l-10.599 7.066a.5.5 0 0 1-.777-.416zM10.394 12L4 7.737v8.526zM14 7.737v8.526L20.394 12z" />
</svg>

After

Width:  |  Height:  |  Size: 343 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="#fff" d="M10 7.22L6.603 10H3v4h3.603L10 16.78zM5.889 16H2a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1h3.889l5.294-4.332a.5.5 0 0 1 .817.387v15.89a.5.5 0 0 1-.817.387zm14.525-4l3.536 3.536l-1.415 1.414L19 13.414l-3.536 3.536l-1.414-1.414L17.586 12L14.05 8.465l1.414-1.415L19 10.586l3.535-3.536l1.415 1.415z"/></svg>

After

Width:  |  Height:  |  Size: 395 B

+12
View File
@@ -18,6 +18,7 @@
<file>icons/black/svg/close-line.svg</file>
<file>icons/black/svg/controller-line.svg</file>
<file>icons/black/svg/controller-strike-line.svg</file>
<file>icons/black/svg/dashboard-line.svg</file>
<file>icons/black/svg/debug-step-into-line.svg</file>
<file>icons/black/svg/debug-step-out-line.svg</file>
<file>icons/black/svg/debug-step-over-line.svg</file>
@@ -32,6 +33,7 @@
<file>icons/black/svg/eject-line.svg</file>
<file>icons/black/svg/emulation-line.svg</file>
<file>icons/black/svg/eyetoy-line.svg</file>
<file>icons/black/svg/fast-forward-line.svg</file>
<file>icons/black/svg/file-add-line.svg</file>
<file>icons/black/svg/file-line.svg</file>
<file>icons/black/svg/file-list-line.svg</file>
@@ -78,6 +80,7 @@
<file>icons/black/svg/play-line.svg</file>
<file>icons/black/svg/plus-line.svg</file>
<file>icons/black/svg/Popn-line.svg</file>
<file>icons/black/svg/price-tag-2-line.svg</file>
<file>icons/black/svg/price-tag-3-line.svg</file>
<file>icons/black/svg/printer-line.svg</file>
<file>icons/black/svg/realplay-sphere-line.svg</file>
@@ -90,6 +93,8 @@
<file>icons/black/svg/settings-3-line.svg</file>
<file>icons/black/svg/shut-down-line.svg</file>
<file>icons/black/svg/singstar-line.svg</file>
<file>icons/black/svg/slow-mo.svg</file>
<file>icons/black/svg/speed-line.svg</file>
<file>icons/black/svg/tools-line.svg</file>
<file>icons/black/svg/train-line.svg</file>
<file>icons/black/svg/trance-vibrator-line.svg</file>
@@ -97,6 +102,7 @@
<file>icons/black/svg/trophy-line.svg</file>
<file>icons/black/svg/tv-2-line.svg</file>
<file>icons/black/svg/usb-fill.svg</file>
<file>icons/black/svg/volume-mute-line.svg</file>
<file>icons/black/svg/volume-up-line.svg</file>
<file>icons/black/svg/warning-line.svg</file>
<file>icons/black/svg/wheel-line.svg</file>
@@ -123,6 +129,7 @@
<file>icons/white/svg/close-line.svg</file>
<file>icons/white/svg/controller-line.svg</file>
<file>icons/white/svg/controller-strike-line.svg</file>
<file>icons/white/svg/dashboard-line.svg</file>
<file>icons/white/svg/debug-step-into-line.svg</file>
<file>icons/white/svg/debug-step-out-line.svg</file>
<file>icons/white/svg/debug-step-over-line.svg</file>
@@ -137,6 +144,7 @@
<file>icons/white/svg/eject-line.svg</file>
<file>icons/white/svg/emulation-line.svg</file>
<file>icons/white/svg/eyetoy-line.svg</file>
<file>icons/white/svg/fast-forward-line.svg</file>
<file>icons/white/svg/file-add-line.svg</file>
<file>icons/white/svg/file-line.svg</file>
<file>icons/white/svg/file-list-line.svg</file>
@@ -186,6 +194,7 @@
<file>icons/white/svg/play-line.svg</file>
<file>icons/white/svg/plus-line.svg</file>
<file>icons/white/svg/Popn-line.svg</file>
<file>icons/white/svg/price-tag-2-line.svg</file>
<file>icons/white/svg/price-tag-3-line.svg</file>
<file>icons/white/svg/printer-line.svg</file>
<file>icons/white/svg/realplay-sphere-line.svg</file>
@@ -198,6 +207,8 @@
<file>icons/white/svg/settings-3-line.svg</file>
<file>icons/white/svg/shut-down-line.svg</file>
<file>icons/white/svg/singstar-line.svg</file>
<file>icons/white/svg/slow-mo.svg</file>
<file>icons/white/svg/speed-line.svg</file>
<file>icons/white/svg/tools-line.svg</file>
<file>icons/white/svg/train-line.svg</file>
<file>icons/white/svg/trance-vibrator-line.svg</file>
@@ -205,6 +216,7 @@
<file>icons/white/svg/trophy-line.svg</file>
<file>icons/white/svg/tv-2-line.svg</file>
<file>icons/white/svg/usb-fill.svg</file>
<file>icons/white/svg/volume-mute-line.svg</file>
<file>icons/white/svg/volume-up-line.svg</file>
<file>icons/white/svg/warning-line.svg</file>
<file>icons/white/svg/wheel-line.svg</file>
+296 -159
View File
@@ -183,6 +183,8 @@ namespace Achievements
static void DrawLeaderboardEntry(const rc_client_leaderboard_entry_t& entry, bool is_self, float rank_column_width,
float name_column_width, float time_column_width, float column_spacing);
static void OpenSubset(const rc_client_subset_t* subset);
static bool DrawSubsetSidebar(float sidebar_width, bool& sidebar_has_focus, bool leaderboards_only);
static void DrawSubsetSidebarFooter(bool sidebar_has_focus);
static void OpenLeaderboard(const rc_client_leaderboard_t* lboard);
static void LeaderboardFetchNearbyCallback(
int result, const char* error_message, rc_client_leaderboard_entry_list_t* list, rc_client_t* client, void* callback_userdata);
@@ -237,6 +239,9 @@ namespace Achievements
static float s_leaderboard_list_scroll = 0.0f;
static bool s_restore_leaderboard_scroll = false;
static std::map<std::pair<u32, u32>, bool> s_achievement_buckets_collapsed;
static std::map<std::pair<u32, u8>, bool> s_leaderboard_buckets_collapsed;
static std::vector<LeaderboardTrackerIndicator> s_active_leaderboard_trackers;
static std::vector<AchievementChallengeIndicator> s_active_challenge_indicators;
static std::optional<AchievementProgressIndicator> s_active_progress_indicator;
@@ -934,6 +939,17 @@ void Achievements::UpdateRichPresence(std::unique_lock<std::recursive_mutex>& lo
lock.lock();
}
void Achievements::PlayAchievementSound(bool is_specific_sound_enabled, const std::string& custom_sound_name, const std::string& default_sound_name)
{
if (!EmuConfig.Achievements.SoundEffects || !is_specific_sound_enabled)
return;
if (custom_sound_name.empty() || !FileSystem::FileExists(custom_sound_name.c_str()))
Common::PlaySoundAsync(Path::Combine(EmuFolders::Resources, default_sound_name).c_str());
else
Common::PlaySoundAsync(custom_sound_name.c_str());
}
void Achievements::GameChanged(u32 disc_crc, u32 crc)
{
std::unique_lock lock(s_achievements_mutex);
@@ -1151,13 +1167,7 @@ void Achievements::DisplayAchievementSummary()
}
});
}
if (EmuConfig.Achievements.SoundEffects && EmuConfig.Achievements.InfoSound)
Common::PlaySoundAsync(
(EmuConfig.Achievements.InfoSoundName.empty()
? Path::Combine(EmuFolders::Resources, DEFAULT_INFO_SOUND_NAME)
: EmuConfig.Achievements.InfoSoundName).c_str()
);
Achievements::PlayAchievementSound(EmuConfig.Achievements.InfoSound, EmuConfig.Achievements.InfoSoundName, DEFAULT_INFO_SOUND_NAME);
}
void Achievements::DisplayHardcoreDeferredMessage()
@@ -1207,13 +1217,7 @@ void Achievements::HandleUnlockEvent(const rc_client_event_t* event)
std::move(title), std::move(summary), std::move(badge_path));
});
}
if (EmuConfig.Achievements.SoundEffects && EmuConfig.Achievements.UnlockSound)
Common::PlaySoundAsync(
(EmuConfig.Achievements.UnlockSoundName.empty()
? Path::Combine(EmuFolders::Resources, DEFAULT_UNLOCK_SOUND_NAME)
: EmuConfig.Achievements.UnlockSoundName).c_str()
);
Achievements::PlayAchievementSound(EmuConfig.Achievements.UnlockSound, EmuConfig.Achievements.UnlockSoundName, DEFAULT_UNLOCK_SOUND_NAME);
}
void Achievements::HandleGameCompleteEvent(const rc_client_event_t* event)
@@ -1332,13 +1336,7 @@ void Achievements::HandleLeaderboardSubmittedEvent(const rc_client_event_t* even
}
});
}
if (EmuConfig.Achievements.SoundEffects && EmuConfig.Achievements.LBSubmitSound)
Common::PlaySoundAsync(
(EmuConfig.Achievements.LBSubmitSoundName.empty()
? Path::Combine(EmuFolders::Resources, DEFAULT_LBSUBMIT_SOUND_NAME)
: EmuConfig.Achievements.LBSubmitSoundName).c_str()
);
Achievements::PlayAchievementSound(EmuConfig.Achievements.LBSubmitSound, EmuConfig.Achievements.LBSubmitSoundName, DEFAULT_LBSUBMIT_SOUND_NAME);
}
void Achievements::HandleLeaderboardScoreboardEvent(const rc_client_event_t* event)
@@ -2032,6 +2030,9 @@ void Achievements::ClearUIState()
rc_client_destroy_achievement_list(s_achievement_list);
s_achievement_list = nullptr;
}
s_achievement_buckets_collapsed.clear();
s_leaderboard_buckets_collapsed.clear();
}
template <typename T>
@@ -2419,6 +2420,9 @@ bool Achievements::PrepareAchievementsWindow()
CloseSubset();
s_subset_list = rc_client_create_subset_list(s_client);
if (!s_subset_list)
Console.Warning("Achievements: rc_client_create_subset_list() returned null");
s_achievement_buckets_collapsed.clear();
if (s_achievement_list)
rc_client_destroy_achievement_list(s_achievement_list);
@@ -2433,6 +2437,131 @@ bool Achievements::PrepareAchievementsWindow()
return true;
}
bool Achievements::DrawSubsetSidebar(float sidebar_width, bool& sidebar_has_focus, bool leaderboards_only)
{
if (!s_subset_list)
return false;
using ImGuiFullscreen::g_medium_font;
using ImGuiFullscreen::LayoutScale;
static constexpr float alpha = 0.8f;
const ImVec4 sidebar_bg(0.10f, 0.10f, 0.10f, alpha);
ImGui::PushStyleColor(ImGuiCol_ChildBg, sidebar_bg);
ImGui::PushStyleColor(ImGuiCol_NavCursor, IM_COL32(0, 0, 0, 0));
bool subset_selected = false;
if (ImGui::BeginChild("subset_sidebar", ImVec2(sidebar_width, 0.0f), 0, 0))
{
sidebar_has_focus = ImGui::IsWindowFocused();
ImGuiFullscreen::BeginMenuButtons();
const float padding = LayoutScale(10.0f);
const float spacing = LayoutScale(8.0f);
const float icon_size = LayoutScale(28.0f);
for (u32 i = 0; i < s_subset_list->num_subsets; i++)
{
const rc_client_subset_t* subset = s_subset_list->subsets[i];
if (leaderboards_only && subset->num_leaderboards == 0)
continue;
TinyString id_str;
id_str.format("sidebar_subset_{}", subset->id);
ImRect bb;
bool visible, hovered;
ImGuiFullscreen::MenuButtonFrame(id_str, true, ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY,
&visible, &hovered, &bb.Min, &bb.Max, 0, 1.0f);
if (s_open_subset && s_open_subset->id == subset->id)
{
ImGui::GetWindowDrawList()->AddRectFilled(
ImVec2(bb.Min.x, bb.Min.y), ImVec2(bb.Min.x + LayoutScale(4.0f), bb.Max.y), IM_COL32(52, 152, 219, 255));
}
const ImVec2 icon_min(bb.Min.x + padding, bb.Min.y + (bb.Max.y - bb.Min.y - icon_size) * 0.5f);
const ImVec2 icon_max(icon_min.x + icon_size, icon_min.y + icon_size);
std::string badge_path = GetSubsetBadgePath(subset);
if (!badge_path.empty())
{
GSTexture* badge = ImGuiFullscreen::GetCachedTextureAsync(badge_path);
if (badge)
{
ImGui::GetWindowDrawList()->AddImage(reinterpret_cast<ImTextureID>(badge->GetNativeHandle()),
icon_min, icon_max, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
}
}
const ImRect text_bb(ImVec2(icon_max.x + spacing, bb.Min.y), ImVec2(bb.Max.x - padding, bb.Max.y));
ImGui::PushFont(g_medium_font.first, g_medium_font.second);
ImGuiFullscreen::RenderTextClippedWithShadow(
text_bb.Min, text_bb.Max, subset->title, nullptr, nullptr, ImVec2(0.0f, 0.5f), &text_bb);
ImGui::PopFont();
if (ImGui::IsItemClicked() || ImGui::IsItemActivated())
{
OpenSubset(subset);
subset_selected = true;
break;
}
}
ImGuiFullscreen::EndMenuButtons();
}
ImGui::EndChild();
ImGui::PopStyleColor(2);
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f));
ImGui::SameLine();
ImGui::PopStyleVar();
return subset_selected;
}
void Achievements::DrawSubsetSidebarFooter(bool sidebar_has_focus)
{
if (ImGuiFullscreen::IsGamepadInputSource())
{
const bool circleOK = ImGui::GetIO().ConfigNavSwapGamepadButtons;
const auto glyphs = ImGuiFullscreen::GetGamepadGlyphs();
if (sidebar_has_focus)
{
ImGuiFullscreen::SetFullscreenFooterText(std::array{
std::make_pair(glyphs.dpad_ud, TRANSLATE_SV("Achievements", "Navigate Subsets")),
std::make_pair(glyphs.confirm(circleOK), TRANSLATE_SV("Achievements", "Select")),
std::make_pair(glyphs.cancel(circleOK), TRANSLATE_SV("Achievements", "Back")),
});
}
else
{
ImGuiFullscreen::SetFullscreenFooterText(std::array{
std::make_pair(glyphs.dpad_ud, TRANSLATE_SV("Achievements", "Change Selection")),
std::make_pair(glyphs.cancel(circleOK), TRANSLATE_SV("Achievements", "Subsets")),
});
}
}
else
{
if (sidebar_has_focus)
{
ImGuiFullscreen::SetFullscreenFooterText(std::array{
std::make_pair(ICON_PF_ARROW_UP ICON_PF_ARROW_DOWN, TRANSLATE_SV("Achievements", "Navigate Subsets")),
std::make_pair(ICON_PF_ENTER, TRANSLATE_SV("Achievements", "Select")),
std::make_pair(ICON_PF_ESC, TRANSLATE_SV("Achievements", "Back")),
});
}
else
{
ImGuiFullscreen::SetFullscreenFooterText(std::array{
std::make_pair(ICON_PF_ARROW_UP ICON_PF_ARROW_DOWN, TRANSLATE_SV("Achievements", "Change Selection")),
std::make_pair(ICON_PF_ESC, TRANSLATE_SV("Achievements", "Subsets")),
});
}
}
}
void Achievements::DrawAchievementsWindow()
{
using ImGuiFullscreen::g_large_font;
@@ -2449,7 +2578,10 @@ void Achievements::DrawAchievementsWindow()
Achievements::IdleUpdate();
const bool has_multiple_subsets = (s_subset_list && s_subset_list->num_subsets > 1);
bool s_sidebar_has_focus = false;
static bool sidebar_has_focus = false;
static bool focus_sidebar = false;
if (!has_multiple_subsets)
sidebar_has_focus = false;
static constexpr float alpha = 0.8f;
static constexpr float heading_alpha = 0.95f;
@@ -2493,11 +2625,18 @@ void Achievements::DrawAchievementsWindow()
SmallString text;
ImVec2 text_size;
const bool wants_close = ImGuiFullscreen::WantsToCloseMenu();
if (ImGuiFullscreen::FloatingButton(ICON_FA_SQUARE_XMARK, 10.0f, 10.0f, -1.0f, -1.0f, 1.0f, 0.0f, true, g_large_font) ||
ImGuiFullscreen::WantsToCloseMenu())
(wants_close && (!has_multiple_subsets || sidebar_has_focus)))
{
sidebar_has_focus = false;
FullscreenUI::ReturnToPreviousWindow();
}
else if (has_multiple_subsets && !sidebar_has_focus && wants_close)
{
sidebar_has_focus = true;
focus_sidebar = true;
}
const ImRect title_bb(ImVec2(left, top), ImVec2(right, top + GetLineHeight(g_large_font)));
text.assign(s_open_subset ? s_open_subset->title : s_game_title);
@@ -2589,75 +2728,13 @@ void Achievements::DrawAchievementsWindow()
{
if (has_multiple_subsets)
{
const ImVec4 sidebar_bg(0.10f, 0.10f, 0.10f, alpha);
ImGui::PushStyleColor(ImGuiCol_ChildBg, sidebar_bg);
if (ImGui::BeginChild("subset_sidebar", ImVec2(sidebar_width, 0.0f), ImGuiChildFlags_NavFlattened, 0))
if (focus_sidebar)
{
ImGuiFullscreen::BeginMenuButtons();
for (u32 i = 0; i < s_subset_list->num_subsets; i++)
{
const rc_client_subset_t* subset = s_subset_list->subsets[i];
TinyString id_str;
id_str.format("sidebar_subset_{}", subset->id);
ImRect bb;
bool visible, hovered;
ImGuiFullscreen::MenuButtonFrame(id_str, true, ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY,
&visible, &hovered, &bb.Min, &bb.Max, 0, 1.0f);
if (ImGui::IsItemHovered() || ImGui::IsItemFocused())
s_sidebar_has_focus = true;
const float padding = LayoutScale(10.0f);
const float spacing = LayoutScale(8.0f);
const float icon_size = LayoutScale(28.0f);
if (s_open_subset && s_open_subset->id == subset->id)
{
const float line_width = LayoutScale(4.0f);
const ImVec2 line_min(bb.Min.x, bb.Min.y);
const ImVec2 line_max(bb.Min.x + line_width, bb.Max.y);
ImGui::GetWindowDrawList()->AddRectFilled(line_min, line_max, IM_COL32(52, 152, 219, 255));
}
const ImVec2 icon_min(bb.Min.x + padding, bb.Min.y + (bb.Max.y - bb.Min.y - icon_size) * 0.5f);
const ImVec2 icon_max(icon_min.x + icon_size, icon_min.y + icon_size);
std::string badge_path = GetSubsetBadgePath(subset);
if (!badge_path.empty())
{
GSTexture* badge = ImGuiFullscreen::GetCachedTextureAsync(badge_path);
if (badge)
{
ImGui::GetWindowDrawList()->AddImage(reinterpret_cast<ImTextureID>(badge->GetNativeHandle()),
icon_min, icon_max, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
}
}
const float text_x = icon_max.x + spacing;
const ImRect text_bb(ImVec2(text_x, bb.Min.y), ImVec2(bb.Max.x - padding, bb.Max.y));
ImGui::PushFont(g_medium_font.first, g_medium_font.second);
ImGuiFullscreen::RenderTextClippedWithShadow(
text_bb.Min, text_bb.Max, subset->title, nullptr, nullptr, ImVec2(0.0f, 0.5f), &text_bb);
ImGui::PopFont();
if (ImGui::IsItemClicked() || ImGui::IsItemActivated())
OpenSubset(subset);
}
ImGuiFullscreen::EndMenuButtons();
ImGui::SetNextWindowFocus();
focus_sidebar = false;
}
ImGui::EndChild();
if (ImGui::IsItemHovered() || ImGui::IsItemFocused())
s_sidebar_has_focus = true;
ImGui::PopStyleColor();
ImGui::SameLine();
if (DrawSubsetSidebar(sidebar_width, sidebar_has_focus, false))
ImGui::SetNextWindowFocus();
}
const float content_width = has_multiple_subsets ? (display_size.x - sidebar_width) : display_size.x;
@@ -2667,12 +2744,9 @@ void Achievements::DrawAchievementsWindow()
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImGui::GetColorU32(ImGuiFullscreen::UIPrimaryColor));
ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImGui::GetColorU32(ImGuiFullscreen::UISecondaryColor));
if (ImGui::BeginChild("achievements_content", ImVec2(content_width, 0.0f), ImGuiChildFlags_NavFlattened, 0))
if (ImGui::BeginChild("achievements_content", ImVec2(content_width, 0.0f), 0, 0))
{
if (ImGui::IsWindowHovered() || ImGui::IsWindowFocused())
s_sidebar_has_focus = false;
static std::map<std::pair<u32, u32>, bool> buckets_collapsed;
static const char* bucket_names[NUM_RC_CLIENT_ACHIEVEMENT_BUCKETS] = {
TRANSLATE_NOOP("Achievements", "Unknown"),
TRANSLATE_NOOP("Achievements", "Locked"),
@@ -2701,7 +2775,7 @@ void Achievements::DrawAchievementsWindow()
pxAssert(bucket.bucket_type < NUM_RC_CLIENT_ACHIEVEMENT_BUCKETS);
bool& bucket_collapsed = buckets_collapsed[std::make_pair(bucket.subset_id, bucket.bucket_type)];
bool& bucket_collapsed = s_achievement_buckets_collapsed[std::make_pair(bucket.subset_id, bucket.bucket_type)];
const char* translated_bucket_name = Host::TranslateToCString("Achievements", bucket_names[bucket.bucket_type]);
bucket_collapsed ^=
@@ -2726,54 +2800,9 @@ void Achievements::DrawAchievementsWindow()
ImGuiFullscreen::EndFullscreenWindow();
if (has_multiple_subsets)
{
if (ImGuiFullscreen::IsGamepadInputSource())
{
const bool circleOK = ImGui::GetIO().ConfigNavSwapGamepadButtons;
const auto glyphs = ImGuiFullscreen::GetGamepadGlyphs();
if (s_sidebar_has_focus)
{
ImGuiFullscreen::SetFullscreenFooterText(std::array{
std::make_pair(glyphs.dpad_ud, TRANSLATE_SV("Achievements", "Navigate Subsets")),
std::make_pair(glyphs.dpad_lr, TRANSLATE_SV("Achievements", "Back to List")),
std::make_pair(glyphs.confirm(circleOK), TRANSLATE_SV("Achievements", "Select")),
std::make_pair(glyphs.cancel(circleOK), TRANSLATE_SV("Achievements", "Back")),
});
}
else
{
ImGuiFullscreen::SetFullscreenFooterText(std::array{
std::make_pair(glyphs.dpad_lr, TRANSLATE_SV("Achievements", "Navigate Subsets")),
std::make_pair(glyphs.dpad_ud, TRANSLATE_SV("Achievements", "Change Selection")),
std::make_pair(glyphs.cancel(circleOK), TRANSLATE_SV("Achievements", "Back")),
});
}
}
else
{
if (s_sidebar_has_focus)
{
ImGuiFullscreen::SetFullscreenFooterText(std::array{
std::make_pair(ICON_PF_ARROW_UP ICON_PF_ARROW_DOWN, TRANSLATE_SV("Achievements", "Navigate Subsets")),
std::make_pair(ICON_PF_ARROW_LEFT ICON_PF_ARROW_RIGHT, TRANSLATE_SV("Achievements", "Back to List")),
std::make_pair(ICON_PF_ENTER, TRANSLATE_SV("Achievements", "Select")),
std::make_pair(ICON_PF_ESC, TRANSLATE_SV("Achievements", "Back")),
});
}
else
{
ImGuiFullscreen::SetFullscreenFooterText(std::array{
std::make_pair(ICON_PF_ARROW_LEFT ICON_PF_ARROW_RIGHT, TRANSLATE_SV("Achievements", "Navigate Subsets")),
std::make_pair(ICON_PF_ARROW_UP ICON_PF_ARROW_DOWN, TRANSLATE_SV("Achievements", "Change Selection")),
std::make_pair(ICON_PF_ESC, TRANSLATE_SV("Achievements", "Back")),
});
}
}
}
DrawSubsetSidebarFooter(sidebar_has_focus);
else
{
FullscreenUI::SetStandardSelectionFooterText(true);
}
}
void Achievements::DrawAchievement(const rc_client_achievement_t* cheevo)
@@ -2989,9 +3018,21 @@ bool Achievements::PrepareLeaderboardsWindow()
s_achievement_badge_paths = {};
CloseLeaderboard();
if (s_subset_list)
{
rc_client_destroy_subset_list(s_subset_list);
s_subset_list = nullptr;
}
CloseSubset();
s_subset_list = rc_client_create_subset_list(client);
if (!s_subset_list)
Console.Warning("Achievements: rc_client_create_subset_list() returned null");
s_leaderboard_buckets_collapsed.clear();
if (s_leaderboard_list)
rc_client_destroy_leaderboard_list(s_leaderboard_list);
s_leaderboard_list = rc_client_create_leaderboard_list(client, RC_CLIENT_LEADERBOARD_LIST_GROUPING_NONE);
s_leaderboard_list = rc_client_create_leaderboard_list(client, RC_CLIENT_LEADERBOARD_LIST_GROUPING_TRACKING);
if (!s_leaderboard_list)
{
Console.Error("Achievements: rc_client_create_leaderboard_list() returned null");
@@ -3004,6 +3045,18 @@ bool Achievements::PrepareLeaderboardsWindow()
s_restore_leaderboard_scroll = false;
s_leaderboard_list_scroll = 0.0f;
if (s_subset_list)
{
for (u32 i = 0; i < s_subset_list->num_subsets; i++)
{
if (s_subset_list->subsets[i]->num_leaderboards > 0)
{
OpenSubset(s_subset_list->subsets[i]);
break;
}
}
}
return true;
}
@@ -3027,6 +3080,14 @@ void Achievements::DrawLeaderboardsWindow()
const bool is_leaderboard_open = (s_open_leaderboard != nullptr);
bool close_leaderboard_on_exit = false;
const bool has_multiple_subsets = (s_subset_list && std::count_if(s_subset_list->subsets,
s_subset_list->subsets + s_subset_list->num_subsets,
[](const rc_client_subset_t* subset) { return subset->num_leaderboards > 0; }) > 1);
static bool sidebar_has_focus = false;
static bool focus_sidebar = false;
if (!has_multiple_subsets || is_leaderboard_open)
sidebar_has_focus = false;
ImRect bb;
const ImVec4 background(0.13f, 0.13f, 0.13f, alpha);
@@ -3089,11 +3150,18 @@ void Achievements::DrawLeaderboardsWindow()
if (!is_leaderboard_open)
{
const bool wants_close = ImGuiFullscreen::WantsToCloseMenu();
if (ImGuiFullscreen::FloatingButton(ICON_FA_SQUARE_XMARK, 10.0f, 10.0f, -1.0f, -1.0f, 1.0f, 0.0f, true, g_large_font) ||
ImGuiFullscreen::WantsToCloseMenu())
(wants_close && (!has_multiple_subsets || sidebar_has_focus)))
{
sidebar_has_focus = false;
FullscreenUI::ReturnToPreviousWindow();
}
else if (has_multiple_subsets && !sidebar_has_focus && wants_close)
{
sidebar_has_focus = true;
focus_sidebar = true;
}
}
else
{
@@ -3250,36 +3318,105 @@ void Achievements::DrawLeaderboardsWindow()
if (!is_leaderboard_open)
{
const float sidebar_width = has_multiple_subsets ? LayoutScale(200.0f) : 0.0f;
if (ImGuiFullscreen::BeginFullscreenWindow(
ImVec2(0.0f, heading_height),
ImVec2(display_size.x, display_size.y - heading_height - LayoutScale(ImGuiFullscreen::LAYOUT_FOOTER_HEIGHT)),
"leaderboards", background, 0.0f, ImVec2(ImGuiFullscreen::LAYOUT_MENU_WINDOW_X_PADDING, 0.0f), 0))
"leaderboards", background, 0.0f, ImVec2(0.0f, 0.0f), 0))
{
if (s_restore_leaderboard_scroll)
if (has_multiple_subsets)
{
ImGui::SetScrollY(s_leaderboard_list_scroll);
s_restore_leaderboard_scroll = false;
if (focus_sidebar)
{
ImGui::SetNextWindowFocus();
focus_sidebar = false;
}
if (DrawSubsetSidebar(sidebar_width, sidebar_has_focus, true))
ImGui::SetNextWindowFocus();
}
const bool had_pending_focus_request = s_restore_leaderboard_focus;
const float content_width = has_multiple_subsets ? (display_size.x - sidebar_width) : display_size.x;
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(ImGuiFullscreen::LAYOUT_MENU_WINDOW_X_PADDING, 0.0f));
ImGui::PushStyleColor(ImGuiCol_ChildBg, background);
ImGui::PushStyleColor(ImGuiCol_Header, ImGui::GetColorU32(ImGuiFullscreen::UIPrimaryDarkColor));
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImGui::GetColorU32(ImGuiFullscreen::UIPrimaryColor));
ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImGui::GetColorU32(ImGuiFullscreen::UISecondaryColor));
ImGuiFullscreen::BeginMenuButtons();
for (u32 bucket_index = 0; bucket_index < s_leaderboard_list->num_buckets; bucket_index++)
if (ImGui::BeginChild("leaderboards_content", ImVec2(content_width, 0.0f), 0, 0))
{
const rc_client_leaderboard_bucket_t& bucket = s_leaderboard_list->buckets[bucket_index];
for (u32 i = 0; i < bucket.num_leaderboards; i++)
DrawLeaderboardListEntry(bucket.leaderboards[i]);
if (s_restore_leaderboard_scroll)
{
ImGui::SetScrollY(s_leaderboard_list_scroll);
s_restore_leaderboard_scroll = false;
}
const bool had_pending_focus_request = s_restore_leaderboard_focus;
ImGuiFullscreen::BeginMenuButtons();
static const char* lboard_bucket_names[NUM_RC_CLIENT_LEADERBOARD_BUCKETS] = {
TRANSLATE_NOOP("Achievements", "Unknown"),
TRANSLATE_NOOP("Achievements", "Inactive"),
TRANSLATE_NOOP("Achievements", "Active"),
TRANSLATE_NOOP("Achievements", "Unsupported"),
TRANSLATE_NOOP("Achievements", "All"),
};
const bool show_bucket_headers = (s_leaderboard_list->num_buckets > 1);
for (u32 bucket_index = 0; bucket_index < s_leaderboard_list->num_buckets; bucket_index++)
{
const rc_client_leaderboard_bucket_t& bucket = s_leaderboard_list->buckets[bucket_index];
if (s_open_subset && bucket.subset_id != 0 && bucket.subset_id != s_open_subset->id)
continue;
if (show_bucket_headers)
{
pxAssert(bucket.bucket_type < NUM_RC_CLIENT_LEADERBOARD_BUCKETS);
const char* type_name = Host::TranslateToCString("Achievements", lboard_bucket_names[bucket.bucket_type]);
TinyString bucket_label;
if (bucket.subset_id != 0 && !s_open_subset && s_subset_list)
{
auto subset_iter = std::find_if(s_subset_list->subsets, s_subset_list->subsets + s_subset_list->num_subsets,
[&](const rc_client_subset_t* s) { return s->id == bucket.subset_id; });
if (subset_iter != s_subset_list->subsets + s_subset_list->num_subsets)
bucket_label.format("{} - {}", (*subset_iter)->title, type_name);
else
bucket_label.assign(type_name);
}
else
{
bucket_label.assign(type_name);
}
bool& bucket_collapsed = s_leaderboard_buckets_collapsed[std::make_pair(bucket.subset_id, bucket.bucket_type)];
if (ImGuiFullscreen::MenuHeadingButton(
bucket_label, bucket_collapsed ? ICON_FA_CHEVRON_DOWN : ICON_FA_CHEVRON_UP))
bucket_collapsed = !bucket_collapsed;
if (bucket_collapsed)
continue;
}
for (u32 i = 0; i < bucket.num_leaderboards; i++)
DrawLeaderboardListEntry(bucket.leaderboards[i]);
}
ImGuiFullscreen::EndMenuButtons();
if (had_pending_focus_request && s_restore_leaderboard_focus)
s_restore_leaderboard_focus = false;
}
ImGui::EndChild();
ImGuiFullscreen::EndMenuButtons();
if (had_pending_focus_request && s_restore_leaderboard_focus)
s_restore_leaderboard_focus = false;
ImGui::PopStyleColor(4);
ImGui::PopStyleVar();
}
ImGuiFullscreen::EndFullscreenWindow();
FullscreenUI::SetStandardSelectionFooterText(true);
if (has_multiple_subsets)
DrawSubsetSidebarFooter(sidebar_has_focus);
else
FullscreenUI::SetStandardSelectionFooterText(true);
}
else
{
+3
View File
@@ -67,6 +67,9 @@ namespace Achievements
/// Called when the system changes game, or is booting.
void GameChanged(u32 disc_crc, u32 crc);
/// Play achievement related sounds effects
void PlayAchievementSound(bool is_specific_sound_enabled, const std::string& custom_sound_name, const std::string& default_sound_name);
/// Re-enables hardcode mode if it is enabled in the settings.
bool ResetHardcoreMode(bool is_booting);
+1 -1
View File
@@ -1338,7 +1338,7 @@ function(setup_main_executable target)
endforeach()
get_target_property(WINDEPLOYQT_EXE Qt6::windeployqt IMPORTED_LOCATION)
install(CODE "execute_process(COMMAND \"${WINDEPLOYQT_EXE}\" \"${CMAKE_SOURCE_DIR}/bin/$<TARGET_FILE_NAME:${target}>\" --plugindir \"${CMAKE_SOURCE_DIR}/bin/QtPlugins\" --pdb --no-compiler-runtime --no-system-d3d-compiler --no-system-dxc-compiler --no-translations COMMAND_ERROR_IS_FATAL ANY)")
install(CODE "execute_process(COMMAND \"${WINDEPLOYQT_EXE}\" \"${CMAKE_SOURCE_DIR}/bin/$<TARGET_FILE_NAME:${target}>\" --plugindir \"${CMAKE_SOURCE_DIR}/bin/QtPlugins\" --pdb --no-compiler-runtime --no-system-d3d-compiler --no-system-dxc-compiler --no-translations --no-network COMMAND_ERROR_IS_FATAL ANY)")
install(CODE "file(WRITE \"${CMAKE_SOURCE_DIR}/bin/qt.conf\" \"[Paths]\\nPlugins = ./QtPlugins\")")
endif()
+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"
+7 -3
View File
@@ -384,9 +384,8 @@ enum class GSDumpCompressionMethod : u8
enum class SavestateCompressionMethod : u8
{
Uncompressed = 0,
Deflate64 = 1,
Zstandard = 2,
LZMA2 = 3
Deflate = 1,
Zstandard = 2
};
enum class SavestateCompressionLevel : u8
@@ -400,6 +399,7 @@ enum class SavestateCompressionLevel : u8
enum class GSHardwareDownloadMode : u8
{
Enabled,
EnabledForceFull,
NoReadbacks,
Unsynchronized,
Disabled
@@ -774,6 +774,7 @@ struct Pcsx2Config
OsdShowCPU : 1,
OsdShowGPU : 1,
OsdShowGPUDebug : 1,
OsdShowGPUStats : 1,
OsdShowIndicators : 1,
OsdShowFrameTimes : 1,
OsdShowHardwareInfo : 1,
@@ -794,6 +795,9 @@ struct Pcsx2Config
HWMipmap : 1,
HWAccurateAlphaTest : 1,
HWAA1 : 1,
HWROV : 1,
HWROVLogging : 1,
HWROVBarriersVK : 1,
ManualUserHacks : 1,
UserHacks_AlignSpriteX : 1,
UserHacks_CPUFBConversion : 1,
+2
View File
@@ -183,6 +183,8 @@ The clamp modes are also numerically based.
* recommendedBlendingLevel [`0` or `1` or `2` or `3` or `4` or `5`] {Minimum, Basic, Medium, High, Full(Slow), Maximum (Very Slow)} Default: Automatic (No value, looks up GameDB)
* readTCOnClose [`0` or `1`] {Off, On} Default: Off (`0`) // Tab 3 Hardware Fixes (4th checkbox on right row 2)
* limit24BitDepth [`0` or `1`or `2`] {Disabled, Prioritise Upper Bits, Prioritise Lower Bits} Default: Off (`0`)
* recommendedAccurateAlphaTest [`0` or `1`] {Disabled, Advise to use AAT} Default: Off (`0`)
* recommendedHWAA1 [`0` or `1`] {Disabled, Advise to use AA1} Default: Off (`0`)
### GS Hardware Upscaling Fixes
+10
View File
@@ -229,6 +229,16 @@
"minimum": 0,
"maximum": 1
},
"recommendedAccurateAlphaTest": {
"type": "integer",
"minimum": 0,
"maximum": 1
},
"recommendedHWAA1": {
"type": "integer",
"minimum": 0,
"maximum": 1
},
"trilinearFiltering": {
"type": "integer",
"minimum": 0,
+42 -13
View File
@@ -161,7 +161,10 @@ static bool OpenGSDevice(GSRendererType renderer, bool clear_state_on_fail, bool
return false;
}
GSConfig.OsdShowGPU = GSConfig.OsdShowGPU && g_gs_device->SetGPUTimingEnabled(true);
if (!g_gs_device->SetGPUTimingEnabled(true))
GSConfig.OsdShowGPU = false;
if (!g_gs_device->SetGPUPipelineStatisticsEnabled(true))
GSConfig.OsdShowGPUStats = false;
Console.WriteLn(Color_StrongGreen, "%s Graphics Driver Info:", GSDevice::RenderAPIToString(new_api));
Console.WriteLn(g_gs_device->GetDriverInfo());
@@ -701,16 +704,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::TextureCopiesROV)),
(int)std::ceil(pm.Get(GSPerfMon::TextureUploads)));
}
}
}
@@ -858,11 +881,17 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
g_gs_renderer->PurgeTextureCache(true, false, true);
}
if (GSConfig.OsdShowGPU != old_config.OsdShowGPU)
if (GSConfig.OsdShowGPU && !old_config.OsdShowGPU)
{
if (!g_gs_device->SetGPUTimingEnabled(GSConfig.OsdShowGPU))
if (!g_gs_device->SetGPUTimingEnabled(true))
GSConfig.OsdShowGPU = false;
}
if (GSConfig.OsdShowGPUStats != old_config.OsdShowGPUStats)
{
if (!g_gs_device->SetGPUPipelineStatisticsEnabled(GSConfig.OsdShowGPUStats))
GSConfig.OsdShowGPUStats = false;
}
}
void GSSetSoftwareRendering(bool software_renderer, GSInterlaceMode new_interlace)
+1 -1
View File
@@ -447,7 +447,7 @@ void GSClut::Read32(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA)
if (!dst)
{
// allocate texture lazily
dst = g_gs_device->CreateRenderTarget(dst_size, 1, GSTexture::Format::Color, false);
dst = g_gs_device->CreateFeedbackTarget(dst_size, 1, GSTexture::Format::Color, false);
is_4bit ? (m_gpu_clut4 = dst) : (m_gpu_clut8 = dst);
m_gpu_clut_dirty = true;
}
+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,
TextureCopiesROV, // 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,
+76 -52
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;
}
}
@@ -385,7 +389,7 @@ void GSState::ResetDrawBuffers()
// exclude_current is used if there is a flush for a reason other than the normal context change.
void GSState::FlushBuffers(bool flush_base_only, bool use_flush_reason, GSFlushReason flush_reason)
{
const u32 current_idx = m_current_buffer_idx;
const int current_idx = m_current_buffer_idx;
bool restore_env = false;
if (m_used_buffers_idx > 0)
@@ -398,8 +402,7 @@ void GSState::FlushBuffers(bool flush_base_only, bool use_flush_reason, GSFlushR
else if (m_index_buffers[0].tail == 0)
return;
int max_flushes = flush_base_only ? 1 : m_used_buffers_idx;
//DevCon.Warning("Flushing %d draw buffers from draw %d", m_used_buffers_idx, s_n);
const int max_flushes = flush_base_only ? 1 : m_used_buffers_idx;
for (int i = 0; i < max_flushes; i++)
{
m_current_buffer_idx = i;
@@ -431,7 +434,7 @@ void GSState::FlushBuffers(bool flush_base_only, bool use_flush_reason, GSFlushR
}
else if (restore_env)
memcpy(&m_env, &m_temp_env, sizeof(m_env));
//DevCon.Warning("Flushing position %d ABE is %d TME %d TEX0 TBP %x", i, m_prev_env.PRIM.ABE, m_prev_env.PRIM.TME, m_prev_env.CTXT[m_prev_env.PRIM.CTXT].TEX0.TBP0);
if (use_flush_reason && (i == current_idx || flush_reason == VSYNC))
FlushDraw(flush_reason);
else
@@ -443,7 +446,6 @@ void GSState::FlushBuffers(bool flush_base_only, bool use_flush_reason, GSFlushR
m_current_buffer_idx = current_idx;
m_index = &m_index_buffers[m_current_buffer_idx];
m_vertex = &m_vertex_buffers[m_current_buffer_idx];
m_dirty_gs_regs = 0;
const int ctx = m_env_buffers[m_current_buffer_idx].m_backed_up_ctx;
std::memcpy(&m_prev_env, &m_env_buffers[m_current_buffer_idx].m_env, 88);
@@ -451,8 +453,6 @@ void GSState::FlushBuffers(bool flush_base_only, bool use_flush_reason, GSFlushR
std::memcpy(&m_prev_env.CTXT[1], &m_env_buffers[m_current_buffer_idx].m_env.CTXT[1], 96);
std::memcpy(&m_prev_env.CTXT[ctx].offset, &m_env_buffers[m_current_buffer_idx].m_env.CTXT[ctx].offset, sizeof(m_env_buffers[m_current_buffer_idx].m_env.CTXT[ctx].offset));
std::memcpy(&m_prev_env.CTXT[ctx].scissor, &m_env_buffers[m_current_buffer_idx].m_env.CTXT[ctx].scissor, sizeof(m_env_buffers[m_current_buffer_idx].m_env.CTXT[ctx].scissor));
//DevCon.Warning("Flush complete, draw now %d", s_n);
//UpdateContext();
}
void GSState::PushBuffer()
@@ -460,7 +460,6 @@ void GSState::PushBuffer()
// Just in case it tries to overflow.
if (m_used_buffers_idx >= MAX_DRAW_BUFFERS)
{
//DevCon.Warning("Attempted to add a draw to buffer when full. Flushing");
FlushBuffers(false, false);
ResetDrawBufferIdx();
return;
@@ -487,15 +486,11 @@ void GSState::PushBuffer()
{
for (u32 i = 0; i < copy_amt; i++)
{
GSVector4i* RESTRICT vert_ptr = (GSVector4i*)&m_vertex->buff[m_vertex->head + i];
GSVector4i v = vert_ptr[1];
v = v.xxxx().u16to32().sub32(m_xyof);
v = v.blend32<12>(v.sra32<4>());
m_vertex->xy[i & 3] = v;
m_vertex->xy_tail = std::min(copy_amt, 2U);
m_vertex->xy[i & 3] = m_vertex_buffers[m_current_buffer_idx].xy[((m_vertex_buffers[m_current_buffer_idx].xy_tail - copy_amt) + i) & 3];
m_vertex->xy_tail++;
if (i == 0)
m_vertex->xyhead = v;
m_vertex->xyhead = m_vertex_buffers[m_current_buffer_idx].xyhead;
}
}
else
@@ -506,7 +501,6 @@ void GSState::PushBuffer()
m_dirty_gs_regs = 0;
m_used_buffers_idx++;
m_recent_buffer_switch = true;
//DevCon.Warning("Pushing new buffer %d vertex tail %d index tail %d TME %d TBP0 0x%x draw %d", m_current_buffer_idx, m_vertex->tail, m_index->tail, m_env.PRIM.TME, m_env.CTXT[m_env.PRIM.CTXT].TEX0.TBP0, s_n);
}
}
@@ -520,10 +514,11 @@ bool GSState::CanBufferNewDraw()
// If the base draw isn't writing to the Z buffer, but following draws do, we can't use it.
// Also the base draw needs to be solid, not an alpha blend.
if (base_context.ZBUF.ZMSK || cur_context.FRAME.FBP != base_context.FRAME.FBP || cur_context.ZBUF.ZBP != base_context.ZBUF.ZBP || (m_env_buffers[0].m_env.PRIM.TME && base_context.TEX0.TFX > TFX_DECAL) ||
if (base_context.ZBUF.ZMSK || cur_context.FRAME.FBP != base_context.FRAME.FBP || cur_context.ZBUF.ZBP != base_context.ZBUF.ZBP || (m_env_buffers[0].m_env.PRIM.TME &&
(base_context.TEX0.TFX > TFX_DECAL || (m_env_buffers[0].m_env.PRIM.ABE && !base_context.TEX0.TCC && m_v.RGBAQ.A != 128))) ||
((base_context.TEST.ATE && base_context.TEST.ATST > ATST_ALWAYS && base_context.TEST.AREF != 0) && (base_context.TEST.AFAIL & AFAIL_FB_ONLY) == AFAIL_KEEP))
{
//DevCon.Warning("Flushing, cannot buffer draw due to incompatible base");
// Incompatible base.
return false;
}
@@ -542,6 +537,8 @@ bool GSState::CanBufferNewDraw()
if (!std::memcmp(&m_env_buffers[i].m_env, &m_env, 88))
{
GSDrawingEnvironment& buffered_ctx = m_env_buffers[i].m_env;
if (m_index_buffers[i].tail > m_index_buffers[0].tail)
return false;
if (buffered_ctx.CTXT[ctx].SCISSOR.U64 ^ cur_context.SCISSOR.U64)
continue;
@@ -565,6 +562,9 @@ bool GSState::CanBufferNewDraw()
continue;
if (buffered_ctx.CTXT[ctx].TEX1.U32[0] ^ cur_context.TEX1.U32[0])
continue;
if (buffered_ctx.CTXT[ctx].TEX1.MXL != cur_context.TEX1.MXL)
continue;
if (cur_context.TEX1.MXL)
{
if (buffered_ctx.CTXT[ctx].TEX1.U32[1] ^ cur_context.TEX1.U32[1])
@@ -600,27 +600,32 @@ bool GSState::CanBufferNewDraw()
if (i == 1 && !m_env_buffers[i].draw_rect.eq(m_env_buffers[0].draw_rect))
{
FlushWrite();
FlushBuffers(true, false);
const bool base_only = m_env_buffers[i].draw_rect.rintersect(m_env_buffers[0].draw_rect).rempty();
FlushBuffers(base_only, false);
ResetDrawBufferIdx();
i = -1;
continue;
if (!base_only)
break;
else
{
i = -1;
continue;
}
}
else
return false;
}
/*if (i != (m_current_buffer_idx + 1) && i != 0)
return false;*/
// We found a matching draw
//DevCon.Warning("Matching buffered draw detected in index %d, using", i);
m_index = &m_index_buffers[i];
m_vertex = &m_vertex_buffers[i];
const u32 copy_amt = m_vertex_buffers[m_current_buffer_idx].tail - m_vertex_buffers[m_current_buffer_idx].head;
m_recent_buffer_switch = m_vertex->tail == m_vertex->head;
m_vertex->tail = m_index->buff[m_index->tail - 1] + 1;
if (m_index->tail)
m_vertex->tail = m_index->buff[m_index->tail - 1] + 1;
else
m_vertex->tail = 0;
if (copy_amt)
memcpy(&m_vertex->buff[m_vertex->tail], &m_vertex_buffers[m_current_buffer_idx].buff[m_vertex_buffers[m_current_buffer_idx].head], sizeof(GSVertex) * copy_amt);
@@ -641,18 +646,13 @@ bool GSState::CanBufferNewDraw()
if (copy_amt)
{
for (u32 j = 0; j < copy_amt; j++)
for (u32 i = 0; i < copy_amt; i++)
{
GSVector4i* RESTRICT vert_ptr = (GSVector4i*)&m_vertex->buff[m_vertex->head + j];
GSVector4i v = vert_ptr[1];
v = v.xxxx().u16to32().sub32(m_xyof);
v = v.blend32<12>(v.sra32<4>());
m_vertex->xy[j & 3] = v;
m_vertex->xy[m_vertex->xy_tail & 3] = m_vertex_buffers[m_current_buffer_idx].xy[((m_vertex_buffers[m_current_buffer_idx].xy_tail - copy_amt) + i) & 3];
m_vertex->xy_tail++;
if (j == 0)
m_vertex->xyhead = v;
m_vertex->xy_tail = copy_amt;
if (i == 0)
m_vertex->xyhead = m_vertex_buffers[m_current_buffer_idx].xyhead;
}
}
else
@@ -663,7 +663,7 @@ bool GSState::CanBufferNewDraw()
}
m_dirty_gs_regs = 0;
//DevCon.Warning("Picking buffer %d vertex tail %d index tail %d TME %d TBP0 0x%x dirty %x draw %d", m_current_buffer_idx, m_vertex->tail, m_index->tail, m_env.PRIM.TME, m_env.CTXT[m_env.PRIM.CTXT].TEX0.TBP0, m_dirty_gs_regs, s_n);
return true;
}
}
@@ -688,7 +688,6 @@ bool GSState::CanBufferNewDraw()
return false;
PushBuffer();
//DevCon.Warning("Buffering new draw! now buffering %d", m_used_buffers_idx);
return true;
}
@@ -4234,7 +4233,7 @@ bool GSState::TrianglesAreQuads(bool shuffle_check)
}
template<u32 primclass>
GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlist, bool save_bbox, float bbox_scale)
GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlist, bool save_bbox, float bbox_scale, u32* max_size)
{
const GSVector4i xyof = m_context->scissor.xyof.xyxy();
@@ -4282,7 +4281,14 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlis
if (primclass == GS_TRIANGLE_CLASS && m_quad_check_valid && m_are_quads && !using_aa1)
{
// The triangles-are-quads check already ensures that there is no overlap.
m_drawlist.push_back(m_index->tail / n);
if (save_drawlist)
{
m_drawlist.push_back(m_index->tail / n);
}
else if (max_size)
{
*max_size = 1;
}
if (save_bbox)
{
const GSVector4i draw_area = GSVector4i(m_vt.m_min.p.upld(m_vt.m_max.p) * GSVector4(16.0f)) + xyof;
@@ -4294,6 +4300,7 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlis
PRIM_OVERLAP overlap = PRIM_OVERLAP_NO;
bool check_quads = (primclass == GS_TRIANGLE_CLASS) && !using_aa1;
u32 drawlist_size = 0;
u32 i = 0;
u32 skip = 0; // Number of indices to skip if we have the bbox from the previous iteration.
@@ -4691,6 +4698,17 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlis
{
m_drawlist.push_back((j - i) / n); // Prim count
}
else if (max_size)
{
// If the max size pointer is passed it means we just want to peek at
// the drawlist size up to the given limit to avoid unecessary work.
drawlist_size++;
if (drawlist_size >= *max_size)
{
*max_size = drawlist_size;
return drawlist_size > 0 ? PRIM_OVERLAP_YES : PRIM_OVERLAP_UNKNOW;
}
}
else if (j < count)
{
return PRIM_OVERLAP_YES; // Early exit if not saving drawlist.
@@ -4704,21 +4722,27 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlis
all = bbox;
i = j;
}
if (max_size)
{
*max_size = drawlist_size;
}
return overlap;
}
GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlist(bool save_drawlist, bool save_bbox, float bbox_scale)
GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlist(bool save_drawlist, bool save_bbox, float bbox_scale, u32* max_size)
{
switch (m_vt.m_primclass)
{
case GS_POINT_CLASS:
return GetPrimitiveOverlapDrawlistImpl<GS_POINT_CLASS>(save_drawlist, save_bbox, bbox_scale);
return GetPrimitiveOverlapDrawlistImpl<GS_POINT_CLASS>(save_drawlist, save_bbox, bbox_scale, max_size);
case GS_LINE_CLASS:
return GetPrimitiveOverlapDrawlistImpl<GS_LINE_CLASS>(save_drawlist, save_bbox, bbox_scale);
return GetPrimitiveOverlapDrawlistImpl<GS_LINE_CLASS>(save_drawlist, save_bbox, bbox_scale, max_size);
case GS_TRIANGLE_CLASS:
return GetPrimitiveOverlapDrawlistImpl<GS_TRIANGLE_CLASS>(save_drawlist, save_bbox, bbox_scale);
return GetPrimitiveOverlapDrawlistImpl<GS_TRIANGLE_CLASS>(save_drawlist, save_bbox, bbox_scale, max_size);
case GS_SPRITE_CLASS:
return GetPrimitiveOverlapDrawlistImpl<GS_SPRITE_CLASS>(save_drawlist, save_bbox, bbox_scale);
return GetPrimitiveOverlapDrawlistImpl<GS_SPRITE_CLASS>(save_drawlist, save_bbox, bbox_scale, max_size);
default:
pxFail("Invalid primclass."); // Impossible.
return PRIM_OVERLAP_UNKNOW;
@@ -5753,10 +5777,10 @@ bool GSState::CheckOverlapVerts(u32 n)
if (m_index->tail > 0)
{
int matching_verts = 0;
u32 matching_verts = 0;
for (u32 i = 0; i < n; i++)
{
const int pos = m_index->buff[(m_index->tail - n) + i];
const u32 pos = m_index->buff[(m_index->tail - n) + i];
const GSVector2i prev_vert = GSVector2i(v[pos].XYZ.X - m_context->XYOFFSET.OFX, v[pos].XYZ.Y - m_context->XYOFFSET.OFY);
for (u32 j = 0; j < n; j++)
@@ -6081,7 +6105,7 @@ __forceinline void GSState::VertexKick(u32 skip)
// Update rectangle for the current draw. Needs exclusive endpoints.
const GSVector4i draw_rect = bbox.sra32<4>() + GSVector4i(0, 0, 1, 1);
if (m_vertex->tail != n)
if (m_index->tail != n)
temp_draw_rect = temp_draw_rect.runion(draw_rect);
else
temp_draw_rect = draw_rect;
+4 -2
View File
@@ -532,8 +532,10 @@ public:
bool TrianglesAreQuadsImpl();
bool TrianglesAreQuads(bool shuffle_check = false);
template <u32 primclass>
PRIM_OVERLAP GetPrimitiveOverlapDrawlistImpl(bool save_drawlist = false, bool save_bbox = false, float bbox_scale = 1.0f);
PRIM_OVERLAP GetPrimitiveOverlapDrawlist(bool save_drawlist = false, bool save_bbox = false, float bbox_scale = 1.0f);
PRIM_OVERLAP GetPrimitiveOverlapDrawlistImpl(bool save_drawlist = false, bool save_bbox = false,
float bbox_scale = 1.0f, u32* max_size = nullptr);
PRIM_OVERLAP GetPrimitiveOverlapDrawlist(bool save_drawlist = false, bool save_bbox = false,
float bbox_scale = 1.0f, u32* max_size = nullptr);
PRIM_OVERLAP PrimitiveOverlap(bool save_drawlist = false);
bool SpriteDrawWithoutGaps();
void CalculatePrimitiveCoversWithoutGaps();
+359 -172
View File
@@ -22,29 +22,13 @@
#include <ostream>
#include <fstream>
int SetDATMShader(SetDATM datm)
{
switch (datm)
{
case SetDATM::DATM1_RTA_CORRECTION:
return static_cast<int>(ShaderConvert::DATM_1_RTA_CORRECTION);
case SetDATM::DATM0_RTA_CORRECTION:
return static_cast<int>(ShaderConvert::DATM_0_RTA_CORRECTION);
case SetDATM::DATM1:
return static_cast<int>(ShaderConvert::DATM_1);
case SetDATM::DATM0:
default:
return static_cast<int>(ShaderConvert::DATM_0);
}
}
const char* shaderName(ShaderConvert value)
const char* ShaderEntryPoint(ShaderConvert value)
{
switch (value)
{
// clang-format off
// clang-format off
case ShaderConvert::COPY: return "ps_copy";
case ShaderConvert::RGBA8_TO_16_BITS: return "ps_convert_rgba8_16bits";
case ShaderConvert::RGB5A1_TO_16_BITS: return "ps_convert_rgb5a1_16bits";
case ShaderConvert::DATM_1: return "ps_datm1";
case ShaderConvert::DATM_0: return "ps_datm0";
case ShaderConvert::DATM_1_RTA_CORRECTION: return "ps_datm1_rta_correction";
@@ -54,22 +38,16 @@ const char* shaderName(ShaderConvert value)
case ShaderConvert::RTA_CORRECTION: return "ps_rta_correction";
case ShaderConvert::RTA_DECORRECTION: return "ps_rta_decorrection";
case ShaderConvert::TRANSPARENCY_FILTER: return "ps_filter_transparency";
case ShaderConvert::FLOAT32_TO_16_BITS: return "ps_convert_float32_32bits";
case ShaderConvert::FLOAT32_TO_32_BITS: return "ps_convert_float32_32bits";
case ShaderConvert::FLOAT32_TO_RGBA8: return "ps_convert_float32_rgba8";
case ShaderConvert::FLOAT32_TO_RGB8: return "ps_convert_float32_rgba8";
case ShaderConvert::FLOAT16_TO_RGB5A1: return "ps_convert_float16_rgb5a1";
case ShaderConvert::RGBA8_TO_FLOAT32: return "ps_convert_rgba8_float32";
case ShaderConvert::RGBA8_TO_FLOAT24: return "ps_convert_rgba8_float24";
case ShaderConvert::RGBA8_TO_FLOAT16: return "ps_convert_rgba8_float16";
case ShaderConvert::RGB5A1_TO_FLOAT16: return "ps_convert_rgb5a1_float16";
case ShaderConvert::RGBA8_TO_FLOAT32_BILN: return "ps_convert_rgba8_float32_biln";
case ShaderConvert::RGBA8_TO_FLOAT24_BILN: return "ps_convert_rgba8_float24_biln";
case ShaderConvert::RGBA8_TO_FLOAT16_BILN: return "ps_convert_rgba8_float16_biln";
case ShaderConvert::RGB5A1_TO_FLOAT16_BILN: return "ps_convert_rgb5a1_float16_biln";
case ShaderConvert::FLOAT32_DEPTH_TO_COLOR: return "ps_convert_float32_depth_to_color";
case ShaderConvert::FLOAT32_COLOR_TO_DEPTH: return "ps_convert_float32_color_to_depth";
case ShaderConvert::FLOAT32_TO_FLOAT24: return "ps_convert_float32_float24";
case ShaderConvert::DEPTH32_TO_16_BITS: return "ps_convert_depth32_32bits";
case ShaderConvert::DEPTH32_TO_32_BITS: return "ps_convert_depth32_32bits";
case ShaderConvert::DEPTH32_TO_RGBA8: return "ps_convert_depth32_rgba8";
case ShaderConvert::DEPTH32_TO_RGB8: return "ps_convert_depth32_rgba8";
case ShaderConvert::DEPTH16_TO_RGB5A1: return "ps_convert_depth16_rgb5a1";
case ShaderConvert::RGBA8_TO_DEPTH32: return "ps_convert_rgba8_depth32";
case ShaderConvert::RGBA8_TO_DEPTH24: return "ps_convert_rgba8_depth24";
case ShaderConvert::RGBA8_TO_DEPTH16: return "ps_convert_rgba8_depth16";
case ShaderConvert::RGB5A1_TO_DEPTH16: return "ps_convert_rgb5a1_depth16";
case ShaderConvert::DEPTH32_TO_DEPTH24: return "ps_convert_depth32_depth24";
case ShaderConvert::DEPTH_COPY: return "ps_depth_copy";
case ShaderConvert::DOWNSAMPLE_COPY: return "ps_downsample_copy";
case ShaderConvert::RGBA_TO_8I: return "ps_convert_rgba_8i";
@@ -77,18 +55,18 @@ const char* shaderName(ShaderConvert value)
case ShaderConvert::CLUT_4: return "ps_convert_clut_4";
case ShaderConvert::CLUT_8: return "ps_convert_clut_8";
case ShaderConvert::YUV: return "ps_yuv";
// clang-format on
// clang-format on
default:
pxAssert(0);
return "ShaderConvertUnknownShader";
}
}
const char* shaderName(PresentShader value)
const char* ShaderEntryPoint(PresentShader value)
{
switch (value)
{
// clang-format off
// clang-format off
case PresentShader::COPY: return "ps_copy";
case PresentShader::SCANLINE: return "ps_filter_scanlines";
case PresentShader::DIAGONAL_FILTER: return "ps_filter_diagonal";
@@ -97,13 +75,53 @@ const char* shaderName(PresentShader value)
case PresentShader::LOTTES_FILTER: return "ps_filter_lottes";
case PresentShader::SUPERSAMPLE_4xRGSS: return "ps_4x_rgss";
case PresentShader::SUPERSAMPLE_AUTO: return "ps_automagical_supersampling";
// clang-format on
// clang-format on
default:
pxAssert(0);
return "DisplayShaderUnknownShader";
}
}
const char* ShaderConvertName(ShaderConvert shader)
{
#define ENTRY(x) case ShaderConvert::x: return #x
switch (shader)
{
ENTRY(COPY);
ENTRY(DEPTH_COPY);
ENTRY(RGB5A1_TO_16_BITS);
ENTRY(DATM_1);
ENTRY(DATM_0);
ENTRY(DATM_1_RTA_CORRECTION);
ENTRY(DATM_0_RTA_CORRECTION);
ENTRY(COLCLIP_INIT);
ENTRY(COLCLIP_RESOLVE);
ENTRY(RTA_CORRECTION);
ENTRY(RTA_DECORRECTION);
ENTRY(TRANSPARENCY_FILTER);
ENTRY(DEPTH32_TO_16_BITS);
ENTRY(DEPTH32_TO_32_BITS);
ENTRY(DEPTH32_TO_RGBA8);
ENTRY(DEPTH32_TO_RGB8);
ENTRY(DEPTH16_TO_RGB5A1);
ENTRY(RGBA8_TO_DEPTH32);
ENTRY(RGBA8_TO_DEPTH24);
ENTRY(RGBA8_TO_DEPTH16);
ENTRY(RGB5A1_TO_DEPTH16);
ENTRY(DEPTH32_TO_DEPTH24);
ENTRY(DOWNSAMPLE_COPY);
ENTRY(RGBA_TO_8I);
ENTRY(RGB5A1_TO_8I);
ENTRY(CLUT_4);
ENTRY(CLUT_8);
ENTRY(YUV);
case ShaderConvert::Count: break;
}
#undef ENTRY
pxAssert(false);
return nullptr;
}
#ifdef PCSX2_DEVBUILD
enum class TextureLabel
@@ -126,53 +144,59 @@ enum class TextureLabel
static std::array<u32, static_cast<u32>(TextureLabel::Last) + 1> s_texture_counts;
static TextureLabel GetTextureLabel(GSTexture::Type type, GSTexture::Format format)
static TextureLabel GetTextureLabel(GSTexture::Usage usage, GSTexture::Format format)
{
switch (type)
if (GSTexture::IsRenderTarget(usage))
{
case GSTexture::Type::RenderTarget:
switch (format)
{
case GSTexture::Format::Color:
return TextureLabel::ColorRT;
case GSTexture::Format::ColorHQ:
return TextureLabel::ColorHQRT;
case GSTexture::Format::ColorHDR:
return TextureLabel::ColorHDRRT;
case GSTexture::Format::ColorClip:
return TextureLabel::ColorClipRT;
case GSTexture::Format::UInt16:
return TextureLabel::U16RT;
case GSTexture::Format::UInt32:
return TextureLabel::U32RT;
case GSTexture::Format::PrimID:
return TextureLabel::PrimIDTexture;
default:
return TextureLabel::Other;
}
case GSTexture::Type::Texture:
switch (format)
{
case GSTexture::Format::Color:
return TextureLabel::Texture;
case GSTexture::Format::UNorm8:
return TextureLabel::CLUTTexture;
case GSTexture::Format::BC1:
case GSTexture::Format::BC2:
case GSTexture::Format::BC3:
case GSTexture::Format::BC7:
case GSTexture::Format::ColorHDR:
return TextureLabel::ReplacementTexture;
default:
return TextureLabel::Other;
}
case GSTexture::Type::DepthStencil:
return TextureLabel::DepthStencil;
case GSTexture::Type::RWTexture:
return TextureLabel::RWTexture;
case GSTexture::Type::Invalid:
default:
return TextureLabel::Other;
switch (format)
{
case GSTexture::Format::Color:
return TextureLabel::ColorRT;
case GSTexture::Format::ColorHQ:
return TextureLabel::ColorHQRT;
case GSTexture::Format::ColorHDR:
return TextureLabel::ColorHDRRT;
case GSTexture::Format::ColorClip:
return TextureLabel::ColorClipRT;
case GSTexture::Format::UInt16:
return TextureLabel::U16RT;
case GSTexture::Format::UInt32:
return TextureLabel::U32RT;
case GSTexture::Format::PrimID:
return TextureLabel::PrimIDTexture;
default:
return TextureLabel::Other;
}
}
else if (GSTexture::IsTexture(usage))
{
switch (format)
{
case GSTexture::Format::Color:
return TextureLabel::Texture;
case GSTexture::Format::UNorm8:
return TextureLabel::CLUTTexture;
case GSTexture::Format::BC1:
case GSTexture::Format::BC2:
case GSTexture::Format::BC3:
case GSTexture::Format::BC7:
case GSTexture::Format::ColorHDR:
return TextureLabel::ReplacementTexture;
default:
return TextureLabel::Other;
}
}
else if (GSTexture::IsDepthStencil(usage))
{
return TextureLabel::DepthStencil;
}
else if (GSTexture::IsShaderWrite(usage))
{
return TextureLabel::RWTexture;
}
else
{
return TextureLabel::Other;
}
}
@@ -297,7 +321,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)
@@ -576,11 +600,16 @@ void GSDevice::TextureRecycleDeleter::operator()(GSTexture* const tex)
g_gs_device->Recycle(tex);
}
GSTexture* GSDevice::FetchSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format, bool clear, bool prefer_unused_texture)
GSTexture* GSDevice::FetchSurface(GSTexture::Usage usage, const GSVector2i& size, int levels, GSTexture::Format format, bool clear, bool prefer_reuse)
{
return FetchSurface(usage, size.x, size.y, levels, format, clear, prefer_reuse);
}
GSTexture* GSDevice::FetchSurface(GSTexture::Usage usage, int width, int height, int levels, GSTexture::Format format, bool clear, bool prefer_reuse)
{
const GSVector2i size(std::clamp(width, 1, static_cast<int>(g_gs_device->GetMaxTextureSize())),
std::clamp(height, 1, static_cast<int>(g_gs_device->GetMaxTextureSize())));
FastList<GSTexture*>& pool = m_pool[type != GSTexture::Type::Texture];
FastList<GSTexture*>& pool = m_pool[!GSTexture::IsTexture(usage)];
GSTexture* t = nullptr;
auto fallback = pool.end();
@@ -591,9 +620,9 @@ GSTexture* GSDevice::FetchSurface(GSTexture::Type type, int width, int height, i
pxAssert(t);
if (t->GetType() == type && t->GetFormat() == format && t->GetSize() == size && t->GetMipmapLevels() == levels)
if (t->GetUsage() == usage && t->GetFormat() == format && t->GetSize() == size && t->GetMipmapLevels() == levels)
{
if (!prefer_unused_texture || t->GetLastFrameUsed() != m_frame)
if (prefer_reuse || t->GetLastFrameUsed() != m_frame)
{
m_pool_memory_usage -= t->GetMemUsage();
pool.erase(i);
@@ -610,7 +639,7 @@ GSTexture* GSDevice::FetchSurface(GSTexture::Type type, int width, int height, i
if (!t)
{
if (pool.size() >= ((type == GSTexture::Type::Texture) ? MAX_POOLED_TEXTURES : MAX_POOLED_TARGETS) &&
if (pool.size() >= (GSTexture::IsTexture(usage) ? MAX_POOLED_TEXTURES : MAX_POOLED_TARGETS) &&
fallback != pool.end())
{
t = *fallback;
@@ -619,12 +648,12 @@ GSTexture* GSDevice::FetchSurface(GSTexture::Type type, int width, int height, i
}
else
{
t = CreateSurface(type, size.x, size.y, levels, format);
t = CreateSurface(usage, size.x, size.y, levels, format);
if (!t)
{
ERROR_LOG("GS: Memory allocation failure for {}x{} texture. Purging pool and retrying.", size.x, size.y);
PurgePool();
t = CreateSurface(type, size.x, size.y, levels, format);
t = CreateSurface(usage, size.x, size.y, levels, format);
if (!t)
{
ERROR_LOG("GS: Memory allocation failure for {}x{} texture after purging pool.", size.x, size.y);
@@ -635,7 +664,7 @@ GSTexture* GSDevice::FetchSurface(GSTexture::Type type, int width, int height, i
#ifdef PCSX2_DEVBUILD
if (GSConfig.UseDebugDevice)
{
const TextureLabel label = GetTextureLabel(type, format);
const TextureLabel label = GetTextureLabel(usage, format);
const u32 id = ++s_texture_counts[static_cast<u32>(label)];
t->SetDebugName(TinyString::from_format("{} {}", TextureLabelString(label), id));
}
@@ -643,26 +672,19 @@ GSTexture* GSDevice::FetchSurface(GSTexture::Type type, int width, int height, i
}
}
switch (type)
if (t->IsRenderTarget())
{
case GSTexture::Type::RenderTarget:
{
if (clear)
ClearRenderTarget(t, 0);
else
InvalidateRenderTarget(t);
}
break;
case GSTexture::Type::DepthStencil:
{
if (clear)
ClearDepth(t, 0.0f);
else
InvalidateRenderTarget(t);
}
break;
default:
break;
if (clear)
ClearRenderTarget(t, 0);
else
InvalidateRenderTarget(t);
}
else if (t->IsDepthStencil())
{
if (clear)
ClearDepth(t, 0.0f);
else
InvalidateRenderTarget(t);
}
return t;
@@ -674,6 +696,10 @@ void GSDevice::Recycle(GSTexture* t)
return;
t->SetLastFrameUsed(m_frame);
#ifdef PCSX2_DEVBUILD
t->SetDebugName("");
#endif
FastList<GSTexture*>& pool = m_pool[!t->IsTexture()];
pool.push_front(t);
@@ -738,75 +764,147 @@ void GSDevice::PurgePool()
GSTexture* GSDevice::CreateRenderTarget(int w, int h, GSTexture::Format format, bool clear, bool prefer_reuse)
{
return FetchSurface(GSTexture::Type::RenderTarget, w, h, 1, format, clear, !prefer_reuse);
return FetchSurface(GSTexture::RenderTarget, w, h, 1, format, clear, prefer_reuse);
}
GSTexture* GSDevice::CreateDepthStencil(int w, int h, GSTexture::Format format, bool clear, bool prefer_reuse)
GSTexture* GSDevice::CreateRenderTarget(const GSVector2i& size, GSTexture::Format format, bool clear, bool prefer_reuse)
{
return FetchSurface(GSTexture::Type::DepthStencil, w, h, 1, format, clear, !prefer_reuse);
return FetchSurface(GSTexture::RenderTarget, size.x, size.y, 1, format, clear, prefer_reuse);
}
GSTexture* GSDevice::CreateTexture(int w, int h, int mipmap_levels, GSTexture::Format format, bool prefer_reuse /* = false */)
GSTexture* GSDevice::CreateFeedbackTarget(int w, int h, GSTexture::Format format, bool clear, bool prefer_reuse)
{
return FetchSurface(GSTexture::FeedbackTarget, w, h, 1, format, clear, prefer_reuse);
}
GSTexture* GSDevice::CreateFeedbackTarget(const GSVector2i& size, GSTexture::Format format, bool clear, bool prefer_reuse)
{
return FetchSurface(GSTexture::FeedbackTarget, size.x, size.y, 1, format, clear, prefer_reuse);
}
GSTexture* GSDevice::CreateShaderWriteTarget(int w, int h, GSTexture::Format format, bool clear, bool prefer_reuse)
{
return FetchSurface(GSTexture::ShaderWriteTarget, w, h, 1, format, clear, prefer_reuse);
}
GSTexture* GSDevice::CreateShaderWriteTarget(const GSVector2i& size, GSTexture::Format format, bool clear, bool prefer_reuse)
{
return FetchSurface(GSTexture::ShaderWriteTarget, size.x, size.y, 1, format, clear, prefer_reuse);
}
GSTexture* GSDevice::CreateDepthStencil(int w, int h, bool clear, bool prefer_reuse)
{
return FetchSurface(GSTexture::DepthStencil, w, h, 1, GSTexture::Format::DepthStencil, clear, prefer_reuse);
}
GSTexture* GSDevice::CreateDepthStencil(const GSVector2i& size, bool clear, bool prefer_reuse)
{
return FetchSurface(GSTexture::DepthStencil, size.x, size.y, 1, GSTexture::Format::DepthStencil, clear, prefer_reuse);
}
GSTexture* GSDevice::CreateTexture(int w, int h, int mipmap_levels, GSTexture::Format format, bool prefer_reuse)
{
pxAssert(mipmap_levels != 0 && (mipmap_levels < 0 || mipmap_levels <= GetMipmapLevelsForSize(w, h)));
const int levels = mipmap_levels < 0 ? GetMipmapLevelsForSize(w, h) : mipmap_levels;
return FetchSurface(GSTexture::Type::Texture, w, h, levels, format, false, m_features.prefer_new_textures && !prefer_reuse);
return FetchSurface(GSTexture::Texture, w, h, levels, format, false, m_features.prefer_new_textures && prefer_reuse);
}
void GSDevice::DoStretchRectWithAssertions(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect,
GSHWDrawConfig::ColorMaskSelector cms, ShaderConvert shader, bool linear)
GSTexture* GSDevice::CreateTexture(const GSVector2i& size, int mipmap_levels, GSTexture::Format format, bool prefer_reuse)
{
pxAssert((dTex && dTex->IsDepthStencil()) == HasDepthOutput(shader));
pxAssert(linear ? SupportsBilinear(shader) : SupportsNearest(shader));
GL_INS("StretchRect(%d) {%d,%d} %dx%d -> {%d,%d) %dx%d", shader, int(sRect.left), int(sRect.top),
return CreateTexture(size.x, size.y, mipmap_levels, format, prefer_reuse);
}
GSTexture* GSDevice::CreateCompatible(GSTexture* tex, bool clear, bool prefer_reuse)
{
return CreateCompatible(tex, tex->GetWidth(), tex->GetHeight(), clear, prefer_reuse);
}
GSTexture* GSDevice::CreateCompatible(GSTexture* tex, const GSVector2i& size, bool clear, bool prefer_reuse)
{
return CreateCompatible(tex, size.x, size.y, clear, prefer_reuse);
}
GSTexture* GSDevice::CreateCompatible(GSTexture* tex, int w, int h, bool clear, bool prefer_reuse)
{
return FetchSurface(tex->GetUsage(), w, h, 1, tex->GetFormat(), clear, prefer_reuse);
}
void GSDevice::DoStretchRectWithAssertions(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex,
const GSVector4& dRect, ShaderConvertSelector shader, Filter filter)
{
pxAssert((dTex && dTex->IsDepthLike()) == shader.Float32Output());
pxAssert(!(filter == Biln && shader.SupportsBilinear())); // Don't allow HW bilinear if SW bilinear is required.
GL_INS("StretchRect(%s) {%d,%d} %dx%d -> {%d,%d) %dx%d", ShaderConvertName(shader.Shader()),
int(sRect.left), int(sRect.top),
int(sRect.right - sRect.left), int(sRect.bottom - sRect.top), int(dRect.left), int(dRect.top),
int(dRect.right - dRect.left), int(dRect.bottom - dRect.top));
DoStretchRect(sTex, sRect, dTex, dRect, cms, shader, linear);
DoStretchRect(sTex, sRect, dTex, dRect, shader, filter);
}
void GSDevice::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect,
bool red, bool green, bool blue, bool alpha, ShaderConvert shader)
ShaderConvertSelector shader, Filter filter)
{
GSHWDrawConfig::ColorMaskSelector cms;
cms.wr = red;
cms.wg = green;
cms.wb = blue;
cms.wa = alpha;
pxAssert(HasVariableWriteMask(shader));
GL_INS("ColorCopy Red:%d Green:%d Blue:%d Alpha:%d", cms.wr, cms.wg, cms.wb, cms.wa);
DoStretchRectWithAssertions(sTex, sRect, dTex, dRect, cms, shader, false);
DoStretchRectWithAssertions(sTex, sRect, dTex, dRect, shader, filter);
}
void GSDevice::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect,
ShaderConvert shader, bool linear)
void GSDevice::StretchRect(GSTexture* sTex, GSTexture* dTex, const GSVector4& dRect, ShaderConvertSelector shader, Filter filter)
{
DoStretchRectWithAssertions(sTex, sRect, dTex, dRect, GSHWDrawConfig::ColorMaskSelector(ShaderConvertWriteMask(shader)), shader, linear);
StretchRect(sTex, GSVector4(0, 0, 1, 1), dTex, dRect, shader, filter);
}
void GSDevice::StretchRect(GSTexture* sTex, GSTexture* dTex, const GSVector4& dRect, ShaderConvert shader, bool linear)
void GSDevice::StretchRect(GSTexture* sTex, GSTexture* dTex, ShaderConvertSelector shader, Filter filter)
{
StretchRect(sTex, GSVector4(0, 0, 1, 1), dTex, dRect, shader, linear);
StretchRect(sTex, dTex, GSVector4(dTex->GetRect()), shader, filter);
}
void GSDevice::StretchRectAuto(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect,
Filter filter, u32 src_bpp, u32 dst_bpp)
{
ShaderConvertSelector shader = GetConvertShader(sTex, dTex, src_bpp, dst_bpp);
if (shader.SupportsBilinear() && filter == Biln)
{
// Bilinear is emulated in the shader.
shader.SetFilter(Biln);
filter = Nearest;
}
StretchRect(sTex, sRect, dTex, dRect, shader, filter);
}
void GSDevice::StretchRectAuto(GSTexture* sTex, GSTexture* dTex, const GSVector4& dRect, Filter filter, u32 src_bpp, u32 dst_bpp)
{
StretchRectAuto(sTex, GSVector4(0, 0, 1, 1), dTex, dRect, filter, src_bpp, dst_bpp);
}
void GSDevice::StretchRectAuto(GSTexture* sTex, GSTexture* dTex, Filter filter, u32 src_bpp, u32 dst_bpp)
{
StretchRectAuto(sTex, dTex, GSVector4(dTex->GetRect()), filter, src_bpp, dst_bpp);
}
void GSDevice::StretchRectAutoMask(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect,
bool red, bool green, bool blue, bool alpha, u32 src_bpp, u32 dst_bpp)
{
StretchRect(sTex, sRect, dTex, dRect, GetConvertShaderMask(sTex, dTex, src_bpp, dst_bpp, red, green, blue, alpha), Nearest);
}
void GSDevice::StretchRectAutoMask(GSTexture* sTex, GSTexture* dTex, const GSVector4& dRect, bool red, bool green, bool blue, bool alpha, u32 src_bpp, u32 dst_bpp)
{
StretchRectAutoMask(sTex, GSVector4(0, 0, 1, 1), dTex, dRect, red, green, blue, alpha, src_bpp, dst_bpp);
}
void GSDevice::StretchRectAutoMask(GSTexture* sTex, GSTexture* dTex, bool red, bool green, bool blue, bool alpha,
u32 src_bpp, u32 dst_bpp)
{
StretchRectAutoMask(sTex, dTex, GSVector4(dTex->GetRect()), red, green, blue, alpha, src_bpp, dst_bpp);
}
void GSDevice::DrawMultiStretchRects(
const MultiStretchRect* rects, u32 num_rects, GSTexture* dTex, ShaderConvert shader)
const MultiStretchRect* rects, u32 num_rects, GSTexture* dTex, ShaderConvertSelector shader)
{
for (u32 i = 0; i < num_rects; i++)
{
const MultiStretchRect& sr = rects[i];
pxAssert(HasVariableWriteMask(shader) || rects[0].wmask.wrgba == 0xf);
if (rects[0].wmask.wrgba != 0xf)
{
g_gs_device->StretchRect(sr.src, sr.src_rect, dTex, sr.dst_rect, rects[0].wmask.wr,
rects[0].wmask.wg, rects[0].wmask.wb, rects[0].wmask.wa, shader);
}
else
{
g_gs_device->StretchRect(sr.src, sr.src_rect, dTex, sr.dst_rect, shader, sr.linear);
}
g_gs_device->StretchRect(sr.src, sr.src_rect, dTex, sr.dst_rect,
shader.SetMask(rects[0].wmask.wrgba).SetFilter(sr.filter), sr.filter);
}
}
@@ -814,7 +912,7 @@ void GSDevice::SortMultiStretchRects(MultiStretchRect* rects, u32 num_rects)
{
// Depending on num_rects, insertion sort may be better here.
std::sort(rects, rects + num_rects, [](const MultiStretchRect& lhs, const MultiStretchRect& rhs) {
return lhs.src < rhs.src || lhs.linear < rhs.linear;
return lhs.src < rhs.src || lhs.filter < rhs.filter;
});
}
@@ -840,7 +938,7 @@ void GSDevice::ClearCurrent()
void GSDevice::Merge(GSTexture* sTex[3], GSVector4* sRect, GSVector4* dRect, const GSVector2i& fs, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, u32 c)
{
if (ResizeRenderTarget(&m_merge, fs.x, fs.y, false, false))
DoMerge(sTex, sRect, m_merge, dRect, PMODE, EXTBUF, c, GSConfig.PCRTCOffsets);
DoMerge(sTex, sRect, m_merge, dRect, PMODE, EXTBUF, c, BilnIf(GSConfig.PCRTCOffsets));
m_current = m_merge;
}
@@ -851,7 +949,7 @@ void GSDevice::Interlace(const GSVector2i& ds, int field, int mode, float yoffse
float offset = yoffset * static_cast<float>(field);
offset = GSConfig.DisableInterlaceOffset ? 0.0f : offset;
auto do_interlace = [this](GSTexture* sTex, GSTexture* dTex, ShaderInterlace shader, bool linear, float yoffset, int bufIdx) {
auto do_interlace = [this](GSTexture* sTex, GSTexture* dTex, ShaderInterlace shader, Filter filter, float yoffset, int bufIdx) {
const GSVector2i ds_i = dTex->GetSize();
const GSVector2 ds = GSVector2(static_cast<float>(ds_i.x), static_cast<float>(ds_i.y));
@@ -872,28 +970,28 @@ void GSDevice::Interlace(const GSVector2i& ds, int field, int mode, float yoffse
GSVector4(static_cast<float>(bufIdx), 1.0f / ds.y, ds.y, MAD_SENSITIVITY)
};
GL_PUSH("DoInterlace %dx%d Shader:%d Linear:%d", ds_i.x, ds_i.y, static_cast<int>(shader), linear);
DoInterlace(sTex, sRect, dTex, dRect, shader, linear, cb);
GL_PUSH("DoInterlace %dx%d Shader:%d Filter:%d", ds_i.x, ds_i.y, static_cast<int>(shader), filter);
DoInterlace(sTex, sRect, dTex, dRect, shader, filter, cb);
};
switch (mode)
{
case 0: // Weave
ResizeRenderTarget(&m_weavebob, ds.x, ds.y, true, false);
do_interlace(m_merge, m_weavebob, ShaderInterlace::WEAVE, false, offset, field);
do_interlace(m_merge, m_weavebob, ShaderInterlace::WEAVE, Nearest, offset, field);
m_current = m_weavebob;
break;
case 1: // Bob
// Field is reversed here as we are countering the bounce.
ResizeRenderTarget(&m_weavebob, ds.x, ds.y, true, false);
do_interlace(m_merge, m_weavebob, ShaderInterlace::BOB, true, yoffset * (1 - field), 0);
do_interlace(m_merge, m_weavebob, ShaderInterlace::BOB, Biln, yoffset * (1 - field), 0);
m_current = m_weavebob;
break;
case 2: // Blend
ResizeRenderTarget(&m_weavebob, ds.x, ds.y, true, false);
do_interlace(m_merge, m_weavebob, ShaderInterlace::WEAVE, false, offset, field);
do_interlace(m_merge, m_weavebob, ShaderInterlace::WEAVE, Nearest, offset, field);
ResizeRenderTarget(&m_blend, ds.x, ds.y, true, false);
do_interlace(m_weavebob, m_blend, ShaderInterlace::BLEND, false, 0, 0);
do_interlace(m_weavebob, m_blend, ShaderInterlace::BLEND, Biln, 0, 0);
m_current = m_blend;
break;
case 3: // FastMAD Motion Adaptive Deinterlacing
@@ -902,9 +1000,9 @@ void GSDevice::Interlace(const GSVector2i& ds, int field, int mode, float yoffse
bufIdx |= field;
bufIdx &= 3;
ResizeRenderTarget(&m_mad, ds.x, ds.y * 2.0f, true, false);
do_interlace(m_merge, m_mad, ShaderInterlace::MAD_BUFFER, false, offset, bufIdx);
do_interlace(m_merge, m_mad, ShaderInterlace::MAD_BUFFER, Nearest, offset, bufIdx);
ResizeRenderTarget(&m_weavebob, ds.x, ds.y, true, false);
do_interlace(m_mad, m_weavebob, ShaderInterlace::MAD_RECONSTRUCT, false, 0, bufIdx);
do_interlace(m_mad, m_weavebob, ShaderInterlace::MAD_RECONSTRUCT, Nearest, 0, bufIdx);
m_current = m_weavebob;
break;
default:
@@ -961,7 +1059,7 @@ void GSDevice::Resize(int width, int height)
{
const GSVector4 sRect(0, 0, 1, 1);
const GSVector4 dRect(0, 0, s.x, s.y);
StretchRect(m_current, sRect, dTex, dRect, ShaderConvert::COPY, false);
StretchRectAuto(m_current, sRect, dTex, dRect, Nearest);
m_current = dTex;
}
}
@@ -980,8 +1078,9 @@ bool GSDevice::ResizeRenderTarget(GSTexture** t, int w, int h, bool preserve_con
}
const GSTexture::Format fmt = orig_tex ? orig_tex->GetFormat() : GSTexture::Format::Color;
const GSTexture::Usage usage = orig_tex ? orig_tex->GetUsage() : GSTexture::RenderTarget;
const bool really_preserve_contents = (preserve_contents && orig_tex);
GSTexture* new_tex = FetchSurface(GSTexture::Type::RenderTarget, w, h, 1, fmt, !really_preserve_contents, true);
GSTexture* new_tex = FetchSurface(usage, w, h, 1, fmt, !really_preserve_contents, false);
if (!new_tex)
{
Console.WriteLn("%dx%d texture allocation failed in ResizeTexture()", w, h);
@@ -992,7 +1091,7 @@ bool GSDevice::ResizeRenderTarget(GSTexture** t, int w, int h, bool preserve_con
{
constexpr GSVector4 sRect = GSVector4::cxpr(0, 0, 1, 1);
const GSVector4 dRect = GSVector4(orig_tex->GetRect());
StretchRect(orig_tex, sRect, new_tex, dRect, ShaderConvert::COPY, true);
StretchRect(orig_tex, sRect, new_tex, dRect, ShaderConvert::COPY, Biln);
}
if (orig_tex)
@@ -1012,10 +1111,10 @@ void GSDevice::BeginDSAsRT(GSTexture* ds, const GSVector4i& drawarea)
// Create a temporary RT and copy the area needed for the draw.
const int w = ds->GetWidth();
const int h = ds->GetHeight();
m_ds_as_rt = g_gs_device->CreateRenderTarget(w, h, GSTexture::Format::Float32, false, true);
m_ds_as_rt = g_gs_device->CreateFeedbackTarget(w, h, GSTexture::Format::DepthColor, false, true);
const GSVector4 dRect(drawarea);
const GSVector4 sRect(dRect.x / w, dRect.y / h, dRect.z / w, dRect.w / h);
StretchRect(ds, sRect, m_ds_as_rt, dRect, ShaderConvert::FLOAT32_DEPTH_TO_COLOR, false);
StretchRectAuto(ds, sRect, m_ds_as_rt, dRect, Nearest);
}
void GSDevice::EndDSAsRT()
@@ -1068,7 +1167,7 @@ void GSDevice::CAS(GSTexture*& tex, GSVector4i& src_rect, GSVector4& src_uv, con
if (!m_cas || m_cas->GetWidth() != dst_width || m_cas->GetHeight() != dst_height)
{
delete m_cas;
m_cas = CreateSurface(GSTexture::Type::RWTexture, dst_width, dst_height, 1, GSTexture::Format::Color);
m_cas = CreateSurface(GSTexture::ShaderWriteTexture, dst_width, dst_height, 1, GSTexture::Format::Color);
if (!m_cas)
{
Console.Error("Failed to allocate CAS RW texture.");
@@ -1414,9 +1513,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";
@@ -1437,6 +1536,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);
@@ -1492,9 +1602,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)
@@ -1600,6 +1714,7 @@ static void DumpPSConstantBuffer(DrawConfigWriter& out, const GSHWDrawConfig::PS
DumpVector4(out, "DitherMatrix_2", cb.DitherMatrix[2]);
DumpVector4(out, "DitherMatrix_3", cb.DitherMatrix[3]);
DumpVector4(out, "ScaleFactor", cb.ScaleFactor);
out.WriteLn("LineCovScale: {}", cb.LineCovScale);
}
static void DumpVSConstantBuffer(DrawConfigWriter& out, const GSHWDrawConfig::VSConstantBuffer& cb)
@@ -1609,7 +1724,8 @@ static void DumpVSConstantBuffer(DrawConfigWriter& out, const GSHWDrawConfig::VS
DumpVector2(out, "texture_scale", cb.texture_scale);
DumpVector2(out, "texture_offset", cb.texture_offset);
DumpVector2(out, "point_size", cb.point_size);
DumpVector2(out, "max_depth", cb.max_depth);
out.WriteLn("max_depth: {}", cb.max_depth);
out.WriteLn("line_aa1_width: {}", cb.line_aa1_width);
}
static void DumpConfig(DrawConfigWriter& out, const GSHWDrawConfig& conf,
@@ -1618,6 +1734,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));
@@ -1691,6 +1809,75 @@ void GSHWDrawConfig::DumpConfig(const std::string& path, const GSHWDrawConfig& c
}
}
static constexpr u32 NUM_REMAP_INPUTS = static_cast<u32>(ShaderConvert::Count) * 4;
static constexpr ShaderConvertSelector GetRemappedShader(u32 idx)
{
ShaderConvert convert = static_cast<ShaderConvert>(idx >> 2);
bool depth_out = (idx >> 0) & 1;
Filter filter = static_cast<Filter>((idx >> 1) & 1);
return ShaderConvertSelector(convert, 0xf, depth_out, filter);
}
static constexpr bool RemapIndexIsValid(u32 idx)
{
ShaderConvert convert = static_cast<ShaderConvert>(idx >> 2);
bool depth_out = (idx >> 0) & 1;
Filter filter = static_cast<Filter>((idx >> 1) & 1);
if (HasVariableWriteMask(convert) && !depth_out && filter == Nearest)
return false; // Handled as variable write mask
if (depth_out && !HasFloat32Output(convert))
return false;
if (filter == Biln && !SupportsBilinear(convert))
return false;
return true;
}
static constexpr u32 CalcNumRemappedShaders()
{
u32 num = 0;
for (u32 i = 0; i < NUM_REMAP_INPUTS; i++)
num += RemapIndexIsValid(i);
return num;
}
static constexpr u32 NUM_REMAPPED_SHADERS = CalcNumRemappedShaders();
static constexpr u32 NUM_TOTAL_SHADERS = NUM_REMAPPED_SHADERS +
16 * ShaderConvertSelector::NUM_VARIABLE_WRITE_MASK_SHADERS;
static_assert(NUM_REMAPPED_SHADERS <= 256); // We use u8 for the remap indices.
static constexpr std::array<u8, NUM_REMAP_INPUTS> GenRemapArray()
{
std::array<u8, NUM_REMAP_INPUTS> out{};
u8 out_idx = 0;
const u8 invalid = 0xff;
for (u32 i = 0; i < NUM_REMAP_INPUTS; i++)
out[i] = RemapIndexIsValid(i) ? out_idx++ : invalid;
return out;
}
static constexpr std::array<ShaderConvertSelector, NUM_TOTAL_SHADERS> GetPackedShaders()
{
std::array<ShaderConvertSelector, NUM_TOTAL_SHADERS> out{};
u32 append_idx = 0;
for (u32 i = 0; i < NUM_REMAP_INPUTS; i++)
{
if (RemapIndexIsValid(i))
out[append_idx++] = GetRemappedShader(i);
}
for (u32 i = 0; i < 16; i++)
out[append_idx++] = ShaderConvertSelector(ShaderConvert::COPY, i);
for (u32 i = 0; i < 16; i++)
out[append_idx++] = ShaderConvertSelector(ShaderConvert::RTA_CORRECTION, i);
return out;
}
constinit const u32 ShaderConvertSelector::NUM_REMAPPED_SHADERS = ::NUM_REMAPPED_SHADERS;
constinit const u32 ShaderConvertSelector::NUM_TOTAL_SHADERS = ::NUM_TOTAL_SHADERS;
constinit const std::array<u8, NUM_REMAP_INPUTS> ShaderConvertSelector::INDEX_REMAP = GenRemapArray();
static constexpr auto PACKED_SHADERS = GetPackedShaders();
const std::span<const ShaderConvertSelector> ShaderConvertSelector::SHADERS = PACKED_SHADERS;
// clang-format off
// Maps PS2 blend modes to our best approximation of them with PC hardware

Some files were not shown because too many files have changed in this diff Show More