Compare commits

...

55 Commits

Author SHA1 Message Date
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
38 changed files with 3587 additions and 1462 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

@@ -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)
@@ -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)
{

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);
@@ -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;

View File

@@ -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);
@@ -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;

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

@@ -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

@@ -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);
@@ -113,12 +113,14 @@ private:
void SetTCOffset();
bool NextDrawHDR() 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

@@ -2114,7 +2114,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);

View File

@@ -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)
@@ -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

View File

@@ -2496,12 +2496,6 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
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();
}
IASetVertexBuffer(config.verts, config.nverts);
if (config.vs.expand != GSHWDrawConfig::VSExpand::None && !GLAD_GL_ARB_shader_draw_parameters)
@@ -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)

View File

@@ -5641,6 +5641,10 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
PipelineSelector& pipe = m_pipeline_selector;
UpdateHWPipelineSelector(config, pipe);
// 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 hdr texture back to the original target
if (hdr_rt)
{

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 = 62;

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>