Compare commits

...

71 Commits

Author SHA1 Message Date
GregoireLD
a19bb91041 GameDB: Add a new Ico version (#11103) 2025-04-18 13:33:51 -04:00
Filoppi
a545982a28 GS: Fix crash if CAS pipeline failed to compile (it'd crash when the user enables CAS, which is still enough of a good reason to make the whole rendering backend fail to initialize) 2025-04-18 19:11:11 +02:00
Filoppi
2109df04ca GS: Remove duplicate shader creation for RTA correction. The same shader would have been created just above. 2025-04-18 19:11:11 +02:00
Filoppi
f485cf8ebc GS: fix more badly named debug objects 2025-04-18 19:11:11 +02:00
Filoppi
bb48110f95 GS: Fix VK also naming the wrong pipelines 2025-04-18 19:11:11 +02:00
Filoppi
718adda749 GS: Fix DX12 setting the object names for the wrong pipelines (possibly causing random memory writes), also fix some bad naming 2025-04-18 19:11:11 +02:00
Filoppi
a170c7ccb1 GS: Fix DX12 m_color_copy shaders not applying the right RTA_CORRECTION offset (see the VK implementation, which is identical except for this issue) 2025-04-18 19:11:11 +02:00
Filoppi
fec3fe3b6b GS: Add DX11 shaders debug names 2025-04-18 19:11:11 +02:00
Filoppi
00f19c9777 GS: Fix displays that are currently unplugged (disabled) from throwing unnecessary warnings 2025-04-18 19:11:11 +02:00
Filoppi
93aae28593 GS: Polish spacing and comments 2025-04-18 19:11:11 +02:00
Filoppi
8be2b907b3 GS: Add actual HDR and HQ textures and rename the "HDR" textures to colclip (hw) given that's actually what they are (HDR was a very loose term for it) 2025-04-18 19:11:11 +02:00
Filoppi
fcca07765b GS: Clarify HW blends code a bit 2025-04-18 19:11:11 +02:00
EXtremeExploit
559e4e75eb GameDB: Add memcard filters for Mortal Kombat Armageddon Premium 2025-04-18 10:10:16 -04:00
TheLastRar
8ae84614d5 SDLInputSource: Support auto mapping pressure sense buttons
Also provide UI strings and icons
2025-04-18 10:09:48 -04:00
TheLastRar
18a7e8b22c SDLInputSource: Enable support for the Sixaxis driver on Windows 2025-04-18 10:09:48 -04:00
Immersion95
01120f6120 GameDB: Adds Software FMV hack to Soulcalibur 2/3 Games
Fixes https://github.com/PCSX2/pcsx2/issues/2852
2025-04-18 02:44:08 +02:00
TheLastRar
07be6bb5ae DEV9: Ignore UDP socket ICMP errors on recv
These where already ignored on send
2025-04-18 02:39:06 +02:00
TheLastRar
39b4905ef1 DEV9: Fix race condition when handling closed socket connections 2025-04-18 02:39:06 +02:00
TheLastRar
6c49a5aa9d DEV9: Fix race condition in UDP sockets 2025-04-18 02:39:06 +02:00
TheLastRar
aaeff2ea0f DEV9: Deduplicate some UDP sockets code 2025-04-18 02:39:06 +02:00
PCSX2 Bot
cd48e78667 [ci skip] Qt: Update Base Translation. 2025-04-18 02:28:36 +02:00
KamFretoZ
f2ab4e840e Qt: Change Default Theme 2025-04-18 02:28:15 +02:00
dependabot[bot]
9a476f8283 Bump @octokit/request and @octokit/plugin-throttling
Bumps [@octokit/request](https://github.com/octokit/request.js) to 9.2.2 and updates ancestor dependency [@octokit/plugin-throttling](https://github.com/octokit/plugin-throttling.js). These dependencies need to be updated together.


Updates `@octokit/request` from 5.6.2 to 9.2.2
- [Release notes](https://github.com/octokit/request.js/releases)
- [Commits](https://github.com/octokit/request.js/compare/v5.6.2...v9.2.2)

Updates `@octokit/plugin-throttling` from 3.5.2 to 9.6.0
- [Release notes](https://github.com/octokit/plugin-throttling.js/releases)
- [Commits](https://github.com/octokit/plugin-throttling.js/compare/v3.5.2...v9.6.0)

---
updated-dependencies:
- dependency-name: "@octokit/request"
  dependency-version: 9.2.2
  dependency-type: indirect
- dependency-name: "@octokit/plugin-throttling"
  dependency-version: 9.6.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-17 19:32:38 -04:00
lightningterror
8e24ad724c CDVD: Adjust ram requirements when precaching. 2025-04-17 18:59:14 -04:00
TheLastRar
c70aba2ca5 Common: Allow shared reading of log files 2025-04-17 18:57:36 -04:00
refractionpcsx2
c6a20961b8 GS/HW: Improve quad detection on triangle strips 2025-04-17 18:54:33 -04:00
refractionpcsx2
4741354883 GS/HW: Clamp native scaling texture read size to texture size
Fixes Transformers light rays
2025-04-17 18:54:33 -04:00
refractionpcsx2
a3051f5d58 GS/HW: More RT in RT regression fixes/improvements 2025-04-17 18:54:33 -04:00
refractionpcsx2
730207e75b GS/HW: Do not set a source region when using channel shuffles or tex is rt 2025-04-17 18:54:33 -04:00
refractionpcsx2
27e067889d GS/HW: Try to improve first barrier placement for Metal, Vulkan and OGL 2025-04-17 18:54:33 -04:00
refractionpcsx2
22c9433c1c GS/HW: Split out invalidation in case RT processing invalidates Z, causing a use after free 2025-04-17 18:54:33 -04:00
refractionpcsx2
c6b558cb3d GS/HW: Fix up source region behaviour 2025-04-17 18:54:33 -04:00
refractionpcsx2
a6d5598c08 GS/HW: More RT in RT regression fixes 2025-04-17 18:54:33 -04:00
refractionpcsx2
13ee2abeef GS/HW: Fix some clear behaviour 2025-04-17 18:54:33 -04:00
refractionpcsx2
ca9c841477 GS/HW: Attempt to reduce the load of copies for offset Z 2025-04-17 18:54:33 -04:00
refractionpcsx2
0dbae3c46c GS/HW: Reduce number of targets in pool when doing channels shuffles 2025-04-17 18:54:33 -04:00
refractionpcsx2
045bcbc7da GS/HW: More RT in RT regression fixes and adjustments
Restored the Z clear CRC hack for Battlefield 2, it's probably the least invasive one and the most difficult one to emulate, it was still problematic.
2025-04-17 18:54:33 -04:00
refractionpcsx2
9f98e28b08 GS/HW: Fix Z Tex in RT regions + read back sources for SW if needed 2025-04-17 18:54:33 -04:00
refractionpcsx2
59dfbae9b4 GS/HW: Disable per page split shuffle when RT in RT is enabled 2025-04-17 18:54:33 -04:00
refractionpcsx2
3f0250957b GS/HW: Fix more regressions with RT in RT 2025-04-17 18:54:33 -04:00
refractionpcsx2
73595d93f3 GS/HW: Fix some format conversion scaling problems 2025-04-17 18:54:33 -04:00
refractionpcsx2
19b8755c89 GS/HW: Support RT in RT in SW renderer fallback check 2025-04-17 18:54:33 -04:00
refractionpcsx2
14aad730de GS: Code cleanup at the behest of Const-Man 2025-04-17 18:54:33 -04:00
refractionpcsx2
6c7eec5778 GS/HW: Don't allow Tex in RT if not contained 2025-04-17 18:54:33 -04:00
refractionpcsx2
2c5ce5763a GS/HW: Intercept excessively large clears 2025-04-17 18:54:33 -04:00
refractionpcsx2
b568e1387a GS/HW: Allow offsetting in to a target if full contained. 2025-04-17 18:54:33 -04:00
refractionpcsx2
85bd46e457 GameDB-GS/HW: Remove Battlefield 2 CRC hacks, add Tex Inside RT instead 2025-04-17 18:54:33 -04:00
refractionpcsx2
bd4a77992e GS/HW: Predict valid sizes based on repeated draws and scissor
- this should be okay/limited to certain situations like Battlefield 2. Scissor isn't 100% guaranteed to be right, but it's probably better than nothing.
2025-04-17 18:54:33 -04:00
refractionpcsx2
a8750ce8ad GS/HW: Check all overlapping pages when clearing sources 2025-04-17 18:54:33 -04:00
refractionpcsx2
4e8887c80b GameDB: Adjust fixes for games affected by RT in RT 2025-04-17 18:54:33 -04:00
refractionpcsx2
c38a0cdec9 GS/HW: Don't update TBP on targets + make target src's temporary 2025-04-17 18:54:33 -04:00
refractionpcsx2
6f961edcb1 GS/HW: Remove no longer required CRCs 2025-04-17 18:54:33 -04:00
refractionpcsx2
bdd9f30404 GS: Add CRC hack for Guitar Hero 3 to handle crowds 2025-04-17 18:54:33 -04:00
refractionpcsx2
f94d5faaf2 GS/HW: Further fixes and rewrite of AlignedRectTranslate 2025-04-17 18:54:33 -04:00
refractionpcsx2
c1ffd93f28 GS/HW: Fix up shuffle behaviour and affected areas
- Channel shuffles now check how many pages require drawing before doing the shuffle.
- Split texture shuffles don't create new targets with bad valid areas.
2025-04-17 18:54:33 -04:00
refractionpcsx2
05bf7af859 GS/HW: Further fixes to HW renderer behaviour 2025-04-17 18:54:33 -04:00
refractionpcsx2
eae359a3b8 GS/HW: Don't interfere with Tales/Urban Chaos HLE shuffles 2025-04-17 18:54:33 -04:00
refractionpcsx2
2156906341 GS/HW: Allow 1:1 quads to be optimized for textures. Fixes for shuffles 2025-04-17 18:54:33 -04:00
refractionpcsx2
e7bd669362 GS/HW: Centralize new target resizing calls to fix statistics/tidy up
Also add an override for GSVector4i loadl to take a GSVector2i
2025-04-17 18:54:33 -04:00
refractionpcsx2
17ac5b5e06 GS/HW: Fixes for Tex in RT and shuffle detection 2025-04-17 18:54:33 -04:00
refractionpcsx2
671cc753b5 GS/HW: Sync depth texture information when updating dst_match 2025-04-17 18:54:33 -04:00
refractionpcsx2
fd81b47413 GS/HW: Fix some back to back shuffles and inside source invalidation 2025-04-17 18:54:33 -04:00
refractionpcsx2
710b07a857 GS/HW: Fix offset Z channel shuffle hazard. Adjust Tekken 5 CRC 2025-04-17 18:54:33 -04:00
refractionpcsx2
5b93d2642b GS/HW: More changes some regressions 2025-04-17 18:54:33 -04:00
refractionpcsx2
38d792fd97 GS/HW: More alterations for new RT in RT system 2025-04-17 18:54:33 -04:00
refractionpcsx2
fed266f5ac GS/HW: Fixes to texture is target offsets 2025-04-17 18:54:33 -04:00
refractionpcsx2
a547d10ab9 GS/HW: Further fixes for RT in RT changes in behaviour 2025-04-17 18:54:33 -04:00
refractionpcsx2
4cc6043737 GS/HW: Further RT in RT changes to improve compatibility 2025-04-17 18:54:33 -04:00
refractionpcsx2
0a42313b6f GS/HW: Further fixes to RT in RT - Still a ways to go... 2025-04-17 18:54:33 -04:00
refractionpcsx2
0b53f541d1 GS/HW: Initial work implementing RT in RT support 2025-04-17 18:54:33 -04:00
PCSX2 Bot
ddd17b18d7 [ci skip] Qt: Update Base Translation. 2025-04-16 02:02:30 +02:00
59 changed files with 4235 additions and 1908 deletions

View File

@@ -9,123 +9,11 @@
"license": "ISC",
"dependencies": {
"@octokit/plugin-retry": "^3.0.9",
"@octokit/plugin-throttling": "^3.5.2",
"@octokit/plugin-throttling": "^9.6.0",
"@octokit/rest": "^21.1.1"
}
},
"node_modules/@octokit/auth-token": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz",
"integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==",
"peer": true,
"dependencies": {
"@octokit/types": "^6.0.3"
}
},
"node_modules/@octokit/core": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.5.1.tgz",
"integrity": "sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw==",
"peer": true,
"dependencies": {
"@octokit/auth-token": "^2.4.4",
"@octokit/graphql": "^4.5.8",
"@octokit/request": "^5.6.0",
"@octokit/request-error": "^2.0.5",
"@octokit/types": "^6.0.3",
"before-after-hook": "^2.2.0",
"universal-user-agent": "^6.0.0"
}
},
"node_modules/@octokit/endpoint": {
"version": "6.0.12",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz",
"integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==",
"peer": true,
"dependencies": {
"@octokit/types": "^6.0.3",
"is-plain-object": "^5.0.0",
"universal-user-agent": "^6.0.0"
}
},
"node_modules/@octokit/graphql": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz",
"integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==",
"peer": true,
"dependencies": {
"@octokit/request": "^5.6.0",
"@octokit/types": "^6.0.3",
"universal-user-agent": "^6.0.0"
}
},
"node_modules/@octokit/openapi-types": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.1.0.tgz",
"integrity": "sha512-dWZfYvCCdjZzDYA3lIAMF72Q0jld8xidqCq5Ryw09eBJXZdcM6he0vWBTvw/b5UnGYqexxOyHWgfrsTlUJL3Gw=="
},
"node_modules/@octokit/plugin-retry": {
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-3.0.9.tgz",
"integrity": "sha512-r+fArdP5+TG6l1Rv/C9hVoty6tldw6cE2pRHNGmFPdyfrc696R6JjrQ3d7HdVqGwuzfyrcaLAKD7K8TX8aehUQ==",
"dependencies": {
"@octokit/types": "^6.0.3",
"bottleneck": "^2.15.3"
}
},
"node_modules/@octokit/plugin-throttling": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-3.5.2.tgz",
"integrity": "sha512-Eu7kfJxU8vmHqWGNszWpg+GVp2tnAfax3XQV5CkYPEE69C+KvInJXW9WajgSeW+cxYe0UVdouzCtcreGNuJo7A==",
"dependencies": {
"@octokit/types": "^6.0.1",
"bottleneck": "^2.15.3"
},
"peerDependencies": {
"@octokit/core": "^3.5.0"
}
},
"node_modules/@octokit/request": {
"version": "5.6.2",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.2.tgz",
"integrity": "sha512-je66CvSEVf0jCpRISxkUcCa0UkxmFs6eGDRSbfJtAVwbLH5ceqF+YEyC8lj8ystKyZTy8adWr0qmkY52EfOeLA==",
"peer": true,
"dependencies": {
"@octokit/endpoint": "^6.0.1",
"@octokit/request-error": "^2.1.0",
"@octokit/types": "^6.16.1",
"is-plain-object": "^5.0.0",
"node-fetch": "^2.6.1",
"universal-user-agent": "^6.0.0"
}
},
"node_modules/@octokit/request-error": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz",
"integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==",
"peer": true,
"dependencies": {
"@octokit/types": "^6.0.3",
"deprecation": "^2.0.0",
"once": "^1.4.0"
}
},
"node_modules/@octokit/rest": {
"version": "21.1.1",
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-21.1.1.tgz",
"integrity": "sha512-sTQV7va0IUVZcntzy1q3QqPm/r8rWtDCqpRAmb8eXXnKkjoQEtFe3Nt5GTVsHft+R6jJoHeSiVLcgcvhtue/rg==",
"license": "MIT",
"dependencies": {
"@octokit/core": "^6.1.4",
"@octokit/plugin-paginate-rest": "^11.4.2",
"@octokit/plugin-request-log": "^5.3.1",
"@octokit/plugin-rest-endpoint-methods": "^13.3.0"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/@octokit/rest/node_modules/@octokit/auth-token": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.2.tgz",
"integrity": "sha512-JcQDsBdg49Yky2w2ld20IHAlwr8d/d8N6NiOXbtuoPCqzbsiJgF633mVUw3x4mo0H5ypataQIX7SFu3yy44Mpw==",
@@ -134,7 +22,7 @@
"node": ">= 18"
}
},
"node_modules/@octokit/rest/node_modules/@octokit/core": {
"node_modules/@octokit/core": {
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.4.tgz",
"integrity": "sha512-lAS9k7d6I0MPN+gb9bKDt7X8SdxknYqAMh44S5L+lNqIN2NuV8nvv3g8rPp7MuRxcOpxpUIATWprO0C34a8Qmg==",
@@ -152,7 +40,22 @@
"node": ">= 18"
}
},
"node_modules/@octokit/rest/node_modules/@octokit/endpoint": {
"node_modules/@octokit/core/node_modules/@octokit/openapi-types": {
"version": "24.2.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
"integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
"license": "MIT"
},
"node_modules/@octokit/core/node_modules/@octokit/types": {
"version": "13.10.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
"integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
"license": "MIT",
"dependencies": {
"@octokit/openapi-types": "^24.2.0"
}
},
"node_modules/@octokit/endpoint": {
"version": "10.1.3",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.3.tgz",
"integrity": "sha512-nBRBMpKPhQUxCsQQeW+rCJ/OPSMcj3g0nfHn01zGYZXuNDvvXudF/TYY6APj5THlurerpFN4a/dQAIAaM6BYhA==",
@@ -165,7 +68,22 @@
"node": ">= 18"
}
},
"node_modules/@octokit/rest/node_modules/@octokit/graphql": {
"node_modules/@octokit/endpoint/node_modules/@octokit/openapi-types": {
"version": "24.2.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
"integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
"license": "MIT"
},
"node_modules/@octokit/endpoint/node_modules/@octokit/types": {
"version": "13.10.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
"integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
"license": "MIT",
"dependencies": {
"@octokit/openapi-types": "^24.2.0"
}
},
"node_modules/@octokit/graphql": {
"version": "8.2.1",
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.2.1.tgz",
"integrity": "sha512-n57hXtOoHrhwTWdvhVkdJHdhTv0JstjDbDRhJfwIRNfFqmSo1DaK/mD2syoNUoLCyqSjBpGAKOG0BuwF392slw==",
@@ -179,6 +97,139 @@
"node": ">= 18"
}
},
"node_modules/@octokit/graphql/node_modules/@octokit/openapi-types": {
"version": "24.2.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
"integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
"license": "MIT"
},
"node_modules/@octokit/graphql/node_modules/@octokit/types": {
"version": "13.10.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
"integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
"license": "MIT",
"dependencies": {
"@octokit/openapi-types": "^24.2.0"
}
},
"node_modules/@octokit/openapi-types": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.1.0.tgz",
"integrity": "sha512-dWZfYvCCdjZzDYA3lIAMF72Q0jld8xidqCq5Ryw09eBJXZdcM6he0vWBTvw/b5UnGYqexxOyHWgfrsTlUJL3Gw=="
},
"node_modules/@octokit/plugin-retry": {
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-3.0.9.tgz",
"integrity": "sha512-r+fArdP5+TG6l1Rv/C9hVoty6tldw6cE2pRHNGmFPdyfrc696R6JjrQ3d7HdVqGwuzfyrcaLAKD7K8TX8aehUQ==",
"dependencies": {
"@octokit/types": "^6.0.3",
"bottleneck": "^2.15.3"
}
},
"node_modules/@octokit/plugin-throttling": {
"version": "9.6.0",
"resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-9.6.0.tgz",
"integrity": "sha512-zn7m1N3vpJDaVzLqjCRdJ0cRzNiekHEWPi8Ww9xyPNrDt5PStHvVE0eR8wy4RSU8Eg7YO8MHyvn6sv25EGVhhg==",
"license": "MIT",
"dependencies": {
"@octokit/types": "^13.7.0",
"bottleneck": "^2.15.3"
},
"engines": {
"node": ">= 18"
},
"peerDependencies": {
"@octokit/core": "^6.1.3"
}
},
"node_modules/@octokit/plugin-throttling/node_modules/@octokit/openapi-types": {
"version": "24.2.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
"integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
"license": "MIT"
},
"node_modules/@octokit/plugin-throttling/node_modules/@octokit/types": {
"version": "13.10.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
"integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
"license": "MIT",
"dependencies": {
"@octokit/openapi-types": "^24.2.0"
}
},
"node_modules/@octokit/request": {
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.2.2.tgz",
"integrity": "sha512-dZl0ZHx6gOQGcffgm1/Sf6JfEpmh34v3Af2Uci02vzUYz6qEN6zepoRtmybWXIGXFIK8K9ylE3b+duCWqhArtg==",
"license": "MIT",
"dependencies": {
"@octokit/endpoint": "^10.1.3",
"@octokit/request-error": "^6.1.7",
"@octokit/types": "^13.6.2",
"fast-content-type-parse": "^2.0.0",
"universal-user-agent": "^7.0.2"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/@octokit/request-error": {
"version": "6.1.7",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.7.tgz",
"integrity": "sha512-69NIppAwaauwZv6aOzb+VVLwt+0havz9GT5YplkeJv7fG7a40qpLt/yZKyiDxAhgz0EtgNdNcb96Z0u+Zyuy2g==",
"license": "MIT",
"dependencies": {
"@octokit/types": "^13.6.2"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/@octokit/request-error/node_modules/@octokit/openapi-types": {
"version": "24.2.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
"integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
"license": "MIT"
},
"node_modules/@octokit/request-error/node_modules/@octokit/types": {
"version": "13.10.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
"integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
"license": "MIT",
"dependencies": {
"@octokit/openapi-types": "^24.2.0"
}
},
"node_modules/@octokit/request/node_modules/@octokit/openapi-types": {
"version": "24.2.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
"integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
"license": "MIT"
},
"node_modules/@octokit/request/node_modules/@octokit/types": {
"version": "13.10.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
"integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
"license": "MIT",
"dependencies": {
"@octokit/openapi-types": "^24.2.0"
}
},
"node_modules/@octokit/rest": {
"version": "21.1.1",
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-21.1.1.tgz",
"integrity": "sha512-sTQV7va0IUVZcntzy1q3QqPm/r8rWtDCqpRAmb8eXXnKkjoQEtFe3Nt5GTVsHft+R6jJoHeSiVLcgcvhtue/rg==",
"license": "MIT",
"dependencies": {
"@octokit/core": "^6.1.4",
"@octokit/plugin-paginate-rest": "^11.4.2",
"@octokit/plugin-request-log": "^5.3.1",
"@octokit/plugin-rest-endpoint-methods": "^13.3.0"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/@octokit/rest/node_modules/@octokit/openapi-types": {
"version": "23.0.1",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-23.0.1.tgz",
@@ -227,34 +278,6 @@
"@octokit/core": ">=6"
}
},
"node_modules/@octokit/rest/node_modules/@octokit/request": {
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.2.2.tgz",
"integrity": "sha512-dZl0ZHx6gOQGcffgm1/Sf6JfEpmh34v3Af2Uci02vzUYz6qEN6zepoRtmybWXIGXFIK8K9ylE3b+duCWqhArtg==",
"license": "MIT",
"dependencies": {
"@octokit/endpoint": "^10.1.3",
"@octokit/request-error": "^6.1.7",
"@octokit/types": "^13.6.2",
"fast-content-type-parse": "^2.0.0",
"universal-user-agent": "^7.0.2"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/@octokit/rest/node_modules/@octokit/request-error": {
"version": "6.1.7",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.7.tgz",
"integrity": "sha512-69NIppAwaauwZv6aOzb+VVLwt+0havz9GT5YplkeJv7fG7a40qpLt/yZKyiDxAhgz0EtgNdNcb96Z0u+Zyuy2g==",
"license": "MIT",
"dependencies": {
"@octokit/types": "^13.6.2"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/@octokit/rest/node_modules/@octokit/types": {
"version": "13.8.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.8.0.tgz",
@@ -264,18 +287,6 @@
"@octokit/openapi-types": "^23.0.1"
}
},
"node_modules/@octokit/rest/node_modules/before-after-hook": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-3.0.2.tgz",
"integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==",
"license": "Apache-2.0"
},
"node_modules/@octokit/rest/node_modules/universal-user-agent": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz",
"integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==",
"license": "ISC"
},
"node_modules/@octokit/types": {
"version": "6.33.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.33.0.tgz",
@@ -285,22 +296,16 @@
}
},
"node_modules/before-after-hook": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz",
"integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==",
"peer": true
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-3.0.2.tgz",
"integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==",
"license": "Apache-2.0"
},
"node_modules/bottleneck": {
"version": "2.19.5",
"resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz",
"integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw=="
},
"node_modules/deprecation": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
"integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==",
"peer": true
},
"node_modules/fast-content-type-parse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-2.0.1.tgz",
@@ -317,77 +322,11 @@
],
"license": "MIT"
},
"node_modules/is-plain-object": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/node-fetch": {
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
"peer": true,
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
},
"peerDependencies": {
"encoding": "^0.1.0"
},
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"peer": true,
"dependencies": {
"wrappy": "1"
}
},
"node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=",
"peer": true
},
"node_modules/universal-user-agent": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz",
"integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==",
"peer": true
},
"node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=",
"peer": true
},
"node_modules/whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
"peer": true,
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"peer": true
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz",
"integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==",
"license": "ISC"
}
}
}

View File

