Compare commits

...
64 Commits
Author SHA1 Message Date
MrlinkwiiandConnor McLaughlin 4d54e28e63 GameDB: remove all god hand getSkipCount 2023-08-09 21:34:07 +10:00
MrlinkwiiandConnor McLaughlin c89d023f91 CRC/HW: remove god hand GSC 2023-08-09 21:34:07 +10:00
Stenzek c7cfea1daa GS/Vulkan: Make VK_EXT_provoking_vertex optional again
RenderDoc doesn't support it.
2023-08-09 21:33:45 +10:00
Stenzek beb07365a0 Build: Disable Vulkan on macOS builds
MoltenVK does not support the extensions we now require.
2023-08-09 19:34:23 +10:00
Stenzek 0787c65e51 GS/Vulkan: Mandate well-supported extensions
- VK_EXT_provoking_vertex and provokingVertexLast
 - VK_EXT_line_rasterization and bresenhamLines
 - VK_KHR_shader_draw_parameters

Lack of these would cause rendering issues anyway.
2023-08-09 19:34:23 +10:00
Stenzek d48dea7273 GS/Vulkan: Don't track vertex buffer state
It never changes.
2023-08-09 19:34:23 +10:00
Stenzek 2432cc6ed2 GS/Vulkan: clang-format 2023-08-09 19:34:23 +10:00
Stenzek cf523d4215 GS/Vulkan: Use push descriptors instead of per-frame allocations 2023-08-09 19:34:23 +10:00
lightningterror 906d87c4b8 GS/HW: Fix nfs undercover crashing on dev/debug builds.
We were reading the wrong value.
2023-08-09 10:23:47 +02:00
Connor McLaughlinandTellowKrinkle 9c4a92c015 GS/Metal: Fix crash when Z write is on second pass 2023-08-08 18:27:54 -05:00
lightningterror 0c056ba691 USB: Fix function parameter should be passed by const reference warnings.
Codacy.
2023-08-08 21:27:36 +02:00
lightningterror 424158b28d DEV9: Fix function parameter should be passed by const reference warnings.
Codacy.
2023-08-08 21:27:36 +02:00
JordanTheToasterandrefractionpcsx2 57f118a5b5 GameDB: Various fixes
Fixes line across the screen and offset blur in Samurai Westerner and corrects region of Chinese entry's to properly use the flag included in the icons folder.
2023-08-08 08:58:55 +01:00
refractionpcsx2 65e75f3da2 GS/TC: Ignore linear part of rect when translating 2023-08-08 08:57:09 +01:00
refractionpcsx2 41174cde45 GS: Elide flushes on GS transfer if not required 2023-08-08 08:57:09 +01:00
refractionpcsx2 cb7d01fb36 GameDB: Improve Myst 3 patch 2023-08-08 08:56:52 +01:00
refractionpcsx2 3e78f8e87d IPU: Improve DMA/IPU call locations to reduce looping 2023-08-08 08:56:52 +01:00
BacklogOdysseyandConnor McLaughlin f2c032ba07 RetroAchievements/Qt: Add configurable achievement notification duration 2023-08-08 13:17:28 +10:00
refractionpcsx2 2ef1589e76 GameDB: Remove NFS HP2 GS fix which did nothing. 2023-08-07 23:03:11 +01:00
refractionpcsx2 5c394557e3 GS/HW: Handle split shuffles with a width of 1 2023-08-07 23:03:11 +01:00
PCSX2 Botandrefractionpcsx2 5c4a5b0a61 PAD: Update to latest controller database. 2023-08-07 17:10:55 +01:00
Silentandrefractionpcsx2 be22b7349d GameList: Add an option to exclude entire directories 2023-08-07 16:50:15 +01:00
refractionpcsx2 023e4774a1 UI: Only allow relative memcard files in memcard interface 2023-08-07 16:49:45 +01:00
Stenzekandrefractionpcsx2 56b706f25e Achievements: Tag a few missing strings for translation 2023-08-07 16:49:34 +01:00
Stenzekandrefractionpcsx2 9d07ee43eb VMManager: Fix achievements HC mode not applying on reset 2023-08-07 16:49:34 +01:00
Connor McLaughlinandTellowKrinkle 147a6c81a1 GS/Metal: Don't end up with two encoders on readback 2023-08-06 16:38:59 -05:00
RedPanda4552andrefractionpcsx2 5972f4947d InputManager: Fix incorrect compare for multitap enabled 2023-08-06 00:45:58 +01:00
refractionpcsx2 9c99a624d7 GS/HW: Optimise usage of 24bit alphas 2023-08-05 22:01:59 +01:00
refractionpcsx2 d812d83dda GS/HW: Limit alpha range on 16bit targets 2023-08-05 22:01:59 +01:00
refractionpcsx2 f81c5c2455 GS/HW: Guess alpha values when using 16bit RT as TEX = RT 2023-08-05 22:01:59 +01:00
refractionpcsx2 dc8c5e6209 GS/HW: Use texture alpha for 16bit textures in CalcAlphaMinMax 2023-08-05 22:01:59 +01:00
Stenzekandrefractionpcsx2 14833582b2 GS/HW: Re-optimize alpha test after updating direct alpha range 2023-08-05 22:01:59 +01:00
Mrlinkwiiandrefractionpcsx2 cca813fd1d GameDB: fixes for Panzer Elite Action & Totally Spies 2023-08-04 16:30:19 +01:00
lightningterror d4102e75c5 Readme: Update requirements.
Minimum: Lower STR requirements to 1500 from 1800.
Recommended: Lower STR requirements to 2000 from 2500.
Add Heavy category with STR requirements 2600.
2023-08-04 16:32:16 +02:00
Tyler Wildingandrefractionpcsx2 76a04efc5c ci: properly fetch release info to make announcement 2023-08-03 21:41:46 +01:00
JordanTheToasterandrefractionpcsx2 a34e139843 GameDB: Remove CCLUT from Raw Danger
Doesn't appear to do anything currently and causes the game to do almost 100 readbacks totally killing performance in hardware mode.
2023-08-03 11:59:09 +01:00
Tyler Wildingandrefractionpcsx2 a1727a2ac7 ci: announce release after it's published
It's no longer working because workflows will not implicitly trigger from another workflow when initiated by the default token.

