mirror of
https://github.com/PCSX2/pcsx2.git
synced 2026-07-31 11:18:41 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d54e28e63 | ||
|
|
c89d023f91 | ||
|
|
c7cfea1daa | ||
|
|
beb07365a0 | ||
|
|
0787c65e51 | ||
|
|
d48dea7273 | ||
|
|
2432cc6ed2 | ||
|
|
cf523d4215 | ||
|
|
906d87c4b8 | ||
|
|
9c4a92c015 | ||
|
|
0c056ba691 | ||
|
|
424158b28d | ||
|
|
57f118a5b5 | ||
|
|
65e75f3da2 | ||
|
|
41174cde45 | ||
|
|
cb7d01fb36 | ||
|
|
3e78f8e87d | ||
|
|
f2c032ba07 | ||
|
|
2ef1589e76 | ||
|
|
5c394557e3 | ||
|
|
5c4a5b0a61 | ||
|
|
be22b7349d | ||
|
|
023e4774a1 | ||
|
|
56b706f25e | ||
|
|
9d07ee43eb | ||
|
|
147a6c81a1 | ||
|
|
5972f4947d | ||
|
|
9c99a624d7 | ||
|
|
d812d83dda | ||
|
|
f81c5c2455 | ||
|
|
dc8c5e6209 | ||
|
|
14833582b2 | ||
|
|
cca813fd1d | ||
|
|
d4102e75c5 | ||
|
|
76a04efc5c | ||
|
|
a34e139843 | ||
|
|
a1727a2ac7 | ||
|
|
7ac224adf3 | ||
|
|
d5e3d02a98 | ||
|
|
68ad3e8db4 | ||
|
|
31029b2fe4 | ||
|
|
8b0c101768 | ||
|
|
50b66f526b | ||
|
|
f3fcf010fb | ||
|
|
4da53bd00d | ||
|
|
c6cef45f0e | ||
|
|
e36cd7fe5c | ||
|
|
0c24f2db7b | ||
|
|
8866faa8d5 | ||
|
|
b2dc31ba80 | ||
|
|
061ea30199 | ||
|
|
0e9e94d090 | ||
|
|
3321cb37ef | ||
|
|
d60a818791 | ||
|
|
3937a52b4f | ||
|
|
2c3902b1a1 | ||
|
|
f43b5ee76f | ||
|
|
bab16a5251 | ||
|
|
08c734d7fd | ||
|
|
36126a3ad9 | ||
|
|
ce505d33bd | ||
|
|
de0d859502 | ||
|
|
ce96d4bcc5 | ||
|
|
fb242274d5 |
@@ -105,6 +105,7 @@ jobs:
|
||||
cmake -DCMAKE_PREFIX_PATH="$HOME/deps" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DUSE_OPENGL=OFF \
|
||||
-DUSE_VULKAN=OFF \
|
||||
-DDISABLE_ADVANCE_SIMD=ON \
|
||||
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON \
|
||||
-DUSE_SYSTEM_LIBS=OFF \
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
name: 📢 Announce Release
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
announce:
|
||||
if: github.repository == 'PCSX2/pcsx2'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
- name: Announce Release
|
||||
env:
|
||||
DISCORD_BUILD_WEBHOOK: ${{ secrets.DISCORD_BUILD_WEBHOOK }}
|
||||
run: |
|
||||
cd ./.github/workflows/scripts/releases/announce-release
|
||||
npm ci
|
||||
node index.js
|
||||
@@ -27,8 +27,10 @@ permissions:
|
||||
jobs:
|
||||
cut-release:
|
||||
if: github.repository == 'PCSX2/pcsx2'
|
||||
runs-on: ubuntu-latest
|
||||
name: "Create Tag and Release"
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
new_tag: ${{ steps.tag_version.outputs.new_tag }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -131,6 +133,7 @@ jobs:
|
||||
upload_artifacts:
|
||||
if: github.repository == 'PCSX2/pcsx2'
|
||||
needs:
|
||||
- cut-release
|
||||
- build_linux_flatpak
|
||||
- build_linux_qt
|
||||
- build_windows_qt
|
||||
@@ -140,10 +143,6 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
# actions/checkout elides tags, fetch them primarily for releases
|
||||
- name: Fetch Tags
|
||||
run: git fetch --tags --no-recurse-submodules
|
||||
|
||||
- name: Prepare Artifact Folder
|
||||
run: mkdir ./ci-artifacts/
|
||||
|
||||
@@ -169,7 +168,7 @@ jobs:
|
||||
SCAN_DIR: ${{ github.WORKSPACE }}/ci-artifacts
|
||||
OUT_DIR: ${{ github.WORKSPACE }}/ci-artifacts/out
|
||||
run: |
|
||||
TAG_VAL=$(git tag --points-at HEAD)
|
||||
TAG_VAL=${{needs.cut-release.outputs.new_tag}}
|
||||
echo "TAG_VAL=${TAG_VAL}"
|
||||
gh release list --repo PCSX2/pcsx2
|
||||
mkdir -p ${{ github.WORKSPACE }}/ci-artifacts/out
|
||||
@@ -181,6 +180,22 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
TAG_VAL=$(git tag --points-at HEAD)
|
||||
TAG_VAL=${{needs.cut-release.outputs.new_tag}}
|
||||
echo "TAG_VAL=${TAG_VAL}"
|
||||
gh release edit ${TAG_VAL} --draft=false --repo PCSX2/pcsx2
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
- name: Announce Release
|
||||
env:
|
||||
OWNER: PCSX2
|
||||
REPO: pcsx2
|
||||
DISCORD_BUILD_WEBHOOK: ${{ secrets.DISCORD_BUILD_WEBHOOK }}
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
TAG_VAL=${{needs.cut-release.outputs.new_tag}}
|
||||
cd ./.github/workflows/scripts/releases/announce-release
|
||||
npm ci
|
||||
TAG_VAL=${TAG_VAL} node index.js
|
||||
|
||||
@@ -1,7 +1,58 @@
|
||||
import { MessageEmbed, WebhookClient } from "discord.js";
|
||||
import * as github from '@actions/github';
|
||||
import { Octokit } from "@octokit/rest";
|
||||
import { throttling } from "@octokit/plugin-throttling";
|
||||
import { retry } from "@octokit/plugin-retry";
|
||||
|
||||
const releaseInfo = github.context.payload.release;
|
||||
let owner = process.env.OWNER;
|
||||
let repo = process.env.REPO;
|
||||
|
||||
Octokit.plugin(throttling);
|
||||
Octokit.plugin(retry);
|
||||
const octokit = new Octokit({
|
||||
auth: process.env.GITHUB_TOKEN,
|
||||
userAgent: `${owner}/${repo}`,
|
||||
log: {
|
||||
debug: () => { },
|
||||
info: () => { },
|
||||
warn: console.warn,
|
||||
error: console.error
|
||||
},
|
||||
throttle: {
|
||||
onRateLimit: (retryAfter, options) => {
|
||||
octokit.log.warn(
|
||||
`Request quota exhausted for request ${options.method} ${options.url}`
|
||||
);
|
||||
|
||||
// Retry twice after hitting a rate limit error, then give up
|
||||
if (options.request.retryCount <= 2) {
|
||||
console.log(`Retrying after ${retryAfter} seconds!`);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
onAbuseLimit: (retryAfter, options) => {
|
||||
// does not retry, only logs a warning
|
||||
octokit.log.warn(
|
||||
`Abuse detected for request ${options.method} ${options.url}`
|
||||
);
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
if (process.env.TAG_VAL === undefined || process.env.TAG_VAL === "") {
|
||||
console.log(`Not announcing - TAG_VAL not defined`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const { data: releaseInfo } = await octokit.rest.repos.getReleaseByTag({
|
||||
owner: owner,
|
||||
repo: repo,
|
||||
tag: process.env.TAG_VAL,
|
||||
});
|
||||
|
||||
if (releaseInfo === undefined) {
|
||||
console.log(`Not announcing - could not locate release with tag ${process.env.TAG_VAL}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!releaseInfo.prerelease) {
|
||||
console.log("Not announcing - release was not a pre-release (aka a Nightly)");
|
||||
@@ -18,6 +69,7 @@ const embed = new MessageEmbed()
|
||||
{ name: 'Installation Steps', value: '[See Here](https://github.com/PCSX2/pcsx2/wiki/Nightly-Build-Usage-Guide)', inline: true },
|
||||
{ name: 'Included Changes', value: releaseInfo.body, inline: false }
|
||||
);
|
||||
console.log(embed);
|
||||
|
||||
// Get all webhooks, simple comma-sep string
|
||||
const webhookUrls = process.env.DISCORD_BUILD_WEBHOOK.split(",");
|
||||
|
||||
@@ -5,32 +5,16 @@
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "announce-release",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@actions/github": "^5.0.0",
|
||||
"@octokit/plugin-retry": "^3.0.9",
|
||||
"@octokit/plugin-throttling": "^3.5.2",
|
||||
"@octokit/rest": "^18.12.0",
|
||||
"discord.js": "^13.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/github": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@actions/github/-/github-5.0.0.tgz",
|
||||
"integrity": "sha512-QvE9eAAfEsS+yOOk0cylLBIO/d6WyWIOvsxxzdrPFaud39G6BOkUwScXZn1iBzQzHyu9SBkkLSWlohDWdsasAQ==",
|
||||
"dependencies": {
|
||||
"@actions/http-client": "^1.0.11",
|
||||
"@octokit/core": "^3.4.0",
|
||||
"@octokit/plugin-paginate-rest": "^2.13.3",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^5.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/http-client": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.11.tgz",
|
||||
"integrity": "sha512-VRYHGQV1rqnROJqdMvGUbY/Kn8vriQe/F9HR2AlYHzmKuM/p3kjNuXhmdBfcVgsvRWTz5C5XW5xvndZrVBuAYg==",
|
||||
"dependencies": {
|
||||
"tunnel": "0.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@discordjs/builders": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-0.6.0.tgz",
|
||||
@@ -131,6 +115,14 @@
|
||||
"@octokit/types": "^6.33.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/plugin-request-log": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz",
|
||||
"integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==",
|
||||
"peerDependencies": {
|
||||
"@octokit/core": ">=3"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/plugin-rest-endpoint-methods": {
|
||||
"version": "5.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.12.1.tgz",
|
||||
@@ -140,6 +132,27 @@
|
||||
"deprecation": "^2.3.1"
|
||||
}
|
||||
},
|
||||
"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.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-3.7.0.tgz",
|
||||
"integrity": "sha512-qrKT1Yl/KuwGSC6/oHpLBot3ooC9rq0/ryDYBCpkRtoj+R8T47xTMDT6Tk2CxWopFota/8Pi/2SqArqwC0JPow==",
|
||||
"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",
|
||||
@@ -163,6 +176,17 @@
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/rest": {
|
||||
"version": "18.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz",
|
||||
"integrity": "sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q==",
|
||||
"dependencies": {
|
||||
"@octokit/core": "^3.5.1",
|
||||
"@octokit/plugin-paginate-rest": "^2.16.8",
|
||||
"@octokit/plugin-request-log": "^1.0.4",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^5.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/types": {
|
||||
"version": "6.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.33.0.tgz",
|
||||
@@ -214,6 +238,11 @@
|
||||
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz",
|
||||
"integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ=="
|
||||
},
|
||||
"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/callsites": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||
@@ -388,14 +417,6 @@
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
|
||||
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
|
||||
},
|
||||
"node_modules/tunnel": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
|
||||
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
|
||||
"engines": {
|
||||
"node": ">=0.6.11 <=0.7.0 || >=0.7.3"
|
||||
}
|
||||
},
|
||||
"node_modules/type-fest": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz",
|
||||
@@ -461,25 +482,6 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/github": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@actions/github/-/github-5.0.0.tgz",
|
||||
"integrity": "sha512-QvE9eAAfEsS+yOOk0cylLBIO/d6WyWIOvsxxzdrPFaud39G6BOkUwScXZn1iBzQzHyu9SBkkLSWlohDWdsasAQ==",
|
||||
"requires": {
|
||||
"@actions/http-client": "^1.0.11",
|
||||
"@octokit/core": "^3.4.0",
|
||||
"@octokit/plugin-paginate-rest": "^2.13.3",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^5.1.1"
|
||||
}
|
||||
},
|
||||
"@actions/http-client": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.11.tgz",
|
||||
"integrity": "sha512-VRYHGQV1rqnROJqdMvGUbY/Kn8vriQe/F9HR2AlYHzmKuM/p3kjNuXhmdBfcVgsvRWTz5C5XW5xvndZrVBuAYg==",
|
||||
"requires": {
|
||||
"tunnel": "0.0.6"
|
||||
}
|
||||
},
|
||||
"@discordjs/builders": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-0.6.0.tgz",
|
||||
@@ -569,6 +571,12 @@
|
||||
"@octokit/types": "^6.33.0"
|
||||
}
|
||||
},
|
||||
"@octokit/plugin-request-log": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz",
|
||||
"integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==",
|
||||
"requires": {}
|
||||
},
|
||||
"@octokit/plugin-rest-endpoint-methods": {
|
||||
"version": "5.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.12.1.tgz",
|
||||
@@ -578,6 +586,24 @@
|
||||
"deprecation": "^2.3.1"
|
||||
}
|
||||
},
|
||||
"@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==",
|
||||
"requires": {
|
||||
"@octokit/types": "^6.0.3",
|
||||
"bottleneck": "^2.15.3"
|
||||
}
|
||||
},
|
||||
"@octokit/plugin-throttling": {
|
||||
"version": "3.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-3.7.0.tgz",
|
||||
"integrity": "sha512-qrKT1Yl/KuwGSC6/oHpLBot3ooC9rq0/ryDYBCpkRtoj+R8T47xTMDT6Tk2CxWopFota/8Pi/2SqArqwC0JPow==",
|
||||
"requires": {
|
||||
"@octokit/types": "^6.0.1",
|
||||
"bottleneck": "^2.15.3"
|
||||
}
|
||||
},
|
||||
"@octokit/request": {
|
||||
"version": "5.6.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.2.tgz",
|
||||
@@ -601,6 +627,17 @@
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"@octokit/rest": {
|
||||
"version": "18.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz",
|
||||
"integrity": "sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q==",
|
||||
"requires": {
|
||||
"@octokit/core": "^3.5.1",
|
||||
"@octokit/plugin-paginate-rest": "^2.16.8",
|
||||
"@octokit/plugin-request-log": "^1.0.4",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^5.12.0"
|
||||
}
|
||||
},
|
||||
"@octokit/types": {
|
||||
"version": "6.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.33.0.tgz",
|
||||
@@ -642,6 +679,11 @@
|
||||
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz",
|
||||
"integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ=="
|
||||
},
|
||||
"bottleneck": {
|
||||
"version": "2.19.5",
|
||||
"resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz",
|
||||
"integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw=="
|
||||
},
|
||||
"callsites": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||
@@ -765,11 +807,6 @@
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
|
||||
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
|
||||
},
|
||||
"tunnel": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
|
||||
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="
|
||||
},
|
||||
"type-fest": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz",
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@actions/github": "^5.0.0",
|
||||
"@octokit/plugin-retry": "^3.0.9",
|
||||
"@octokit/plugin-throttling": "^3.5.2",
|
||||
"@octokit/rest": "^18.12.0",
|
||||
"discord.js": "^13.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,17 +22,23 @@ Installers and binaries for both stable and development builds are available fro
|
||||
|
||||
| Operating System | CPU | GPU | RAM |
|
||||
| ------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---- |
|
||||
| - Windows 10 Version 1809 or later (64-bit) <br/> - Ubuntu 22.04/Debian or newer, Arch Linux, or other distro (64-bit) <br/> - macOS 10.14 | - Supports SSE4.1 <br/> - [PassMark Thread Performance](https://www.cpubenchmark.net/CPU_mega_page.html) rating near or greater than 1800<br/> - Two physical cores, with hyperthreading | - Direct3D 11 (Feature Level 11.0) support <br/> - OpenGL 3.3 support <br/> - Vulkan 1.1 support <br/> - Metal support <br/> - [PassMark G3D Mark](https://www.videocardbenchmark.net/high_end_gpus.html) rating around 3000 (Geforce GTX 750, Radeon RX 560, Intel Arc A380) <br/> - 2 GB Video Memory | 4 GB |
|
||||
|
||||
_Note: Recommended Single Thread Performance is based on moderately complex games. Games that pushed the PS2 hardware to its limits will struggle on CPUs at this level. Some release titles and 2D games which underutilized the PS2 hardware may run on CPUs rated as low as 1200. A quick reference for CPU **intensive games**: [Wiki](https://wiki.pcsx2.net/Category:CPU_intensive_games), [Forum](https://forums.pcsx2.net/Thread-LIST-The-Most-CPU-Intensive-Games) and CPU **light** games: [Forum](https://forums.pcsx2.net/Thread-LIST-Games-that-don-t-need-a-strong-CPU-to-emulate)_
|
||||
| - Windows 10 Version 1809 or later (64-bit) <br/> - Ubuntu 22.04/Debian or newer, Arch Linux, or other distro (64-bit) <br/> - macOS 10.14 | - Supports SSE4.1 <br/> - [PassMark Thread Performance](https://www.cpubenchmark.net/CPU_mega_page.html) rating near or greater than 1500<br/> - Two physical cores, with hyperthreading | - Direct3D 11 (Feature Level 11.0) support <br/> - OpenGL 3.3 support <br/> - Vulkan 1.1 support <br/> - Metal support <br/> - [PassMark G3D Mark](https://www.videocardbenchmark.net/high_end_gpus.html) rating around 3000 (Geforce GTX 750, Radeon RX 560, Intel Arc A380) <br/> - 2 GB Video Memory | 4 GB |
|
||||
|
||||
### Recommended
|
||||
|
||||
| Operating System | CPU | GPU | RAM |
|
||||
| ----------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---- |
|
||||
| - Windows 10 22H2 (64-bit) <br/> - Ubuntu 23.04/Debian or newer, Arch Linux, or other distro (64-bit) <br/> - macOS 10.14 | - Supports AVX2 <br/> - [PassMark Single Thread Performance](https://www.cpubenchmark.net/CPU_mega_page.html) rating near or greater than 2000<br/> - Four physical cores, with or without hyperthreading | - Direct3D12 support <br/> - OpenGL 4.6 support <br/> - Vulkan 1.3 support <br/> - Metal support <br/> - [PassMark G3D Mark](https://www.videocardbenchmark.net/high_end_gpus.html) rating around 6000 (GeForce GTX 1650, Radeon RX 570) <br/> - 4 GB Video Memory | 8 GB |
|
||||
|
||||
### Heavy
|
||||
|
||||
| Operating System | CPU | GPU | RAM |
|
||||
| ----------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---- |
|
||||
| - Windows 10 22H2 (64-bit) <br/> - Ubuntu 23.04/Debian or newer, Arch Linux, or other distro (64-bit) <br/> - macOS 10.14 | - Supports AVX2 <br/> - [PassMark Single Thread Performance](https://www.cpubenchmark.net/CPU_mega_page.html) rating near or greater than 2600<br/> - Four physical cores, with or without hyperthreading | - Direct3D12 support <br/> - OpenGL 4.6 support <br/> - Vulkan 1.3 support <br/> - Metal support <br/> - [PassMark G3D Mark](https://www.videocardbenchmark.net/high_end_gpus.html) rating around 6000 (GeForce GTX 1650, Radeon RX 570) <br/> - 4 GB Video Memory | 8 GB |
|
||||
|
||||
_Note: Recommended GPU is based on 3x Internal, ~1080p resolution requirements. Higher resolutions will require stronger cards; 6x Internal, ~4K resolution will require a [PassMark G3D Mark](https://www.videocardbenchmark.net/high_end_gpus.html) rating around 12000 (GeForce RTX 2060 Radeon RX 6600 Intel Arc A750). Just like CPU requirements, this is also highly game dependent. A quick reference for GPU **intensive games**: [Wiki](https://wiki.pcsx2.net/Category:GPU_intensive_games)_
|
||||
_Note1: Recommended Single Thread Performance is based on moderately complex games. Games that pushed the PS2 hardware to its limits will struggle on CPUs at this level. Some release titles and 2D games which underutilized the PS2 hardware may run on CPUs rated as low as 1200. A quick reference for CPU **intensive games**: [Wiki](https://wiki.pcsx2.net/Category:CPU_intensive_games), [Forum](https://forums.pcsx2.net/Thread-LIST-The-Most-CPU-Intensive-Games) and CPU **light** games: [Forum](https://forums.pcsx2.net/Thread-LIST-Games-that-don-t-need-a-strong-CPU-to-emulate)_
|
||||
|
||||
_Note2: Recommended GPU is based on 3x Internal, ~1080p resolution requirements. Higher resolutions will require stronger cards; 6x Internal, ~4K resolution will require a [PassMark G3D Mark](https://www.videocardbenchmark.net/high_end_gpus.html) rating around 12000 (GeForce RTX 2060 Radeon RX 6600 Intel Arc A750). Just like CPU requirements, this is also highly game dependent. A quick reference for GPU **intensive games**: [Wiki](https://wiki.pcsx2.net/Category:GPU_intensive_games)_
|
||||
|
||||
### Technical Notes
|
||||
|
||||
|
||||
+724
-147
File diff suppressed because it is too large
Load Diff
@@ -315,6 +315,7 @@
|
||||
03000000d81d00001000000000000000,iBuffalo BSGP1204P Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
|
||||
030000005c0a00000285000000000000,iDroidCon,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b6,platform:Windows,
|
||||
03000000696400006964000000000000,iDroidCon Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
|
||||
03000000511d00000230000000000000,iGUGU Gamecore,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b1,leftstick:b4,lefttrigger:b3,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b2,platform:Windows,
|
||||
03000000b50700001403000000000000,Impact Black,a:b2,b:b3,back:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,
|
||||
030000006f0e00002401000000000000,Injustice Fightstick PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,
|
||||
03000000830500005130000000000000,InterAct ActionPad,a:b0,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,
|
||||
@@ -1065,10 +1066,10 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
05000000c82d00002038000000010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
03000000c82d00000751000000010000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:a8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
05000000c82d00000851000000010000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:a8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000c82d00000660000011010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
05000000c82d00000660000000010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
03000000c82d00000631000014010000,8BitDo Pro 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
03000000c82d00000660000011010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
03000000c82d00001030000011010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
05000000c82d00000660000000010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
03000000c82d00000020000000000000,8BitDo Pro 2 for Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
06000000c82d00000020000006010000,8BitDo Pro 2 for Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
03000000c82d00000131000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
@@ -1094,9 +1095,9 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
05000000c82d00000261000000010000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
05000000202800000900000000010000,8BitDo SNES30,a:b1,b:b0,back:b10,dpdown:b122,dpleft:b119,dpright:b120,dpup:b117,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,
|
||||
05000000c82d00001230000000010000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000c82d00001730000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000c82d00001530000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000c82d00001630000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000c82d00001730000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000c82d00001130000011010000,8BitDo Ultimate Wired,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b24,paddle2:b25,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000c82d00000760000011010000,8BitDo Ultimate Wireless,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
03000000c82d00001230000011010000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
@@ -1462,6 +1463,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
03000000321500000009000011010000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,
|
||||
050000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,
|
||||
0300000032150000030a000001010000,Razer Wildcat,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
03000000321500000b10000011010000,Razer Wolverine PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,
|
||||
03000000790000001100000010010000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,start:b9,x:b0,y:b3,platform:Linux,
|
||||
190000004b4800000111000000010000,RetroGame Joypad,a:b1,b:b0,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
|
||||
0300000081170000990a000001010000,Retronic Adapter,a:b0,leftx:a0,lefty:a1,platform:Linux,
|
||||
|
||||
@@ -352,16 +352,16 @@ layout(set = 1, binding = 1) uniform texture2D Palette;
|
||||
|
||||
#if PS_FEEDBACK_LOOP_IS_NEEDED
|
||||
#if defined(DISABLE_TEXTURE_BARRIER) || defined(HAS_FEEDBACK_LOOP_LAYOUT)
|
||||
layout(set = 2, binding = 0) uniform texture2D RtSampler;
|
||||
layout(set = 1, binding = 2) uniform texture2D RtSampler;
|
||||
vec4 sample_from_rt() { return texelFetch(RtSampler, ivec2(gl_FragCoord.xy), 0); }
|
||||
#else
|
||||
layout(input_attachment_index = 0, set = 2, binding = 0) uniform subpassInput RtSampler;
|
||||
layout(input_attachment_index = 0, set = 1, binding = 2) uniform subpassInput RtSampler;
|
||||
vec4 sample_from_rt() { return subpassLoad(RtSampler); }
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if PS_DATE > 0
|
||||
layout(set = 2, binding = 1) uniform texture2D PrimMinTexture;
|
||||
layout(set = 1, binding = 3) uniform texture2D PrimMinTexture;
|
||||
#endif
|
||||
|
||||
#if NEEDS_TEX
|
||||
|
||||
@@ -748,13 +748,6 @@ s64 FileSystem::FTell64(std::FILE* fp)
|
||||
|
||||
s64 FileSystem::FSize64(std::FILE* fp)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
const int fd = _fileno(fp);
|
||||
if (fd >= 0)
|
||||
{
|
||||
return _filelengthi64(fd);
|
||||
}
|
||||
#else
|
||||
const s64 pos = FTell64(fp);
|
||||
if (pos >= 0)
|
||||
{
|
||||
@@ -765,7 +758,6 @@ s64 FileSystem::FSize64(std::FILE* fp)
|
||||
return size;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -2285,7 +2285,8 @@ void MainWindow::setGameListEntryCoverImage(const GameList::Entry* entry)
|
||||
if (filename.isEmpty())
|
||||
return;
|
||||
|
||||
if (!GameList::GetCoverImagePathForEntry(entry).empty())
|
||||
const QString old_filename = QString::fromStdString(GameList::GetCoverImagePathForEntry(entry));
|
||||
if (!old_filename.isEmpty())
|
||||
{
|
||||
if (QMessageBox::question(this, tr("Cover Already Exists"),
|
||||
tr("A cover image for this game already exists, do you wish to replace it?"), QMessageBox::Yes,
|
||||
@@ -2311,6 +2312,12 @@ void MainWindow::setGameListEntryCoverImage(const GameList::Entry* entry)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!old_filename.isEmpty() && old_filename != new_filename && !QFile::remove(old_filename))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Copy Error"), tr("Failed to remove '%1'").arg(old_filename));
|
||||
return;
|
||||
}
|
||||
|
||||
m_game_list_widget->refreshGridCovers();
|
||||
}
|
||||
|
||||
|
||||
@@ -1078,7 +1078,10 @@ namespace SettingWidgetBinder
|
||||
static inline void BindSliderToIntSetting(SettingsInterface* sif, QSlider* slider, QLabel* label, const QString& label_suffix,
|
||||
std::string section, std::string key, s32 default_value)
|
||||
{
|
||||
const s32 global_value = Host::GetBaseIntSettingValue(section.c_str(), key.c_str(), default_value);
|
||||
s32 global_value = Host::GetBaseIntSettingValue(section.c_str(), key.c_str(), default_value);
|
||||
|
||||
//Clamp in case setting was updated manually using INI
|
||||
global_value = std::clamp(global_value, slider->minimum(), slider->maximum());
|
||||
|
||||
if (sif)
|
||||
{
|
||||
@@ -1086,7 +1089,9 @@ namespace SettingWidgetBinder
|
||||
QFont bold_font(orig_font);
|
||||
bold_font.setBold(true);
|
||||
|
||||
const s32 current_value = sif->GetOptionalIntValue(section.c_str(), key.c_str()).value_or(global_value);
|
||||
s32 current_value = sif->GetOptionalIntValue(section.c_str(), key.c_str()).value_or(global_value);
|
||||
current_value = std::clamp(current_value, slider->minimum(), slider->maximum());
|
||||
|
||||
slider->setValue(current_value);
|
||||
|
||||
label->setText(QStringLiteral("%1%2").arg(current_value).arg(label_suffix));
|
||||
@@ -1100,8 +1105,10 @@ namespace SettingWidgetBinder
|
||||
[sif, slider, label, label_suffix, orig_font = std::move(orig_font), section, key, default_value](const QPoint& pt) {
|
||||
QMenu menu(slider);
|
||||
slider->connect(menu.addAction(qApp->translate("SettingWidgetBinder", "Reset")), &QAction::triggered, slider,
|
||||
[sif, label, label_suffix, orig_font, section, key, default_value]() {
|
||||
const s32 global_value = Host::GetBaseIntSettingValue(section.c_str(), key.c_str(), default_value);
|
||||
[sif, slider, label, label_suffix, orig_font, section, key, default_value]() {
|
||||
s32 global_value = Host::GetBaseIntSettingValue(section.c_str(), key.c_str(), default_value);
|
||||
global_value = std::clamp(global_value, slider->minimum(), slider->maximum());
|
||||
|
||||
label->setText(QStringLiteral("%1%2").arg(global_value).arg(label_suffix));
|
||||
label->setFont(orig_font);
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
|
||||
static constexpr s32 DEFAULT_NOTIFICATIONS_DURATION = 5;
|
||||
|
||||
AchievementSettingsWidget::AchievementSettingsWidget(SettingsDialog* dialog, QWidget* parent)
|
||||
: QWidget(parent)
|
||||
, m_dialog(dialog)
|
||||
@@ -48,6 +50,9 @@ AchievementSettingsWidget::AchievementSettingsWidget(SettingsDialog* dialog, QWi
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.soundEffects, "Achievements", "SoundEffects", true);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.primedIndicators, "Achievements", "PrimedIndicators", true);
|
||||
|
||||
SettingWidgetBinder::BindSliderToIntSetting(sif, m_ui.notifications_duration, m_ui.notifications_duration_seconds,
|
||||
tr(" seconds"), "Achievements", "NotificationsDuration", DEFAULT_NOTIFICATIONS_DURATION);
|
||||
|
||||
dialog->registerWidgetHelp(m_ui.enable, tr("Enable Achievements"), tr("Unchecked"),
|
||||
tr("When enabled and logged in, PCSX2 will scan for achievements on game load."));
|
||||
dialog->registerWidgetHelp(m_ui.testMode, tr("Enable Test Mode"), tr("Unchecked"),
|
||||
@@ -70,10 +75,18 @@ AchievementSettingsWidget::AchievementSettingsWidget(SettingsDialog* dialog, QWi
|
||||
dialog->registerWidgetHelp(m_ui.primedIndicators, tr("Show Challenge Indicators"), tr("Checked"),
|
||||
tr("Shows icons in the lower-right corner of the screen when a challenge/primed achievement is active."));
|
||||
|
||||
dialog->registerWidgetHelp(m_ui.notifications_duration, tr("Notification Duration"),
|
||||
tr("5 seconds"), tr("The duration, in seconds, an achievement popup notification will remain on screen."));
|
||||
dialog->registerWidgetHelp(m_ui.notifications_duration_label, tr("Notification Duration"),
|
||||
tr("5 seconds"), tr("The duration, in seconds, an achievement popup notification will remain on screen."));
|
||||
dialog->registerWidgetHelp(m_ui.notifications_duration_seconds, tr("Notification Duration"),
|
||||
tr("5 seconds"), tr("The duration, in seconds, an achievement popup notification will remain on screen."));
|
||||
|
||||
connect(m_ui.enable, &QCheckBox::stateChanged, this, &AchievementSettingsWidget::updateEnableState);
|
||||
connect(m_ui.notifications, &QCheckBox::stateChanged, this, &AchievementSettingsWidget::updateEnableState);
|
||||
connect(m_ui.challengeMode, &QCheckBox::stateChanged, this, &AchievementSettingsWidget::updateEnableState);
|
||||
connect(m_ui.challengeMode, &QCheckBox::stateChanged, this, &AchievementSettingsWidget::onChallengeModeStateChanged);
|
||||
connect(m_ui.notifications_duration, &QSlider::valueChanged, this, &AchievementSettingsWidget::onNotificationsDurationChanged);
|
||||
|
||||
if (!m_dialog->isPerGameSettings())
|
||||
{
|
||||
@@ -105,6 +118,8 @@ void AchievementSettingsWidget::updateEnableState()
|
||||
{
|
||||
const bool enabled = m_dialog->getEffectiveBoolValue("Achievements", "Enabled", false);
|
||||
const bool challenge = m_dialog->getEffectiveBoolValue("Achievements", "ChallengeMode", false);
|
||||
const bool notifications = m_dialog->getEffectiveBoolValue("Achievements", "Notifications", true);
|
||||
|
||||
m_ui.testMode->setEnabled(enabled);
|
||||
m_ui.unofficialTestMode->setEnabled(enabled);
|
||||
m_ui.richPresence->setEnabled(enabled);
|
||||
@@ -113,6 +128,10 @@ void AchievementSettingsWidget::updateEnableState()
|
||||
m_ui.notifications->setEnabled(enabled);
|
||||
m_ui.soundEffects->setEnabled(enabled);
|
||||
m_ui.primedIndicators->setEnabled(enabled);
|
||||
|
||||
m_ui.notifications_duration->setEnabled(enabled && notifications);
|
||||
m_ui.notifications_duration_label->setEnabled(enabled && notifications);
|
||||
m_ui.notifications_duration_seconds->setEnabled(enabled && notifications);
|
||||
}
|
||||
|
||||
void AchievementSettingsWidget::onChallengeModeStateChanged()
|
||||
@@ -196,3 +215,9 @@ void AchievementSettingsWidget::onAchievementsRefreshed(quint32 id, const QStrin
|
||||
{
|
||||
m_ui.gameInfo->setText(game_info_string);
|
||||
}
|
||||
|
||||
void AchievementSettingsWidget::onNotificationsDurationChanged()
|
||||
{
|
||||
m_ui.notifications_duration_seconds->setText(tr("%1 seconds")
|
||||
.arg(m_ui.notifications_duration->value()));
|
||||
}
|
||||
@@ -33,6 +33,7 @@ private Q_SLOTS:
|
||||
void onLoginLogoutPressed();
|
||||
void onViewProfilePressed();
|
||||
void onAchievementsRefreshed(quint32 id, const QString& game_info_string, quint32 total, quint32 points);
|
||||
void onNotificationsDurationChanged();
|
||||
|
||||
private:
|
||||
void updateLoginState();
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>658</width>
|
||||
<height>496</height>
|
||||
<width>829</width>
|
||||
<height>641</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -39,13 +39,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="primedIndicators">
|
||||
<property name="text">
|
||||
<string>Show Challenge Indicators</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="richPresence">
|
||||
<property name="text">
|
||||
@@ -74,13 +67,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QCheckBox" name="testMode">
|
||||
<property name="text">
|
||||
<string>Enable Test Mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="soundEffects">
|
||||
<property name="text">
|
||||
@@ -89,12 +75,93 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="primedIndicators">
|
||||
<property name="text">
|
||||
<string>Show Challenge Indicators</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="testMode">
|
||||
<property name="text">
|
||||
<string>Enable Test Mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="notificationBox">
|
||||
<property name="title">
|
||||
<string>Notifications</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="notifications_box_layout" stretch="0,0">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="notifications">
|
||||
<property name="text">
|
||||
<string>Show Notifications</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="notifications_duration_layout" stretch="0,0,0">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="notifications_duration_label">
|
||||
<property name="text">
|
||||
<string>Duration</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="notifications_duration">
|
||||
<property name="minimum">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="pageStep">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="invertedAppearance">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="tickPosition">
|
||||
<enum>QSlider::TicksBelow</enum>
|
||||
</property>
|
||||
<property name="tickInterval">
|
||||
<number>1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="notifications_duration_seconds">
|
||||
<property name="text">
|
||||
<string>5 seconds</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -52,8 +52,8 @@ ControllerBindingWidget::ControllerBindingWidget(QWidget* parent, ControllerSett
|
||||
populateControllerTypes();
|
||||
onTypeChanged();
|
||||
|
||||
ControllerSettingWidgetBinder::BindWidgetToInputProfileString(
|
||||
m_dialog->getProfileSettingsInterface(), m_ui.controllerType, m_config_section, "Type", Pad::GetDefaultPadType(port));
|
||||
ControllerSettingWidgetBinder::BindWidgetToInputProfileString(m_dialog->getProfileSettingsInterface(),
|
||||
m_ui.controllerType, m_config_section, "Type", Pad::GetControllerInfo(Pad::GetDefaultPadType(port))->name);
|
||||
|
||||
connect(m_ui.controllerType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ControllerBindingWidget::onTypeChanged);
|
||||
connect(m_ui.bindings, &QPushButton::clicked, this, &ControllerBindingWidget::onBindingsClicked);
|
||||
@@ -79,7 +79,15 @@ void ControllerBindingWidget::populateControllerTypes()
|
||||
void ControllerBindingWidget::onTypeChanged()
|
||||
{
|
||||
const bool is_initializing = (m_ui.stackedWidget->count() == 0);
|
||||
m_controller_type = m_dialog->getStringValue(m_config_section.c_str(), "Type", Pad::GetDefaultPadType(m_port_number));
|
||||
const std::string type_name = m_dialog->getStringValue(
|
||||
m_config_section.c_str(), "Type", Pad::GetControllerInfo(Pad::GetDefaultPadType(m_port_number))->name);
|
||||
const Pad::ControllerInfo* cinfo = Pad::GetControllerInfoByName(type_name);
|
||||
if (!cinfo)
|
||||
{
|
||||
Console.Error(fmt::format("Invalid controller type name '{}' in config, ignoring.", type_name));
|
||||
cinfo = Pad::GetControllerInfo(Pad::ControllerType::NotConnected);
|
||||
}
|
||||
m_controller_type = cinfo->type;
|
||||
|
||||
if (m_bindings_widget)
|
||||
{
|
||||
@@ -100,17 +108,16 @@ void ControllerBindingWidget::onTypeChanged()
|
||||
m_macros_widget = nullptr;
|
||||
}
|
||||
|
||||
const Pad::ControllerInfo* cinfo = Pad::GetControllerInfo(m_controller_type);
|
||||
const bool has_settings = (cinfo && !cinfo->settings.empty());
|
||||
const bool has_macros = (cinfo && !cinfo->bindings.empty());
|
||||
const bool has_settings = (!cinfo->settings.empty());
|
||||
const bool has_macros = (!cinfo->bindings.empty());
|
||||
m_ui.settings->setEnabled(has_settings);
|
||||
m_ui.macros->setEnabled(has_macros);
|
||||
|
||||
if (m_controller_type == "DualShock2")
|
||||
if (cinfo->type == Pad::ControllerType::DualShock2)
|
||||
{
|
||||
m_bindings_widget = ControllerBindingWidget_DualShock2::createInstance(this);
|
||||
}
|
||||
else if (m_controller_type == "Guitar")
|
||||
else if (cinfo->type == Pad::ControllerType::Guitar)
|
||||
{
|
||||
m_bindings_widget = ControllerBindingWidget_Guitar::createInstance(this);
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
|
||||
__fi ControllerSettingsDialog* getDialog() const { return m_dialog; }
|
||||
__fi const std::string& getConfigSection() const { return m_config_section; }
|
||||
__fi const std::string& getControllerType() const { return m_controller_type; }
|
||||
__fi Pad::ControllerType getControllerType() const { return m_controller_type; }
|
||||
__fi u32 getPortNumber() const { return m_port_number; }
|
||||
|
||||
private Q_SLOTS:
|
||||
@@ -70,7 +70,7 @@ private:
|
||||
ControllerSettingsDialog* m_dialog;
|
||||
|
||||
std::string m_config_section;
|
||||
std::string m_controller_type;
|
||||
Pad::ControllerType m_controller_type;
|
||||
u32 m_port_number;
|
||||
|
||||
ControllerBindingWidget_Base* m_bindings_widget = nullptr;
|
||||
@@ -170,7 +170,7 @@ public:
|
||||
|
||||
__fi ControllerSettingsDialog* getDialog() const { return static_cast<ControllerBindingWidget*>(parent())->getDialog(); }
|
||||
__fi const std::string& getConfigSection() const { return static_cast<ControllerBindingWidget*>(parent())->getConfigSection(); }
|
||||
__fi const std::string& getControllerType() const { return static_cast<ControllerBindingWidget*>(parent())->getControllerType(); }
|
||||
__fi Pad::ControllerType getControllerType() const { return static_cast<ControllerBindingWidget*>(parent())->getControllerType(); }
|
||||
__fi u32 getPortNumber() const { return static_cast<ControllerBindingWidget*>(parent())->getPortNumber(); }
|
||||
|
||||
virtual QIcon getIcon() const;
|
||||
|
||||
@@ -51,6 +51,7 @@ GameListSettingsWidget::GameListSettingsWidget(SettingsDialog* dialog, QWidget*
|
||||
&GameListSettingsWidget::onAddSearchDirectoryButtonClicked);
|
||||
connect(m_ui.removeSearchDirectoryButton, &QPushButton::clicked, this,
|
||||
&GameListSettingsWidget::onRemoveSearchDirectoryButtonClicked);
|
||||
connect(m_ui.addExcludedFile, &QPushButton::clicked, this, &GameListSettingsWidget::onAddExcludedFileButtonClicked);
|
||||
connect(m_ui.addExcludedPath, &QPushButton::clicked, this, &GameListSettingsWidget::onAddExcludedPathButtonClicked);
|
||||
connect(m_ui.removeExcludedPath, &QPushButton::clicked, this,
|
||||
&GameListSettingsWidget::onRemoveExcludedPathButtonClicked);
|
||||
@@ -215,10 +216,21 @@ void GameListSettingsWidget::onRemoveSearchDirectoryButtonClicked()
|
||||
delete item;
|
||||
}
|
||||
|
||||
void GameListSettingsWidget::onAddExcludedFileButtonClicked()
|
||||
{
|
||||
QString path =
|
||||
QDir::toNativeSeparators(QFileDialog::getOpenFileName(QtUtils::GetRootWidget(this), tr("Select File")));
|
||||
if (path.isEmpty())
|
||||
return;
|
||||
|
||||
addExcludedPath(path.toStdString());
|
||||
}
|
||||
|
||||
void GameListSettingsWidget::onAddExcludedPathButtonClicked()
|
||||
{
|
||||
QString path =
|
||||
QDir::toNativeSeparators(QFileDialog::getOpenFileName(QtUtils::GetRootWidget(this), tr("Select Path")));
|
||||
QDir::toNativeSeparators(QFileDialog::getExistingDirectory(QtUtils::GetRootWidget(this), tr("Select Directory")));
|
||||
|
||||
if (path.isEmpty())
|
||||
return;
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ private Q_SLOTS:
|
||||
void onDirectoryListContextMenuRequested(const QPoint& point);
|
||||
void onAddSearchDirectoryButtonClicked();
|
||||
void onRemoveSearchDirectoryButtonClicked();
|
||||
void onAddExcludedFileButtonClicked();
|
||||
void onAddExcludedPathButtonClicked();
|
||||
void onRemoveExcludedPathButtonClicked();
|
||||
void onScanForNewGamesClicked();
|
||||
|
||||
@@ -57,12 +57,15 @@
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add</string>
|
||||
<string>Add...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="folder-add-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
@@ -80,6 +83,9 @@
|
||||
<iconset theme="folder-reduce-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
@@ -129,12 +135,35 @@
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add</string>
|
||||
<string>Directory...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="folder-add-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="addExcludedFile">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>File...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="file-add-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
@@ -152,6 +181,9 @@
|
||||
<iconset theme="file-reduce-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><center><strong>Note:</strong> Converting a Memory Card creates a <strong>COPY</strong> of your existing Memory Card. It does <strong">NOT delete, modify, or replace</strong> your existing Memory Card.</center></string>
|
||||
<string><center><strong>Note:</strong> Converting a Memory Card creates a <strong>COPY</strong> of your existing Memory Card. It does <strong>NOT delete, modify, or replace</strong> your existing Memory Card.</center></string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
|
||||
@@ -151,7 +151,7 @@ void SetupWizardDialog::updatePageLabels(int prev_page)
|
||||
void SetupWizardDialog::updatePageButtons()
|
||||
{
|
||||
const int page = m_ui.pages->currentIndex();
|
||||
m_ui.next->setText((page == Page_Complete) ? "&Finish" : "&Next");
|
||||
m_ui.next->setText((page == Page_Complete) ? tr("&Finish") : tr("&Next"));
|
||||
m_ui.back->setEnabled(page > 0);
|
||||
}
|
||||
|
||||
@@ -417,7 +417,7 @@ void SetupWizardDialog::setupControllerPage()
|
||||
for (const auto& [name, display_name] : Pad::GetControllerTypeNames())
|
||||
w.type_combo->addItem(QString::fromUtf8(display_name), QString::fromUtf8(name));
|
||||
ControllerSettingWidgetBinder::BindWidgetToInputProfileString(
|
||||
nullptr, w.type_combo, section, "Type", Pad::GetDefaultPadType(port));
|
||||
nullptr, w.type_combo, section, "Type", Pad::GetControllerInfo(Pad::GetDefaultPadType(port))->name);
|
||||
|
||||
w.mapping_result->setText((port == 0) ? tr("Default (Keyboard)") : tr("Default (None)"));
|
||||
|
||||
|
||||
@@ -371,12 +371,15 @@
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add</string>
|
||||
<string>Add...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="folder-add-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
@@ -394,6 +397,9 @@
|
||||
<iconset theme="folder-reduce-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
||||
@@ -338,22 +338,22 @@ static constexpr const QtHost::GlyphInfo s_glyph_info[] = {
|
||||
{
|
||||
"ja-JP", "msgothic.ttc", nullptr, "ヒラギノ角ゴシック W3.ttc",
|
||||
// auto update by update_glyph_ranges.py with pcsx2-qt_ja-JP.ts
|
||||
u"□□△△◯◯✕✕、。〜〜ああいいううええおきくぐここささしすせせそそただっつてにのはびびへべほほまみめもややよれわわをんァイウコサソタチッツテニネパビロワワンンーー一一上下不不中中丸丸了了互互今今他他代代仮仮作作使使保保信信修修倍倍値値停停備備像像入入全全公公内内再再出出切切別別利利制制削削副副割割力力加加効効動動勧勧化化十十南南参参反収取取古古可台右右合合同名向向回回固固国国在在報報場場境境壊壊変変外外大大失失奨奨始始字存完完定定実実左左帰帰常常幅幅度度式式張張形形待待後後御御性性情情想想意意感感態態成成投投択択拡拡推推提提換換敗敗数数整整新新方方既既日日明明時時更更書書替最有有期期本本果果検検標標橙橙機機止正毎毎比比況況注注消消港港湾湾準準無無照照状状率率現現璧璧環環生生用用画画異異的的直直知知確確示示種種稿稿空空索索終終緑緑編編績績能能自自般般行行表表製製複複視視覧覧解解言言設設認認語語読読赤赤起起跡跡転転軸軸込込近近追追送送通通速速進進遅遅遊遊適適選選部部長長閉閉開開関関防防限限除除隅隅集集青青非非面面韓韓音音類類香香高高黄黄++??"
|
||||
u"……□□△△◯◯✕✕ 。々々「』〜〜ああいいううええおせそそたちっぬのばびびぶぶへべほぼまやょろわわをんァイウチッツテニネロワワンン・ー一一上下不与世世両両中中乗乗了了予予事事互互交交今介他他付付代以仮仮件件任任伸伸似似位低体体何何作作使使例例供供係係保保信信修修個個倍倍借借値値停停側側備備像像優優元元先光入入全全公公共共具典内内再再冗冗処処出出分切列列初初判別利利制刷則則削削前前副副割割力力加加効効動動勧勧化化区区十十半半南単印印即即去去参参及及反収取受古古可台右右号号各各合合同名向向否否含含告告周周味味命命品品商商問問善善四四回回囲囲固固国国圧在地地均均垂垂型型域域基基報報場場境境増増壊壊声声変変外外多多大大太太央央失失奇奇奨奨好好妙妙妨妨始始子子字存安安完完定定実実容容寄寄密密対対射射小小少少尾尾展展岐岐崩崩左左巨巨己己帰帰常常幅幅平平序序度座延延式式引引弱弱張張強強当当形形彩彩影影役役待待後後得得御御復復微微心心必必応応忠忠念念急急性性悪悪情情想想意意感感態態慣慣成成戻戻所所手手打打承承投投択択抱抱押押拒拒招招拡拡拳拳持持指指振振挿挿捗捗排排接接推推描提換換揮揮援援揺揺損損撃撃撮撮操操支支改改放放敗敗数数整整文文料料断断新新方方既既日日早早明明時時曲曲更更書書替最有有望望期期未本条条析析果果桁桁案案械械検検極極楽楽概概構構標標権権橙橙機機欄欄欠次止正殊残毎毎比比水水求求汎汎決決況況法法波波注注浪浪浮浮消消深深済済減減渡渡港港湾湾満満源源準準滑滑点為無無照照牲牲特特犠犠状状獲獲率率現現理理璧璧環環生生用用由由申申画画番番異異発登白白的的目目直直相相省省知知短短破破確確示示禁禁秒秒移移程程種種稿稿空空立立端端競競第第算算管管範範精精索索細細終終組組経経結結絞絞統統続続維維緑緒線線編編縦縦縮縮績績繰繰置置義義者者肢肢能能自自致致般般良良色色荷荷落落蔵蔵行行表表衰衰装装製製複複要要見見規規視視覚覚覧覧観観角角解解言言計計記記設設許許訳訳証証試試該詳認認語語誤誤説読調調識識警警象象負負責責費貼質質赤赤起起超超足足跡跡転転軸軸軽軽辞辞込込近近返返追追送送逆逆途途通通速速連連進進遅遅遊遊過過適適選選避避部部配配重量鉄鉄録録長長閉閉開開間間関関閾閾防防降降限限除除陽陽隅隅際際集集離難電電青青非非面面韓韓音音響響頂頂順順領領頻頼題題類類飾飾香香駄駄高高鮮鮮黄黄!!()//12::>?~~"
|
||||
},
|
||||
{
|
||||
"ko-KR", "malgun.ttf", nullptr, "AppleSDGothicNeo.ttc",
|
||||
// auto update by update_glyph_ranges.py with pcsx2-qt_ko-KR.ts
|
||||
u""
|
||||
u"“”……□□▲△▶▶▼▼◀◀◯◯✕✕んん茶茶가각간간갈갈감값강강같같개개거거건건걸걸검겁것것게게겠겠격격견견결결경경계계고곡공공과과관관교교구국군군권권귀귀규규균균그극근근글글금급긍긍기기긴긴길길깁깁깃깃깅깅깊깊까까깝깝깨깨꺼꺼께께꾸꾸끄끄끔끔끝끝나나날날남남낭낮내내낸낸냅냅너너널널넣네넬넬넷넷노녹높놓누누눈눈눌눌뉴뉴느느는는늘늘능능니닉닌닌님닙닛닛다다단단닫달담담당당대대댑댑더더덜덜덤덤덩덩덮덮데덱덴덴델델뎁뎁도독돌돌동동됐됐되되된된될될됨됩두두듀듀듈듈드득든든들들듬듭등등디디딩딩때때떠떠떤떤떨떨또또뛰뛰뛸뛸뜁뜁뜨뜨라락란란랍랍랑랑래랙랜랜램램랫랫량량러럭런런럴럴럼럽렀렀렇렉렌렌렛렛려력련련렬렬렸령로록롤롤롭롭롯롯료료루루룸룸류류률률르르른른를를름름리릭린린릴릴림립릿릿링링마막만만많많말말맞맞매매맨맨맵맵맺맺머머먼먼멀멀멈멈멋멋메메며며면면명명모목못못몽몽무무문문뮬뮬므므미믹민민밀밀밉밉밍밍및및바바반반받밝밥밥방방배백밴밴뱃뱃버버번번벌벌범법벗벗베벡벤벤벨벨벼벽변변별별병병보복본본볼볼부부분분불불붙붙뷰뷰브브블블비빅빈빈빌빌빙빙빛빛빠빠빨빨사삭산산살살삼삽상상새색샘샘생생샷샷서석선선설설성성세섹센센셀셀셈셉셋셋셔셔션션셰셰소속손손솔솔송송쇄쇄쇠쇠쇼쇼수수순순숨숨쉬쉬슈슈스스슬슬습습시식신신실실심십싱싱싶싶쌍쌍쓰쓰쓸쓸씬씬아아안안않않알알암압았앙애액앤앤앨앨앵앵야약양양어어언언얻얼업없었었에에엔엔여역연연열열영영예예오오온온올올옵옵와와완완왑왑외외왼왼요요용용우우운운울울움웁웃웃워워원원월월웨웨웹웹위위유유율율으으은은을을음음응응의의이이인인일읽임입있있자작잘잘잠잠장장재재잿잿저적전전절절점접정정제제젠젠젤젤젯젯져져조족존존종종좋좌죄죄주주준준줄줄줍줍중중즈즉즌즌즐즐즘즘증증지직진진질질짐집짝짝짧짧째째쪽쪽찌찍차착참참창찾채책챕챕처척천천철철첨첩첫첫청청체체쳐쳐초초촬촬최최추축출출춤춥충충춰춰취취츠측치칙칠칠침칩칭칭카카칸칸칼칼캐캐캔캔캘캘캠캡커커컨컨컬컬컴컴케케켓켓켜켜코코콘콘콜콜콩콩쿳쿳쿼쿼퀀퀀퀴퀴큐큐크크큰큰클클큽큽키키킨킨킬킬킵킵킹킹타타탈탈탐탐태택탠탠탬탭터터턴턴텀텀테텍텐텐텔텔템템토토톱톱통통투투툴툴트특튼튼틀틀티틱팀팀팅팅파파팔팔팝팝패패팻팻퍼퍼페펙편편평평포포폴폴폼폼표표푸푸풀풀품품풍풍퓨퓨프프픈픈플플피픽필필핑핑하하한한할할함합핫핫항항해핵했행향향허허헌헌헤헤현현형형호혹혼혼홈홈홍홍화확환환활활황황회획횟횟횡횡효효후후훨훨휠휠휴휴흐흐흔흔희희히히힌힌"
|
||||
},
|
||||
{
|
||||
"zh-CN", "msyh.ttc", nullptr, "Hiragino Sans GB.ttc",
|
||||
// auto update by update_glyph_ranges.py with pcsx2-qt_zh-CN.ts
|
||||
u"‘’□□△△○○、。一丁三下不与且且世世丢丢两两个个中中丰丰串串为主么义之之乎乎乐乐乘乘也也了了事二于于互互亚些交交产产享享亮亮亲亲人人什什仅仅介介仍从他他代以们们件价任任份份伍伍休休优优会会传传估估伸伸似似但但位住佑佑体体何何余余作作佣佣佳佳使使例例供供侧侧便便俄俄保保信信修修倍倍值值倾倾假假偏偏做做停停储储像像儿儿允允元元充充先光克克免免入入全全六六兰共关关兵典兼兼内内册再写写冲决况况净净准准减减几几出击函函刀刀刃刃分切列列则则创创删删利利别别到到制刷刹刹前前剪剪副副力力功务动助势势勿勿包包化化匹区十十升升半半协协单单南南占卡卫卫印印即即压压原原去去参参及及双反发发取变叠叠口古另另只只可台右右号号各各合吉同后向向吗吗否否含含启启呈呈告告员员味味命命和和哈哈响响哪哪唤唤商商善善器器四四回回因因团团围围国图圈圈在在地地场场址址均均坏坐块块坛坛垂垂型型域域基基堆堆堪堪塔塔填填增增士士声声处处备备复复外外多多够够大大天太失失头头夹夹奇奇奏奏套套奥奥奶奶好好如如始始威威娱娱婴婴媒媒子子字存它它安安完完宏宏官官定定宝实害害家家容容宽宽宿宿寄寄密密富富寸对导导封封射射将将小小少少尔尔尚尚尝尝就就尺尺尼尾局局层层屏屏展展属属崩崩工左巨巨差差己已巴巴希希带帧帮帮常常幅幅幕幕平平并并幸幸序序库底度度延延建建开开异弃弊弊式式引引张张弹强归当录录形形彩彩影影径待很很得得循循微微德德心心必忆志志忙忙快快忽忽态态性性怪怪恢恢息息您您悬悬情情惑惑惯惯意意感感慢慢戏我或或战战截截戳戳户户所扁手手打打托托执执扩扩扫扬扳扳找找技技抑抑抖抗护护拆拆拉拉拍拍拒拒拟拟拦拦择择括括拳拳持持指指按按挑挑挡挡挪挪振振捕捕损损换换据据掌掌排排接接控掩描提插插握握搜搜摇摇撕撕撤撤播播操擎支支收收改改放放故故效效敏敏散散数数整整文文断断斯新方方旋旋无无日日旧旧时时明明易易星映是是显显晕晕普普晰晰暂暂暗暗曲曲更更替最有有服服期期未未本本机机权权杆杆束束条条来来板板极极果果柄柄某某染染查查栅栅标栈栏栏校校样根格格框框案案档档桥桥检检棕棕榜榜模模橙橙次次欧欧止步死死殊殊段段每每比比毫毫水水求求汇汇没没油油法法波波注注泻泻洲洲活活流流浅浅测测浏浏浪浪浮浮海海消消深深混混添添清清港港渲渲游游湖湖湾湾溃溃源源溢溢滑滑满满滤滤演演澳澳激激灰灰灵灵点点烈烈热热焦焦然然煞煞照照片版牌牌牙牙特特状状狂狂独独狼狼猎猎猩猩率率王王玩玩环现班班理理瑞瑞甚甚生生用用由甲电电画画畅畅界界留留略略疤疤登登白百的的皇皇盖盘目目直直相相省省看看真眠着着知知短短石石码码破破础础硬硬确确碌碌磁磁示示神神禁禁离离种种秒秒积称移移程程稍稍稳稳空空突突窗窗立立站站端端符符第第等等筛筛签签简简算算管管类类粉粉粘粘精精糊糊系系素素索索紫紫纠纠红红级级纯纯纳纳纹纹线线组组细终经经绑绑结结绕绕绘给络绝统统继继绪绪续续维维绿缀缓缓编编缩缩网网罗罗置置美美翻翻考考者者而而耗耗耳耳联联肩肩胖胖能能腊腊自自至致舍舍良良色色节节芬芬英英范范荐荐荷荷莱莱获获菜菜萄萄著著葡葡蓝蓝藏藏虑虑虚虚融融行行衡衡补补表表被被裁裂装装西西要要覆覆观观规规视视览觉角角解解触触言言警警计计认认让让议议记记许许论论设访证证诊诊译译试试询询该详语语误误说说请诸读读调调谍谍豹豹负负败账质质贴贴费费赛赛起起超超越越足足跟跟跨跨路路跳跳踏踏踪踪身身车车轨轨转转轮软轴轴轻轻载载较较辅辅辑辑输输辨辨边边达达过过运近还这进进连迟述述追追退适选选逐逐递递通通速造遇遇道道避避那那部部都都配配醒醒采采释释里重量量针针钮钮铁铁铺铺链链销锁锐锐错错键锯镜镜长长门门闭问闲闲间间队队防防阴阴附附陆陆降降限限除除险险随隐隔隔隙隙障障雄雄集集零零雾雾需需震震静静非非靠靠面面韩韩音音顶顶项须顿顿预预频频题题颜额风风饱饱馈馈首首香香马马驱驱验验高高鬼鬼魂魂魔魔麦麦黄黄黑黑默默鼓鼓鼠鼠齐齐齿齿,,??"
|
||||
u"‘’□□△△○○、。一丁三下不与且且世世丢丢两两个个中中串串为主么义之之乎乎乐乐乘乘也也了了事二于于互互亚些交交产产享享亮亮亲亲人人什什仅仅今今仍从他他代以们们件价任任份份伍伍休休优优会会传传估估伸伸似似但但位住佑佑体体何何余余作作你你佳佳使使例例供供侧侧便便俄俄保保信信修修倍倍借借值值倾倾假假偏偏做做停停储储像像儿儿允允元元充充先光克克免免入入全全六六兰共关关其兹兼兼内内册再写写冲决况况准准减减几几出击函函刀刀刃刃分切列列则则创创删删利利别别到到制刷刹刹前前剪剪副副力力功务动助势势勿勿包包化化匹区十十升升半半协协单单南南占卡卫卫印印即即卸卸压压原原去去参参叉及双反发发取变叠叠口古另另只只可台右右号号各各合吉同后向向吗吗否否含含启启呈呈告告员员味味命命和和哈哈响响哪哪唤唤商商善善器器四四回回因因团团围围国图圈圈在在地地场场址址均均坏坐块块坛坛垂垂型型域域基基堆堆堪堪塔塔填填增增士士声声处处备备复复外外多多够够大大天太失失头头夹夹奇奇奏奏套套奥奥奶奶好好如如始始威威娱娱婴婴媒媒子子字存它它安安完完宏宏官官定定宝实害害家家容容宽宽宿宿寄寄密密富富寸对导导封封射射将将小小少少尔尔尚尚尝尝就就尺尺尼尾局局层层屏屏展展属属崩崩工左巨巨差差己已巴巴希希带帧帮帮常常幅幅幕幕平平并并幸幸序序库底度度延延建建开开异弃弊弊式式引引张张弹强归当录录形形彩彩影影径待很很律律得得循循微微德德心心必忆志志忙忙快快忽忽态态性性怪怪恢恢息息您您悬悬情情惑惑惯惯想想愉愉意意感感慢慢戏我或或战战截截戳戳户户所扁手手才才打打托托执执扩扩扫扬扳扳找找技技抑抑抖抗护护拆拆拉拉拍拍拒拒拟拟拥拦择择括括拳拳持持指指按按挑挑挡挡挪挪振振捕捕损损换换据据捷捷掌掌排排接接控掩描提插插握握搜搜摇摇摘摘撕撕撤撤播播操擎支支收收改改放放故故效效敏敏散散数数整整文文断断斯新方方旋旋无无日日旧旧时时明明易易星映是是显显晕晕普普晰晰暂暂暗暗曲曲更更替最有有服服望望期期未未本本机机权权杆杆束束条条来来板板极极果果柄柄某某染染查查栅栅标栈栏栏校校样根格格框框案案档档桥桥检检棕棕榜榜槽槽模模橙橙次欢欧欧止步死死殊殊段段每每比比毫毫水水求求汇汇没没油油法法波波注注泻泻洲洲活活流流浅浅测测浏浏浪浪浮浮海海消消淡淡深深混混添添清清港港渲渲游游湖湖湾湾溃溃源源溢溢滑滑满满滤滤演演澳澳激激灰灰灵灵点点烈烈热热焦焦然然煞煞照照片版牌牌牙牙特特状状狂狂独独狼狼猎猎猩猩率率王王玩玩环现班班理理瑞瑞甚甚生生用用由由电电画画畅畅界界留留略略疤疤登登白百的的皇皇盖盘目目直直相相省省看看真眠着着知知短短石石码码破破础础硬硬确确碌碌磁磁示示神神禁禁离离种种秒秒积称移移程程稍稍稳稳空空突突窗窗立立站站端端符符第第等等答答筛筛签签简简算算管管类类粉粉粘粘精精糊糊系系素素索索紫紫纠纠红红级级纳纳纹纹线线组组细终经经绑绑结结绕绕绘给络绝统统继继绪绪续续维维绿缀缓缓编编缩缩缺缺网网罗罗置置美美翻翻考考者者而而耗耗耳耳联联肩肩胖胖能能腊腊自自至致舍舍航航良良色色节节芬芬英英范范荐荐荷荷莱莱获获菜菜萄萄著著葡葡蓝蓝藏藏虑虑虚虚融融行行衡衡补补表表被被裁裂装装西西要要覆覆见观规规视视览觉角角解解触触言言警警计计认认让让议议记记许许论论设访证证识识诊诊译译试试询询该详语语误误说说请诸读读调调谍谍豹豹负负败账质质贴贴费费赛赛赫赫起起超超越越足足跃跃跟跟跨跨路路跳跳踏踏踪踪身身车车轨轨转转轮软轴轴轻轻载载较较辅辅辑辑输输辨辨边边达达过过迎迎运近还这进进连迟述述追追退适逆逆选选逐逐递递通通速造遇遇道道避避那那邻邻部部都都配配醒醒采采释释里重量量针针钮钮铁铁铺铺链链销锁锐锐错错键锯镜镜长长门门闭问闲闲间间阅阅队队防防阴阴附陆降降限限除除险险随隐隔隔隙隙障障雄雄集集零零雾雾需需震震静静非非靠靠面面韩韩音音顶顶项须顿顿预预频频题题颜额风风饱饱馈馈首首香香马马驱驱骤骤高高鬼鬼魂魂魔魔麦麦黄黄黑黑默默鼓鼓鼠鼠齐齐齿齿,,::??"
|
||||
},
|
||||
{
|
||||
"zh-TW", "msyh.ttc", nullptr, "Hiragino Sans GB.ttc",
|
||||
// auto update by update_glyph_ranges.py with pcsx2-qt_zh-TW.ts
|
||||
u""
|
||||
u"、。一一上下不不且且並並中中主主了了互互今今令以件件任任位位何何使使來來係係保保個個優優儲儲免免入入內全具具兼兼冊冊再再到到創創功加助助動動務務勢勢包包協協南南及及取取只只可可合合名名和和商商問問啟啟嘗嘗器器嚮嚮在在壇壇多多失失如如始始娛娛存存它它安安完完官官定定容容密密將將對導就就尼尼局局希希幫幫序序庫庫建建後後得得從從您您情情意意態態應應成成戲戲戶戶所所持持指指接接擇擇擬擬支支改改敗敗新新方方於於是是時時更更會會有有服服望望本本果果查查樂樂標標模模機機檢檢次次歡步沒沒況況消消源源為為然然版版牌牌狀狀獲獲玩玩現現理理生生用用界界登發的的看看硬硬確確碼碼程程種種站站第第管管系系索索組組絡絡統統網網緒緒編編置置而而聯聯能能自自與與虛虛裝裝要要解解言言訪訪設設許許註註試試該該認認語語誤誤請請論論譯議費費起起載載輸輸迎迎這通連連遊遊過過選選配配釋釋重重錄錄錯錯鏈鏈開開間間關關防防附附需需面面項項題題驟驟默默"
|
||||
},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -7692,7 +7692,7 @@ Right click to clear binding</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.ui" line="55"/>
|
||||
<location filename="../MainWindow.cpp" line="2346"/>
|
||||
<location filename="../MainWindow.cpp" line="2353"/>
|
||||
<source>Load State</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
@@ -8281,7 +8281,7 @@ Are you sure you want to continue?</source>
|
||||
<location filename="../MainWindow.cpp" line="1103"/>
|
||||
<location filename="../MainWindow.cpp" line="1905"/>
|
||||
<location filename="../MainWindow.cpp" line="2271"/>
|
||||
<location filename="../MainWindow.cpp" line="2365"/>
|
||||
<location filename="../MainWindow.cpp" line="2372"/>
|
||||
<source>Error</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
@@ -8442,50 +8442,56 @@ Are you sure you want to continue?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.cpp" line="2290"/>
|
||||
<location filename="../MainWindow.cpp" line="2291"/>
|
||||
<source>Cover Already Exists</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.cpp" line="2291"/>
|
||||
<location filename="../MainWindow.cpp" line="2292"/>
|
||||
<source>A cover image for this game already exists, do you wish to replace it?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.cpp" line="2304"/>
|
||||
<location filename="../MainWindow.cpp" line="2310"/>
|
||||
<location filename="../MainWindow.cpp" line="2305"/>
|
||||
<location filename="../MainWindow.cpp" line="2311"/>
|
||||
<location filename="../MainWindow.cpp" line="2317"/>
|
||||
<source>Copy Error</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.cpp" line="2304"/>
|
||||
<location filename="../MainWindow.cpp" line="2305"/>
|
||||
<source>Failed to remove existing cover '%1'</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.cpp" line="2310"/>
|
||||
<location filename="../MainWindow.cpp" line="2311"/>
|
||||
<source>Failed to copy '%1' to '%2'</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.cpp" line="2319"/>
|
||||
<location filename="../MainWindow.cpp" line="2317"/>
|
||||
<source>Failed to remove '%1'</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.cpp" line="2326"/>
|
||||
<source>Confirm Reset</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.cpp" line="2320"/>
|
||||
<location filename="../MainWindow.cpp" line="2327"/>
|
||||
<source>Are you sure you want to reset the play time for '%1'?
|
||||
|
||||
This action cannot be undone.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.cpp" line="2341"/>
|
||||
<location filename="../MainWindow.cpp" line="2348"/>
|
||||
<source>Load Resume State</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.cpp" line="2343"/>
|
||||
<location filename="../MainWindow.cpp" line="2350"/>
|
||||
<source>A resume save state was found for this game, saved at:
|
||||
|
||||
%1.
|
||||
@@ -8494,112 +8500,112 @@ Do you want to load this state, or start from a fresh boot?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.cpp" line="2347"/>
|
||||
<location filename="../MainWindow.cpp" line="2354"/>
|
||||
<source>Fresh Boot</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.cpp" line="2348"/>
|
||||
<location filename="../MainWindow.cpp" line="2355"/>
|
||||
<source>Delete And Boot</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.cpp" line="2365"/>
|
||||
<location filename="../MainWindow.cpp" line="2372"/>
|
||||
<source>Failed to delete save state file '%1'.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.cpp" line="2423"/>
|
||||
<location filename="../MainWindow.cpp" line="2430"/>
|
||||
<source>Load State File...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.cpp" line="2423"/>
|
||||
<location filename="../MainWindow.cpp" line="2430"/>
|
||||
<source>Load From File...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.cpp" line="2425"/>
|
||||
<location filename="../MainWindow.cpp" line="2495"/>
|
||||
<location filename="../MainWindow.cpp" line="2432"/>
|
||||
<location filename="../MainWindow.cpp" line="2502"/>
|
||||
<source>Select Save State File</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.cpp" line="2425"/>
|
||||
<location filename="../MainWindow.cpp" line="2495"/>
|
||||
<location filename="../MainWindow.cpp" line="2432"/>
|
||||
<location filename="../MainWindow.cpp" line="2502"/>
|
||||
<source>Save States (*.p2s)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.cpp" line="2432"/>
|
||||
<location filename="../MainWindow.cpp" line="2439"/>
|
||||
<source>Delete Save States...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.cpp" line="2437"/>
|
||||
<location filename="../MainWindow.cpp" line="2444"/>
|
||||
<source>Undo Load State</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.cpp" line="2451"/>
|
||||
<location filename="../MainWindow.cpp" line="2458"/>
|
||||
<source>Resume (%2)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.cpp" line="2467"/>
|
||||
<location filename="../MainWindow.cpp" line="2474"/>
|
||||
<source>Load Slot %1 (%2)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.cpp" line="2476"/>
|
||||
<location filename="../MainWindow.cpp" line="2484"/>
|
||||
<location filename="../MainWindow.cpp" line="2483"/>
|
||||
<location filename="../MainWindow.cpp" line="2491"/>
|
||||
<source>Delete Save States</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.cpp" line="2477"/>
|
||||
<location filename="../MainWindow.cpp" line="2484"/>
|
||||
<source>Are you sure you want to delete all save states for %1?
|
||||
|
||||
The saves will not be recoverable.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.cpp" line="2484"/>
|
||||
<location filename="../MainWindow.cpp" line="2491"/>
|
||||
<source>%1 save states deleted.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.cpp" line="2494"/>
|
||||
<location filename="../MainWindow.cpp" line="2501"/>
|
||||
<source>Save To File...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.cpp" line="2513"/>
|
||||
<location filename="../MainWindow.cpp" line="2520"/>
|
||||
<source>Empty</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.cpp" line="2515"/>
|
||||
<location filename="../MainWindow.cpp" line="2522"/>
|
||||
<source>Save Slot %1 (%2)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.cpp" line="2560"/>
|
||||
<location filename="../MainWindow.cpp" line="2567"/>
|
||||
<source>Confirm Disc Change</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.cpp" line="2561"/>
|
||||
<location filename="../MainWindow.cpp" line="2568"/>
|
||||
<source>Do you want to swap discs or boot the new image (via system reset)?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.cpp" line="2562"/>
|
||||
<location filename="../MainWindow.cpp" line="2569"/>
|
||||
<source>Swap Disc</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.cpp" line="2563"/>
|
||||
<location filename="../MainWindow.cpp" line="2570"/>
|
||||
<source>Reset</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
@@ -8666,7 +8672,7 @@ The saves will not be recoverable.</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/MemoryCardConvertDialog.ui" line="89"/>
|
||||
<source><center><strong>Note:</strong> Converting a Memory Card creates a <strong>COPY</strong> of your existing Memory Card. It does <strong">NOT delete, modify, or replace</strong> your existing Memory Card.</center></source>
|
||||
<source><center><strong>Note:</strong> Converting a Memory Card creates a <strong>COPY</strong> of your existing Memory Card. It does <strong>NOT delete, modify, or replace</strong> your existing Memory Card.</center></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
@@ -9444,12 +9450,12 @@ This action cannot be reversed, and you will lose any saves on the card.</source
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/SIO/Pad/PadDualshock2.cpp" line="674"/>
|
||||
<location filename="../../pcsx2/SIO/Pad/PadDualshock2.cpp" line="704"/>
|
||||
<source>Analog light is now on for port {} / slot {}</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/SIO/Pad/PadDualshock2.cpp" line="675"/>
|
||||
<location filename="../../pcsx2/SIO/Pad/PadDualshock2.cpp" line="705"/>
|
||||
<source>Analog light is now off for port {} / slot {}</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
@@ -9815,6 +9821,17 @@ The URL was: %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SaveState</name>
|
||||
<message>
|
||||
<location filename="../../pcsx2/SaveState.cpp" line="1068"/>
|
||||
<source>This savestate is an unsupported version and cannot be used.
|
||||
|
||||
You can download PCSX2 {} from pcsx2.net and make a normal memory card save.
|
||||
Otherwise delete the savestate and do a fresh boot.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingWidgetBinder</name>
|
||||
<message>
|
||||
@@ -10250,6 +10267,7 @@ The URL was: %1</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SetupWizardDialog.ui" line="646"/>
|
||||
<location filename="../SetupWizardDialog.cpp" line="154"/>
|
||||
<source>&Next</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
@@ -10276,6 +10294,11 @@ The URL was: %1</source>
|
||||
Are you sure you want to continue?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SetupWizardDialog.cpp" line="154"/>
|
||||
<source>&Finish</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SetupWizardDialog.cpp" line="160"/>
|
||||
<source>Cancel Setup</source>
|
||||
|
||||
+30
-14
@@ -132,6 +132,8 @@ namespace Achievements
|
||||
static void UnlockAchievementCallback(s32 status_code, const std::string& content_type, Common::HTTPDownloader::Request::Data data);
|
||||
static void SubmitLeaderboardCallback(s32 status_code, const std::string& content_type, Common::HTTPDownloader::Request::Data data, u32 lboard_id);
|
||||
|
||||
static s32 GetNotificationsDuration();
|
||||
|
||||
static bool s_active = false;
|
||||
static bool s_logged_in = false;
|
||||
static bool s_challenge_mode = false;
|
||||
@@ -553,7 +555,10 @@ void Achievements::UpdateSettings(const Pcsx2Config::AchievementsOptions& old_co
|
||||
else if (!s_challenge_mode && EmuConfig.Achievements.ChallengeMode)
|
||||
{
|
||||
if (HasActiveGame())
|
||||
ImGuiFullscreen::ShowToast(std::string(), "Hardcore mode will be enabled on system reset.", 10.0f);
|
||||
{
|
||||
ImGuiFullscreen::ShowToast(std::string(),
|
||||
TRANSLATE_STR("Achievements", "Hardcore mode will be enabled on system reset."), 10.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -586,10 +591,10 @@ bool Achievements::ConfirmChallengeModeDisable(const char* trigger)
|
||||
return (RA_WarnDisableHardcore(trigger) != 0);
|
||||
#endif
|
||||
|
||||
const bool confirmed =
|
||||
Host::ConfirmMessage("Confirm Hardcore Mode", fmt::format("{0} cannot be performed while hardcore mode is active. Do you want to "
|
||||
"disable hardcore mode? {0} will be cancelled if you select No.",
|
||||
trigger));
|
||||
const bool confirmed = Host::ConfirmMessage("Confirm Hardcore Mode",
|
||||
fmt::format(TRANSLATE_FS("Achievements", "{0} cannot be performed while hardcore mode is active. Do you want "
|
||||
"to disable hardcore mode? {0} will be cancelled if you select No."),
|
||||
trigger));
|
||||
if (!confirmed)
|
||||
return false;
|
||||
|
||||
@@ -639,7 +644,12 @@ void Achievements::SetChallengeMode(bool enabled)
|
||||
s_challenge_mode = enabled;
|
||||
|
||||
if (HasActiveGame())
|
||||
ImGuiFullscreen::ShowToast(std::string(), enabled ? TRANSLATE("Achievements", "Hardcore mode is now enabled.") : TRANSLATE("Achievements", "Hardcore mode is now disabled."), 10.0f);
|
||||
{
|
||||
ImGuiFullscreen::ShowToast(std::string(),
|
||||
enabled ? TRANSLATE("Achievements", "Hardcore mode is now enabled.") :
|
||||
TRANSLATE("Achievements", "Hardcore mode is now disabled."),
|
||||
10.0f);
|
||||
}
|
||||
|
||||
if (HasActiveGame() && !IsTestModeActive())
|
||||
{
|
||||
@@ -1109,9 +1119,9 @@ void Achievements::DisplayAchievementSummary()
|
||||
summary.append(TRANSLATE_SV("Achievements", "Leaderboard submission is enabled."));
|
||||
}
|
||||
|
||||
MTGS::RunOnGSThread([title = std::move(title), summary = std::move(summary), icon = s_game_icon]() {
|
||||
MTGS::RunOnGSThread([duration = GetNotificationsDuration(), title = std::move(title), summary = std::move(summary), icon = s_game_icon]() {
|
||||
if (FullscreenUI::IsInitialized())
|
||||
ImGuiFullscreen::AddNotification(10.0f, std::move(title), std::move(summary), std::move(icon));
|
||||
ImGuiFullscreen::AddNotification(duration, std::move(title), std::move(summary), std::move(icon));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1129,9 +1139,9 @@ void Achievements::DisplayMasteredNotification()
|
||||
std::string message(fmt::format(
|
||||
"{} achievements, {} points{}", GetAchievementCount(), GetCurrentPointsForGame(), s_challenge_mode ? " (Hardcore Mode)" : ""));
|
||||
|
||||
MTGS::RunOnGSThread([title = std::move(title), message = std::move(message), icon = s_game_icon]() {
|
||||
MTGS::RunOnGSThread([duration = GetNotificationsDuration(), title = std::move(title), message = std::move(message), icon = s_game_icon]() {
|
||||
if (FullscreenUI::IsInitialized())
|
||||
ImGuiFullscreen::AddNotification(20.0f, std::move(title), std::move(message), std::move(icon));
|
||||
ImGuiFullscreen::AddNotification(duration, std::move(title), std::move(message), std::move(icon));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1842,9 +1852,9 @@ void Achievements::SubmitLeaderboardCallback(s32 status_code, const std::string&
|
||||
std::string summary = fmt::format(
|
||||
"Your Score: {} (Best: {})\nLeaderboard Position: {} of {}", submitted_score, best_score, response.new_rank, response.num_entries);
|
||||
|
||||
MTGS::RunOnGSThread([title = lb->title, summary = std::move(summary), icon = s_game_icon]() {
|
||||
MTGS::RunOnGSThread([duration = GetNotificationsDuration(), title = lb->title, summary = std::move(summary), icon = s_game_icon]() {
|
||||
if (FullscreenUI::IsInitialized())
|
||||
ImGuiFullscreen::AddNotification(10.0f, std::move(title), std::move(summary), std::move(icon));
|
||||
ImGuiFullscreen::AddNotification(duration, std::move(title), std::move(summary), std::move(icon));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1886,8 +1896,8 @@ void Achievements::UnlockAchievement(u32 achievement_id, bool add_notification /
|
||||
}
|
||||
|
||||
MTGS::RunOnGSThread(
|
||||
[title = std::move(title), description = achievement->description, icon = GetAchievementBadgePath(*achievement)]() {
|
||||
ImGuiFullscreen::AddNotification(15.0f, std::move(title), std::move(description), std::move(icon));
|
||||
[duration = GetNotificationsDuration(), title = std::move(title), description = achievement->description, icon = GetAchievementBadgePath(*achievement)]() {
|
||||
ImGuiFullscreen::AddNotification(duration, std::move(title), std::move(description), std::move(icon));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2145,6 +2155,12 @@ void Achievements::PokeMemory(unsigned address, unsigned num_bytes, void* ud, un
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
s32 Achievements::GetNotificationsDuration()
|
||||
{
|
||||
return EmuConfig.Achievements.NotificationsDuration;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_RAINTEGRATION
|
||||
|
||||
#include "RA_Consoles.h"
|
||||
|
||||
@@ -518,7 +518,6 @@ set(pcsx2GSSources
|
||||
GS/GSAlignedClass.cpp
|
||||
GS/GSCapture.cpp
|
||||
GS/GSClut.cpp
|
||||
GS/GSCrc.cpp
|
||||
GS/GSDrawingContext.cpp
|
||||
GS/GSDump.cpp
|
||||
GS/GSLocalMemory.cpp
|
||||
@@ -554,7 +553,6 @@ set(pcsx2GSHeaders
|
||||
GS/GSBlock.h
|
||||
GS/GSCapture.h
|
||||
GS/GSClut.h
|
||||
GS/GSCrc.h
|
||||
GS/GSDrawingContext.h
|
||||
GS/GSDrawingEnvironment.h
|
||||
GS/GSDump.h
|
||||
@@ -1024,8 +1022,8 @@ set(pcsx2x86Sources
|
||||
x86/iR5900Analysis.cpp
|
||||
x86/iR5900Misc.cpp
|
||||
x86/ir5900tables.cpp
|
||||
x86/ix86-32/iCore-32.cpp
|
||||
x86/ix86-32/iR5900-32.cpp
|
||||
x86/ix86-32/iCore.cpp
|
||||
x86/ix86-32/iR5900.cpp
|
||||
x86/ix86-32/iR5900Arit.cpp
|
||||
x86/ix86-32/iR5900AritImm.cpp
|
||||
x86/ix86-32/iR5900Branch.cpp
|
||||
|
||||
+42
-7
@@ -27,6 +27,11 @@ class SettingsWrapper;
|
||||
|
||||
enum class CDVD_SourceType : uint8_t;
|
||||
|
||||
namespace Pad
|
||||
{
|
||||
enum class ControllerType : u8;
|
||||
}
|
||||
|
||||
/// Generic setting information which can be reused in multiple components.
|
||||
struct SettingInfo
|
||||
{
|
||||
@@ -1196,6 +1201,39 @@ struct Pcsx2Config
|
||||
bool operator!=(const USBOptions& right) const;
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
struct PadOptions
|
||||
{
|
||||
static constexpr u32 NUM_PORTS = 8;
|
||||
|
||||
struct Port
|
||||
{
|
||||
Pad::ControllerType Type;
|
||||
|
||||
bool operator==(const PadOptions::Port& right) const;
|
||||
bool operator!=(const PadOptions::Port& right) const;
|
||||
};
|
||||
|
||||
std::array<Port, NUM_PORTS> Ports;
|
||||
|
||||
BITFIELD32()
|
||||
bool
|
||||
MultitapPort0_Enabled : 1,
|
||||
MultitapPort1_Enabled;
|
||||
BITFIELD_END
|
||||
|
||||
PadOptions();
|
||||
void LoadSave(SettingsWrapper& wrap);
|
||||
|
||||
bool IsMultitapPortEnabled(u32 port) const
|
||||
{
|
||||
return (port == 0) ? MultitapPort0_Enabled : MultitapPort1_Enabled;
|
||||
}
|
||||
|
||||
bool operator==(const PadOptions& right) const;
|
||||
bool operator!=(const PadOptions& right) const;
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Options struct for each memory card.
|
||||
//
|
||||
@@ -1224,12 +1262,14 @@ struct Pcsx2Config
|
||||
PrimedIndicators : 1;
|
||||
BITFIELD_END
|
||||
|
||||
s32 NotificationsDuration = 5;
|
||||
|
||||
AchievementsOptions();
|
||||
void LoadSave(SettingsWrapper& wrap);
|
||||
|
||||
bool operator==(const AchievementsOptions& right) const
|
||||
{
|
||||
return OpEqu(bitset);
|
||||
return OpEqu(bitset) && OpEqu(NotificationsDuration);
|
||||
}
|
||||
|
||||
bool operator!=(const AchievementsOptions& right) const
|
||||
@@ -1266,10 +1306,6 @@ struct Pcsx2Config
|
||||
McdEnableEjection : 1,
|
||||
McdFolderAutoManage : 1,
|
||||
|
||||
MultitapPort0_Enabled : 1,
|
||||
MultitapPort1_Enabled : 1,
|
||||
|
||||
ConsoleToStdio : 1,
|
||||
HostFs : 1,
|
||||
|
||||
WarnAboutUnsafeSettings : 1;
|
||||
@@ -1290,6 +1326,7 @@ struct Pcsx2Config
|
||||
SPU2Options SPU2;
|
||||
DEV9Options DEV9;
|
||||
USBOptions USB;
|
||||
PadOptions Pad;
|
||||
|
||||
TraceLogFilters Trace;
|
||||
|
||||
@@ -1323,8 +1360,6 @@ struct Pcsx2Config
|
||||
std::string FullpathToBios() const;
|
||||
std::string FullpathToMcd(uint slot) const;
|
||||
|
||||
bool MultitapEnabled(uint port) const;
|
||||
|
||||
bool operator==(const Pcsx2Config& right) const;
|
||||
bool operator!=(const Pcsx2Config& right) const
|
||||
{
|
||||
|
||||
@@ -286,5 +286,5 @@ private:
|
||||
static void WriteUInt16(u8* data, int* index, u16 value);
|
||||
static void WriteUInt32(u8* data, int* index, u32 value);
|
||||
static void WriteUInt64(u8* data, int* index, u64 value);
|
||||
static void WritePaddedString(u8* data, int* index, std::string value, u32 len);
|
||||
static void WritePaddedString(u8* data, int* index, const std::string& value, u32 len);
|
||||
};
|
||||
|
||||
@@ -37,7 +37,7 @@ void ATA::WriteUInt64(u8* data, int* index, u64 value)
|
||||
}
|
||||
|
||||
//No null char
|
||||
void ATA::WritePaddedString(u8* data, int* index, std::string value, u32 len)
|
||||
void ATA::WritePaddedString(u8* data, int* index, const std::string& value, u32 len)
|
||||
{
|
||||
memset(&data[*index], (u8)' ', len);
|
||||
memcpy(&data[*index], value.c_str(), value.length() < len ? value.length() : len);
|
||||
|
||||
@@ -39,7 +39,7 @@ void HddCreate::Start()
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
void HddCreate::WriteImage(std::string hddPath, u64 fileBytes, u64 zeroSizeBytes)
|
||||
void HddCreate::WriteImage(const std::string& hddPath, u64 fileBytes, u64 zeroSizeBytes)
|
||||
{
|
||||
constexpr int buffsize = 4 * 1024;
|
||||
const u8 buff[buffsize] = {0}; // 4kb.
|
||||
|
||||
@@ -49,5 +49,5 @@ protected:
|
||||
void SetCanceled();
|
||||
|
||||
private:
|
||||
void WriteImage(std::string hddPath, u64 fileBytes, u64 zeroSizeBytes);
|
||||
void WriteImage(const std::string& hddPath, u64 fileBytes, u64 zeroSizeBytes);
|
||||
};
|
||||
|
||||
@@ -277,7 +277,7 @@ namespace InternalServers
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
void DNS_Server::GetHost(std::string url, DNS_State* state)
|
||||
void DNS_Server::GetHost(const std::string& url, DNS_State* state)
|
||||
{
|
||||
//Need to convert to UTF16
|
||||
const int size = MultiByteToWideChar(CP_UTF8, 0, url.c_str(), -1, nullptr, 0);
|
||||
@@ -347,7 +347,7 @@ namespace InternalServers
|
||||
delete data;
|
||||
}
|
||||
#elif defined(__POSIX__)
|
||||
void DNS_Server::GetHost(std::string url, DNS_State* state)
|
||||
void DNS_Server::GetHost(const std::string& url, DNS_State* state)
|
||||
{
|
||||
//Need to spin up thread, pass the parms to it
|
||||
|
||||
@@ -357,7 +357,7 @@ namespace InternalServers
|
||||
GetHostThread.detach();
|
||||
}
|
||||
|
||||
void DNS_Server::GetAddrInfoThread(std::string url, DNS_State* state)
|
||||
void DNS_Server::GetAddrInfoThread(const std::string& url, DNS_State* state)
|
||||
{
|
||||
addrinfo hints{0};
|
||||
hints.ai_family = AF_INET;
|
||||
|
||||
@@ -95,7 +95,7 @@ namespace InternalServers
|
||||
private:
|
||||
void LoadHostList();
|
||||
bool CheckHostList(std::string url, DNS_State* state);
|
||||
void GetHost(std::string url, DNS_State* state);
|
||||
void GetHost(const std::string& url, DNS_State* state);
|
||||
void FinaliseDNS(DNS_State* state);
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -111,7 +111,7 @@ namespace InternalServers
|
||||
|
||||
static void __stdcall GetAddrInfoExCallback(DWORD dwError, DWORD dwBytes, OVERLAPPED* lpOverlapped);
|
||||
#elif defined(__POSIX__)
|
||||
void GetAddrInfoThread(std::string url, DNS_State* state);
|
||||
void GetAddrInfoThread(const std::string& url, DNS_State* state);
|
||||
#endif
|
||||
};
|
||||
} // namespace InternalServers
|
||||
|
||||
+5
-6
@@ -202,6 +202,7 @@ static bool OpenGSRenderer(GSRendererType renderer, u8* basemem)
|
||||
|
||||
g_gs_renderer->SetRegsMem(basemem);
|
||||
g_gs_renderer->ResetPCRTC();
|
||||
g_gs_renderer->UpdateRenderFixes();
|
||||
g_perfmon.Reset();
|
||||
return true;
|
||||
}
|
||||
@@ -239,7 +240,6 @@ bool GSreopen(bool recreate_device, bool recreate_renderer, const Pcsx2Config::G
|
||||
}
|
||||
|
||||
u8* basemem = g_gs_renderer->GetRegsMem();
|
||||
const u32 gamecrc = g_gs_renderer->GetGameCRC();
|
||||
|
||||
freezeData fd = {};
|
||||
std::unique_ptr<u8[]> fd_data;
|
||||
@@ -309,8 +309,6 @@ bool GSreopen(bool recreate_device, bool recreate_renderer, const Pcsx2Config::G
|
||||
Console.Error("(GSreopen) Failed to defrost");
|
||||
return false;
|
||||
}
|
||||
|
||||
g_gs_renderer->SetGameCRC(gamecrc);
|
||||
}
|
||||
|
||||
if (!capture_filename.empty())
|
||||
@@ -504,9 +502,10 @@ void GSThrottlePresentation()
|
||||
Threading::SleepUntil(s_next_manual_present_time);
|
||||
}
|
||||
|
||||
void GSSetGameCRC(u32 crc)
|
||||
void GSGameChanged()
|
||||
{
|
||||
g_gs_renderer->SetGameCRC(crc);
|
||||
if (GSConfig.UseHardwareRenderer())
|
||||
GSTextureReplacements::GameChanged();
|
||||
}
|
||||
|
||||
void GSResizeDisplayWindow(int width, int height, float scale)
|
||||
@@ -731,7 +730,7 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
|
||||
GSConfig.BeforeDrawFunctionId != old_config.BeforeDrawFunctionId ||
|
||||
GSConfig.MoveHandlerFunctionId != old_config.MoveHandlerFunctionId)
|
||||
{
|
||||
g_gs_renderer->UpdateCRCHacks();
|
||||
g_gs_renderer->UpdateRenderFixes();
|
||||
}
|
||||
|
||||
// renderer-specific options (e.g. auto flush, TC offset)
|
||||
|
||||
+1
-1
@@ -94,7 +94,7 @@ bool GSBeginCapture(std::string filename);
|
||||
void GSEndCapture();
|
||||
void GSPresentCurrentFrame();
|
||||
void GSThrottlePresentation();
|
||||
void GSSetGameCRC(u32 crc);
|
||||
void GSGameChanged();
|
||||
void GSSetDisplayAlignment(GSDisplayAlignment alignment);
|
||||
void GSResizeDisplayWindow(int width, int height, float scale);
|
||||
void GSUpdateDisplayWindow();
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "GS/GSCrc.h"
|
||||
#include "GS/GSExtra.h"
|
||||
#include "GS/GS.h"
|
||||
#include "Host.h"
|
||||
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
const CRC::Game CRC::m_games[] =
|
||||
{
|
||||
// Note: IDs 0x7ACF7E03, 0x7D4EA48F, 0x37C53760 - shouldn't be added as it's from the multiloaders when packing games.
|
||||
{0x00000000, NoTitle /* NoRegion */},
|
||||
{0xFC46EA61, Tekken5 /* JP */},
|
||||
{0x1F88EE37, Tekken5 /* EU */},
|
||||
{0x1F88BECD, Tekken5 /* EU */}, // language selector...
|
||||
{0x652050D2, Tekken5 /* US */},
|
||||
{0xEA64EF39, Tekken5 /* KO */},
|
||||
};
|
||||
|
||||
const CRC::Game& CRC::Lookup(u32 crc)
|
||||
{
|
||||
for (const Game& game : m_games)
|
||||
{
|
||||
if (game.crc == crc)
|
||||
return game;
|
||||
}
|
||||
|
||||
return m_games[0];
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
|
||||
class CRC
|
||||
{
|
||||
public:
|
||||
enum Title : u32
|
||||
{
|
||||
NoTitle,
|
||||
Tekken5,
|
||||
TitleCount,
|
||||
};
|
||||
|
||||
struct Game
|
||||
{
|
||||
u32 crc;
|
||||
Title title;
|
||||
};
|
||||
|
||||
private:
|
||||
static const Game m_games[];
|
||||
|
||||
public:
|
||||
static const Game& Lookup(u32 crc);
|
||||
};
|
||||
@@ -502,6 +502,63 @@ GSVector4i GSLocalMemory::GetRectForPageOffset(u32 base_bp, u32 offset_bp, u32 b
|
||||
return GSVector4i(pgs * page_offset_xy).xyxy() + GSVector4i::loadh(pgs);
|
||||
}
|
||||
|
||||
bool GSLocalMemory::HasOverlap(const u32 src_bp, const u32 src_bw, const u32 src_psm, const GSVector4i src_rect
|
||||
, const u32 dst_bp, const u32 dst_bw, const u32 dst_psm, const GSVector4i dst_rect)
|
||||
{
|
||||
const u32 src_start_bp = GSLocalMemory::GetStartBlockAddress(src_bp, src_bw, src_psm, src_rect) & ~(BLOCKS_PER_PAGE - 1);
|
||||
const u32 dst_start_bp = GSLocalMemory::GetStartBlockAddress(dst_bp, dst_bw, dst_psm, dst_rect) & ~(BLOCKS_PER_PAGE - 1);
|
||||
|
||||
u32 src_end_bp = ((GSLocalMemory::GetEndBlockAddress(src_bp, src_bw, src_psm, src_rect) + 1) + (BLOCKS_PER_PAGE - 1)) & ~(BLOCKS_PER_PAGE - 1);
|
||||
u32 dst_end_bp = ((GSLocalMemory::GetEndBlockAddress(dst_bp, dst_bw, dst_psm, dst_rect) + 1) + (BLOCKS_PER_PAGE - 1)) & ~(BLOCKS_PER_PAGE - 1);
|
||||
|
||||
if (src_start_bp == src_end_bp)
|
||||
{
|
||||
src_end_bp = (src_end_bp + BLOCKS_PER_PAGE) & ~(MAX_BLOCKS - 1);
|
||||
}
|
||||
|
||||
if (dst_start_bp == dst_end_bp)
|
||||
{
|
||||
dst_end_bp = (dst_end_bp + BLOCKS_PER_PAGE) & ~(MAX_BLOCKS - 1);
|
||||
}
|
||||
|
||||
// Source has wrapped, 2 separate checks.
|
||||
if (src_end_bp <= src_start_bp)
|
||||
{
|
||||
// Destination has also wrapped, so they *have* to overlap.
|
||||
if (dst_end_bp <= dst_start_bp)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dst_end_bp > src_start_bp)
|
||||
return true;
|
||||
|
||||
if (dst_start_bp < src_end_bp)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else // No wrapping on source.
|
||||
{
|
||||
// Destination wraps.
|
||||
if (dst_end_bp <= dst_start_bp)
|
||||
{
|
||||
if (src_end_bp > dst_start_bp)
|
||||
return true;
|
||||
|
||||
if (src_start_bp < dst_end_bp)
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dst_start_bp < src_end_bp && dst_end_bp > src_start_bp)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
void GSLocalMemory::ReadTexture(const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA)
|
||||
|
||||
@@ -546,6 +546,7 @@ public:
|
||||
GSPixelOffset* GetPixelOffset(const GIFRegFRAME& FRAME, const GIFRegZBUF& ZBUF);
|
||||
GSPixelOffset4* GetPixelOffset4(const GIFRegFRAME& FRAME, const GIFRegZBUF& ZBUF);
|
||||
std::vector<GSVector2i>* GetPage2TileMap(const GIFRegTEX0& TEX0);
|
||||
static bool HasOverlap(u32 src_bp, u32 src_bw, u32 src_psm, GSVector4i src_rect, u32 dst_bp, u32 dst_bw, u32 dst_psm, GSVector4i dst_rect);
|
||||
static bool IsPageAligned(u32 psm, const GSVector4i& rc);
|
||||
static u32 GetStartBlockAddress(u32 bp, u32 bw, u32 psm, GSVector4i rect);
|
||||
static u32 GetEndBlockAddress(u32 bp, u32 bw, u32 psm, GSVector4i rect);
|
||||
|
||||
+172
-108
@@ -1416,7 +1416,7 @@ void GSState::GIFRegHandlerTRXDIR(const GIFReg* RESTRICT r)
|
||||
{
|
||||
GL_REG("TRXDIR = 0x%x_%x", r->U32[1], r->U32[0]);
|
||||
|
||||
Flush(GSFlushReason::GSTRANSFER);
|
||||
FlushWrite();
|
||||
|
||||
m_env.TRXDIR = r->TRXDIR;
|
||||
|
||||
@@ -1429,6 +1429,7 @@ void GSState::GIFRegHandlerTRXDIR(const GIFReg* RESTRICT r)
|
||||
m_tr.Init(m_env.TRXPOS.SSAX, m_env.TRXPOS.SSAY, m_env.BITBLTBUF, false);
|
||||
break;
|
||||
case 2: // local -> local
|
||||
CheckWriteOverlap(true, true);
|
||||
Move();
|
||||
break;
|
||||
default: // 3 deactivated as stated by manual. Tested on hardware and no transfers happen.
|
||||
@@ -1476,6 +1477,7 @@ void GSState::Flush(GSFlushReason reason)
|
||||
}
|
||||
|
||||
m_dirty_gs_regs = 0;
|
||||
temp_draw_rect = GSVector4i::zero();
|
||||
}
|
||||
|
||||
m_state_flush_reason = GSFlushReason::UNKNOWN;
|
||||
@@ -1508,6 +1510,8 @@ void GSState::FlushWrite()
|
||||
|
||||
g_perfmon.Put(GSPerfMon::Swizzle, len);
|
||||
s_transfer_n++;
|
||||
if (m_tr.start >= m_tr.total)
|
||||
m_env.TRXDIR.XDIR = 3;
|
||||
}
|
||||
|
||||
// This function decides if the context has changed in a way which warrants flushing the draw.
|
||||
@@ -1687,59 +1691,148 @@ void GSState::FlushPrim()
|
||||
}
|
||||
}
|
||||
}
|
||||
GSVector4i GSState::GetTEX0Rect()
|
||||
{
|
||||
GSVector4i ret = GSVector4i::zero();
|
||||
const GSDrawingContext& prev_ctx = m_prev_env.CTXT[m_prev_env.PRIM.CTXT];
|
||||
|
||||
if (prev_ctx.CLAMP.WMS <= 1) // CLAMP/REPEAT
|
||||
{
|
||||
ret.x = 0;
|
||||
ret.z = 1 << prev_ctx.TEX0.TW;
|
||||
}
|
||||
else if (prev_ctx.CLAMP.WMS == 3) // REGION_REPEAT
|
||||
{
|
||||
ret.x = prev_ctx.CLAMP.MAXU;
|
||||
ret.z = prev_ctx.CLAMP.MAXU | prev_ctx.CLAMP.MINU;
|
||||
}
|
||||
else // 2 REGION_CLAMP
|
||||
{
|
||||
ret.x = prev_ctx.CLAMP.MINU;
|
||||
ret.z = prev_ctx.CLAMP.MAXU;
|
||||
}
|
||||
|
||||
if (prev_ctx.CLAMP.WMT <= 1) // CLAMP/REPEAT
|
||||
{
|
||||
ret.y = 0;
|
||||
ret.w = 1 << prev_ctx.TEX0.TH;
|
||||
}
|
||||
else if (prev_ctx.CLAMP.WMT == 3) // REGION_REPEAT
|
||||
{
|
||||
ret.y = prev_ctx.CLAMP.MAXV;
|
||||
ret.w = prev_ctx.CLAMP.MAXV | prev_ctx.CLAMP.MINV;
|
||||
}
|
||||
else // 2 REGION_CLAMP
|
||||
{
|
||||
ret.y = prev_ctx.CLAMP.MINV;
|
||||
ret.w = prev_ctx.CLAMP.MAXV;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void GSState::CheckWriteOverlap(bool req_write, bool req_read)
|
||||
{
|
||||
const int w = m_env.TRXREG.RRW;
|
||||
const int h = m_env.TRXREG.RRH;
|
||||
const GIFRegBITBLTBUF& blit = m_env.BITBLTBUF;
|
||||
|
||||
const GSDrawingContext& prev_ctx = m_prev_env.CTXT[m_prev_env.PRIM.CTXT];
|
||||
const GSVector4i write_rect = GSVector4i(m_env.TRXPOS.DSAX, m_env.TRXPOS.DSAY, m_env.TRXPOS.DSAX + w, m_env.TRXPOS.DSAY + h);
|
||||
const u32 write_start_bp = GSLocalMemory::GetStartBlockAddress(blit.DBP, blit.DBW, blit.DPSM, write_rect);
|
||||
const u32 write_end_bp = ((GSLocalMemory::GetEndBlockAddress(blit.DBP, blit.DBW, blit.DPSM, write_rect) + 1) + (BLOCKS_PER_PAGE - 1)) & ~(BLOCKS_PER_PAGE - 1);
|
||||
const GSVector4i tex_rect = m_prev_env.PRIM.TME ? GetTEX0Rect() : GSVector4i::zero();
|
||||
|
||||
if (m_index.tail > 0)
|
||||
{
|
||||
// Only flush on a NEW transfer if a pending one is using the same address or overlap.
|
||||
// Check Fast & Furious (Hardare mode) and Assault Suits Valken (either renderer) and Tomb Raider - Angel of Darkness menu (TBP != DBP but overlaps).
|
||||
// Cartoon Network overwrites its own Z buffer in the middle of a draw.
|
||||
// Alias wraps its transfers, so be careful
|
||||
const GSVector4i read_rect = GSVector4i(m_env.TRXPOS.SSAX, m_env.TRXPOS.SSAY, m_env.TRXPOS.SSAX + w, m_env.TRXPOS.SSAY + h);
|
||||
|
||||
if (req_write && m_prev_env.PRIM.TME)
|
||||
{
|
||||
if (GSLocalMemory::HasOverlap(blit.DBP, blit.DBW, blit.DPSM, write_rect, prev_ctx.TEX0.TBP0, prev_ctx.TEX0.TBW, prev_ctx.TEX0.PSM, tex_rect))
|
||||
{
|
||||
|
||||
Flush(GSFlushReason::UPLOADDIRTYTEX);
|
||||
}
|
||||
if (prev_ctx.TEX1.MXL > 0 && prev_ctx.TEX1.MMIN >= 2 && prev_ctx.TEX1.MMIN <= 5)
|
||||
{
|
||||
switch (prev_ctx.TEX1.MXL)
|
||||
{
|
||||
case 6:
|
||||
if (GSLocalMemory::HasOverlap(blit.DBP, blit.DBW, blit.DPSM, write_rect, prev_ctx.MIPTBP2.TBP6, prev_ctx.MIPTBP2.TBW6, prev_ctx.TEX0.PSM, GSVector4i(tex_rect.x >> 6, tex_rect.y >> 6, tex_rect.z >> 6, tex_rect.w >> 6)))
|
||||
Flush(GSFlushReason::UPLOADDIRTYTEX);
|
||||
[[fallthrough]];
|
||||
case 5:
|
||||
if (GSLocalMemory::HasOverlap(blit.DBP, blit.DBW, blit.DPSM, write_rect, prev_ctx.MIPTBP2.TBP5, prev_ctx.MIPTBP2.TBW5, prev_ctx.TEX0.PSM, GSVector4i(tex_rect.x >> 5, tex_rect.y >> 5, tex_rect.z >> 5, tex_rect.w >> 5)))
|
||||
Flush(GSFlushReason::UPLOADDIRTYTEX);
|
||||
[[fallthrough]];
|
||||
case 4:
|
||||
if (GSLocalMemory::HasOverlap(blit.DBP, blit.DBW, blit.DPSM, write_rect, prev_ctx.MIPTBP2.TBP4, prev_ctx.MIPTBP2.TBW4, prev_ctx.TEX0.PSM, GSVector4i(tex_rect.x >> 4, tex_rect.y >> 4, tex_rect.z >> 4, tex_rect.w >> 4)))
|
||||
Flush(GSFlushReason::UPLOADDIRTYTEX);
|
||||
[[fallthrough]];
|
||||
case 3:
|
||||
if (GSLocalMemory::HasOverlap(blit.DBP, blit.DBW, blit.DPSM, write_rect, prev_ctx.MIPTBP1.TBP3, prev_ctx.MIPTBP1.TBW3, prev_ctx.TEX0.PSM, GSVector4i(tex_rect.x >> 3, tex_rect.y >> 3, tex_rect.z >> 3, tex_rect.w >> 3)))
|
||||
Flush(GSFlushReason::UPLOADDIRTYTEX);
|
||||
[[fallthrough]];
|
||||
case 2:
|
||||
if (GSLocalMemory::HasOverlap(blit.DBP, blit.DBW, blit.DPSM, write_rect, prev_ctx.MIPTBP1.TBP2, prev_ctx.MIPTBP1.TBW2, prev_ctx.TEX0.PSM, GSVector4i(tex_rect.x >> 2, tex_rect.y >> 2, tex_rect.z >> 2, tex_rect.w >> 2)))
|
||||
Flush(GSFlushReason::UPLOADDIRTYTEX);
|
||||
[[fallthrough]];
|
||||
case 1:
|
||||
if (GSLocalMemory::HasOverlap(blit.DBP, blit.DBW, blit.DPSM, write_rect, prev_ctx.MIPTBP1.TBP1, prev_ctx.MIPTBP1.TBW1, prev_ctx.TEX0.PSM, GSVector4i(tex_rect.x >> 1, tex_rect.y >> 1, tex_rect.z >> 1, tex_rect.w >> 1)))
|
||||
Flush(GSFlushReason::UPLOADDIRTYTEX);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const u32 frame_mask = GSLocalMemory::m_psm[prev_ctx.FRAME.PSM].fmsk;
|
||||
const bool frame_required = (!(prev_ctx.TEST.ATE && prev_ctx.TEST.ATST == 0 && (prev_ctx.TEST.AFAIL == 2 || prev_ctx.TEST.AFAIL == 0)) && ((prev_ctx.FRAME.FBMSK & frame_mask) != frame_mask)) || prev_ctx.TEST.DATE;
|
||||
if (frame_required)
|
||||
{
|
||||
if ((req_write && GSLocalMemory::HasOverlap(blit.DBP, blit.DBW, blit.DPSM, write_rect, prev_ctx.FRAME.Block(), prev_ctx.FRAME.FBW, prev_ctx.FRAME.PSM, temp_draw_rect)) ||
|
||||
(req_read && GSLocalMemory::HasOverlap(blit.SBP, blit.SBW, blit.SPSM, read_rect, prev_ctx.FRAME.Block(), prev_ctx.FRAME.FBW, prev_ctx.FRAME.PSM, temp_draw_rect)))
|
||||
Flush(GSFlushReason::UPLOADDIRTYFRAME);
|
||||
}
|
||||
|
||||
const bool zbuf_required = (!(prev_ctx.TEST.ATE && prev_ctx.TEST.ATST == 0 && prev_ctx.TEST.AFAIL != 2) && !prev_ctx.ZBUF.ZMSK) || (prev_ctx.TEST.ZTE && prev_ctx.TEST.ZTST > ZTST_ALWAYS);
|
||||
if (zbuf_required)
|
||||
{
|
||||
if ((req_write && GSLocalMemory::HasOverlap(blit.DBP, blit.DBW, blit.DPSM, write_rect, prev_ctx.ZBUF.Block(), prev_ctx.FRAME.FBW, prev_ctx.ZBUF.PSM, temp_draw_rect)) ||
|
||||
(req_read && GSLocalMemory::HasOverlap(blit.SBP, blit.SBW, blit.SPSM, read_rect, prev_ctx.ZBUF.Block(), prev_ctx.FRAME.FBW, prev_ctx.ZBUF.PSM, temp_draw_rect)))
|
||||
Flush(GSFlushReason::UPLOADDIRTYZBUF);
|
||||
}
|
||||
}
|
||||
|
||||
// Invalid the CLUT if it crosses paths.
|
||||
m_mem.m_clut.InvalidateRange(write_start_bp, write_end_bp);
|
||||
}
|
||||
|
||||
void GSState::Write(const u8* mem, int len)
|
||||
{
|
||||
if (m_env.TRXDIR.XDIR == 3)
|
||||
return;
|
||||
|
||||
const int w = m_env.TRXREG.RRW;
|
||||
int h = m_env.TRXREG.RRH;
|
||||
const int h = m_env.TRXREG.RRH;
|
||||
|
||||
CheckWriteOverlap(true, false);
|
||||
|
||||
if (!m_tr.Update(w, h, GSLocalMemory::m_psm[m_env.BITBLTBUF.DPSM].trbpp, len))
|
||||
{
|
||||
m_env.TRXDIR.XDIR = 3;
|
||||
return;
|
||||
}
|
||||
|
||||
GIFRegBITBLTBUF& blit = m_tr.m_blit;
|
||||
const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[blit.DPSM];
|
||||
|
||||
if (!m_tr.Update(w, h, psm.trbpp, len))
|
||||
return;
|
||||
|
||||
if (m_tr.end == 0)
|
||||
{
|
||||
const GSDrawingContext& prev_ctx = m_prev_env.CTXT[m_prev_env.PRIM.CTXT];
|
||||
const u32 write_start_bp = m_mem.m_psm[blit.DPSM].info.bn(m_env.TRXPOS.DSAX, m_env.TRXPOS.DSAY, blit.DBP, blit.DBW);
|
||||
const u32 write_end_bp = m_mem.m_psm[blit.DPSM].info.bn(m_env.TRXPOS.DSAX + w - 1, m_env.TRXPOS.DSAY + h - 1, blit.DBP, blit.DBW);
|
||||
|
||||
// Only flush on a NEW transfer if a pending one is using the same address or overlap.
|
||||
// Check Fast & Furious (Hardare mode) and Assault Suits Valken (either renderer) and Tomb Raider - Angel of Darkness menu (TBP != DBP but overlaps).
|
||||
if (m_index.tail > 0)
|
||||
{
|
||||
const u32 tex_end_bp = m_mem.m_psm[prev_ctx.TEX0.PSM].info.bn((1 << prev_ctx.TEX0.TW) - 1, (1 << prev_ctx.TEX0.TH) - 1, prev_ctx.TEX0.TBP0, prev_ctx.TEX0.TBW);
|
||||
|
||||
if (m_prev_env.PRIM.TME && write_end_bp > prev_ctx.TEX0.TBP0 && write_start_bp <= tex_end_bp)
|
||||
Flush(GSFlushReason::UPLOADDIRTYTEX);
|
||||
else
|
||||
{
|
||||
const u32 frame_mask = GSLocalMemory::m_psm[prev_ctx.FRAME.PSM].fmsk;
|
||||
const bool frame_required = (!(prev_ctx.TEST.ATE && prev_ctx.TEST.ATST == 0 && prev_ctx.TEST.AFAIL == 2) && ((prev_ctx.FRAME.FBMSK & frame_mask) != frame_mask)) || (prev_ctx.TEST.ATE && prev_ctx.TEST.ATST > ATST_ALWAYS);
|
||||
if (frame_required)
|
||||
{
|
||||
const u32 draw_start_bp = m_mem.m_psm[prev_ctx.FRAME.PSM].info.bn(temp_draw_rect.x, temp_draw_rect.y, prev_ctx.FRAME.Block(), prev_ctx.FRAME.FBW);
|
||||
const u32 draw_end_bp = m_mem.m_psm[prev_ctx.FRAME.PSM].info.bn(temp_draw_rect.z - 1, temp_draw_rect.w - 1, prev_ctx.FRAME.Block(), prev_ctx.FRAME.FBW);
|
||||
|
||||
if (write_end_bp > draw_start_bp && write_start_bp <= draw_end_bp)
|
||||
Flush(GSFlushReason::UPLOADDIRTYFRAME);
|
||||
}
|
||||
|
||||
const bool zbuf_required = (!(prev_ctx.TEST.ATE && prev_ctx.TEST.ATST == 0 && prev_ctx.TEST.AFAIL != 2) && !prev_ctx.ZBUF.ZMSK) || (prev_ctx.TEST.ZTE && prev_ctx.TEST.ZTST > ZTST_ALWAYS);
|
||||
if (zbuf_required)
|
||||
{
|
||||
const u32 draw_start_bp = m_mem.m_psm[prev_ctx.ZBUF.PSM].info.bn(temp_draw_rect.x, temp_draw_rect.y, prev_ctx.ZBUF.Block(), prev_ctx.FRAME.FBW);
|
||||
const u32 draw_end_bp = m_mem.m_psm[prev_ctx.ZBUF.PSM].info.bn(temp_draw_rect.z - 1, temp_draw_rect.w - 1, prev_ctx.ZBUF.Block(), prev_ctx.FRAME.FBW);
|
||||
|
||||
if (write_end_bp > draw_start_bp && write_start_bp <= draw_end_bp)
|
||||
Flush(GSFlushReason::UPLOADDIRTYZBUF);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Invalid the CLUT if it crosses paths.
|
||||
m_mem.m_clut.InvalidateRange(write_start_bp, write_end_bp);
|
||||
|
||||
GSVector4i r;
|
||||
|
||||
r.left = m_env.TRXPOS.DSAX;
|
||||
@@ -1780,7 +1873,7 @@ void GSState::Write(const u8* mem, int len)
|
||||
|
||||
g_perfmon.Put(GSPerfMon::Swizzle, len);
|
||||
s_transfer_n++;
|
||||
|
||||
m_env.TRXDIR.XDIR = 3;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1799,11 +1892,16 @@ void GSState::InitReadFIFO(u8* mem, int len)
|
||||
if (len <= 0 || m_tr.total != 0)
|
||||
return;
|
||||
|
||||
if (m_env.TRXDIR.XDIR == 3)
|
||||
return;
|
||||
|
||||
const int w = m_env.TRXREG.RRW;
|
||||
const int h = m_env.TRXREG.RRH;
|
||||
|
||||
const u16 bpp = GSLocalMemory::m_psm[m_env.BITBLTBUF.SPSM].trbpp;
|
||||
|
||||
CheckWriteOverlap(false, true);
|
||||
|
||||
if (!m_tr.Update(w, h, bpp, len))
|
||||
return;
|
||||
|
||||
@@ -1834,13 +1932,24 @@ void GSState::Read(u8* mem, int len)
|
||||
if (len <= 0 || m_tr.total == 0)
|
||||
return;
|
||||
|
||||
if (m_env.TRXDIR.XDIR == 3)
|
||||
return;
|
||||
|
||||
const int w = m_env.TRXREG.RRW;
|
||||
const int h = m_env.TRXREG.RRH;
|
||||
const u16 bpp = GSLocalMemory::m_psm[m_env.BITBLTBUF.SPSM].trbpp;
|
||||
|
||||
CheckWriteOverlap(false, true);
|
||||
|
||||
if (!m_tr.Update(w, h, bpp, len))
|
||||
return;
|
||||
|
||||
const u32 draw = s_n;
|
||||
|
||||
|
||||
if (draw != s_n)
|
||||
DevCon.Warning("Warning! Possible incorrect data download");
|
||||
|
||||
// If it wraps memory, we need to break it up so we don't read out of bounds.
|
||||
if ((m_tr.end + len) > m_mem.m_vmsize)
|
||||
{
|
||||
@@ -1856,6 +1965,9 @@ void GSState::Read(u8* mem, int len)
|
||||
memcpy(mem, &m_tr.buff[m_tr.end], len);
|
||||
m_tr.end += len;
|
||||
}
|
||||
|
||||
if(m_tr.end >= m_tr.total)
|
||||
m_env.TRXDIR.XDIR = 3;
|
||||
}
|
||||
|
||||
void GSState::Move()
|
||||
@@ -1864,6 +1976,9 @@ void GSState::Move()
|
||||
// guitar hero copies the far end of the board to do a similar blend too
|
||||
s_transfer_n++;
|
||||
|
||||
if (m_env.TRXDIR.XDIR == 3)
|
||||
return;
|
||||
|
||||
int sx = m_env.TRXPOS.SSAX;
|
||||
int sy = m_env.TRXPOS.SSAY;
|
||||
int dx = m_env.TRXPOS.DSAX;
|
||||
@@ -1909,43 +2024,6 @@ void GSState::Move()
|
||||
const GSOffset spo = m_mem.GetOffset(sbp, sbw, m_env.BITBLTBUF.SPSM);
|
||||
const GSOffset dpo = m_mem.GetOffset(dbp, dbw, m_env.BITBLTBUF.DPSM);
|
||||
|
||||
const GSDrawingContext& prev_ctx = m_prev_env.CTXT[m_prev_env.PRIM.CTXT];
|
||||
const u32 write_start_bp = m_mem.m_psm[m_env.BITBLTBUF.DPSM].info.bn(m_env.TRXPOS.DSAX, m_env.TRXPOS.DSAY, dbp, dbw);
|
||||
const u32 write_end_bp = m_mem.m_psm[m_env.BITBLTBUF.DPSM].info.bn(m_env.TRXPOS.DSAX + w - 1, m_env.TRXPOS.DSAY + h - 1, dbp, dbw);
|
||||
|
||||
// Only flush on a NEW transfer if a pending one is using the same address or overlap.
|
||||
// Unknown if games use this one, but best to be safe.
|
||||
if (m_index.tail > 0)
|
||||
{
|
||||
const u32 tex_end_bp = m_mem.m_psm[prev_ctx.TEX0.PSM].info.bn((1 << prev_ctx.TEX0.TW) - 1, (1 << prev_ctx.TEX0.TH) - 1, prev_ctx.TEX0.TBP0, prev_ctx.TEX0.TBW);
|
||||
|
||||
if (m_prev_env.PRIM.TME && write_end_bp >= prev_ctx.TEX0.TBP0 && write_start_bp <= tex_end_bp)
|
||||
Flush(GSFlushReason::LOCALTOLOCALMOVE);
|
||||
else
|
||||
{
|
||||
const u32 frame_mask = GSLocalMemory::m_psm[prev_ctx.FRAME.PSM].fmsk;
|
||||
const bool frame_required = (!(prev_ctx.TEST.ATE && prev_ctx.TEST.ATST == 0 && prev_ctx.TEST.AFAIL == 2) && ((prev_ctx.FRAME.FBMSK & frame_mask) != frame_mask)) || (prev_ctx.TEST.ATE && prev_ctx.TEST.ATST > ATST_ALWAYS);
|
||||
if (frame_required)
|
||||
{
|
||||
const u32 draw_start_bp = m_mem.m_psm[prev_ctx.FRAME.PSM].info.bn(temp_draw_rect.x, temp_draw_rect.y, prev_ctx.FRAME.Block(), prev_ctx.FRAME.FBW);
|
||||
const u32 draw_end_bp = m_mem.m_psm[prev_ctx.FRAME.PSM].info.bn(temp_draw_rect.z - 1, temp_draw_rect.w - 1, prev_ctx.FRAME.Block(), prev_ctx.FRAME.FBW);
|
||||
|
||||
if (write_end_bp > draw_start_bp && write_start_bp <= draw_end_bp)
|
||||
Flush(GSFlushReason::UPLOADDIRTYFRAME);
|
||||
}
|
||||
|
||||
const bool zbuf_required = (!(prev_ctx.TEST.ATE && prev_ctx.TEST.ATST == 0 && prev_ctx.TEST.AFAIL != 2) && !prev_ctx.ZBUF.ZMSK) || (prev_ctx.TEST.ZTE && prev_ctx.TEST.ZTST > ZTST_ALWAYS);
|
||||
if (zbuf_required)
|
||||
{
|
||||
const u32 draw_start_bp = m_mem.m_psm[prev_ctx.ZBUF.PSM].info.bn(temp_draw_rect.x, temp_draw_rect.y, prev_ctx.ZBUF.Block(), prev_ctx.FRAME.FBW);
|
||||
const u32 draw_end_bp = m_mem.m_psm[prev_ctx.ZBUF.PSM].info.bn(temp_draw_rect.z - 1, temp_draw_rect.w - 1, prev_ctx.ZBUF.Block(), prev_ctx.FRAME.FBW);
|
||||
|
||||
if (write_end_bp > draw_start_bp && write_start_bp <= draw_end_bp)
|
||||
Flush(GSFlushReason::UPLOADDIRTYZBUF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GSVector4i r;
|
||||
r.left = m_env.TRXPOS.DSAX;
|
||||
r.top = m_env.TRXPOS.DSAY;
|
||||
@@ -1969,9 +2047,6 @@ void GSState::Move()
|
||||
m_draw_transfers.push_back(new_transfer);
|
||||
}
|
||||
|
||||
// Invalid the CLUT if it crosses paths.
|
||||
m_mem.m_clut.InvalidateRange(write_start_bp, write_end_bp);
|
||||
|
||||
auto genericCopy = [=](const GSOffset& dpo, const GSOffset& spo, auto&& getPAHelper, auto&& pxCopyFn)
|
||||
{
|
||||
int _sy = sy, _dy = dy; // Faster with local copied variables, compiler optimizations are dumb
|
||||
@@ -2116,6 +2191,8 @@ void GSState::Move()
|
||||
(m_mem.*dpsm.wpa)(doff, (m_mem.*spsm.rpa)(soff));
|
||||
});
|
||||
}
|
||||
|
||||
m_env.TRXDIR.XDIR = 3;
|
||||
}
|
||||
|
||||
void GSState::SoftReset(u32 mask)
|
||||
@@ -2139,8 +2216,6 @@ void GSState::SoftReset(u32 mask)
|
||||
|
||||
void GSState::ReadFIFO(u8* mem, int size)
|
||||
{
|
||||
Flush(GSFlushReason::DOWNLOADFIFO);
|
||||
|
||||
size *= 16;
|
||||
|
||||
Read(mem, size);
|
||||
@@ -2187,11 +2262,6 @@ void GSState::ReadLocalMemoryUnsync(u8* mem, int qwc, GIFRegBITBLTBUF BITBLTBUF,
|
||||
}
|
||||
}
|
||||
|
||||
void GSState::PurgePool()
|
||||
{
|
||||
g_gs_device->PurgePool();
|
||||
}
|
||||
|
||||
void GSState::PurgeTextureCache()
|
||||
{
|
||||
}
|
||||
@@ -2610,17 +2680,6 @@ int GSState::Defrost(const freezeData* fd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GSState::SetGameCRC(u32 crc)
|
||||
{
|
||||
m_crc = crc;
|
||||
UpdateCRCHacks();
|
||||
}
|
||||
|
||||
void GSState::UpdateCRCHacks()
|
||||
{
|
||||
m_game = CRC::Lookup(GSConfig.UserHacks_DisableRenderFixes ? 0 : m_crc);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
void GSState::UpdateContext()
|
||||
@@ -3705,7 +3764,8 @@ void GSState::CalcAlphaMinMax(const int tex_alpha_min, const int tex_alpha_max)
|
||||
return;
|
||||
|
||||
// We wanted to force an update as we now know the alpha of the non-indexed texture.
|
||||
int min = tex_alpha_min, max = tex_alpha_max;
|
||||
// Limit max to 255 as we send 500 when we don't know, makes calculating 24/16bit easier.
|
||||
int min = tex_alpha_min, max = std::min(tex_alpha_max, 255);
|
||||
|
||||
if (IsCoverageAlpha())
|
||||
{
|
||||
@@ -3727,12 +3787,16 @@ void GSState::CalcAlphaMinMax(const int tex_alpha_min, const int tex_alpha_max)
|
||||
a.w = max;
|
||||
break;
|
||||
case 1:
|
||||
a.y = env.TEXA.AEM ? 0 : env.TEXA.TA0;
|
||||
a.w = env.TEXA.TA0;
|
||||
// If we're using the alpha from the texture, not the whole range, we can just use tex_alpha_min/max.
|
||||
// AEM and TA0 re precomputed with GSBlock::ReadAndExpandBlock24, so already worked out for tex_alpha.
|
||||
a.y = (tex_alpha_max < 500) ? min : (env.TEXA.AEM ? 0 : env.TEXA.TA0);
|
||||
a.w = (tex_alpha_max < 500) ? max : env.TEXA.TA0;
|
||||
break;
|
||||
case 2:
|
||||
a.y = env.TEXA.AEM ? 0 : std::min(env.TEXA.TA0, env.TEXA.TA1);
|
||||
a.w = std::max(env.TEXA.TA0, env.TEXA.TA1);
|
||||
// If we're using the alpha from the texture, not the whole range, we can just use tex_alpha_min/max.
|
||||
// AEM, TA0 and TA1 are precomputed with GSBlock::ReadAndExpandBlock16, so already worked out for tex_alpha.
|
||||
a.y = (tex_alpha_max < 500) ? min : (env.TEXA.AEM ? 0 : std::min(env.TEXA.TA0, env.TEXA.TA1));
|
||||
a.w = (tex_alpha_max < 500) ? max : std::max(env.TEXA.TA0, env.TEXA.TA1);
|
||||
break;
|
||||
case 3:
|
||||
m_mem.m_clut.GetAlphaMinMax32(a.y, a.w);
|
||||
|
||||
+3
-9
@@ -23,7 +23,6 @@
|
||||
#include "GS/Renderers/Common/GSVertexTrace.h"
|
||||
#include "GS/Renderers/Common/GSDevice.h"
|
||||
#include "GS/GSVector.h"
|
||||
#include "GSCrc.h"
|
||||
#include "GSAlignedClass.h"
|
||||
|
||||
class GSDumpBase;
|
||||
@@ -176,7 +175,7 @@ protected:
|
||||
GSVertexTrace::VertexAlpha& GetAlphaMinMax()
|
||||
{
|
||||
if (!m_vt.m_alpha.valid)
|
||||
CalcAlphaMinMax(0, 255);
|
||||
CalcAlphaMinMax(0, 500);
|
||||
return m_vt.m_alpha;
|
||||
}
|
||||
struct TextureMinMaxResult
|
||||
@@ -218,8 +217,6 @@ public:
|
||||
const GSDrawingEnvironment* m_draw_env = &m_env;
|
||||
GSDrawingContext* m_context = nullptr;
|
||||
GSVector4i temp_draw_rect = {};
|
||||
u32 m_crc = 0;
|
||||
CRC::Game m_game = {};
|
||||
std::unique_ptr<GSDumpBase> m_dump;
|
||||
bool m_scissor_invalid = false;
|
||||
bool m_nativeres = false;
|
||||
@@ -389,7 +386,6 @@ public:
|
||||
bool TestDrawChanged();
|
||||
void FlushWrite();
|
||||
virtual void Draw() = 0;
|
||||
virtual void PurgePool();
|
||||
virtual void PurgeTextureCache();
|
||||
virtual void ReadbackTextureCache();
|
||||
virtual void InvalidateVideoMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r) {}
|
||||
@@ -397,6 +393,8 @@ public:
|
||||
|
||||
virtual void Move();
|
||||
|
||||
GSVector4i GetTEX0Rect();
|
||||
void CheckWriteOverlap(bool req_write, bool req_read);
|
||||
void Write(const u8* mem, int len);
|
||||
void Read(u8* mem, int len);
|
||||
void InitReadFIFO(u8* mem, int len);
|
||||
@@ -409,10 +407,6 @@ public:
|
||||
int Freeze(freezeData* fd, bool sizeonly);
|
||||
int Defrost(const freezeData* fd);
|
||||
|
||||
u32 GetGameCRC() const { return m_crc; }
|
||||
virtual void SetGameCRC(u32 crc);
|
||||
virtual void UpdateCRCHacks();
|
||||
|
||||
u8* GetRegsMem() const { return reinterpret_cast<u8*>(m_regs); }
|
||||
void SetRegsMem(u8* basemem) { m_regs = reinterpret_cast<GSPrivRegSet*>(basemem); }
|
||||
|
||||
|
||||
@@ -87,6 +87,15 @@ void GSRenderer::Destroy()
|
||||
GSCapture::EndCapture();
|
||||
}
|
||||
|
||||
void GSRenderer::PurgePool()
|
||||
{
|
||||
g_gs_device->PurgePool();
|
||||
}
|
||||
|
||||
void GSRenderer::UpdateRenderFixes()
|
||||
{
|
||||
}
|
||||
|
||||
bool GSRenderer::Merge(int field)
|
||||
{
|
||||
GSVector2i fs(0, 0);
|
||||
@@ -659,6 +668,9 @@ void GSRenderer::VSync(u32 field, bool registers_written, bool idle_frame)
|
||||
|
||||
if (!m_dump && m_dump_frames > 0)
|
||||
{
|
||||
if (GSConfig.UserHacks_ReadTCOnClose)
|
||||
ReadbackTextureCache();
|
||||
|
||||
freezeData fd = {0, nullptr};
|
||||
Freeze(&fd, true);
|
||||
fd.data = new u8[fd.size];
|
||||
@@ -673,26 +685,23 @@ void GSRenderer::VSync(u32 field, bool registers_written, bool idle_frame)
|
||||
std::string_view compression_str;
|
||||
if (GSConfig.GSDumpCompression == GSDumpCompressionMethod::Uncompressed)
|
||||
{
|
||||
m_dump = std::unique_ptr<GSDumpBase>(new GSDumpUncompressed(m_snapshot, VMManager::GetDiscSerial(), m_crc,
|
||||
screenshot_width, screenshot_height,
|
||||
screenshot_pixels.empty() ? nullptr : screenshot_pixels.data(),
|
||||
fd, m_regs));
|
||||
m_dump = std::unique_ptr<GSDumpBase>(new GSDumpUncompressed(m_snapshot, VMManager::GetDiscSerial(),
|
||||
VMManager::GetDiscCRC(), screenshot_width, screenshot_height,
|
||||
screenshot_pixels.empty() ? nullptr : screenshot_pixels.data(), fd, m_regs));
|
||||
compression_str = "with no compression";
|
||||
}
|
||||
else if (GSConfig.GSDumpCompression == GSDumpCompressionMethod::LZMA)
|
||||
{
|
||||
m_dump = std::unique_ptr<GSDumpBase>(new GSDumpXz(m_snapshot, VMManager::GetDiscSerial(), m_crc,
|
||||
screenshot_width, screenshot_height,
|
||||
screenshot_pixels.empty() ? nullptr : screenshot_pixels.data(),
|
||||
fd, m_regs));
|
||||
m_dump = std::unique_ptr<GSDumpBase>(
|
||||
new GSDumpXz(m_snapshot, VMManager::GetDiscSerial(), VMManager::GetDiscCRC(), screenshot_width,
|
||||
screenshot_height, screenshot_pixels.empty() ? nullptr : screenshot_pixels.data(), fd, m_regs));
|
||||
compression_str = "with LZMA compression";
|
||||
}
|
||||
else
|
||||
{
|
||||
m_dump = std::unique_ptr<GSDumpBase>(new GSDumpZst(m_snapshot, VMManager::GetDiscSerial(), m_crc,
|
||||
screenshot_width, screenshot_height,
|
||||
screenshot_pixels.empty() ? nullptr : screenshot_pixels.data(),
|
||||
fd, m_regs));
|
||||
m_dump = std::unique_ptr<GSDumpBase>(
|
||||
new GSDumpZst(m_snapshot, VMManager::GetDiscSerial(), VMManager::GetDiscCRC(), screenshot_width,
|
||||
screenshot_height, screenshot_pixels.empty() ? nullptr : screenshot_pixels.data(), fd, m_regs));
|
||||
compression_str = "with Zstandard compression";
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,10 @@ public:
|
||||
|
||||
virtual void Destroy();
|
||||
|
||||
virtual void UpdateRenderFixes();
|
||||
|
||||
void PurgePool();
|
||||
|
||||
virtual void VSync(u32 field, bool registers_written, bool idle_frame);
|
||||
virtual bool CanUpscale() { return false; }
|
||||
virtual float GetUpscaleMultiplier() { return 1.0f; }
|
||||
|
||||
@@ -58,7 +58,7 @@ public:
|
||||
Invalidated
|
||||
};
|
||||
|
||||
union alignas(16) ClearValue
|
||||
union ClearValue
|
||||
{
|
||||
u32 color;
|
||||
float depth;
|
||||
|
||||
@@ -195,6 +195,36 @@ bool GSHwHack::GSC_Tekken5(GSRendererHW& r, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
{
|
||||
if (r.IsPossibleChannelShuffle())
|
||||
{
|
||||
pxAssertMsg((RTBP0 & 31) == 0, "TEX0 should be page aligned");
|
||||
|
||||
GSTextureCache::Target* rt = g_texture_cache->LookupTarget(GIFRegTEX0::Create(RTBP0, RFBW, RFPSM),
|
||||
GSVector2i(1, 1), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget);
|
||||
if (!rt)
|
||||
return false;
|
||||
|
||||
GL_INS("GSC_Tekken5(): HLE channel shuffle");
|
||||
|
||||
// have to set up the palette ourselves too, since GSC executes before it does
|
||||
r.m_mem.m_clut.Read32(RTEX0, r.m_draw_env->TEXA);
|
||||
std::shared_ptr<GSTextureCache::Palette> palette =
|
||||
g_texture_cache->LookupPaletteObject(r.m_mem.m_clut, GSLocalMemory::m_psm[RTEX0.PSM].pal, true);
|
||||
if (!palette)
|
||||
return false;
|
||||
|
||||
GSHWDrawConfig& conf = r.BeginHLEHardwareDraw(
|
||||
rt->GetTexture(), nullptr, rt->GetScale(), rt->GetTexture(), rt->GetScale(), rt->GetUnscaledRect());
|
||||
conf.pal = palette->GetPaletteGSTexture();
|
||||
conf.ps.channel = ChannelFetch_RGB;
|
||||
conf.colormask.wa = false;
|
||||
r.EndHLEHardwareDraw(false);
|
||||
|
||||
// 12 pages: 2 calls by channel, 3 channels, 1 blit
|
||||
skip = 12 * (3 + 3 + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!s_nativeres && RTME && (RFBP == 0x02d60 || RFBP == 0x02d80 || RFBP == 0x02ea0 || RFBP == 0x03620 || RFBP == 0x03640) && RFPSM == RTPSM && RTBP0 == 0x00000 && RTPSM == PSMCT32)
|
||||
{
|
||||
// Don't enable hack on native res if crc is below aggressive.
|
||||
@@ -493,19 +523,6 @@ bool GSHwHack::GSC_SakuraWarsSoLongMyLove(GSRendererHW& r, int& skip)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSHwHack::GSC_GodHand(GSRendererHW& r, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
{
|
||||
if (RTME && (RFBP == 0x0) && (RTBP0 == 0x2800) && RFPSM == RTPSM && RTPSM == PSMCT32)
|
||||
{
|
||||
skip = 1; // Blur
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSHwHack::GSC_KnightsOfTheTemple2(GSRendererHW& r, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
@@ -1408,7 +1425,6 @@ bool GSHwHack::MV_Ico(GSRendererHW& r)
|
||||
#define CRC_F(name) { #name, &GSHwHack::name }
|
||||
|
||||
const GSHwHack::Entry<GSRendererHW::GSC_Ptr> GSHwHack::s_get_skip_count_functions[] = {
|
||||
CRC_F(GSC_GodHand),
|
||||
CRC_F(GSC_KnightsOfTheTemple2),
|
||||
CRC_F(GSC_Kunoichi),
|
||||
CRC_F(GSC_Manhunt2),
|
||||
@@ -1502,9 +1518,9 @@ s16 GSLookupMoveHandlerFunctionId(const std::string_view& name)
|
||||
return -1;
|
||||
}
|
||||
|
||||
void GSRendererHW::UpdateCRCHacks()
|
||||
void GSRendererHW::UpdateRenderFixes()
|
||||
{
|
||||
GSRenderer::UpdateCRCHacks();
|
||||
GSRenderer::UpdateRenderFixes();
|
||||
|
||||
m_nativeres = (GSConfig.UpscaleMultiplier == 1.0f);
|
||||
s_nativeres = m_nativeres;
|
||||
|
||||
@@ -32,7 +32,6 @@ public:
|
||||
static bool GSC_Kunoichi(GSRendererHW& r, int& skip);
|
||||
static bool GSC_ZettaiZetsumeiToshi2(GSRendererHW& r, int& skip);
|
||||
static bool GSC_SakuraWarsSoLongMyLove(GSRendererHW& r, int& skip);
|
||||
static bool GSC_GodHand(GSRendererHW& r, int& skip);
|
||||
static bool GSC_KnightsOfTheTemple2(GSRendererHW& r, int& skip);
|
||||
static bool GSC_UltramanFightingEvolution(GSRendererHW& r, int& skip);
|
||||
static bool GSC_TalesofSymphonia(GSRendererHW& r, int& skip);
|
||||
|
||||
@@ -76,13 +76,6 @@ GSTexture* GSRendererHW::LookupPaletteSource(u32 CBP, u32 CPSM, u32 CBW, GSVecto
|
||||
return g_texture_cache->LookupPaletteSource(CBP, CPSM, CBW, offset, scale, size);
|
||||
}
|
||||
|
||||
void GSRendererHW::SetGameCRC(u32 crc)
|
||||
{
|
||||
GSRenderer::SetGameCRC(crc);
|
||||
|
||||
GSTextureReplacements::GameChanged();
|
||||
}
|
||||
|
||||
bool GSRendererHW::CanUpscale()
|
||||
{
|
||||
return GSConfig.UpscaleMultiplier != 1.0f;
|
||||
@@ -380,7 +373,7 @@ void GSRendererHW::ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba)
|
||||
// We can't use the draw rect exactly here, because if the target was actually larger
|
||||
// for some reason... unhandled clears, maybe, it won't have been halved correctly.
|
||||
// So, halve it ourselves.
|
||||
const GSVector4i dr = GetSplitTextureShuffleDrawRect();
|
||||
const GSVector4i dr = m_r;
|
||||
const GSVector4i r = dr.blend32<9>(dr.sra32(1));
|
||||
GL_CACHE("ConvertSpriteTextureShuffle: Rewrite from %d,%d => %d,%d to %d,%d => %d,%d",
|
||||
static_cast<int>(m_vt.m_min.p.x), static_cast<int>(m_vt.m_min.p.y), static_cast<int>(m_vt.m_min.p.z),
|
||||
@@ -793,12 +786,8 @@ bool GSRendererHW::IsPossibleChannelShuffle() const
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GSRendererHW::IsSplitTextureShuffle()
|
||||
bool GSRendererHW::NextDrawMatchesShuffle() const
|
||||
{
|
||||
// For this to work, we're peeking into the next draw, therefore we need dirty registers.
|
||||
if (m_dirty_gs_regs == 0)
|
||||
return false;
|
||||
|
||||
// Make sure nothing unexpected has changed.
|
||||
// Twinsanity seems to screw with ZBUF here despite it being irrelevant.
|
||||
const GSDrawingContext& next_ctx = m_env.CTXT[m_backed_up_ctx];
|
||||
@@ -812,13 +801,24 @@ bool GSRendererHW::IsSplitTextureShuffle()
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSRendererHW::IsSplitTextureShuffle(u32 rt_tbw)
|
||||
{
|
||||
// For this to work, we're peeking into the next draw, therefore we need dirty registers.
|
||||
if (m_dirty_gs_regs == 0)
|
||||
return false;
|
||||
|
||||
if (!NextDrawMatchesShuffle())
|
||||
return false;
|
||||
|
||||
// Different channel being shuffled, so needs to be handled separately (misdetection in 50 Cent)
|
||||
if (m_vertex.buff[m_index.buff[0]].U != m_v.U)
|
||||
return false;
|
||||
|
||||
// Check that both the position and texture coordinates are page aligned, so we can work in pages instead of coordinates.
|
||||
// For texture shuffles, the U will be offset by 8.
|
||||
const GSLocalMemory::psm_t& frame_psm = GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM];
|
||||
|
||||
const GSVector4i pos_rc = GSVector4i(m_vt.m_min.p.upld(m_vt.m_max.p + GSVector4::cxpr(0.5f)));
|
||||
const GSVector4i tex_rc = GSVector4i(m_vt.m_min.t.upld(m_vt.m_max.t));
|
||||
@@ -830,6 +830,8 @@ bool GSRendererHW::IsSplitTextureShuffle()
|
||||
// X might be offset by up to -8/+8, but either the position or UV should be aligned.
|
||||
GSVector4i aligned_rc = pos_rc.min_i32(tex_rc).blend32<12>(pos_rc.max_i32(tex_rc));
|
||||
|
||||
const GSLocalMemory::psm_t& frame_psm = GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM];
|
||||
const GSDrawingContext& next_ctx = m_env.CTXT[m_backed_up_ctx];
|
||||
// Y should be page aligned. X should be too, but if it's doing a copy with a shuffle (which is kinda silly), both the
|
||||
// position and coordinates may be offset by +8. See Psi-Ops - The Mindgate Conspiracy.
|
||||
if ((aligned_rc.x & 7) != 0 || aligned_rc.x > 8 || (aligned_rc.z & 7) != 0 ||
|
||||
@@ -843,7 +845,7 @@ bool GSRendererHW::IsSplitTextureShuffle()
|
||||
|
||||
// We should have the same number of pages in both the position and UV.
|
||||
const u32 pages_high = static_cast<u32>(aligned_rc.height()) / frame_psm.pgs.y;
|
||||
const u32 num_pages = m_cached_ctx.FRAME.FBW * pages_high;
|
||||
const u32 num_pages = m_context->FRAME.FBW * pages_high;
|
||||
|
||||
// If this is a split texture shuffle, the next draw's FRAME/TEX0 should line up.
|
||||
// Re-add the offset we subtracted in Draw() to get the original FBP/TBP0.. this won't handle wrapping. Oh well.
|
||||
@@ -873,10 +875,19 @@ bool GSRendererHW::IsSplitTextureShuffle()
|
||||
{
|
||||
m_split_texture_shuffle_start_FBP = m_cached_ctx.FRAME.FBP;
|
||||
m_split_texture_shuffle_start_TBP = m_cached_ctx.TEX0.TBP0;
|
||||
|
||||
// If the game has changed the texture width to 1 we need to retanslate it to whatever the rt has so the final rect is correct.
|
||||
if (m_cached_ctx.FRAME.FBW == 1)
|
||||
m_split_texture_shuffle_fbw = rt_tbw;
|
||||
else
|
||||
m_split_texture_shuffle_fbw = m_cached_ctx.FRAME.FBW;
|
||||
}
|
||||
|
||||
if ((m_split_texture_shuffle_pages % m_split_texture_shuffle_fbw) == 0)
|
||||
m_split_texture_shuffle_pages_high += pages_high;
|
||||
|
||||
m_split_texture_shuffle_pages += num_pages;
|
||||
m_split_texture_shuffle_pages_high += pages_high;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -888,7 +899,6 @@ GSVector4i GSRendererHW::GetSplitTextureShuffleDrawRect() const
|
||||
// Some games (e.g. Crash Twinsanity) adjust both FBP and TBP0, so the rectangle will be half the size
|
||||
// of the actual shuffle. Others leave the FBP alone, but only adjust TBP0, and offset the draw rectangle
|
||||
// to the second half of the fb. In which case, the rectangle bounds will be correct.
|
||||
|
||||
if (m_context->FRAME.FBP != m_split_texture_shuffle_start_FBP)
|
||||
{
|
||||
const int pages_high = (r.height() + frame_psm.pgs.y - 1) / frame_psm.pgs.y;
|
||||
@@ -1139,7 +1149,7 @@ bool GSRendererHW::IsTBPFrameOrZ(u32 tbp) const
|
||||
|
||||
const u32 max_z = (0xFFFFFFFF >> (GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].fmt * 8));
|
||||
const bool no_rt = (m_context->ALPHA.IsCd() && PRIM->ABE && (m_cached_ctx.FRAME.PSM == 1))
|
||||
|| (!m_cached_ctx.TEST.DATE && (m_cached_ctx.FRAME.FBMSK & GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].fmsk) == GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].fmsk);
|
||||
|| (!m_cached_ctx.TEST.DATE && (fm & GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].fmsk) == GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].fmsk);
|
||||
const bool no_ds = (
|
||||
// Depth is always pass/fail (no read) and write are discarded.
|
||||
(zm != 0 && m_cached_ctx.TEST.ZTST <= ZTST_ALWAYS) ||
|
||||
@@ -1228,6 +1238,9 @@ void GSRendererHW::Move()
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_env.TRXDIR.XDIR == 3)
|
||||
return;
|
||||
|
||||
const int sx = m_env.TRXPOS.SSAX;
|
||||
const int sy = m_env.TRXPOS.SSAY;
|
||||
const int dx = m_env.TRXPOS.DSAX;
|
||||
@@ -1239,6 +1252,7 @@ void GSRendererHW::Move()
|
||||
if (g_texture_cache->Move(m_env.BITBLTBUF.SBP, m_env.BITBLTBUF.SBW, m_env.BITBLTBUF.SPSM, sx, sy,
|
||||
m_env.BITBLTBUF.DBP, m_env.BITBLTBUF.DBW, m_env.BITBLTBUF.DPSM, dx, dy, w, h))
|
||||
{
|
||||
m_env.TRXDIR.XDIR = 3;
|
||||
// Handled entirely in TC, no need to update local memory.
|
||||
return;
|
||||
}
|
||||
@@ -1768,7 +1782,7 @@ void GSRendererHW::Draw()
|
||||
// Note: FF DoC has both buffer at same location but disable the depth test (write?) with ZTE = 0
|
||||
const u32 max_z = (0xFFFFFFFF >> (GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].fmt * 8));
|
||||
bool no_rt = (context->ALPHA.IsCd() && PRIM->ABE && (m_cached_ctx.FRAME.PSM == 1))
|
||||
|| (!m_cached_ctx.TEST.DATE && (m_cached_ctx.FRAME.FBMSK & GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].fmsk) == GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].fmsk);
|
||||
|| (!m_cached_ctx.TEST.DATE && (fm & GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].fmsk) == GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].fmsk);
|
||||
const bool all_depth_tests_pass =
|
||||
// Depth is always pass/fail (no read) and write are discarded.
|
||||
(!m_cached_ctx.TEST.ZTE || m_cached_ctx.TEST.ZTST <= ZTST_ALWAYS) ||
|
||||
@@ -1835,8 +1849,10 @@ void GSRendererHW::Draw()
|
||||
const bool tex_is_rt = (process_texture && m_cached_ctx.TEX0.TBP0 >= m_cached_ctx.FRAME.Block() &&
|
||||
m_cached_ctx.TEX0.TBP0 < frame_end_bp);
|
||||
const bool not_writing_to_all = (!PrimitiveCoversWithoutGaps() || !all_depth_tests_pass);
|
||||
const bool preserve_rt_rgb = (!no_rt && (!IsDiscardingDstRGB() || not_writing_to_all));
|
||||
const bool preserve_rt_alpha = (!no_rt && (!IsDiscardingDstAlpha() || not_writing_to_all));
|
||||
const bool preserve_rt_rgb = (!no_rt && (!IsDiscardingDstRGB() || not_writing_to_all || tex_is_rt));
|
||||
const bool preserve_rt_alpha =
|
||||
(!no_rt && (!IsDiscardingDstAlpha() || not_writing_to_all ||
|
||||
(tex_is_rt && GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].trbpp != 24)));
|
||||
bool preserve_rt_color = preserve_rt_rgb || preserve_rt_alpha;
|
||||
bool preserve_depth =
|
||||
preserve_rt_color || (!no_ds && (!all_depth_tests_pass || !m_cached_ctx.DepthWrite() || m_cached_ctx.TEST.ATE));
|
||||
@@ -1907,7 +1923,41 @@ void GSRendererHW::Draw()
|
||||
|
||||
// Fudge FRAME and TEX0 to point to the start of the shuffle.
|
||||
m_cached_ctx.TEX0.TBP0 = m_split_texture_shuffle_start_TBP;
|
||||
SetNewFRAME(m_split_texture_shuffle_start_FBP << 5, m_cached_ctx.FRAME.FBW, m_cached_ctx.FRAME.PSM);
|
||||
|
||||
// We correct this again at the end of the split
|
||||
SetNewFRAME(m_split_texture_shuffle_start_FBP << 5, m_context->FRAME.FBW, m_cached_ctx.FRAME.PSM);
|
||||
|
||||
// TEX0 may also be just using single width with offsets also, so let's deal with that.
|
||||
if (m_split_texture_shuffle_pages > 1 && !NextDrawMatchesShuffle())
|
||||
{
|
||||
if (m_context->FRAME.FBW != m_split_texture_shuffle_fbw && m_cached_ctx.TEX0.TBW == 1)
|
||||
{
|
||||
const GSLocalMemory::psm_t& frame_psm = GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM];
|
||||
const GSLocalMemory::psm_t& tex_psm = GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM];
|
||||
// This is the final draw of the shuffle, so let's fudge the numbers
|
||||
// Need to update the final rect as it could be wrong.
|
||||
if (m_context->FRAME.FBW == 1 && m_split_texture_shuffle_fbw != m_context->FRAME.FBW)
|
||||
{
|
||||
m_r.x = 0; // Need to keep the X offset to calculate the shuffle.
|
||||
m_r.z = m_split_texture_shuffle_fbw * frame_psm.pgs.x;
|
||||
m_r.y = 0;
|
||||
m_r.w = std::min(1024U, m_split_texture_shuffle_pages_high * frame_psm.pgs.y); // Max we can shuffle is 1024 (512)
|
||||
|
||||
//Fudge the scissor and frame
|
||||
m_context->scissor.in = m_r;
|
||||
|
||||
SetNewFRAME(m_split_texture_shuffle_start_FBP << 5, m_split_texture_shuffle_fbw, m_cached_ctx.FRAME.PSM);
|
||||
}
|
||||
|
||||
const int pages = m_split_texture_shuffle_pages + 1;
|
||||
const int width = m_split_texture_shuffle_fbw;
|
||||
const int height = (pages >= width) ? (pages / width) : 1;
|
||||
// We must update the texture size! It will likely be 64x64, which is no good, so let's fudge that.
|
||||
m_cached_ctx.TEX0.TW = std::ceil(std::log2(std::min(1024, width * tex_psm.pgs.x)));
|
||||
m_cached_ctx.TEX0.TH = std::ceil(std::log2(std::min(1024, height * tex_psm.pgs.y)));
|
||||
m_cached_ctx.TEX0.TBW = m_split_texture_shuffle_fbw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto cleanup_draw = [&]() {
|
||||
@@ -2118,8 +2168,8 @@ void GSRendererHW::Draw()
|
||||
MIP_CLAMP.MAXV + 1);
|
||||
}
|
||||
const bool possible_shuffle = m_cached_ctx.FRAME.Block() == m_cached_ctx.TEX0.TBP0 || IsPossibleChannelShuffle();
|
||||
src = tex_psm.depth ? g_texture_cache->LookupDepthSource(TEX0, env.TEXA, MIP_CLAMP, tmm.coverage, possible_shuffle) :
|
||||
g_texture_cache->LookupSource(TEX0, env.TEXA, MIP_CLAMP, tmm.coverage, (GSConfig.HWMipmap >= HWMipmapLevel::Basic || GSConfig.TriFilter == TriFiltering::Forced) ? &hash_lod_range : nullptr, possible_shuffle);
|
||||
src = tex_psm.depth ? g_texture_cache->LookupDepthSource(TEX0, env.TEXA, MIP_CLAMP, tmm.coverage, possible_shuffle, m_vt.IsLinear()) :
|
||||
g_texture_cache->LookupSource(TEX0, env.TEXA, MIP_CLAMP, tmm.coverage, (GSConfig.HWMipmap >= HWMipmapLevel::Basic || GSConfig.TriFilter == TriFiltering::Forced) ? &hash_lod_range : nullptr, possible_shuffle, m_vt.IsLinear());
|
||||
if (unlikely(!src))
|
||||
{
|
||||
GL_INS("ERROR: Source lookup failed, skipping.");
|
||||
@@ -2127,8 +2177,32 @@ void GSRendererHW::Draw()
|
||||
return;
|
||||
}
|
||||
|
||||
if(GSLocalMemory::m_psm[src->m_TEX0.PSM].pal == 0)
|
||||
// We don't know the alpha range of direct sources when we first tried to optimize the alpha test.
|
||||
// Moving the texture lookup before the ATST optimization complicates things a lot, so instead,
|
||||
// recompute it, and everything derived from it again if it changes.
|
||||
if (GSLocalMemory::m_psm[src->m_TEX0.PSM].pal == 0)
|
||||
{
|
||||
CalcAlphaMinMax(src->m_alpha_minmax.first, src->m_alpha_minmax.second);
|
||||
|
||||
u32 new_fm = m_context->FRAME.FBMSK;
|
||||
u32 new_zm = m_context->ZBUF.ZMSK || m_context->TEST.ZTE == 0 ? 0xffffffff : 0;
|
||||
if (m_cached_ctx.TEST.ATE && GSRenderer::TryAlphaTest(new_fm, fm_mask, new_zm))
|
||||
{
|
||||
m_cached_ctx.TEST.ATE = false;
|
||||
m_cached_ctx.FRAME.FBMSK = new_fm;
|
||||
m_cached_ctx.ZBUF.ZMSK = (new_zm != 0);
|
||||
fm = new_fm;
|
||||
zm = new_zm;
|
||||
no_rt = no_rt || (!m_cached_ctx.TEST.DATE && (fm & GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].fmsk) == GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].fmsk);
|
||||
no_ds = no_ds || (zm != 0 && all_depth_tests_pass);
|
||||
if (no_rt && no_ds)
|
||||
{
|
||||
GL_INS("Late draw cancel because no pixels pass alpha test.");
|
||||
cleanup_cancelled_draw();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Estimate size based on the scissor rectangle and height cache.
|
||||
@@ -2137,7 +2211,9 @@ void GSRendererHW::Draw()
|
||||
|
||||
// Ensure draw rect is clamped to framebuffer size. Necessary for updating valid area.
|
||||
const GSVector4i unclamped_draw_rect = m_r;
|
||||
m_r = m_r.rintersect(t_size_rect);
|
||||
// Don't clamp on shuffle, the height cache may troll us with the REAL height.
|
||||
if (!m_texture_shuffle && m_split_texture_shuffle_pages == 0)
|
||||
m_r = m_r.rintersect(t_size_rect);
|
||||
|
||||
float target_scale = GetTextureScaleFactor();
|
||||
|
||||
@@ -2257,7 +2333,7 @@ void GSRendererHW::Draw()
|
||||
GL_INS("WARNING: Possible misdetection of effect, texture shuffle is %s", m_texture_shuffle ? "Enabled" : "Disabled");
|
||||
}
|
||||
|
||||
if (m_texture_shuffle && IsSplitTextureShuffle())
|
||||
if (m_texture_shuffle && IsSplitTextureShuffle(rt->m_TEX0.TBW))
|
||||
{
|
||||
// If TEX0 == FBP, we're going to have a source left in the TC.
|
||||
// That source will get used in the actual draw unsafely, so kick it out.
|
||||
@@ -2510,7 +2586,7 @@ void GSRendererHW::Draw()
|
||||
{
|
||||
const int copy_width = (old_ds->m_texture->GetWidth()) > (ds->m_texture->GetWidth()) ? (ds->m_texture->GetWidth()) : old_ds->m_texture->GetWidth();
|
||||
const int copy_height = (old_ds->m_texture->GetHeight()) > (ds->m_texture->GetHeight() - old_height) ? (ds->m_texture->GetHeight() - old_height) : old_ds->m_texture->GetHeight();
|
||||
GL_INS("DS double buffer copy from FBP 0x%x, %dx%d => %d,%d", old_rt->m_TEX0.TBP0, copy_width, copy_height, 0, old_height);
|
||||
GL_INS("DS double buffer copy from FBP 0x%x, %dx%d => %d,%d", old_ds->m_TEX0.TBP0, copy_width, copy_height, 0, old_height);
|
||||
|
||||
g_gs_device->CopyRect(old_ds->m_texture, ds->m_texture, GSVector4i(0, 0, copy_width, copy_height), 0, old_height);
|
||||
preserve_depth = true;
|
||||
@@ -3135,37 +3211,7 @@ bool GSRendererHW::TestChannelShuffle(GSTextureCache::Target* src)
|
||||
|
||||
__ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool test_only)
|
||||
{
|
||||
// First let's check we really have a channel shuffle effect
|
||||
if (m_game.title == CRC::Tekken5)
|
||||
{
|
||||
if (m_cached_ctx.FRAME.FBW == 1)
|
||||
{
|
||||
// Used in stages: Secret Garden, Acid Rain, Moonlit Wilderness
|
||||
GL_INS("Tekken5 RGB Channel");
|
||||
if (test_only)
|
||||
{
|
||||
pxAssertMsg((m_context->TEX0.TBP0 & 31) == 0, "TEX0 should be page aligned");
|
||||
m_cached_ctx.FRAME.FBP = m_context->TEX0.TBP0 >> 5;
|
||||
return true;
|
||||
}
|
||||
|
||||
m_conf.ps.channel = ChannelFetch_RGB;
|
||||
m_cached_ctx.FRAME.FBMSK = 0xFF000000;
|
||||
// 12 pages: 2 calls by channel, 3 channels, 1 blit
|
||||
// Minus current draw call
|
||||
m_skip = 12 * (3 + 3 + 1) - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Could skip model drawing if wrongly detected
|
||||
if (test_only)
|
||||
return false;
|
||||
|
||||
m_channel_shuffle = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if ((src->m_texture->GetType() == GSTexture::Type::DepthStencil) && !src->m_32_bits_fmt)
|
||||
if ((src->m_texture->GetType() == GSTexture::Type::DepthStencil) && !src->m_32_bits_fmt)
|
||||
{
|
||||
// So far 2 games hit this code path. Urban Chaos and Tales of Abyss
|
||||
// UC: will copy depth to green channel
|
||||
@@ -4783,6 +4829,12 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
|
||||
blend_alpha_min = std::min(blend_alpha_min, rt->m_alpha_min);
|
||||
blend_alpha_max = std::max(blend_alpha_max, rt->m_alpha_max);
|
||||
}
|
||||
|
||||
if (!rt->m_32_bits_fmt)
|
||||
{
|
||||
rt->m_alpha_max &= 128;
|
||||
rt->m_alpha_min &= 128;
|
||||
}
|
||||
}
|
||||
|
||||
// Not gonna spend too much time with this, it's not likely to be used much, can't be less accurate than it was.
|
||||
@@ -4791,6 +4843,12 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
|
||||
ds->m_alpha_max = std::max(ds->m_alpha_max, static_cast<int>(m_vt.m_max.p.z) >> 24);
|
||||
ds->m_alpha_min = std::min(ds->m_alpha_min, static_cast<int>(m_vt.m_min.p.z) >> 24);
|
||||
GL_INS("New DS Alpha Range: %d-%d", ds->m_alpha_min, ds->m_alpha_max);
|
||||
|
||||
if (GSLocalMemory::m_psm[ds->m_TEX0.PSM].bpp == 16)
|
||||
{
|
||||
ds->m_alpha_max &= 128;
|
||||
ds->m_alpha_min &= 128;
|
||||
}
|
||||
}
|
||||
|
||||
bool blending_alpha_pass = false;
|
||||
@@ -5581,6 +5639,12 @@ bool GSRendererHW::TryTargetClear(GSTextureCache::Target* rt, GSTextureCache::Ta
|
||||
g_gs_device->ClearRenderTarget(rt->m_texture, c);
|
||||
rt->m_alpha_max = c >> 24;
|
||||
rt->m_alpha_min = c >> 24;
|
||||
|
||||
if (!rt->m_32_bits_fmt)
|
||||
{
|
||||
rt->m_alpha_max &= 128;
|
||||
rt->m_alpha_min &= 128;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -5599,6 +5663,12 @@ bool GSRendererHW::TryTargetClear(GSTextureCache::Target* rt, GSTextureCache::Ta
|
||||
g_gs_device->ClearDepth(ds->m_texture, d);
|
||||
ds->m_alpha_max = z >> 24;
|
||||
ds->m_alpha_min = z >> 24;
|
||||
|
||||
if (GSLocalMemory::m_psm[ds->m_TEX0.PSM].bpp == 16)
|
||||
{
|
||||
ds->m_alpha_max &= 128;
|
||||
ds->m_alpha_min &= 128;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -5814,16 +5884,17 @@ bool GSRendererHW::OI_BlitFMV(GSTextureCache::Target* _rt, GSTextureCache::Sourc
|
||||
|
||||
bool GSRendererHW::IsDiscardingDstColor()
|
||||
{
|
||||
return (
|
||||
(!PRIM->ABE || IsOpaque() || m_context->ALPHA.IsBlack()) && // no blending or writing black
|
||||
!m_cached_ctx.TEST.ATE && // not testing alpha (might discard some pixels)
|
||||
!m_cached_ctx.TEST.DATE && // not reading alpha
|
||||
return ((!PRIM->ABE || IsOpaque() || m_context->ALPHA.IsBlack()) && // no blending or writing black
|
||||
!(m_draw_env->SCANMSK.MSK & 2) && // not skipping rows
|
||||
!m_cached_ctx.TEST.ATE && // not testing alpha (might discard some pixels)
|
||||
!m_cached_ctx.TEST.DATE && // not reading alpha
|
||||
(m_cached_ctx.FRAME.FBMSK & GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].fmsk) == 0); // no channels masked
|
||||
}
|
||||
|
||||
bool GSRendererHW::IsDiscardingDstRGB()
|
||||
{
|
||||
return ((!PRIM->ABE || IsOpaque() || m_context->ALPHA.IsBlack()) && // no blending or writing black
|
||||
!(m_draw_env->SCANMSK.MSK & 2) && // not skipping rows
|
||||
!m_cached_ctx.TEST.ATE && // not testing alpha (might discard some pixels)
|
||||
!m_cached_ctx.TEST.DATE && // not reading alpha
|
||||
((m_cached_ctx.FRAME.FBMSK & GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].fmsk) & 0xFFFFFFu) ==
|
||||
@@ -5833,6 +5904,7 @@ bool GSRendererHW::IsDiscardingDstRGB()
|
||||
bool GSRendererHW::IsDiscardingDstAlpha()
|
||||
{
|
||||
return ((!PRIM->ABE || m_context->ALPHA.C != 1) && // not using Ad
|
||||
!(m_draw_env->SCANMSK.MSK & 2) && // not skipping rows
|
||||
!m_cached_ctx.TEST.ATE && // not testing alpha (might discard some pixels)
|
||||
!m_cached_ctx.TEST.DATE && // not reading alpha
|
||||
((m_cached_ctx.FRAME.FBMSK & GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].fmsk) & 0xFF000000u) ==
|
||||
|
||||
@@ -104,7 +104,8 @@ private:
|
||||
void SetTCOffset();
|
||||
|
||||
bool IsPossibleChannelShuffle() const;
|
||||
bool IsSplitTextureShuffle();
|
||||
bool NextDrawMatchesShuffle() const;
|
||||
bool IsSplitTextureShuffle(u32 rt_tbw);
|
||||
GSVector4i GetSplitTextureShuffleDrawRect() const;
|
||||
u32 GetEffectiveTextureShuffleFbmsk() const;
|
||||
|
||||
@@ -155,6 +156,7 @@ private:
|
||||
u32 m_split_texture_shuffle_pages_high = 0;
|
||||
u32 m_split_texture_shuffle_start_FBP = 0;
|
||||
u32 m_split_texture_shuffle_start_TBP = 0;
|
||||
u32 m_split_texture_shuffle_fbw = 0;
|
||||
|
||||
u32 m_last_channel_shuffle_fbmsk = 0;
|
||||
|
||||
@@ -185,8 +187,7 @@ public:
|
||||
|
||||
void Destroy() override;
|
||||
|
||||
void SetGameCRC(u32 crc) override;
|
||||
void UpdateCRCHacks() override;
|
||||
void UpdateRenderFixes() override;
|
||||
|
||||
bool CanUpscale() override;
|
||||
float GetUpscaleMultiplier() override;
|
||||
|
||||
@@ -586,7 +586,7 @@ __ri static GSTextureCache::Source* FindSourceInMap(const GIFRegTEX0& TEX0, cons
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GSTextureCache::Source* GSTextureCache::LookupDepthSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, const bool possible_shuffle, bool palette)
|
||||
GSTextureCache::Source* GSTextureCache::LookupDepthSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, const bool possible_shuffle, const bool linear, bool palette)
|
||||
{
|
||||
if (GSConfig.UserHacks_DisableDepthSupport)
|
||||
{
|
||||
@@ -646,12 +646,19 @@ GSTextureCache::Source* GSTextureCache::LookupDepthSource(const GIFRegTEX0& TEX0
|
||||
const GSVector2i page_size = GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs;
|
||||
const bool can_translate = CanTranslate(bp, TEX0.TBW, psm, r, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW);
|
||||
const bool swizzle_match = psm_s.depth == GSLocalMemory::m_psm[t->m_TEX0.PSM].depth;
|
||||
GSVector4i new_rect = r;
|
||||
|
||||
if (linear)
|
||||
{
|
||||
new_rect.z -= 1;
|
||||
new_rect.w -= 1;
|
||||
}
|
||||
|
||||
if (can_translate)
|
||||
{
|
||||
if (swizzle_match)
|
||||
{
|
||||
target_rc = TranslateAlignedRectByPage(t, bp, psm, TEX0.TBW, r);
|
||||
target_rc = TranslateAlignedRectByPage(t, bp, psm, TEX0.TBW, new_rect);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -669,8 +676,8 @@ GSTextureCache::Source* GSTextureCache::LookupDepthSource(const GIFRegTEX0& TEX0
|
||||
{
|
||||
target_rc.x &= ~(page_size.x - 1);
|
||||
target_rc.y &= ~(page_size.y - 1);
|
||||
target_rc.z = (r.z + (page_size.x - 1)) & ~(page_size.x - 1);
|
||||
target_rc.w = (r.w + (page_size.y - 1)) & ~(page_size.y - 1);
|
||||
target_rc.z = (new_rect.z + (page_size.x - 1)) & ~(page_size.x - 1);
|
||||
target_rc.w = (new_rect.w + (page_size.y - 1)) & ~(page_size.y - 1);
|
||||
}
|
||||
target_rc = TranslateAlignedRectByPage(t, bp & ~((1 << 5) - 1), psm, TEX0.TBW, target_rc);
|
||||
}
|
||||
@@ -681,6 +688,11 @@ GSTextureCache::Source* GSTextureCache::LookupDepthSource(const GIFRegTEX0& TEX0
|
||||
inside_target = true;
|
||||
}
|
||||
}
|
||||
if (linear)
|
||||
{
|
||||
new_rect.z += 1;
|
||||
new_rect.w += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -764,7 +776,7 @@ GSTextureCache::Source* GSTextureCache::LookupDepthSource(const GIFRegTEX0& TEX0
|
||||
else
|
||||
{
|
||||
// This is a bit of a worry, since it could load junk from local memory... but it's better than skipping the draw.
|
||||
return LookupSource(TEX0, TEXA, CLAMP, r, nullptr, possible_shuffle);
|
||||
return LookupSource(TEX0, TEXA, CLAMP, r, nullptr, possible_shuffle, linear);
|
||||
}
|
||||
|
||||
ASSERT(src->m_texture);
|
||||
@@ -773,7 +785,7 @@ GSTextureCache::Source* GSTextureCache::LookupDepthSource(const GIFRegTEX0& TEX0
|
||||
return src;
|
||||
}
|
||||
|
||||
GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, const GSVector2i* lod, const bool possible_shuffle)
|
||||
GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, const GSVector2i* lod, const bool possible_shuffle, const bool linear)
|
||||
{
|
||||
GL_CACHE("TC: Lookup Source <%d,%d => %d,%d> (0x%x, %s, BW: %u, CBP: 0x%x, TW: %d, TH: %d)", r.x, r.y, r.z, r.w, TEX0.TBP0, psm_str(TEX0.PSM), TEX0.TBW, TEX0.CBP, 1 << TEX0.TW, 1 << TEX0.TH);
|
||||
|
||||
@@ -857,6 +869,13 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
|
||||
if (rect_clean && tex_overlaps && !t->m_dirty.empty() && width_match)
|
||||
{
|
||||
GSVector4i new_rect = r;
|
||||
|
||||
if (linear)
|
||||
{
|
||||
new_rect.z -= 1;
|
||||
new_rect.w -= 1;
|
||||
}
|
||||
|
||||
bool partial = false;
|
||||
// If it's compatible and page aligned, then handle it this way.
|
||||
// It's quicker, and Surface Offsets can get it wrong.
|
||||
@@ -865,14 +884,14 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
|
||||
if (bp > t->m_TEX0.TBP0)
|
||||
{
|
||||
const GSVector2i page_size = GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs;
|
||||
const bool can_translate = CanTranslate(bp, bw, psm, r, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW);
|
||||
const bool can_translate = CanTranslate(bp, bw, psm, new_rect, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW);
|
||||
const bool swizzle_match = GSLocalMemory::m_psm[psm].depth == GSLocalMemory::m_psm[t->m_TEX0.PSM].depth;
|
||||
|
||||
if (can_translate)
|
||||
{
|
||||
if (swizzle_match)
|
||||
{
|
||||
new_rect = TranslateAlignedRectByPage(t, bp, psm, bw, r);
|
||||
new_rect = TranslateAlignedRectByPage(t, bp, psm, bw, new_rect);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -887,8 +906,8 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
|
||||
{
|
||||
new_rect.x &= ~(page_size.x - 1);
|
||||
new_rect.y &= ~(page_size.y - 1);
|
||||
new_rect.z = (r.z + (page_size.x - 1)) & ~(page_size.x - 1);
|
||||
new_rect.w = (r.w + (page_size.y - 1)) & ~(page_size.y - 1);
|
||||
new_rect.z = (new_rect.z + (page_size.x - 1)) & ~(page_size.x - 1);
|
||||
new_rect.w = (new_rect.w + (page_size.y - 1)) & ~(page_size.y - 1);
|
||||
}
|
||||
new_rect = TranslateAlignedRectByPage(t, bp & ~((1 << 5) - 1), psm, bw, new_rect);
|
||||
}
|
||||
@@ -901,7 +920,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
|
||||
sok.elems[0].bp = bp;
|
||||
sok.elems[0].bw = bw;
|
||||
sok.elems[0].psm = psm;
|
||||
sok.elems[0].rect = r;
|
||||
sok.elems[0].rect = new_rect;
|
||||
sok.elems[1].bp = t->m_TEX0.TBP0;
|
||||
sok.elems[1].bw = t->m_TEX0.TBW;
|
||||
sok.elems[1].psm = t->m_TEX0.PSM;
|
||||
@@ -941,6 +960,12 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
|
||||
// If not all channels are clean/dirty or only part of the rect is dirty, we need to update the target.
|
||||
if (((channels & channel_mask) != channel_mask || partial))
|
||||
t->Update();
|
||||
|
||||
if (linear)
|
||||
{
|
||||
new_rect.z += 1;
|
||||
new_rect.w += 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1014,23 +1039,29 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
|
||||
|
||||
if (bp > t->m_TEX0.TBP0)
|
||||
{
|
||||
GSVector4i new_rect = r;
|
||||
if (linear)
|
||||
{
|
||||
new_rect.z -= 1;
|
||||
new_rect.w -= 1;
|
||||
}
|
||||
// Check if it is possible to hit with valid <x,y> offset on the given Target.
|
||||
// Fixes Jak eyes rendering.
|
||||
// Fixes Xenosaga 3 last dungeon graphic bug.
|
||||
// Fixes Pause menu in The Getaway.
|
||||
const bool can_translate = CanTranslate(bp, bw, psm, r, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW);
|
||||
const bool can_translate = CanTranslate(bp, bw, psm, new_rect, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW);
|
||||
if (can_translate)
|
||||
{
|
||||
const bool swizzle_match = GSLocalMemory::m_psm[psm].depth == GSLocalMemory::m_psm[t->m_TEX0.PSM].depth;
|
||||
const GSVector2i& page_size = GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs;
|
||||
const GSVector4i page_mask(GSVector4i((page_size.x - 1), (page_size.y - 1)).xyxy());
|
||||
GSVector4i rect = r & ~page_mask;
|
||||
GSVector4i rect = new_rect & ~page_mask;
|
||||
|
||||
if (swizzle_match)
|
||||
{
|
||||
rect = TranslateAlignedRectByPage(t, bp, psm, bw, r);
|
||||
rect.x -= r.x;
|
||||
rect.y -= r.y;
|
||||
rect = TranslateAlignedRectByPage(t, bp, psm, bw, new_rect);
|
||||
rect.x -= new_rect.x;
|
||||
rect.y -= new_rect.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1045,12 +1076,12 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
|
||||
{
|
||||
rect.x &= ~(page_size.x - 1);
|
||||
rect.y &= ~(page_size.y - 1);
|
||||
rect.z = (r.z + (page_size.x - 1)) & ~(page_size.x - 1);
|
||||
rect.w = (r.w + (page_size.y - 1)) & ~(page_size.y - 1);
|
||||
rect.z = (new_rect.z + (page_size.x - 1)) & ~(page_size.x - 1);
|
||||
rect.w = (new_rect.w + (page_size.y - 1)) & ~(page_size.y - 1);
|
||||
}
|
||||
rect = TranslateAlignedRectByPage(t, bp & ~((1 << 5) - 1), psm, bw, rect);
|
||||
rect.x -= r.x & ~(page_size.y - 1);
|
||||
rect.y -= r.x & ~(page_size.y - 1);
|
||||
rect.x -= new_rect.x & ~(page_size.y - 1);
|
||||
rect.y -= new_rect.x & ~(page_size.y - 1);
|
||||
}
|
||||
|
||||
rect = rect.rintersect(t->m_valid);
|
||||
@@ -1085,7 +1116,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
|
||||
{
|
||||
// Improves Beyond Good & Evil shadow.
|
||||
const u32 bp_unwrap = bp + GSTextureCache::MAX_BP + 0x1;
|
||||
so = ComputeSurfaceOffset(bp_unwrap, bw, psm, r, t);
|
||||
so = ComputeSurfaceOffset(bp_unwrap, bw, psm, new_rect, t);
|
||||
}
|
||||
if (so.is_valid)
|
||||
{
|
||||
@@ -1099,6 +1130,11 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (linear)
|
||||
{
|
||||
new_rect.z += 1;
|
||||
new_rect.w += 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1172,11 +1208,11 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
|
||||
GIFRegTEX0 depth_TEX0;
|
||||
depth_TEX0.U32[0] = TEX0.U32[0] | (0x30u << 20u);
|
||||
depth_TEX0.U32[1] = TEX0.U32[1];
|
||||
return LookupDepthSource(depth_TEX0, TEXA, CLAMP, r, possible_shuffle);
|
||||
return LookupDepthSource(depth_TEX0, TEXA, CLAMP, r, possible_shuffle, linear);
|
||||
}
|
||||
else
|
||||
{
|
||||
return LookupDepthSource(TEX0, TEXA, CLAMP, r, possible_shuffle, true);
|
||||
return LookupDepthSource(TEX0, TEXA, CLAMP, r, possible_shuffle, linear, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1571,14 +1607,6 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||
if (dst)
|
||||
{
|
||||
dst->m_used |= used;
|
||||
|
||||
if (is_frame)
|
||||
{
|
||||
// TODO: Why are we doing this?!
|
||||
dst->m_valid_alpha_low = false;
|
||||
dst->m_valid_alpha_high = false;
|
||||
}
|
||||
|
||||
dst->readbacks_since_draw = 0;
|
||||
|
||||
assert(dst && dst->m_texture && dst->m_scale == scale);
|
||||
@@ -1980,6 +2008,7 @@ bool GSTextureCache::CopyRGBFromDepthToColor(Target* dst, Target* depth_src)
|
||||
|
||||
dst->m_unscaled_size = new_size;
|
||||
dst->m_valid_rgb = true;
|
||||
dst->m_32_bits_fmt = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3653,8 +3682,26 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
||||
src->m_target = true;
|
||||
src->m_from_target = dst;
|
||||
src->m_from_target_TEX0 = dst->m_TEX0;
|
||||
src->m_alpha_minmax.first = dst->m_alpha_min;
|
||||
src->m_alpha_minmax.second = dst->m_alpha_max;
|
||||
|
||||
if ((src->m_TEX0.PSM & 0xf) == PSMCT24)
|
||||
{
|
||||
src->m_alpha_minmax.first = TEXA.AEM ? 0 : TEXA.TA0;
|
||||
src->m_alpha_minmax.second = TEXA.TA0;
|
||||
}
|
||||
else
|
||||
{
|
||||
src->m_alpha_minmax.first = dst->m_alpha_min;
|
||||
src->m_alpha_minmax.second = dst->m_alpha_max;
|
||||
|
||||
if (!dst->m_32_bits_fmt)
|
||||
{
|
||||
const bool using_both = (src->m_alpha_minmax.first ^ src->m_alpha_minmax.second) & 128;
|
||||
const bool using_ta1 = (src->m_alpha_minmax.second & 128);
|
||||
|
||||
src->m_alpha_minmax.first = TEXA.AEM ? 0 : (using_both ? std::min(TEXA.TA1, TEXA.TA0) : (using_ta1 ? TEXA.TA1 : TEXA.TA0));
|
||||
src->m_alpha_minmax.second = (using_both ? std::max(TEXA.TA1, TEXA.TA0) : (using_ta1 ? TEXA.TA1 : TEXA.TA0));
|
||||
}
|
||||
}
|
||||
|
||||
if (psm.pal > 0)
|
||||
{
|
||||
@@ -3695,8 +3742,26 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
||||
src->m_from_target_TEX0 = dst->m_TEX0;
|
||||
src->m_valid_rect = dst->m_valid;
|
||||
src->m_end_block = dst->m_end_block;
|
||||
src->m_alpha_minmax.first = dst->m_alpha_min;
|
||||
src->m_alpha_minmax.second = dst->m_alpha_max;
|
||||
|
||||
if ((src->m_TEX0.PSM & 0xf) == PSMCT24)
|
||||
{
|
||||
src->m_alpha_minmax.first = TEXA.AEM ? 0 : TEXA.TA0;
|
||||
src->m_alpha_minmax.second = TEXA.TA0;
|
||||
}
|
||||
else
|
||||
{
|
||||
src->m_alpha_minmax.first = dst->m_alpha_min;
|
||||
src->m_alpha_minmax.second = dst->m_alpha_max;
|
||||
|
||||
if (!dst->m_32_bits_fmt)
|
||||
{
|
||||
const bool using_both = (src->m_alpha_minmax.first ^ src->m_alpha_minmax.second) & 128;
|
||||
const bool using_ta1 = (src->m_alpha_minmax.second & 128);
|
||||
|
||||
src->m_alpha_minmax.first = TEXA.AEM ? 0 : (using_both ? std::min(TEXA.TA1, TEXA.TA0) : (using_ta1 ? TEXA.TA1 : TEXA.TA0));
|
||||
src->m_alpha_minmax.second = (using_both ? std::max(TEXA.TA1, TEXA.TA0) : (using_ta1 ? TEXA.TA1 : TEXA.TA0));
|
||||
}
|
||||
}
|
||||
|
||||
dst->Update();
|
||||
|
||||
@@ -5197,8 +5262,16 @@ void GSTextureCache::Target::Update()
|
||||
g_gs_device->DrawMultiStretchRects(drects, ndrects, m_texture, shader);
|
||||
}
|
||||
|
||||
m_alpha_min = 0;
|
||||
m_alpha_max = 255;
|
||||
if ((m_TEX0.PSM & 0xf) == PSMCT24)
|
||||
{
|
||||
m_alpha_min = 128;
|
||||
m_alpha_max = 128;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_alpha_min = 0;
|
||||
m_alpha_max = m_32_bits_fmt ? 255 : 128;
|
||||
}
|
||||
g_gs_device->Recycle(t);
|
||||
m_dirty.clear();
|
||||
}
|
||||
|
||||
@@ -473,8 +473,8 @@ public:
|
||||
GSTexture* LookupPaletteSource(u32 CBP, u32 CPSM, u32 CBW, GSVector2i& offset, float* scale, const GSVector2i& size);
|
||||
std::shared_ptr<Palette> LookupPaletteObject(const u32* clut, u16 pal, bool need_gs_texture);
|
||||
|
||||
Source* LookupSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, const GSVector2i* lod, const bool possible_shuffle);
|
||||
Source* LookupDepthSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, const bool possible_shuffle, bool palette = false);
|
||||
Source* LookupSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, const GSVector2i* lod, const bool possible_shuffle, const bool linear);
|
||||
Source* LookupDepthSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, const bool possible_shuffle, const bool linear, bool palette = false);
|
||||
|
||||
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,
|
||||
|
||||
@@ -2141,8 +2141,6 @@ void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
|
||||
}
|
||||
|
||||
// Try to reduce render pass restarts
|
||||
if (!stencil && config.depth.key == DepthStencilSelector::NoDepth().key && (m_current_render.color_target != rt || m_current_render.depth_target != config.ds))
|
||||
config.ds = nullptr;
|
||||
if (!config.ds && m_current_render.color_target == rt && stencil == m_current_render.stencil_target && m_current_render.depth_target != config.tex)
|
||||
config.ds = m_current_render.depth_target;
|
||||
if (!rt && config.ds == m_current_render.depth_target && m_current_render.color_target != config.tex)
|
||||
|
||||
@@ -194,8 +194,8 @@ void GSDownloadTextureMTL::CopyFromTexture(
|
||||
GetTransferPitch(use_transfer_pitch ? static_cast<u32>(drc.width()) : m_width, PITCH_ALIGNMENT);
|
||||
GetTransferSize(drc, ©_offset, ©_size, ©_rows);
|
||||
|
||||
m_dev->EndRenderPass();
|
||||
mtlTex->FlushClears();
|
||||
m_dev->EndRenderPass();
|
||||
g_perfmon.Put(GSPerfMon::Readbacks, 1);
|
||||
|
||||
m_copy_cmdbuffer = MRCRetain(m_dev->GetRenderCmdBuf());
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,10 +16,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "GS/Renderers/Common/GSDevice.h"
|
||||
#include "GS/GSVector.h"
|
||||
#include "GS/Renderers/Vulkan/GSTextureVK.h"
|
||||
#include "GS/Renderers/Vulkan/VKLoader.h"
|
||||
#include "GS/Renderers/Vulkan/VKStreamBuffer.h"
|
||||
#include "GS/GSVector.h"
|
||||
|
||||
#include "common/HashCombine.h"
|
||||
#include "common/ReadbackSpinManager.h"
|
||||
@@ -50,13 +50,10 @@ public:
|
||||
bool vk_ext_provoking_vertex : 1;
|
||||
bool vk_ext_memory_budget : 1;
|
||||
bool vk_ext_calibrated_timestamps : 1;
|
||||
bool vk_ext_line_rasterization : 1;
|
||||
bool vk_ext_rasterization_order_attachment_access : 1;
|
||||
bool vk_ext_attachment_feedback_loop_layout : 1;
|
||||
bool vk_ext_full_screen_exclusive : 1;
|
||||
bool vk_khr_driver_properties : 1;
|
||||
bool vk_khr_fragment_shader_barycentric : 1;
|
||||
bool vk_khr_shader_draw_parameters : 1;
|
||||
};
|
||||
|
||||
// Global state accessors
|
||||
@@ -108,9 +105,6 @@ public:
|
||||
__fi VKStreamBuffer& GetTextureUploadBuffer() { return m_texture_stream_buffer; }
|
||||
VkCommandBuffer GetCurrentInitCommandBuffer();
|
||||
|
||||
/// Allocates a descriptor set from the pool reserved for the current frame.
|
||||
VkDescriptorSet AllocateDescriptorSet(VkDescriptorSetLayout set_layout);
|
||||
|
||||
/// Allocates a descriptor set from the pool reserved for the current frame.
|
||||
VkDescriptorSet AllocatePersistentDescriptorSet(VkDescriptorSetLayout set_layout);
|
||||
|
||||
@@ -199,12 +193,11 @@ private:
|
||||
};
|
||||
|
||||
using ExtensionList = std::vector<const char*>;
|
||||
static bool SelectInstanceExtensions(
|
||||
ExtensionList* extension_list, const WindowInfo& wi, bool enable_debug_utils);
|
||||
static bool SelectInstanceExtensions(ExtensionList* extension_list, const WindowInfo& wi, bool enable_debug_utils);
|
||||
bool SelectDeviceExtensions(ExtensionList* extension_list, bool enable_surface);
|
||||
bool SelectDeviceFeatures();
|
||||
bool CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer);
|
||||
void ProcessDeviceExtensions();
|
||||
bool ProcessDeviceExtensions();
|
||||
|
||||
bool CreateAllocator();
|
||||
bool CreateCommandBuffers();
|
||||
@@ -237,7 +230,6 @@ private:
|
||||
// [0] - Init (upload) command buffer, [1] - draw command buffer
|
||||
VkCommandPool command_pool = VK_NULL_HANDLE;
|
||||
std::array<VkCommandBuffer, 2> command_buffers{VK_NULL_HANDLE, VK_NULL_HANDLE};
|
||||
VkDescriptorPool descriptor_pool = VK_NULL_HANDLE;
|
||||
VkFence fence = VK_NULL_HANDLE;
|
||||
u64 fence_counter = 0;
|
||||
s32 spin_id = -1;
|
||||
@@ -388,11 +380,7 @@ public:
|
||||
enum : u32
|
||||
{
|
||||
NUM_TFX_DYNAMIC_OFFSETS = 2,
|
||||
NUM_TFX_DRAW_TEXTURES = 2,
|
||||
NUM_TFX_RT_TEXTURES = 2,
|
||||
NUM_TFX_TEXTURES = NUM_TFX_DRAW_TEXTURES + NUM_TFX_RT_TEXTURES,
|
||||
NUM_CONVERT_TEXTURES = 1,
|
||||
NUM_CONVERT_SAMPLERS = 1,
|
||||
NUM_UTILITY_SAMPLERS = 1,
|
||||
CONVERT_PUSH_CONSTANTS_SIZE = 96,
|
||||
|
||||
NUM_CAS_PIPELINES = 2,
|
||||
@@ -401,10 +389,18 @@ public:
|
||||
{
|
||||
TFX_DESCRIPTOR_SET_UBO,
|
||||
TFX_DESCRIPTOR_SET_TEXTURES,
|
||||
TFX_DESCRIPTOR_SET_RT,
|
||||
|
||||
NUM_TFX_DESCRIPTOR_SETS,
|
||||
};
|
||||
enum TFX_TEXTURES : u32
|
||||
{
|
||||
TFX_TEXTURE_TEXTURE,
|
||||
TFX_TEXTURE_PALETTE,
|
||||
TFX_TEXTURE_RT,
|
||||
TFX_TEXTURE_PRIMID,
|
||||
|
||||
NUM_TFX_TEXTURES
|
||||
};
|
||||
enum DATE_RENDER_PASS : u32
|
||||
{
|
||||
DATE_RENDER_PASS_NONE = 0,
|
||||
@@ -419,8 +415,7 @@ private:
|
||||
VkPipelineLayout m_utility_pipeline_layout = VK_NULL_HANDLE;
|
||||
|
||||
VkDescriptorSetLayout m_tfx_ubo_ds_layout = VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout m_tfx_sampler_ds_layout = VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout m_tfx_rt_texture_ds_layout = VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout m_tfx_texture_ds_layout = VK_NULL_HANDLE;
|
||||
VkPipelineLayout m_tfx_pipeline_layout = VK_NULL_HANDLE;
|
||||
|
||||
VKStreamBuffer m_vertex_stream_buffer;
|
||||
@@ -449,7 +444,8 @@ private:
|
||||
VkPipeline m_shadeboost_pipeline = {};
|
||||
|
||||
std::unordered_map<u32, VkShaderModule> m_tfx_vertex_shaders;
|
||||
std::unordered_map<GSHWDrawConfig::PSSelector, VkShaderModule, GSHWDrawConfig::PSSelectorHash> m_tfx_fragment_shaders;
|
||||
std::unordered_map<GSHWDrawConfig::PSSelector, VkShaderModule, GSHWDrawConfig::PSSelectorHash>
|
||||
m_tfx_fragment_shaders;
|
||||
std::unordered_map<PipelineSelector, VkPipeline, PipelineSelectorHash> m_tfx_pipelines;
|
||||
|
||||
VkRenderPass m_utility_color_render_pass_load = VK_NULL_HANDLE;
|
||||
@@ -473,15 +469,18 @@ private:
|
||||
|
||||
std::string m_tfx_source;
|
||||
|
||||
GSTexture* CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format) override;
|
||||
GSTexture* CreateSurface(
|
||||
GSTexture::Type type, int width, int height, int levels, GSTexture::Format format) override;
|
||||
|
||||
void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE,
|
||||
const GSRegEXTBUF& EXTBUF, u32 c, const bool linear) final;
|
||||
void DoInterlace(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderInterlace shader, bool linear, const InterlaceConstantBuffer& cb) final;
|
||||
void DoInterlace(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect,
|
||||
ShaderInterlace shader, bool linear, const InterlaceConstantBuffer& cb) final;
|
||||
void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) final;
|
||||
void DoFXAA(GSTexture* sTex, GSTexture* dTex) final;
|
||||
|
||||
bool DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array<u32, NUM_CAS_CONSTANTS>& constants) final;
|
||||
bool DoCAS(
|
||||
GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array<u32, NUM_CAS_CONSTANTS>& constants) final;
|
||||
|
||||
VkSampler GetSampler(GSHWDrawConfig::SamplerSelector ss);
|
||||
void ClearSamplerCache() final;
|
||||
@@ -520,7 +519,8 @@ public:
|
||||
|
||||
__fi static GSDeviceVK* GetInstance() { return static_cast<GSDeviceVK*>(g_gs_device.get()); }
|
||||
|
||||
static void GetAdaptersAndFullscreenModes(std::vector<std::string>* adapters, std::vector<std::string>* fullscreen_modes);
|
||||
static void GetAdaptersAndFullscreenModes(
|
||||
std::vector<std::string>* adapters, std::vector<std::string>* fullscreen_modes);
|
||||
|
||||
/// Returns true if Vulkan is suitable as a default for the devices in the system.
|
||||
static bool IsSuitableDefaultRenderer();
|
||||
@@ -571,7 +571,8 @@ public:
|
||||
bool green, bool blue, bool alpha) override;
|
||||
void PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect,
|
||||
PresentShader shader, float shaderTime, bool linear) override;
|
||||
void DrawMultiStretchRects(const MultiStretchRect* rects, u32 num_rects, GSTexture* dTex, ShaderConvert shader) override;
|
||||
void DrawMultiStretchRects(
|
||||
const MultiStretchRect* rects, u32 num_rects, GSTexture* dTex, ShaderConvert shader) override;
|
||||
void DoMultiStretchRects(const MultiStretchRect* rects, u32 num_rects, GSTextureVK* dTex, ShaderConvert shader);
|
||||
|
||||
void BeginRenderPassForStretchRect(
|
||||
@@ -583,8 +584,10 @@ public:
|
||||
void BlitRect(GSTexture* sTex, const GSVector4i& sRect, u32 sLevel, GSTexture* dTex, const GSVector4i& dRect,
|
||||
u32 dLevel, bool linear);
|
||||
|
||||
void UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize) override;
|
||||
void ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM, GSTexture* dTex, u32 DBW, u32 DPSM) override;
|
||||
void UpdateCLUTTexture(
|
||||
GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize) override;
|
||||
void ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM,
|
||||
GSTexture* dTex, u32 DBW, u32 DPSM) override;
|
||||
|
||||
void SetupDATE(GSTexture* rt, GSTexture* ds, bool datm, const GSVector4i& bbox);
|
||||
GSTextureVK* SetupPrimitiveTrackingDATE(GSHWDrawConfig& config);
|
||||
@@ -631,8 +634,7 @@ public:
|
||||
bool ApplyUtilityState(bool already_execed = false);
|
||||
bool ApplyTFXState(bool already_execed = false);
|
||||
|
||||
void SetVertexBuffer(VkBuffer buffer, VkDeviceSize offset);
|
||||
void SetIndexBuffer(VkBuffer buffer, VkDeviceSize offset, VkIndexType type);
|
||||
void SetIndexBuffer(VkBuffer buffer);
|
||||
void SetBlendConstants(u8 color);
|
||||
void SetLineWidth(float width);
|
||||
|
||||
@@ -657,25 +659,32 @@ public:
|
||||
private:
|
||||
enum DIRTY_FLAG : u32
|
||||
{
|
||||
DIRTY_FLAG_TFX_SAMPLERS_DS = (1 << 0),
|
||||
DIRTY_FLAG_TFX_RT_TEXTURE_DS = (1 << 1),
|
||||
DIRTY_FLAG_TFX_DYNAMIC_OFFSETS = (1 << 2),
|
||||
DIRTY_FLAG_UTILITY_TEXTURE = (1 << 3),
|
||||
DIRTY_FLAG_BLEND_CONSTANTS = (1 << 4),
|
||||
DIRTY_FLAG_LINE_WIDTH = (1 << 5),
|
||||
DIRTY_FLAG_VERTEX_BUFFER = (1 << 6),
|
||||
DIRTY_FLAG_INDEX_BUFFER = (1 << 7),
|
||||
DIRTY_FLAG_VIEWPORT = (1 << 8),
|
||||
DIRTY_FLAG_SCISSOR = (1 << 9),
|
||||
DIRTY_FLAG_PIPELINE = (1 << 10),
|
||||
DIRTY_FLAG_VS_CONSTANT_BUFFER = (1 << 11),
|
||||
DIRTY_FLAG_PS_CONSTANT_BUFFER = (1 << 12),
|
||||
DIRTY_FLAG_TFX_TEXTURE_0 = (1 << 0), // 0, 1, 2, 3
|
||||
DIRTY_FLAG_TFX_UBO = (1 << 4),
|
||||
DIRTY_FLAG_UTILITY_TEXTURE = (1 << 5),
|
||||
DIRTY_FLAG_BLEND_CONSTANTS = (1 << 6),
|
||||
DIRTY_FLAG_LINE_WIDTH = (1 << 7),
|
||||
DIRTY_FLAG_INDEX_BUFFER = (1 << 8),
|
||||
DIRTY_FLAG_VIEWPORT = (1 << 9),
|
||||
DIRTY_FLAG_SCISSOR = (1 << 10),
|
||||
DIRTY_FLAG_PIPELINE = (1 << 11),
|
||||
DIRTY_FLAG_VS_CONSTANT_BUFFER = (1 << 12),
|
||||
DIRTY_FLAG_PS_CONSTANT_BUFFER = (1 << 13),
|
||||
|
||||
DIRTY_BASE_STATE = DIRTY_FLAG_VERTEX_BUFFER | DIRTY_FLAG_INDEX_BUFFER | DIRTY_FLAG_PIPELINE |
|
||||
DIRTY_FLAG_VIEWPORT | DIRTY_FLAG_SCISSOR | DIRTY_FLAG_BLEND_CONSTANTS | DIRTY_FLAG_LINE_WIDTH,
|
||||
DIRTY_TFX_STATE = DIRTY_BASE_STATE | DIRTY_FLAG_TFX_SAMPLERS_DS | DIRTY_FLAG_TFX_RT_TEXTURE_DS,
|
||||
DIRTY_FLAG_TFX_TEXTURE_TEX = (DIRTY_FLAG_TFX_TEXTURE_0 << 0),
|
||||
DIRTY_FLAG_TFX_TEXTURE_PALETTE = (DIRTY_FLAG_TFX_TEXTURE_0 << 1),
|
||||
DIRTY_FLAG_TFX_TEXTURE_RT = (DIRTY_FLAG_TFX_TEXTURE_0 << 2),
|
||||
DIRTY_FLAG_TFX_TEXTURE_PRIMID = (DIRTY_FLAG_TFX_TEXTURE_0 << 3),
|
||||
|
||||
DIRTY_FLAG_TFX_TEXTURES = DIRTY_FLAG_TFX_TEXTURE_TEX | DIRTY_FLAG_TFX_TEXTURE_PALETTE |
|
||||
DIRTY_FLAG_TFX_TEXTURE_RT | DIRTY_FLAG_TFX_TEXTURE_PRIMID,
|
||||
|
||||
DIRTY_BASE_STATE = DIRTY_FLAG_INDEX_BUFFER | DIRTY_FLAG_PIPELINE | DIRTY_FLAG_VIEWPORT | DIRTY_FLAG_SCISSOR |
|
||||
DIRTY_FLAG_BLEND_CONSTANTS | DIRTY_FLAG_LINE_WIDTH,
|
||||
DIRTY_TFX_STATE = DIRTY_BASE_STATE | DIRTY_FLAG_TFX_TEXTURES,
|
||||
DIRTY_UTILITY_STATE = DIRTY_BASE_STATE | DIRTY_FLAG_UTILITY_TEXTURE,
|
||||
DIRTY_CONSTANT_BUFFER_STATE = DIRTY_FLAG_VS_CONSTANT_BUFFER | DIRTY_FLAG_PS_CONSTANT_BUFFER,
|
||||
ALL_DIRTY_STATE = DIRTY_BASE_STATE | DIRTY_TFX_STATE | DIRTY_UTILITY_STATE | DIRTY_CONSTANT_BUFFER_STATE,
|
||||
};
|
||||
|
||||
enum class PipelineLayout
|
||||
@@ -688,6 +697,7 @@ private:
|
||||
void InitializeState();
|
||||
bool CreatePersistentDescriptorSets();
|
||||
|
||||
void SetInitialState(VkCommandBuffer cmdbuf);
|
||||
void ApplyBaseState(u32 flags, VkCommandBuffer cmdbuf);
|
||||
|
||||
// Which bindings/state has to be updated before the next draw.
|
||||
@@ -695,12 +705,7 @@ private:
|
||||
FeedbackLoopFlag m_current_framebuffer_feedback_loop = FeedbackLoopFlag_None;
|
||||
bool m_warned_slow_spin = false;
|
||||
|
||||
// input assembly
|
||||
VkBuffer m_vertex_buffer = VK_NULL_HANDLE;
|
||||
VkDeviceSize m_vertex_buffer_offset = 0;
|
||||
VkBuffer m_index_buffer = VK_NULL_HANDLE;
|
||||
VkDeviceSize m_index_buffer_offset = 0;
|
||||
VkIndexType m_index_type = VK_INDEX_TYPE_UINT16;
|
||||
|
||||
GSTextureVK* m_current_render_target = nullptr;
|
||||
GSTextureVK* m_current_depth_target = nullptr;
|
||||
|
||||
@@ -15,14 +15,14 @@
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "GS/GSGL.h"
|
||||
#include "GS/GSPerfMon.h"
|
||||
#include "GS/Renderers/Vulkan/GSDeviceVK.h"
|
||||
#include "GS/Renderers/Vulkan/GSTextureVK.h"
|
||||
#include "GS/Renderers/Vulkan/VKBuilders.h"
|
||||
#include "GS/GSPerfMon.h"
|
||||
#include "GS/GSGL.h"
|
||||
|
||||
#include "common/BitUtils.h"
|
||||
#include "common/Assertions.h"
|
||||
#include "common/BitUtils.h"
|
||||
|
||||
static constexpr const VkComponentMapping s_identity_swizzle{VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY};
|
||||
@@ -52,7 +52,8 @@ static VkImageLayout GetVkImageLayout(GSTextureVK::Layout layout)
|
||||
|
||||
static VkAccessFlagBits GetFeedbackLoopInputAccessBits()
|
||||
{
|
||||
return GSDeviceVK::GetInstance()->UseFeedbackLoopLayout() ? VK_ACCESS_SHADER_READ_BIT : VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
|
||||
return GSDeviceVK::GetInstance()->UseFeedbackLoopLayout() ? VK_ACCESS_SHADER_READ_BIT :
|
||||
VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
|
||||
}
|
||||
|
||||
GSTextureVK::GSTextureVK(Type type, Format format, int width, int height, int levels, VkImage image,
|
||||
@@ -111,10 +112,11 @@ std::unique_ptr<GSTextureVK> GSTextureVK::Create(Type type, Format format, int w
|
||||
case Type::RenderTarget:
|
||||
{
|
||||
pxAssert(levels == 1);
|
||||
ici.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
|
||||
(GSDeviceVK::GetInstance()->UseFeedbackLoopLayout() ? VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT :
|
||||
VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT);
|
||||
ici.usage =
|
||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
|
||||
(GSDeviceVK::GetInstance()->UseFeedbackLoopLayout() ? VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT :
|
||||
VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -124,7 +126,8 @@ std::unique_ptr<GSTextureVK> GSTextureVK::Create(Type type, Format format, int w
|
||||
ici.usage =
|
||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
||||
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
|
||||
(GSDeviceVK::GetInstance()->UseFeedbackLoopLayout() ? VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT : 0);
|
||||
(GSDeviceVK::GetInstance()->UseFeedbackLoopLayout() ? VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT :
|
||||
0);
|
||||
vci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
}
|
||||
break;
|
||||
@@ -430,8 +433,8 @@ bool GSTextureVK::Map(GSMap& m, const GSVector4i* r, int layer)
|
||||
m_map_area = r ? *r : GetRect();
|
||||
m_map_level = layer;
|
||||
|
||||
m.pitch =
|
||||
Common::AlignUpPow2(CalcUploadPitch(m_map_area.width()), GSDeviceVK::GetInstance()->GetBufferCopyRowPitchAlignment());
|
||||
m.pitch = Common::AlignUpPow2(
|
||||
CalcUploadPitch(m_map_area.width()), GSDeviceVK::GetInstance()->GetBufferCopyRowPitchAlignment());
|
||||
|
||||
// see note in Update() for the reason why.
|
||||
const u32 required_size = CalcUploadSize(m_map_area.height(), m.pitch);
|
||||
@@ -459,7 +462,8 @@ void GSTextureVK::Unmap()
|
||||
|
||||
const u32 width = m_map_area.width();
|
||||
const u32 height = m_map_area.height();
|
||||
const u32 pitch = Common::AlignUpPow2(CalcUploadPitch(width), GSDeviceVK::GetInstance()->GetBufferCopyRowPitchAlignment());
|
||||
const u32 pitch =
|
||||
Common::AlignUpPow2(CalcUploadPitch(width), GSDeviceVK::GetInstance()->GetBufferCopyRowPitchAlignment());
|
||||
const u32 required_size = CalcUploadSize(height, pitch);
|
||||
VKStreamBuffer& buffer = GSDeviceVK::GetInstance()->GetTextureUploadBuffer();
|
||||
const u32 buffer_offset = buffer.GetCurrentOffset();
|
||||
@@ -826,7 +830,8 @@ GSDownloadTextureVK::~GSDownloadTextureVK()
|
||||
|
||||
std::unique_ptr<GSDownloadTextureVK> GSDownloadTextureVK::Create(u32 width, u32 height, GSTexture::Format format)
|
||||
{
|
||||
const u32 buffer_size = GetBufferSize(width, height, format, GSDeviceVK::GetInstance()->GetBufferCopyRowPitchAlignment());
|
||||
const u32 buffer_size =
|
||||
GetBufferSize(width, height, format, GSDeviceVK::GetInstance()->GetBufferCopyRowPitchAlignment());
|
||||
|
||||
const VkBufferCreateInfo bci = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, nullptr, 0u, buffer_size,
|
||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_SHARING_MODE_EXCLUSIVE, 0u, nullptr};
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GS/GS.h"
|
||||
#include "GS/Renderers/Common/GSTexture.h"
|
||||
#include "GS/GS.h"
|
||||
#include "GS/Renderers/Vulkan/VKLoader.h"
|
||||
|
||||
#include <limits>
|
||||
@@ -76,8 +76,8 @@ public:
|
||||
void OverrideImageLayout(Layout new_layout);
|
||||
|
||||
void TransitionToLayout(VkCommandBuffer command_buffer, Layout new_layout);
|
||||
void TransitionSubresourcesToLayout(VkCommandBuffer command_buffer, int start_level, int num_levels,
|
||||
Layout old_layout, Layout new_layout);
|
||||
void TransitionSubresourcesToLayout(
|
||||
VkCommandBuffer command_buffer, int start_level, int num_levels, Layout old_layout, Layout new_layout);
|
||||
|
||||
/// Framebuffers are lazily allocated.
|
||||
VkFramebuffer GetFramebuffer(bool feedback_loop);
|
||||
|
||||
@@ -141,6 +141,11 @@ void Vulkan::DescriptorSetLayoutBuilder::Clear()
|
||||
m_ci.bindingCount = 0;
|
||||
}
|
||||
|
||||
void Vulkan::DescriptorSetLayoutBuilder::SetPushFlag()
|
||||
{
|
||||
m_ci.flags |= VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR;
|
||||
}
|
||||
|
||||
VkDescriptorSetLayout Vulkan::DescriptorSetLayoutBuilder::Create(VkDevice device)
|
||||
{
|
||||
VkDescriptorSetLayout layout;
|
||||
@@ -724,6 +729,17 @@ void Vulkan::DescriptorSetUpdateBuilder::Update(VkDevice device, bool clear /*=
|
||||
Clear();
|
||||
}
|
||||
|
||||
void Vulkan::DescriptorSetUpdateBuilder::PushUpdate(
|
||||
VkCommandBuffer cmdbuf, VkPipelineBindPoint bind_point, VkPipelineLayout layout, u32 set, bool clear /*= true*/)
|
||||
{
|
||||
pxAssert(m_num_writes > 0);
|
||||
|
||||
vkCmdPushDescriptorSetKHR(cmdbuf, bind_point, layout, set, m_num_writes, m_writes.data());
|
||||
|
||||
if (clear)
|
||||
Clear();
|
||||
}
|
||||
|
||||
void Vulkan::DescriptorSetUpdateBuilder::AddImageDescriptorWrite(VkDescriptorSet set, u32 binding, VkImageView view,
|
||||
VkImageLayout layout /*= VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL*/)
|
||||
{
|
||||
|
||||
@@ -48,6 +48,7 @@ namespace Vulkan
|
||||
DescriptorSetLayoutBuilder();
|
||||
|
||||
void Clear();
|
||||
void SetPushFlag();
|
||||
|
||||
VkDescriptorSetLayout Create(VkDevice device);
|
||||
|
||||
@@ -209,7 +210,7 @@ namespace Vulkan
|
||||
|
||||
VkSpecializationInfo m_si;
|
||||
std::array<VkSpecializationMapEntry, MAX_SPECIALIZATION_CONSTANTS> m_smap_entries;
|
||||
std::array<u8, SPECIALIZATION_CONSTANT_SIZE* MAX_SPECIALIZATION_CONSTANTS> m_smap_constants;
|
||||
std::array<u8, SPECIALIZATION_CONSTANT_SIZE * MAX_SPECIALIZATION_CONSTANTS> m_smap_constants;
|
||||
};
|
||||
|
||||
class SamplerBuilder
|
||||
@@ -248,6 +249,8 @@ namespace Vulkan
|
||||
void Clear();
|
||||
|
||||
void Update(VkDevice device, bool clear = true);
|
||||
void PushUpdate(VkCommandBuffer cmdbuf, VkPipelineBindPoint bind_point, VkPipelineLayout layout, u32 set,
|
||||
bool clear = true);
|
||||
|
||||
void AddImageDescriptorWrite(VkDescriptorSet set, u32 binding, VkImageView view,
|
||||
VkImageLayout layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
|
||||
@@ -243,4 +243,7 @@ VULKAN_DEVICE_ENTRY_POINT(vkReleaseFullScreenExclusiveModeEXT, false)
|
||||
// VK_EXT_calibrated_timestamps
|
||||
VULKAN_DEVICE_ENTRY_POINT(vkGetCalibratedTimestampsEXT, false)
|
||||
|
||||
// VK_KHR_push_descriptor
|
||||
VULKAN_DEVICE_ENTRY_POINT(vkCmdPushDescriptorSetKHR, false)
|
||||
|
||||
#endif // VULKAN_DEVICE_ENTRY_POINT
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "GS/Renderers/Vulkan/VKShaderCache.h"
|
||||
#include "GS/GS.h"
|
||||
#include "GS/Renderers/Vulkan/GSDeviceVK.h"
|
||||
#include "GS/Renderers/Vulkan/VKBuilders.h"
|
||||
#include "GS/GS.h"
|
||||
#include "GS/Renderers/Vulkan/VKShaderCache.h"
|
||||
|
||||
#include "Config.h"
|
||||
#include "ShaderCacheVersion.h"
|
||||
@@ -44,28 +44,29 @@
|
||||
|
||||
std::unique_ptr<VKShaderCache> g_vulkan_shader_cache;
|
||||
|
||||
namespace {
|
||||
namespace
|
||||
{
|
||||
#pragma pack(push, 4)
|
||||
struct VK_PIPELINE_CACHE_HEADER
|
||||
{
|
||||
u32 header_length;
|
||||
u32 header_version;
|
||||
u32 vendor_id;
|
||||
u32 device_id;
|
||||
u8 uuid[VK_UUID_SIZE];
|
||||
};
|
||||
struct VK_PIPELINE_CACHE_HEADER
|
||||
{
|
||||
u32 header_length;
|
||||
u32 header_version;
|
||||
u32 vendor_id;
|
||||
u32 device_id;
|
||||
u8 uuid[VK_UUID_SIZE];
|
||||
};
|
||||
|
||||
struct CacheIndexEntry
|
||||
{
|
||||
u64 source_hash_low;
|
||||
u64 source_hash_high;
|
||||
u32 source_length;
|
||||
u32 shader_type;
|
||||
u32 file_offset;
|
||||
u32 blob_size;
|
||||
};
|
||||
struct CacheIndexEntry
|
||||
{
|
||||
u64 source_hash_low;
|
||||
u64 source_hash_high;
|
||||
u32 source_length;
|
||||
u32 shader_type;
|
||||
u32 file_offset;
|
||||
u32 blob_size;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
}
|
||||
} // namespace
|
||||
|
||||
static bool ValidatePipelineCacheHeader(const VK_PIPELINE_CACHE_HEADER& header)
|
||||
{
|
||||
@@ -478,7 +479,8 @@ bool VKShaderCache::FlushPipelineCache()
|
||||
return false;
|
||||
|
||||
size_t data_size;
|
||||
VkResult res = vkGetPipelineCacheData(GSDeviceVK::GetInstance()->GetDevice(), m_pipeline_cache, &data_size, nullptr);
|
||||
VkResult res =
|
||||
vkGetPipelineCacheData(GSDeviceVK::GetInstance()->GetDevice(), m_pipeline_cache, &data_size, nullptr);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkGetPipelineCacheData() failed: ");
|
||||
|
||||
@@ -16,11 +16,11 @@
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "GS/Renderers/Vulkan/GSDeviceVK.h"
|
||||
#include "GS/Renderers/Vulkan/VKStreamBuffer.h"
|
||||
#include "GS/Renderers/Vulkan/VKBuilders.h"
|
||||
#include "GS/Renderers/Vulkan/VKStreamBuffer.h"
|
||||
|
||||
#include "common/BitUtils.h"
|
||||
#include "common/Assertions.h"
|
||||
#include "common/BitUtils.h"
|
||||
#include "common/Console.h"
|
||||
|
||||
VKStreamBuffer::VKStreamBuffer() = default;
|
||||
@@ -79,7 +79,8 @@ bool VKStreamBuffer::Create(VkBufferUsageFlags usage, u32 size)
|
||||
VmaAllocationInfo ai = {};
|
||||
VkBuffer new_buffer = VK_NULL_HANDLE;
|
||||
VmaAllocation new_allocation = VK_NULL_HANDLE;
|
||||
VkResult res = vmaCreateBuffer(GSDeviceVK::GetInstance()->GetAllocator(), &bci, &aci, &new_buffer, &new_allocation, &ai);
|
||||
VkResult res =
|
||||
vmaCreateBuffer(GSDeviceVK::GetInstance()->GetAllocator(), &bci, &aci, &new_buffer, &new_allocation, &ai);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreateBuffer failed: ");
|
||||
|
||||
@@ -35,6 +35,7 @@ public:
|
||||
|
||||
__fi bool IsValid() const { return (m_buffer != VK_NULL_HANDLE); }
|
||||
__fi VkBuffer GetBuffer() const { return m_buffer; }
|
||||
__fi const VkBuffer* GetBufferPtr() const { return &m_buffer; }
|
||||
__fi u8* GetHostPointer() const { return m_host_pointer; }
|
||||
__fi u8* GetCurrentHostPointer() const { return m_host_pointer + m_current_offset; }
|
||||
__fi u32 GetCurrentSize() const { return m_size; }
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "GS/Renderers/Vulkan/GSDeviceVK.h"
|
||||
#include "GS/Renderers/Vulkan/VKSwapChain.h"
|
||||
#include "GS/Renderers/Vulkan/VKBuilders.h"
|
||||
#include "GS/Renderers/Vulkan/VKSwapChain.h"
|
||||
|
||||
#include "common/Assertions.h"
|
||||
#include "common/CocoaTools.h"
|
||||
@@ -183,8 +183,8 @@ static VkFormat GetLinearFormat(VkFormat format)
|
||||
std::optional<VkSurfaceFormatKHR> VKSwapChain::SelectSurfaceFormat(VkSurfaceKHR surface)
|
||||
{
|
||||
u32 format_count;
|
||||
VkResult res =
|
||||
vkGetPhysicalDeviceSurfaceFormatsKHR(GSDeviceVK::GetInstance()->GetPhysicalDevice(), surface, &format_count, nullptr);
|
||||
VkResult res = vkGetPhysicalDeviceSurfaceFormatsKHR(
|
||||
GSDeviceVK::GetInstance()->GetPhysicalDevice(), surface, &format_count, nullptr);
|
||||
if (res != VK_SUCCESS || format_count == 0)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceSurfaceFormatsKHR failed: ");
|
||||
@@ -254,8 +254,8 @@ std::optional<VkPresentModeKHR> VKSwapChain::SelectPresentMode(VkSurfaceKHR surf
|
||||
{
|
||||
VkResult res;
|
||||
u32 mode_count;
|
||||
res =
|
||||
vkGetPhysicalDeviceSurfacePresentModesKHR(GSDeviceVK::GetInstance()->GetPhysicalDevice(), surface, &mode_count, nullptr);
|
||||
res = vkGetPhysicalDeviceSurfacePresentModesKHR(
|
||||
GSDeviceVK::GetInstance()->GetPhysicalDevice(), surface, &mode_count, nullptr);
|
||||
if (res != VK_SUCCESS || mode_count == 0)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceSurfaceFormatsKHR failed: ");
|
||||
@@ -376,7 +376,8 @@ bool VKSwapChain::CreateSwapChain()
|
||||
GSDeviceVK::GetInstance()->GetGraphicsQueueFamilyIndex(),
|
||||
GSDeviceVK::GetInstance()->GetPresentQueueFamilyIndex(),
|
||||
}};
|
||||
if (GSDeviceVK::GetInstance()->GetGraphicsQueueFamilyIndex() != GSDeviceVK::GetInstance()->GetPresentQueueFamilyIndex())
|
||||
if (GSDeviceVK::GetInstance()->GetGraphicsQueueFamilyIndex() !=
|
||||
GSDeviceVK::GetInstance()->GetPresentQueueFamilyIndex())
|
||||
{
|
||||
swap_chain_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
|
||||
swap_chain_info.queueFamilyIndexCount = 2;
|
||||
@@ -462,7 +463,8 @@ bool VKSwapChain::CreateSwapChain()
|
||||
ImageSemaphores sema;
|
||||
|
||||
const VkSemaphoreCreateInfo semaphore_info = {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, nullptr, 0};
|
||||
res = vkCreateSemaphore(GSDeviceVK::GetInstance()->GetDevice(), &semaphore_info, nullptr, &sema.available_semaphore);
|
||||
res = vkCreateSemaphore(
|
||||
GSDeviceVK::GetInstance()->GetDevice(), &semaphore_info, nullptr, &sema.available_semaphore);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreateSemaphore failed: ");
|
||||
|
||||
+3
-3
@@ -582,7 +582,7 @@ void GameList::RewriteCacheFile()
|
||||
|
||||
static bool IsPathExcluded(const std::vector<std::string>& excluded_paths, const std::string& path)
|
||||
{
|
||||
return (std::find(excluded_paths.begin(), excluded_paths.end(), path) != excluded_paths.end());
|
||||
return std::find_if(excluded_paths.begin(), excluded_paths.end(), [&path](const std::string& entry) { return path.starts_with(entry); }) != excluded_paths.end();
|
||||
}
|
||||
|
||||
void GameList::ScanDirectory(const char* path, bool recursive, bool only_cache, const std::vector<std::string>& excluded_paths,
|
||||
@@ -596,7 +596,7 @@ void GameList::ScanDirectory(const char* path, bool recursive, bool only_cache,
|
||||
FileSystem::FindResultsArray files;
|
||||
FileSystem::FindFiles(path, "*",
|
||||
recursive ? (FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_HIDDEN_FILES | FILESYSTEM_FIND_RECURSIVE) :
|
||||
(FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_HIDDEN_FILES),
|
||||
(FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_HIDDEN_FILES),
|
||||
&files);
|
||||
|
||||
u32 files_scanned = 0;
|
||||
@@ -808,7 +808,7 @@ bool GameList::RescanPath(const std::string& path)
|
||||
{
|
||||
// cancel if excluded
|
||||
const std::vector<std::string> excluded_paths(Host::GetBaseStringListSetting("GameList", "ExcludedPaths"));
|
||||
if (std::find(excluded_paths.begin(), excluded_paths.end(), path) != excluded_paths.end())
|
||||
if (IsPathExcluded(excluded_paths, path))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -95,7 +95,7 @@ __fi void IPUProcessInterrupt()
|
||||
if (ipuRegs.ctrl.BUSY && !CommandExecuteQueued)
|
||||
IPUWorker();
|
||||
|
||||
if (ipuRegs.ctrl.BUSY)
|
||||
if (ipuRegs.ctrl.BUSY && !IPU1Status.DataRequested && !(cpuRegs.interrupt & 1 << IPU_PROCESS))
|
||||
{
|
||||
CPU_INT(IPU_PROCESS, ProcessedData ? ProcessedData : 64);
|
||||
}
|
||||
|
||||
@@ -93,6 +93,8 @@ int IPU_Fifo_Input::write(const u32* pMem, int size)
|
||||
if (g_BP.IFC == 8)
|
||||
IPU1Status.DataRequested = false;
|
||||
|
||||
CPU_INT(IPU_PROCESS, transfer_size * BIAS);
|
||||
|
||||
return transfer_size;
|
||||
}
|
||||
|
||||
@@ -106,7 +108,7 @@ int IPU_Fifo_Input::read(void *value)
|
||||
|
||||
if(ipu1ch.chcr.STR && cpuRegs.eCycle[4] == 0x9999)
|
||||
{
|
||||
CPU_INT( DMAC_TO_IPU, 4);
|
||||
CPU_INT( DMAC_TO_IPU, std::min(8U, ipu1ch.qwc));
|
||||
}
|
||||
|
||||
if (g_BP.IFC == 0) return 0;
|
||||
@@ -183,7 +185,7 @@ void WriteFIFO_IPUin(const mem128_t* value)
|
||||
{
|
||||
if (ipuRegs.ctrl.BUSY && !CommandExecuteQueued)
|
||||
{
|
||||
CommandExecuteQueued = true;
|
||||
CommandExecuteQueued = false;
|
||||
CPU_INT(IPU_PROCESS, 8);
|
||||
}
|
||||
}
|
||||
|
||||
+37
-25
@@ -983,7 +983,7 @@ __fi static void finishmpeg2sliceIDEC()
|
||||
__ri static bool mpeg2sliceIDEC()
|
||||
{
|
||||
u16 code;
|
||||
|
||||
static bool ready_to_decode = true;
|
||||
switch (ipu_cmd.pos[0])
|
||||
{
|
||||
case 0:
|
||||
@@ -1007,6 +1007,13 @@ __ri static bool mpeg2sliceIDEC()
|
||||
ipu_cmd.pos[0] = 2;
|
||||
while (1)
|
||||
{
|
||||
if (ready_to_decode == true)
|
||||
{
|
||||
ready_to_decode = false;
|
||||
CPU_INT(IPU_PROCESS, 64); // Should probably be much higher, but myst 3 doesn't like it right now.
|
||||
ipu_cmd.pos[0] = 2;
|
||||
return false;
|
||||
}
|
||||
// IPU0 isn't ready for data, so let's wait for it to be
|
||||
if ((!ipu0ch.chcr.STR || ipuRegs.ctrl.OFC || ipu0ch.qwc == 0) && ipu_cmd.pos[1] <= 2)
|
||||
{
|
||||
@@ -1116,15 +1123,13 @@ __ri static bool mpeg2sliceIDEC()
|
||||
ipu_dither(rgb32, rgb16, decoder.dte);
|
||||
decoder.SetOutputTo(rgb16);
|
||||
}
|
||||
ProcessedData += decoder.ipu0_data;
|
||||
ipu_cmd.pos[1] = 2;
|
||||
return false;
|
||||
|
||||
[[fallthrough]];
|
||||
case 2:
|
||||
{
|
||||
|
||||
pxAssert(decoder.ipu0_data > 0);
|
||||
|
||||
ready_to_decode = true;
|
||||
uint read = ipu_fifo.out.write((u32*)decoder.GetIpuDataPtr(), decoder.ipu0_data);
|
||||
decoder.AdvanceIpuDataBy(read);
|
||||
|
||||
@@ -1205,6 +1210,7 @@ __ri static bool mpeg2sliceIDEC()
|
||||
|
||||
ipu_cmd.pos[1] = 0;
|
||||
ipu_cmd.pos[2] = 0;
|
||||
ready_to_decode = true;
|
||||
}
|
||||
|
||||
finish_idec:
|
||||
@@ -1269,6 +1275,7 @@ finish_idec:
|
||||
__fi static bool mpeg2_slice()
|
||||
{
|
||||
int DCT_offset, DCT_stride;
|
||||
static bool ready_to_decode = true;
|
||||
|
||||
macroblock_8& mb8 = decoder.mb8;
|
||||
macroblock_16& mb16 = decoder.mb16;
|
||||
@@ -1299,7 +1306,12 @@ __fi static bool mpeg2_slice()
|
||||
|
||||
case 2:
|
||||
ipu_cmd.pos[0] = 2;
|
||||
|
||||
if (ready_to_decode == true)
|
||||
{
|
||||
ready_to_decode = false;
|
||||
CPU_INT(IPU_PROCESS, 64); // Should probably be much higher, but myst 3 doesn't like it right now.
|
||||
return false;
|
||||
}
|
||||
// IPU0 isn't ready for data, so let's wait for it to be
|
||||
if ((!ipu0ch.chcr.STR || ipuRegs.ctrl.OFC || ipu0ch.qwc == 0) && ipu_cmd.pos[0] <= 3)
|
||||
{
|
||||
@@ -1406,23 +1418,23 @@ __fi static bool mpeg2_slice()
|
||||
{
|
||||
if (decoder.macroblock_modes & MACROBLOCK_PATTERN)
|
||||
{
|
||||
switch(ipu_cmd.pos[1])
|
||||
switch (ipu_cmd.pos[1])
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
// Get coded block pattern
|
||||
const CBPtab* tab;
|
||||
u16 code = UBITS(16);
|
||||
{
|
||||
// Get coded block pattern
|
||||
const CBPtab* tab;
|
||||
u16 code = UBITS(16);
|
||||
|
||||
if (code >= 0x2000)
|
||||
tab = CBP_7 + (UBITS(7) - 16);
|
||||
else
|
||||
tab = CBP_9 + UBITS(9);
|
||||
if (code >= 0x2000)
|
||||
tab = CBP_7 + (UBITS(7) - 16);
|
||||
else
|
||||
tab = CBP_9 + UBITS(9);
|
||||
|
||||
DUMPBITS(tab->len);
|
||||
decoder.coded_block_pattern = tab->cbp;
|
||||
}
|
||||
[[fallthrough]];
|
||||
DUMPBITS(tab->len);
|
||||
decoder.coded_block_pattern = tab->cbp;
|
||||
}
|
||||
[[fallthrough]];
|
||||
|
||||
case 1:
|
||||
if (decoder.coded_block_pattern & 0x20)
|
||||
@@ -1490,9 +1502,11 @@ __fi static bool mpeg2_slice()
|
||||
}
|
||||
break;
|
||||
|
||||
jNO_DEFAULT;
|
||||
jNO_DEFAULT;
|
||||
}
|
||||
}
|
||||
else
|
||||
DevCon.Warning("No macroblock mode");
|
||||
}
|
||||
|
||||
// Send The MacroBlock via DmaIpuFrom
|
||||
@@ -1500,14 +1514,11 @@ __fi static bool mpeg2_slice()
|
||||
coded_block_pattern = decoder.coded_block_pattern;
|
||||
|
||||
decoder.SetOutputTo(mb16);
|
||||
ProcessedData += decoder.ipu0_data;
|
||||
ipu_cmd.pos[0] = 3;
|
||||
return false;
|
||||
|
||||
[[fallthrough]];
|
||||
case 3:
|
||||
{
|
||||
pxAssert(decoder.ipu0_data > 0);
|
||||
|
||||
ready_to_decode = true;
|
||||
uint read = ipu_fifo.out.write((u32*)decoder.GetIpuDataPtr(), decoder.ipu0_data);
|
||||
decoder.AdvanceIpuDataBy(read);
|
||||
|
||||
@@ -1577,6 +1588,7 @@ __fi static bool mpeg2_slice()
|
||||
break;
|
||||
}
|
||||
|
||||
ready_to_decode = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -149,12 +149,6 @@ void IPU1dma()
|
||||
}
|
||||
}
|
||||
|
||||
if (ipuRegs.ctrl.BUSY && !CommandExecuteQueued)
|
||||
{
|
||||
CommandExecuteQueued = true;
|
||||
CPU_INT(IPU_PROCESS, totalqwc * BIAS);
|
||||
}
|
||||
|
||||
IPU_LOG("Completed Call IPU1 DMA QWC Remaining %x Finished %d In Progress %d tadr %x", ipu1ch.qwc, IPU1Status.DMAFinished, IPU1Status.InProgress, ipu1ch.tadr);
|
||||
}
|
||||
|
||||
@@ -198,13 +192,14 @@ void IPU0dma()
|
||||
dmacRegs.stadr.ADDR = ipu0ch.madr;
|
||||
}
|
||||
|
||||
IPU_INT_FROM( readsize * BIAS );
|
||||
if (!ipu0ch.qwc)
|
||||
IPU_INT_FROM(readsize * BIAS);
|
||||
|
||||
if (ipuRegs.ctrl.BUSY && !CommandExecuteQueued)
|
||||
{
|
||||
CommandExecuteQueued = true;
|
||||
CommandExecuteQueued = false;
|
||||
CPU_SET_DMASTALL(DMAC_FROM_IPU, true);
|
||||
CPU_INT(IPU_PROCESS, readsize * BIAS);
|
||||
IPUProcessInterrupt();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3854,17 +3854,14 @@ void FullscreenUI::DrawControllerSettingsPage()
|
||||
.c_str());
|
||||
|
||||
const char* section = sections[global_slot];
|
||||
const std::string type(bsi->GetStringValue(section, "Type", Pad::GetDefaultPadType(global_slot)));
|
||||
const Pad::ControllerInfo* ci = Pad::GetControllerInfo(type);
|
||||
const Pad::ControllerInfo* ci = Pad::GetConfigControllerType(*bsi, section, global_slot);
|
||||
if (MenuButton(ICON_FA_GAMEPAD " Controller Type", ci ? ci->display_name : "Unknown"))
|
||||
{
|
||||
const std::vector<std::pair<const char*, const char*>> raw_options = Pad::GetControllerTypeNames();
|
||||
ImGuiFullscreen::ChoiceDialogOptions options;
|
||||
options.reserve(raw_options.size());
|
||||
for (auto& it : raw_options)
|
||||
{
|
||||
options.emplace_back(it.second, type == it.first);
|
||||
}
|
||||
options.emplace_back(it.second, (ci && ci->name == it.first));
|
||||
OpenChoiceDialog(fmt::format("Port {} Controller Type", global_slot + 1).c_str(), false, std::move(options),
|
||||
[game_settings = IsEditingGameSettings(bsi), section, raw_options = std::move(raw_options)](
|
||||
s32 index, const std::string& title, bool checked) {
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "Input/InputManager.h"
|
||||
#include "Input/InputSource.h"
|
||||
#include "SIO/Pad/Pad.h"
|
||||
#include "SIO/Sio.h"
|
||||
#include "USB/USB.h"
|
||||
#include "VMManager.h"
|
||||
|
||||
@@ -111,7 +112,7 @@ namespace InputManager
|
||||
static float ApplySingleBindingScale(float sensitivity, float deadzone, float value);
|
||||
|
||||
static void AddHotkeyBindings(SettingsInterface& si);
|
||||
static void AddPadBindings(SettingsInterface& si, u32 pad, const char* default_type);
|
||||
static void AddPadBindings(SettingsInterface& si, u32 pad);
|
||||
static void AddUSBBindings(SettingsInterface& si, u32 port);
|
||||
static void UpdateContinuedVibration();
|
||||
static void GenerateRelativeMouseEvents();
|
||||
@@ -380,7 +381,7 @@ void InputManager::AddBinding(const std::string_view& binding, const InputEventH
|
||||
|
||||
if (ibinding->num_keys == MAX_KEYS_PER_BINDING)
|
||||
{
|
||||
Console.WriteLn(fmt::format("Too many chord parts, max is {} ({})", MAX_KEYS_PER_BINDING, binding));
|
||||
Console.WriteLn(fmt::format("Too many chord parts, max is {} ({})", static_cast<u32>(MAX_KEYS_PER_BINDING), binding));
|
||||
ibinding.reset();
|
||||
break;
|
||||
}
|
||||
@@ -625,16 +626,25 @@ void InputManager::AddHotkeyBindings(SettingsInterface& si)
|
||||
}
|
||||
}
|
||||
|
||||
void InputManager::AddPadBindings(SettingsInterface& si, u32 pad_index, const char* default_type)
|
||||
void InputManager::AddPadBindings(SettingsInterface& si, u32 pad_index)
|
||||
{
|
||||
const std::string section(fmt::format("Pad{}", pad_index + 1));
|
||||
const std::string type(si.GetStringValue(section.c_str(), "Type", default_type));
|
||||
if (type.empty() || type == "None")
|
||||
const Pad::ControllerType type = EmuConfig.Pad.Ports[pad_index].Type;
|
||||
|
||||
// Don't bother checking macros/vibration if it's not a connected type.
|
||||
if (type == Pad::ControllerType::NotConnected)
|
||||
return;
|
||||
|
||||
// Or if it's a multitap port, and this multitap isn't enabled.
|
||||
if (sioPadIsMultitapSlot(pad_index))
|
||||
{
|
||||
const auto& [mt_port, mt_slot] = sioConvertPadToPortAndSlot(pad_index);
|
||||
if (!EmuConfig.Pad.IsMultitapPortEnabled(mt_port))
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string section = Pad::GetConfigSection(pad_index);
|
||||
const Pad::ControllerInfo* cinfo = Pad::GetControllerInfo(type);
|
||||
if (!cinfo)
|
||||
return;
|
||||
pxAssert(cinfo);
|
||||
|
||||
for (const InputBindingInfo& bi : cinfo->bindings)
|
||||
{
|
||||
@@ -1291,7 +1301,7 @@ void InputManager::ReloadBindings(SettingsInterface& si, SettingsInterface& bind
|
||||
// If there's an input profile, we load pad bindings from it alone, rather than
|
||||
// falling back to the base configuration.
|
||||
for (u32 pad = 0; pad < Pad::NUM_CONTROLLER_PORTS; pad++)
|
||||
AddPadBindings(binding_si, pad, Pad::GetDefaultPadType(pad));
|
||||
AddPadBindings(binding_si, pad);
|
||||
|
||||
constexpr float ui_ctrl_range = 100.0f;
|
||||
constexpr float pointer_sensitivity = 0.05f;
|
||||
|
||||
@@ -551,7 +551,7 @@ static void intExecute()
|
||||
eeloadHook();
|
||||
if (VMManager::Internal::IsFastBootInProgress())
|
||||
{
|
||||
// See comments on this code in iR5900-32.cpp's recRecompile()
|
||||
// See comments on this code in iR5900.cpp's recRecompile()
|
||||
const u32 typeAexecjump = memRead32(EELOAD_START + 0x470);
|
||||
const u32 typeBexecjump = memRead32(EELOAD_START + 0x5B0);
|
||||
const u32 typeCexecjump = memRead32(EELOAD_START + 0x618);
|
||||
|
||||
+9
-30
@@ -57,9 +57,6 @@ namespace MTGS
|
||||
}
|
||||
};
|
||||
|
||||
static bool TryOpenGS();
|
||||
static void CloseGS();
|
||||
|
||||
static void ThreadEntryPoint();
|
||||
static void MainLoop();
|
||||
|
||||
@@ -176,7 +173,8 @@ void MTGS::ThreadEntryPoint()
|
||||
}
|
||||
|
||||
// try initializing.. this could fail
|
||||
const bool opened = TryOpenGS();
|
||||
std::memcpy(RingBuffer.Regs, PS2MEM_GS, sizeof(PS2MEM_GS));
|
||||
const bool opened = GSopen(EmuConfig.GS, EmuConfig.GS.Renderer, RingBuffer.Regs);
|
||||
s_open_flag.store(opened, std::memory_order_release);
|
||||
|
||||
// notify emu thread that we finished opening (or failed)
|
||||
@@ -195,7 +193,7 @@ void MTGS::ThreadEntryPoint()
|
||||
// when we come back here, it's because we closed (or shutdown)
|
||||
// that means the emu thread should be blocked, waiting for us to be done
|
||||
pxAssertRel(!s_open_flag.load(std::memory_order_relaxed), "Open flag is clear on close");
|
||||
CloseGS();
|
||||
GSclose();
|
||||
s_open_or_close_done.Post();
|
||||
|
||||
// we need to reset sem_event here, because MainLoop() kills it.
|
||||
@@ -313,17 +311,6 @@ union PacketTagType
|
||||
};
|
||||
};
|
||||
|
||||
bool MTGS::TryOpenGS()
|
||||
{
|
||||
std::memcpy(RingBuffer.Regs, PS2MEM_GS, sizeof(PS2MEM_GS));
|
||||
|
||||
if (!GSopen(EmuConfig.GS, EmuConfig.GS.Renderer, RingBuffer.Regs))
|
||||
return false;
|
||||
|
||||
GSSetGameCRC(VMManager::GetDiscCRC());
|
||||
return true;
|
||||
}
|
||||
|
||||
void MTGS::MainLoop()
|
||||
{
|
||||
// Threading info: run in MTGS thread
|
||||
@@ -554,10 +541,6 @@ void MTGS::MainLoop()
|
||||
}
|
||||
break;
|
||||
|
||||
case Command::CRC:
|
||||
GSSetGameCRC(tag.data[0]);
|
||||
break;
|
||||
|
||||
case Command::InitAndReadFIFO:
|
||||
MTGS_LOG("(MTGS Packet Read) ringtype=Fifo2, size=%d", tag.data[0]);
|
||||
GSInitAndReadFIFO((u8*)tag.pointer, tag.data[0]);
|
||||
@@ -623,11 +606,6 @@ void MTGS::MainLoop()
|
||||
s_sem_event.Kill();
|
||||
}
|
||||
|
||||
void MTGS::CloseGS()
|
||||
{
|
||||
GSclose();
|
||||
}
|
||||
|
||||
// Waits for the GS to empty out the entire ring buffer contents.
|
||||
// If syncRegs, then writes pcsx2's gs regs to MTGS's internal copy
|
||||
// If weakWait, then this function is allowed to exit after MTGS finished a path1 packet
|
||||
@@ -894,11 +872,6 @@ void MTGS::SendPointerPacket(Command type, u32 data0, void* data1)
|
||||
_FinishSimplePacket();
|
||||
}
|
||||
|
||||
void MTGS::SendGameCRC(u32 crc)
|
||||
{
|
||||
SendSimplePacket(Command::CRC, crc, 0, 0);
|
||||
}
|
||||
|
||||
bool MTGS::WaitForOpen()
|
||||
{
|
||||
if (IsOpen())
|
||||
@@ -956,6 +929,12 @@ void MTGS::RunOnGSThread(AsyncCallType func)
|
||||
SetEvent();
|
||||
}
|
||||
|
||||
void MTGS::GameChanged()
|
||||
{
|
||||
pxAssertRel(IsOpen(), "MTGS is running");
|
||||
RunOnGSThread(GSGameChanged);
|
||||
}
|
||||
|
||||
void MTGS::ApplySettings()
|
||||
{
|
||||
pxAssertRel(IsOpen(), "MTGS is running");
|
||||
|
||||
+1
-2
@@ -41,7 +41,6 @@ namespace MTGS
|
||||
Freeze,
|
||||
Reset, // issues a GSreset() command.
|
||||
SoftReset, // issues a soft reset for the GIF
|
||||
CRC,
|
||||
GSPacket,
|
||||
MTVUGSPacket,
|
||||
InitAndReadFIFO,
|
||||
@@ -71,7 +70,6 @@ namespace MTGS
|
||||
void WaitGS(bool syncRegs = true, bool weakWait = false, bool isMTVU = false);
|
||||
void ResetGS(bool hardware_reset);
|
||||
|
||||
void SendGameCRC(u32 crc);
|
||||
bool WaitForOpen();
|
||||
void WaitForClose();
|
||||
void Freeze(FreezeAction mode, FreezeData& data);
|
||||
@@ -80,6 +78,7 @@ namespace MTGS
|
||||
void InitAndReadFIFO(u8* mem, u32 qwc);
|
||||
|
||||
void RunOnGSThread(AsyncCallType func);
|
||||
void GameChanged();
|
||||
void ApplySettings();
|
||||
void ResizeDisplayWindow(int width, int height, float scale);
|
||||
void UpdateDisplayWindow();
|
||||
|
||||
+78
-7
@@ -24,6 +24,7 @@
|
||||
#include "GS.h"
|
||||
#include "CDVD/CDVDcommon.h"
|
||||
#include "SIO/Memcard/MemoryCardFile.h"
|
||||
#include "SIO/Pad/Pad.h"
|
||||
#include "USB/USB.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -1347,6 +1348,74 @@ bool Pcsx2Config::USBOptions::operator!=(const USBOptions& right) const
|
||||
return !this->operator==(right);
|
||||
}
|
||||
|
||||
Pcsx2Config::PadOptions::PadOptions()
|
||||
{
|
||||
for (u32 i = 0; i < static_cast<u32>(Ports.size()); i++)
|
||||
{
|
||||
Port& port = Ports[i];
|
||||
port.Type = Pad::GetDefaultPadType(i);
|
||||
}
|
||||
|
||||
bitset = 0;
|
||||
}
|
||||
|
||||
void Pcsx2Config::PadOptions::LoadSave(SettingsWrapper& wrap)
|
||||
{
|
||||
for (u32 i = 0; i < static_cast<u32>(Ports.size()); i++)
|
||||
{
|
||||
Port& port = Ports[i];
|
||||
|
||||
std::string section = Pad::GetConfigSection(i);
|
||||
std::string type_name = Pad::GetControllerInfo(port.Type)->name;
|
||||
wrap.Entry(section.c_str(), "Type", type_name, type_name);
|
||||
|
||||
if (wrap.IsLoading())
|
||||
{
|
||||
const Pad::ControllerInfo* cinfo = Pad::GetControllerInfoByName(type_name);
|
||||
if (cinfo)
|
||||
{
|
||||
port.Type = cinfo->type;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Error(fmt::format("Invalid controller type {} specified in config, disconnecting.", type_name));
|
||||
port.Type = Pad::ControllerType::NotConnected;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsWrapSection("Pad");
|
||||
SettingsWrapBitBoolEx(MultitapPort0_Enabled, "MultitapPort1");
|
||||
SettingsWrapBitBoolEx(MultitapPort1_Enabled, "MultitapPort2");
|
||||
}
|
||||
|
||||
|
||||
bool Pcsx2Config::PadOptions::operator==(const PadOptions& right) const
|
||||
{
|
||||
for (u32 i = 0; i < static_cast<u32>(Ports.size()); i++)
|
||||
{
|
||||
if (!OpEqu(Ports[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Pcsx2Config::PadOptions::operator!=(const PadOptions& right) const
|
||||
{
|
||||
return !this->operator==(right);
|
||||
}
|
||||
|
||||
bool Pcsx2Config::PadOptions::Port::operator==(const PadOptions::Port& right) const
|
||||
{
|
||||
return OpEqu(Type);
|
||||
}
|
||||
|
||||
bool Pcsx2Config::PadOptions::Port::operator!=(const PadOptions::Port& right) const
|
||||
{
|
||||
return !this->operator==(right);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_ACHIEVEMENTS
|
||||
|
||||
Pcsx2Config::AchievementsOptions::AchievementsOptions()
|
||||
@@ -1360,6 +1429,7 @@ Pcsx2Config::AchievementsOptions::AchievementsOptions()
|
||||
Notifications = true;
|
||||
SoundEffects = true;
|
||||
PrimedIndicators = true;
|
||||
NotificationsDuration = 5;
|
||||
}
|
||||
|
||||
void Pcsx2Config::AchievementsOptions::LoadSave(SettingsWrapper& wrap)
|
||||
@@ -1375,6 +1445,13 @@ void Pcsx2Config::AchievementsOptions::LoadSave(SettingsWrapper& wrap)
|
||||
SettingsWrapBitBool(Notifications);
|
||||
SettingsWrapBitBool(SoundEffects);
|
||||
SettingsWrapBitBool(PrimedIndicators);
|
||||
SettingsWrapBitfield(NotificationsDuration);
|
||||
|
||||
if (wrap.IsLoading())
|
||||
{
|
||||
//Clamp in case setting was updated manually using the INI
|
||||
NotificationsDuration = std::clamp(NotificationsDuration, 3, 10);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1438,7 +1515,6 @@ void Pcsx2Config::LoadSave(SettingsWrapper& wrap)
|
||||
SettingsWrapBitBool(SaveStateOnShutdown);
|
||||
SettingsWrapBitBool(EnableDiscordPresence);
|
||||
SettingsWrapBitBool(InhibitScreensaver);
|
||||
SettingsWrapBitBool(ConsoleToStdio);
|
||||
SettingsWrapBitBool(HostFs);
|
||||
|
||||
SettingsWrapBitBool(BackupSavestate);
|
||||
@@ -1461,6 +1537,7 @@ void Pcsx2Config::LoadSave(SettingsWrapper& wrap)
|
||||
Debugger.LoadSave(wrap);
|
||||
Trace.LoadSave(wrap);
|
||||
USB.LoadSave(wrap);
|
||||
Pad.LoadSave(wrap);
|
||||
|
||||
#ifdef ENABLE_ACHIEVEMENTS
|
||||
Achievements.LoadSave(wrap);
|
||||
@@ -1510,12 +1587,6 @@ void Pcsx2Config::LoadSaveMemcards(SettingsWrapper& wrap)
|
||||
}
|
||||
}
|
||||
|
||||
bool Pcsx2Config::MultitapEnabled(uint port) const
|
||||
{
|
||||
pxAssert(port < 2);
|
||||
return (port == 0) ? MultitapPort0_Enabled : MultitapPort1_Enabled;
|
||||
}
|
||||
|
||||
std::string Pcsx2Config::FullpathToBios() const
|
||||
{
|
||||
std::string ret;
|
||||
|
||||
@@ -272,9 +272,9 @@ void FileMemoryCard::Open()
|
||||
|
||||
if (FileMcd_IsMultitapSlot(slot))
|
||||
{
|
||||
if (!EmuConfig.MultitapPort0_Enabled && (FileMcd_GetMtapPort(slot) == 0))
|
||||
if (!EmuConfig.Pad.MultitapPort0_Enabled && (FileMcd_GetMtapPort(slot) == 0))
|
||||
continue;
|
||||
if (!EmuConfig.MultitapPort1_Enabled && (FileMcd_GetMtapPort(slot) == 1))
|
||||
if (!EmuConfig.Pad.MultitapPort1_Enabled && (FileMcd_GetMtapPort(slot) == 1))
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -888,6 +888,11 @@ std::vector<AvailableMcdInfo> FileMcd_GetAvailableCards(bool include_in_use_card
|
||||
continue;
|
||||
}
|
||||
|
||||
// We only want relevant file types.
|
||||
if (!(StringUtil::EndsWith(fd.FileName, ".ps2") || StringUtil::EndsWith(fd.FileName, ".mcr") ||
|
||||
StringUtil::EndsWith(fd.FileName, ".mcd") || StringUtil::EndsWith(fd.FileName, ".bin")))
|
||||
continue;
|
||||
|
||||
if (fd.Attributes & FILESYSTEM_FILE_ATTRIBUTE_DIRECTORY)
|
||||
{
|
||||
if (!IsMemoryCardFolder(fd.FileName))
|
||||
|
||||
@@ -2021,6 +2021,7 @@ std::FILE* FileAccessHelper::Open(const std::string_view& folderName, MemoryCard
|
||||
MemoryCardFileHandleStructure handleStruct;
|
||||
handleStruct.fileHandle = file;
|
||||
handleStruct.fileRef = fileRef;
|
||||
handleStruct.hostFilePath = std::move(filename);
|
||||
m_files.emplace(std::move(internalPath), std::move(handleStruct));
|
||||
|
||||
if (writeMetadata)
|
||||
|
||||
+66
-94
@@ -25,6 +25,7 @@
|
||||
|
||||
#include "IconsFontAwesome5.h"
|
||||
|
||||
#include "common/Assertions.h"
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/Path.h"
|
||||
#include "common/SettingsInterface.h"
|
||||
@@ -46,11 +47,13 @@ namespace Pad
|
||||
bool trigger_state; ///< Whether the macro button is active.
|
||||
};
|
||||
|
||||
static const char* GetControllerTypeName(Pad::ControllerType type);
|
||||
|
||||
static std::unique_ptr<PadBase> CreatePad(u8 unifiedSlot, Pad::ControllerType controllerType);
|
||||
static PadBase* ChangePadType(u8 unifiedSlot, Pad::ControllerType controllerType);
|
||||
|
||||
void LoadMacroButtonConfig(
|
||||
const SettingsInterface& si, u32 pad, const std::string_view& type, const std::string& section);
|
||||
const SettingsInterface& si, u32 pad, const ControllerInfo* ci, const std::string& section);
|
||||
static void ApplyMacroButton(u32 controller, const MacroButton& mb);
|
||||
|
||||
static std::array<std::array<MacroButton, NUM_MACRO_BUTTONS_PER_CONTROLLER>, NUM_CONTROLLER_PORTS> s_macro_buttons;
|
||||
@@ -73,40 +76,35 @@ const char* Pad::ControllerInfo::GetLocalizedName() const
|
||||
return Host::TranslateToCString("Pad", display_name);
|
||||
}
|
||||
|
||||
std::optional<u32> Pad::ControllerInfo::GetBindIndex(const std::string_view& name) const
|
||||
{
|
||||
for (u32 i = 0; i < static_cast<u32>(bindings.size()); i++)
|
||||
{
|
||||
if (name == bindings[i].name)
|
||||
return i;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void Pad::LoadConfig(const SettingsInterface& si)
|
||||
{
|
||||
s_macro_buttons = {};
|
||||
|
||||
EmuConfig.MultitapPort0_Enabled = si.GetBoolValue("Pad", "MultitapPort1", false);
|
||||
EmuConfig.MultitapPort1_Enabled = si.GetBoolValue("Pad", "MultitapPort2", false);
|
||||
|
||||
// This is where we would load controller types, if onepad supported them.
|
||||
for (u32 i = 0; i < Pad::NUM_CONTROLLER_PORTS; i++)
|
||||
{
|
||||
const std::string section(GetConfigSection(i));
|
||||
const std::string type(si.GetStringValue(section.c_str(), "Type", GetDefaultPadType(i)));
|
||||
const ControllerInfo* ci = GetControllerInfo(type);
|
||||
PadBase* pad = Pad::GetPad(i);
|
||||
const std::string section = GetConfigSection(i);
|
||||
const ControllerInfo* ci = GetControllerInfo(EmuConfig.Pad.Ports[i].Type);
|
||||
pxAssert(ci);
|
||||
|
||||
// If a pad is not yet constructed, at minimum place a NotConnected pad in the slot.
|
||||
// Do not abort the for loop - If there pad settings, we want those to be applied to the slot.
|
||||
if (!pad)
|
||||
{
|
||||
pad = Pad::ChangePadType(i, Pad::ControllerType::NotConnected);
|
||||
}
|
||||
|
||||
if (!ci)
|
||||
{
|
||||
pad = Pad::ChangePadType(i, Pad::ControllerType::NotConnected);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
const Pad::ControllerType oldType = pad->GetType();
|
||||
|
||||
if (ci->type != oldType)
|
||||
PadBase* pad = Pad::GetPad(i);
|
||||
if (!pad || pad->GetType() != ci->type)
|
||||
{
|
||||
pad = Pad::ChangePadType(i, ci->type);
|
||||
pxAssert(pad);
|
||||
}
|
||||
|
||||
const float axis_deadzone = si.GetFloatValue(section.c_str(), "Deadzone", Pad::DEFAULT_STICK_DEADZONE);
|
||||
@@ -133,13 +131,13 @@ void Pad::LoadConfig(const SettingsInterface& si)
|
||||
const int invert_r = si.GetIntValue(section.c_str(), "InvertR", 0);
|
||||
pad->SetAnalogInvertL((invert_l & 1) != 0, (invert_l & 2) != 0);
|
||||
pad->SetAnalogInvertR((invert_r & 1) != 0, (invert_r & 2) != 0);
|
||||
LoadMacroButtonConfig(si, i, type, section);
|
||||
LoadMacroButtonConfig(si, i, ci, section);
|
||||
}
|
||||
}
|
||||
|
||||
const char* Pad::GetDefaultPadType(u32 pad)
|
||||
Pad::ControllerType Pad::GetDefaultPadType(u32 pad)
|
||||
{
|
||||
return (pad == 0) ? "DualShock2" : "None";
|
||||
return (pad == 0) ? ControllerType::DualShock2 : ControllerType::NotConnected;
|
||||
}
|
||||
|
||||
void Pad::SetDefaultControllerConfig(SettingsInterface& si)
|
||||
@@ -166,36 +164,32 @@ void Pad::SetDefaultControllerConfig(SettingsInterface& si)
|
||||
// PCSX2 Controller Settings - Default pad types and parameters.
|
||||
for (u32 i = 0; i < Pad::NUM_CONTROLLER_PORTS; i++)
|
||||
{
|
||||
const char* type = GetDefaultPadType(i);
|
||||
const std::string section(GetConfigSection(i));
|
||||
const std::string section = GetConfigSection(i);
|
||||
const ControllerInfo* ci = GetControllerInfo(GetDefaultPadType(i));
|
||||
si.ClearSection(section.c_str());
|
||||
si.SetStringValue(section.c_str(), "Type", type);
|
||||
si.SetStringValue(section.c_str(), "Type", ci->name);
|
||||
|
||||
const ControllerInfo* ci = GetControllerInfo(type);
|
||||
if (ci)
|
||||
for (const SettingInfo& csi : ci->settings)
|
||||
{
|
||||
for (const SettingInfo& csi : ci->settings)
|
||||
switch (csi.type)
|
||||
{
|
||||
switch (csi.type)
|
||||
{
|
||||
case SettingInfo::Type::Boolean:
|
||||
si.SetBoolValue(section.c_str(), csi.name, csi.BooleanDefaultValue());
|
||||
break;
|
||||
case SettingInfo::Type::Integer:
|
||||
case SettingInfo::Type::IntegerList:
|
||||
si.SetIntValue(section.c_str(), csi.name, csi.IntegerDefaultValue());
|
||||
break;
|
||||
case SettingInfo::Type::Float:
|
||||
si.SetFloatValue(section.c_str(), csi.name, csi.FloatDefaultValue());
|
||||
break;
|
||||
case SettingInfo::Type::String:
|
||||
case SettingInfo::Type::StringList:
|
||||
case SettingInfo::Type::Path:
|
||||
si.SetStringValue(section.c_str(), csi.name, csi.StringDefaultValue());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
case SettingInfo::Type::Boolean:
|
||||
si.SetBoolValue(section.c_str(), csi.name, csi.BooleanDefaultValue());
|
||||
break;
|
||||
case SettingInfo::Type::Integer:
|
||||
case SettingInfo::Type::IntegerList:
|
||||
si.SetIntValue(section.c_str(), csi.name, csi.IntegerDefaultValue());
|
||||
break;
|
||||
case SettingInfo::Type::Float:
|
||||
si.SetFloatValue(section.c_str(), csi.name, csi.FloatDefaultValue());
|
||||
break;
|
||||
case SettingInfo::Type::String:
|
||||
case SettingInfo::Type::StringList:
|
||||
case SettingInfo::Type::Path:
|
||||
si.SetStringValue(section.c_str(), csi.name, csi.StringDefaultValue());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -269,7 +263,7 @@ const Pad::ControllerInfo* Pad::GetControllerInfo(Pad::ControllerType type)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const Pad::ControllerInfo* Pad::GetControllerInfo(const std::string_view& name)
|
||||
const Pad::ControllerInfo* Pad::GetControllerInfoByName(const std::string_view& name)
|
||||
{
|
||||
for (const ControllerInfo* info : s_controller_info)
|
||||
{
|
||||
@@ -296,31 +290,17 @@ const std::vector<std::pair<const char*, const char*>> Pad::GetControllerTypeNam
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<std::string> Pad::GetControllerBinds(const std::string_view& type)
|
||||
const Pad::ControllerInfo* Pad::GetConfigControllerType(const SettingsInterface& si, const char* section, u32 port)
|
||||
{
|
||||
std::vector<std::string> ret;
|
||||
|
||||
const ControllerInfo* info = GetControllerInfo(type);
|
||||
if (info)
|
||||
{
|
||||
for (const InputBindingInfo& bi : info->bindings)
|
||||
{
|
||||
if (bi.bind_type == InputBindingInfo::Type::Unknown || bi.bind_type == InputBindingInfo::Type::Motor)
|
||||
continue;
|
||||
|
||||
ret.emplace_back(bi.name);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
return GetControllerInfoByName(
|
||||
si.GetStringValue(section, "Type", GetControllerInfo(GetDefaultPadType(port))->name).c_str());
|
||||
}
|
||||
|
||||
void Pad::ClearPortBindings(SettingsInterface& si, u32 port)
|
||||
{
|
||||
const std::string section(StringUtil::StdStringFromFormat("Pad%u", port + 1));
|
||||
const std::string type(si.GetStringValue(section.c_str(), "Type", GetDefaultPadType(port)));
|
||||
|
||||
const ControllerInfo* info = GetControllerInfo(type);
|
||||
// Why don't these just access EmuConfig? Input Profiles.
|
||||
const std::string section = GetConfigSection(port);
|
||||
const ControllerInfo* info = GetConfigControllerType(si, section.c_str(), port);
|
||||
if (!info)
|
||||
return;
|
||||
|
||||
@@ -351,14 +331,13 @@ void Pad::CopyConfiguration(SettingsInterface* dest_si, const SettingsInterface&
|
||||
|
||||
for (u32 port = 0; port < Pad::NUM_CONTROLLER_PORTS; port++)
|
||||
{
|
||||
const std::string section(fmt::format("Pad{}", port + 1));
|
||||
const std::string type(src_si.GetStringValue(section.c_str(), "Type", GetDefaultPadType(port)));
|
||||
const std::string section = GetConfigSection(port);
|
||||
if (copy_pad_config)
|
||||
dest_si->SetStringValue(section.c_str(), "Type", type.c_str());
|
||||
dest_si->CopyStringValue(src_si, section.c_str(), "Type");
|
||||
|
||||
const ControllerInfo* info = GetControllerInfo(type);
|
||||
const Pad::ControllerInfo* info = GetConfigControllerType(src_si, section.c_str(), port);
|
||||
if (!info)
|
||||
return;
|
||||
continue;
|
||||
|
||||
if (copy_pad_bindings)
|
||||
{
|
||||
@@ -451,9 +430,8 @@ static u32 TryMapGenericMapping(SettingsInterface& si, const std::string& sectio
|
||||
bool Pad::MapController(SettingsInterface& si, u32 controller,
|
||||
const std::vector<std::pair<GenericInputBinding, std::string>>& mapping)
|
||||
{
|
||||
const std::string section(StringUtil::StdStringFromFormat("Pad%u", controller + 1));
|
||||
const std::string type(si.GetStringValue(section.c_str(), "Type", GetDefaultPadType(controller)));
|
||||
const ControllerInfo* info = GetControllerInfo(type);
|
||||
const std::string section = GetConfigSection(controller);
|
||||
const ControllerInfo* info = GetConfigControllerType(si, section.c_str(), controller);
|
||||
if (!info)
|
||||
return false;
|
||||
|
||||
@@ -571,8 +549,8 @@ bool Pad::Freeze(StateWrapper& sw)
|
||||
"Controller port {}, slot {} has a {} connected, but the save state has a "
|
||||
"{}.\nLeaving the original controller type connected, but this may cause issues."),
|
||||
port, slot,
|
||||
Pad::GetControllerTypeName(pad ? pad->GetType() : Pad::ControllerType::NotConnected),
|
||||
Pad::GetControllerTypeName(type)));
|
||||
GetControllerTypeName(pad ? pad->GetType() : Pad::ControllerType::NotConnected),
|
||||
GetControllerTypeName(type)));
|
||||
|
||||
// Reset the transfer etc state of the pad, at least it has a better chance of surviving.
|
||||
if (pad)
|
||||
@@ -606,11 +584,8 @@ bool Pad::Freeze(StateWrapper& sw)
|
||||
}
|
||||
|
||||
|
||||
void Pad::LoadMacroButtonConfig(const SettingsInterface& si, u32 pad, const std::string_view& type, const std::string& section)
|
||||
void Pad::LoadMacroButtonConfig(const SettingsInterface& si, u32 pad, const ControllerInfo* ci, const std::string& section)
|
||||
{
|
||||
// lazily initialized
|
||||
std::vector<std::string> binds;
|
||||
|
||||
for (u32 i = 0; i < NUM_MACRO_BUTTONS_PER_CONTROLLER; i++)
|
||||
{
|
||||
std::string binds_string;
|
||||
@@ -618,9 +593,6 @@ void Pad::LoadMacroButtonConfig(const SettingsInterface& si, u32 pad, const std:
|
||||
continue;
|
||||
|
||||
const u32 frequency = si.GetUIntValue(section.c_str(), StringUtil::StdStringFromFormat("Macro%uFrequency", i + 1).c_str(), 0u);
|
||||
if (binds.empty())
|
||||
binds = GetControllerBinds(type);
|
||||
|
||||
const float pressure = si.GetFloatValue(section.c_str(), fmt::format("Macro{}Pressure", i + 1).c_str(), 1.0f);
|
||||
|
||||
// convert binds
|
||||
@@ -630,14 +602,14 @@ void Pad::LoadMacroButtonConfig(const SettingsInterface& si, u32 pad, const std:
|
||||
continue;
|
||||
for (const std::string_view& button : buttons_split)
|
||||
{
|
||||
auto it = std::find(binds.begin(), binds.end(), button);
|
||||
if (it == binds.end())
|
||||
std::optional<u32> bind_index = ci->GetBindIndex(button);
|
||||
if (!bind_index.has_value())
|
||||
{
|
||||
Console.Error("Invalid bind '%.*s' in macro button %u for pad %u", static_cast<int>(button.size()), button.data(), pad, i);
|
||||
Console.Error(fmt::format("Invalid bind '{}' in macro button {} for pad {}", button, i, pad));
|
||||
continue;
|
||||
}
|
||||
|
||||
bind_indices.push_back(static_cast<u32>(std::distance(binds.begin(), it)));
|
||||
bind_indices.push_back(bind_index.value());
|
||||
}
|
||||
if (bind_indices.empty())
|
||||
continue;
|
||||
|
||||
+6
-6
@@ -32,7 +32,7 @@ namespace Pad
|
||||
void Shutdown();
|
||||
|
||||
// Returns the default type for the specified port.
|
||||
const char* GetDefaultPadType(u32 pad);
|
||||
Pad::ControllerType GetDefaultPadType(u32 pad);
|
||||
|
||||
// Reloads configuration.
|
||||
void LoadConfig(const SettingsInterface& si);
|
||||
@@ -51,13 +51,13 @@ namespace Pad
|
||||
// Returns a list of controller type names. Pair of [name, display name].
|
||||
const std::vector<std::pair<const char*, const char*>> GetControllerTypeNames();
|
||||
|
||||
// Returns the list of binds for the specified controller type.
|
||||
std::vector<std::string> GetControllerBinds(const std::string_view& type);
|
||||
|
||||
// Returns general information for the specified controller type.
|
||||
const ControllerInfo* GetControllerInfo(Pad::ControllerType type);
|
||||
const ControllerInfo* GetControllerInfo(const std::string_view& name);
|
||||
const char* GetControllerTypeName(Pad::ControllerType type);
|
||||
const ControllerInfo* GetControllerInfoByName(const std::string_view& name);
|
||||
|
||||
// Returns controller info based on the type in the config.
|
||||
// Needed because we can't just read EmuConfig when altering input profiles.
|
||||
const ControllerInfo* GetConfigControllerType(const SettingsInterface& si, const char* section, u32 port);
|
||||
|
||||
// Performs automatic controller mapping with the provided list of generic mappings.
|
||||
bool MapController(
|
||||
|
||||
@@ -132,7 +132,7 @@ u8 PadDualshock2::ButtonQuery(u8 commandByte)
|
||||
{
|
||||
case 8:
|
||||
g_Sio0.SetAcknowledge(false);
|
||||
return 0x5a;
|
||||
return 0x00;
|
||||
default:
|
||||
return 0x00;
|
||||
}
|
||||
@@ -142,6 +142,8 @@ u8 PadDualshock2::ButtonQuery(u8 commandByte)
|
||||
u8 PadDualshock2::Poll(u8 commandByte)
|
||||
{
|
||||
const u32 buttons = GetButtons();
|
||||
u8 largeMotor = 0x00;
|
||||
u8 smallMotor = 0x00;
|
||||
|
||||
switch (commandBytesReceived)
|
||||
{
|
||||
@@ -150,12 +152,38 @@ u8 PadDualshock2::Poll(u8 commandByte)
|
||||
return (buttons >> 8) & 0xff;
|
||||
case 4:
|
||||
this->vibrationMotors[1] = commandByte;
|
||||
|
||||
// Apply the vibration mapping to the motors
|
||||
switch (this->largeMotorLastConfig)
|
||||
{
|
||||
case 0x00:
|
||||
largeMotor = this->vibrationMotors[0];
|
||||
break;
|
||||
case 0x01:
|
||||
largeMotor = this->vibrationMotors[1];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (this->smallMotorLastConfig)
|
||||
{
|
||||
case 0x00:
|
||||
smallMotor = this->vibrationMotors[0];
|
||||
break;
|
||||
case 0x01:
|
||||
smallMotor = this->vibrationMotors[1];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Order is reversed here - SetPadVibrationIntensity takes large motor first, then small. PS2 orders small motor first, large motor second.
|
||||
InputManager::SetPadVibrationIntensity(this->unifiedSlot,
|
||||
std::min(static_cast<float>(this->vibrationMotors[1]) * GetVibrationScale(1) * (1.0f / 255.0f), 1.0f),
|
||||
std::min(static_cast<float>(largeMotor) * GetVibrationScale(1) * (1.0f / 255.0f), 1.0f),
|
||||
// Small motor on the PS2 is either on full power or zero power, it has no variable speed. If the game supplies any value here at all,
|
||||
// the pad in turn supplies full power to the motor, or no power at all if zero.
|
||||
std::min(static_cast<float>((this->vibrationMotors[0] ? 0xff : 0)) * GetVibrationScale(0) * (1.0f / 255.0f), 1.0f)
|
||||
std::min(static_cast<float>((smallMotor ? 0xff : 0)) * GetVibrationScale(0) * (1.0f / 255.0f), 1.0f)
|
||||
);
|
||||
|
||||
// PS1 mode: If the controller is still in digital mode, it is time to stop acknowledging.
|
||||
@@ -349,6 +377,8 @@ u8 PadDualshock2::Constant2(u8 commandByte)
|
||||
{
|
||||
case 5:
|
||||
return 0x02;
|
||||
case 7:
|
||||
return 0x01;
|
||||
case 8:
|
||||
g_Sio0.SetAcknowledge(false);
|
||||
return 0x00;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include "Config.h"
|
||||
|
||||
#include <optional>
|
||||
#include <span>
|
||||
|
||||
namespace Pad
|
||||
@@ -95,12 +96,15 @@ namespace Pad
|
||||
std::span<const SettingInfo> settings;
|
||||
VibrationCapabilities vibration_caps;
|
||||
|
||||
// Returns localized controller type name.
|
||||
/// Returns localized controller type name.
|
||||
const char* GetLocalizedName() const;
|
||||
|
||||
/// Returns the index of the specified binding point, by name.
|
||||
std::optional<u32> GetBindIndex(const std::string_view& name) const;
|
||||
};
|
||||
|
||||
// Total number of pad ports, across both multitaps.
|
||||
static constexpr u32 NUM_CONTROLLER_PORTS = 8;
|
||||
static constexpr u32 NUM_CONTROLLER_PORTS = Pcsx2Config::PadOptions::NUM_PORTS;
|
||||
|
||||
// Default stick deadzone/sensitivity.
|
||||
static constexpr float DEFAULT_STICK_DEADZONE = 0.0f;
|
||||
|
||||
+1
-1
@@ -166,7 +166,7 @@ void Sio2::Multitap()
|
||||
{
|
||||
g_Sio2FifoOut.push_back(0x00);
|
||||
|
||||
const bool multitapEnabled = (port == 0 && EmuConfig.MultitapPort0_Enabled) || (port == 1 && EmuConfig.MultitapPort1_Enabled);
|
||||
const bool multitapEnabled = EmuConfig.Pad.IsMultitapPortEnabled(port);
|
||||
SetRecv1(multitapEnabled ? Recv1::CONNECTED : Recv1::DISCONNECTED);
|
||||
|
||||
if (multitapEnabled)
|
||||
|
||||
+30
-5
@@ -40,6 +40,7 @@
|
||||
#include "VMManager.h"
|
||||
#include "VUmicro.h"
|
||||
#include "ps2/BiosTools.h"
|
||||
#include "svnrev.h"
|
||||
|
||||
#include "common/Error.h"
|
||||
#include "common/FileSystem.h"
|
||||
@@ -336,6 +337,7 @@ void memLoadingState::FreezeMem( void* data, int size )
|
||||
static const char* EntryFilename_StateVersion = "PCSX2 Savestate Version.id";
|
||||
static const char* EntryFilename_Screenshot = "Screenshot.png";
|
||||
static const char* EntryFilename_InternalStructures = "PCSX2 Internal Structures.dat";
|
||||
static constexpr u32 STATE_PCSX2_VERSION_SIZE = 32;
|
||||
|
||||
struct SysState_Component
|
||||
{
|
||||
@@ -938,9 +940,26 @@ static bool SaveState_AddToZip(zip_t* zf, ArchiveEntryList* srclist, SaveStateSc
|
||||
|
||||
// version indicator
|
||||
{
|
||||
zip_source_t* const zs = zip_source_buffer(zf, &g_SaveVersion, sizeof(g_SaveVersion), 0);
|
||||
struct VersionIndicator
|
||||
{
|
||||
u32 save_version;
|
||||
char version[STATE_PCSX2_VERSION_SIZE];
|
||||
};
|
||||
|
||||
VersionIndicator* vi = static_cast<VersionIndicator*>(std::malloc(sizeof(VersionIndicator)));
|
||||
vi->save_version = g_SaveVersion;
|
||||
#if GIT_TAGGED_COMMIT
|
||||
StringUtil::Strlcpy(vi->version, GIT_TAG, std::size(vi->version));
|
||||
#else
|
||||
StringUtil::Strlcpy(vi->version, "Unknown", std::size(vi->version));
|
||||
#endif
|
||||
|
||||
zip_source_t* const zs = zip_source_buffer(zf, vi, sizeof(*vi), 1);
|
||||
if (!zs)
|
||||
{
|
||||
std::free(vi);
|
||||
return false;
|
||||
}
|
||||
|
||||
// NOTE: Source should not be freed if successful.
|
||||
const s64 fi = zip_file_add(zf, EntryFilename_StateVersion, zs, ZIP_FL_ENC_UTF_8);
|
||||
@@ -1034,16 +1053,22 @@ static bool CheckVersion(const std::string& filename, zip_t* zf, Error* error)
|
||||
return false;
|
||||
}
|
||||
|
||||
char version_string[STATE_PCSX2_VERSION_SIZE];
|
||||
if (zip_fread(zff.get(), version_string, STATE_PCSX2_VERSION_SIZE) == STATE_PCSX2_VERSION_SIZE)
|
||||
version_string[STATE_PCSX2_VERSION_SIZE - 1] = 0;
|
||||
else
|
||||
StringUtil::Strlcpy(version_string, "Unknown", std::size(version_string));
|
||||
|
||||
// Major version mismatch. Means we can't load this savestate at all. Support for it
|
||||
// was removed entirely.
|
||||
// check for a "minor" version incompatibility; which happens if the savestate being loaded is a newer version
|
||||
// than the emulator recognizes. 99% chance that trying to load it will just corrupt emulation or crash.
|
||||
if (savever > g_SaveVersion || (savever >> 16) != (g_SaveVersion >> 16))
|
||||
{
|
||||
Error::SetString(error, fmt::format("The state is an unsupported version. (PCSX2 ver={:x}, state ver={:x}).\n"
|
||||
"Option 1: Download an older PCSX2 version from pcsx2.net and make a memcard save like on the physical PS2.\n"
|
||||
"Option 2: Delete the savestates.",
|
||||
g_SaveVersion, savever));
|
||||
Error::SetString(error, fmt::format(TRANSLATE_FS("SaveState","This savestate is an unsupported version and cannot be used.\n\n"
|
||||
"You can download PCSX2 {} from pcsx2.net and make a normal memory card save.\n"
|
||||
"Otherwise delete the savestate and do a fresh boot."),
|
||||
version_string));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,4 +15,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 = 29;
|
||||
static constexpr u32 SHADER_CACHE_VERSION = 30;
|
||||
|
||||
@@ -267,7 +267,7 @@ namespace usb_eyetoy
|
||||
return devList;
|
||||
}
|
||||
|
||||
static int v4l_open(std::string selectedDevice)
|
||||
static int v4l_open(const std::string& selectedDevice)
|
||||
{
|
||||
char dev_name[64];
|
||||
struct v4l2_capability cap;
|
||||
|
||||
@@ -123,7 +123,7 @@ namespace usb_eyetoy
|
||||
return devList;
|
||||
}
|
||||
|
||||
int DirectShow::InitializeDevice(std::wstring selectedDevice)
|
||||
int DirectShow::InitializeDevice(const std::wstring& selectedDevice)
|
||||
{
|
||||
|
||||
// Create the Capture Graph Builder.
|
||||
|
||||
@@ -92,7 +92,7 @@ namespace usb_eyetoy
|
||||
void SetCallback(DShowVideoCaptureCallback cb) { callbackhandler->SetCallback(cb); }
|
||||
bool Start();
|
||||
void Stop();
|
||||
int InitializeDevice(std::wstring selectedDevice);
|
||||
int InitializeDevice(const std::wstring& selectedDevice);
|
||||
|
||||
private:
|
||||
wil::unique_couninitialize_call dshowCoInitialize;
|
||||
|
||||
+5
-1
@@ -905,7 +905,7 @@ void VMManager::UpdateDiscDetails(bool booting)
|
||||
ReportGameChangeToHost();
|
||||
Achievements::GameChanged(s_disc_crc, s_current_crc);
|
||||
if (MTGS::IsOpen())
|
||||
MTGS::SendGameCRC(s_disc_crc);
|
||||
MTGS::GameChanged();
|
||||
ReloadPINE();
|
||||
UpdateDiscordPresence(Achievements::GetRichPresenceString());
|
||||
|
||||
@@ -1418,6 +1418,10 @@ void VMManager::Reset()
|
||||
#ifdef ENABLE_ACHIEVEMENTS
|
||||
if (!Achievements::OnReset())
|
||||
return;
|
||||
|
||||
// Re-enforce hardcode mode constraints if we're now enabling it.
|
||||
if (Achievements::ResetChallengeMode())
|
||||
ApplySettings();
|
||||
#endif
|
||||
|
||||
vu1Thread.WaitVU();
|
||||
|
||||
+2
-4
@@ -284,7 +284,6 @@
|
||||
<ClCompile Include="GS\GSBlock.cpp" />
|
||||
<ClCompile Include="GS\GSCapture.cpp" />
|
||||
<ClCompile Include="GS\GSClut.cpp" />
|
||||
<ClCompile Include="GS\GSCrc.cpp" />
|
||||
<ClCompile Include="GS\Renderers\Common\GSDevice.cpp" />
|
||||
<ClCompile Include="GS\Renderers\DX11\GSDevice11.cpp" />
|
||||
<ClCompile Include="GS\Renderers\OpenGL\GSDeviceOGL.cpp" />
|
||||
@@ -412,7 +411,7 @@
|
||||
<ClCompile Include="x86\iMMI.cpp" />
|
||||
<ClCompile Include="x86\iR5900Misc.cpp" />
|
||||
<ClCompile Include="x86\ir5900tables.cpp" />
|
||||
<ClCompile Include="x86\ix86-32\iR5900-32.cpp" />
|
||||
<ClCompile Include="x86\ix86-32\iR5900.cpp" />
|
||||
<ClCompile Include="x86\ix86-32\iR5900Arit.cpp" />
|
||||
<ClCompile Include="x86\ix86-32\iR5900AritImm.cpp" />
|
||||
<ClCompile Include="x86\ix86-32\iR5900Branch.cpp" />
|
||||
@@ -474,7 +473,7 @@
|
||||
<ClCompile Include="rdebug\deci2_ttyp.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="x86\ix86-32\iCore-32.cpp" />
|
||||
<ClCompile Include="x86\ix86-32\iCore.cpp" />
|
||||
<ClCompile Include="x86\iCore.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -626,7 +625,6 @@
|
||||
<ClInclude Include="GS\GSBlock.h" />
|
||||
<ClInclude Include="GS\GSCapture.h" />
|
||||
<ClInclude Include="GS\GSClut.h" />
|
||||
<ClInclude Include="GS\GSCrc.h" />
|
||||
<ClInclude Include="GS\Renderers\Common\GSDevice.h" />
|
||||
<ClInclude Include="GS\Renderers\DX11\GSDevice11.h" />
|
||||
<ClInclude Include="GS\Renderers\OpenGL\GSDeviceOGL.h" />
|
||||
|
||||
@@ -548,7 +548,7 @@
|
||||
<ClCompile Include="x86\ir5900tables.cpp">
|
||||
<Filter>System\Ps2\EmotionEngine\EE\Dynarec</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="x86\ix86-32\iR5900-32.cpp">
|
||||
<ClCompile Include="x86\ix86-32\iR5900.cpp">
|
||||
<Filter>System\Ps2\EmotionEngine\EE\Dynarec\ix86-32</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="x86\ix86-32\iR5900Arit.cpp">
|
||||
@@ -698,7 +698,7 @@
|
||||
<ClCompile Include="rdebug\deci2_ttyp.cpp">
|
||||
<Filter>System\Ps2\Debug\rdebug</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="x86\ix86-32\iCore-32.cpp">
|
||||
<ClCompile Include="x86\ix86-32\iCore.cpp">
|
||||
<Filter>System\Ps2\iCore</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="x86\iCore.cpp">
|
||||
@@ -995,9 +995,6 @@
|
||||
<ClCompile Include="GS\GSClut.cpp">
|
||||
<Filter>System\Ps2\GS</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GS\GSCrc.cpp">
|
||||
<Filter>System\Ps2\GS</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GS\GSDump.cpp">
|
||||
<Filter>System\Ps2\GS</Filter>
|
||||
</ClCompile>
|
||||
@@ -1915,9 +1912,6 @@
|
||||
<ClInclude Include="GS\GSClut.h">
|
||||
<Filter>System\Ps2\GS</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GS\GSCrc.h">
|
||||
<Filter>System\Ps2\GS</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GS\GSDump.h">
|
||||
<Filter>System\Ps2\GS</Filter>
|
||||
</ClInclude>
|
||||
|
||||
@@ -595,7 +595,7 @@ static void recAlloc()
|
||||
s_nInstCacheSize = 128;
|
||||
s_pInstCache = (EEINST*)malloc(sizeof(EEINST) * s_nInstCacheSize);
|
||||
if (!s_pInstCache)
|
||||
pxFailRel("Failed to allocate R5900-32 InstCache array");
|
||||
pxFailRel("Failed to allocate R5900 InstCache array");
|
||||
}
|
||||
|
||||
// No errors.. Proceed with initialization:
|
||||
@@ -609,7 +609,7 @@ alignas(16) static u8 manual_counter[Ps2MemSize::MainRam >> 12];
|
||||
////////////////////////////////////////////////////
|
||||
static void recResetRaw()
|
||||
{
|
||||
Console.WriteLn(Color_StrongBlack, "EE/iR5900-32 Recompiler Reset");
|
||||
Console.WriteLn(Color_StrongBlack, "EE/iR5900 Recompiler Reset");
|
||||
|
||||
EE::Profiler.Reset();
|
||||
|
||||
Reference in New Issue
Block a user