@@ -11,7 +11,7 @@
"license": "ISC",
"dependencies": {
"@octokit/plugin-retry": "^3.0.9",
"@octokit/plugin-throttling": "^3.5.2",
"@octokit/plugin-throttling": "^9.6.0",
"@octokit/rest": "^21.1.1"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -170,7 +170,7 @@ PS_OUTPUT ps_rta_decorrection(PS_INPUT input)
return output;
}
PS_OUTPUT ps_hdr_init(PS_INPUT input)
PS_OUTPUT ps_colclip_init(PS_INPUT input)
{
PS_OUTPUT output;
float4 value = sample_c(input.t);
@@ -178,7 +178,7 @@ PS_OUTPUT ps_hdr_init(PS_INPUT input)
return output;
}
PS_OUTPUT ps_hdr_resolve(PS_INPUT input)
PS_OUTPUT ps_colclip_resolve(PS_INPUT input)
{
PS_OUTPUT output;
float4 value = sample_c(input.t);

View File

@@ -55,7 +55,7 @@
#define PS_CHANNEL_FETCH 0
#define PS_TALES_OF_ABYSS_HLE 0
#define PS_URBAN_CHAOS_HLE 0
#define PS_HDR 0
#define PS_COLCLIP_HW 0
#define PS_RTA_CORRECTION 0
#define PS_RTA_SRC_CORRECTION 0
#define PS_COLCLIP 0
@@ -767,7 +767,7 @@ float4 ps_color(PS_INPUT input)
float4 T = sample_color(st, input.t.w);
#endif
if (PS_SHUFFLE && !PS_SHUFFLE_SAME && !PS_READ16_SRC)
if (PS_SHUFFLE && !PS_SHUFFLE_SAME && !PS_READ16_SRC && !(PS_PROCESS_BA == SHUFFLE_READWRITE && PS_PROCESS_RG == SHUFFLE_READWRITE))
{
uint4 denorm_c_before = uint4(T);
if (PS_PROCESS_BA & SHUFFLE_READ)
@@ -799,7 +799,7 @@ void ps_fbmask(inout float4 C, float2 pos_xy)
{
if (PS_FBMASK)
{
float multi = PS_HDR ? 65535.0f : 255.0f;
float multi = PS_COLCLIP_HW ? 65535.0f : 255.0f;
float4 RT = trunc(RtTexture.Load(int3(pos_xy, 0)) * multi + 0.1f);
C = (float4)(((uint4)C & ~FbMask) | ((uint4)RT & FbMask));
}
@@ -843,13 +843,13 @@ void ps_color_clamp_wrap(inout float3 C)
C += 7.0f; // Need to round up, not down since the shader will invert
// Standard Clamp
if (PS_COLCLIP == 0 && PS_HDR == 0)
if (PS_COLCLIP == 0 && PS_COLCLIP_HW == 0)
C = clamp(C, (float3)0.0f, (float3)255.0f);
// In 16 bits format, only 5 bits of color are used. It impacts shadows computation of Castlevania
if (PS_DST_FMT == FMT_16 && PS_DITHER != 3 && (PS_BLEND_MIX == 0 || PS_DITHER))
C = (float3)((int3)C & (int3)0xF8);
else if (PS_COLCLIP == 1 || PS_HDR == 1)
else if (PS_COLCLIP == 1 || PS_COLCLIP_HW == 1)
C = (float3)((int3)C & (int3)0xFF);
}
else if (PS_DST_FMT == FMT_16 && PS_DITHER != 3 && PS_BLEND_MIX == 0 && PS_BLEND_HW == 0)
@@ -898,7 +898,7 @@ void ps_blend(inout float4 Color, inout float4 As_rgba, float2 pos_xy)
}
float Ad = PS_RTA_CORRECTION ? trunc(RT.a * 128.0f + 0.1f) / 128.0f : trunc(RT.a * 255.0f + 0.1f) / 128.0f;
float color_multi = PS_HDR ? 65535.0f : 255.0f;
float color_multi = PS_COLCLIP_HW ? 65535.0f : 255.0f;
float3 Cd = trunc(RT.rgb * color_multi + 0.1f);
float3 Cs = Color.rgb;
@@ -1086,7 +1086,7 @@ PS_OUTPUT ps_main(PS_INPUT input)
if (PS_SHUFFLE)
{
if (!PS_SHUFFLE_SAME && !PS_READ16_SRC)
if (!PS_SHUFFLE_SAME && !PS_READ16_SRC && !(PS_PROCESS_BA == SHUFFLE_READWRITE && PS_PROCESS_RG == SHUFFLE_READWRITE))
{
uint4 denorm_c_after = uint4(C);
if (PS_PROCESS_BA & SHUFFLE_READ)
@@ -1127,11 +1127,8 @@ PS_OUTPUT ps_main(PS_INPUT input)
{
if (PS_PROCESS_BA == SHUFFLE_READWRITE && PS_PROCESS_RG == SHUFFLE_READWRITE)
{
C.rb = C.br;
float g_temp = C.g;
C.g = C.a;
C.a = g_temp;
C.br = C.rb;
C.ag = C.ga;
}
else if(PS_PROCESS_BA & SHUFFLE_READ)
{
@@ -1160,7 +1157,7 @@ PS_OUTPUT ps_main(PS_INPUT input)
#if !PS_NO_COLOR
output.c0.a = PS_RTA_CORRECTION ? C.a / 128.0f : C.a / 255.0f;
output.c0.rgb = PS_HDR ? float3(C.rgb / 65535.0f) : C.rgb / 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

View File

@@ -348,16 +348,16 @@ void ps_rta_decorrection()
}
#endif
#ifdef ps_hdr_init
void ps_hdr_init()
#ifdef ps_colclip_init
void ps_colclip_init()
{
vec4 value = sample_c();
SV_Target0 = vec4(round(value.rgb * 255.0f) / 65535.0f, value.a);
}
#endif
#ifdef ps_hdr_resolve
void ps_hdr_resolve()
#ifdef ps_colclip_resolve
void ps_colclip_resolve()
{
vec4 value = sample_c();
SV_Target0 = vec4(vec3(uvec3(value.rgb * 65535.0f) & 255u) / 255.0f, value.a);

View File

@@ -679,7 +679,7 @@ vec4 ps_color()
vec4 T = sample_color(st);
#endif
#if PS_SHUFFLE && !PS_READ16_SRC && !PS_SHUFFLE_SAME
#if PS_SHUFFLE && !PS_READ16_SRC && !PS_SHUFFLE_SAME && !(PS_PROCESS_BA == SHUFFLE_READWRITE && PS_PROCESS_RG == SHUFFLE_READWRITE)
uvec4 denorm_c_before = uvec4(T);
#if (PS_PROCESS_BA & SHUFFLE_READ)
T.r = float((denorm_c_before.b << 3) & 0xF8u);
@@ -707,7 +707,7 @@ void ps_fbmask(inout vec4 C)
{
// FIXME do I need special case for 16 bits
#if PS_FBMASK
#if PS_HDR == 1
#if PS_COLCLIP_HW == 1
vec4 RT = trunc(sample_from_rt() * 65535.0f);
#else
vec4 RT = trunc(sample_from_rt() * 255.0f + 0.1f);
@@ -757,7 +757,7 @@ void ps_color_clamp_wrap(inout vec3 C)
#endif
// Correct the Color value based on the output format
#if PS_COLCLIP == 0 && PS_HDR == 0
#if PS_COLCLIP == 0 && PS_COLCLIP_HW == 0
// Standard Clamp
C = clamp(C, vec3(0.0f), vec3(255.0f));
#endif
@@ -771,7 +771,7 @@ void ps_color_clamp_wrap(inout vec3 C)
#if PS_DST_FMT == FMT_16 && PS_DITHER < 3 && (PS_BLEND_MIX == 0 || PS_DITHER)
// In 16 bits format, only 5 bits of colors are used. It impacts shadows computation of Castlevania
C = vec3(ivec3(C) & ivec3(0xF8));
#elif PS_COLCLIP == 1 || PS_HDR == 1
#elif PS_COLCLIP == 1 || PS_COLCLIP_HW == 1
C = vec3(ivec3(C) & ivec3(0xFF));
#endif
@@ -828,7 +828,7 @@ float As = As_rgba.a;
#endif
// Let the compiler do its jobs !
#if PS_HDR == 1
#if PS_COLCLIP_HW == 1
vec3 Cd = trunc(RT.rgb * 65535.0f);
#else
vec3 Cd = trunc(RT.rgb * 255.0f + 0.1f);
@@ -1064,7 +1064,7 @@ void ps_main()
#if PS_SHUFFLE
#if !PS_READ16_SRC && !PS_SHUFFLE_SAME
#if !PS_READ16_SRC && !PS_SHUFFLE_SAME && !(PS_PROCESS_BA == SHUFFLE_READWRITE && PS_PROCESS_RG == SHUFFLE_READWRITE)
uvec4 denorm_c_after = uvec4(C);
#if (PS_PROCESS_BA & SHUFFLE_READ)
C.b = float(((denorm_c_after.r >> 3) & 0x1Fu) | ((denorm_c_after.g << 2) & 0xE0u));
@@ -1095,11 +1095,8 @@ void ps_main()
C.ga = vec2(float((denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.x & 0x80u)));
#elif PS_SHUFFLE_ACROSS
#if(PS_PROCESS_BA == SHUFFLE_READWRITE && PS_PROCESS_RG == SHUFFLE_READWRITE)
C.rb = C.br;
float g_temp = C.g;
C.g = C.a;
C.a = g_temp;
C.br = C.rb;
C.ag = C.ga;
#elif(PS_PROCESS_BA & SHUFFLE_READ)
C.rb = C.bb;
C.ga = C.aa;
@@ -1128,7 +1125,7 @@ void ps_main()
#else
SV_Target0.a = C.a / 255.0f;
#endif
#if PS_HDR == 1
#if PS_COLCLIP_HW == 1
SV_Target0.rgb = vec3(C.rgb / 65535.0f);
#else
SV_Target0.rgb = C.rgb / 255.0f;

View File

@@ -148,16 +148,16 @@ void ps_rta_decorrection()
}
#endif
#ifdef ps_hdr_init
void ps_hdr_init()
#ifdef ps_colclip_init
void ps_colclip_init()
{
vec4 value = sample_c(v_tex);
o_col0 = vec4(roundEven(value.rgb * 255.0f) / 65535.0f, value.a);
}
#endif
#ifdef ps_hdr_resolve
void ps_hdr_resolve()
#ifdef ps_colclip_resolve
void ps_colclip_resolve()
{
vec4 value = sample_c(v_tex);
o_col0 = vec4(vec3(uvec3(value.rgb * 65535.5f) & 255u) / 255.0f, value.a);

View File

@@ -281,7 +281,7 @@ void main()
#define PS_CHANNEL_FETCH 0
#define PS_TALES_OF_ABYSS_HLE 0
#define PS_URBAN_CHAOS_HLE 0
#define PS_HDR 0
#define PS_COLCLIP_HW 0
#define PS_COLCLIP 0
#define PS_BLEND_A 0
#define PS_BLEND_B 0
@@ -946,7 +946,7 @@ vec4 ps_color()
vec4 T = sample_color(st);
#endif
#if PS_SHUFFLE && !PS_READ16_SRC && !PS_SHUFFLE_SAME
#if PS_SHUFFLE && !PS_READ16_SRC && !PS_SHUFFLE_SAME && !(PS_PROCESS_BA == SHUFFLE_READWRITE && PS_PROCESS_RG == SHUFFLE_READWRITE)
uvec4 denorm_c_before = uvec4(T);
#if (PS_PROCESS_BA & SHUFFLE_READ)
T.r = float((denorm_c_before.b << 3) & 0xF8u);
@@ -974,7 +974,7 @@ void ps_fbmask(inout vec4 C)
{
#if PS_FBMASK
#if PS_HDR == 1
#if PS_COLCLIP_HW == 1
vec4 RT = trunc(sample_from_rt() * 65535.0f);
#else
vec4 RT = trunc(sample_from_rt() * 255.0f + 0.1f);
@@ -1027,7 +1027,7 @@ void ps_color_clamp_wrap(inout vec3 C)
#endif
// Correct the Color value based on the output format
#if PS_COLCLIP == 0 && PS_HDR == 0
#if PS_COLCLIP == 0 && PS_COLCLIP_HW == 0
// Standard Clamp
C = clamp(C, vec3(0.0f), vec3(255.0f));
#endif
@@ -1041,7 +1041,7 @@ void ps_color_clamp_wrap(inout vec3 C)
#if PS_DST_FMT == FMT_16 && PS_DITHER != 3 && (PS_BLEND_MIX == 0 || PS_DITHER > 0)
// In 16 bits format, only 5 bits of colors are used. It impacts shadows computation of Castlevania
C = vec3(ivec3(C) & ivec3(0xF8));
#elif PS_COLCLIP == 1 || PS_HDR == 1
#elif PS_COLCLIP == 1 || PS_COLCLIP_HW == 1
C = vec3(ivec3(C) & ivec3(0xFF));
#endif
@@ -1098,7 +1098,7 @@ void ps_blend(inout vec4 Color, inout vec4 As_rgba)
#endif
// Let the compiler do its jobs !
#if PS_HDR == 1
#if PS_COLCLIP_HW == 1
vec3 Cd = trunc(RT.rgb * 65535.0f);
#else
vec3 Cd = trunc(RT.rgb * 255.0f + 0.1f);
@@ -1332,7 +1332,7 @@ void main()
ps_blend(C, alpha_blend);
#if PS_SHUFFLE
#if !PS_READ16_SRC && !PS_SHUFFLE_SAME
#if !PS_READ16_SRC && !PS_SHUFFLE_SAME && !(PS_PROCESS_BA == SHUFFLE_READWRITE && PS_PROCESS_RG == SHUFFLE_READWRITE)
uvec4 denorm_c_after = uvec4(C);
#if (PS_PROCESS_BA & SHUFFLE_READ)
C.b = float(((denorm_c_after.r >> 3) & 0x1Fu) | ((denorm_c_after.g << 2) & 0xE0u));
@@ -1343,8 +1343,6 @@ void main()
#endif
#endif
// Special case for 32bit input and 16bit output, shuffle used by The Godfather
#if PS_SHUFFLE_SAME
#if (PS_PROCESS_BA & SHUFFLE_READ)
@@ -1362,11 +1360,8 @@ void main()
// Write RB part. Mask will take care of the correct destination
#elif PS_SHUFFLE_ACROSS
#if(PS_PROCESS_BA == SHUFFLE_READWRITE && PS_PROCESS_RG == SHUFFLE_READWRITE)
C.rb = C.br;
float g_temp = C.g;
C.g = C.a;
C.a = g_temp;
C.br = C.rb;
C.ag = C.ga;
#elif(PS_PROCESS_BA & SHUFFLE_READ)
C.rb = C.bb;
C.ga = C.aa;
@@ -1395,7 +1390,7 @@ void main()
#else
o_col0.a = C.a / 255.0f;
#endif
#if PS_HDR == 1
#if PS_COLCLIP_HW == 1
o_col0.rgb = vec3(C.rgb / 65535.0f);
#else
o_col0.rgb = C.rgb / 255.0f;

View File

@@ -342,7 +342,7 @@ bool Log::SetFileOutputLevel(LOGLEVEL level, std::string path)
if (!s_file_handle || s_file_path != path)
{
s_file_handle.reset();
s_file_handle = FileSystem::OpenManagedCFile(path.c_str(), "wb");
s_file_handle = FileSystem::OpenManagedSharedCFile(path.c_str(), "wb", FileSystem::FileShareMode::DenyWrite);
if (s_file_handle)
{
s_file_path = std::move(path);

View File

@@ -181,6 +181,10 @@ private:
extern u64 GetTickFrequency();
extern u64 GetCPUTicks();
extern u64 GetPhysicalMemory();
#ifdef _WIN32
// TODO: Someone do linux/mac.
extern u64 GetAvailablePhysicalMemory();
#endif
/// Spin for a short period of time (call while spinning waiting for a lock)
/// Returns the approximate number of ns that passed
extern u32 ShortSpin();

View File

@@ -57,6 +57,14 @@ u64 GetPhysicalMemory()
return status.ullTotalPhys;
}
u64 GetAvailablePhysicalMemory()
{
MEMORYSTATUSEX status;
status.dwLength = sizeof(status);
GlobalMemoryStatusEx(&status);
return status.ullAvailPhys;
}
// Calculates the Windows OS Version and processor architecture, and returns it as a
// human-readable string. :)
std::string GetOSVersionString()

View File

@@ -28,7 +28,7 @@ const char* QtHost::GetDefaultThemeName()
#ifdef __APPLE__
return "";
#else
return "darkfusion";
return "darkfusionblue";
#endif
}

View File

@@ -2392,7 +2392,12 @@ Leaderboard Position: {1} of {2}</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/CDVD/ThreadedFileReader.cpp" line="276"/>
<location filename="../../pcsx2/CDVD/ThreadedFileReader.cpp" line="279"/>
<source>Not enough free memory available for precaching, ({}GB) required.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/CDVD/ThreadedFileReader.cpp" line="292"/>
<source>Required memory ({}GB) is the above the maximum allowed ({}GB).</source>
<translation type="unfinished"></translation>
</message>
@@ -10789,77 +10794,77 @@ Do you want to load this save and continue?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="537"/>
<location filename="../../pcsx2/GS/GS.cpp" line="546"/>
<source>Failed to change window after update. The log may contain more information.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="1065"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1074"/>
<source>Upscale multiplier set to {}x.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/Renderers/Common/GSRenderer.cpp" line="441"/>
<location filename="../../pcsx2/GS/Renderers/Common/GSRenderer.cpp" line="432"/>
<source>Saving screenshot to &apos;{}&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/Renderers/Common/GSRenderer.cpp" line="453"/>
<location filename="../../pcsx2/GS/Renderers/Common/GSRenderer.cpp" line="444"/>
<source>Saved screenshot to &apos;{}&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/Renderers/Common/GSRenderer.cpp" line="460"/>
<location filename="../../pcsx2/GS/Renderers/Common/GSRenderer.cpp" line="451"/>
<source>Failed to save screenshot to &apos;{}&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/Renderers/Common/GSRenderer.cpp" line="531"/>
<location filename="../../pcsx2/GS/Renderers/Common/GSRenderer.cpp" line="522"/>
<source>Host GPU device encountered an error and was recovered. This may have broken rendering.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/Renderers/Common/GSRenderer.cpp" line="636"/>
<location filename="../../pcsx2/GS/Renderers/Common/GSRenderer.cpp" line="627"/>
<source>CAS is not available, your graphics driver does not support the required functionality.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/Renderers/Common/GSRenderer.cpp" line="691"/>
<location filename="../../pcsx2/GS/Renderers/Common/GSRenderer.cpp" line="682"/>
<source>with no compression</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/Renderers/Common/GSRenderer.cpp" line="698"/>
<location filename="../../pcsx2/GS/Renderers/Common/GSRenderer.cpp" line="689"/>
<source>with LZMA compression</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/Renderers/Common/GSRenderer.cpp" line="705"/>
<location filename="../../pcsx2/GS/Renderers/Common/GSRenderer.cpp" line="696"/>
<source>with Zstandard compression</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/Renderers/Common/GSRenderer.cpp" line="711"/>
<location filename="../../pcsx2/GS/Renderers/Common/GSRenderer.cpp" line="702"/>
<source>Saving {0} GS dump {1} to &apos;{2}&apos;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/Renderers/Common/GSRenderer.cpp" line="712"/>
<location filename="../../pcsx2/GS/Renderers/Common/GSRenderer.cpp" line="703"/>
<source>single frame</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/Renderers/Common/GSRenderer.cpp" line="712"/>
<location filename="../../pcsx2/GS/Renderers/Common/GSRenderer.cpp" line="703"/>
<source>multi-frame</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/Renderers/Common/GSRenderer.cpp" line="732"/>
<location filename="../../pcsx2/GS/Renderers/Common/GSRenderer.cpp" line="723"/>
<source>Failed to render/download screenshot.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/Renderers/Common/GSRenderer.cpp" line="743"/>
<location filename="../../pcsx2/GS/Renderers/Common/GSRenderer.cpp" line="734"/>
<source>Saved GS dump to &apos;{}&apos;.</source>
<translation type="unfinished"></translation>
</message>
@@ -11787,7 +11792,7 @@ Scanning recursively takes more time, but will identify files in subdirectories.
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="51"/>
<location filename="../Settings/GameSummaryWidget.ui" line="366"/>
<location filename="../Settings/GameSummaryWidget.ui" line="363"/>
<source>Restore</source>
<translation type="unfinished"></translation>
</message>
@@ -11833,237 +11838,237 @@ Scanning recursively takes more time, but will identify files in subdirectories.
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="173"/>
<location filename="../Settings/GameSummaryWidget.ui" line="172"/>
<source>PS1 Disc</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="182"/>
<location filename="../Settings/GameSummaryWidget.ui" line="180"/>
<source>ELF (PS2 Executable)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="194"/>
<location filename="../Settings/GameSummaryWidget.ui" line="191"/>
<source>Region:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="210"/>
<location filename="../Settings/GameSummaryWidget.ui" line="207"/>
<source>NTSC-B (Brazil)</source>
<extracomment>Leave the code as-is, translate the country&apos;s name.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="215"/>
<location filename="../Settings/GameSummaryWidget.ui" line="212"/>
<source>NTSC-C (China)</source>
<extracomment>Leave the code as-is, translate the country&apos;s name.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="220"/>
<location filename="../Settings/GameSummaryWidget.ui" line="217"/>
<source>NTSC-HK (Hong Kong)</source>
<extracomment>Leave the code as-is, translate the country&apos;s name.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="225"/>
<location filename="../Settings/GameSummaryWidget.ui" line="222"/>
<source>NTSC-J (Japan)</source>
<extracomment>Leave the code as-is, translate the country&apos;s name.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="230"/>
<location filename="../Settings/GameSummaryWidget.ui" line="227"/>
<source>NTSC-K (Korea)</source>
<extracomment>Leave the code as-is, translate the country&apos;s name.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="235"/>
<location filename="../Settings/GameSummaryWidget.ui" line="232"/>
<source>NTSC-T (Taiwan)</source>
<extracomment>Leave the code as-is, translate the country&apos;s name.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="240"/>
<location filename="../Settings/GameSummaryWidget.ui" line="237"/>
<source>NTSC-U (US)</source>
<extracomment>Leave the code as-is, translate the country&apos;s name.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="245"/>
<location filename="../Settings/GameSummaryWidget.ui" line="242"/>
<source>Other</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="250"/>
<location filename="../Settings/GameSummaryWidget.ui" line="247"/>
<source>PAL-A (Australia)</source>
<extracomment>Leave the code as-is, translate the country&apos;s name.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="255"/>
<location filename="../Settings/GameSummaryWidget.ui" line="252"/>
<source>PAL-AF (South Africa)</source>
<extracomment>Leave the code as-is, translate the country&apos;s name.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="260"/>
<location filename="../Settings/GameSummaryWidget.ui" line="257"/>
<source>PAL-AU (Austria)</source>
<extracomment>Leave the code as-is, translate the country&apos;s name.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="265"/>
<location filename="../Settings/GameSummaryWidget.ui" line="262"/>
<source>PAL-BE (Belgium)</source>
<extracomment>Leave the code as-is, translate the country&apos;s name.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="270"/>
<location filename="../Settings/GameSummaryWidget.ui" line="267"/>
<source>PAL-E (Europe/Australia)</source>
<extracomment>Leave the code as-is, translate the country&apos;s name.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="275"/>
<location filename="../Settings/GameSummaryWidget.ui" line="272"/>
<source>PAL-F (France)</source>
<extracomment>Leave the code as-is, translate the country&apos;s name.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="280"/>
<location filename="../Settings/GameSummaryWidget.ui" line="277"/>
<source>PAL-FI (Finland)</source>
<extracomment>Leave the code as-is, translate the country&apos;s name.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="285"/>
<location filename="../Settings/GameSummaryWidget.ui" line="282"/>
<source>PAL-G (Germany)</source>
<extracomment>Leave the code as-is, translate the country&apos;s name.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="290"/>
<location filename="../Settings/GameSummaryWidget.ui" line="287"/>
<source>PAL-GR (Greece)</source>
<extracomment>Leave the code as-is, translate the country&apos;s name.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="295"/>
<location filename="../Settings/GameSummaryWidget.ui" line="292"/>
<source>PAL-I (Italy)</source>
<extracomment>Leave the code as-is, translate the country&apos;s name.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="300"/>
<location filename="../Settings/GameSummaryWidget.ui" line="297"/>
<source>PAL-IN (India)</source>
<extracomment>Leave the code as-is, translate the country&apos;s name.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="305"/>
<location filename="../Settings/GameSummaryWidget.ui" line="302"/>
<source>PAL-M (Europe/Australia)</source>
<extracomment>Leave the code as-is, translate the country&apos;s name.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="310"/>
<location filename="../Settings/GameSummaryWidget.ui" line="307"/>
<source>PAL-NL (Netherlands)</source>
<extracomment>Leave the code as-is, translate the country&apos;s name.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="315"/>
<location filename="../Settings/GameSummaryWidget.ui" line="312"/>
<source>PAL-NO (Norway)</source>
<extracomment>Leave the code as-is, translate the country&apos;s name.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="320"/>
<location filename="../Settings/GameSummaryWidget.ui" line="317"/>
<source>PAL-P (Portugal)</source>
<extracomment>Leave the code as-is, translate the country&apos;s name.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="325"/>
<location filename="../Settings/GameSummaryWidget.ui" line="322"/>
<source>PAL-PL (Poland)</source>
<extracomment>Leave the code as-is, translate the country&apos;s name.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="330"/>
<location filename="../Settings/GameSummaryWidget.ui" line="327"/>
<source>PAL-R (Russia)</source>
<extracomment>Leave the code as-is, translate the country&apos;s name.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="335"/>
<location filename="../Settings/GameSummaryWidget.ui" line="332"/>
<source>PAL-S (Spain)</source>
<extracomment>Leave the code as-is, translate the country&apos;s name.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="340"/>
<location filename="../Settings/GameSummaryWidget.ui" line="337"/>
<source>PAL-SC (Scandinavia)</source>
<extracomment>Leave the code as-is, translate the country&apos;s name.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="345"/>
<location filename="../Settings/GameSummaryWidget.ui" line="342"/>
<source>PAL-SW (Sweden)</source>
<extracomment>Leave the code as-is, translate the country&apos;s name.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="350"/>
<location filename="../Settings/GameSummaryWidget.ui" line="347"/>
<source>PAL-SWI (Switzerland)</source>
<extracomment>Leave the code as-is, translate the country&apos;s name.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="355"/>
<location filename="../Settings/GameSummaryWidget.ui" line="352"/>
<source>PAL-UK (United Kingdom)</source>
<extracomment>Leave the code as-is, translate the country&apos;s name.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="375"/>
<location filename="../Settings/GameSummaryWidget.ui" line="372"/>
<source>Compatibility:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="389"/>
<location filename="../Settings/GameSummaryWidget.ui" line="386"/>
<source>Input Profile:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="403"/>
<location filename="../Settings/GameSummaryWidget.ui" line="400"/>
<source>Shared</source>
<extracomment>Refers to the shared settings profile.</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="411"/>
<location filename="../Settings/GameSummaryWidget.ui" line="408"/>
<source>Disc Path:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="423"/>
<location filename="../Settings/GameSummaryWidget.ui" line="420"/>
<source>Browse...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="430"/>
<location filename="../Settings/GameSummaryWidget.ui" line="427"/>
<source>Clear</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="482"/>
<location filename="../Settings/GameSummaryWidget.ui" line="479"/>
<source>Verify</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/GameSummaryWidget.ui" line="525"/>
<location filename="../Settings/GameSummaryWidget.ui" line="522"/>
<source>Search on Redump.org...</source>
<translation type="unfinished"></translation>
</message>
@@ -14779,190 +14784,190 @@ Swap chain: see Microsoft&apos;s Terminology Portal.</extracomment>
<context>
<name>Hotkeys</name>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="1086"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1094"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1114"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1121"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1095"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1103"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1123"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1130"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1136"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1142"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1148"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1153"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1166"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1179"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1207"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1219"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1233"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1139"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1145"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1151"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1157"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1162"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1175"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1188"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1216"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1228"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1242"/>
<source>Graphics</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="1087"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1096"/>
<source>Save Screenshot</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="1094"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1103"/>
<source>Toggle Video Capture</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="1114"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1123"/>
<source>Save Single Frame GS Dump</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="1121"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1130"/>
<source>Save Multi Frame GS Dump</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="1131"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1140"/>
<source>Toggle Software Rendering</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="1137"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1146"/>
<source>Increase Upscale Multiplier</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="1143"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1152"/>
<source>Decrease Upscale Multiplier</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="1148"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1157"/>
<source>Toggle On-Screen Display</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="1153"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1162"/>
<source>Cycle Aspect Ratio</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="1162"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1171"/>
<source>Aspect ratio set to &apos;{}&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="1166"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1175"/>
<source>Toggle Hardware Mipmapping</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="1173"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1182"/>
<source>Hardware mipmapping is now enabled.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="1174"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1183"/>
<source>Hardware mipmapping is now disabled.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="1179"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1188"/>
<source>Cycle Deinterlace Mode</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="1185"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1194"/>
<source>Automatic</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="1186"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1195"/>
<source>Off</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="1187"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1196"/>
<source>Weave (Top Field First)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="1188"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1197"/>
<source>Weave (Bottom Field First)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="1189"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1198"/>
<source>Bob (Top Field First)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="1190"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1199"/>
<source>Bob (Bottom Field First)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="1191"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1200"/>
<source>Blend (Top Field First)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="1192"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1201"/>
<source>Blend (Bottom Field First)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="1193"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1202"/>
<source>Adaptive (Top Field First)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="1194"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1203"/>
<source>Adaptive (Bottom Field First)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="1201"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1210"/>
<source>Deinterlace mode set to &apos;{}&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="1207"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1216"/>
<source>Toggle Texture Dumping</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="1213"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1222"/>
<source>Texture dumping is now enabled.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="1214"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1223"/>
<source>Texture dumping is now disabled.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="1220"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1229"/>
<source>Toggle Texture Replacements</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="1227"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1236"/>
<source>Texture replacements are now enabled.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="1228"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1237"/>
<source>Texture replacements are now disabled.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="1234"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1243"/>
<source>Reload Texture Replacements</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="1241"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1250"/>
<source>Texture replacements are not enabled.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/GS.cpp" line="1246"/>
<location filename="../../pcsx2/GS/GS.cpp" line="1255"/>
<source>Reloading texture replacements...</source>
<translation type="unfinished"></translation>
</message>

View File

@@ -266,10 +266,26 @@ bool ThreadedFileReader::Precache2(ProgressCallback* progress, Error* error)
bool ThreadedFileReader::CheckAvailableMemoryForPrecaching(u64 required_size, Error* error)
{
#ifdef _WIN32
// We want to check available physical memory instead of total.
const u64 memory_available = GetAvailablePhysicalMemory();
// Reserve 2GB of available memory for headroom.
constexpr u64 memory_reserve = 2147483648;
const u64 max_precache_size = std::max(0LL, static_cast<s64>(memory_available - memory_reserve));
if (required_size > max_precache_size)
{
Error::SetStringFmt(error,
TRANSLATE_FS("CDVD", "Not enough free memory available for precaching, ({}GB) required."),
(required_size + memory_reserve) / _1gb);
return false;
}
#else
// Don't allow precaching to use more than 50% of system memory.
// Hopefully nobody's running 2-4GB potatoes anymore....
const u64 memory_size = GetPhysicalMemory();
const u64 max_precache_size = memory_size / 2;
if (required_size > max_precache_size)
{
Error::SetStringFmt(error,
@@ -277,6 +293,7 @@ bool ThreadedFileReader::CheckAvailableMemoryForPrecaching(u64 required_size, Er
required_size / _1gb, max_precache_size / _1gb);
return false;
}
#endif
return true;
}

View File

@@ -309,6 +309,7 @@ set(pcsx2DEV9Sources
DEV9/Sessions/TCP_Session/TCP_Session.cpp
DEV9/Sessions/TCP_Session/TCP_Session_In.cpp
DEV9/Sessions/TCP_Session/TCP_Session_Out.cpp
DEV9/Sessions/UDP_Session/UDP_Common.cpp
DEV9/Sessions/UDP_Session/UDP_FixedPort.cpp
DEV9/Sessions/UDP_Session/UDP_Session.cpp
DEV9/smap.cpp
@@ -354,6 +355,7 @@ set(pcsx2DEV9Headers
DEV9/Sessions/BaseSession.h
DEV9/Sessions/ICMP_Session/ICMP_Session.h
DEV9/Sessions/TCP_Session/TCP_Session.h
DEV9/Sessions/UDP_Session/UDP_Common.h
DEV9/Sessions/UDP_Session/UDP_FixedPort.h
DEV9/Sessions/UDP_Session/UDP_BaseSession.h
DEV9/Sessions/UDP_Session/UDP_Session.h

View File

@@ -16,5 +16,6 @@ namespace Sessions
}
virtual bool WillRecive(PacketReader::IP::IP_Address parDestIP) = 0;
virtual void ForceClose() { RaiseEventConnectionClosed(); };
};
} // namespace Sessions

View File

@@ -0,0 +1,193 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#include <bit>
#include "common/Console.h"
#ifdef __POSIX__
#define SOCKET_ERROR -1
#define INVALID_SOCKET -1
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <netinet/in.h>
#endif
#ifdef _WIN32
#include "common/RedtapeWindows.h"
#include <winsock2.h>
#else
#include <unistd.h>
#endif
#include "UDP_Common.h"
#include "DEV9/PacketReader/IP/UDP/UDP_Packet.h"
using namespace PacketReader;
using namespace PacketReader::IP;
using namespace PacketReader::IP::UDP;
namespace Sessions::UDP_Common
{
#ifdef _WIN32
SOCKET CreateSocket(IP_Address adapterIP, std::optional<u16> port)
{
SOCKET client = INVALID_SOCKET;
#elif defined(__POSIX__)
int CreateSocket(IP_Address adapterIP, std::optional<u16> port)
{
int client = INVALID_SOCKET;
#endif
int ret;
client = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (client == INVALID_SOCKET)
{
Console.Error("DEV9: UDP: Failed to open socket. Error: %d",
#ifdef _WIN32
WSAGetLastError());
#elif defined(__POSIX__)
errno);
#endif
return INVALID_SOCKET;
}
constexpr int reuseAddress = true; // BOOL on Windows
ret = setsockopt(client, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char*>(&reuseAddress), sizeof(reuseAddress));
if (ret == SOCKET_ERROR)
Console.Error("DEV9: UDP: Failed to set SO_REUSEADDR. Error: %d",
#ifdef _WIN32
WSAGetLastError());
#elif defined(__POSIX__)
errno);
#endif
if (port.has_value() || adapterIP != IP_Address{{{0, 0, 0, 0}}})
{
sockaddr_in endpoint{};
endpoint.sin_family = AF_INET;
endpoint.sin_addr = std::bit_cast<in_addr>(adapterIP);
if (port.has_value())
endpoint.sin_port = htons(port.value());
ret = bind(client, reinterpret_cast<const sockaddr*>(&endpoint), sizeof(endpoint));
if (ret == SOCKET_ERROR)
Console.Error("DEV9: UDP: Failed to bind socket. Error: %d",
#ifdef _WIN32
WSAGetLastError());
#elif defined(__POSIX__)
errno);
#endif
}
return client;
}
#ifdef _WIN32
std::tuple<std::optional<ReceivedPayload>, bool> RecvFrom(SOCKET client, u16 port)
#elif defined(__POSIX__)
std::tuple<std::optional<ReceivedPayload>, bool> RecvFrom(int client, u16 port)
#endif
{
int ret;
fd_set sReady;
fd_set sExcept;
// not const Linux
timeval nowait{};
FD_ZERO(&sReady);
FD_ZERO(&sExcept);
FD_SET(client, &sReady);
FD_SET(client, &sExcept);
ret = select(client + 1, &sReady, nullptr, &sExcept, &nowait);
if (ret == SOCKET_ERROR)
{
Console.Error("DEV9: UDP: select failed. Error code: %d",
#ifdef _WIN32
WSAGetLastError());
#elif defined(__POSIX__)
errno);
#endif
return {std::nullopt, true};
}
else if (FD_ISSET(client, &sExcept))
{
#ifdef _WIN32
int len = sizeof(ret);
if (getsockopt(client, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&ret), &len) < 0)
{
Console.Error("DEV9: UDP: Unknown UDP connection error (getsockopt error: %d)", WSAGetLastError());
}
#elif defined(__POSIX__)
socklen_t len = sizeof(ret);
if (getsockopt(client, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&ret), &len) < 0)
Console.Error("DEV9: UDP: Unknown UDP connection error (getsockopt error: %d)", errno);
#endif
else
Console.Error("DEV9: UDP: Socket error: %d", ret);
// All socket errors assumed fatal.
return {std::nullopt, false};
}
else if (FD_ISSET(client, &sReady))
{
unsigned long available = 0;
PayloadData* recived = nullptr;
std::unique_ptr<u8[]> buffer;
sockaddr_in endpoint{};
// FIONREAD returns total size of all available messages
// however, we only read one message at a time
#ifdef _WIN32
ret = ioctlsocket(client, FIONREAD, &available);
#elif defined(__POSIX__)
ret = ioctl(client, FIONREAD, &available);
#endif
if (ret != SOCKET_ERROR)
{
buffer = std::make_unique<u8[]>(available);
#ifdef _WIN32
int fromlen = sizeof(endpoint);
#elif defined(__POSIX__)
socklen_t fromlen = sizeof(endpoint);
#endif
ret = recvfrom(client, reinterpret_cast<char*>(buffer.get()), available, 0, reinterpret_cast<sockaddr*>(&endpoint), &fromlen);
}
if (ret == SOCKET_ERROR)
{
#ifdef _WIN32
ret = WSAGetLastError();
#elif defined(__POSIX__)
ret = errno;
#endif
Console.Error("DEV9: UDP: recvfrom error: %d", ret);
/*
* We can receive an ICMP Port Unreacable error as a WSAECONNRESET/ECONNREFUSED error
* Ignore the error, recv will be retried next loop
*/
return {std::nullopt,
#ifdef _WIN32
ret == WSAECONNRESET};
#elif defined(__POSIX__)
ret == ECONNREFUSED};
#endif
}
recived = new PayloadData(ret);
memcpy(recived->data.get(), buffer.get(), ret);
std::unique_ptr<UDP_Packet> iRet = std::make_unique<UDP_Packet>(recived);
iRet->destinationPort = port;
iRet->sourcePort = ntohs(endpoint.sin_port);
return {ReceivedPayload{std::bit_cast<IP_Address>(endpoint.sin_addr), std::move(iRet)}, true};
}
return {std::nullopt, true};
}
} // namespace Sessions::UDP_Common

View File

@@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#pragma once
#include <tuple>
#ifdef _WIN32
#include <winsock2.h>
#elif defined(__POSIX__)
#include <sys/socket.h>
#endif
#include "common/Pcsx2Defs.h"
#include "DEV9/Sessions/BaseSession.h"
namespace Sessions::UDP_Common
{
// Binds the socket when provided with an IP
#ifdef _WIN32
SOCKET CreateSocket(PacketReader::IP::IP_Address adapterIP, std::optional<u16> port);
#elif defined(__POSIX__)
int CreateSocket(PacketReader::IP::IP_Address adapterIP, std::optional<u16> port);
#endif
// Receives from the client and packages the data into ReceivedPayload
// port is the local port to be written to the UDP header in ReceivedPayload
#ifdef _WIN32
std::tuple<std::optional<ReceivedPayload>, bool> RecvFrom(SOCKET client, u16 port);
#elif defined(__POSIX__)
std::tuple<std::optional<ReceivedPayload>, bool> RecvFrom(int client, u16 port);
#endif
} // namespace Sessions::UDP_Common

View File

@@ -21,6 +21,7 @@
#include <unistd.h>
#endif
#include "UDP_Common.h"
#include "UDP_FixedPort.h"
#include "DEV9/PacketReader/IP/UDP/UDP_Packet.h"
@@ -51,33 +52,15 @@ namespace Sessions
void UDP_FixedPort::Init()
{
int ret;
client = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
client = UDP_Common::CreateSocket(adapterIP, port);
if (client == INVALID_SOCKET)
{
Console.Error("DEV9: UDP: Failed to open socket. Error: %d",
#ifdef _WIN32
WSAGetLastError());
#elif defined(__POSIX__)
errno);
#endif
RaiseEventConnectionClosed();
return;
}
constexpr int reuseAddress = true; // BOOL on Windows
ret = setsockopt(client, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char*>(&reuseAddress), sizeof(reuseAddress));
if (ret == SOCKET_ERROR)
Console.Error("DEV9: UDP: Failed to set SO_REUSEADDR. Error: %d",
#ifdef _WIN32
WSAGetLastError());
#elif defined(__POSIX__)
errno);
#endif
constexpr int broadcastEnable = true; // BOOL on Windows
ret = setsockopt(client, SOL_SOCKET, SO_BROADCAST, reinterpret_cast<const char*>(&broadcastEnable), sizeof(broadcastEnable));
const int ret = setsockopt(client, SOL_SOCKET, SO_BROADCAST, reinterpret_cast<const char*>(&broadcastEnable), sizeof(broadcastEnable));
if (ret == SOCKET_ERROR)
Console.Error("DEV9: UDP: Failed to set SO_BROADCAST. Error: %d",
@@ -87,25 +70,6 @@ namespace Sessions
errno);
#endif
sockaddr_in endpoint{};
endpoint.sin_family = AF_INET;
endpoint.sin_addr = std::bit_cast<in_addr>(adapterIP);
endpoint.sin_port = htons(port);
ret = bind(client, reinterpret_cast<const sockaddr*>(&endpoint), sizeof(endpoint));
if (ret == SOCKET_ERROR)
{
Console.Error("DEV9: UDP: Failed to bind socket. Error: %d",
#ifdef _WIN32
WSAGetLastError());
#elif defined(__POSIX__)
errno);
#endif
RaiseEventConnectionClosed();
return;
}
open.store(true);
}
@@ -114,102 +78,45 @@ namespace Sessions
if (!open.load())
return std::nullopt;
int ret;
fd_set sReady;
fd_set sExcept;
std::optional<ReceivedPayload> ret;
bool success;
std::tie(ret, success) = UDP_Common::RecvFrom(client, port);
timeval nowait{};
FD_ZERO(&sReady);
FD_ZERO(&sExcept);
FD_SET(client, &sReady);
FD_SET(client, &sExcept);
ret = select(client + 1, &sReady, nullptr, &sExcept, &nowait);
bool hasData;
if (ret == SOCKET_ERROR)
if (!success)
{
hasData = false;
Console.Error("DEV9: UDP: select failed. Error code: %d",
#ifdef _WIN32
WSAGetLastError());
#elif defined(__POSIX__)
errno);
#endif
}
else if (FD_ISSET(client, &sExcept))
{
hasData = false;
int error = 0;
#ifdef _WIN32
int len = sizeof(error);
if (getsockopt(client, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&error), &len) < 0)
Console.Error("DEV9: UDP: Unknown UDP connection error (getsockopt error: %d)", WSAGetLastError());
#elif defined(__POSIX__)
socklen_t len = sizeof(error);
if (getsockopt(client, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&error), &len) < 0)
Console.Error("DEV9: UDP: Unknown UDP connection error (getsockopt error: %d)", errno);
#endif
else
Console.Error("DEV9: UDP: Recv error: %d", error);
}
else
hasData = FD_ISSET(client, &sReady);
if (hasData)
{
unsigned long available = 0;
PayloadData* recived = nullptr;
std::unique_ptr<u8[]> buffer;
sockaddr_in endpoint{};
// FIONREAD returns total size of all available messages
// however, we only read one message at a time
#ifdef _WIN32
ret = ioctlsocket(client, FIONREAD, &available);
#elif defined(__POSIX__)
ret = ioctl(client, FIONREAD, &available);
#endif
if (ret != SOCKET_ERROR)
// See Reset() for why we copy the vector.
std::vector<UDP_BaseSession*> connectionsCopy;
{
buffer = std::make_unique<u8[]>(available);
#ifdef _WIN32
int fromlen = sizeof(endpoint);
#elif defined(__POSIX__)
socklen_t fromlen = sizeof(endpoint);
#endif
ret = recvfrom(client, reinterpret_cast<char*>(buffer.get()), available, 0, reinterpret_cast<sockaddr*>(&endpoint), &fromlen);
std::lock_guard numberlock(connectionSentry);
open.store(false);
connectionsCopy = connections;
}
if (ret == SOCKET_ERROR)
if (connectionsCopy.size() == 0)
{
Console.Error("DEV9: UDP: UDP recv error: %d",
#ifdef _WIN32
WSAGetLastError());
#elif defined(__POSIX__)
errno);
#endif
// Can close immediately.
RaiseEventConnectionClosed();
return std::nullopt;
}
else
{
// Need to wait for child connections to close.
for (size_t i = 0; i < connectionsCopy.size(); i++)
connectionsCopy[i]->ForceClose();
recived = new PayloadData(ret);
memcpy(recived->data.get(), buffer.get(), ret);
std::unique_ptr<UDP_Packet> iRet = std::make_unique<UDP_Packet>(recived);
iRet->destinationPort = port;
iRet->sourcePort = ntohs(endpoint.sin_port);
IP_Address srvIP = std::bit_cast<IP_Address>(endpoint.sin_addr);
return std::nullopt;
}
}
else if (ret.has_value())
{
{
std::lock_guard numberlock(connectionSentry);
for (size_t i = 0; i < connections.size(); i++)
{
UDP_BaseSession* s = connections[i];
if (s->WillRecive(srvIP))
return ReceivedPayload{srvIP, std::move(iRet)};
if (s->WillRecive(ret.value().sourceIP))
return ret;
}
}
Console.Error("DEV9: UDP: Unexpected packet, dropping");
@@ -225,10 +132,12 @@ namespace Sessions
void UDP_FixedPort::Reset()
{
// Reseting a session may cause that session to close itself,
// when that happens, the connections vector gets modified via our close handler.
// Duplicate the vector to avoid iterating over a modified collection,
// this also avoids the issue of recursive locking when our close handler takes a lock.
/*
* Reseting a session may cause that session to close itself,
* when that happens, the connections vector gets modified via our close handler.
* Duplicate the vector to avoid iterating over a modified collection,
* this also avoids the issue of recursive locking when our close handler takes a lock.
*/
std::vector<UDP_BaseSession*> connectionsCopy;
{
std::lock_guard numberlock(connectionSentry);
@@ -241,16 +150,15 @@ namespace Sessions
UDP_Session* UDP_FixedPort::NewClientSession(ConnectionKey parNewKey, bool parIsBrodcast, bool parIsMulticast)
{
// Lock the whole function so we can't race between the open check and creating the session
std::lock_guard numberlock(connectionSentry);
if (!open.load())
return nullptr;
UDP_Session* s = new UDP_Session(parNewKey, adapterIP, parIsBrodcast, parIsMulticast, client);
s->AddConnectionClosedHandler([&](BaseSession* session) { HandleChildConnectionClosed(session); });
{
std::lock_guard numberlock(connectionSentry);
connections.push_back(s);
}
connections.push_back(s);
return s;
}
@@ -272,6 +180,8 @@ namespace Sessions
UDP_FixedPort::~UDP_FixedPort()
{
DevCon.WriteLn("DEV9: Socket: UDPFixedPort %d had %d child connections", port, connections.size());
open.store(false);
if (client != INVALID_SOCKET)
{

View File

@@ -20,6 +20,7 @@
#endif
#include "UDP_Session.h"
#include "UDP_Common.h"
#include "DEV9/PacketReader/IP/UDP/UDP_Packet.h"
using namespace PacketReader;
@@ -75,97 +76,17 @@ namespace Sessions
return std::nullopt;
}
int ret;
fd_set sReady;
fd_set sExcept;
std::optional<ReceivedPayload> ret;
bool success;
std::tie(ret, success) = UDP_Common::RecvFrom(client, srcPort);
timeval nowait{};
FD_ZERO(&sReady);
FD_ZERO(&sExcept);
FD_SET(client, &sReady);
FD_SET(client, &sExcept);
ret = select(client + 1, &sReady, nullptr, &sExcept, &nowait);
bool hasData;
if (ret == SOCKET_ERROR)
if (!success)
{
hasData = false;
Console.Error("DEV9: UDP: Select failed. Error code: %d",
#ifdef _WIN32
WSAGetLastError());
#elif defined(__POSIX__)
errno);
#endif
}
else if (FD_ISSET(client, &sExcept))
{
hasData = false;
int error = 0;
#ifdef _WIN32
int len = sizeof(error);
if (getsockopt(client, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&error), &len) < 0)
Console.Error("DEV9: UDP: Unknown UDP connection error (getsockopt error: %d)", WSAGetLastError());
#elif defined(__POSIX__)
socklen_t len = sizeof(error);
if (getsockopt(client, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&error), &len) < 0)
Console.Error("DEV9: UDP: Unknown UDP connection error (getsockopt error: %d)", errno);
#endif
else
Console.Error("DEV9: UDP: Recv error: %d", error);
}
else
hasData = FD_ISSET(client, &sReady);
if (hasData)
{
unsigned long available = 0;
PayloadData* recived = nullptr;
std::unique_ptr<u8[]> buffer;
sockaddr_in endpoint{};
// FIONREAD returns total size of all available messages
// however, we only read one message at a time
#ifdef _WIN32
ret = ioctlsocket(client, FIONREAD, &available);
#elif defined(__POSIX__)
ret = ioctl(client, FIONREAD, &available);
#endif
if (ret != SOCKET_ERROR)
{
buffer = std::make_unique<u8[]>(available);
#ifdef _WIN32
int fromlen = sizeof(endpoint);
#elif defined(__POSIX__)
socklen_t fromlen = sizeof(endpoint);
#endif
ret = recvfrom(client, reinterpret_cast<char*>(buffer.get()), available, 0, reinterpret_cast<sockaddr*>(&endpoint), &fromlen);
}
if (ret == SOCKET_ERROR)
{
Console.Error("DEV9: UDP: Recv error: %d",
#ifdef _WIN32
WSAGetLastError());
#elif defined(__POSIX__)
errno);
#endif
RaiseEventConnectionClosed();
return std::nullopt;
}
recived = new PayloadData(ret);
memcpy(recived->data.get(), buffer.get(), ret);
std::unique_ptr<UDP_Packet> iRet = std::make_unique<UDP_Packet>(recived);
iRet->destinationPort = srcPort;
iRet->sourcePort = destPort;
deathClockStart.store(std::chrono::steady_clock::now());
return ReceivedPayload{destIP, std::move(iRet)};
RaiseEventConnectionClosed();
return std::nullopt;
}
else if (ret.has_value())
return ret;
if (std::chrono::steady_clock::now() - deathClockStart.load() > MAX_IDLE)
{
@@ -211,54 +132,19 @@ namespace Sessions
destPort = udp.destinationPort;
srcPort = udp.sourcePort;
int ret;
client = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
client = UDP_Common::CreateSocket(adapterIP, std::nullopt);
if (client == INVALID_SOCKET)
{
Console.Error("DEV9: UDP: Failed to open socket. Error: %d",
#ifdef _WIN32
WSAGetLastError());
#elif defined(__POSIX__)
errno);
#endif
RaiseEventConnectionClosed();
return false;
}
constexpr int reuseAddress = true; // BOOL on Windows
ret = setsockopt(client, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char*>(&reuseAddress), sizeof(reuseAddress));
if (ret == SOCKET_ERROR)
Console.Error("DEV9: UDP: Failed to set SO_REUSEADDR. Error: %d",
#ifdef _WIN32
WSAGetLastError());
#elif defined(__POSIX__)
errno);
#endif
if (adapterIP.integer != 0)
{
sockaddr_in endpoint{};
endpoint.sin_family = AF_INET;
endpoint.sin_addr = std::bit_cast<in_addr>(adapterIP);
ret = bind(client, reinterpret_cast<const sockaddr*>(&endpoint), sizeof(endpoint));
if (ret == SOCKET_ERROR)
Console.Error("DEV9: UDP: Failed to bind socket. Error: %d",
#ifdef _WIN32
WSAGetLastError());
#elif defined(__POSIX__)
errno);
#endif
}
sockaddr_in endpoint{};
endpoint.sin_family = AF_INET;
endpoint.sin_addr = std::bit_cast<in_addr>(destIP);
endpoint.sin_port = htons(destPort);
ret = connect(client, reinterpret_cast<const sockaddr*>(&endpoint), sizeof(endpoint));
const int ret = connect(client, reinterpret_cast<const sockaddr*>(&endpoint), sizeof(endpoint));
if (ret == SOCKET_ERROR)
{

View File

@@ -203,14 +203,19 @@ bool SocketAdapter::recv(NetPacket* pkt)
ScopedGuard cleanup([&]() {
// Garbage collect closed connections
for (BaseSession* s : deleteQueueRecvThread)
delete s;
deleteQueueRecvThread.clear();
if (deleteQueueRecvThread.size() != 0)
{
std::lock_guard deletelock(deleteRecvSentry);
for (BaseSession* s : deleteQueueRecvThread)
delete s;
deleteQueueRecvThread.clear();
}
});
EthernetFrame* bFrame;
if (!vRecBuffer.Dequeue(&bFrame))
{
std::lock_guard deletelock(deleteSendSentry);
std::vector<ConnectionKey> keys = connections.GetKeys();
for (size_t i = 0; i < keys.size(); i++)
{
@@ -259,9 +264,13 @@ bool SocketAdapter::send(NetPacket* pkt)
pxAssert(std::this_thread::get_id() == sendThreadId);
ScopedGuard cleanup([&]() {
// Garbage collect closed connections
for (BaseSession* s : deleteQueueSendThread)
delete s;
deleteQueueSendThread.clear();
if (deleteQueueSendThread.size() != 0)
{
std::lock_guard deletelock(deleteSendSentry);
for (BaseSession* s : deleteQueueSendThread)
delete s;
deleteQueueSendThread.clear();
}
});
EthernetFrame frame(pkt);
@@ -375,6 +384,7 @@ bool SocketAdapter::SendIP(IP_Packet* ipPkt)
Key.ip = ipPkt->destinationIP;
Key.protocol = ipPkt->protocol;
std::lock_guard deletelock(deleteRecvSentry);
switch (ipPkt->protocol) //(Prase Payload)
{
case (u8)IP_Type::ICMP:

View File

@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-3.0+
#pragma once
#include <mutex>
#include <vector>
#include "net.h"
@@ -23,14 +24,17 @@ class SocketAdapter : public NetAdapter
bool initialized = false;
PacketReader::IP::IP_Address adapterIP;
//Sentrys replaced by the requirment for each session class to have thread safe destructor
ThreadSafeMap<Sessions::ConnectionKey, Sessions::BaseSession*> connections;
ThreadSafeMap<u16, Sessions::BaseSession*> fixedUDPPorts;
std::thread::id sendThreadId;
std::vector<Sessions::BaseSession*> deleteQueueSendThread;
std::vector<Sessions::BaseSession*> deleteQueueRecvThread;
//Mutex to be held when processing the delete queue.
//The Send thread will lock the RecvSentry to prevent the recv thread
//from deleting a session the send thread might be currently working on.
std::mutex deleteSendSentry;
std::mutex deleteRecvSentry;
public:
SocketAdapter();

View File

@@ -435,6 +435,15 @@ void GSgifTransfer3(u8* mem, u32 size)
void GSvsync(u32 field, bool registers_written)
{
// Update this here because we need to check if the pending draw affects the current frame, so our regs need to be updated.
g_gs_renderer->PCRTCDisplays.SetVideoMode(g_gs_renderer->GetVideoMode());
g_gs_renderer->PCRTCDisplays.EnableDisplays(g_gs_renderer->m_regs->PMODE, g_gs_renderer->m_regs->SMODE2, g_gs_renderer->isReallyInterlaced());
g_gs_renderer->PCRTCDisplays.CheckSameSource();
g_gs_renderer->PCRTCDisplays.SetRects(0, g_gs_renderer->m_regs->DISP[0].DISPLAY, g_gs_renderer->m_regs->DISP[0].DISPFB);
g_gs_renderer->PCRTCDisplays.SetRects(1, g_gs_renderer->m_regs->DISP[1].DISPLAY, g_gs_renderer->m_regs->DISP[1].DISPFB);
g_gs_renderer->PCRTCDisplays.CalculateDisplayOffset(g_gs_renderer->m_scanmask_used);
g_gs_renderer->PCRTCDisplays.CalculateFramebufferOffset(g_gs_renderer->m_scanmask_used);
// Do not move the flush into the VSync() method. It's here because EE transfers
// get cleared in HW VSync, and may be needed for a buffered draw (FFX FMVs).
g_gs_renderer->Flush(GSState::VSYNC);

View File

@@ -119,6 +119,10 @@ GSState::~GSState()
_aligned_free(m_vertex.buff);
if (m_index.buff)
_aligned_free(m_index.buff);
if (m_draw_vertex.buff)
_aligned_free(m_draw_vertex.buff);
if (m_draw_index.buff)
_aligned_free(m_draw_index.buff);
}
std::string GSState::GetDrawDumpPath(const char* format, ...)
@@ -467,7 +471,8 @@ void GSState::DumpVertices(const std::string& filename)
file << std::setfill('0') << std::setw(3) << unsigned(v.RGBAQ.R) << DEL;
file << std::setfill('0') << std::setw(3) << unsigned(v.RGBAQ.G) << DEL;
file << std::setfill('0') << std::setw(3) << unsigned(v.RGBAQ.B) << DEL;
file << std::setfill('0') << std::setw(3) << unsigned(v.RGBAQ.A);
file << std::setfill('0') << std::setw(3) << unsigned(v.RGBAQ.A) << DEL;
file << "FOG: " << std::setfill('0') << std::setw(3) << unsigned(v.FOG);
file << std::endl;
}
@@ -849,7 +854,7 @@ void GSState::ApplyTEX0(GIFRegTEX0& TEX0)
// Urban Chaos writes to the memory backing the CLUT in the middle of a shuffle, and
// it's unclear whether the CLUT would actually get reloaded in that case.
if (TEX0.CBP != m_mem.m_clut.GetCLUTCBP())
m_channel_shuffle = false;
m_channel_shuffle_abort = true;
}
TEX0.CPSM &= 0xa; // 1010b
@@ -1472,6 +1477,35 @@ void GSState::Flush(GSFlushReason reason)
if (m_index.tail > 0)
{
// Unless Vsync really needs the pending draw, don't do it when VSync happens as it can really screw up our heuristics when looking ahead.
if (reason == VSYNC)
{
GSDrawingContext* draw_ctx = &m_prev_env.CTXT[m_prev_env.PRIM.CTXT];
const u32 start_bp = GSLocalMemory::GetStartBlockAddress(draw_ctx->FRAME.Block(), draw_ctx->FRAME.FBW, draw_ctx->FRAME.PSM, temp_draw_rect);
const u32 end_bp = GSLocalMemory::GetEndBlockAddress(draw_ctx->FRAME.Block(), draw_ctx->FRAME.FBW, draw_ctx->FRAME.PSM, temp_draw_rect);
bool needs_flush[2] = {PCRTCDisplays.PCRTCDisplays[0].enabled, PCRTCDisplays.PCRTCDisplays[1].enabled};
if (PCRTCDisplays.PCRTCDisplays[1].enabled)
{
const u32 out_start_bp = GSLocalMemory::GetStartBlockAddress(PCRTCDisplays.PCRTCDisplays[1].Block(), PCRTCDisplays.PCRTCDisplays[1].FBW, PCRTCDisplays.PCRTCDisplays[1].PSM, PCRTCDisplays.PCRTCDisplays[1].framebufferRect);
const u32 out_end_bp = GSLocalMemory::GetEndBlockAddress(PCRTCDisplays.PCRTCDisplays[1].Block(), PCRTCDisplays.PCRTCDisplays[1].FBW, PCRTCDisplays.PCRTCDisplays[1].PSM, PCRTCDisplays.PCRTCDisplays[1].framebufferRect);
if (out_start_bp > end_bp || out_end_bp < start_bp)
needs_flush[1] = false;
}
if (PCRTCDisplays.PCRTCDisplays[0].enabled)
{
const u32 out_start_bp = GSLocalMemory::GetStartBlockAddress(PCRTCDisplays.PCRTCDisplays[0].Block(), PCRTCDisplays.PCRTCDisplays[0].FBW, PCRTCDisplays.PCRTCDisplays[0].PSM, PCRTCDisplays.PCRTCDisplays[0].framebufferRect);
const u32 out_end_bp = GSLocalMemory::GetEndBlockAddress(PCRTCDisplays.PCRTCDisplays[0].Block(), PCRTCDisplays.PCRTCDisplays[0].FBW, PCRTCDisplays.PCRTCDisplays[0].PSM, PCRTCDisplays.PCRTCDisplays[0].framebufferRect);
if (out_start_bp > end_bp || out_end_bp < start_bp)
needs_flush[0] = false;
}
if (!needs_flush[0] && !needs_flush[1])
return;
}
m_state_flush_reason = reason;
// Used to prompt the current draw that it's modifying its own CLUT.
@@ -1674,7 +1708,8 @@ void GSState::FlushPrim()
Console.Warning("GS: Possible invalid draw, Frame PSM %x ZPSM %x", m_context->FRAME.PSM, m_context->ZBUF.PSM);
}
#endif
// Update scissor, it may have been modified by a previous draw
m_env.CTXT[PRIM->CTXT].UpdateScissor();
m_vt.Update(m_vertex.buff, m_index.buff, m_vertex.tail, m_index.tail, GSUtil::GetPrimClass(PRIM->PRIM));
// Texel coordinate rounding
@@ -1723,6 +1758,7 @@ void GSState::FlushPrim()
// Skip draw if Z test is enabled, but set to fail all pixels.
const bool skip_draw = (m_context->TEST.ZTE && m_context->TEST.ZTST == ZTST_NEVER);
m_quad_check_valid = false;
if (!skip_draw)
Draw();
@@ -1936,10 +1972,10 @@ void GSState::Write(const u8* mem, int len)
m_draw_transfers.push_back(new_transfer);
}
GL_CACHE("Write! %u ... => 0x%x W:%d F:%s (DIR %d%d), dPos(%d %d) size(%d %d)", s_transfer_n,
GL_CACHE("Write! %u ... => 0x%x W:%d F:%s (DIR %d%d), dPos(%d %d) size(%d %d) draw %d", s_transfer_n,
blit.DBP, blit.DBW, psm_str(blit.DPSM),
m_env.TRXPOS.DIRX, m_env.TRXPOS.DIRY,
m_env.TRXPOS.DSAX, m_env.TRXPOS.DSAY, w, h);
m_env.TRXPOS.DSAX, m_env.TRXPOS.DSAY, w, h, s_n);
if (len >= m_tr.total)
{
@@ -2794,8 +2830,10 @@ void GSState::GrowVertexBuffer()
const u32 maxcount = std::max<u32>(m_vertex.maxcount * 3 / 2, 10000);
GSVertex* vertex = static_cast<GSVertex*>(_aligned_malloc(sizeof(GSVertex) * maxcount, 32));
GSVertex* draw_vertex = static_cast<GSVertex*>(_aligned_malloc(sizeof(GSVertex) * maxcount, 32));
// Worst case index list is a list of points with vs expansion, 6 indices per point
u16* index = static_cast<u16*>(_aligned_malloc(sizeof(u16) * maxcount * 6, 32));
u16* draw_index = static_cast<u16*>(_aligned_malloc(sizeof(u16) * maxcount * 6, 32));
if (!vertex || !index)
{
@@ -2821,16 +2859,38 @@ void GSState::GrowVertexBuffer()
_aligned_free(m_index.buff);
}
if (m_draw_vertex.buff)
{
std::memcpy(draw_vertex, m_draw_vertex.buff, sizeof(GSVertex) * m_vertex.tail);
_aligned_free(m_draw_vertex.buff);
}
if (m_draw_index.buff)
{
std::memcpy(draw_index, m_draw_index.buff, sizeof(u16) * m_index.tail);
_aligned_free(m_draw_index.buff);
}
m_draw_vertex.buff = draw_vertex;
m_draw_index.buff = draw_index;
m_vertex.buff = vertex;
m_vertex.maxcount = maxcount - 3; // -3 to have some space at the end of the buffer before DrawingKick can grow it
m_index.buff = index;
}
bool GSState::TrianglesAreQuads(bool shuffle_check) const
bool GSState::TrianglesAreQuads(bool shuffle_check)
{
// If this is a quad, there should only be two distinct values for both X and Y, which
// also happen to be the minimum/maximum bounds of the primitive.
if (!shuffle_check && m_quad_check_valid)
return m_are_quads;
const GSVertex* const v = m_vertex.buff;
m_are_quads = false;
m_quad_check_valid = !shuffle_check;
for (u32 idx = 0; idx < m_index.tail; idx += 6)
{
const u16* const i = m_index.buff + idx;
@@ -2853,15 +2913,44 @@ bool GSState::TrianglesAreQuads(bool shuffle_check) const
return false;
}
// Degenerate triangles should've been culled already, so we can check indices.
u32 extra_verts = 0;
for (u32 j = 3; j < 6; j++)
// This doesn't really make much sense when it's a triangle strip as it will always have 1 extra vert, so check for distinct values for them.
if (PRIM->PRIM != GS_TRIANGLESTRIP)
{
const u16 tri2_idx = i[j];
if (tri2_idx != i[0] && tri2_idx != i[1] && tri2_idx != i[2])
extra_verts++;
u32 extra_verts = 0;
for (u32 j = 3; j < 6; j++)
{
const u16 tri2_idx = i[j];
if (tri2_idx != i[0] && tri2_idx != i[1] && tri2_idx != i[2])
extra_verts++;
}
if (extra_verts == 1)
continue;
}
else if (m_index.tail == 6)
{
const int first_X = m_vertex.buff[m_index.buff[0]].XYZ.X;
const int first_Y = m_vertex.buff[m_index.buff[0]].XYZ.Y;
const int second_X = m_vertex.buff[m_index.buff[1]].XYZ.X;
const int second_Y = m_vertex.buff[m_index.buff[1]].XYZ.Y;
const int third_X = m_vertex.buff[m_index.buff[2]].XYZ.X;
const int third_Y = m_vertex.buff[m_index.buff[2]].XYZ.Y;
const int new_X = m_vertex.buff[m_index.buff[5]].XYZ.X;
const int new_Y = m_vertex.buff[m_index.buff[5]].XYZ.Y;
const int middle_Y = (second_Y >= third_Y) ? (third_Y + ((second_Y - third_Y) / 2)) : (second_Y + ((third_Y - second_Y) / 2));
const int middle_X = (second_X >= third_X) ? (third_X + ((second_X - third_X) / 2)) : (second_X + ((third_X - second_X) / 2));
const bool first_lt_X = first_X <= middle_X;
const bool first_lt_Y = first_Y <= middle_Y;
const bool new_lt_X = new_X <= middle_X;
const bool new_lt_Y = new_Y <= middle_Y;
// Check if verts are on the same side. Not totally accurate, but should be good enough.
if (first_lt_X == new_lt_X && new_lt_Y == first_lt_Y)
return false;
m_prim_overlap = PRIM_OVERLAP_NO;
break;
}
if (extra_verts == 1)
continue;
// As a fallback, they might've used different vertices with a tri list, not strip.
// Note that this won't work unless the quad is axis-aligned.
@@ -2889,6 +2978,7 @@ bool GSState::TrianglesAreQuads(bool shuffle_check) const
}
}
m_are_quads = true;
return true;
}
@@ -3051,6 +3141,46 @@ bool GSState::SpriteDrawWithoutGaps()
return true;
}
// Assume it's small sprites. NFSMW and a few other games draw 32x32 sprites in rows to fill the screen.
if (((first_dpY + 8) >> 4) == GSLocalMemory::m_psm[m_context->FRAME.PSM].pgs.y)
{
int lastXEdge = std::max(v[1].XYZ.X, v[0].XYZ.X);
int lastYEdge = std::max(v[1].XYZ.Y, v[0].XYZ.Y);
for (u32 i = 2; i < m_vertex.next; i += 2)
{
const int dpY = v[i + 1].XYZ.Y - v[i].XYZ.Y;
if (first_dpY != dpY)
return false;
const int newYStart = std::min(v[i + 1].XYZ.Y, v[i].XYZ.Y);
const int newXEdge = std::max(v[i + 1].XYZ.X, v[i].XYZ.X);
if (lastYEdge != newYStart)
{
if (newYStart != static_cast<int>(m_context->XYOFFSET.OFY))
return false;
const int newXStart = std::min(v[i + 1].XYZ.X, v[i].XYZ.X);
if (newXStart != lastXEdge)
return false;
}
else
{
const int dpX = v[i + 1].XYZ.X - v[i].XYZ.X;
if (first_dpX != dpX || lastXEdge != newXEdge)
return false;
}
lastXEdge = newXEdge;
lastYEdge = std::max(v[i + 1].XYZ.Y, v[i].XYZ.Y);
}
m_prim_overlap = PRIM_OVERLAP_NO;
return true;
}
return false;
}
@@ -3069,7 +3199,7 @@ void GSState::CalculatePrimitiveCoversWithoutGaps()
}
else if (m_vt.m_primclass == GS_TRIANGLE_CLASS)
{
m_primitive_covers_without_gaps = (m_index.tail == 6 && TrianglesAreQuads()) ? m_primitive_covers_without_gaps : GapsFound;
m_primitive_covers_without_gaps = ((m_index.tail == 6 || ((m_index.tail % 6) == 0 && m_primitive_covers_without_gaps == FullCover)) && TrianglesAreQuads()) ? m_primitive_covers_without_gaps : GapsFound;
return;
}
else if (m_vt.m_primclass != GS_SPRITE_CLASS)
@@ -3094,6 +3224,16 @@ __forceinline bool GSState::IsAutoFlushDraw(u32 prim)
if (!(GSUtil::GetChannelMask(m_context->TEX0.PSM) & GSUtil::GetChannelMask(m_context->FRAME.PSM, m_context->FRAME.FBMSK | ~(GSLocalMemory::m_psm[m_context->FRAME.PSM].fmsk))))
return false;
// Try to detect shuffles, because these will not autoflush, they by design clash.
if (GSLocalMemory::m_psm[m_context->FRAME.PSM].bpp == 16 && GSLocalMemory::m_psm[m_context->TEX0.PSM].bpp == 16)
{
// Pretty confident here...
GSVertex* buffer = &m_vertex.buff[0];
const bool const_spacing = std::abs(buffer[m_index.buff[0]].U - buffer[m_index.buff[0]].XYZ.X) == std::abs(m_v.U - m_v.XYZ.X) && std::abs(buffer[m_index.buff[1]].XYZ.X - buffer[m_index.buff[0]].XYZ.X) <= 256; // Lequal to 16 pixels apart.
if (const_spacing)
return false;
}
const u32 frame_mask = GSLocalMemory::m_psm[m_context->FRAME.PSM].fmsk;
const bool frame_hit = m_context->FRAME.Block() == m_context->TEX0.TBP0 && !(m_context->TEST.ATE && m_context->TEST.ATST == 0 && m_context->TEST.AFAIL == 2) && ((m_context->FRAME.FBMSK & frame_mask) != frame_mask);
// There's a strange behaviour we need to test on a PS2 here, if the FRAME is a Z format, like Powerdrome something swaps over, and it seems Alpha Fail of "FB Only" writes to the Z.. it's odd.
@@ -3859,7 +3999,8 @@ GSState::TextureMinMaxResult GSState::GetTextureMinMax(GIFRegTEX0 TEX0, GIFRegCL
const GSVector2 grad(uv_range / pos_range);
// Adjust texture range when sprites get scissor clipped. Since we linearly interpolate, this
// optimization doesn't work when perspective correction is enabled.
if (m_vt.m_primclass == GS_SPRITE_CLASS && PRIM->FST == 1 && m_primitive_covers_without_gaps != NoGapsType::GapsFound)
// Allowing for quads when the gradiant is 1. It's not guaranteed (would need to check the grandient on each vector), but should be close enough.
if (m_primitive_covers_without_gaps != NoGapsType::GapsFound && (m_vt.m_primclass == GS_SPRITE_CLASS || (m_vt.m_primclass == GS_TRIANGLE_CLASS && grad.x == 1.0f && grad.y == 1.0f && TrianglesAreQuads(false))))
{
// When coordinates are fractional, GS appears to draw to the right/bottom (effectively
// taking the ceiling), not to the top/left (taking the floor).
@@ -3870,11 +4011,24 @@ GSState::TextureMinMaxResult GSState::GetTextureMinMax(GIFRegTEX0 TEX0, GIFRegCL
const GSVertex* vert_first = &m_vertex.buff[m_index.buff[0]];
const GSVertex* vert_second = &m_vertex.buff[m_index.buff[1]];
const GSVertex* vert_third = &m_vertex.buff[m_index.buff[2]];
GSVector4 new_st = st;
bool u_forward_check = false;
bool x_forward_check = false;
if (m_vt.m_primclass == GS_TRIANGLE_CLASS)
{
u_forward_check = PRIM->FST ? ((vert_first->U < vert_second->U) || (vert_first->U < vert_third->U)) : (((vert_first->ST.S / vert_first->RGBAQ.Q) < (vert_second->ST.S / vert_second->RGBAQ.Q)) || ((vert_first->ST.S / vert_first->RGBAQ.Q) < (vert_third->ST.S / vert_third->RGBAQ.Q)));
x_forward_check = (vert_first->XYZ.X < vert_second->XYZ.X) || (vert_first->XYZ.X < vert_third->XYZ.X);
}
else
{
u_forward_check = PRIM->FST ? (vert_first->U < vert_second->U) : ((vert_first->ST.T / vert_first->RGBAQ.Q) < (vert_second->ST.T / vert_first->RGBAQ.Q));
x_forward_check = vert_first->XYZ.Y < vert_second->XYZ.Y;
}
// Check if the UV coords are going in a different direction to the verts, if they match direction, no need to swap
const bool u_forward = vert_first->U < vert_second->U;
const bool x_forward = vert_first->XYZ.X < vert_second->XYZ.X;
const bool u_forward = u_forward_check;
const bool x_forward = x_forward_check;
const bool swap_x = u_forward != x_forward;
if (int_rc.left < scissored_rc.left)
@@ -3897,9 +4051,20 @@ GSState::TextureMinMaxResult GSState::GetTextureMinMax(GIFRegTEX0 TEX0, GIFRegCL
st.x = new_st.x;
st.z = new_st.z;
}
const bool v_forward = vert_first->V < vert_second->V;
const bool y_forward = vert_first->XYZ.Y < vert_second->XYZ.Y;
bool v_forward_check = false;
bool y_forward_check = false;
if (m_vt.m_primclass == GS_TRIANGLE_CLASS)
{
v_forward_check = PRIM->FST ? ((vert_first->V < vert_second->V) || (vert_first->V < vert_third->V)) : (((vert_first->ST.T / vert_first->RGBAQ.Q) < (vert_second->ST.T / vert_second->RGBAQ.Q)) || ((vert_first->ST.T / vert_first->RGBAQ.Q) < (vert_third->ST.T / vert_third->RGBAQ.Q)));
y_forward_check = (vert_first->XYZ.Y < vert_second->XYZ.Y) || (vert_first->XYZ.Y < vert_third->XYZ.Y);
}
else
{
v_forward_check = PRIM->FST ? (vert_first->V < vert_second->V) : ((vert_first->ST.T / vert_first->RGBAQ.Q) < (vert_second->ST.T / vert_first->RGBAQ.Q));
y_forward_check = vert_first->XYZ.Y < vert_second->XYZ.Y;
}
const bool v_forward = v_forward_check;
const bool y_forward = y_forward_check;
const bool swap_y = v_forward != y_forward;
if (int_rc.top < scissored_rc.top)
@@ -4409,7 +4574,7 @@ bool GSState::GSTransferBuffer::Update(int tw, int th, int bpp, int& len)
{
if (len > packet_size)
{
#if defined(PCSX2_DEVBUILD) || defined(_DEBUG)
#if defined(_DEBUG)
Console.Warning("GS transfer buffer overflow len %d remaining %d, tex_size %d tw %d th %d bpp %d", len, remaining, tex_size, tw, th, bpp);
#endif
}
@@ -4669,10 +4834,16 @@ GSVector2i GSState::GSPCRTCRegs::GetFramebufferSize(int display)
void GSState::GSPCRTCRegs::SetRects(int display, GSRegDISPLAY displayReg, GSRegDISPFB framebufferReg)
{
// Save framebuffer information first, while we're here.
PCRTCDisplays[display].prevFramebufferReg.FBP = PCRTCDisplays[display].FBP;
PCRTCDisplays[display].prevFramebufferReg.FBW = PCRTCDisplays[display].FBW;
PCRTCDisplays[display].prevFramebufferReg.PSM = PCRTCDisplays[display].PSM;
PCRTCDisplays[display].prevFramebufferReg.DBX = PCRTCDisplays[display].DBX;
PCRTCDisplays[display].prevFramebufferReg.DBY = PCRTCDisplays[display].DBY;
PCRTCDisplays[display].FBP = framebufferReg.FBP;
PCRTCDisplays[display].FBW = framebufferReg.FBW;
PCRTCDisplays[display].PSM = framebufferReg.PSM;
PCRTCDisplays[display].prevFramebufferReg = framebufferReg;
PCRTCDisplays[display].DBX = framebufferReg.DBX;
PCRTCDisplays[display].DBY = framebufferReg.DBY;
// Probably not really enabled but will cause a mess.
// Q-Ball Billiards enables both circuits but doesn't set one of them up.
if (PCRTCDisplays[display].FBW == 0 && displayReg.DW == 0 && displayReg.DH == 0 && displayReg.MAGH == 0)

View File

@@ -145,6 +145,21 @@ protected:
u32 tail;
} m_index = {};
struct
{
GSVertex* buff;
u32 head, tail, next, maxcount; // head: first vertex, tail: last vertex + 1, next: last indexed + 1
u32 xy_tail;
GSVector4i xy[4];
GSVector4i xyhead;
} m_draw_vertex = {};
struct
{
u16* buff;
u32 tail;
} m_draw_index = {};
void UpdateContext();
void UpdateScissor();
@@ -219,11 +234,19 @@ public:
GSVector4i temp_draw_rect = {};
std::unique_ptr<GSDumpBase> m_dump;
bool m_scissor_invalid = false;
bool m_quad_check_valid = false;
bool m_are_quads = false;
bool m_nativeres = false;
bool m_mipmap = false;
bool m_texflush_flag = false;
bool m_isPackedUV_HackFlag = false;
bool m_channel_shuffle = false;
bool m_using_temp_z = false;
bool m_temp_z_full_copy = false;
bool m_in_target_draw = false;
bool m_channel_shuffle_abort = false;
u32 m_target_offset = 0;
u8 m_scanmask_used = 0;
u32 m_dirty_gs_regs = 0;
int m_backed_up_ctx = 0;
@@ -302,6 +325,8 @@ public:
int FBP;
int FBW;
int PSM;
int DBY;
int DBX;
GSRegDISPFB prevFramebufferReg;
GSVector2i prevDisplayOffset;
GSVector2i displayOffset;
@@ -416,7 +441,7 @@ public:
void DumpVertices(const std::string& filename);
bool TrianglesAreQuads(bool shuffle_check = false) const;
bool TrianglesAreQuads(bool shuffle_check = false);
PRIM_OVERLAP PrimitiveOverlap();
bool SpriteDrawWithoutGaps();
void CalculatePrimitiveCoversWithoutGaps();

View File

@@ -1599,6 +1599,11 @@ public:
return loadh(&v);
}
__forceinline static GSVector4i loadl(const GSVector2i& v)
{
return loadl(&v);
}
__forceinline static GSVector4i load(const void* pl, const void* ph)
{
return loadh(ph, loadl(pl));

View File

@@ -46,8 +46,8 @@ const char* shaderName(ShaderConvert value)
case ShaderConvert::DATM_0: return "ps_datm0";
case ShaderConvert::DATM_1_RTA_CORRECTION: return "ps_datm1_rta_correction";
case ShaderConvert::DATM_0_RTA_CORRECTION: return "ps_datm0_rta_correction";
case ShaderConvert::HDR_INIT: return "ps_hdr_init";
case ShaderConvert::HDR_RESOLVE: return "ps_hdr_resolve";
case ShaderConvert::COLCLIP_INIT: return "ps_colclip_init";
case ShaderConvert::COLCLIP_RESOLVE: return "ps_colclip_resolve";
case ShaderConvert::RTA_CORRECTION: return "ps_rta_correction";
case ShaderConvert::RTA_DECORRECTION: return "ps_rta_decorrection";
case ShaderConvert::TRANSPARENCY_FILTER: return "ps_filter_transparency";
@@ -103,7 +103,9 @@ const char* shaderName(PresentShader value)
enum class TextureLabel
{
ColorRT,
HDRRT,
ColorHQRT,
ColorHDRRT,
ColorClipRT,
U16RT,
U32RT,
DepthStencil,
@@ -127,8 +129,12 @@ static TextureLabel GetTextureLabel(GSTexture::Type type, GSTexture::Format form
{
case GSTexture::Format::Color:
return TextureLabel::ColorRT;
case GSTexture::Format::HDRColor:
return TextureLabel::HDRRT;
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:
@@ -149,6 +155,7 @@ static TextureLabel GetTextureLabel(GSTexture::Type type, GSTexture::Format form
case GSTexture::Format::BC2:
case GSTexture::Format::BC3:
case GSTexture::Format::BC7:
case GSTexture::Format::ColorHDR:
return TextureLabel::ReplacementTexture;
default:
return TextureLabel::Other;
@@ -163,14 +170,19 @@ static TextureLabel GetTextureLabel(GSTexture::Type type, GSTexture::Format form
}
}
// Debug names
static const char* TextureLabelString(TextureLabel label)
{
switch (label)
{
case TextureLabel::ColorRT:
return "Color RT";
case TextureLabel::HDRRT:
return "HDR RT";
case TextureLabel::ColorHQRT:
return "Color HQ RT";
case TextureLabel::ColorHDRRT:
return "Color HDR RT";
case TextureLabel::ColorClipRT:
return "Color Clip RT";
case TextureLabel::U16RT:
return "U16 RT";
case TextureLabel::U32RT:
@@ -904,6 +916,7 @@ bool GSHWDrawConfig::BlendState::IsEffective(ColorMaskSelector colormask) const
// clang-format off
// Maps PS2 blend modes to our best approximation of them with PC hardware
const std::array<HWBlend, 3*3*3*3> GSDevice::m_blendMap =
{{
{ BLEND_NO_REC , OP_ADD , CONST_ONE , CONST_ZERO} , // 0000: (Cs - Cs)*As + Cs ==> Cs

View File

@@ -22,8 +22,8 @@ enum class ShaderConvert
DATM_0,
DATM_1_RTA_CORRECTION,
DATM_0_RTA_CORRECTION,
HDR_INIT,
HDR_RESOLVE,
COLCLIP_INIT,
COLCLIP_RESOLVE,
RTA_CORRECTION,
RTA_DECORRECTION,
TRANSPARENCY_FILTER,
@@ -257,11 +257,15 @@ enum HWBlendFlags
BLEND_A_MAX = 0x8000, // Impossible blending uses coeff bigger than 1
};
// Determines the HW blend function for DX11/OGL
// Determines the HW blend function for the video backend
struct HWBlend
{
typedef u8 BlendOp; /*GSDevice::BlendOp*/
typedef u8 BlendFactor; /*GSDevice::BlendFactor*/
u16 flags;
u8 op, src, dst;
BlendOp op;
BlendFactor src, dst;
};
struct alignas(16) GSHWDrawConfig
@@ -354,12 +358,12 @@ struct alignas(16) GSHWDrawConfig
u32 blend_c : 2;
u32 blend_d : 2;
u32 fixed_one_a : 1;
u32 blend_hw : 3;
u32 blend_hw : 3; /*HWBlendType*/
u32 a_masked : 1;
u32 hdr : 1;
u32 colclip_hw : 1; // colclip (COLCLAMP off) emulation through HQ textures
u32 rta_correction : 1;
u32 rta_source_correction : 1;
u32 colclip : 1;
u32 colclip : 1; // COLCLAMP off (color blend outputs wrap around 0-255)
u32 blend_mix : 2;
u32 round_inv : 1; // Blending will invert the value, so rounding needs to go the other way
u32 pabe : 1;
@@ -631,26 +635,30 @@ struct alignas(16) GSHWDrawConfig
return true;
}
};
// For hardware rendering backends
struct BlendState
{
typedef u8 BlendOp; /*GSDevice::BlendOp*/
typedef u8 BlendFactor; /*GSDevice::BlendFactor*/
union
{
struct
{
u8 enable : 1;
u8 constant_enable : 1;
u8 op : 6;
u8 src_factor : 4;
u8 dst_factor : 4;
u8 src_factor_alpha : 4;
u8 dst_factor_alpha : 4;
bool enable : 1;
bool constant_enable : 1;
BlendOp op : 6;
BlendFactor src_factor : 4;
BlendFactor dst_factor : 4;
BlendFactor src_factor_alpha : 4;
BlendFactor dst_factor_alpha : 4;
u8 constant;
};
u32 key;
};
constexpr BlendState(): key(0) {}
constexpr BlendState(bool enable_, u8 src_factor_, u8 dst_factor_, u8 op_,
u8 src_alpha_factor_, u8 dst_alpha_factor_, bool constant_enable_, u8 constant_)
constexpr BlendState(bool enable_, BlendFactor src_factor_, BlendFactor dst_factor_, BlendOp op_,
BlendFactor src_alpha_factor_, BlendFactor dst_alpha_factor_, bool constant_enable_, u8 constant_)
: key(0)
{
enable = enable_;
@@ -675,7 +683,7 @@ struct alignas(16) GSHWDrawConfig
Full, ///< Full emulation (using barriers / ROV)
};
enum class HDRMode : u8
enum class ColClipMode : u8
{
NoModify = 0,
ConvertOnly = 1,
@@ -730,7 +738,7 @@ struct alignas(16) GSHWDrawConfig
struct BlendMultiPass
{
BlendState blend;
u8 blend_hw;
u8 blend_hw; /*HWBlendType*/
u8 dither;
bool enable;
};
@@ -742,9 +750,9 @@ struct alignas(16) GSHWDrawConfig
PSConstantBuffer cb_ps;
// These are here as they need to be preserved between draws, and the state clear only does up to the constant buffers.
HDRMode hdr_mode;
GIFRegFRAME hdr_frame;
GSVector4i hdr_update_area; ///< Area in the framebuffer which HDR will modify;
ColClipMode colclip_mode;
GIFRegFRAME colclip_frame;
GSVector4i colclip_update_area; ///< Area in the framebuffer which colclip will modify;
};
class GSDevice : public GSAlignedClass<32>
@@ -865,7 +873,7 @@ protected:
GSTexture* m_target_tmp = nullptr;
GSTexture* m_current = nullptr;
GSTexture* m_cas = nullptr;
GSTexture* m_hdr_rt = nullptr; ///< Temp HDR texture
GSTexture* m_colclip_rt = nullptr; ///< Temp hw colclip texture
bool AcquireWindow(bool recreate_window);
@@ -890,9 +898,9 @@ public:
/// Returns a string containing current adapter in use.
const std::string& GetName() const { return m_name; }
GSTexture* GetHDRTexture() const { return m_hdr_rt; }
GSTexture* GetColorClipTexture() const { return m_colclip_rt; }
void SetHDRTexture(GSTexture* tex) { m_hdr_rt = tex; }
void SetColorClipTexture(GSTexture* tex) { m_colclip_rt = tex; }
/// Returns a string representing the specified API.
static const char* RenderAPIToString(RenderAPI api);

View File

@@ -87,10 +87,6 @@ bool GSRenderer::Merge(int field)
int y_offset[3] = { 0, 0, 0 };
const bool feedback_merge = m_regs->EXTWRITE.WRITE == 1;
PCRTCDisplays.SetVideoMode(GetVideoMode());
PCRTCDisplays.EnableDisplays(m_regs->PMODE, m_regs->SMODE2, isReallyInterlaced());
PCRTCDisplays.CheckSameSource();
if (!PCRTCDisplays.PCRTCDisplays[0].enabled && !PCRTCDisplays.PCRTCDisplays[1].enabled)
{
m_real_size = GSVector2i(0, 0);
@@ -101,11 +97,6 @@ bool GSRenderer::Merge(int field)
const bool game_deinterlacing = (m_regs->DISP[0].DISPFB.DBY != PCRTCDisplays.PCRTCDisplays[0].prevFramebufferReg.DBY) !=
(m_regs->DISP[1].DISPFB.DBY != PCRTCDisplays.PCRTCDisplays[1].prevFramebufferReg.DBY);
PCRTCDisplays.SetRects(0, m_regs->DISP[0].DISPLAY, m_regs->DISP[0].DISPFB);
PCRTCDisplays.SetRects(1, m_regs->DISP[1].DISPLAY, m_regs->DISP[1].DISPFB);
PCRTCDisplays.CalculateDisplayOffset(m_scanmask_used);
PCRTCDisplays.CalculateFramebufferOffset(m_scanmask_used);
// Only need to check the right/bottom on software renderer, hardware always gets the full texture then cuts a bit out later.
if (PCRTCDisplays.FrameRectMatch() && !PCRTCDisplays.FrameWrap() && !feedback_merge)
{

View File

@@ -66,7 +66,9 @@ const char* GSTexture::GetFormatName(Format format)
static constexpr const char* format_names[] = {
"Invalid",
"Color",
"HDRColor",
"ColorHQ",
"ColorHDR",
"ColorClip",
"DepthStencil",
"UNorm8",
"UInt16",
@@ -90,7 +92,9 @@ u32 GSTexture::GetCompressedBytesPerBlock(Format format)
static constexpr u32 bytes_per_block[] = {
1, // Invalid
4, // Color/RGBA8
8, // HDRColor/RGBA16
4, // ColorHQ/RGB10A2
8, // ColorHDR/RGBA16F
8, // ColorClip/RGBA16
4, // DepthStencil
1, // UNorm8/R8
2, // UInt16/R16UI
@@ -99,7 +103,7 @@ u32 GSTexture::GetCompressedBytesPerBlock(Format format)
8, // BC1 - 16 pixels in 64 bits
16, // BC2 - 16 pixels in 128 bits
16, // BC3 - 16 pixels in 128 bits
16, // BC4 - 16 pixels in 128 bits
16, // BC7 - 16 pixels in 128 bits
};
return bytes_per_block[static_cast<u32>(format)];

View File

@@ -22,15 +22,17 @@ public:
Invalid = 0,
RenderTarget = 1,
DepthStencil,
Texture,
RWTexture,
Texture, // Generic texture (usually is color textures loaded by the game)
RWTexture, // UAV
};
enum class Format : u8
{
Invalid = 0, ///< Used for initialization
Color, ///< Standard (RGBA8) color texture
HDRColor, ///< Color texture with more bits for colclip emulation (RGBA16Unorm)
Color, ///< Standard (RGBA8) color texture (used to store most of PS2's textures)
ColorHQ, ///< High quality (RGB10A2) color texture (no proper alpha)
ColorHDR, ///< High dynamic range (RGBA16F) color texture
ColorClip, ///< Color texture with more bits for colclip (wrap) emulation, given that blending requires 9bpc (RGBA16Unorm)
DepthStencil, ///< Depth stencil texture
UNorm8, ///< A8UNorm texture for paletted textures and the OSD font
UInt16, ///< UInt16 texture for reading back 16-bit depth
@@ -40,6 +42,7 @@ public:
BC2, ///< BC2, aka DXT2/3 compressed texture for replacements
BC3, ///< BC3, aka DXT4/5 compressed texture for replacements
BC7, ///< BC7, aka BPTC compressed texture for replacements
Last = BC7,
};
enum class State : u8

View File

@@ -87,6 +87,7 @@ std::vector<GSAdapterInfo> D3D::GetAdapterInfo(IDXGIFactory5* factory)
ai.max_upscale_multiplier = GSGetMaxUpscaleMultiplier(ai.max_texture_size);
wil::com_ptr_nothrow<IDXGIOutput> output;
// Only check the first output, which would be the primary display (if any is connected)
if (SUCCEEDED(hr = adapter->EnumOutputs(0, &output)))
{
UINT num_modes = 0;
@@ -111,7 +112,7 @@ std::vector<GSAdapterInfo> D3D::GetAdapterInfo(IDXGIFactory5* factory)
ERROR_LOG("GetDisplayModeList() failed: {:08X}", static_cast<unsigned>(hr));
}
}
else
else if (hr != DXGI_ERROR_NOT_FOUND)
{
ERROR_LOG("EnumOutputs() failed: {:08X}", static_cast<unsigned>(hr));
}
@@ -515,7 +516,7 @@ wil::com_ptr_nothrow<ID3DBlob> D3D::CompileShader(D3D::ShaderType type, D3D_FEAT
}
static constexpr UINT flags_non_debug = D3DCOMPILE_OPTIMIZATION_LEVEL3;
static constexpr UINT flags_debug = D3DCOMPILE_SKIP_OPTIMIZATION | D3DCOMPILE_DEBUG;
static constexpr UINT flags_debug = D3DCOMPILE_SKIP_OPTIMIZATION | D3DCOMPILE_DEBUG | D3DCOMPILE_DEBUG_NAME_FOR_SOURCE;
wil::com_ptr_nothrow<ID3DBlob> blob;
wil::com_ptr_nothrow<ID3DBlob> error_blob;

View File

@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-3.0+
#include "GS/Renderers/DX11/D3D11ShaderCache.h"
#include "GS/Renderers/DX11/GSDevice11.h"
#include "GS/GS.h"
#include "Config.h"
@@ -300,6 +301,12 @@ wil::com_ptr_nothrow<ID3D11VertexShader> D3D11ShaderCache::GetVertexShader(ID3D1
return {};
}
const char* shader_name = entry_point; // Ideally we'd feed in a proper name
if (shader_name)
{
GSDevice11::SetD3DDebugObjectName(shader.get(), shader_name);
}
return shader;
}
@@ -320,6 +327,12 @@ bool D3D11ShaderCache::GetVertexShaderAndInputLayout(ID3D11Device* device, ID3D1
return {};
}
const char* shader_name = entry_point; // Ideally we'd feed in a proper name
if (shader_name)
{
GSDevice11::SetD3DDebugObjectName(actual_vs.get(), shader_name);
}
hr = device->CreateInputLayout(layout, layout_size, blob->GetBufferPointer(), blob->GetBufferSize(), il);
if (FAILED(hr))
{
@@ -348,6 +361,12 @@ wil::com_ptr_nothrow<ID3D11PixelShader> D3D11ShaderCache::GetPixelShader(ID3D11D
return {};
}
const char* shader_name = entry_point; // Ideally we'd feed in a proper name
if (shader_name)
{
GSDevice11::SetD3DDebugObjectName(shader.get(), shader_name);
}
return shader;
}
@@ -368,6 +387,12 @@ wil::com_ptr_nothrow<ID3D11ComputeShader> D3D11ShaderCache::GetComputeShader(ID3
return {};
}
const char* shader_name = entry_point; // Ideally we'd feed in a proper name
if (shader_name)
{
GSDevice11::SetD3DDebugObjectName(shader.get(), shader_name);
}
return shader;
}

View File

@@ -1307,7 +1307,6 @@ void GSDevice11::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
else
{
ds = GSVector2i(m_window_info.surface_width, m_window_info.surface_height);
}
// om
@@ -1318,8 +1317,6 @@ void GSDevice11::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
OMSetBlendState(bs, 0);
// ia
const float left = dRect.x * 2 / ds.x - 1.0f;
@@ -1336,7 +1333,6 @@ void GSDevice11::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
};
IASetVertexBuffer(vertices, sizeof(vertices[0]), std::size(vertices));
IASetInputLayout(m_convert.il.get());
IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
@@ -1345,7 +1341,6 @@ void GSDevice11::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
VSSetShader(m_convert.vs.get(), nullptr);
// ps
PSSetShaderResource(0, sTex);
@@ -1382,8 +1377,6 @@ void GSDevice11::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
OMSetDepthStencilState(m_convert.dss.get(), 0);
OMSetBlendState(m_convert.bs[D3D11_COLOR_WRITE_ENABLE_ALL].get(), 0);
// ia
const float left = dRect.x * 2 / ds.x - 1.0f;
@@ -1399,8 +1392,6 @@ void GSDevice11::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
{GSVector4(right, bottom, 0.5f, 1.0f), GSVector2(sRect.z, sRect.w)},
};
IASetVertexBuffer(vertices, sizeof(vertices[0]), std::size(vertices));
IASetInputLayout(m_present.il.get());
IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
@@ -1409,7 +1400,6 @@ void GSDevice11::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
VSSetShader(m_present.vs.get(), nullptr);
// ps
PSSetShaderResource(0, sTex);
@@ -1748,7 +1738,7 @@ void GSDevice11::SetupPS(const PSSelector& sel, const GSHWDrawConfig::PSConstant
sm.AddMacro("PS_DST_FMT", sel.dst_fmt);
sm.AddMacro("PS_DEPTH_FMT", sel.depth_fmt);
sm.AddMacro("PS_PAL_FMT", sel.pal_fmt);
sm.AddMacro("PS_HDR", sel.hdr);
sm.AddMacro("PS_COLCLIP_HW", sel.colclip_hw);
sm.AddMacro("PS_RTA_CORRECTION", sel.rta_correction);
sm.AddMacro("PS_RTA_SRC_CORRECTION", sel.rta_source_correction);
sm.AddMacro("PS_COLCLIP", sel.colclip);
@@ -2182,8 +2172,6 @@ void GSDevice11::SetupDATE(GSTexture* rt, GSTexture* ds, const GSVertexPT1* vert
//
DrawPrimitive();
//
}
void* GSDevice11::IAMapVertexBuffer(u32 stride, u32 count)
@@ -2521,43 +2509,43 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
GSVector2i rtsize = (config.rt ? config.rt : config.ds)->GetSize();
GSTexture* hdr_rt = g_gs_device->GetHDRTexture();
GSTexture* colclip_rt = g_gs_device->GetColorClipTexture();
if (hdr_rt)
if (colclip_rt)
{
if (config.hdr_mode == GSHWDrawConfig::HDRMode::EarlyResolve)
if (config.colclip_mode == GSHWDrawConfig::ColClipMode::EarlyResolve)
{
const GSVector2i size = config.rt->GetSize();
const GSVector4 dRect(config.hdr_update_area);
const GSVector4 dRect(config.colclip_update_area);
const GSVector4 sRect = dRect / GSVector4(size.x, size.y).xyxy();
StretchRect(hdr_rt, sRect, config.rt, dRect, ShaderConvert::HDR_RESOLVE, false);
StretchRect(colclip_rt, sRect, config.rt, dRect, ShaderConvert::COLCLIP_RESOLVE, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
Recycle(hdr_rt);
Recycle(colclip_rt);
g_gs_device->SetHDRTexture(nullptr);
g_gs_device->SetColorClipTexture(nullptr);
hdr_rt = nullptr;
colclip_rt = nullptr;
}
else
config.ps.hdr = 1;
config.ps.colclip_hw = 1;
}
if (config.ps.hdr)
if (config.ps.colclip_hw)
{
if (!hdr_rt)
if (!colclip_rt)
{
config.hdr_update_area = config.drawarea;
config.colclip_update_area = config.drawarea;
const GSVector4 dRect = GSVector4((config.hdr_mode == GSHWDrawConfig::HDRMode::ConvertOnly) ? GSVector4i::loadh(rtsize) : config.drawarea);
const GSVector4 dRect = GSVector4((config.colclip_mode == GSHWDrawConfig::ColClipMode::ConvertOnly) ? GSVector4i::loadh(rtsize) : config.drawarea);
const GSVector4 sRect = dRect / GSVector4(rtsize.x, rtsize.y).xyxy();
hdr_rt = CreateRenderTarget(rtsize.x, rtsize.y, GSTexture::Format::HDRColor);
if (!hdr_rt)
colclip_rt = CreateRenderTarget(rtsize.x, rtsize.y, GSTexture::Format::ColorClip);
if (!colclip_rt)
return;
g_gs_device->SetHDRTexture(hdr_rt);
g_gs_device->SetColorClipTexture(colclip_rt);
// Warning: StretchRect must be called before BeginScene otherwise
// vertices will be overwritten. Trust me you don't want to do that.
StretchRect(config.rt, sRect, hdr_rt, dRect, ShaderConvert::HDR_INIT, false);
StretchRect(config.rt, sRect, colclip_rt, dRect, ShaderConvert::COLCLIP_INIT, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
}
}
@@ -2569,7 +2557,7 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
if (!primid_tex)
return;
StretchRect(hdr_rt ? hdr_rt : config.rt, GSVector4(config.drawarea) / GSVector4(rtsize).xyxy(),
StretchRect(colclip_rt ? colclip_rt : config.rt, GSVector4(config.drawarea) / GSVector4(rtsize).xyxy(),
primid_tex, GSVector4(config.drawarea), m_date.primid_init_ps[static_cast<u8>(config.datm)].get(), nullptr, false);
}
else if (config.destination_alpha != GSHWDrawConfig::DestinationAlphaMode::Off)
@@ -2585,7 +2573,7 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
{GSVector4(dst.z, -dst.w, 0.5f, 1.0f), GSVector2(src.z, src.w)},
};
SetupDATE(hdr_rt ? hdr_rt : config.rt, config.ds, vertices, config.datm);
SetupDATE(colclip_rt ? colclip_rt : config.rt, config.ds, vertices, config.datm);
}
if (config.vs.expand != GSHWDrawConfig::VSExpand::None)
@@ -2649,7 +2637,7 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
// Do not always bind the rt when it's not needed,
// only bind it when effects use it such as fbmask emulation currently
// because we copy the frame buffer and it is quite slow.
CloneTexture(hdr_rt ? hdr_rt : config.rt, &rt_copy, config.drawarea);
CloneTexture(colclip_rt ? colclip_rt : config.rt, &rt_copy, config.drawarea);
if (rt_copy)
{
if (config.require_one_barrier)
@@ -2679,7 +2667,7 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
}
SetupOM(config.depth, OMBlendSelector(config.colormask, config.blend), config.blend.constant);
OMSetRenderTargets(hdr_rt ? hdr_rt : config.rt, config.ds, &config.scissor);
OMSetRenderTargets(colclip_rt ? colclip_rt : config.rt, config.ds, &config.scissor);
DrawIndexedPrimitive();
if (config.blend_multi_pass.enable)
@@ -2714,20 +2702,20 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
if (primid_tex)
Recycle(primid_tex);
if (hdr_rt)
if (colclip_rt)
{
config.hdr_update_area = config.hdr_update_area.runion(config.drawarea);
config.colclip_update_area = config.colclip_update_area.runion(config.drawarea);
if (config.hdr_mode == GSHWDrawConfig::HDRMode::ResolveOnly || config.hdr_mode == GSHWDrawConfig::HDRMode::ConvertAndResolve)
if (config.colclip_mode == GSHWDrawConfig::ColClipMode::ResolveOnly || config.colclip_mode == GSHWDrawConfig::ColClipMode::ConvertAndResolve)
{
const GSVector2i size = config.rt->GetSize();
const GSVector4 dRect(config.hdr_update_area);
const GSVector4 dRect(config.colclip_update_area);
const GSVector4 sRect = dRect / GSVector4(size.x, size.y).xyxy();
StretchRect(hdr_rt, sRect, config.rt, dRect, ShaderConvert::HDR_RESOLVE, false);
StretchRect(colclip_rt, sRect, config.rt, dRect, ShaderConvert::COLCLIP_RESOLVE, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
Recycle(hdr_rt);
Recycle(colclip_rt);
g_gs_device->SetHDRTexture(nullptr);
g_gs_device->SetColorClipTexture(nullptr);
}
}
}

View File

@@ -29,7 +29,9 @@ DXGI_FORMAT GSTexture11::GetDXGIFormat(Format format)
switch (format)
{
case GSTexture::Format::Color: return DXGI_FORMAT_R8G8B8A8_UNORM;
case GSTexture::Format::HDRColor: return DXGI_FORMAT_R16G16B16A16_UNORM;
case GSTexture::Format::ColorHQ: return DXGI_FORMAT_R10G10B10A2_UNORM;
case GSTexture::Format::ColorHDR: return DXGI_FORMAT_R16G16B16A16_FLOAT;
case GSTexture::Format::ColorClip: return DXGI_FORMAT_R16G16B16A16_UNORM;
case GSTexture::Format::DepthStencil: return DXGI_FORMAT_R32G8X24_TYPELESS;
case GSTexture::Format::UNorm8: return DXGI_FORMAT_A8_UNORM;
case GSTexture::Format::UInt16: return DXGI_FORMAT_R16_UINT;

View File

@@ -739,7 +739,9 @@ bool GSDevice12::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
return false;
}
CompileCASPipelines();
if (!CompileCASPipelines())
return false;
if (!CompileImGuiPipeline())
return false;
@@ -1265,13 +1267,17 @@ void GSDevice12::DrawIndexedPrimitive(int offset, int count)
void GSDevice12::LookupNativeFormat(GSTexture::Format format, DXGI_FORMAT* d3d_format, DXGI_FORMAT* srv_format,
DXGI_FORMAT* rtv_format, DXGI_FORMAT* dsv_format) const
{
static constexpr std::array<std::array<DXGI_FORMAT, 4>, static_cast<int>(GSTexture::Format::BC7) + 1>
static constexpr std::array<std::array<DXGI_FORMAT, 4>, static_cast<int>(GSTexture::Format::Last) + 1>
s_format_mapping = {{
{DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // Invalid
{DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM,
DXGI_FORMAT_UNKNOWN}, // Color
{DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_FORMAT_R10G10B10A2_UNORM,
DXGI_FORMAT_UNKNOWN}, // ColorHQ
{DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT,
DXGI_FORMAT_UNKNOWN}, // ColorHDR
{DXGI_FORMAT_R16G16B16A16_UNORM, DXGI_FORMAT_R16G16B16A16_UNORM, DXGI_FORMAT_R16G16B16A16_UNORM,
DXGI_FORMAT_UNKNOWN}, // HDRColor
DXGI_FORMAT_UNKNOWN}, // ColorClip
{DXGI_FORMAT_D32_FLOAT_S8X24_UINT, DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS, DXGI_FORMAT_UNKNOWN,
DXGI_FORMAT_D32_FLOAT_S8X24_UINT}, // DepthStencil
{DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_UNKNOWN}, // UNorm8
@@ -1424,9 +1430,10 @@ void GSDevice12::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
GL_PUSH("ColorCopy Red:%d Green:%d Blue:%d Alpha:%d", red, green, blue, alpha);
const u32 index = (red ? 1 : 0) | (green ? 2 : 0) | (blue ? 4 : 0) | (alpha ? 8 : 0);
int rta_offset = (shader == ShaderConvert::RTA_CORRECTION) ? 16 : 0;
const bool allow_discard = (index == 0xf);
DoStretchRect(static_cast<GSTexture12*>(sTex), sRect, static_cast<GSTexture12*>(dTex), dRect,
m_color_copy[index].get(), false, allow_discard);
m_color_copy[index + rta_offset].get(), false, allow_discard);
}
void GSDevice12::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect,
@@ -2520,13 +2527,6 @@ bool GSDevice12::CompileConvertPipelines()
// compile color copy pipelines
gpb.SetRenderTarget(0, DXGI_FORMAT_R8G8B8A8_UNORM);
gpb.SetDepthStencilFormat(DXGI_FORMAT_UNKNOWN);
ComPtr<ID3DBlob> ps(GetUtilityPixelShader(*shader, shaderName(i)));
if (!ps)
return false;
gpb.SetPixelShader(ps.get());
for (u32 j = 16; j < 32; j++)
{
pxAssert(!m_color_copy[j]);
@@ -2540,10 +2540,10 @@ bool GSDevice12::CompileConvertPipelines()
j & 1u, (j >> 1) & 1u, (j >> 2) & 1u, (j >> 3) & 1u));
}
}
else if (i == ShaderConvert::HDR_INIT || i == ShaderConvert::HDR_RESOLVE)
else if (i == ShaderConvert::COLCLIP_INIT || i == ShaderConvert::COLCLIP_RESOLVE)
{
const bool is_setup = i == ShaderConvert::HDR_INIT;
std::array<ComPtr<ID3D12PipelineState>, 2>& arr = is_setup ? m_hdr_setup_pipelines : m_hdr_finish_pipelines;
const bool is_setup = i == ShaderConvert::COLCLIP_INIT;
std::array<ComPtr<ID3D12PipelineState>, 2>& arr = is_setup ? m_colclip_setup_pipelines : m_colclip_finish_pipelines;
for (u32 ds = 0; ds < 2; ds++)
{
pxAssert(!arr[ds]);
@@ -2554,7 +2554,7 @@ bool GSDevice12::CompileConvertPipelines()
if (!arr[ds])
return false;
D3D12::SetObjectName(arr[ds].get(), TinyString::from_format("HDR {}/copy pipeline (ds={})", is_setup ? "setup" : "finish", ds));
D3D12::SetObjectName(arr[ds].get(), TinyString::from_format("ColorClip {}/copy pipeline (ds={})", is_setup ? "setup" : "finish", ds));
}
}
}
@@ -2598,8 +2598,8 @@ bool GSDevice12::CompilePresentPipelines()
return false;
}
ComPtr<ID3DBlob> m_convert_vs = GetUtilityVertexShader(*shader, "vs_main");
if (!m_convert_vs)
ComPtr<ID3DBlob> vs = GetUtilityVertexShader(*shader, "vs_main");
if (!vs)
return false;
D3D12::GraphicsPipelineBuilder gpb;
@@ -2607,7 +2607,7 @@ bool GSDevice12::CompilePresentPipelines()
AddUtilityVertexAttributes(gpb);
gpb.SetNoCullRasterizationState();
gpb.SetNoBlendingState();
gpb.SetVertexShader(m_convert_vs.get());
gpb.SetVertexShader(vs.get());
gpb.SetDepthState(false, false, D3D12_COMPARISON_FUNC_ALWAYS);
gpb.SetNoStencilState();
gpb.SetRenderTarget(0, DXGI_FORMAT_R8G8B8A8_UNORM);
@@ -2663,7 +2663,7 @@ bool GSDevice12::CompileInterlacePipelines()
if (!m_interlace[i])
return false;
D3D12::SetObjectName(m_convert[i].get(), TinyString::from_format("Interlace pipeline {}", static_cast<int>(i)));
D3D12::SetObjectName(m_interlace[i].get(), TinyString::from_format("Interlace pipeline {}", static_cast<int>(i)));
}
return true;
@@ -2700,7 +2700,7 @@ bool GSDevice12::CompileMergePipelines()
if (!m_merge[i])
return false;
D3D12::SetObjectName(m_convert[i].get(), TinyString::from_format("Merge pipeline {}", i));
D3D12::SetObjectName(m_merge[i].get(), TinyString::from_format("Merge pipeline {}", i));
}
return true;
@@ -2780,8 +2780,8 @@ void GSDevice12::DestroyResources()
m_color_copy = {};
m_present = {};
m_convert = {};
m_hdr_setup_pipelines = {};
m_hdr_finish_pipelines = {};
m_colclip_setup_pipelines = {};
m_colclip_finish_pipelines = {};
m_date_image_setup_pipelines = {};
m_fxaa_pipeline.reset();
m_shadeboost_pipeline.reset();
@@ -2897,7 +2897,7 @@ const ID3DBlob* GSDevice12::GetTFXPixelShader(const GSHWDrawConfig::PSSelector&
sm.AddMacro("PS_DST_FMT", sel.dst_fmt);
sm.AddMacro("PS_DEPTH_FMT", sel.depth_fmt);
sm.AddMacro("PS_PAL_FMT", sel.pal_fmt);
sm.AddMacro("PS_HDR", sel.hdr);
sm.AddMacro("PS_COLCLIP_HW", sel.colclip_hw);
sm.AddMacro("PS_RTA_CORRECTION", sel.rta_correction);
sm.AddMacro("PS_RTA_SRC_CORRECTION", sel.rta_source_correction);
sm.AddMacro("PS_COLCLIP", sel.colclip);
@@ -2955,7 +2955,7 @@ GSDevice12::ComPtr<ID3D12PipelineState> GSDevice12::CreateTFXPipeline(const Pipe
{
const GSTexture::Format format = IsDATEModePrimIDInit(p.ps.date) ?
GSTexture::Format::PrimID :
(p.ps.hdr ? GSTexture::Format::HDRColor : GSTexture::Format::Color);
(p.ps.colclip_hw ? GSTexture::Format::ColorClip : GSTexture::Format::Color);
DXGI_FORMAT native_format;
LookupNativeFormat(format, nullptr, nullptr, &native_format, nullptr);
@@ -3817,7 +3817,7 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
const bool stencil_DATE = (config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::Stencil ||
config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::StencilOne);
GSTexture12* hdr_rt = static_cast<GSTexture12*>(g_gs_device->GetHDRTexture());
GSTexture12* colclip_rt = static_cast<GSTexture12*>(g_gs_device->GetColorClipTexture());
GSTexture12* draw_rt = static_cast<GSTexture12*>(config.rt);
GSTexture12* draw_ds = static_cast<GSTexture12*>(config.ds);
GSTexture12* draw_rt_clone = nullptr;
@@ -3830,15 +3830,15 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
// figure out the pipeline
UpdateHWPipelineSelector(config);
// now blit the hdr texture back to the original target
if (hdr_rt)
// now blit the colclip texture back to the original target
if (colclip_rt)
{
if (config.hdr_mode == GSHWDrawConfig::HDRMode::EarlyResolve)
if (config.colclip_mode == GSHWDrawConfig::ColClipMode::EarlyResolve)
{
GL_PUSH("Blit HDR back to RT");
GL_PUSH("Blit ColorClip back to RT");
EndRenderPass();
hdr_rt->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
colclip_rt->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
draw_rt = static_cast<GSTexture12*>(config.rt);
OMSetRenderTargets(draw_rt, draw_ds, config.scissor);
@@ -3850,19 +3850,19 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS,
draw_rt->GetUNormClearColor(), 0.0f, 0);
const GSVector4 sRect(GSVector4(config.hdr_update_area) / GSVector4(rtsize.x, rtsize.y).xyxy());
SetPipeline(m_hdr_finish_pipelines[pipe.ds].get());
SetUtilityTexture(hdr_rt, m_point_sampler_cpu);
DrawStretchRect(sRect, GSVector4(config.hdr_update_area), rtsize);
const GSVector4 sRect(GSVector4(config.colclip_update_area) / GSVector4(rtsize.x, rtsize.y).xyxy());
SetPipeline(m_colclip_finish_pipelines[pipe.ds].get());
SetUtilityTexture(colclip_rt, m_point_sampler_cpu);
DrawStretchRect(sRect, GSVector4(config.colclip_update_area), rtsize);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
Recycle(hdr_rt);
g_gs_device->SetHDRTexture(nullptr);
Recycle(colclip_rt);
g_gs_device->SetColorClipTexture(nullptr);
}
else
{
draw_rt = hdr_rt;
pipe.ps.hdr = 1;
draw_rt = colclip_rt;
pipe.ps.colclip_hw = 1;
}
}
@@ -3903,7 +3903,7 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
if (config.require_one_barrier)
{
// requires a copy of the RT
draw_rt_clone = static_cast<GSTexture12*>(CreateTexture(rtsize.x, rtsize.y, 1, hdr_rt ? GSTexture::Format::HDRColor : GSTexture::Format::Color, true));
draw_rt_clone = static_cast<GSTexture12*>(CreateTexture(rtsize.x, rtsize.y, 1, colclip_rt ? GSTexture::Format::ColorClip : GSTexture::Format::Color, true));
if (draw_rt_clone)
{
EndRenderPass();
@@ -3917,19 +3917,19 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
}
}
// Switch to hdr target for colclip rendering
if (pipe.ps.hdr)
// Switch to colclip target for colclip hw rendering
if (pipe.ps.colclip_hw)
{
if (!hdr_rt)
if (!colclip_rt)
{
config.hdr_update_area = config.drawarea;
config.colclip_update_area = config.drawarea;
EndRenderPass();
hdr_rt = static_cast<GSTexture12*>(CreateRenderTarget(rtsize.x, rtsize.y, GSTexture::Format::HDRColor, false));
if (!hdr_rt)
colclip_rt = static_cast<GSTexture12*>(CreateRenderTarget(rtsize.x, rtsize.y, GSTexture::Format::ColorClip, false));
if (!colclip_rt)
{
Console.WriteLn("D3D12: Failed to allocate HDR render target, aborting draw.");
Console.WriteLn("D3D12: Failed to allocate ColorClip render target, aborting draw.");
if (date_image)
Recycle(date_image);
@@ -3937,17 +3937,17 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
return;
}
g_gs_device->SetHDRTexture(static_cast<GSTexture*>(hdr_rt));
g_gs_device->SetColorClipTexture(static_cast<GSTexture*>(colclip_rt));
// propagate clear value through if the hdr render is the first
// propagate clear value through if the colclip render is the first
if (draw_rt->GetState() == GSTexture::State::Cleared)
{
hdr_rt->SetState(GSTexture::State::Cleared);
hdr_rt->SetClearColor(draw_rt->GetClearColor());
colclip_rt->SetState(GSTexture::State::Cleared);
colclip_rt->SetClearColor(draw_rt->GetClearColor());
}
else if (draw_rt->GetState() == GSTexture::State::Dirty)
{
GL_PUSH_("HDR Render Target Setup");
GL_PUSH_("ColorClip Render Target Setup");
draw_rt->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
}
@@ -3956,7 +3956,7 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
PSSetShaderResource(2, draw_rt, true);
}
draw_rt = hdr_rt;
draw_rt = colclip_rt;
}
// clear texture binding when it's bound to RT or DS
@@ -3966,11 +3966,10 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
PSSetShaderResource(0, nullptr, false);
}
// avoid restarting the render pass just to switch from rt+depth to rt and vice versa
if (m_in_render_pass && (m_current_render_target == draw_rt || m_current_depth_target == draw_ds))
{
// avoid restarting the render pass just to switch from rt+depth to rt and vice versa
// keep the depth even if doing HDR draws, because the next draw will probably re-enable depth
// keep the depth even if doing colclip hw draws, because the next draw will probably re-enable depth
if (!draw_rt && m_current_render_target && config.tex != m_current_render_target &&
m_current_render_target->GetSize() == draw_ds->GetSize())
{
@@ -3991,9 +3990,9 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
if (!m_in_render_pass)
{
GSVector4 clear_color = draw_rt ? draw_rt->GetUNormClearColor() : GSVector4::zero();
if (pipe.ps.hdr)
if (pipe.ps.colclip_hw)
{
// Denormalize clear color for HDR.
// Denormalize clear color for hw colclip.
clear_color *= GSVector4::cxpr(255.0f / 65535.0f, 255.0f / 65535.0f, 255.0f / 65535.0f, 1.0f);
}
BeginRenderPass(GetLoadOpForTexture(draw_rt),
@@ -4007,13 +4006,13 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
clear_color, draw_ds ? draw_ds->GetClearDepth() : 0.0f, 1);
}
// rt -> hdr blit if enabled
if (hdr_rt && (config.hdr_mode == GSHWDrawConfig::HDRMode::ConvertOnly || config.hdr_mode == GSHWDrawConfig::HDRMode::ConvertAndResolve) && config.rt->GetState() == GSTexture::State::Dirty)
// rt -> colclip hw blit if enabled
if (colclip_rt && (config.colclip_mode == GSHWDrawConfig::ColClipMode::ConvertOnly || config.colclip_mode == GSHWDrawConfig::ColClipMode::ConvertAndResolve) && config.rt->GetState() == GSTexture::State::Dirty)
{
SetUtilityTexture(static_cast<GSTexture12*>(config.rt), m_point_sampler_cpu);
SetPipeline(m_hdr_setup_pipelines[pipe.ds].get());
SetPipeline(m_colclip_setup_pipelines[pipe.ds].get());
const GSVector4 drawareaf = GSVector4((config.hdr_mode == GSHWDrawConfig::HDRMode::ConvertOnly) ? GSVector4i::loadh(rtsize) : config.drawarea);
const GSVector4 drawareaf = GSVector4((config.colclip_mode == GSHWDrawConfig::ColClipMode::ConvertOnly) ? GSVector4i::loadh(rtsize) : config.drawarea);
const GSVector4 sRect(drawareaf / GSVector4(rtsize.x, rtsize.y).xyxy());
DrawStretchRect(sRect, GSVector4(drawareaf), rtsize);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
@@ -4021,9 +4020,9 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
GL_POP();
}
// VB/IB upload, if we did DATE setup and it's not HDR this has already been done
// VB/IB upload, if we did DATE setup and it's not colclip hw this has already been done
SetPrimitiveTopology(s_primitive_topology_mapping[static_cast<u8>(config.topology)]);
if (!date_image || hdr_rt)
if (!date_image || colclip_rt)
UploadHWDrawVerticesAndIndices(config);
// now we can do the actual draw
@@ -4067,17 +4066,17 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
if (date_image)
Recycle(date_image);
// now blit the hdr texture back to the original target
if (hdr_rt)
// now blit the colclip texture back to the original target
if (colclip_rt)
{
config.hdr_update_area = config.hdr_update_area.runion(config.drawarea);
config.colclip_update_area = config.colclip_update_area.runion(config.drawarea);
if ((config.hdr_mode == GSHWDrawConfig::HDRMode::ResolveOnly || config.hdr_mode == GSHWDrawConfig::HDRMode::ConvertAndResolve))
if ((config.colclip_mode == GSHWDrawConfig::ColClipMode::ResolveOnly || config.colclip_mode == GSHWDrawConfig::ColClipMode::ConvertAndResolve))
{
GL_PUSH("Blit HDR back to RT");
GL_PUSH("Blit ColorClip back to RT");
EndRenderPass();
hdr_rt->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
colclip_rt->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
draw_rt = static_cast<GSTexture12*>(config.rt);
OMSetRenderTargets(draw_rt, draw_ds, config.scissor);
@@ -4089,14 +4088,14 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS,
draw_rt->GetUNormClearColor(), 0.0f, 0);
const GSVector4 sRect(GSVector4(config.hdr_update_area) / GSVector4(rtsize.x, rtsize.y).xyxy());
SetPipeline(m_hdr_finish_pipelines[pipe.ds].get());
SetUtilityTexture(hdr_rt, m_point_sampler_cpu);
DrawStretchRect(sRect, GSVector4(config.hdr_update_area), rtsize);
const GSVector4 sRect(GSVector4(config.colclip_update_area) / GSVector4(rtsize.x, rtsize.y).xyxy());
SetPipeline(m_colclip_finish_pipelines[pipe.ds].get());
SetUtilityTexture(colclip_rt, m_point_sampler_cpu);
DrawStretchRect(sRect, GSVector4(config.colclip_update_area), rtsize);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
Recycle(hdr_rt);
g_gs_device->SetHDRTexture(nullptr);
Recycle(colclip_rt);
g_gs_device->SetColorClipTexture(nullptr);
}
}
}

View File

@@ -315,8 +315,8 @@ private:
std::array<ComPtr<ID3D12PipelineState>, 32> m_color_copy{};
std::array<ComPtr<ID3D12PipelineState>, 2> m_merge{};
std::array<ComPtr<ID3D12PipelineState>, NUM_INTERLACE_SHADERS> m_interlace{};
std::array<ComPtr<ID3D12PipelineState>, 2> m_hdr_setup_pipelines{}; // [depth]
std::array<ComPtr<ID3D12PipelineState>, 2> m_hdr_finish_pipelines{}; // [depth]
std::array<ComPtr<ID3D12PipelineState>, 2> m_colclip_setup_pipelines{}; // [depth]
std::array<ComPtr<ID3D12PipelineState>, 2> m_colclip_finish_pipelines{}; // [depth]
std::array<std::array<ComPtr<ID3D12PipelineState>, 4>, 2> m_date_image_setup_pipelines{}; // [depth][datm]
ComPtr<ID3D12PipelineState> m_fxaa_pipeline;
ComPtr<ID3D12PipelineState> m_shadeboost_pipeline;

View File

@@ -35,52 +35,6 @@ static bool s_nativeres;
// Partial level, broken on all renderers.
////////////////////////////////////////////////////////////////////////////////
bool GSHwHack::GSC_DeathByDegreesTekkenNinaWilliams(GSRendererHW& r, int& skip)
{
// Note: Game also has issues with texture shuffle not supported on strange clamp mode.
// See https://forums.pcsx2.net/Thread-GSDX-Texture-Cache-Bug-Report-Death-By-Degrees-SLUS-20934-NTSC
if (skip == 0)
{
if (!s_nativeres && RTME && RFBP == 0 && RTBP0 == 0x34a0 && RTPSM == PSMCT32)
{
// Don't enable hack on native res if crc is below aggressive.
// Upscaling issue similar to Tekken 5.
skip = 1; // Animation pane
}
#if 0
else if (RFBP == 0x3500 && RTPSM == PSMT8 && RFBMSK == 0xFFFF00FF)
{
// Needs to be further tested so put it on Aggressive for now, likely channel shuffle.
skip = 4; // Underwater white fog
}
#endif
}
else
{
if (!s_nativeres && RTME && (RFBP | RTBP0 | RFPSM | RTPSM) && RFBMSK == 0x00FFFFFF)
{
// Needs to be further tested so assume it's related with the upscaling hack.
skip = 1; // Animation speed
}
}
return true;
}
bool GSHwHack::GSC_GiTS(GSRendererHW& r, int& skip)
{
if (skip == 0)
{
if (RTME && RFBP == 0x03000 && RFPSM == PSMCT32 && RTPSM == PSMT8)
{
// Channel effect not properly supported yet
skip = 9;
}
}
return true;
}
// Channel effect not properly supported yet
bool GSHwHack::GSC_Manhunt2(GSRendererHW& r, int& skip)
{
@@ -131,6 +85,22 @@ bool GSHwHack::GSC_SacredBlaze(GSRendererHW& r, int& skip)
return true;
}
bool GSHwHack::GSC_GuitarHero(GSRendererHW& r, int& skip)
{
// Crowd sprite generation is a mess, better done in software.
if (skip == 0)
{
if (RTBW <= 4 && RTME && RFBW <= 4 && (r.m_context->TEX1.MMIN & 1) == 0)
{
r.ClearGSLocalMemory(r.m_context->offset.zb, r.m_r, 0);
r.SwPrimRender(r, RFBP != 0x2DC0, false);
skip = 1;
}
}
return true;
}
bool GSHwHack::GSC_SFEX3(GSRendererHW& r, int& skip)
{
if (skip == 0)
@@ -248,19 +218,25 @@ bool GSHwHack::GSC_Tekken5(GSRendererHW& r, int& skip)
if (!s_nativeres && r.PRIM->PRIM == GS_SPRITE && RTME && RTEX0.TFX == 1 && RFPSM == RTPSM && RTPSM == PSMCT32 && RFBMSK == 0xFF000000 && r.m_index.tail > 2)
{
GSVertex* v = &r.m_vertex.buff[0];
// Don't enable hack on native res.
// Fixes ghosting/blur effect and white lines appearing in stages: Moonfit Wilderness, Acid Rain - caused by upscaling.
// Game copies the framebuffer as individual page rects with slight offsets (like 1/16 of a pixel etc) which doesn't wokr well with upscaling.
// This should catch all the scenarios, maybe overdoes it, but it's for 1 game and it's non-detrimental, it's better than squares all over the screen.
const GSVector4i draw_size(r.m_vt.m_min.p.x, r.m_vt.m_min.p.y, r.m_vt.m_max.p.x + 1.0f, r.m_vt.m_max.p.y + 1.0f);
const GSVector4i read_size(r.m_vt.m_min.t.x, r.m_vt.m_min.t.y, r.m_vt.m_max.t.x + 0.5f, r.m_vt.m_max.t.y + 0.5f);
r.ReplaceVerticesWithSprite(draw_size, read_size, GSVector2i(read_size.width(), read_size.height()), draw_size);
}
else if (RZTST == 1 && RTME && (RFBP == 0x02bc0 || RFBP == 0x02be0 || RFBP == 0x02d00 || RFBP == 0x03480 || RFBP == 0x034a0) && RFPSM == RTPSM && RTBP0 == 0x00000 && RTPSM == PSMCT32)
{
// The moving display effect(flames) is not emulated properly in the entire screen so let's remove the effect in the stage: Burning Temple. Related to half screen bottom issue.
// Fixes black lines in the stage: Burning Temple - caused by upscaling. Note the black lines can also be fixed with Merge Sprite hack.
skip = 2;
if (v[0].XYZ.X & 0xF)
{
const GSVector4i draw_size(r.m_vt.m_min.p.x, r.m_vt.m_min.p.y, r.m_vt.m_max.p.x + 1.0f, r.m_vt.m_max.p.y + 1.0f);
const GSVector4i read_size(r.m_vt.m_min.t.x, r.m_vt.m_min.t.y, r.m_vt.m_max.t.x + 0.5f, r.m_vt.m_max.t.y + 0.5f);
r.ReplaceVerticesWithSprite(draw_size, read_size, GSVector2i(read_size.width(), read_size.height()), draw_size);
}
else
{
// Fixes the alignment of the two halves for the heat haze on the temple stage.
for (u32 i = 0; i < r.m_index.tail; i+=2)
{
v[i].XYZ.Y -= 0x8;
}
}
}
}
@@ -766,7 +742,7 @@ bool GSHwHack::GSC_PolyphonyDigitalGames(GSRendererHW& r, int& skip)
for (u32 channel = 0; channel < 3; channel++)
{
const GIFRegTEX0 TEX0 = GIFRegTEX0::Create(base + channel * page_offset, RTEX0.TBW, PSMCT32);
const GIFRegTEX0 TEX0 = GIFRegTEX0::Create(base + channel * page_offset, 10, PSMCT32);
GSTextureCache::Target* dst = g_texture_cache->LookupTarget(TEX0, src->GetUnscaledSize(), src->GetScale(), GSTextureCache::RenderTarget, true, fbmsk);
if (!dst)
{
@@ -799,6 +775,35 @@ bool GSHwHack::GSC_PolyphonyDigitalGames(GSRendererHW& r, int& skip)
}
}
bool GSHwHack::GSC_Battlefield2(GSRendererHW& r, int& skip)
{
if (skip == 0)
{
if (RZBP >= RFBP && RFBP >= 0x2000 && RZBP >= 0x2700 && ((RZBP - RFBP) == 0x700))
{
skip = 7;
GIFRegTEX0 TEX0 = {};
TEX0.TBP0 = RFBP;
TEX0.TBW = 8;
GSTextureCache::Target* dst = g_texture_cache->LookupTarget(TEX0, r.GetTargetSize(), r.GetTextureScaleFactor(), GSTextureCache::DepthStencil);
if (!dst)
dst = g_texture_cache->CreateTarget(TEX0, r.GetTargetSize(), r.GetValidSize(nullptr), r.GetTextureScaleFactor(), GSTextureCache::DepthStencil,
true, 0, false, false, false, GSVector4i(0,0,1,1), nullptr);
if (dst)
{
float dc = r.m_vertex.buff[1].XYZ.Z;
g_gs_device->ClearDepth(dst->m_texture, dc * std::exp2(-32.0f));
}
}
}
return true;
}
bool GSHwHack::GSC_BlueTongueGames(GSRendererHW& r, int& skip)
{
GSDrawingContext* context = r.m_context;
@@ -915,44 +920,6 @@ bool GSHwHack::GSC_MetalGearSolid3(GSRendererHW& r, int& skip)
return true;
}
bool GSHwHack::GSC_BigMuthaTruckers(GSRendererHW& r, int& skip)
{
// Rendering pattern:
// CRTC frontbuffer at 0x0 is interlaced (half vertical resolution),
// game needs to do a depth effect (so green channel to alpha),
// but there is a vram limitation so green is pushed into the alpha channel of the CRCT buffer,
// vertical resolution is half so only half is processed at once
// We, however, don't have this limitation so we'll replace the draw with a full-screen TS.
const GIFRegTEX0& Texture = RTEX0;
GIFRegTEX0 Frame = {};
Frame.TBW = RFRAME.FBW;
Frame.TBP0 = RFRAME.Block();
const int frame_offset_pal = GSLocalMemory::GetEndBlockAddress(0xa00, 10, PSMCT32, GSVector4i(0, 0, 640, 256)) + 1;
const int frame_offset_ntsc = GSLocalMemory::GetEndBlockAddress(0xa00, 10, PSMCT32, GSVector4i(0, 0, 640, 224)) + 1;
const GSVector4i rect = GSVector4i(r.m_vt.m_min.p.x, r.m_vt.m_min.p.y, r.m_vt.m_max.p.x, r.m_vt.m_max.p.y);
if (RPRIM->TME && Frame.TBW == 10 && Texture.TBW == 10 && Texture.PSM == PSMCT16 && ((rect.w == 512 && Frame.TBP0 == frame_offset_pal) || (Frame.TBP0 == frame_offset_ntsc && rect.w == 448)))
{
// 224 ntsc, 256 pal.
GL_INS("GSC_BigMuthaTruckers half bottom offset %d", r.m_context->XYOFFSET.OFX >> 4);
const size_t count = r.m_vertex.next;
GSVertex* v = &r.m_vertex.buff[0];
const u16 offset = (u16)rect.w * 16;
for (size_t i = 0; i < count; i++)
v[i].XYZ.Y += offset;
r.m_vt.m_min.p.y += rect.w;
r.m_vt.m_max.p.y += rect.w;
r.m_cached_ctx.FRAME.FBP = 0x50; // 0xA00 >> 5
}
return true;
}
bool GSHwHack::GSC_HitmanBloodMoney(GSRendererHW& r, int& skip)
{
// The game does a stupid thing where it backs up the last 2 pages of the framebuffer with shuffles, uploads a CT32 texture to it
@@ -998,6 +965,10 @@ bool GSHwHack::OI_PointListPalette(GSRendererHW& r, GSTexture* rt, GSTexture* ds
&& r.m_cached_ctx.FRAME.FBMSK == 0 // No frame buffer masking.
)
{
const int mask = (r.m_vt.m_max.p.xyxy() == r.m_vt.m_min.p.xyxy()).mask();
if (mask == 0xf)
return true;
const u32 FBP = r.m_cached_ctx.FRAME.Block();
const u32 FBW = r.m_cached_ctx.FRAME.FBW;
GL_INS("PointListPalette - m_r = <%d, %d => %d, %d>, n_vertices = %u, FBP = 0x%x, FBW = %u", r.m_r.x, r.m_r.y, r.m_r.z, r.m_r.w, n_vertices, FBP, FBW);
@@ -1099,7 +1070,7 @@ bool GSHwHack::OI_SonicUnleashed(GSRendererHW& r, GSTexture* rt, GSTexture* ds,
// compute shadow in RG,
// save result in alpha with a TS,
// Restore RG channel that we previously copied to render shadows.
// Important note: The game downsizes the target to half height, then later expands it back up to full size, that's why PCSX2 doesn't like it, we don't support that behaviour.
const GIFRegTEX0& Texture = RTEX0;
GIFRegTEX0 Frame = {};
@@ -1110,20 +1081,48 @@ bool GSHwHack::OI_SonicUnleashed(GSRendererHW& r, GSTexture* rt, GSTexture* ds,
if ((!rt) || (!RPRIM->TME) || (GSLocalMemory::m_psm[Texture.PSM].bpp != 16) || (GSLocalMemory::m_psm[Frame.PSM].bpp != 16) || (Texture.TBP0 == Frame.TBP0) || (Frame.TBW != 16 && Texture.TBW != 16))
return true;
GL_INS("OI_SonicUnleashed replace draw by a copy");
GL_INS("OI_SonicUnleashed replace draw by a copy draw %d", r.s_n);
GSTextureCache::Target* src = g_texture_cache->LookupTarget(Texture, GSVector2i(1, 1), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget);
GSTextureCache::Target* src = g_texture_cache->LookupTarget(Texture, GSVector2i(1, 1), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget, true, 0, false, false, true, true, GSVector4i::zero(), true);
if (!src)
return true;
const GSVector2i src_size(src->m_texture->GetSize());
GSTextureCache::Target* rt_again = g_texture_cache->LookupTarget(Frame, src_size, src->m_scale, GSTextureCache::RenderTarget);
if ((rt_again->m_TEX0.PSM & 0x3) == PSMCT16)
{
GSVector4 dRect;
GSVector4 source_rect = GSVector4(static_cast<float>(rt_again->m_valid.x) / static_cast<float>(rt_again->m_unscaled_size.x), static_cast<float>(rt_again->m_valid.y) / static_cast<float>(rt_again->m_unscaled_size.y),
static_cast<float>(rt_again->m_valid.z) / static_cast<float>(rt_again->m_unscaled_size.x), static_cast<float>(rt_again->m_valid.w) / static_cast<float>(rt_again->m_unscaled_size.y));
dRect = GSVector4(rt_again->m_valid) * rt_again->m_scale;
dRect.y /= 2;
dRect.w /= 2;
rt_again->m_valid.y /= 2;
rt_again->m_valid.w /= 2;
rt_again->m_TEX0.PSM = PSMCT32;
GSTexture* tex = g_gs_device->CreateRenderTarget(rt_again->m_unscaled_size.x * rt_again->m_scale, rt_again->m_unscaled_size.y * rt_again->m_scale, GSTexture::Format::Color, false);
if (!tex)
return false;
g_gs_device->StretchRect(rt_again->m_texture, source_rect, tex, dRect, ShaderConvert::COPY, false);
g_gs_device->Recycle(rt_again->m_texture);
rt_again->m_texture = tex;
rt = tex;
}
GSVector2i rt_size(rt->GetSize());
// This is awful, but so is the CRC hack... it's a texture shuffle split horizontally instead of vertically.
if (rt_size.x < src_size.x || rt_size.y < src_size.y)
{
GSTextureCache::Target* rt_again = g_texture_cache->LookupTarget(Frame, src_size, src->m_scale, GSTextureCache::RenderTarget);
if (rt_again->m_unscaled_size.x < src->m_unscaled_size.x || rt_again->m_unscaled_size.y < src->m_unscaled_size.y)
{
GSVector2i new_size = GSVector2i(std::max(rt_again->m_unscaled_size.x, src->m_unscaled_size.x),
@@ -1134,10 +1133,12 @@ bool GSHwHack::OI_SonicUnleashed(GSRendererHW& r, GSTexture* rt, GSTexture* ds,
rt_again->UpdateDrawn(GSVector4i::loadh(new_size));
}
}
const GSVector2i copy_size(std::min(rt_size.x, src_size.x), std::min(rt_size.y, src_size.y));
const GSVector4 sRect(0.0f, 0.0f, static_cast<float>(copy_size.x) / static_cast<float>(src_size.x), static_cast<float>(copy_size.y) / static_cast<float>(src_size.y));
// This is kind of a bodge because the game confuses everything since the source is really 16bit and it assumes it's really drawing 16bit on the copy back, resizing the target.
const GSVector4 dRect(0, 0, copy_size.x, copy_size.y);
g_gs_device->StretchRect(src->m_texture, sRect, rt, dRect, true, true, true, false);
@@ -1199,43 +1200,6 @@ bool GSHwHack::OI_BurnoutGames(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GS
return false;
}
bool GSHwHack::GSC_Battlefield2(GSRendererHW& r, int& skip)
{
if (skip == 0)
{
if (RZBP >= RFBP && RFBP >= 0x2000 && RZBP >= 0x2700 && ((RZBP - RFBP) == 0x700))
{
skip = 7;
GIFRegTEX0 TEX0 = {};
TEX0.TBP0 = RFBP;
TEX0.TBW = 8;
GSTextureCache::Target* dst = g_texture_cache->LookupTarget(TEX0, r.GetTargetSize(), r.GetTextureScaleFactor(), GSTextureCache::DepthStencil);
if (dst)
{
g_gs_device->ClearDepth(dst->m_texture, 0.0f);
}
}
}
return true;
}
bool GSHwHack::OI_Battlefield2(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
{
if (!RPRIM->TME || RFRAME.Block() > 0xD00 || RTEX0.TBP0 > 0x1D00)
return true;
if (rt && t && RFRAME.Block() == 0 && RTEX0.TBP0 == 0x1000)
{
const GSVector4i rc(0, 0, std::min(rt->GetWidth(), t->m_texture->GetWidth()), std::min(rt->GetHeight(), t->m_texture->GetHeight()));
g_gs_device->CopyRect(t->m_texture, rt, rc, 0, 0);
}
g_texture_cache->InvalidateTemporarySource();
return false;
}
bool GSHwHack::OI_HauntingGround(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
{
// Haunting Ground clears two targets by doing a direct colour write at 0x3000, covering a target at 0x3380.
@@ -1520,6 +1484,7 @@ const GSHwHack::Entry<GSRendererHW::GSC_Ptr> GSHwHack::s_get_skip_count_function
CRC_F(GSC_Manhunt2),
CRC_F(GSC_MidnightClub3),
CRC_F(GSC_SacredBlaze),
CRC_F(GSC_GuitarHero),
CRC_F(GSC_SakuraWarsSoLongMyLove),
CRC_F(GSC_Simple2000Vol114),
CRC_F(GSC_SFEX3),
@@ -1530,14 +1495,13 @@ const GSHwHack::Entry<GSRendererHW::GSC_Ptr> GSHwHack::s_get_skip_count_function
CRC_F(GSC_ZettaiZetsumeiToshi2),
CRC_F(GSC_BlackAndBurnoutSky),
CRC_F(GSC_BlueTongueGames),
CRC_F(GSC_Battlefield2),
CRC_F(GSC_NFSUndercover),
CRC_F(GSC_PolyphonyDigitalGames),
CRC_F(GSC_MetalGearSolid3),
CRC_F(GSC_HitmanBloodMoney),
CRC_F(GSC_Battlefield2),
// Channel Effect
CRC_F(GSC_GiTS),
CRC_F(GSC_SteambotChronicles),
// Depth Issue
@@ -1546,10 +1510,6 @@ const GSHwHack::Entry<GSRendererHW::GSC_Ptr> GSHwHack::s_get_skip_count_function
// Half Screen bottom issue
CRC_F(GSC_Tekken5),
// Texture shuffle
CRC_F(GSC_DeathByDegreesTekkenNinaWilliams), // + Upscaling issues
CRC_F(GSC_BigMuthaTruckers),
// Upscaling hacks
CRC_F(GSC_UltramanFightingEvolution),
};
@@ -1561,7 +1521,6 @@ const GSHwHack::Entry<GSRendererHW::OI_Ptr> GSHwHack::s_before_draw_functions[]
CRC_F(OI_SonicUnleashed),
CRC_F(OI_ArTonelico2),
CRC_F(OI_BurnoutGames),
CRC_F(OI_Battlefield2),
CRC_F(OI_HauntingGround),
};

View File

@@ -6,10 +6,9 @@
class GSHwHack
{
public:
static bool GSC_DeathByDegreesTekkenNinaWilliams(GSRendererHW& r, int& skip);
static bool GSC_GiTS(GSRendererHW& r, int& skip);
static bool GSC_Manhunt2(GSRendererHW& r, int& skip);
static bool GSC_SacredBlaze(GSRendererHW& r, int& skip);
static bool GSC_GuitarHero(GSRendererHW& r, int& skip);
static bool GSC_SFEX3(GSRendererHW& r, int& skip);
static bool GSC_DTGames(GSRendererHW& r, int& skip);
static bool GSC_Tekken5(GSRendererHW& r, int& skip);
@@ -26,11 +25,10 @@ public:
static bool GSC_UrbanReign(GSRendererHW& r, int& skip);
static bool GSC_SteambotChronicles(GSRendererHW& r, int& skip);
static bool GSC_BlueTongueGames(GSRendererHW& r, int& skip);
static bool GSC_Battlefield2(GSRendererHW& r, int& skip);
static bool GSC_NFSUndercover(GSRendererHW& r, int& skip);
static bool GSC_Battlefield2(GSRendererHW& r, int& skip);
static bool GSC_PolyphonyDigitalGames(GSRendererHW& r, int& skip);
static bool GSC_MetalGearSolid3(GSRendererHW& r, int& skip);
static bool GSC_BigMuthaTruckers(GSRendererHW& r, int& skip);
static bool GSC_HitmanBloodMoney(GSRendererHW& r, int& skip);
static bool OI_PointListPalette(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
@@ -39,7 +37,6 @@ public:
static bool OI_SonicUnleashed(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
static bool OI_ArTonelico2(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
static bool OI_BurnoutGames(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
static bool OI_Battlefield2(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
static bool OI_HauntingGround(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
static bool MV_Growlanser(GSRendererHW& r);

File diff suppressed because it is too large Load Diff

View File

@@ -92,9 +92,9 @@ private:
void DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Target* ds, GSTextureCache::Source* tex, const TextureMinMaxResult& tmm);
void ResetStates();
void SetupIA(float target_scale, float sx, float sy);
void SetupIA(float target_scale, float sx, float sy, bool req_vert_backup);
void EmulateTextureShuffleAndFbmask(GSTextureCache::Target* rt, GSTextureCache::Source* tex);
bool EmulateChannelShuffle(GSTextureCache::Target* src, bool test_only);
bool EmulateChannelShuffle(GSTextureCache::Target* src, bool test_only, GSTextureCache::Target* rt = nullptr);
void EmulateBlending(int rt_alpha_min, int rt_alpha_max, const bool DATE, bool& DATE_PRIMID, bool& DATE_BARRIER, GSTextureCache::Target* rt,
bool can_scale_rt_alpha, bool& new_rt_alpha_scale);
void CleanupDraw(bool invalidate_temp_src);
@@ -111,14 +111,16 @@ private:
void EmulateATST(float& AREF, GSHWDrawConfig::PSSelector& ps, bool pass_2);
void SetTCOffset();
bool NextDrawHDR() const;
bool NextDrawColClip() const;
bool IsPossibleChannelShuffle() const;
bool IsPageCopy() const;
bool NextDrawMatchesShuffle() const;
bool IsSplitTextureShuffle(GSTextureCache::Target* rt);
bool IsSplitTextureShuffle(GIFRegTEX0& rt_TEX0, GSVector4i& valid_area);
GSVector4i GetSplitTextureShuffleDrawRect() const;
u32 GetEffectiveTextureShuffleFbmsk() const;
static GSVector4i GetDrawRectForPages(u32 bw, u32 psm, u32 num_pages);
bool IsSinglePageDraw() const;
bool TryToResolveSinglePageFramebuffer(GIFRegFRAME& FRAME, bool only_next_draw);
bool IsSplitClearActive() const;
@@ -172,7 +174,13 @@ private:
u32 m_last_channel_shuffle_fbmsk = 0;
u32 m_last_channel_shuffle_fbp = 0;
u32 m_last_channel_shuffle_tbp = 0;
u32 m_last_channel_shuffle_end_block = 0;
u32 m_channel_shuffle_width = 0;
GSVector4i m_channel_shuffle_src_valid = GSVector4i::zero();
bool m_full_screen_shuffle = false;
GSTextureCache::Target* m_last_rt;
GIFRegFRAME m_split_clear_start = {};
GIFRegZBUF m_split_clear_start_Z = {};
@@ -199,6 +207,7 @@ public:
__fi static GSRendererHW* GetInstance() { return static_cast<GSRendererHW*>(g_gs_renderer.get()); }
__fi HWCachedCtx* GetCachedCtx() { return &m_cached_ctx; }
__fi u32 GetLastChannelShuffleFBP() { return m_last_channel_shuffle_fbp; }
void Destroy() override;
void UpdateRenderFixes() override;

File diff suppressed because it is too large Load Diff

View File

@@ -206,6 +206,12 @@ public:
bool operator()(const PaletteKey& lhs, const PaletteKey& rhs) const;
};
struct TempZAddress
{
u32 ZBP;
u32 offset;
};
class Target : public Surface
{
public:
@@ -238,7 +244,7 @@ public:
static Target* Create(GIFRegTEX0 TEX0, int w, int h, float scale, int type, bool clear);
__fi bool HasValidAlpha() const { return (m_valid_alpha_low | m_valid_alpha_high); }
bool HasValidBitsForFormat(u32 psm, bool req_color, bool req_alpha);
bool HasValidBitsForFormat(u32 psm, bool req_color, bool req_alpha, bool width_match);
void ResizeDrawn(const GSVector4i& rect);
void UpdateDrawn(const GSVector4i& rect, bool can_resize = true);
@@ -257,7 +263,7 @@ public:
void UpdateValidChannels(u32 psm, u32 fbmsk);
/// Resizes target texture, DOES NOT RESCALE.
bool ResizeTexture(int new_unscaled_width, int new_unscaled_height, bool recycle_old = true);
bool ResizeTexture(int new_unscaled_width, int new_unscaled_height, bool recycle_old = true, bool require_offset = false, GSVector4i offset = GSVector4i::zero(), bool keep_old = false);
private:
void UpdateTextureDebugName();
@@ -427,6 +433,8 @@ protected:
std::unordered_map<SurfaceOffsetKey, SurfaceOffset, SurfaceOffsetKeyHash, SurfaceOffsetKeyEqual> m_surface_offset_cache;
Source* m_temporary_source = nullptr; // invalidated after the draw
GSTexture* m_temporary_z = nullptr; // invalidated after the draw
TempZAddress m_temporary_z_info;
std::unique_ptr<GSDownloadTexture> m_color_download_texture;
std::unique_ptr<GSDownloadTexture> m_uint16_download_texture;
@@ -491,7 +499,7 @@ public:
Target* FindTargetOverlap(Target* target, int type, int psm);
Target* LookupTarget(GIFRegTEX0 TEX0, const GSVector2i& size, float scale, int type, bool used = true, u32 fbmask = 0,
bool is_frame = false, bool preload = GSConfig.PreloadFrameWithGSData, bool preserve_rgb = true, bool preserve_alpha = true,
const GSVector4i draw_rc = GSVector4i::zero(), bool is_shuffle = false, bool possible_clear = false, bool preserve_scale = false);
const GSVector4i draw_rc = GSVector4i::zero(), bool is_shuffle = false, bool possible_clear = false, bool preserve_scale = false, GSTextureCache::Source* src = nullptr, GSTextureCache::Target* ds = nullptr, int offset = -1);
Target* CreateTarget(GIFRegTEX0 TEX0, const GSVector2i& size, const GSVector2i& valid_size,float scale, int type, bool used = true, u32 fbmask = 0,
bool is_frame = false, bool preload = GSConfig.PreloadFrameWithGSData, bool preserve_target = true,
const GSVector4i draw_rc = GSVector4i::zero(), GSTextureCache::Source* src = nullptr);
@@ -508,7 +516,7 @@ public:
bool HasTargetInHeightCache(u32 bp, u32 fbw, u32 psm, u32 max_age = std::numeric_limits<u32>::max(), bool move_front = true);
bool Has32BitTarget(u32 bp);
void InvalidateContainedTargets(u32 start_bp, u32 end_bp, u32 write_psm = PSMCT32);
void InvalidateContainedTargets(u32 start_bp, u32 end_bp, u32 write_psm = PSMCT32, u32 write_bw = 1);
void InvalidateVideoMemType(int type, u32 bp, u32 write_psm = PSMCT32, u32 write_fbmsk = 0, bool dirty_only = false);
void InvalidateVideoMemSubTarget(GSTextureCache::Target* rt);
void InvalidateVideoMem(const GSOffset& off, const GSVector4i& r, bool target = true);
@@ -517,7 +525,7 @@ public:
/// Removes any sources which point to the specified target.
void InvalidateSourcesFromTarget(const Target* t);
/// Replaces a source's texture externally. Required for some CRC hacks.
/// Removes any sources which point to the same address as a new target.
void ReplaceSourceTexture(Source* s, GSTexture* new_texture, float new_scale, const GSVector2i& new_unscaled_size,
HashCacheEntry* hc_entry, bool new_texture_is_shared);
@@ -551,6 +559,12 @@ public:
/// Invalidates a temporary source, a partial copy only created from the current RT/DS for the current draw.
void InvalidateTemporarySource();
void SetTemporaryZ(GSTexture* temp_z);
GSTexture* GetTemporaryZ();
TempZAddress GetTemporaryZInfo();
void SetTemporaryZInfo(u32 address, u32 offset);
/// Invalidates a temporary Z, a partial copy only created from the current DS for the current draw when Z is not offset but RT is.
void InvalidateTemporaryZ();
/// Injects a texture into the hash cache, by using GSTexture::Swap(), transitively applying to all sources. Ownership of tex is transferred.
void InjectHashCacheTexture(const HashCacheKey& key, GSTexture* tex, const std::pair<u8, u8>& alpha_minmax);

View File

@@ -465,7 +465,7 @@ static bool ParseDDSHeader(std::FILE* fp, DDSLoadInfo* info)
}
const GSDevice::FeatureSupport features(g_gs_device->Features());
if (header.ddspf.dwFourCC == MAKEFOURCC('D', 'X', 'T', '1') || dxt10_format == 71)
if (header.ddspf.dwFourCC == MAKEFOURCC('D', 'X', 'T', '1') || dxt10_format == 71 /*DXGI_FORMAT_BC1_UNORM*/)
{
info->format = GSTexture::Format::BC1;
info->block_size = 4;
@@ -473,7 +473,7 @@ static bool ParseDDSHeader(std::FILE* fp, DDSLoadInfo* info)
if (!features.dxt_textures)
return false;
}
else if (header.ddspf.dwFourCC == MAKEFOURCC('D', 'X', 'T', '2') || header.ddspf.dwFourCC == MAKEFOURCC('D', 'X', 'T', '3') || dxt10_format == 74)
else if (header.ddspf.dwFourCC == MAKEFOURCC('D', 'X', 'T', '2') || header.ddspf.dwFourCC == MAKEFOURCC('D', 'X', 'T', '3') || dxt10_format == 74 /*DXGI_FORMAT_BC2_UNORM*/)
{
info->format = GSTexture::Format::BC2;
info->block_size = 4;
@@ -481,7 +481,7 @@ static bool ParseDDSHeader(std::FILE* fp, DDSLoadInfo* info)
if (!features.dxt_textures)
return false;
}
else if (header.ddspf.dwFourCC == MAKEFOURCC('D', 'X', 'T', '4') || header.ddspf.dwFourCC == MAKEFOURCC('D', 'X', 'T', '5') || dxt10_format == 77)
else if (header.ddspf.dwFourCC == MAKEFOURCC('D', 'X', 'T', '4') || header.ddspf.dwFourCC == MAKEFOURCC('D', 'X', 'T', '5') || dxt10_format == 77 /*DXGI_FORMAT_BC3_UNORM*/)
{
info->format = GSTexture::Format::BC3;
info->block_size = 4;
@@ -489,7 +489,7 @@ static bool ParseDDSHeader(std::FILE* fp, DDSLoadInfo* info)
if (!features.dxt_textures)
return false;
}
else if (dxt10_format == 98)
else if (dxt10_format == 98 /*DXGI_FORMAT_BC7_UNORM*/)
{
info->format = GSTexture::Format::BC7;
info->block_size = 4;

View File

@@ -256,11 +256,9 @@ public:
MRCOwned<id<MTLRenderPipelineState>> m_clut_pipeline[2];
MRCOwned<id<MTLRenderPipelineState>> m_stencil_clear_pipeline;
MRCOwned<id<MTLRenderPipelineState>> m_primid_init_pipeline[2][4];
MRCOwned<id<MTLRenderPipelineState>> m_hdr_init_pipeline;
MRCOwned<id<MTLRenderPipelineState>> m_hdr_rta_init_pipeline;
MRCOwned<id<MTLRenderPipelineState>> m_hdr_clear_pipeline;
MRCOwned<id<MTLRenderPipelineState>> m_hdr_resolve_pipeline;
MRCOwned<id<MTLRenderPipelineState>> m_hdr_rta_resolve_pipeline;
MRCOwned<id<MTLRenderPipelineState>> m_colclip_init_pipeline;
MRCOwned<id<MTLRenderPipelineState>> m_colclip_clear_pipeline;
MRCOwned<id<MTLRenderPipelineState>> m_colclip_resolve_pipeline;
MRCOwned<id<MTLRenderPipelineState>> m_fxaa_pipeline;
MRCOwned<id<MTLRenderPipelineState>> m_shadeboost_pipeline;
MRCOwned<id<MTLRenderPipelineState>> m_imgui_pipeline;

View File

@@ -510,7 +510,9 @@ static constexpr MTLPixelFormat ConvertPixelFormat(GSTexture::Format format)
case GSTexture::Format::UInt16: return MTLPixelFormatR16Uint;
case GSTexture::Format::UNorm8: return MTLPixelFormatA8Unorm;
case GSTexture::Format::Color: return MTLPixelFormatRGBA8Unorm;
case GSTexture::Format::HDRColor: return MTLPixelFormatRGBA16Unorm;
case GSTexture::Format::ColorHQ: return MTLPixelFormatRGB10A2Unorm;
case GSTexture::Format::ColorHDR: return MTLPixelFormatRGBA16Float;
case GSTexture::Format::ColorClip: return MTLPixelFormatRGBA16Unorm;
case GSTexture::Format::DepthStencil: return MTLPixelFormatDepth32Float_Stencil8;
case GSTexture::Format::Invalid: return MTLPixelFormatInvalid;
case GSTexture::Format::BC1: return MTLPixelFormatBC1_RGBA;
@@ -1065,14 +1067,14 @@ bool GSDeviceMTL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
auto pdesc = [[MTLRenderPipelineDescriptor new] autorelease];
// FS Triangle Pipelines
pdesc.colorAttachments[0].pixelFormat = ConvertPixelFormat(GSTexture::Format::Color);
m_hdr_resolve_pipeline = MakePipeline(pdesc, fs_triangle, LoadShader(@"ps_hdr_resolve"), @"HDR Resolve");
m_colclip_resolve_pipeline = MakePipeline(pdesc, fs_triangle, LoadShader(@"ps_colclip_resolve"), @"ColorClip Resolve");
m_fxaa_pipeline = MakePipeline(pdesc, fs_triangle, LoadShader(@"ps_fxaa"), @"fxaa");
m_shadeboost_pipeline = MakePipeline(pdesc, fs_triangle, LoadShader(@"ps_shadeboost"), @"shadeboost");
m_clut_pipeline[0] = MakePipeline(pdesc, fs_triangle, LoadShader(@"ps_convert_clut_4"), @"4-bit CLUT Update");
m_clut_pipeline[1] = MakePipeline(pdesc, fs_triangle, LoadShader(@"ps_convert_clut_8"), @"8-bit CLUT Update");
pdesc.colorAttachments[0].pixelFormat = ConvertPixelFormat(GSTexture::Format::HDRColor);
m_hdr_init_pipeline = MakePipeline(pdesc, fs_triangle, LoadShader(@"ps_hdr_init"), @"HDR Init");
m_hdr_clear_pipeline = MakePipeline(pdesc, fs_triangle, LoadShader(@"ps_clear"), @"HDR Clear");
pdesc.colorAttachments[0].pixelFormat = ConvertPixelFormat(GSTexture::Format::ColorClip);
m_colclip_init_pipeline = MakePipeline(pdesc, fs_triangle, LoadShader(@"ps_colclip_init"), @"ColorClip Init");
m_colclip_clear_pipeline = MakePipeline(pdesc, fs_triangle, LoadShader(@"ps_clear"), @"ColorClip Clear");
pdesc.colorAttachments[0].pixelFormat = MTLPixelFormatInvalid;
pdesc.stencilAttachmentPixelFormat = MTLPixelFormatDepth32Float_Stencil8;
m_datm_pipeline[0] = MakePipeline(pdesc, fs_triangle, LoadShader(@"ps_datm0"), @"datm0");
@@ -1116,8 +1118,8 @@ bool GSDeviceMTL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
case ShaderConvert::DATM_1_RTA_CORRECTION:
case ShaderConvert::CLUT_4:
case ShaderConvert::CLUT_8:
case ShaderConvert::HDR_INIT:
case ShaderConvert::HDR_RESOLVE:
case ShaderConvert::COLCLIP_INIT:
case ShaderConvert::COLCLIP_RESOLVE:
continue;
case ShaderConvert::FLOAT32_TO_32_BITS:
pdesc.colorAttachments[0].pixelFormat = ConvertPixelFormat(GSTexture::Format::UInt32);
@@ -1174,7 +1176,7 @@ bool GSDeviceMTL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
m_present_pipeline[i] = MakePipeline(pdesc, vs_convert, LoadShader(name), [NSString stringWithFormat:@"present_%s", shaderName(conv) + 3]);
}
pdesc.colorAttachments[0].pixelFormat = MTLPixelFormatRGBA8Unorm;
pdesc.colorAttachments[0].pixelFormat = ConvertPixelFormat(GSTexture::Format::Color);
for (size_t i = 0; i < std::size(m_convert_pipeline_copy_mask); i++)
{
MTLColorWriteMask mask = MTLColorWriteMaskNone;
@@ -1852,7 +1854,7 @@ void GSDeviceMTL::MRESetHWPipelineState(GSHWDrawConfig::VSSelector vssel, GSHWDr
setFnConstantI(m_fn_constants, pssel.blend_d, GSMTLConstantIndex_PS_BLEND_D);
setFnConstantI(m_fn_constants, pssel.blend_hw, GSMTLConstantIndex_PS_BLEND_HW);
setFnConstantB(m_fn_constants, pssel.a_masked, GSMTLConstantIndex_PS_A_MASKED);
setFnConstantB(m_fn_constants, pssel.hdr, GSMTLConstantIndex_PS_HDR);
setFnConstantB(m_fn_constants, pssel.colclip_hw, GSMTLConstantIndex_PS_COLCLIP_HW);
setFnConstantB(m_fn_constants, pssel.rta_correction, GSMTLConstantIndex_PS_RTA_CORRECTION);
setFnConstantB(m_fn_constants, pssel.rta_source_correction, GSMTLConstantIndex_PS_RTA_SRC_CORRECTION);
setFnConstantB(m_fn_constants, pssel.colclip, GSMTLConstantIndex_PS_COLCLIP);
@@ -2114,7 +2116,7 @@ void GSDeviceMTL::MREInitHWDraw(GSHWDrawConfig& config, const Map& verts)
void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
{ @autoreleasepool {
if (config.tex && config.ds == config.tex)
if (config.tex && (config.ds == config.tex || config.rt == config.tex))
EndRenderPass(); // Barrier
size_t vertsize = config.nverts * sizeof(*config.verts);
@@ -2142,53 +2144,53 @@ void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
GSTexture* stencil = nullptr;
GSTexture* primid_tex = nullptr;
GSTexture* rt = config.rt;
GSTexture* hdr_rt = g_gs_device->GetHDRTexture();
GSTexture* colclip_rt = g_gs_device->GetColorClipTexture();
if (hdr_rt)
if (colclip_rt)
{
if (config.hdr_mode == GSHWDrawConfig::HDRMode::EarlyResolve)
if (config.colclip_mode == GSHWDrawConfig::ColClipMode::EarlyResolve)
{
BeginRenderPass(@"HDR Resolve", config.rt, MTLLoadActionLoad, nullptr, MTLLoadActionDontCare);
RenderCopy(hdr_rt, m_hdr_resolve_pipeline, config.hdr_update_area);
BeginRenderPass(@"ColorClip Resolve", config.rt, MTLLoadActionLoad, nullptr, MTLLoadActionDontCare);
RenderCopy(colclip_rt, m_colclip_resolve_pipeline, config.colclip_update_area);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
Recycle(hdr_rt);
Recycle(colclip_rt);
g_gs_device->SetHDRTexture(nullptr);
g_gs_device->SetColorClipTexture(nullptr);
hdr_rt = nullptr;
colclip_rt = nullptr;
}
else
config.ps.hdr = 1;
config.ps.colclip_hw = 1;
}
if (config.ps.hdr)
if (config.ps.colclip_hw)
{
if (!hdr_rt)
if (!colclip_rt)
{
config.hdr_update_area = config.drawarea;
config.colclip_update_area = config.drawarea;
GSVector2i size = config.rt->GetSize();
rt = hdr_rt = CreateRenderTarget(size.x, size.y, GSTexture::Format::HDRColor, false);
rt = colclip_rt = CreateRenderTarget(size.x, size.y, GSTexture::Format::ColorClip, false);
g_gs_device->SetHDRTexture(hdr_rt);
g_gs_device->SetColorClipTexture(colclip_rt);
const GSVector4i copy_rect = (config.hdr_mode == GSHWDrawConfig::HDRMode::ConvertOnly) ? GSVector4i::loadh(size) : config.drawarea;
const GSVector4i copy_rect = (config.colclip_mode == GSHWDrawConfig::ColClipMode::ConvertOnly) ? GSVector4i::loadh(size) : config.drawarea;
switch (config.rt->GetState())
{
case GSTexture::State::Dirty:
BeginRenderPass(@"HDR Init", hdr_rt, MTLLoadActionDontCare, nullptr, MTLLoadActionDontCare);
RenderCopy(config.rt, m_hdr_init_pipeline, copy_rect);
BeginRenderPass(@"ColorClip Init", colclip_rt, MTLLoadActionDontCare, nullptr, MTLLoadActionDontCare);
RenderCopy(config.rt, m_colclip_init_pipeline, copy_rect);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
break;
case GSTexture::State::Cleared:
{
BeginRenderPass(@"HDR Clear", hdr_rt, MTLLoadActionDontCare, nullptr, MTLLoadActionDontCare);
BeginRenderPass(@"ColorClip Clear", colclip_rt, MTLLoadActionDontCare, nullptr, MTLLoadActionDontCare);
GSVector4 color = GSVector4::rgba32(config.rt->GetClearColor()) / GSVector4::cxpr(65535, 65535, 65535, 255);
[m_current_render.encoder setFragmentBytes:&color length:sizeof(color) atIndex:GSMTLBufferIndexUniforms];
RenderCopy(nullptr, m_hdr_clear_pipeline, copy_rect);
RenderCopy(nullptr, m_colclip_clear_pipeline, copy_rect);
break;
}
@@ -2197,7 +2199,7 @@ void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
}
}
rt = hdr_rt;
rt = colclip_rt;
}
switch (config.destination_alpha)
@@ -2284,19 +2286,19 @@ void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
SendHWDraw(config, mtlenc, index_buffer, index_buffer_offset);
}
if (hdr_rt)
if (colclip_rt)
{
config.hdr_update_area = config.hdr_update_area.runion(config.drawarea);
config.colclip_update_area = config.colclip_update_area.runion(config.drawarea);
if ((config.hdr_mode == GSHWDrawConfig::HDRMode::ResolveOnly || config.hdr_mode == GSHWDrawConfig::HDRMode::ConvertAndResolve))
if ((config.colclip_mode == GSHWDrawConfig::ColClipMode::ResolveOnly || config.colclip_mode == GSHWDrawConfig::ColClipMode::ConvertAndResolve))
{
BeginRenderPass(@"HDR Resolve", config.rt, MTLLoadActionLoad, nullptr, MTLLoadActionDontCare);
RenderCopy(hdr_rt, m_hdr_resolve_pipeline, config.hdr_update_area);
BeginRenderPass(@"ColorClip Resolve", config.rt, MTLLoadActionLoad, nullptr, MTLLoadActionDontCare);
RenderCopy(colclip_rt, m_colclip_resolve_pipeline, config.colclip_update_area);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
Recycle(hdr_rt);
Recycle(colclip_rt);
g_gs_device->SetHDRTexture(nullptr);
g_gs_device->SetColorClipTexture(nullptr);
}
}

View File

@@ -192,7 +192,7 @@ enum GSMTLFnConstants
GSMTLConstantIndex_PS_BLEND_D,
GSMTLConstantIndex_PS_BLEND_HW,
GSMTLConstantIndex_PS_A_MASKED,
GSMTLConstantIndex_PS_HDR,
GSMTLConstantIndex_PS_COLCLIP_HW,
GSMTLConstantIndex_PS_RTA_CORRECTION,
GSMTLConstantIndex_PS_RTA_SRC_CORRECTION,
GSMTLConstantIndex_PS_COLCLIP,

View File

@@ -138,13 +138,13 @@ fragment float4 ps_rta_decorrection(ConvertShaderData data [[stage_in]], Convert
return float4(in.rgb, in.a * (128.25f / 255.0f));
}
fragment float4 ps_hdr_init(float4 p [[position]], DirectReadTextureIn<float> tex)
fragment float4 ps_colclip_init(float4 p [[position]], DirectReadTextureIn<float> tex)
{
float4 in = tex.read(p);
return float4(round(in.rgb * 255.f) / 65535.f, in.a);
}
fragment float4 ps_hdr_resolve(float4 p [[position]], DirectReadTextureIn<float> tex)
fragment float4 ps_colclip_resolve(float4 p [[position]], DirectReadTextureIn<float> tex)
{
float4 in = tex.read(p);
return float4(float3(uint3(in.rgb * 65535.5f) & 255) / 255.f, in.a);

View File

@@ -47,7 +47,7 @@ constant uint PS_BLEND_C [[function_constant(GSMTLConstantIndex_PS_BL
constant uint PS_BLEND_D [[function_constant(GSMTLConstantIndex_PS_BLEND_D)]];
constant uint PS_BLEND_HW [[function_constant(GSMTLConstantIndex_PS_BLEND_HW)]];
constant bool PS_A_MASKED [[function_constant(GSMTLConstantIndex_PS_A_MASKED)]];
constant bool PS_HDR [[function_constant(GSMTLConstantIndex_PS_HDR)]];
constant bool PS_COLCLIP_HW [[function_constant(GSMTLConstantIndex_PS_COLCLIP_HW)]];
constant bool PS_RTA_CORRECTION [[function_constant(GSMTLConstantIndex_PS_RTA_CORRECTION)]];
constant bool PS_RTA_SRC_CORRECTION [[function_constant(GSMTLConstantIndex_PS_RTA_SRC_CORRECTION)]];
constant bool PS_COLCLIP [[function_constant(GSMTLConstantIndex_PS_COLCLIP)]];
@@ -826,7 +826,7 @@ struct PSMain
else
T = sample_color(st);
if (PS_SHUFFLE && !PS_SHUFFLE_SAME && !PS_READ16_SRC)
if (PS_SHUFFLE && !PS_SHUFFLE_SAME && !PS_READ16_SRC && !(PS_PROCESS_BA == SHUFFLE_READWRITE && PS_PROCESS_RG == SHUFFLE_READWRITE))
{
uint4 denorm_c_before = uint4(T);
if (PS_PROCESS_BA & SHUFFLE_READ)
@@ -858,7 +858,7 @@ struct PSMain
{
if (PS_FBMASK)
{
float multi = PS_HDR ? 65535.0 : 255.5;
float multi = PS_COLCLIP_HW ? 65535.0 : 255.5;
C = float4((uint4(int4(C)) & (cb.fbmask ^ 0xff)) | (uint4(current_color * float4(multi, multi, multi, 255)) & cb.fbmask));
}
}
@@ -898,7 +898,7 @@ struct PSMain
C.rgb += 7.f; // Need to round up, not down since the shader will invert
// Correct the Color value based on the output format
if (PS_COLCLIP == 0 && PS_HDR == 0)
if (PS_COLCLIP == 0 && PS_COLCLIP_HW == 0)
C.rgb = clamp(C.rgb, 0.f, 255.f); // Standard Clamp
// FIXME rouding of negative float?
@@ -910,7 +910,7 @@ struct PSMain
// In 16 bits format, only 5 bits of colors are used. It impacts shadows computation of Castlevania
if (PS_DST_FMT == FMT_16 && PS_DITHER != 3 && (PS_BLEND_MIX == 0 || PS_DITHER))
C.rgb = float3(short3(C.rgb) & 0xF8);
else if (PS_COLCLIP == 1 || PS_HDR == 1)
else if (PS_COLCLIP == 1 || PS_COLCLIP_HW == 1)
C.rgb = float3(short3(C.rgb) & 0xFF);
}
else if (PS_DST_FMT == FMT_16 && PS_DITHER != 3 && PS_BLEND_MIX == 0 && PS_BLEND_HW == 0)
@@ -963,7 +963,7 @@ struct PSMain
current_color.a = float(denorm_rt.g & 0x80);
}
}
float multi = PS_HDR ? 65535.0 : 255.5;
float multi = PS_COLCLIP_HW ? 65535.0 : 255.5;
float3 Cd = trunc(current_color.rgb * multi);
float3 Cs = Color.rgb;
@@ -1134,7 +1134,7 @@ struct PSMain
if (PS_SHUFFLE)
{
if (!PS_SHUFFLE_SAME && !PS_READ16_SRC)
if (!PS_SHUFFLE_SAME && !PS_READ16_SRC && !(PS_PROCESS_BA == SHUFFLE_READWRITE && PS_PROCESS_RG == SHUFFLE_READWRITE))
{
uint4 denorm_c_after = uint4(C);
if (PS_PROCESS_BA & SHUFFLE_READ)
@@ -1175,11 +1175,8 @@ struct PSMain
{
if (PS_PROCESS_BA == SHUFFLE_READWRITE && PS_PROCESS_RG == SHUFFLE_READWRITE)
{
C.rb = C.br;
float g_temp = C.g;
C.g = C.a;
C.a = g_temp;
C.br = C.rb;
C.ag = C.ga;
}
else if(PS_PROCESS_BA & SHUFFLE_READ)
{
@@ -1193,7 +1190,7 @@ struct PSMain
}
}
}
ps_dither(C, alpha_blend.a);
// Color clamp/wrap needs to be done after sw blending and dithering
@@ -1208,7 +1205,7 @@ struct PSMain
if (PS_COLOR0)
{
out.c0.a = PS_RTA_CORRECTION ? C.a / 128.f : C.a / 255.f;
out.c0.rgb = PS_HDR ? float3(C.rgb / 65535.f) : C.rgb / 255.f;
out.c0.rgb = PS_COLCLIP_HW ? float3(C.rgb / 65535.f) : C.rgb / 255.f;
if (PS_AFAIL == 3 && !PS_COLOR1 && !atst_pass) // Doing RGB_ONLY without COLOR1
out.c0.a = current_color.a;
}

View File

@@ -1394,7 +1394,7 @@ std::string GSDeviceOGL::GetPSSource(const PSSelector& sel)
+ fmt::format("#define PS_READ16_SRC {}\n", sel.real16src)
+ fmt::format("#define PS_WRITE_RG {}\n", sel.write_rg)
+ fmt::format("#define PS_FBMASK {}\n", sel.fbmask)
+ fmt::format("#define PS_HDR {}\n", sel.hdr)
+ fmt::format("#define PS_COLCLIP_HW {}\n", sel.colclip_hw)
+ fmt::format("#define PS_RTA_CORRECTION {}\n", sel.rta_correction)
+ fmt::format("#define PS_RTA_SRC_CORRECTION {}\n", sel.rta_source_correction)
+ fmt::format("#define PS_DITHER {}\n", sel.dither)
@@ -2414,43 +2414,43 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
GSVector2i rtsize = (config.rt ? config.rt : config.ds)->GetSize();
GSTexture* primid_texture = nullptr;
GSTexture* hdr_rt = g_gs_device->GetHDRTexture();
GSTexture* colclip_rt = g_gs_device->GetColorClipTexture();
if (hdr_rt)
if (colclip_rt)
{
if (config.hdr_mode == GSHWDrawConfig::HDRMode::EarlyResolve)
if (config.colclip_mode == GSHWDrawConfig::ColClipMode::EarlyResolve)
{
const GSVector2i size = config.rt->GetSize();
const GSVector4 dRect(config.hdr_update_area);
const GSVector4 dRect(config.colclip_update_area);
const GSVector4 sRect = dRect / GSVector4(size.x, size.y).xyxy();
StretchRect(hdr_rt, sRect, config.rt, dRect, ShaderConvert::HDR_RESOLVE, false);
StretchRect(colclip_rt, sRect, config.rt, dRect, ShaderConvert::COLCLIP_RESOLVE, false);
Recycle(hdr_rt);
Recycle(colclip_rt);
g_gs_device->SetHDRTexture(nullptr);
g_gs_device->SetColorClipTexture(nullptr);
hdr_rt = nullptr;
colclip_rt = nullptr;
}
else
{
config.ps.hdr = 1;
config.ps.colclip_hw = 1;
}
}
if (config.ps.hdr)
if (config.ps.colclip_hw)
{
if (!hdr_rt)
if (!colclip_rt)
{
config.hdr_update_area = config.drawarea;
config.colclip_update_area = config.drawarea;
hdr_rt = CreateRenderTarget(rtsize.x, rtsize.y, GSTexture::Format::HDRColor, false);
OMSetRenderTargets(hdr_rt, config.ds, nullptr);
colclip_rt = CreateRenderTarget(rtsize.x, rtsize.y, GSTexture::Format::ColorClip, false);
OMSetRenderTargets(colclip_rt, config.ds, nullptr);
g_gs_device->SetHDRTexture(hdr_rt);
g_gs_device->SetColorClipTexture(colclip_rt);
const GSVector4 dRect = GSVector4((config.hdr_mode == GSHWDrawConfig::HDRMode::ConvertOnly) ? GSVector4i::loadh(rtsize) : config.drawarea);
const GSVector4 dRect = GSVector4((config.colclip_mode == GSHWDrawConfig::ColClipMode::ConvertOnly) ? GSVector4i::loadh(rtsize) : config.drawarea);
const GSVector4 sRect = dRect / GSVector4(rtsize.x, rtsize.y).xyxy();
StretchRect(config.rt, sRect, hdr_rt, dRect, ShaderConvert::HDR_INIT, false);
StretchRect(config.rt, sRect, colclip_rt, dRect, ShaderConvert::COLCLIP_INIT, false);
}
}
@@ -2461,7 +2461,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
case GSHWDrawConfig::DestinationAlphaMode::Full:
break; // No setup
case GSHWDrawConfig::DestinationAlphaMode::PrimIDTracking:
primid_texture = InitPrimDateTexture(hdr_rt ? hdr_rt : config.rt, config.drawarea, config.datm);
primid_texture = InitPrimDateTexture(colclip_rt ? colclip_rt : config.rt, config.drawarea, config.datm);
break;
case GSHWDrawConfig::DestinationAlphaMode::StencilOne:
if (m_features.texture_barrier)
@@ -2481,7 +2481,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
{GSVector4(dst.x, dst.w, 0.0f, 0.0f), GSVector2(src.x, src.w)},
{GSVector4(dst.z, dst.w, 0.0f, 0.0f), GSVector2(src.z, src.w)},
};
SetupDATE(hdr_rt ? hdr_rt : config.rt, config.ds, vertices, config.datm);
SetupDATE(colclip_rt ? colclip_rt : config.rt, config.ds, vertices, config.datm);
}
}
@@ -2490,17 +2490,11 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
if (config.require_one_barrier && !m_features.texture_barrier)
{
// Requires a copy of the RT
draw_rt_clone = CreateTexture(rtsize.x, rtsize.y, 1, hdr_rt ? GSTexture::Format::HDRColor : GSTexture::Format::Color, true);
draw_rt_clone = CreateTexture(rtsize.x, rtsize.y, 1, colclip_rt ? GSTexture::Format::ColorClip : GSTexture::Format::Color, true);
GL_PUSH("Copy RT to temp texture for fbmask {%d,%d %dx%d}",
config.drawarea.left, config.drawarea.top,
config.drawarea.width(), config.drawarea.height());
CopyRect(hdr_rt ? hdr_rt : config.rt, draw_rt_clone, config.drawarea, config.drawarea.left, config.drawarea.top);
}
else if (config.tex && config.tex == config.ds)
{
// Ensure all depth writes are finished before sampling
GL_INS("Texture barrier to flush depth before reading");
glTextureBarrier();
CopyRect(colclip_rt ? colclip_rt : config.rt, draw_rt_clone, config.drawarea, config.drawarea.left, config.drawarea.top);
}
IASetVertexBuffer(config.verts, config.nverts);
@@ -2540,7 +2534,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
if (draw_rt_clone)
PSSetShaderResource(2, draw_rt_clone);
else if (config.require_one_barrier || config.require_full_barrier)
PSSetShaderResource(2, hdr_rt ? hdr_rt : config.rt);
PSSetShaderResource(2, colclip_rt ? colclip_rt : config.rt);
SetupSampler(config.sampler);
@@ -2563,6 +2557,15 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
SetupPipeline(psel);
const bool check_barrier = !(config.require_one_barrier && !m_features.texture_barrier);
// Be careful of the rt already being bound and the blend using the RT without a barrier.
if (check_barrier && ((config.tex && (config.tex == config.ds || config.tex == config.rt)) || ((psel.ps.IsFeedbackLoop() || psel.ps.blend_c == 1) && GLState::rt == config.rt)))
{
// Ensure all depth writes are finished before sampling
GL_INS("Texture barrier to flush depth or rt before reading");
glTextureBarrier();
}
// additional non-pipeline config stuff
const bool point_size_enabled = config.vs.point_size;
if (GLState::point_size != point_size_enabled)
@@ -2619,7 +2622,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
}
// avoid changing framebuffer just to switch from rt+depth to rt and vice versa
GSTexture* draw_rt = hdr_rt ? hdr_rt : config.rt;
GSTexture* draw_rt = colclip_rt ? colclip_rt : config.rt;
GSTexture* draw_ds = config.ds;
if (!draw_rt && GLState::rt && GLState::ds == draw_ds && config.tex != GLState::rt &&
GLState::rt->GetSize() == draw_ds->GetSize())
@@ -2697,20 +2700,20 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
if (draw_rt_clone)
Recycle(draw_rt_clone);
if (hdr_rt)
if (colclip_rt)
{
config.hdr_update_area = config.hdr_update_area.runion(config.drawarea);
config.colclip_update_area = config.colclip_update_area.runion(config.drawarea);
if ((config.hdr_mode == GSHWDrawConfig::HDRMode::ResolveOnly || config.hdr_mode == GSHWDrawConfig::HDRMode::ConvertAndResolve))
if ((config.colclip_mode == GSHWDrawConfig::ColClipMode::ResolveOnly || config.colclip_mode == GSHWDrawConfig::ColClipMode::ConvertAndResolve))
{
const GSVector2i size = config.rt->GetSize();
const GSVector4 dRect(config.hdr_update_area);
const GSVector4 dRect(config.colclip_update_area);
const GSVector4 sRect = dRect / GSVector4(size.x, size.y).xyxy();
StretchRect(hdr_rt, sRect, config.rt, dRect, ShaderConvert::HDR_RESOLVE, false);
StretchRect(colclip_rt, sRect, config.rt, dRect, ShaderConvert::COLCLIP_RESOLVE, false);
Recycle(hdr_rt);
Recycle(colclip_rt);
g_gs_device->SetHDRTexture(nullptr);
g_gs_device->SetColorClipTexture(nullptr);
}
}
}

View File

@@ -65,6 +65,8 @@ GSTextureOGL::GSTextureOGL(Type type, int width, int height, int levels, Format
// 4 channel normalized
case Format::Color:
case Format::ColorHQ:
case Format::ColorHDR:
gl_fmt = GL_RGBA8;
m_int_format = GL_RGBA;
m_int_type = GL_UNSIGNED_BYTE;
@@ -72,7 +74,7 @@ GSTextureOGL::GSTextureOGL(Type type, int width, int height, int levels, Format
break;
// 4 channel float
case Format::HDRColor:
case Format::ColorClip:
gl_fmt = GL_RGBA16;
m_int_format = GL_RGBA;
m_int_type = GL_UNSIGNED_SHORT;

View File

@@ -2104,7 +2104,8 @@ bool GSDeviceVK::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
return false;
}
CompileCASPipelines();
if (!CompileCASPipelines())
return false;
if (!CompileImGuiPipeline())
return false;
@@ -2715,10 +2716,12 @@ void GSDeviceVK::DrawIndexedPrimitive(int offset, int count)
VkFormat GSDeviceVK::LookupNativeFormat(GSTexture::Format format) const
{
static constexpr std::array<VkFormat, static_cast<int>(GSTexture::Format::BC7) + 1> s_format_mapping = {{
static constexpr std::array<VkFormat, static_cast<int>(GSTexture::Format::Last) + 1> s_format_mapping = {{
VK_FORMAT_UNDEFINED, // Invalid
VK_FORMAT_R8G8B8A8_UNORM, // Color
VK_FORMAT_R16G16B16A16_UNORM, // HDRColor
VK_FORMAT_A2B10G10R10_UNORM_PACK32, // ColorHQ
VK_FORMAT_R16G16B16A16_SFLOAT, // ColorHDR
VK_FORMAT_R16G16B16A16_UNORM, // ColorClip
VK_FORMAT_D32_SFLOAT_S8_UINT, // DepthStencil
VK_FORMAT_R8_UNORM, // UNorm8
VK_FORMAT_R16_UINT, // UInt16
@@ -3764,7 +3767,7 @@ bool GSDeviceVK::CreatePipelineLayouts()
plb.AddDescriptorSet(m_utility_ds_layout);
if ((m_utility_pipeline_layout = plb.Create(dev)) == VK_NULL_HANDLE)
return false;
Vulkan::SetObjectName(dev, m_utility_ds_layout, "Convert pipeline layout");
Vulkan::SetObjectName(dev, m_utility_pipeline_layout, "Convert pipeline layout");
//////////////////////////////////////////////////////////////////////////
// Draw/TFX Pipeline Layout
@@ -3820,14 +3823,14 @@ bool GSDeviceVK::CreateRenderPasses()
} while (0)
const VkFormat rt_format = LookupNativeFormat(GSTexture::Format::Color);
const VkFormat hdr_rt_format = LookupNativeFormat(GSTexture::Format::HDRColor);
const VkFormat colclip_rt_format = LookupNativeFormat(GSTexture::Format::ColorClip);
const VkFormat depth_format = LookupNativeFormat(GSTexture::Format::DepthStencil);
for (u32 rt = 0; rt < 2; rt++)
{
for (u32 ds = 0; ds < 2; ds++)
{
for (u32 hdr = 0; hdr < 2; hdr++)
for (u32 colclip = 0; colclip < 2; colclip++)
{
for (u32 stencil = 0; stencil < 2; stencil++)
{
@@ -3841,12 +3844,12 @@ bool GSDeviceVK::CreateRenderPasses()
opb++)
{
const VkFormat rp_rt_format =
(rt != 0) ? ((hdr != 0) ? hdr_rt_format : rt_format) : VK_FORMAT_UNDEFINED;
(rt != 0) ? ((colclip != 0) ? colclip_rt_format : rt_format) : VK_FORMAT_UNDEFINED;
const VkFormat rp_depth_format = (ds != 0) ? depth_format : VK_FORMAT_UNDEFINED;
const VkAttachmentLoadOp opc = (!stencil || !m_features.stencil_buffer) ?
VK_ATTACHMENT_LOAD_OP_DONT_CARE :
VK_ATTACHMENT_LOAD_OP_LOAD;
GET(m_tfx_render_pass[rt][ds][hdr][stencil][fbl][dsp][opa][opb], rp_rt_format,
GET(m_tfx_render_pass[rt][ds][colclip][stencil][fbl][dsp][opa][opb], rp_rt_format,
rp_depth_format, (fbl != 0), (dsp != 0), static_cast<VkAttachmentLoadOp>(opa),
static_cast<VkAttachmentLoadOp>(opb), static_cast<VkAttachmentLoadOp>(opc));
}
@@ -3999,15 +4002,7 @@ bool GSDeviceVK::CompileConvertPipelines()
}
else if (i == ShaderConvert::RTA_CORRECTION)
{
// compile color copy pipelines
gpb.SetRenderPass(m_utility_color_render_pass_discard, 0);
VkShaderModule ps = GetUtilityFragmentShader(*shader, shaderName(i));
if (ps == VK_NULL_HANDLE)
return false;
ScopedGuard ps_guard([this, &ps]() { vkDestroyShaderModule(m_device, ps, nullptr); });
gpb.SetFragmentShader(ps);
for (u32 j = 16; j < 32; j++)
{
pxAssert(!m_color_copy[j]);
@@ -4023,10 +4018,10 @@ bool GSDeviceVK::CompileConvertPipelines()
(j >> 1) & 1u, (j >> 2) & 1u, (j >> 3) & 1u);
}
}
else if (i == ShaderConvert::HDR_INIT || i == ShaderConvert::HDR_RESOLVE)
else if (i == ShaderConvert::COLCLIP_INIT || i == ShaderConvert::COLCLIP_RESOLVE)
{
const bool is_setup = i == ShaderConvert::HDR_INIT;
VkPipeline(&arr)[2][2] = *(is_setup ? &m_hdr_setup_pipelines : &m_hdr_finish_pipelines);
const bool is_setup = i == ShaderConvert::COLCLIP_INIT;
VkPipeline(&arr)[2][2] = *(is_setup ? &m_colclip_setup_pipelines : &m_colclip_finish_pipelines);
for (u32 ds = 0; ds < 2; ds++)
{
for (u32 fbl = 0; fbl < 2; fbl++)
@@ -4040,7 +4035,7 @@ bool GSDeviceVK::CompileConvertPipelines()
if (!arr[ds][fbl])
return false;
Vulkan::SetObjectName(m_device, arr[ds][fbl], "HDR %s/copy pipeline (ds=%u, fbl=%u)",
Vulkan::SetObjectName(m_device, arr[ds][fbl], "ColorClip %s/copy pipeline (ds=%u, fbl=%u)",
is_setup ? "setup" : "finish", i, ds, fbl);
}
}
@@ -4196,7 +4191,7 @@ bool GSDeviceVK::CompileInterlacePipelines()
if (!m_interlace[i])
return false;
Vulkan::SetObjectName(m_device, m_convert[i], "Interlace pipeline %d", i);
Vulkan::SetObjectName(m_device, m_interlace[i], "Interlace pipeline %d", i);
}
return true;
@@ -4248,7 +4243,7 @@ bool GSDeviceVK::CompileMergePipelines()
if (!m_merge[i])
return false;
Vulkan::SetObjectName(m_device, m_convert[i], "Merge pipeline %d", i);
Vulkan::SetObjectName(m_device, m_merge[i], "Merge pipeline %d", i);
}
return true;
@@ -4610,10 +4605,10 @@ void GSDeviceVK::DestroyResources()
{
for (u32 fbl = 0; fbl < 2; fbl++)
{
if (m_hdr_setup_pipelines[ds][fbl] != VK_NULL_HANDLE)
vkDestroyPipeline(m_device, m_hdr_setup_pipelines[ds][fbl], nullptr);
if (m_hdr_finish_pipelines[ds][fbl] != VK_NULL_HANDLE)
vkDestroyPipeline(m_device, m_hdr_finish_pipelines[ds][fbl], nullptr);
if (m_colclip_setup_pipelines[ds][fbl] != VK_NULL_HANDLE)
vkDestroyPipeline(m_device, m_colclip_setup_pipelines[ds][fbl], nullptr);
if (m_colclip_finish_pipelines[ds][fbl] != VK_NULL_HANDLE)
vkDestroyPipeline(m_device, m_colclip_finish_pipelines[ds][fbl], nullptr);
}
}
for (u32 ds = 0; ds < 2; ds++)
@@ -4782,7 +4777,7 @@ VkShaderModule GSDeviceVK::GetTFXFragmentShader(const GSHWDrawConfig::PSSelector
AddMacro(ss, "PS_READ16_SRC", sel.real16src);
AddMacro(ss, "PS_WRITE_RG", sel.write_rg);
AddMacro(ss, "PS_FBMASK", sel.fbmask);
AddMacro(ss, "PS_HDR", sel.hdr);
AddMacro(ss, "PS_COLCLIP_HW", sel.colclip_hw);
AddMacro(ss, "PS_RTA_CORRECTION", sel.rta_correction);
AddMacro(ss, "PS_RTA_SRC_CORRECTION", sel.rta_source_correction);
AddMacro(ss, "PS_DITHER", sel.dither);
@@ -4838,7 +4833,7 @@ VkPipeline GSDeviceVK::CreateTFXPipeline(const PipelineSelector& p)
else
{
gpb.SetRenderPass(
GetTFXRenderPass(p.rt, p.ds, p.ps.hdr, p.dss.date,
GetTFXRenderPass(p.rt, p.ds, p.ps.colclip_hw, p.dss.date,
p.IsRTFeedbackLoop(), p.IsTestingAndSamplingDepth(),
p.rt ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_DONT_CARE,
p.ds ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_DONT_CARE),
@@ -4967,7 +4962,7 @@ void GSDeviceVK::InitializeState()
Vulkan::SetObjectName(m_device, m_point_sampler, "Point sampler");
m_linear_sampler = GetSampler(GSHWDrawConfig::SamplerSelector::Linear());
if (m_linear_sampler)
Vulkan::SetObjectName(m_device, m_point_sampler, "Linear sampler");
Vulkan::SetObjectName(m_device, m_linear_sampler, "Linear sampler");
m_tfx_sampler_sel = GSHWDrawConfig::SamplerSelector::Point().key;
m_tfx_sampler = m_point_sampler;
@@ -5595,7 +5590,7 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
GSTextureVK* draw_rt = static_cast<GSTextureVK*>(config.rt);
GSTextureVK* draw_ds = static_cast<GSTextureVK*>(config.ds);
GSTextureVK* draw_rt_clone = nullptr;
GSTextureVK* hdr_rt = static_cast<GSTextureVK*>(g_gs_device->GetHDRTexture());
GSTextureVK* colclip_rt = static_cast<GSTextureVK*>(g_gs_device->GetColorClipTexture());
// stream buffer in first, in case we need to exec
SetVSConstantBuffer(config.cb_vs);
@@ -5621,11 +5616,11 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
GSTextureVK* date_image = nullptr;
if (config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::PrimIDTracking)
{
// If we have a HDR in progress, we need to use the HDR texture, but we can't check this later as there's a chicken/egg problem with the pipe setup.
// If we have a colclip in progress, we need to use the colclip texture, but we can't check this later as there's a chicken/egg problem with the pipe setup.
GSTexture* backup_rt = config.rt;
if(hdr_rt)
config.rt = hdr_rt;
if(colclip_rt)
config.rt = colclip_rt;
date_image = SetupPrimitiveTrackingDATE(config);
if (!date_image)
@@ -5641,15 +5636,19 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
PipelineSelector& pipe = m_pipeline_selector;
UpdateHWPipelineSelector(config, pipe);
// now blit the hdr texture back to the original target
if (hdr_rt)
// If we don't have a barrier but the texture was drawn to last draw, end the pass to insert a barrier.
if (InRenderPass() && !pipe.IsRTFeedbackLoop() && (config.tex == m_current_render_target || config.tex == m_current_depth_target))
EndRenderPass();
// now blit the colclip texture back to the original target
if (colclip_rt)
{
if (config.hdr_mode == GSHWDrawConfig::HDRMode::EarlyResolve)
if (config.colclip_mode == GSHWDrawConfig::ColClipMode::EarlyResolve)
{
GL_PUSH("Blit HDR back to RT");
GL_PUSH("Blit ColorClip back to RT");
EndRenderPass();
hdr_rt->TransitionToLayout(GSTextureVK::Layout::ShaderReadOnly);
colclip_rt->TransitionToLayout(GSTextureVK::Layout::ShaderReadOnly);
draw_rt = static_cast<GSTextureVK*>(config.rt);
OMSetRenderTargets(draw_rt, draw_ds, GSVector4i::loadh(rtsize), static_cast<FeedbackLoopFlag>(pipe.feedback_loop_flags));
@@ -5677,22 +5676,22 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
draw_rt->GetRect());
}
const GSVector4 drawareaf = GSVector4(config.hdr_update_area);
const GSVector4 drawareaf = GSVector4(config.colclip_update_area);
const GSVector4 sRect(drawareaf / GSVector4(rtsize).xyxy());
SetPipeline(m_hdr_finish_pipelines[pipe.ds][pipe.IsRTFeedbackLoop()]);
SetUtilityTexture(hdr_rt, m_point_sampler);
SetPipeline(m_colclip_finish_pipelines[pipe.ds][pipe.IsRTFeedbackLoop()]);
SetUtilityTexture(colclip_rt, m_point_sampler);
DrawStretchRect(sRect, drawareaf, rtsize);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
Recycle(hdr_rt);
g_gs_device->SetHDRTexture(nullptr);
Recycle(colclip_rt);
g_gs_device->SetColorClipTexture(nullptr);
hdr_rt = nullptr;
colclip_rt = nullptr;
}
else
{
pipe.ps.hdr = 1;
draw_rt = hdr_rt;
pipe.ps.colclip_hw = 1;
draw_rt = colclip_rt;
}
}
@@ -5722,7 +5721,7 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
if (config.require_one_barrier && !m_features.texture_barrier)
{
// requires a copy of the RT
draw_rt_clone = static_cast<GSTextureVK*>(CreateTexture(rtsize.x, rtsize.y, 1, hdr_rt ? GSTexture::Format::HDRColor : GSTexture::Format::Color, true));
draw_rt_clone = static_cast<GSTextureVK*>(CreateTexture(rtsize.x, rtsize.y, 1, colclip_rt ? GSTexture::Format::ColorClip : GSTexture::Format::Color, true));
if (draw_rt_clone)
{
EndRenderPass();
@@ -5735,17 +5734,17 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
}
}
// Switch to hdr target for colclip rendering
if (pipe.ps.hdr)
// Switch to colclip target for colclip hw rendering
if (pipe.ps.colclip_hw)
{
if (!hdr_rt)
if (!colclip_rt)
{
config.hdr_update_area = config.drawarea;
config.colclip_update_area = config.drawarea;
EndRenderPass();
hdr_rt = static_cast<GSTextureVK*>(CreateRenderTarget(rtsize.x, rtsize.y, GSTexture::Format::HDRColor, false));
if (!hdr_rt)
colclip_rt = static_cast<GSTextureVK*>(CreateRenderTarget(rtsize.x, rtsize.y, GSTexture::Format::ColorClip, false));
if (!colclip_rt)
{
Console.WriteLn("Failed to allocate HDR render target, aborting draw.");
Console.WriteLn("Failed to allocate ColorClip render target, aborting draw.");
if (date_image)
Recycle(date_image);
@@ -5753,13 +5752,13 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
GL_POP();
return;
}
g_gs_device->SetHDRTexture(static_cast<GSTexture*>(hdr_rt));
g_gs_device->SetColorClipTexture(static_cast<GSTexture*>(colclip_rt));
// propagate clear value through if the hdr render is the first
// propagate clear value through if the colclip render is the first
if (draw_rt->GetState() == GSTexture::State::Cleared)
{
hdr_rt->SetState(GSTexture::State::Cleared);
hdr_rt->SetClearColor(draw_rt->GetClearColor());
colclip_rt->SetState(GSTexture::State::Cleared);
colclip_rt->SetClearColor(draw_rt->GetClearColor());
// If depth is cleared, we need to commit it, because we're only going to draw to the active part of the FB.
if (draw_ds && draw_ds->GetState() == GSTexture::State::Cleared && !config.drawarea.eq(GSVector4i::loadh(rtsize)))
@@ -5767,7 +5766,7 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
}
else if (draw_rt->GetState() == GSTexture::State::Dirty)
{
GL_PUSH_("HDR Render Target Setup");
GL_PUSH_("ColorClip Render Target Setup");
draw_rt->TransitionToLayout(GSTextureVK::Layout::ShaderReadOnly);
}
@@ -5775,7 +5774,7 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
if (config.require_one_barrier && !m_features.texture_barrier)
PSSetShaderResource(2, draw_rt, true);
}
draw_rt = hdr_rt;
draw_rt = colclip_rt;
}
// clear texture binding when it's bound to RT or DS.
@@ -5786,15 +5785,15 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
}
// render pass restart optimizations
if (hdr_rt && (config.hdr_mode == GSHWDrawConfig::HDRMode::ConvertAndResolve || config.hdr_mode == GSHWDrawConfig::HDRMode::ConvertOnly))
if (colclip_rt && (config.colclip_mode == GSHWDrawConfig::ColClipMode::ConvertAndResolve || config.colclip_mode == GSHWDrawConfig::ColClipMode::ConvertOnly))
{
// HDR requires blitting.
// colclip hw requires blitting.
EndRenderPass();
}
else if (InRenderPass() && (m_current_render_target == draw_rt || m_current_depth_target == draw_ds))
{
// avoid restarting the render pass just to switch from rt+depth to rt and vice versa
// keep the depth even if doing HDR draws, because the next draw will probably re-enable depth
// keep the depth even if doing colclip hw draws, because the next draw will probably re-enable depth
if (!draw_rt && m_current_render_target && config.tex != m_current_render_target &&
m_current_render_target->GetSize() == draw_ds->GetSize())
{
@@ -5813,15 +5812,15 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
}
// We don't need the very first barrier if this is the first draw after switching to feedback loop,
// because the layout change in itself enforces the execution dependency. HDR needs a barrier between
// setup and the first draw to read it. TODO: Make HDR use subpasses instead.
// because the layout change in itself enforces the execution dependency. colclip hw needs a barrier between
// setup and the first draw to read it. TODO: Make colclip hw use subpasses instead.
// However, it turns out *not* doing this causes GPU resets on RDNA3, specifically Windows drivers.
// Despite the layout changing enforcing the execution dependency between previous draws and the first
// input attachment read, it still wants the region/fragment-local barrier...
const bool skip_first_barrier =
(draw_rt && draw_rt->GetLayout() != GSTextureVK::Layout::FeedbackLoop && !pipe.ps.hdr && !IsDeviceAMD());
(draw_rt && draw_rt->GetLayout() != GSTextureVK::Layout::FeedbackLoop && !pipe.ps.colclip_hw && !IsDeviceAMD());
OMSetRenderTargets(draw_rt, draw_ds, config.scissor, static_cast<FeedbackLoopFlag>(pipe.feedback_loop_flags));
if (pipe.IsRTFeedbackLoop())
@@ -5839,14 +5838,14 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
{
const VkAttachmentLoadOp rt_op = GetLoadOpForTexture(draw_rt);
const VkAttachmentLoadOp ds_op = GetLoadOpForTexture(draw_ds);
const VkRenderPass rp = GetTFXRenderPass(pipe.rt, pipe.ds, pipe.ps.hdr,
const VkRenderPass rp = GetTFXRenderPass(pipe.rt, pipe.ds, pipe.ps.colclip_hw,
config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::Stencil, pipe.IsRTFeedbackLoop(),
pipe.IsTestingAndSamplingDepth(), rt_op, ds_op);
const bool is_clearing_rt = (rt_op == VK_ATTACHMENT_LOAD_OP_CLEAR || ds_op == VK_ATTACHMENT_LOAD_OP_CLEAR);
// Only draw to the active area of the HDR target. Except when depth is cleared, we need to use the full
// Only draw to the active area of the colclip hw target. Except when depth is cleared, we need to use the full
// buffer size, otherwise it'll only clear the draw part of the depth buffer.
const GSVector4i render_area = (pipe.ps.hdr && (config.hdr_mode == GSHWDrawConfig::HDRMode::ConvertAndResolve) && ds_op != VK_ATTACHMENT_LOAD_OP_CLEAR) ? config.drawarea :
const GSVector4i render_area = (pipe.ps.colclip_hw && (config.colclip_mode == GSHWDrawConfig::ColClipMode::ConvertAndResolve) && ds_op != VK_ATTACHMENT_LOAD_OP_CLEAR) ? config.drawarea :
GSVector4i::loadh(rtsize);
if (is_clearing_rt)
@@ -5857,9 +5856,9 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
if (draw_rt)
{
GSVector4 clear_color = draw_rt->GetUNormClearColor();
if (pipe.ps.hdr)
if (pipe.ps.colclip_hw)
{
// Denormalize clear color for HDR.
// Denormalize clear color for hw colclip.
clear_color *= GSVector4::cxpr(255.0f / 65535.0f, 255.0f / 65535.0f, 255.0f / 65535.0f, 1.0f);
}
GSVector4::store<true>(&cvs[cv_count++].color, clear_color);
@@ -5884,14 +5883,14 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
vkCmdClearAttachments(m_current_command_buffer, 1, &ca, 1, &rc);
}
// rt -> hdr blit if enabled
if (hdr_rt && (config.hdr_mode == GSHWDrawConfig::HDRMode::ConvertOnly || config.hdr_mode == GSHWDrawConfig::HDRMode::ConvertAndResolve) && config.rt->GetState() == GSTexture::State::Dirty)
// rt -> colclip hw blit if enabled
if (colclip_rt && (config.colclip_mode == GSHWDrawConfig::ColClipMode::ConvertOnly || config.colclip_mode == GSHWDrawConfig::ColClipMode::ConvertAndResolve) && config.rt->GetState() == GSTexture::State::Dirty)
{
OMSetRenderTargets(draw_rt, draw_ds, GSVector4i::loadh(rtsize), static_cast<FeedbackLoopFlag>(pipe.feedback_loop_flags));
SetUtilityTexture(static_cast<GSTextureVK*>(config.rt), m_point_sampler);
SetPipeline(m_hdr_setup_pipelines[pipe.ds][pipe.IsRTFeedbackLoop()]);
SetPipeline(m_colclip_setup_pipelines[pipe.ds][pipe.IsRTFeedbackLoop()]);
const GSVector4 drawareaf = GSVector4((config.hdr_mode == GSHWDrawConfig::HDRMode::ConvertOnly) ? GSVector4i::loadh(rtsize) : config.drawarea);
const GSVector4 drawareaf = GSVector4((config.colclip_mode == GSHWDrawConfig::ColClipMode::ConvertOnly) ? GSVector4i::loadh(rtsize) : config.drawarea);
const GSVector4 sRect(drawareaf / GSVector4(rtsize).xyxy());
DrawStretchRect(sRect, drawareaf, rtsize);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
@@ -5900,8 +5899,8 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
OMSetRenderTargets(draw_rt, draw_ds, config.scissor, static_cast<FeedbackLoopFlag>(pipe.feedback_loop_flags));
}
// VB/IB upload, if we did DATE setup and it's not HDR this has already been done
if (!date_image || hdr_rt)
// VB/IB upload, if we did DATE setup and it's not colclip hw this has already been done
if (!date_image || colclip_rt)
UploadHWDrawVerticesAndIndices(config);
// now we can do the actual draw
@@ -5951,21 +5950,21 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
if (date_image)
Recycle(date_image);
// now blit the hdr texture back to the original target
if (hdr_rt)
// now blit the colclip texture back to the original target
if (colclip_rt)
{
config.hdr_update_area = config.hdr_update_area.runion(config.drawarea);
config.colclip_update_area = config.colclip_update_area.runion(config.drawarea);
if ((config.hdr_mode == GSHWDrawConfig::HDRMode::ResolveOnly || config.hdr_mode == GSHWDrawConfig::HDRMode::ConvertAndResolve))
if ((config.colclip_mode == GSHWDrawConfig::ColClipMode::ResolveOnly || config.colclip_mode == GSHWDrawConfig::ColClipMode::ConvertAndResolve))
{
GL_PUSH("Blit HDR back to RT");
GL_PUSH("Blit ColorClip back to RT");
EndRenderPass();
hdr_rt->TransitionToLayout(GSTextureVK::Layout::ShaderReadOnly);
colclip_rt->TransitionToLayout(GSTextureVK::Layout::ShaderReadOnly);
draw_rt = static_cast<GSTextureVK*>(config.rt);
OMSetRenderTargets(draw_rt, draw_ds, (config.hdr_mode == GSHWDrawConfig::HDRMode::ResolveOnly) ? GSVector4i::loadh(rtsize) : config.scissor, static_cast<FeedbackLoopFlag>(pipe.feedback_loop_flags));
OMSetRenderTargets(draw_rt, draw_ds, (config.colclip_mode == GSHWDrawConfig::ColClipMode::ResolveOnly) ? GSVector4i::loadh(rtsize) : config.scissor, static_cast<FeedbackLoopFlag>(pipe.feedback_loop_flags));
// if this target was cleared and never drawn to, perform the clear as part of the resolve here.
if (draw_rt->GetState() == GSTexture::State::Cleared)
@@ -5990,19 +5989,19 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
draw_rt->GetRect());
}
const GSVector4 drawareaf = GSVector4(config.hdr_update_area);
const GSVector4 drawareaf = GSVector4(config.colclip_update_area);
const GSVector4 sRect(drawareaf / GSVector4(rtsize).xyxy());
SetPipeline(m_hdr_finish_pipelines[pipe.ds][pipe.IsRTFeedbackLoop()]);
SetUtilityTexture(hdr_rt, m_point_sampler);
SetPipeline(m_colclip_finish_pipelines[pipe.ds][pipe.IsRTFeedbackLoop()]);
SetUtilityTexture(colclip_rt, m_point_sampler);
DrawStretchRect(sRect, drawareaf, rtsize);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
Recycle(hdr_rt);
g_gs_device->SetHDRTexture(nullptr);
Recycle(colclip_rt);
g_gs_device->SetColorClipTexture(nullptr);
}
}
config.hdr_mode = GSHWDrawConfig::HDRMode::NoModify;
config.colclip_mode = GSHWDrawConfig::ColClipMode::NoModify;
}
void GSDeviceVK::UpdateHWPipelineSelector(GSHWDrawConfig& config, PipelineSelector& pipe)

View File

@@ -394,8 +394,8 @@ private:
std::array<VkPipeline, 32> m_color_copy{};
std::array<VkPipeline, 2> m_merge{};
std::array<VkPipeline, NUM_INTERLACE_SHADERS> m_interlace{};
VkPipeline m_hdr_setup_pipelines[2][2] = {}; // [depth][feedback_loop]
VkPipeline m_hdr_finish_pipelines[2][2] = {}; // [depth][feedback_loop]
VkPipeline m_colclip_setup_pipelines[2][2] = {}; // [depth][feedback_loop]
VkPipeline m_colclip_finish_pipelines[2][2] = {}; // [depth][feedback_loop]
VkRenderPass m_date_image_setup_render_passes[2][2] = {}; // [depth][clear]
VkPipeline m_date_image_setup_pipelines[2][4] = {}; // [depth][datm]
VkPipeline m_fxaa_pipeline = {};
@@ -415,7 +415,7 @@ private:
VkRenderPass m_date_setup_render_pass = VK_NULL_HANDLE;
VkRenderPass m_swap_chain_render_pass = VK_NULL_HANDLE;
VkRenderPass m_tfx_render_pass[2][2][2][3][2][2][3][3] = {}; // [rt][ds][hdr][date][fbl][dsp][rt_op][ds_op]
VkRenderPass m_tfx_render_pass[2][2][2][3][2][2][3][3] = {}; // [rt][ds][colclip][date][fbl][dsp][rt_op][ds_op]
VkDescriptorSetLayout m_cas_ds_layout = VK_NULL_HANDLE;
VkPipelineLayout m_cas_pipeline_layout = VK_NULL_HANDLE;
@@ -486,10 +486,10 @@ public:
/// Returns true if Vulkan is suitable as a default for the devices in the system.
static bool IsSuitableDefaultRenderer();
__fi VkRenderPass GetTFXRenderPass(bool rt, bool ds, bool hdr, bool stencil, bool fbl, bool dsp,
__fi VkRenderPass GetTFXRenderPass(bool rt, bool ds, bool colclip, bool stencil, bool fbl, bool dsp,
VkAttachmentLoadOp rt_op, VkAttachmentLoadOp ds_op) const
{
return m_tfx_render_pass[rt][ds][hdr][stencil][fbl][dsp][rt_op][ds_op];
return m_tfx_render_pass[rt][ds][colclip][stencil][fbl][dsp][rt_op][ds_op];
}
__fi VkSampler GetPointSampler() const { return m_point_sampler; }
__fi VkSampler GetLinearSampler() const { return m_linear_sampler; }

View File

@@ -60,6 +60,25 @@ static const char* const* s_sdl_trigger_names_list[] = {
// Switch
};
static constexpr const char* s_sdl_ps3_sxs_pressure_names[] = {
nullptr, // JoyAxis0
nullptr, // JoyAxis1
nullptr, // JoyAxis2
nullptr, // JoyAxis3
nullptr, // JoyAxis4
nullptr, // JoyAxis5
"Cross (Pressure)", // JoyAxis6
"Circle (Pressure)", // JoyAxis7
"Square (Pressure)", // JoyAxis8
"Triangle (Pressure)", // JoyAxis9
"L1 (Pressure)", // JoyAxis10
"R1 (Pressure)", // JoyAxis11
"D-Pad Up (Pressure)", // JoyAxis12
"D-Pad Down (Pressure)", // JoyAxis13
"D-Pad Left (Pressure)", // JoyAxis14
"D-Pad Right (Pressure)", // JoyAxis15
};
static constexpr const char* s_sdl_axis_icons[][2] = {
{ICON_PF_LEFT_ANALOG_LEFT, ICON_PF_LEFT_ANALOG_RIGHT}, // SDL_GAMEPAD_AXIS_LEFTX
{ICON_PF_LEFT_ANALOG_UP, ICON_PF_LEFT_ANALOG_DOWN}, // SDL_GAMEPAD_AXIS_LEFTY
@@ -87,6 +106,25 @@ static const char* const* s_sdl_trigger_icons_list[] = {
// Switch
};
static constexpr const char* s_sdl_ps3_pressure_icons[] = {
nullptr, // JoyAxis0
nullptr, // JoyAxis1
nullptr, // JoyAxis2
nullptr, // JoyAxis3
nullptr, // JoyAxis4
nullptr, // JoyAxis5
"P" ICON_PF_BUTTON_CROSS, // JoyAxis6
"P" ICON_PF_BUTTON_CIRCLE, // JoyAxis7
"P" ICON_PF_BUTTON_SQUARE, // JoyAxis8
"P" ICON_PF_BUTTON_TRIANGLE, // JoyAxis9
"P" ICON_PF_LEFT_SHOULDER_L1, // JoyAxis10
"P" ICON_PF_RIGHT_SHOULDER_R1, // JoyAxis11
"P" ICON_PF_DPAD_UP, // JoyAxis12
"P" ICON_PF_DPAD_DOWN, // JoyAxis13
"P" ICON_PF_DPAD_LEFT, // JoyAxis14
"P" ICON_PF_DPAD_RIGHT, // JoyAxis15
};
static constexpr const GenericInputBinding s_sdl_generic_binding_axis_mapping[][2] = {
{GenericInputBinding::LeftStickLeft, GenericInputBinding::LeftStickRight}, // SDL_GAMEPAD_AXIS_LEFTX
{GenericInputBinding::LeftStickUp, GenericInputBinding::LeftStickDown}, // SDL_GAMEPAD_AXIS_LEFTY
@@ -95,6 +133,24 @@ static constexpr const GenericInputBinding s_sdl_generic_binding_axis_mapping[][
{GenericInputBinding::Unknown, GenericInputBinding::L2}, // SDL_GAMEPAD_AXIS_LEFT_TRIGGER
{GenericInputBinding::Unknown, GenericInputBinding::R2}, // SDL_GAMEPAD_AXIS_RIGHT_TRIGGER
};
static constexpr const GenericInputBinding s_sdl_ps3_binding_pressure_mapping[] = {
GenericInputBinding::Unknown, // JoyAxis0
GenericInputBinding::Unknown, // JoyAxis1
GenericInputBinding::Unknown, // JoyAxis2
GenericInputBinding::Unknown, // JoyAxis3
GenericInputBinding::Unknown, // JoyAxis4
GenericInputBinding::Unknown, // JoyAxis5
GenericInputBinding::Cross, // JoyAxis6
GenericInputBinding::Circle, // JoyAxis7
GenericInputBinding::Square, // JoyAxis8
GenericInputBinding::Triangle, // JoyAxis9
GenericInputBinding::L1, // JoyAxis10
GenericInputBinding::R1, // JoyAxis11
GenericInputBinding::DPadUp, // JoyAxis12
GenericInputBinding::DPadDown, // JoyAxis13
GenericInputBinding::DPadLeft, // JoyAxis14
GenericInputBinding::DPadRight, // JoyAxis15
};
static constexpr const char* s_sdl_button_setting_names[] = {
"FaceSouth", // SDL_GAMEPAD_BUTTON_SOUTH
@@ -265,7 +321,7 @@ static constexpr const char* s_sdl_button_ps3_icons[] = {
ICON_PF_BUTTON_SQUARE, // SDL_GAMEPAD_BUTTON_WEST
ICON_PF_BUTTON_TRIANGLE, // SDL_GAMEPAD_BUTTON_NORTH
ICON_PF_SELECT_SHARE, // SDL_GAMEPAD_BUTTON_BACK
ICON_PF_XBOX, // SDL_GAMEPAD_BUTTON_GUIDE
ICON_PF_PLAYSTATION, // SDL_GAMEPAD_BUTTON_GUIDE
ICON_PF_START, // SDL_GAMEPAD_BUTTON_START
ICON_PF_LEFT_ANALOG_CLICK, // SDL_GAMEPAD_BUTTON_LEFT_STICK
ICON_PF_RIGHT_ANALOG_CLICK, // SDL_GAMEPAD_BUTTON_RIGHT_STICK
@@ -360,12 +416,17 @@ static constexpr const GenericInputBinding s_sdl_generic_binding_button_mapping[
GenericInputBinding::DPadDown, // SDL_GAMEPAD_BUTTON_DPAD_DOWN
GenericInputBinding::DPadLeft, // SDL_GAMEPAD_BUTTON_DPAD_LEFT
GenericInputBinding::DPadRight, // SDL_GAMEPAD_BUTTON_DPAD_RIGHT
GenericInputBinding::Unknown, // SDL_GAMEPAD_BUTTON_MISC1
GenericInputBinding::Unknown, // SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1
GenericInputBinding::Unknown, // SDL_GAMEPAD_BUTTON_LEFT_PADDLE1
GenericInputBinding::Unknown, // SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2
GenericInputBinding::Unknown, // SDL_GAMEPAD_BUTTON_LEFT_PADDLE2
GenericInputBinding::Unknown, // SDL_GAMEPAD_BUTTON_TOUCHPAD
};
static constexpr const GenericInputBinding s_sdl_ps3_binding_button_mapping[] = {
GenericInputBinding::Unknown, // SDL_GAMEPAD_BUTTON_SOUTH
GenericInputBinding::Unknown, // SDL_GAMEPAD_BUTTON_EAST
GenericInputBinding::Unknown, // SDL_GAMEPAD_BUTTON_WEST
GenericInputBinding::Unknown, // SDL_GAMEPAD_BUTTON_NORTH
GenericInputBinding::Select, // SDL_GAMEPAD_BUTTON_BACK
GenericInputBinding::System, // SDL_GAMEPAD_BUTTON_GUIDE
GenericInputBinding::Start, // SDL_GAMEPAD_BUTTON_START
GenericInputBinding::L3, // SDL_GAMEPAD_BUTTON_LEFT_STICK
GenericInputBinding::R3, // SDL_GAMEPAD_BUTTON_RIGHT_STICK
};
static constexpr const char* s_sdl_hat_direction_names[] = {
@@ -555,6 +616,11 @@ void SDLInputSource::SetHints()
// Gets us pressure sensitive button support on Linux
// Apparently doesn't work on Windows, so leave it off there
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS3, "1");
#else
// Use the Sixaxis driver (or DsHidMini in SXS mode).
// We don't support DsHidMini's SDF mode as none of the
// PS3 hints allow accessing all the pressure sense axis.
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS3_SIXAXIS_DRIVER, "1");
#endif
#ifdef __APPLE__
@@ -920,7 +986,21 @@ TinyString SDLInputSource::ConvertKeyToString(InputBindingKey key, bool display,
ret.format("SDL-{} {}", static_cast<u32>(key.source_index), s_sdl_trigger_names[trigger_index]);
}
else
ret.format("SDL-{} {}Axis {}{}", static_cast<u32>(key.source_index), modifier, key.data - std::size(s_sdl_axis_setting_names) + 1, key.invert ? "~" : "");
{
bool is_sixaxis = false;
if (it != m_controllers.end())
is_sixaxis = IsControllerSixaxis(*it);
const int joy_axis_Index = key.data - std::size(s_sdl_axis_setting_names);
if (is_sixaxis && key.modifier == InputModifier::FullAxis && key.invert == false &&
joy_axis_Index < std::size(s_sdl_ps3_sxs_pressure_names) && s_sdl_ps3_sxs_pressure_names[joy_axis_Index] != nullptr)
{
ret.format("SDL-{} {}", static_cast<u32>(key.source_index), s_sdl_ps3_sxs_pressure_names[joy_axis_Index]);
}
else
ret.format("SDL-{} {}Axis {}{}", static_cast<u32>(key.source_index), modifier, joy_axis_Index + 1, key.invert ? "~" : "");
}
}
else
{
@@ -1030,6 +1110,13 @@ TinyString SDLInputSource::ConvertKeyToIcon(InputBindingKey key)
ret.format("SDL-{} {}", static_cast<u32>(key.source_index), s_sdl_trigger_icons[trigger_index]);
}
}
else if (it != m_controllers.end() && IsControllerSixaxis(*it) && key.invert == false)
{
const int joy_axis_Index = key.data - std::size(s_sdl_axis_setting_names);
if (joy_axis_Index < std::size(s_sdl_ps3_pressure_icons) && s_sdl_ps3_pressure_icons[joy_axis_Index] != nullptr)
ret.format("SDL-{} {}", static_cast<u32>(key.source_index), s_sdl_ps3_pressure_icons[joy_axis_Index]);
}
}
else if (key.source_subtype == InputSubclass::ControllerButton)
{
@@ -1508,11 +1595,34 @@ bool SDLInputSource::GetGenericBindingMapping(const std::string_view device, Inp
if (positive != GenericInputBinding::Unknown)
mapping->emplace_back(positive, fmt::format("SDL-{}/+{}", pid, s_sdl_axis_setting_names[i]));
}
for (u32 i = 0; i < std::size(s_sdl_generic_binding_button_mapping); i++)
if (IsControllerSixaxis(*it))
{
const GenericInputBinding binding = s_sdl_generic_binding_button_mapping[i];
if (binding != GenericInputBinding::Unknown)
mapping->emplace_back(binding, fmt::format("SDL-{}/{}", pid, s_sdl_button_setting_names[i]));
// PS3 with pressure sensitive support
for (u32 i = 0; i < std::size(s_sdl_ps3_binding_pressure_mapping); i++)
{
const GenericInputBinding binding = s_sdl_ps3_binding_pressure_mapping[i];
if (binding != GenericInputBinding::Unknown)
mapping->emplace_back(binding, fmt::format("SDL-{}/FullJoyAxis{}", pid, i));
}
// PS3 non pressure sensitive buttons
for (u32 i = 0; i < std::size(s_sdl_ps3_binding_button_mapping); i++)
{
const GenericInputBinding binding = s_sdl_ps3_binding_button_mapping[i];
if (binding != GenericInputBinding::Unknown)
mapping->emplace_back(binding, fmt::format("SDL-{}/{}", pid, s_sdl_button_setting_names[i]));
}
}
else
{
// Standard buttons
for (u32 i = 0; i < std::size(s_sdl_generic_binding_button_mapping); i++)
{
const GenericInputBinding binding = s_sdl_generic_binding_button_mapping[i];
if (binding != GenericInputBinding::Unknown)
mapping->emplace_back(binding, fmt::format("SDL-{}/{}", pid, s_sdl_button_setting_names[i]));
}
}
if (it->use_gamepad_rumble || it->haptic_left_right_effect)
@@ -1624,3 +1734,16 @@ void SDLInputSource::SendRumbleUpdate(ControllerData* cd)
SDL_StopHapticRumble(cd->haptic);
}
}
bool SDLInputSource::IsControllerSixaxis(const ControllerData& cd)
{
const SDL_GamepadType type = SDL_GetRealGamepadType(cd.gamepad);
// We check the number of buttons to exclude DsHidMini's SDF mode (which has 17 buttons??)
// SDF's input layout differs from the sixaxis or linux drivers, we only support the latter layout.
// This differing layout also isn't mapped correctly in SDL, I think due to how L2/R2 are exposed.
// Also see SetHints regarding reading the pressure sense from DsHidMini's SDF mode.
return type == SDL_GAMEPAD_TYPE_PS3 &&
SDL_GetNumJoystickAxes(cd.joystick) == 16 &&
SDL_GetNumJoystickButtons(cd.joystick) == 11;
}

View File

@@ -88,6 +88,8 @@ private:
bool HandleJoystickHatEvent(const SDL_JoyHatEvent* ev);
void SendRumbleUpdate(ControllerData* cd);
bool IsControllerSixaxis(const ControllerData& cd);
ControllerDataVector m_controllers;
// ConvertKeyToString and ConvertKeyToIcon can inspect the

View File

@@ -3,4 +3,4 @@
/// Version number for GS and other shaders. Increment whenever any of the contents of the
/// shaders change, to invalidate the cache.
static constexpr u32 SHADER_CACHE_VERSION = 61;
static constexpr u32 SHADER_CACHE_VERSION = 63;

View File

@@ -195,6 +195,7 @@
<ClCompile Include="DEV9\Sessions\TCP_Session\TCP_Session_Out.cpp" />
<ClCompile Include="DEV9\Win32\pcap_io_win32.cpp" />
<ClCompile Include="DEV9\Sessions\BaseSession.cpp" />
<ClCompile Include="DEV9\Sessions\UDP_Session\UDP_Common.cpp" />
<ClCompile Include="DEV9\Sessions\UDP_Session\UDP_FixedPort.cpp" />
<ClCompile Include="DEV9\Sessions\UDP_Session\UDP_Session.cpp" />
<ClCompile Include="DEV9\smap.cpp" />
@@ -636,6 +637,7 @@
<ClInclude Include="DEV9\Sessions\BaseSession.h" />
<ClInclude Include="DEV9\Sessions\ICMP_Session\ICMP_Session.h" />
<ClInclude Include="DEV9\Sessions\TCP_Session\TCP_Session.h" />
<ClInclude Include="DEV9\Sessions\UDP_Session\UDP_Common.h" />
<ClInclude Include="DEV9\Sessions\UDP_Session\UDP_FixedPort.h" />
<ClInclude Include="DEV9\Sessions\UDP_Session\UDP_BaseSession.h" />
<ClInclude Include="DEV9\Sessions\UDP_Session\UDP_Session.h" />

View File

@@ -944,6 +944,9 @@
<ClCompile Include="DEV9\Sessions\TCP_Session\TCP_Session_Out.cpp">
<Filter>System\Ps2\DEV9\Sessions\TCP_Session</Filter>
</ClCompile>
<ClCompile Include="DEV9\Sessions\UDP_Session\UDP_Common.cpp">
<Filter>System\Ps2\DEV9\Sessions\UDP_Session</Filter>
</ClCompile>
<ClCompile Include="DEV9\Sessions\UDP_Session\UDP_FixedPort.cpp">
<Filter>System\Ps2\DEV9\Sessions\UDP_Session</Filter>
</ClCompile>
@@ -1844,6 +1847,9 @@
<ClInclude Include="DEV9\Sessions\TCP_Session\TCP_Session.h">
<Filter>System\Ps2\DEV9\Sessions\TCP_Session</Filter>
</ClInclude>
<ClInclude Include="DEV9\Sessions\UDP_Session\UDP_Common.h">
<Filter>System\Ps2\DEV9\Sessions\UDP_Session</Filter>
</ClInclude>
<ClInclude Include="DEV9\Sessions\UDP_Session\UDP_FixedPort.h">
<Filter>System\Ps2\DEV9\Sessions\UDP_Session</Filter>
</ClInclude>