You must explicitly call them via a `workflow_dispatch` or use a distinct PAT.

The announce workflow isn't really used anywhere else so simplifying and consolidating the steps feels appropriate.
2023-08-03 01:51:58 +01:00
Stenzekandrefractionpcsx2 7ac224adf3 MemoryCardFolder: Initialize hostFilePath 2023-08-02 16:06:37 +01:00
RedDevilusandrefractionpcsx2 d5e3d02a98 GameDB: Missing Korean entries + maintenance
Based on:
https://github.com/PCSX2/pcsx2/pull/9671#issuecomment-1657337888

I do see a lot of the missing entries actually come from the PCSX2 wiki, and the csv was mostly duplicates but I checked and cleaned it up while looking at other things to potentially fix.

GGeneration is faulty naming though, G Genereration needs a space.

There are also 2 reasons why we don't fully follow redump:
For starters Windows uses specific characters in their code and following that would make it impossible to make covers for them and some other things
The other thing is when The blablabla  turns into blablabla, The for filtering purposes.

In future we should probably have a display language for specific languages, ex. Korean entries show Japanese, English or others
2023-08-02 16:02:53 +01:00
Connor McLaughlin 68ad3e8db4 FileSystem: Partial revert of c82f800
Mixing stdio and raw FD access is a bad thing, filelength() isn't going to return correct results when extending a file has been buffered.
2023-08-02 18:45:52 +10:00
Stenzek 31029b2fe4 InputManager: Don't connect bindings for disabled multitap ports 2023-08-02 12:44:31 +10:00
Stenzek 8b0c101768 Pad: Move multitap into its own config section
And more config cleanups.
2023-08-02 12:44:31 +10:00
JordanTheToasterandrefractionpcsx2 50b66f526b GameDB: Various fixes
Fixes menu corruption and likely other issues the game may have had similar to all future entry's.
2023-08-02 01:07:45 +01:00
RedDevilusandrefractionpcsx2 f3fcf010fb GameDB: Remove accidental addition Growlanser 5+6
Texture Inside RT was fixed in https://github.com/PCSX2/pcsx2/pull/9187
2023-08-01 17:31:47 +01:00
PCSX2 Botandrefractionpcsx2 4da53bd00d Qt: Update Base Translation 2023-08-01 17:18:23 +01:00
Mrlinkwiiandrefractionpcsx2 c6cef45f0e Inputmanager: fix possible build issue 2023-08-01 17:07:51 +01:00
Mrlinkwiiandrefractionpcsx2 e36cd7fe5c Build: remove -32 notation from core filenames 2023-08-01 16:09:09 +01:00
Stenzek 0c24f2db7b GS/HW: Don't discard target when SCANMSK is set 2023-08-01 13:31:42 +10:00
Stenzek 8866faa8d5 GS: Remove GSCrc
Finally!
2023-08-01 12:42:06 +10:00
Stenzek b2dc31ba80 GS/HW: Move Tekken 5 CRC hack to the GSC 2023-08-01 12:42:06 +10:00
pgertandrefractionpcsx2 061ea30199 GameDB: entry correction 2023-07-31 22:36:56 +01:00
refractionpcsx2 0e9e94d090 UI: Delete rogue quote breaking HTML in Memcard Convert dialog 2023-07-31 16:36:14 +01:00
Stenzek 3321cb37ef GS/HW: Don't invalidate alpha on display target lookup 2023-07-31 21:08:33 +10:00
JordanTheToasterandrefractionpcsx2 d60a818791 GameDB: Go Go Golf Fixes
Fixes for the pretty awful brightness on pretty PS1 looking textures.
2023-07-31 10:59:54 +01:00
PCSX2 BotandConnor McLaughlin 3937a52b4f Qt: Update Base Translation 2023-07-31 19:28:43 +10:00
Stenzek 2c3902b1a1 GS/HW: Fix bilinear-over-self targets getting discarded 2023-07-31 19:24:08 +10:00
RedPanda4552andrefractionpcsx2 f43b5ee76f Pad: Respect non-standard vibration mappings and pure digital button query 2023-07-30 23:48:12 +01:00
refractionpcsx2 bab16a5251 UI: Update glyphs for asian languages. 2023-07-30 19:20:29 +01:00
Mrlinkwiiandrefractionpcsx2 08c734d7fd GameDB: various fixes 2023-07-30 14:23:45 +01:00
Connor McLaughlin 36126a3ad9 GS: Drop alignment from ClearValue 2023-07-30 21:44:44 +10:00
kamfretozandConnor McLaughlin ce505d33bd Qt: Make incompatible savestate message translateable 2023-07-30 20:28:50 +10:00
Stenzek de0d859502 Qt: Fix changing cover file formats 2023-07-30 19:25:51 +10:00
Stenzek ce96d4bcc5 SaveState: Store the current PCSX2 version in the save state 2023-07-30 18:13:25 +10:00
ChromaryuandConnor McLaughlin fb242274d5 GameDB: Fix PoPoLoCrois Naming 2023-07-30 16:18:55 +10:00
93 changed files with 2548 additions and 1447 deletions
+1
View File
@@ -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 \
-24
View File
@@ -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
+22 -7
View File
@@ -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"
}
}
+10 -4
View File
@@ -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
File diff suppressed because it is too large Load Diff
+5 -3
View File
@@ -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,
+3 -3
View File
@@ -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
-8
View File
@@ -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;
}
+8 -1
View File
@@ -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();
}
+11 -4
View File
@@ -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();
+83 -16
View File
@@ -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>
+15 -8
View File
@@ -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);
}
+3 -3
View File
@@ -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;
+13 -1
View File
@@ -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();
+34 -2
View File
@@ -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>
+1 -1
View File
@@ -86,7 +86,7 @@
</size>
</property>
<property name="text">
<string>&lt;center&gt;&lt;strong&gt;Note:&lt;/strong&gt; Converting a Memory Card creates a &lt;strong&gt;COPY&lt;/strong&gt; of your existing Memory Card. It does &lt;strong&quot;&gt;NOT delete, modify, or replace&lt;/strong&gt; your existing Memory Card.&lt;/center&gt;</string>
<string>&lt;center&gt;&lt;strong&gt;Note:&lt;/strong&gt; Converting a Memory Card creates a &lt;strong&gt;COPY&lt;/strong&gt; of your existing Memory Card. It does &lt;strong&gt;NOT delete, modify, or replace&lt;/strong&gt; your existing Memory Card.&lt;/center&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+2 -2
View File
@@ -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)"));
+7 -1
View File
@@ -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>
+4 -4
View File
@@ -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
+62 -39
View File
@@ -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 &apos;%1&apos;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="2310"/>
<location filename="../MainWindow.cpp" line="2311"/>
<source>Failed to copy &apos;%1&apos; to &apos;%2&apos;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="2319"/>
<location filename="../MainWindow.cpp" line="2317"/>
<source>Failed to remove &apos;%1&apos;</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 &apos;%1&apos;?
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 &apos;%1&apos;.</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>&lt;center&gt;&lt;strong&gt;Note:&lt;/strong&gt; Converting a Memory Card creates a &lt;strong&gt;COPY&lt;/strong&gt; of your existing Memory Card. It does &lt;strong&quot;&gt;NOT delete, modify, or replace&lt;/strong&gt; your existing Memory Card.&lt;/center&gt;</source>
<source>&lt;center&gt;&lt;strong&gt;Note:&lt;/strong&gt; Converting a Memory Card creates a &lt;strong&gt;COPY&lt;/strong&gt; of your existing Memory Card. It does &lt;strong&gt;NOT delete, modify, or replace&lt;/strong&gt; your existing Memory Card.&lt;/center&gt;</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>&amp;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>&amp;Finish</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../SetupWizardDialog.cpp" line="160"/>
<source>Cancel Setup</source>
+30 -14
View File
@@ -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"
+2 -4
View File
@@ -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
View File
@@ -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
{
+1 -1
View File
@@ -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);
};
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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.
+1 -1
View File
@@ -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);
};
+3 -3
View File
@@ -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;
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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();
-45
View File
@@ -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];
}
-41
View File
@@ -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);
};
+57
View File
@@ -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)
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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); }
+21 -12
View File
@@ -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";
}
+4
View File
@@ -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; }
+1 -1
View File
@@ -58,7 +58,7 @@ public:
Invalidated
};
union alignas(16) ClearValue
union ClearValue
{
u32 color;
float depth;
+32 -16
View File
@@ -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;
-1
View File
@@ -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);
+135 -63
View File
@@ -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) ==
+4 -3
View File
@@ -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;
+110 -37
View File
@@ -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();
}
+2 -2
View File
@@ -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,
-2
View File
@@ -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)
+1 -1
View File
@@ -194,8 +194,8 @@ void GSDownloadTextureMTL::CopyFromTexture(
GetTransferPitch(use_transfer_pitch ? static_cast<u32>(drc.width()) : m_width, PITCH_ALIGNMENT);
GetTransferSize(drc, &copy_offset, &copy_size, &copy_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
+55 -50
View File
@@ -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;
+18 -13
View File
@@ -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};
+3 -3
View File
@@ -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);
+16
View File
@@ -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*/)
{
+4 -1
View File
@@ -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
+24 -22
View File
@@ -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: ");
+4 -3
View File
@@ -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; }
+9 -7
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
+4 -2
View File
@@ -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
View File
@@ -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;
}
+4 -9
View File
@@ -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();
}
}
+2 -5
View File
@@ -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 -9
View File
@@ -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;
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+7 -2
View File
@@ -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))
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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(
+33 -3
View File
@@ -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;
+6 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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.
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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" />
+2 -8
View File
@@ -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();