Compare commits

...
81 Commits
Author SHA1 Message Date
refractionpcsx2 054cb9e9fa GS/HW: Correctly remove offset when detecting shuffle coords 2023-08-12 19:39:25 +01:00
refractionpcsx2 1f77e7d90a GS/CRC: Fix the sizing on Sonic Unleashed copy CRC 2023-08-12 19:34:06 +01:00
lightningterror 7ed33832b8 GS/HW: Isolate early returns for blending.
Optimization.
2023-08-12 18:14:37 +02:00
lightningterror d19e9cc662 GS: Bump shader cache version. 2023-08-12 18:14:37 +02:00
lightningterror 846c9cec6a GS/HW: Adjust how we handle RT alpha in blending.
When both rt min and max are equal then we know what Ad value is,
if so use Af bit instead and set AFIX value from rt alpha value that we know.

On OpenGL when BLEND C == 1 but reading the rt is disabled, set the value to 0 instead
of reading an undefined value.
2023-08-12 18:14:37 +02:00
Sanjay Govindandrefractionpcsx2 460e4b8bb3 USB-Turntable: Default multiplier to 1 2023-08-12 14:07:37 +01:00
Stenzek 2e6bd23f22 Qt: Fix arrow key handling on macOS 2023-08-12 16:17:47 +10:00
refractionpcsx2 7763948fe4 GS/HW: Restrict double buffer detection to widths greater than 256 2023-08-12 01:54:31 +01:00
refractionpcsx2 2a0f001902 GIF: Adjust run length of split in intermittent packets 2023-08-12 00:28:28 +01:00
refractionpcsx2 14df69e180 GS/HW: Fix crash where it could process a texture with no RT 2023-08-12 00:28:28 +01:00
refractionpcsx2 28b6389b36 GS/HW: Stop errantly halfing normal height 2023-08-12 00:28:28 +01:00
lightningterror 905c0b8c7a GS: Fix warnings.
GSRendererHW:
Fix Wtautological-constant-out-of-range-compare.

GSSstate:
Fix Wunused-variable.
Fix Wsign-compare.
2023-08-11 21:46:29 +02:00
lightningterror b674cc95ac Qt: Fix Wreorder-ctor warning. 2023-08-11 21:46:29 +02:00
Mrlinkwiiandlightningterror bccb2509b5 GameDB: Fixes for Ghost Recon - Advanced Warfighter & Battlefield 2 2023-08-11 18:16:33 +02:00
refractionpcsx2 cd980c6604 GS/TC: Fix crash in debug message (only affects debug device) 2023-08-11 16:08:38 +01:00
lightningterror ddff4ec083 GameDB: Add full mipmap plus triliear ps2 to The Godfather.
Improves building textures to match sw renderer.
2023-08-11 17:05:21 +02:00
kamfretozandrefractionpcsx2 5bf97be8ef Qt: Even further icon refinement 2023-08-11 15:24:04 +01:00
lightningterrorandrefractionpcsx2 7fadb6ed05 GS/HW: Cleanup texture shuffle shaders. 2023-08-11 15:23:50 +01:00
lightningterrorandrefractionpcsx2 89688d67dd GS: Bump shader cache version. 2023-08-11 15:23:50 +01:00
refractionpcsx2 279007544a GS/HW: Allow double clear when src is lowest bitsize denominator 2023-08-11 14:28:34 +01:00
lightningterrorandrefractionpcsx2 1c0f0e1423 GS: Bump shader cache version. 2023-08-11 13:14:19 +01:00
lightningterrorandrefractionpcsx2 1ab909addc Config/gamedb: Remove screen fix from config entirely.
No longer needed.
2023-08-11 13:14:19 +01:00
lightningterrorandrefractionpcsx2 99dfd15469 ImGui: Remove Half Screen Fix from the ui.
No longer needed.
2023-08-11 13:14:19 +01:00
lightningterrorandrefractionpcsx2 be18ecf74e Qt: Remove Half Screen Fix from the ui.
No longer needed.
2023-08-11 13:14:19 +01:00
lightningterrorandrefractionpcsx2 713bd132dc GS/D3D: Add initial support for complex shuffles. 2023-08-11 13:14:19 +01:00
refractionpcsx2 20f03e237e GameDB: Add Autoflush to Minority Report 2023-08-11 13:14:19 +01:00
refractionpcsx2 7d3c3911ea GS/HW: Update BigMuthaTruckers hack to a GSC 2023-08-11 13:14:19 +01:00
refractionpcsx2 ccfee2be83 GameDB: Add Tex in RT to Godfather + remove skipdraw 2023-08-11 13:14:19 +01:00
refractionpcsx2 1193b0b990 GS/TC: Delete old targets which aren't preserved and don't match size 2023-08-11 13:14:19 +01:00
refractionpcsx2 5a3ba4e563 GS/HW: Add support for complex offset shuffles 2023-08-11 13:14:19 +01:00
refractionpcsx2 e9c342ef74 GS/Autoflush: Account for CLAMP & better handle different formats 2023-08-11 12:35:42 +01:00
refractionpcsx2 8927ffa035 GS: Fix autoflush behaviour when using Z + C formats together 2023-08-11 12:35:42 +01:00
Ty Lamontagneandrefractionpcsx2 fdb0312326 Dis5900: Mask and extend the pmfhl & pmthl LUT
Fixes an OOB access when disassembling an invalid opcode (71756972h)
2023-08-11 00:09:25 +01:00
refractionpcsx2 2f21caa41c GameDB: Add Instant DMA to Jak X Combat Racing 2023-08-10 10:40:43 +01:00
Stenzekandrefractionpcsx2 51a35d40e5 3rdparty: Bump rcheevos to 3af1e2fc5188d6e932ee379942f4049ea877e648 2023-08-10 01:27:20 +01:00
Stenzekandrefractionpcsx2 6ec524adec USB/GunCon2: Fix cursor not starting in center 2023-08-10 01:27:10 +01:00
Stenzekandrefractionpcsx2 7d4a7ec8c7 ImGuiManager: Fix window width/height not being initialized 2023-08-10 01:27:10 +01:00
Silentandrefractionpcsx2 0256c4521d GameList: Support adding custom title and region to files 2023-08-10 01:26:06 +01:00
Silentandrefractionpcsx2 be3ed181c1 SettingsInterface: Fix constness of a few methods 2023-08-10 01:26:06 +01:00
PCSX2 BotandConnor McLaughlin 38cf7884fe Qt: Update Base Translation 2023-08-09 21:37:15 +10:00
JordanTheToasterandConnor McLaughlin 25d426e8b2 GameDB: Various fixes
Fixes for missing recommended blending on MGS 2 Sub and post processing positioning on Project Snowblind.
2023-08-09 21:35:34 +10:00
TellowKrinkleandConnor McLaughlin edff9ae684 GS:MTL: Include full error messages in aborts 2023-08-09 21:34:38 +10:00
TellowKrinkleandConnor McLaughlin a622e3bda6 Mac: Mark crash info as used so it doesn't get DCE'd 2023-08-09 21:34:38 +10:00
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
101 changed files with 3142 additions and 1915 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,
+25 -20
View File
@@ -52,6 +52,7 @@
#define PS_POINT_SAMPLER 0
#define PS_REGION_RECT 0
#define PS_SHUFFLE 0
#define PS_SHUFFLE_SAME 0
#define PS_READ_BA 0
#define PS_READ16_SRC 0
#define PS_DFMT 0
@@ -940,8 +941,17 @@ PS_OUTPUT ps_main(PS_INPUT input)
{
uint4 denorm_c = uint4(C);
uint2 denorm_TA = uint2(float2(TA.xy) * 255.0f + 0.5f);
if (PS_READ16_SRC)
// Special case for 32bit input and 16bit output, shuffle used by The Godfather
if (PS_SHUFFLE_SAME)
{
if (PS_READ_BA)
C = (float4)(float((denorm_c.b & 0x7Fu) | (denorm_c.a & 0x80u)));
else
C.ga = C.rg;
}
// Copy of a 16bit source in to this target
else if (PS_READ16_SRC)
{
C.rb = (float2)float((denorm_c.r >> 3) | (((denorm_c.g >> 3) & 0x7u) << 5));
if (denorm_c.a & 0x80u)
@@ -949,28 +959,23 @@ PS_OUTPUT ps_main(PS_INPUT input)
else
C.ga = (float2)float((denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.x & 0x80u));
}
// Write RB part. Mask will take care of the correct destination
else if (PS_READ_BA)
{
C.rb = C.bb;
if (denorm_c.a & 0x80u)
C.ga = (float2)(float((denorm_c.a & 0x7Fu) | (denorm_TA.y & 0x80u)));
else
C.ga = (float2)(float((denorm_c.a & 0x7Fu) | (denorm_TA.x & 0x80u)));
}
else
{
// Mask will take care of the correct destination
if (PS_READ_BA)
C.rb = C.bb;
else
C.rb = C.rr;
C.rb = C.rr;
if (denorm_c.g & 0x80u)
C.ga = (float2)(float((denorm_c.g & 0x7Fu) | (denorm_TA.y & 0x80u)));
if (PS_READ_BA)
{
if (denorm_c.a & 0x80u)
C.ga = (float2)(float((denorm_c.a & 0x7Fu) | (denorm_TA.y & 0x80u)));
else
C.ga = (float2)(float((denorm_c.a & 0x7Fu) | (denorm_TA.x & 0x80u)));
}
else
{
if (denorm_c.g & 0x80u)
C.ga = (float2)(float((denorm_c.g & 0x7Fu) | (denorm_TA.y & 0x80u)));
else
C.ga = (float2)(float((denorm_c.g & 0x7Fu) | (denorm_TA.x & 0x80u)));
}
C.ga = (float2)(float((denorm_c.g & 0x7Fu) | (denorm_TA.x & 0x80u)));
}
}
+20 -14
View File
@@ -779,17 +779,19 @@ float As = As_rgba.a;
return;
#endif
vec3 Cs = Color.rgb;
#if SW_BLEND_NEEDS_RT
vec4 RT = trunc(fetch_rt() * 255.0f + 0.1f);
#else
// Not used, but we define it to make the selection below simpler.
vec4 RT = vec4(0.0f);
#endif
// FIXME FMT_16 case
// FIXME Ad or Ad * 2?
float Ad = RT.a / 128.0f;
// Let the compiler do its jobs !
vec3 Cd = RT.rgb;
#endif
vec3 Cs = Color.rgb;
#if PS_BLEND_A == 0
vec3 A = Cs;
@@ -950,26 +952,30 @@ void ps_main()
#if PS_SHUFFLE
uvec4 denorm_c = uvec4(C);
uvec2 denorm_TA = uvec2(vec2(TA.xy) * 255.0f + 0.5f);
#if PS_READ16_SRC
// Special case for 32bit input and 16bit output, shuffle used by The Godfather
#if PS_SHUFFLE_SAME
#if (PS_READ_BA)
C = vec4(float((denorm_c.b & 0x7Fu) | (denorm_c.a & 0x80u)));
#else
C.ga = C.rg;
#endif
// Copy of a 16bit source in to this target
#elif PS_READ16_SRC
C.rb = vec2(float((denorm_c.r >> 3) | (((denorm_c.g >> 3) & 0x7u) << 5)));
if (bool(denorm_c.a & 0x80u))
C.ga = vec2(float((denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.y & 0x80u)));
else
C.ga = vec2(float((denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.x & 0x80u)));
#else
// Write RB part. Mask will take care of the correct destination
#if PS_READ_BA
// Write RB part. Mask will take care of the correct destination
#elif PS_READ_BA
C.rb = C.bb;
#else
C.rb = C.rr;
#endif
// FIXME precompute my_TA & 0x80
// Write GA part. Mask will take care of the correct destination
// Note: GLSL 4.50/GL_EXT_shader_integer_mix support a mix instruction to select a component\n"
// However Nvidia emulate it with an if (at least on kepler arch) ...\n"
#if PS_READ_BA
// bit field operation requires GL4 HW. Could be nice to merge it with step/mix below
// uint my_ta = (bool(bitfieldExtract(denorm_c.a, 7, 1))) ? denorm_TA.y : denorm_TA.x;
// denorm_c.a = bitfieldInsert(denorm_c.a, bitfieldExtract(my_ta, 7, 1), 7, 1);
@@ -981,6 +987,7 @@ void ps_main()
C.ga = vec2(float((denorm_c.a & 0x7Fu) | (denorm_TA.x & 0x80u)));
#else
C.rb = C.rr;
if (bool(denorm_c.g & 0x80u))
C.ga = vec2(float((denorm_c.g & 0x7Fu) | (denorm_TA.y & 0x80u)));
else
@@ -992,9 +999,8 @@ void ps_main()
// float sel = step(128.0f, c.g);
// vec2 c_shuffle = vec2((denorm_c.gg & 0x7Fu) | (denorm_TA & 0x80u));
// c.ga = mix(c_shuffle.xx, c_shuffle.yy, sel);
#endif // PS_READ_BA
#endif // READ16_SRC
#endif // PS_SHUFFLE_SAME
#endif // PS_SHUFFLE
// Must be done before alpha correction
+30 -25
View File
@@ -277,7 +277,9 @@ void main()
#define PS_TCOFFSETHACK 0
#define PS_POINT_SAMPLER 0
#define PS_SHUFFLE 0
#define PS_SHUFFLE_SAME 0
#define PS_READ_BA 0
#define PS_WRITE_RG 0
#define PS_READ16_SRC 0
#define PS_DFMT 0
#define PS_DEPTH_FMT 0
@@ -300,7 +302,7 @@ void main()
#endif
#define SW_BLEND (PS_BLEND_A || PS_BLEND_B || PS_BLEND_D)
#define SW_BLEND_NEEDS_RT (PS_BLEND_A == 1 || PS_BLEND_B == 1 || PS_BLEND_C == 1 || PS_BLEND_D == 1)
#define SW_BLEND_NEEDS_RT (SW_BLEND && (PS_BLEND_A == 1 || PS_BLEND_B == 1 || PS_BLEND_C == 1 || PS_BLEND_D == 1))
#define SW_AD_TO_HW (PS_BLEND_C == 1 && PS_A_MASKED)
#define PS_FEEDBACK_LOOP_IS_NEEDED (PS_TEX_IS_FB == 1 || PS_FBMASK || SW_BLEND_NEEDS_RT || (PS_DATE >= 5))
@@ -352,16 +354,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
@@ -1197,33 +1199,36 @@ void main()
#if PS_SHUFFLE
uvec4 denorm_c = uvec4(C);
uvec2 denorm_TA = uvec2(vec2(TA.xy) * 255.0f + 0.5f);
#if PS_READ16_SRC
// Special case for 32bit input and 16bit output, shuffle used by The Godfather
#if PS_SHUFFLE_SAME
#if (PS_READ_BA)
C = vec4(float((denorm_c.b & 0x7Fu) | (denorm_c.a & 0x80u)));
#else
C.ga = C.rg;
#endif
// Copy of a 16bit source in to this target
#elif PS_READ16_SRC
C.rb = vec2(float((denorm_c.r >> 3) | (((denorm_c.g >> 3) & 0x7u) << 5)));
if ((denorm_c.a & 0x80u) != 0u)
C.ga = vec2(float((denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.y & 0x80u)));
else
C.ga = vec2(float((denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.x & 0x80u)));
// Write RB part. Mask will take care of the correct destination
#elif PS_READ_BA
C.rb = C.bb;
if ((denorm_c.a & 0x80u) != 0u)
C.ga = vec2(float((denorm_c.a & 0x7Fu) | (denorm_TA.y & 0x80u)));
else
C.ga = vec2(float((denorm_c.a & 0x7Fu) | (denorm_TA.x & 0x80u)));
#else
// Mask will take care of the correct destination
#if PS_READ_BA
C.rb = C.bb;
#else
C.rb = C.rr;
#endif
#if PS_READ_BA
if ((denorm_c.a & 0x80u) != 0u)
C.ga = vec2(float((denorm_c.a & 0x7Fu) | (denorm_TA.y & 0x80u)));
else
C.ga = vec2(float((denorm_c.a & 0x7Fu) | (denorm_TA.x & 0x80u)));
#else
if ((denorm_c.g & 0x80u) != 0u)
C.ga = vec2(float((denorm_c.g & 0x7Fu) | (denorm_TA.y & 0x80u)));
else
C.ga = vec2(float((denorm_c.g & 0x7Fu) | (denorm_TA.x & 0x80u)));
#endif
#endif
#endif
C.rb = C.rr;
if ((denorm_c.g & 0x80u) != 0u)
C.ga = vec2(float((denorm_c.g & 0x7Fu) | (denorm_TA.y & 0x80u)));
else
C.ga = vec2(float((denorm_c.g & 0x7Fu) | (denorm_TA.x & 0x80u)));
#endif // PS_SHUFFLE_SAME
#endif // PS_SHUFFLE
// Must be done before alpha correction
+1 -1
View File
@@ -110,7 +110,7 @@ struct crash_info_t
u64 reserved;
u64 reserved2;
};
#define CRASH_ANNOTATION __attribute__((section("__DATA,__crash_info")))
#define CRASH_ANNOTATION __attribute__((used, section("__DATA,__crash_info")))
#define CRASH_VERSION 4
extern "C" crash_info_t gCRAnnotations CRASH_ANNOTATION = { CRASH_VERSION };
#endif
+5 -5
View File
@@ -92,31 +92,31 @@ public:
return value;
}
__fi std::optional<int> GetOptionalIntValue(const char* section, const char* key, std::optional<int> default_value = std::nullopt)
__fi std::optional<int> GetOptionalIntValue(const char* section, const char* key, std::optional<int> default_value = std::nullopt) const
{
int ret;
return GetIntValue(section, key, &ret) ? std::optional<int>(ret) : default_value;
}
__fi std::optional<uint> GetOptionalUIntValue(const char* section, const char* key, std::optional<uint> default_value = std::nullopt)
__fi std::optional<uint> GetOptionalUIntValue(const char* section, const char* key, std::optional<uint> default_value = std::nullopt) const
{
uint ret;
return GetUIntValue(section, key, &ret) ? std::optional<uint>(ret) : default_value;
}
__fi std::optional<float> GetOptionalFloatValue(const char* section, const char* key, std::optional<float> default_value = std::nullopt)
__fi std::optional<float> GetOptionalFloatValue(const char* section, const char* key, std::optional<float> default_value = std::nullopt) const
{
float ret;
return GetFloatValue(section, key, &ret) ? std::optional<float>(ret) : default_value;
}
__fi std::optional<double> GetOptionalDoubleValue(const char* section, const char* key, std::optional<double> default_value = std::nullopt)
__fi std::optional<double> GetOptionalDoubleValue(const char* section, const char* key, std::optional<double> default_value = std::nullopt) const
{
double ret;
return GetDoubleValue(section, key, &ret) ? std::optional<double>(ret) : default_value;
}
__fi std::optional<bool> GetOptionalBoolValue(const char* section, const char* key, std::optional<bool> default_value = std::nullopt)
__fi std::optional<bool> GetOptionalBoolValue(const char* section, const char* key, std::optional<bool> default_value = std::nullopt) const
{
bool ret;
return GetBoolValue(section, key, &ret) ? std::optional<bool>(ret) : default_value;
+18 -1
View File
@@ -513,5 +513,22 @@ std::optional<std::string> InputManager::ConvertHostKeyboardCodeToString(u32 cod
u32 QtUtils::KeyEventToCode(const QKeyEvent* ev)
{
return static_cast<u32>(ev->key()) | (static_cast<u32>(ev->modifiers()) & static_cast<u32>(Qt::KeypadModifier));
int key = ev->key();
Qt::KeyboardModifiers modifiers = ev->modifiers();
#ifdef __APPLE__
// On macOS, Qt applies the Keypad modifier regardless of whether the arrow keys, or numpad was pressed.
// The only way to differentiate between the keypad and the arrow keys is by the text.
// Hopefully some keyboard layouts don't change the numpad positioning...
if (modifiers & Qt::KeypadModifier && key >= Qt::Key_Insert && key <= Qt::Key_PageDown)
{
if (ev->text().isEmpty())
{
// Drop the modifier, because it's probably not actually a numpad push.
modifiers &= ~Qt::KeypadModifier;
}
}
#endif
return static_cast<u32>(key) | (static_cast<u32>(modifiers) & static_cast<u32>(Qt::KeypadModifier));
}
+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>
+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>
+52 -7
View File
@@ -63,6 +63,11 @@ GameSummaryWidget::GameSummaryWidget(const GameList::Entry* entry, SettingsDialo
connect(m_ui.inputProfile, &QComboBox::currentIndexChanged, this, &GameSummaryWidget::onInputProfileChanged);
connect(m_ui.verify, &QAbstractButton::clicked, this, &GameSummaryWidget::onVerifyClicked);
connect(m_ui.searchHash, &QAbstractButton::clicked, this, &GameSummaryWidget::onSearchHashClicked);
bool has_custom_title = false, has_custom_region = false;
GameList::CheckCustomAttributesForPath(m_entry_path, has_custom_title, has_custom_region);
m_ui.restoreTitle->setEnabled(has_custom_title);
m_ui.restoreRegion->setEnabled(has_custom_region);
}
GameSummaryWidget::~GameSummaryWidget() = default;
@@ -88,6 +93,24 @@ void GameSummaryWidget::populateDetails(const GameList::Entry* entry)
m_ui.inputProfile->setCurrentIndex(m_ui.inputProfile->findText(QString::fromStdString(profile.value())));
else
m_ui.inputProfile->setCurrentIndex(0);
connect(m_ui.title, &QLineEdit::editingFinished, this, [this]() {
if (m_ui.title->isModified())
{
setCustomTitle(m_ui.title->text().toStdString());
m_ui.title->setModified(false);
}
});
connect(m_ui.restoreTitle, &QAbstractButton::clicked, this, [this]() {
setCustomTitle("");
});
connect(m_ui.region, &QComboBox::currentIndexChanged, this, [this](int index) {
setCustomRegion(index);
});
connect(m_ui.restoreRegion, &QAbstractButton::clicked, this, [this]() {
setCustomRegion(-1);
});
}
void GameSummaryWidget::populateDiscPath(const GameList::Entry* entry)
@@ -129,12 +152,7 @@ void GameSummaryWidget::onDiscPathChanged(const QString& value)
// force rescan of elf to update the serial
g_main_window->rescanFile(m_entry_path);
// and re-fill our details (mainly the serial)
auto lock = GameList::GetLock();
const GameList::Entry* entry = GameList::GetEntryForPath(m_entry_path.c_str());
if (entry)
populateDetails(entry);
repopulateCurrentDetails();
}
void GameSummaryWidget::onDiscPathBrowseClicked()
@@ -352,4 +370,31 @@ void GameSummaryWidget::setVerifyResult(QString error)
m_ui.verifyResult->setPlainText(error);
m_ui.verifyResult->setVisible(true);
m_ui.searchHash->setVisible(true);
}
}
void GameSummaryWidget::repopulateCurrentDetails()
{
auto lock = GameList::GetLock();
const GameList::Entry* entry = GameList::GetEntryForPath(m_entry_path.c_str());
if (entry)
{
populateDetails(entry);
m_dialog->setWindowTitle(QString::fromStdString(entry->title));
}
}
void GameSummaryWidget::setCustomTitle(const std::string& text)
{
m_ui.restoreTitle->setEnabled(!text.empty());
GameList::SaveCustomTitleForPath(m_entry_path, text);
repopulateCurrentDetails();
}
void GameSummaryWidget::setCustomRegion(int region)
{
m_ui.restoreRegion->setEnabled(region >= 0);
GameList::SaveCustomRegionForPath(m_entry_path, region);
repopulateCurrentDetails();
}
+4
View File
@@ -47,6 +47,10 @@ private:
void populateDiscPath(const GameList::Entry* entry);
void populateTrackList(const GameList::Entry* entry);
void setVerifyResult(QString error);
void repopulateCurrentDetails();
void setCustomTitle(const std::string& text);
void setCustomRegion(int region);
Ui::GameSummaryWidget m_ui;
SettingsDialog* m_dialog;
+196 -174
View File
@@ -34,11 +34,25 @@
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="title">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLineEdit" name="title">
<property name="placeholderText">
<string>Clear the line to restore the original title...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="restoreTitle">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Restore</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
@@ -140,170 +154,178 @@
</widget>
</item>
<item row="5" column="1">
<widget class="QComboBox" name="region">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="editable">
<bool>true</bool>
</property>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-B (Brazil)</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QComboBox" name="region">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-B (Brazil)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-C (China)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-HK (Hong Kong)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-J (Japan)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-K (Korea)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-T (Taiwan)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-U (US)</string>
</property>
</item>
<item>
<property name="text">
<string>Other</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-A (Australia)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-AF (South Africa)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-AU (Austria)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-BE (Belgium)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-E (Europe/Australia)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-F (France)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-FI (Finland)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-G (Germany)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-GR (Greece)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-I (Italy)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-IN (India)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-M (Europe/Australia)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-NL (Netherlands)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-NO (Norway)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-P (Portugal)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-PL (Poland)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-R (Russia)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-S (Spain)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-SC (Scandinavia)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-SW (Sweden)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-SWI (Switzerland)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-UK (United Kingdom)</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QPushButton" name="restoreRegion">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Restore</string>
</property>
</widget>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-C (China)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-HK (Hong Kong)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-J (Japan)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-K (Korea)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-T (Taiwan)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-U (US)</string>
</property>
</item>
<item>
<property name="text">
<string>Other</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-A (Australia)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-AF (South Africa)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-AU (Austria)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-BE (Belgium)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-E (Europe/Australia)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-F (France)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-FI (Finland)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-G (Germany)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-GR (Greece)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-I (Italy)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-IN (India)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-M (Europe/Australia)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-NL (Netherlands)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-NO (Norway)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-P (Portugal)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-PL (Poland)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-R (Russia)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-S (Spain)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-SC (Scandinavia)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-SW (Sweden)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-SWI (Switzerland)</string>
</property>
</item>
<item>
<property name="text">
<string extracomment="Leave the code as-is, translate the country's name.">PAL-UK (United Kingdom)</string>
</property>
</item>
</widget>
</layout>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_7">
@@ -488,22 +510,22 @@
<height>60</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="visible">
<bool>false</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="searchHash">
<property name="text">
<string>Search on Redump.org...</string>
</property>
<property name="visible">
<bool>false</bool>
</property>
<property name="text">
<string>Search on Redump.org...</string>
</property>
</widget>
</item>
</layout>
@@ -181,7 +181,6 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
//////////////////////////////////////////////////////////////////////////
// HW Renderer Fixes
//////////////////////////////////////////////////////////////////////////
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.halfScreenFix, "EmuCore/GS", "UserHacks_Half_Bottom_Override", -1, -1);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.cpuSpriteRenderBW, "EmuCore/GS", "UserHacks_CPUSpriteRenderBW", 0);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.cpuSpriteRenderLevel, "EmuCore/GS", "UserHacks_CPUSpriteRenderLevel", 0);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.cpuCLUTRender, "EmuCore/GS", "UserHacks_CPUCLUTRender", 0);
@@ -515,9 +514,6 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
// Hardware Fixes tab
{
dialog->registerWidgetHelp(m_ui.halfScreenFix, tr("Half Screen Fix"), tr("Automatic (Default)"),
tr("Control the half-screen fix detection on texture shuffling."));
dialog->registerWidgetHelp(m_ui.cpuSpriteRenderBW, tr("CPU Sprite Renderer Size"), tr("0 (Disabled)"), tr(""));
dialog->registerWidgetHelp(m_ui.cpuCLUTRender, tr("Software CLUT Render"), tr("0 (Disabled)"), tr(""));
@@ -1050,7 +1046,6 @@ void GraphicsSettingsWidget::resetManualHardwareFixes()
check_bool("EmuCore/GS", "UserHacks", false);
check_int("EmuCore/GS", "UserHacks_Half_Bottom_Override", -1);
check_int("EmuCore/GS", "UserHacks_CPUSpriteRenderBW", 0);
check_int("EmuCore/GS", "UserHacks_CPUCLUTRender", 0);
check_int("EmuCore/GS", "UserHacks_GPUTargetCLUTMode", 0);
@@ -787,25 +787,6 @@
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="halfScreenFix">
<item>
<property name="text">
<string>Automatic (Default)</string>
</property>
</item>
<item>
<property name="text">
<string>Force Disabled</string>
</property>
</item>
<item>
<property name="text">
<string>Force Enabled</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_36">
<property name="text">
+22 -13
View File
@@ -12,7 +12,7 @@
* 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 "MainWindow.h"
@@ -59,9 +59,10 @@ SettingsDialog::SettingsDialog(QWidget* parent)
}
SettingsDialog::SettingsDialog(QWidget* parent, std::unique_ptr<SettingsInterface> sif, const GameList::Entry* game,
std::string serial, u32 disc_crc)
std::string serial, u32 disc_crc, QString filename)
: QDialog(parent)
, m_sif(std::move(sif))
, m_filename(std::move(filename))
, m_serial(std::move(serial))
, m_disc_crc(disc_crc)
{
@@ -80,9 +81,9 @@ void SettingsDialog::setupUi(const GameList::Entry* game)
if (isPerGameSettings())
{
QString summary = tr("<strong>Summary</strong><hr>This page shows details about the selected game. Changing the Input "
"Profile will set the controller binding scheme for this game to whichever profile is chosen, instead "
"of the default (Shared) configuration. The track list and dump verification can be used to determine "
"if your disc image matches a known good dump. If it does not match, the game may be broken.");
"Profile will set the controller binding scheme for this game to whichever profile is chosen, instead "
"of the default (Shared) configuration. The track list and dump verification can be used to determine "
"if your disc image matches a known good dump. If it does not match, the game may be broken.");
if (game)
{
addWidget(new GameSummaryWidget(game, this, m_ui.settingsContainer), tr("Summary"),
@@ -153,7 +154,7 @@ void SettingsDialog::setupUi(const GameList::Entry* game)
addWidget(m_memory_card_settings = new MemoryCardSettingsWidget(this, m_ui.settingsContainer), tr("Memory Cards"),
QStringLiteral("memcard-line"),
tr("<strong>Memory Card Settings</strong><hr>Create and configure Memory Cards here.<br><br>Mouse over an option for "
"additional information."));
"additional information."));
addWidget(m_dev9_settings = new DEV9SettingsWidget(this, m_ui.settingsContainer), tr("Network & HDD"), QStringLiteral("global-line"),
tr("<strong>Network & HDD Settings</strong><hr>These options control the network connectivity and internal HDD storage of the "
@@ -330,6 +331,18 @@ bool SettingsDialog::eventFilter(QObject* object, QEvent* event)
return QDialog::eventFilter(object, event);
}
void SettingsDialog::setWindowTitle(const QString& title)
{
if (m_filename.isEmpty())
{
QDialog::setWindowTitle(title);
}
else
{
QDialog::setWindowTitle(QStringLiteral("%1 [%2]").arg(title, m_filename));
}
}
bool SettingsDialog::getEffectiveBoolValue(const char* section, const char* key, bool default_value) const
{
bool value;
@@ -544,16 +557,12 @@ void SettingsDialog::openGamePropertiesDialog(const GameList::Entry* game, const
}
std::string filename(VMManager::GetGameSettingsPath(serial, disc_crc));
std::unique_ptr<INISettingsInterface> sif = std::make_unique<INISettingsInterface>(std::move(filename));
std::unique_ptr<INISettingsInterface> sif = std::make_unique<INISettingsInterface>(filename);
if (FileSystem::FileExists(sif->GetFileName().c_str()))
sif->Load();
const QString window_title(tr("%1 [%2]")
.arg(QtUtils::StringViewToQString(title))
.arg(QtUtils::StringViewToQString(Path::GetFileName(sif->GetFileName()))));
SettingsDialog* dialog = new SettingsDialog(g_main_window, std::move(sif), game, std::move(serial), disc_crc);
dialog->setWindowTitle(window_title);
SettingsDialog* dialog = new SettingsDialog(g_main_window, std::move(sif), game, std::move(serial), disc_crc, QtUtils::StringViewToQString(Path::GetFileName(filename)));
dialog->setWindowTitle(QtUtils::StringViewToQString(title));
dialog->setModal(false);
dialog->show();
}
+5 -1
View File
@@ -51,7 +51,7 @@ class SettingsDialog final : public QDialog
public:
explicit SettingsDialog(QWidget* parent);
SettingsDialog(QWidget* parent, std::unique_ptr<SettingsInterface> sif, const GameList::Entry* game, std::string serial, u32 disc_crc);
SettingsDialog(QWidget* parent, std::unique_ptr<SettingsInterface> sif, const GameList::Entry* game, std::string serial, u32 disc_crc, QString filename = QString());
~SettingsDialog();
static void openGamePropertiesDialog(const GameList::Entry* game, const std::string_view& title, std::string serial, u32 disc_crc);
@@ -79,6 +79,8 @@ public:
void registerWidgetHelp(QObject* object, QString title, QString recommended_value, QString text);
bool eventFilter(QObject* object, QEvent* event) override;
void setWindowTitle(const QString& title);
QString getCategory() const;
void setCategory(const char* category);
@@ -145,6 +147,8 @@ private:
QObject* m_current_help_widget = nullptr;
QMap<QObject*, QString> m_widget_help_text_map;
QString m_filename;
std::string m_serial;
u32 m_disc_crc;
};
+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>
File diff suppressed because it is too large Load Diff
@@ -1,68 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="512"
height="512"
viewBox="0 0 512 512"
version="1.1"
id="svg5"
inkscape:export-filename="bitmap.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
sodipodi:docname="emulation-line-black.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#505050"
inkscape:document-units="px"
showgrid="false"
inkscape:zoom="2.6074562"
inkscape:cx="129.05298"
inkscape:cy="350.53321"
inkscape:window-width="1920"
inkscape:window-height="956"
inkscape:window-x="0"
inkscape:window-y="42"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs2" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:35;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none"
id="rect1182"
width="123.23695"
height="369.71085"
x="150.59978"
y="71.144577" />
<rect
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:35;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none"
id="rect1184"
width="78.14328"
height="307.17816"
x="273.83676"
y="133.67725" />
<path
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:25;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none"
d="m 357.42017,445.85541 h 84.05005 l -84.05007,-84.05007"
id="path1190" />
<path
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:25;stroke-linecap:square;stroke-linejoin:miter;stroke-dasharray:none"
d="M 142.36951,445.85542 H 67.083026 l 68.461054,-67.55757"
id="path1192" />
</g>
</svg>
<?xml version="1.0" encoding="UTF-8"?><svg id="emulation_Pcsx2_b" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 513 512"><defs><style>.cls-1{fill:none;}</style></defs><rect class="cls-1" width="513" height="512"/><path id="Ps2_-_By_Kam" d="M451.22,440.88l-76.33-77.28V116.18h-88.14V53.64H138.51V362.17l-77.72,78.69c-2.92,2.96-3.78,7.35-2.18,11.19,1.6,3.84,5.33,6.32,9.49,6.32h81.81c.07,0,.13-.02,.2-.02h211.39c.19,.01,.39,.03,.59,.03h81.81c4.16,0,7.89-2.48,9.49-6.32,1.6-3.84,.75-8.23-2.17-11.19Zm-199.48-17.52h-78.24V88.64h78.24V423.36Zm88.15,0h-53.14V151.18h53.14V423.36Z"/></svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 584 B

@@ -1 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="PCSX2_-_load" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 384"><defs><style>.cls-1{stroke-width:28px;}.cls-1,.cls-2{fill:none;stroke:#000;}.cls-2{stroke-width:29px;}</style></defs><g id="By_maxihplay"><path id="Forma_5" class="cls-1" d="M217.39,192.85c-.45,.33-52.68,43.98-52.68,43.98l52.04,47.7"/><path id="Forma_6" class="cls-1" d="M173.41,238.01l161.32-.73"/><path id="Forma_2" class="cls-1" d="M114.53,59.5l1.11,89.07s-1.77,2.71,5.57,3.34c7.33,.63,122.47,1.11,122.47,1.11,2.21-.93,3.56-3.18,3.34-5.57-.09-4.41,0-89.07,0-89.07"/><path id="Forma_1" class="cls-2" d="M322.31,290.47s-.12,21.8-.1,22.87c.31,13.33-11.31,12.25-11.31,12.25,0,0-244.42-1.35-252.73-1.11s-8.91-10.02-8.91-10.02V68.41s1.5-7.5,8.91-8.91,215.99,0,215.99,0l48.05,46.76,.27,72.95"/></g></svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.5.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="PCSX2_-_Save_b" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px" viewBox="0 0 384 384" style="enable-background:new 0 0 384 384;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;stroke:#000000;stroke-width:45;}
.st1{stroke:#000000;stroke-width:15;}
.st2{fill:none;stroke:#000000;stroke-width:30;stroke-linecap:round;stroke-linejoin:round;}
.st3{fill:none;stroke:#000000;stroke-width:28;}
</style>
<g id="By_maxihplay">
<g id="seta">
<line class="st0" x1="192" y1="331.9" x2="192" y2="243.1"/>
<path class="st1" d="M140.5,258l48-48c1.9-1.9,5-1.9,6.9,0l48,48c3.1,3.1,0.9,8.3-3.4,8.3h-96.1C139.6,266.3,137.4,261,140.5,258
L140.5,258z"/>
</g>
<g id="save">
<path class="st2" d="M112.5,318.2c-22.9-0.1-46.2-0.1-48.2-0.1c-8.3,0.2-8.9-10-8.9-10v-246c0,0,1.5-7.5,8.9-8.9
c7.4-1.4,216,0,216,0l48,46.8l0.3,72.9c-0.1,72.4-0.3,124.1-0.3,133.1c0,1.6,0.1,6.3-3,9.4c-2.7,2.6-8.3,2.9-8.3,2.9l-47.6-0.4"/>
<path id="Forma_2" class="st3" d="M120.7,53.4l1.1,89.1c0,0-1.8,2.7,5.6,3.3c7.3,0.6,122.5,1.1,122.5,1.1c2.2-0.9,3.6-3.2,3.3-5.6
c-0.1-4.4,0-89.1,0-89.1"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 818 B

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -1 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="PCSX2_-_load" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 384"><defs><style>.cls-1{stroke-width:28px;}.cls-1,.cls-2{fill:none;stroke:#000;}.cls-2{stroke-width:29px;}</style></defs><g id="By_maxihplay"><path id="Forma_6" class="cls-1" d="M309.03,236.01l-132.24-.6"/><path id="Forma_5" class="cls-1" d="M261.32,188.75c.45,.33,52.68,43.98,52.68,43.98l-52.04,47.7"/><path id="Forma_2" class="cls-1" d="M114.53,59.5l1.11,89.07s-1.77,2.71,5.57,3.34c7.33,.63,122.47,1.11,122.47,1.11,2.21-.93,3.56-3.18,3.34-5.57-.09-4.41,0-89.07,0-89.07"/><path id="Forma_1" class="cls-2" d="M322.31,290.47s-.12,21.8-.1,22.87c.31,13.33-11.31,12.25-11.31,12.25,0,0-244.42-1.35-252.73-1.11s-8.91-10.02-8.91-10.02V68.41s1.5-7.5,8.91-8.91,215.99,0,215.99,0l48.05,46.76,.27,72.95"/></g></svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.5.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="PCSX2_-_load_b" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px" viewBox="0 0 384 384" style="enable-background:new 0 0 384 384;" xml:space="preserve">
<style type="text/css">
.st0{stroke:#000000;stroke-width:15;}
.st1{fill:none;stroke:#000000;stroke-width:45;}
.st2{fill:none;stroke:#000000;stroke-width:30;stroke-linecap:round;stroke-linejoin:round;}
.st3{fill:none;stroke:#000000;stroke-width:28;}
</style>
<g id="By_maxihplay">
<g id="seta">
<path class="st0" d="M242.4,279.2l-48,48c-1.9,1.9-5,1.9-6.9,0l-48-48c-3.1-3.1-0.9-8.3,3.4-8.3H239
C243.3,270.9,245.5,276.2,242.4,279.2L242.4,279.2z"/>
<line class="st1" x1="190.9" y1="205.3" x2="190.9" y2="294.1"/>
</g>
<g id="load">
<path class="st2" d="M112.5,321.7c-22.9-0.1-46.2-0.1-48.2-0.1c-8.3,0.2-8.9-10-8.9-10V65.5c0,0,1.5-7.5,8.9-8.9
c7.4-1.4,216,0,216,0l48,46.8l0.3,72.9c-0.1,72.4-0.3,124.1-0.3,133.1c0,1.6,0.1,6.3-3,9.4c-2.7,2.6-8.3,2.9-8.3,2.9l-47.6-0.4"/>
<path id="Forma_2" class="st3" d="M120.7,56.6l1.1,89.1c0,0-1.8,2.7,5.6,3.3c7.3,0.6,122.5,1.1,122.5,1.1c2.2-0.9,3.6-3.2,3.3-5.6
c-0.1-4.4,0-89.1,0-89.1"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 818 B

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -1 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="guitar_PCSX2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 493.66 474.14"><defs><style>.cls-1,.cls-2{stroke:#000;stroke-miterlimit:10;}.cls-2{stroke-width:20px;}</style></defs><g id="By_maxihplay"><path id="corpo" class="cls-1" d="M188.41,192.55c-.75,14.56,6.75,47.85,20.03,67.76,19.32,28.97,42.12,37.78,54.54,43.83-17.76,8.99-43.47,27.14-53.18,55-1.18,3.26-1.97,6.71-2.27,10.24-3.93,46.09-40.35,63.32-70.26,64.13-14.57,.4-29.14-2.93-42.13-9.52-14.19-7.25-26.02-17.98-35.01-32.02-.41-.58-.81-1.15-1.22-1.73-10.27-14.2-16.49-30.76-17.93-47.84-1.42-16.1,1.39-31.83,7.86-45.71,2.65-5.68,10.86-21.28,28.05-31.02,12.09-6.86,22.94-7.51,28.71-8.28,17.19-2.32,61.18-15.77,82.82-64.84Zm19.53-71.29c-38.91,1.06-45.85,41.06-64.11,71.01-14.61,23.88-38.65,24.92-40.31,24.97h-.1C10.15,223.2-29.95,335.62,26.37,413.63c26.97,41.92,70.63,61.09,111.99,59.96,53.67-1.46,103.78-36.96,109.18-100.8h0c19.93-58.65,105.53-15.58,93.41-112.5-23.14,8.75-40.07,12.44-52.19,12.77-26.98,.73-31.85-18.13-27.99-30.95l-25.37-31.05c-20.51-52.62-2.68-69.18,10.16-86.16-6.4-1.49-32.54-3.87-37.62-3.63h0Z"/><path id="cordas" class="cls-2" d="M103.87,365.16l-2.97-3.25c-3.52-3.71-3.25-9.64,.56-13.17l65.75-61.02c3.8-3.52,9.64-3.25,13.17,.46l2.97,3.25c3.52,3.71,3.25,9.64-.56,13.17l-65.75,61.02c-3.71,3.52-9.64,3.25-13.17-.46h0Z"/><polygon id="braco" class="cls-2" points="239.3 255.5 223.65 236.65 365.87 104.18 382.89 121.88 239.3 255.5"/><path class="cls-1" d="M485.96,27.45l-16.94-18.24c-10.07-10.97-27.21-11.69-38.25-1.56l-68.38,63.39c-10.94,10.08-11.64,27.19-1.51,38.22l16.94,18.24c5.31,5.78,12.58,8.71,19.88,8.71,6.56,0,13.14-2.37,18.37-7.16l68.35-63.35c5.33-4.89,8.42-11.56,8.72-18.79,.29-7.2-2.23-14.09-7.17-19.47Z"/></g></svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.5.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="guitar_PCSX2_b" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px" viewBox="0 0 494.7 469.9" style="enable-background:new 0 0 494.7 469.9;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;}
.st1{stroke:#000000;stroke-width:16;stroke-miterlimit:10;}
.st2{stroke:#000000;stroke-width:20;stroke-miterlimit:10;}
.st3{stroke:#000000;stroke-miterlimit:10;}
</style>
<g id="By_Maxihplay">
<rect class="st0" width="494.7" height="469.9"/>
<path id="cordas" class="st1" d="M134.6,352.6l-2.7-2.5c-3.2-2.9-3.3-7.9-0.4-11l51.8-55.2c3-3.1,7.9-3.3,11.1-0.5l2.7,2.5
c3.2,2.9,3.3,7.9,0.4,11l-51.8,55.2C142.8,355.3,137.8,355.4,134.6,352.6L134.6,352.6z"/>
<polygon id="braco" class="st2" points="252.2,244.7 232.4,225.7 383.6,69.1 405,86.5 "/>
<path id="cabeca" class="st3" d="M488.2,39.1l-72.5,75.3l-50.7-13.3l-11-38.9l80.6-52.8c2.3-1.4,5-1.8,7.7-1.4l41.6,13.3
c0.5,0.2,5.2,2,6.8,7C492,32.1,491,36.2,488.2,39.1L488.2,39.1z"/>
<g id="corpo">
<path d="M202.9,140.8L202.9,140.8 M190.2,169.1l24,70.8c2.6,7.6,7.4,14.1,13.8,18.9l55.7,41c-1.4,0.6-2.8,1.2-4.2,1.8
c-11.7,5-25,10.6-38.2,18.9c-0.6,0.4-1.1,0.8-1.7,1.1c-3.7,2.6-15.1,10.7-25,36.4c-6.4,16.5-9.5,26.1-15.1,54.2
c-1,5-1.9,8.4-2.7,10.7c-14.2,1.9-30.3,3.7-46.5,3.7c-30.9,0-52.4-6.5-65.9-20l-0.3-0.3c-4.5-4.5-18.2-18.2-28.7-36.1
c-10.8-18.4-14.2-34.2-10.3-47.2c1.5-5.1,3.4-10.8,5.5-15.2c4.7-9.8,12.9-17.5,23-21.5c7.1-2.8,15.1-5.4,23.7-8.1
c13-4.2,27.7-8.9,42.2-15.6c9.3-4.3,16.5-12.1,20.1-21.8C165.3,226.3,179.7,193.2,190.2,169.1 M193.2,105.1
c-10.1,0-20.9,7.4-27.6,22.9c-9.4,21.8-34.2,77.5-42.4,99.3c-21.1,9.8-43.9,14.9-63.8,22.9c-19.2,7.7-34.8,22.2-43.7,40.9
c-3.2,6.6-5.5,13.8-7.6,20.6c-17.4,56.8,39,112.6,49,122.5c24.9,24.9,60.3,31.4,93.5,31.4c20.2,0,39.6-2.4,55.3-4.5
c13.5-1.8,25.2-6.1,32.1-41.4c5.2-26.5,7.8-33.8,13.2-47.6c3.2-8.2,7.4-15.9,11.1-18.6c21-13.3,44.1-18.7,65.1-32
c0.9-0.7,10-8.5,9.1-20.9c-0.8-10.6-8.4-16.7-9.6-17.6l-75.6-55.7l-34.7-102.1C212.2,112,203,105.1,193.2,105.1L193.2,105.1
L193.2,105.1z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

@@ -1,68 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="512"
height="512"
viewBox="0 0 512 512"
version="1.1"
id="svg5"
inkscape:export-filename="bitmap.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
sodipodi:docname="emulation-line.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#505050"
inkscape:document-units="px"
showgrid="false"
inkscape:zoom="2.6074562"
inkscape:cx="128.66947"
inkscape:cy="411.51219"
inkscape:window-width="1920"
inkscape:window-height="956"
inkscape:window-x="0"
inkscape:window-y="42"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs2" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:35;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none"
id="rect1182"
width="123.23695"
height="369.71085"
x="150.59978"
y="71.144577" />
<rect
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:35;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none"
id="rect1184"
width="78.14328"
height="307.17816"
x="273.83676"
y="133.67725" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:25;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none"
d="m 357.42017,445.85541 h 84.05005 l -84.05007,-84.05007"
id="path1190" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:25;stroke-linecap:square;stroke-linejoin:miter;stroke-dasharray:none"
d="M 142.36951,445.85542 H 67.083026 l 68.461054,-67.55757"
id="path1192" />
</g>
</svg>
<?xml version="1.0" encoding="UTF-8"?><svg id="emulation_Pcsx2_w" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 513 512"><defs><style>.cls-1{fill:#fff;}.cls-2{fill:none;}</style></defs><rect class="cls-2" width="513" height="512"/><path id="Ps2_-_By_Kam" class="cls-1" d="M451.22,440.88l-76.33-77.28V116.18h-88.14V53.64H138.51V362.17l-77.72,78.69c-2.92,2.96-3.78,7.35-2.18,11.19,1.6,3.84,5.33,6.32,9.49,6.32h81.81c.07,0,.13-.02,.2-.02h211.39c.19,.01,.39,.03,.59,.03h81.81c4.16,0,7.89-2.48,9.49-6.32,1.6-3.84,.75-8.23-2.17-11.19Zm-199.48-17.52h-78.24V88.64h78.24V423.36Zm88.15,0h-53.14V151.18h53.14V423.36Z"/></svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 616 B

@@ -1 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="PCSX2_-_Save" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 384"><defs><style>.cls-1{stroke-width:29px;}.cls-1,.cls-2{fill:none;stroke:#fff;}.cls-2{stroke-width:28px;}</style></defs><g id="By_maxihplay"><path id="Forma_5" class="cls-2" d="M217.39,192.85c-.45,.33-52.68,43.98-52.68,43.98l52.04,47.7"/><path id="Forma_6" class="cls-2" d="M173.41,238.01l161.32-.73"/><path id="Forma_2" class="cls-2" d="M114.53,59.5l1.11,89.07s-1.77,2.71,5.57,3.34c7.33,.63,122.47,1.11,122.47,1.11,2.21-.93,3.56-3.18,3.34-5.57-.09-4.41,0-89.07,0-89.07"/><path id="Forma_1" class="cls-1" d="M322.31,290.47s-.12,21.8-.1,22.87c.31,13.33-11.31,12.25-11.31,12.25,0,0-244.42-1.35-252.73-1.11s-8.91-10.02-8.91-10.02V68.41s1.5-7.5,8.91-8.91,215.99,0,215.99,0l48.05,46.76,.27,72.95"/></g></svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.5.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="PCSX2_-_Save_w" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px" viewBox="0 0 384 384" style="enable-background:new 0 0 384 384;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;stroke:#FFFFFF;stroke-width:45;}
.st1{fill:#FFFFFF;stroke:#FFFFFF;stroke-width:15;}
.st2{fill:none;stroke:#FFFFFF;stroke-width:30;stroke-linecap:round;stroke-linejoin:round;}
.st3{fill:none;stroke:#FFFFFF;stroke-width:28;}
</style>
<g id="By_maxihplay">
<g id="seta">
<line class="st0" x1="192" y1="331.9" x2="192" y2="243.1"/>
<path class="st1" d="M140.5,258l48-48c1.9-1.9,5-1.9,6.9,0l48,48c3.1,3.1,0.9,8.3-3.4,8.3h-96.1C139.6,266.3,137.4,261,140.5,258
L140.5,258z"/>
</g>
<g id="save">
<path class="st2" d="M112.5,318.2c-22.9-0.1-46.2-0.1-48.2-0.1c-8.3,0.2-8.9-10-8.9-10v-246c0,0,1.5-7.5,8.9-8.9
c7.4-1.4,216,0,216,0l48,46.8l0.3,72.9c-0.1,72.4-0.3,124.1-0.3,133.1c0,1.6,0.1,6.3-3,9.4c-2.7,2.6-8.3,2.9-8.3,2.9l-47.6-0.4"/>
<path id="Forma_2" class="st3" d="M120.7,53.4l1.1,89.1c0,0-1.8,2.7,5.6,3.3c7.3,0.6,122.5,1.1,122.5,1.1c2.2-0.9,3.6-3.2,3.3-5.6
c-0.1-4.4,0-89.1,0-89.1"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 818 B

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -1 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="PCSX2_-_load" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 384"><defs><style>.cls-1{stroke-width:29px;}.cls-1,.cls-2{fill:none;stroke:#fff;}.cls-2{stroke-width:28px;}</style></defs><g id="By_maxihplay"><path id="Forma_5" class="cls-2" d="M265.05,192.85c.45,.33,52.68,43.98,52.68,43.98l-52.04,47.7"/><path id="Forma_6" class="cls-2" d="M309.03,238.01l-132.24-.6"/><path id="Forma_2" class="cls-2" d="M114.53,59.5l1.11,89.07s-1.77,2.71,5.57,3.34c7.33,.63,122.47,1.11,122.47,1.11,2.21-.93,3.56-3.18,3.34-5.57-.09-4.41,0-89.07,0-89.07"/><path id="Forma_1" class="cls-1" d="M322.31,290.47s-.12,21.8-.1,22.87c.31,13.33-11.31,12.25-11.31,12.25,0,0-244.42-1.35-252.73-1.11s-8.91-10.02-8.91-10.02V68.41s1.5-7.5,8.91-8.91,215.99,0,215.99,0l48.05,46.76,.27,72.95"/></g></svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.5.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="PCSX2_-_Load_w" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px" viewBox="0 0 384 384" style="enable-background:new 0 0 384 384;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;stroke:#FFFFFF;stroke-width:15;}
.st1{fill:none;stroke:#FFFFFF;stroke-width:45;}
.st2{fill:none;stroke:#FFFFFF;stroke-width:30;stroke-linecap:round;stroke-linejoin:round;}
.st3{fill:none;stroke:#FFFFFF;stroke-width:28;}
</style>
<g id="By_maxihplay">
<g id="seta">
<path class="st0" d="M242.4,279.2l-48,48c-1.9,1.9-5,1.9-6.9,0l-48-48c-3.1-3.1-0.9-8.3,3.4-8.3H239
C243.3,270.9,245.5,276.2,242.4,279.2L242.4,279.2z"/>
<line class="st1" x1="190.9" y1="205.3" x2="190.9" y2="294.1"/>
</g>
<g id="load">
<path class="st2" d="M112.5,321.7c-22.9-0.1-46.2-0.1-48.2-0.1c-8.3,0.2-8.9-10-8.9-10V65.5c0,0,1.5-7.5,8.9-8.9
c7.4-1.4,216,0,216,0l48,46.8l0.3,72.9c-0.1,72.4-0.3,124.1-0.3,133.1c0,1.6,0.1,6.3-3,9.4c-2.7,2.6-8.3,2.9-8.3,2.9l-47.6-0.4"/>
<path id="Forma_2" class="st3" d="M120.7,56.6l1.1,89.1c0,0-1.8,2.7,5.6,3.3c7.3,0.6,122.5,1.1,122.5,1.1c2.2-0.9,3.6-3.2,3.3-5.6
c-0.1-4.4,0-89.1,0-89.1"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 818 B

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -1 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="guitar_PCSX2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 492.66 473.14"><defs><style>.cls-1,.cls-2{fill:#fff;}.cls-2{stroke:#fff;stroke-miterlimit:10;stroke-width:20px;}</style></defs><g id="By_maxihplay"><path id="corpo" class="cls-1" d="M187.91,192.05c-.75,14.56,6.75,47.85,20.03,67.76,19.32,28.97,42.12,37.78,54.54,43.83-17.76,8.99-43.47,27.14-53.18,55-1.18,3.26-1.97,6.71-2.27,10.24-3.93,46.09-40.35,63.32-70.26,64.13-14.57,.4-29.14-2.93-42.13-9.52-14.19-7.25-26.02-17.98-35.01-32.02-.41-.58-.81-1.15-1.22-1.73-10.27-14.2-16.49-30.76-17.93-47.84-1.42-16.1,1.39-31.83,7.86-45.71,2.65-5.68,10.86-21.28,28.05-31.02,12.09-6.86,22.94-7.51,28.71-8.28,17.19-2.32,61.18-15.77,82.82-64.84Zm19.53-71.29c-38.91,1.06-45.85,41.06-64.11,71.01-14.61,23.88-38.65,24.92-40.31,24.97h-.1C9.65,222.7-30.45,335.12,25.87,413.13c26.97,41.92,70.63,61.09,111.99,59.96,53.67-1.46,103.78-36.96,109.18-100.8h0c19.93-58.65,105.53-15.58,93.41-112.5-23.14,8.75-40.07,12.44-52.19,12.77-26.98,.73-31.85-18.13-27.99-30.95l-25.37-31.05c-20.51-52.62-2.68-69.18,10.16-86.16-6.4-1.49-32.54-3.87-37.62-3.63h0Z"/><path id="cordas" class="cls-2" d="M103.37,364.66l-2.97-3.25c-3.52-3.71-3.25-9.64,.56-13.17l65.75-61.02c3.8-3.52,9.64-3.25,13.17,.46l2.97,3.25c3.52,3.71,3.25,9.64-.56,13.17l-65.75,61.02c-3.71,3.52-9.64,3.25-13.17-.46h0Z"/><polygon id="braco" class="cls-2" points="238.8 255 223.15 236.15 365.37 103.68 382.39 121.38 238.8 255"/><path class="cls-1" d="M485.46,26.95l-16.94-18.24c-10.07-10.97-27.21-11.69-38.25-1.56l-68.38,63.39c-10.94,10.08-11.64,27.19-1.51,38.22l16.94,18.24c5.31,5.78,12.58,8.71,19.88,8.71,6.56,0,13.14-2.37,18.37-7.16l68.35-63.35c5.33-4.89,8.42-11.56,8.72-18.79,.29-7.2-2.23-14.09-7.17-19.47Z"/></g></svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.5.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="guitar_PCSX2_w" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px" viewBox="0 0 494.7 469.9" style="enable-background:new 0 0 494.7 469.9;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;}
.st1{fill:#FFFFFF;stroke:#FFFFFF;stroke-width:16;stroke-miterlimit:10;}
.st2{fill:#FFFFFF;stroke:#FFFFFF;stroke-width:20;stroke-miterlimit:10;}
.st3{fill:#FFFFFF;stroke:#FFFFFF;stroke-miterlimit:10;}
.st4{fill:#FFFFFF;}
</style>
<g id="By_Maxihplay">
<rect class="st0" width="494.7" height="469.9"/>
<path id="cordas" class="st1" d="M134.6,352.6l-2.7-2.5c-3.2-2.9-3.3-7.9-0.4-11l51.8-55.2c3-3.1,7.9-3.3,11.1-0.5l2.7,2.5
c3.2,2.9,3.3,7.9,0.4,11l-51.8,55.2C142.8,355.3,137.8,355.4,134.6,352.6L134.6,352.6z"/>
<polygon id="braco" class="st2" points="252.2,244.7 232.4,225.7 383.6,69.1 405,86.5 "/>
<path id="cabeca" class="st3" d="M488.2,39.1l-72.5,75.3l-50.7-13.3l-11-38.9l80.6-52.8c2.3-1.4,5-1.8,7.7-1.4l41.6,13.3
c0.5,0.2,5.2,2,6.8,7C492,32.1,491,36.2,488.2,39.1L488.2,39.1z"/>
<g id="corpo">
<path class="st4" d="M202.9,140.8L202.9,140.8 M190.2,169.1l24,70.8c2.6,7.6,7.4,14.1,13.8,18.9l55.7,41c-1.4,0.6-2.8,1.2-4.2,1.8
c-11.7,5-25,10.6-38.2,18.9c-0.6,0.4-1.1,0.8-1.7,1.1c-3.7,2.6-15.1,10.7-25,36.4c-6.4,16.5-9.5,26.1-15.1,54.2
c-1,5-1.9,8.4-2.7,10.7c-14.2,1.9-30.3,3.7-46.5,3.7c-30.9,0-52.4-6.5-65.9-20l-0.3-0.3c-4.5-4.5-18.2-18.2-28.7-36.1
c-10.8-18.4-14.2-34.2-10.3-47.2c1.5-5.1,3.4-10.8,5.5-15.2c4.7-9.8,12.9-17.5,23-21.5c7.1-2.8,15.1-5.4,23.7-8.1
c13-4.2,27.7-8.9,42.2-15.6c9.3-4.3,16.5-12.1,20.1-21.8C165.3,226.3,179.7,193.2,190.2,169.1 M193.2,105.1
c-10.1,0-20.9,7.4-27.6,22.9c-9.4,21.8-34.2,77.5-42.4,99.3c-21.1,9.8-43.9,14.9-63.8,22.9c-19.2,7.7-34.8,22.2-43.7,40.9
c-3.2,6.6-5.5,13.8-7.6,20.6c-17.4,56.8,39,112.6,49,122.5c24.9,24.9,60.3,31.4,93.5,31.4c20.2,0,39.6-2.4,55.3-4.5
c13.5-1.8,25.2-6.1,32.1-41.4c5.2-26.5,7.8-33.8,13.2-47.6c3.2-8.2,7.4-15.9,11.1-18.6c21-13.3,44.1-18.7,65.1-32
c0.9-0.7,10-8.5,9.1-20.9c-0.8-10.6-8.4-16.7-9.6-17.6l-75.6-55.7l-34.7-102.1C212.2,112,203,105.1,193.2,105.1L193.2,105.1
L193.2,105.1z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

+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"
+3 -2
View File
@@ -765,7 +765,6 @@ struct Pcsx2Config
int SkipDrawEnd = 0;
GSHWAutoFlushLevel UserHacks_AutoFlush = GSHWAutoFlushLevel::Disabled;
s8 UserHacks_HalfBottomOverride = -1;
s8 UserHacks_HalfPixelOffset = 0;
s8 UserHacks_RoundSprite = 0;
s32 UserHacks_TCOffsetX = 0;
@@ -1262,12 +1261,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
+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
+3 -3
View File
@@ -1088,7 +1088,7 @@ void MTSAH( std::string& output ) { _sap("mtsah\t%s, 0x%04X") GPR_REG[DECODE_R
//***************************SPECIAL 2 CPU OPCODES*******************
const char* pmfhl_sub[] = { "lw", "uw", "slw", "lh", "sh" };
const char* pmfhl_sub[] = {"lw", "uw", "slw", "lh", "sh", "??", "??"};
void MADD( std::string& output ) { _sap("madd\t%s, %s %s") GPR_REG[DECODE_RD],GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); }
void MADDU( std::string& output ) { _sap("maddu\t%s, %s %s") GPR_REG[DECODE_RD],GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]);}
@@ -1104,8 +1104,8 @@ void MULTU1( std::string& output ) { _sap("multu1\t%s, %s, %s") GPR_REG[
void DIV1( std::string& output ) { _sap("div1\t%s, %s") GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); }
void DIVU1( std::string& output ) { _sap("divu1\t%s, %s") GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); }
//that have parametres that i haven't figure out how to display...
void PMFHL( std::string& output ) { _sap("pmfhl.%s \t%s") pmfhl_sub[DECODE_SA], GPR_REG[DECODE_RD]); }
void PMTHL( std::string& output ) { _sap("pmthl.%s \t%s") pmfhl_sub[DECODE_SA], GPR_REG[DECODE_RS]); }
void PMFHL( std::string& output ) { _sap("pmfhl.%s \t%s") pmfhl_sub[DECODE_SA & 0x7], GPR_REG[DECODE_RD]); }
void PMTHL( std::string& output ) { _sap("pmthl.%s \t%s") pmfhl_sub[DECODE_SA & 0x7], GPR_REG[DECODE_RS]); }
void PSLLH( std::string& output ) { _sap("psllh \t%s, %s, 0x%02X") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT], DECODE_SA); }
void PSRLH( std::string& output ) { _sap("psrlh \t%s, %s, 0x%02X") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT], DECODE_SA);}
void PSRAH( std::string& output ) { _sap("psrah \t%s, %s, 0x%02X") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT], DECODE_SA);}
+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);
+299 -140
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 int 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);
@@ -2845,13 +2920,14 @@ __forceinline bool GSState::IsAutoFlushDraw(u32 prim)
const u32 frame_mask = GSLocalMemory::m_psm[m_context->FRAME.PSM].fmsk;
const bool frame_hit = m_context->FRAME.Block() == m_context->TEX0.TBP0 && !(m_context->TEST.ATE && m_context->TEST.ATST == 0 && m_context->TEST.AFAIL == 2) && ((m_context->FRAME.FBMSK & frame_mask) != frame_mask);
// There's a strange behaviour we need to test on a PS2 here, if the FRAME is a Z format, like Powerdrome something swaps over, and it seems Alpha Fail of "FB Only" writes to the Z.. it's odd.
const bool zbuf_hit = (m_context->ZBUF.Block() == m_context->TEX0.TBP0) && !(m_context->TEST.ATE && m_context->TEST.ATST == 0 && m_context->TEST.AFAIL != 2) && !m_context->ZBUF.ZMSK;
const bool z_needed = !(m_context->TEST.ATE && m_context->TEST.ATST == 0 && m_context->TEST.AFAIL != 2) && !m_context->ZBUF.ZMSK;
const bool zbuf_hit = (m_context->ZBUF.Block() == m_context->TEX0.TBP0) && z_needed;
const u32 frame_z_psm = frame_hit ? m_context->FRAME.PSM : m_context->ZBUF.PSM;
const u32 frame_z_bp = frame_hit ? m_context->FRAME.Block() : m_context->ZBUF.Block();
if ((frame_hit || zbuf_hit) && GSUtil::HasSharedBits(frame_z_bp, frame_z_psm, m_context->TEX0.TBP0, m_context->TEX0.PSM))
return true;
return false;
}
@@ -2948,10 +3024,6 @@ __forceinline void GSState::HandleAutoFlush()
break;
}
const int page_mask_x = ~(GSLocalMemory::m_psm[m_context->TEX0.PSM].pgs.x - 1);
const int page_mask_y = ~(GSLocalMemory::m_psm[m_context->TEX0.PSM].pgs.y - 1);
const GSVector4i page_mask = { page_mask_x, page_mask_y, page_mask_x, page_mask_y };
GSVector4i tex_coord;
// Prepare the currently processed vertex.
if (PRIM->FST)
@@ -2970,6 +3042,8 @@ __forceinline void GSState::HandleAutoFlush()
GSVector4i tex_rect = tex_coord.xyxy();
const GSLocalMemory::psm_t tex_psm = GSLocalMemory::m_psm[m_context->TEX0.PSM];
const GSLocalMemory::psm_t frame_psm = GSLocalMemory::m_psm[m_context->FRAME.PSM];
// Get the rest of the rect.
for (int i = 0; i < (n - 1); i++)
{
@@ -2995,6 +3069,12 @@ __forceinline void GSState::HandleAutoFlush()
tex_rect.w = std::max(tex_rect.w, tex_coord.y);
}
// If the draw was 1 line thick, make it larger as rects are exclusive of ends.
if (tex_rect.x == tex_rect.z)
tex_rect += GSVector4i::cxpr(0, 0, 1, 0);
if (tex_rect.y == tex_rect.w)
tex_rect += GSVector4i::cxpr(0, 0, 0, 1);
// Get the last texture position from the last draw.
const GSVertex* v = &m_vertex.buff[m_index.buff[m_index.tail - (index_swap ? n : 1)]];
@@ -3012,27 +3092,75 @@ __forceinline void GSState::HandleAutoFlush()
tex_coord.y = (int)((1 << m_context->TEX0.TH) * t);
}
const GSVector4i last_tex_page = tex_coord.xyxy() & page_mask;
const GSVector4i tex_page = tex_rect.xyxy() & page_mask;
const int clamp_minu = m_context->CLAMP.MINU;
const int clamp_maxu = m_context->CLAMP.MAXU;
const int clamp_minv = m_context->CLAMP.MINV;
const int clamp_maxv = m_context->CLAMP.MAXV;
switch (m_context->CLAMP.WMS)
{
case CLAMP_REGION_CLAMP:
tex_rect.x = std::max(tex_rect.x, clamp_minu);
tex_rect.z = std::max(tex_rect.z, clamp_minu);
tex_coord.x = std::max(tex_coord.x, clamp_minu);
tex_rect.x = std::min(tex_rect.x, clamp_maxu);
tex_rect.z = std::min(tex_rect.z, clamp_maxu);
tex_coord.x = std::min(tex_coord.x, clamp_maxu);
break;
case CLAMP_REGION_REPEAT:
tex_rect.x = std::max(tex_rect.x, clamp_maxu);
tex_rect.z = std::max(tex_rect.z, clamp_maxu);
tex_coord.x = std::max(tex_coord.x, clamp_maxu);
tex_rect.x = std::min(tex_rect.x, (clamp_maxu | clamp_minu));
tex_rect.z = std::min(tex_rect.z, (clamp_maxu | clamp_minu));
tex_coord.x = std::min(tex_coord.x, (clamp_maxu | clamp_minu));
break;
default:
break;
}
switch (m_context->CLAMP.WMT)
{
case CLAMP_REGION_CLAMP:
tex_rect.y = std::max(tex_rect.y, clamp_minv);
tex_rect.w = std::max(tex_rect.w, clamp_minv);
tex_coord.y = std::max(tex_coord.y, clamp_minv);
tex_rect.y = std::min(tex_rect.y, clamp_maxv);
tex_rect.w = std::min(tex_rect.w, clamp_maxv);
tex_coord.y = std::min(tex_coord.y, clamp_maxv);
break;
case CLAMP_REGION_REPEAT:
tex_rect.y = std::max(tex_rect.y, clamp_maxv);
tex_rect.w = std::max(tex_rect.w, clamp_maxv);
tex_coord.y = std::max(tex_coord.y, clamp_maxv);
tex_rect.y = std::min(tex_rect.y, (clamp_maxv | clamp_minv));
tex_rect.w = std::min(tex_rect.w, (clamp_maxv | clamp_minv));
tex_coord.y = std::min(tex_coord.y, (clamp_maxv | clamp_minv));
break;
default:
break;
}
// Nothing being drawn intersect with the new texture, so no point in checking further.
if (tex_psm.depth == frame_psm.depth && tex_rect.rintersect(temp_draw_rect).rempty())
return;
const int tex_page_mask_x = ~(tex_psm.pgs.x - 1);
const int tex_page_mask_y = ~(tex_psm.pgs.y - 1);
const GSVector4i tex_page_mask = { tex_page_mask_x, tex_page_mask_y, tex_page_mask_x, tex_page_mask_y };
const GSVector4i last_tex_page = tex_coord.xyxy() & tex_page_mask;
const GSVector4i tex_page = tex_rect.xyxy() & tex_page_mask;
// Crossed page since last draw end
if (!tex_page.eq(last_tex_page) || m_texflush_flag)
{
const u32 frame_mask = GSLocalMemory::m_psm[m_context->TEX0.PSM].fmsk;
const bool frame_hit = (m_context->FRAME.Block() == m_context->TEX0.TBP0) && !(m_context->TEST.ATE && m_context->TEST.ATST == 0 && m_context->TEST.AFAIL == 2) && ((m_context->FRAME.FBMSK & frame_mask) != frame_mask);
const u32 frame_z_psm = frame_hit ? m_context->FRAME.PSM : m_context->ZBUF.PSM;
// Make sure the format matches, otherwise the coordinates aren't gonna match, so the draws won't intersect.
if (GSUtil::HasCompatibleBits(m_context->TEX0.PSM, frame_z_psm) && (m_context->FRAME.FBW == m_context->TEX0.TBW))
if (tex_psm.bpp == frame_psm.bpp && (m_context->FRAME.FBW == m_context->TEX0.TBW))
{
// If the draw was 1 line thick, make it larger as rects are exclusive of ends.
if (tex_rect.x == tex_rect.z)
tex_rect += GSVector4i(0, 0, 1, 0);
if (tex_rect.y == tex_rect.w)
tex_rect += GSVector4i(0, 0, 0, 1);
const GSVector2i offset = GSVector2i(m_context->XYOFFSET.OFX, m_context->XYOFFSET.OFY);
const GSVector4i scissor = m_context->scissor.in;
GSVector4i old_tex_rect = GSVector4i::zero();
GSVector4i old_draw_rect = GSVector4i::zero();
int current_draw_end = m_index.tail;
while (current_draw_end >= n)
@@ -3041,29 +3169,49 @@ __forceinline void GSState::HandleAutoFlush()
{
const GSVertex* v = &m_vertex.buff[m_index.buff[i]];
tex_coord.x = (static_cast<int>(v->XYZ.X) - offset.x) >> 4;
tex_coord.y = (static_cast<int>(v->XYZ.Y) - offset.y) >> 4;
if (i == (current_draw_end - 1))
if (prim == GS_SPRITE && (i & 1))
{
old_tex_rect = tex_coord.xyxy();
tex_coord.x = ((static_cast<int>(v->XYZ.X) - offset.x) >> 4) - 1;
tex_coord.y = ((static_cast<int>(v->XYZ.Y) - offset.y) >> 4) - 1;
}
else
{
old_tex_rect.x = std::min(old_tex_rect.x, tex_coord.x);
old_tex_rect.z = std::max(old_tex_rect.z, tex_coord.x);
old_tex_rect.y = std::min(old_tex_rect.y, tex_coord.y);
old_tex_rect.w = std::max(old_tex_rect.w, tex_coord.y);
tex_coord.x = (static_cast<int>(v->XYZ.X) - offset.x) >> 4;
tex_coord.y = (static_cast<int>(v->XYZ.Y) - offset.y) >> 4;
}
if (tex_psm.depth != frame_psm.depth)
{
tex_coord.x ^= (frame_psm.pgs.x / 2);
tex_coord.y ^= (frame_psm.pgs.y / 2);
}
if (prim == GS_SPRITE && (i & 1))
{
tex_coord.x += 1;
tex_coord.y += 1;
}
if (i == (current_draw_end - 1))
{
old_draw_rect = tex_coord.xyxy();
}
else
{
old_draw_rect.x = std::min(old_draw_rect.x, tex_coord.x);
old_draw_rect.z = std::max(old_draw_rect.z, tex_coord.x);
old_draw_rect.y = std::min(old_draw_rect.y, tex_coord.y);
old_draw_rect.w = std::max(old_draw_rect.w, tex_coord.y);
}
}
if (old_tex_rect.x == old_tex_rect.z)
old_tex_rect += GSVector4i(0, 0, 1, 0);
if (old_tex_rect.y == old_tex_rect.w)
old_tex_rect += GSVector4i(0, 0, 0, 1);
if (old_draw_rect.x == old_draw_rect.z)
old_draw_rect += GSVector4i::cxpr(0, 0, 1, 0);
if (old_draw_rect.y == old_draw_rect.w)
old_draw_rect += GSVector4i::cxpr(0, 0, 0, 1);
old_tex_rect = tex_rect.rintersect(old_tex_rect);
if (!old_tex_rect.rintersect(scissor).rempty())
old_draw_rect = tex_rect.rintersect(old_draw_rect);
if (!old_draw_rect.rintersect(scissor).rempty())
{
Flush(GSFlushReason::AUTOFLUSH);
return;
@@ -3074,26 +3222,27 @@ __forceinline void GSState::HandleAutoFlush()
}
else // Storage of the TEX and FRAME/Z is different, so uhh, just fall back to flushing each page. It's slower, sorry.
{
if (m_context->FRAME.FBW == m_context->TEX0.TBW)
const int frame_width = (m_context->FRAME.FBW * 64) / frame_psm.pgs.x;
const int tex_width = (m_context->TEX0.TBW * 64) / tex_psm.pgs.x;
if ((frame_width == tex_width) || ((tex_rect.w / tex_psm.pgs.y) <= 1 && frame_width >= tex_width))
{
tex_rect += GSVector4i(0, 0, tex_page_mask.z, tex_page_mask.w); // round up to the next page as we will be comparing by page.
//We know we've changed page, so let's set the dimension to cover the page they're in (for different pixel orders)
tex_rect = tex_rect & page_mask;
tex_rect += GSVector4i(0, 0, 1, 1); // Intersect goes on space inside the rect
tex_rect.z += GSLocalMemory::m_psm[m_context->TEX0.PSM].pgs.x;
tex_rect.w += GSLocalMemory::m_psm[m_context->TEX0.PSM].pgs.y;
m_vt.Update(m_vertex.buff, m_index.buff, m_vertex.tail - m_vertex.head, m_index.tail, GSUtil::GetPrimClass(PRIM->PRIM));
GSVector4i area_out = GSVector4i(m_vt.m_min.p.xyxy(m_vt.m_max.p)).rintersect(m_context->scissor.in);
area_out = area_out & page_mask;
area_out += GSVector4i(0, 0, 1, 1); // Intersect goes on space inside the rect
area_out.z += GSLocalMemory::m_psm[m_context->TEX0.PSM].pgs.x;
area_out.w += GSLocalMemory::m_psm[m_context->TEX0.PSM].pgs.y;
tex_rect &= tex_page_mask;
tex_rect = GSVector4i(tex_rect.x / tex_psm.pgs.x, tex_rect.y / tex_psm.pgs.y, tex_rect.z / tex_psm.pgs.x, tex_rect.w / tex_psm.pgs.y);
const int frame_page_mask_x = ~(frame_psm.pgs.x - 1);
const int frame_page_mask_y = ~(frame_psm.pgs.y - 1);
const GSVector4i frame_page_mask = { frame_page_mask_x, frame_page_mask_y, frame_page_mask_x, frame_page_mask_y };
GSVector4i area_out = temp_draw_rect;
area_out += GSVector4i(0, 0, frame_page_mask.z, frame_page_mask.w); // round up to the next page as we will be comparing by page.
area_out &= frame_page_mask;
area_out = GSVector4i(area_out.x / frame_psm.pgs.x, area_out.y / frame_psm.pgs.y, area_out.z / frame_psm.pgs.x, area_out.w / frame_psm.pgs.y);
if (!area_out.rintersect(tex_rect).rempty())
Flush(GSFlushReason::AUTOFLUSH);
}
else // Page width is different, so it's much more difficult to calculate where it's modifying.
else // Formats are too different so just flush it.
Flush(GSFlushReason::AUTOFLUSH);
}
}
@@ -3368,6 +3517,11 @@ __forceinline void GSState::VertexKick(u32 skip)
case GS_SPRITE:
buff[0] = static_cast<u16>(head + 0);
buff[1] = static_cast<u16>(head + 1);
// Update the first vert's Q for ease of doing Autoflush
if (!m_env.PRIM.FST)
m_vertex.buff[buff[0]].RGBAQ.Q = m_vertex.buff[buff[1]].RGBAQ.Q;
m_vertex.head = head + 2;
m_vertex.next = head + 2;
m_index.tail += 2;
@@ -3689,7 +3843,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())
{
@@ -3711,12 +3866,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 -1
View File
@@ -175,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
@@ -393,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);
+1
View File
@@ -316,6 +316,7 @@ struct alignas(16) GSHWDrawConfig
u32 ltf : 1;
// Shuffle and fbmask effect
u32 shuffle : 1;
u32 shuffle_same : 1;
u32 real16src: 1;
u32 read_ba : 1;
u32 write_rg : 1;
+1
View File
@@ -40,6 +40,7 @@ protected:
GSVector2i m_real_size{0, 0};
bool m_texture_shuffle = false;
bool m_copy_16bit_to_target_shuffle = false;
bool m_same_group_texture_shuffle = false;
virtual GSTexture* GetOutput(int i, float& scale, int& y_offset) = 0;
virtual GSTexture* GetFeedbackOutput(float& scale) { return nullptr; }
@@ -138,6 +138,7 @@ void GSDevice11::SetupPS(const PSSelector& sel, const GSHWDrawConfig::PSConstant
sm.AddMacro("PS_POINT_SAMPLER", sel.point_sampler);
sm.AddMacro("PS_REGION_RECT", sel.region_rect);
sm.AddMacro("PS_SHUFFLE", sel.shuffle);
sm.AddMacro("PS_SHUFFLE_SAME", sel.shuffle_same);
sm.AddMacro("PS_READ_BA", sel.read_ba);
sm.AddMacro("PS_READ16_SRC", sel.real16src);
sm.AddMacro("PS_CHANNEL_FETCH", sel.channel);
+1
View File
@@ -2743,6 +2743,7 @@ const ID3DBlob* GSDevice12::GetTFXPixelShader(const GSHWDrawConfig::PSSelector&
sm.AddMacro("PS_POINT_SAMPLER", sel.point_sampler);
sm.AddMacro("PS_REGION_RECT", sel.region_rect);
sm.AddMacro("PS_SHUFFLE", sel.shuffle);
sm.AddMacro("PS_SHUFFLE_SAME", sel.shuffle_same);
sm.AddMacro("PS_READ_BA", sel.read_ba);
sm.AddMacro("PS_READ16_SRC", sel.real16src);
sm.AddMacro("PS_CHANNEL_FETCH", sel.channel);
+43 -48
View File
@@ -523,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)
@@ -864,6 +851,44 @@ bool GSHwHack::GSC_MetalGearSolid3(GSRendererHW& r, int& skip)
return true;
}
bool GSHwHack::GSC_BigMuthaTruckers(GSRendererHW& r, int& skip)
{
// Rendering pattern:
// CRTC frontbuffer at 0x0 is interlaced (half vertical resolution),
// game needs to do a depth effect (so green channel to alpha),
// but there is a vram limitation so green is pushed into the alpha channel of the CRCT buffer,
// vertical resolution is half so only half is processed at once
// We, however, don't have this limitation so we'll replace the draw with a full-screen TS.
const GIFRegTEX0& Texture = RTEX0;
GIFRegTEX0 Frame = {};
Frame.TBW = RFRAME.FBW;
Frame.TBP0 = RFRAME.Block();
const int frame_offset_pal = GSLocalMemory::GetEndBlockAddress(0xa00, 10, PSMCT32, GSVector4i(0, 0, 640, 256)) + 1;
const int frame_offset_ntsc = GSLocalMemory::GetEndBlockAddress(0xa00, 10, PSMCT32, GSVector4i(0, 0, 640, 224)) + 1;
const GSVector4i rect = GSVector4i(r.m_vt.m_min.p.x, r.m_vt.m_min.p.y, r.m_vt.m_max.p.x, r.m_vt.m_max.p.y);
if (RPRIM->TME && Frame.TBW == 10 && Texture.TBW == 10 && Texture.PSM == PSMCT16 && ((rect.w == 512 && Frame.TBP0 == frame_offset_pal) || (Frame.TBP0 == frame_offset_ntsc && rect.w == 448)))
{
// 224 ntsc, 256 pal.
GL_INS("GSC_BigMuthaTruckers half bottom offset %d", r.m_context->XYOFFSET.OFX >> 4);
const size_t count = r.m_vertex.next;
GSVertex* v = &r.m_vertex.buff[0];
const u16 offset = (u16)rect.w * 16;
for (size_t i = 0; i < count; i++)
v[i].XYZ.Y += offset;
r.m_vt.m_min.p.y += rect.w;
r.m_vt.m_max.p.y += rect.w;
r.m_cached_ctx.FRAME.FBP = 0x50; // 0xA00 >> 5
}
return true;
}
bool GSHwHack::OI_PointListPalette(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
{
const u32 n_vertices = r.m_vertex.next;
@@ -917,37 +942,6 @@ bool GSHwHack::OI_PointListPalette(GSRendererHW& r, GSTexture* rt, GSTexture* ds
return true;
}
bool GSHwHack::OI_BigMuthaTruckers(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
{
// Rendering pattern:
// CRTC frontbuffer at 0x0 is interlaced (half vertical resolution),
// game needs to do a depth effect (so green channel to alpha),
// but there is a vram limitation so green is pushed into the alpha channel of the CRCT buffer,
// vertical resolution is half so only half is processed at once
// We, however, don't have this limitation so we'll replace the draw with a full-screen TS.
const GIFRegTEX0& Texture = RTEX0;
GIFRegTEX0 Frame = {};
Frame.TBW = RFRAME.FBW;
Frame.TBP0 = RFRAME.Block();
if (RPRIM->TME && Frame.TBW == 10 && Texture.TBW == 10 && Frame.TBP0 == 0x00a00 && Texture.PSM == PSMT8H && (r.m_r.y == 256 || r.m_r.y == 224))
{
// 224 ntsc, 256 pal.
GL_INS("OI_BigMuthaTruckers half bottom offset");
const size_t count = r.m_vertex.next;
GSVertex* v = &r.m_vertex.buff[0];
const u16 offset = (u16)r.m_r.y * 16;
for (size_t i = 0; i < count; i++)
v[i].V += offset;
}
return true;
}
bool GSHwHack::OI_DBZBTGames(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
{
if (t && t->m_from_target) // Avoid slow framebuffer readback
@@ -1065,9 +1059,11 @@ bool GSHwHack::OI_SonicUnleashed(GSRendererHW& r, GSTexture* rt, GSTexture* ds,
GSTextureCache::Target* rt_again = g_texture_cache->LookupTarget(Frame, src_size, src->m_scale, GSTextureCache::RenderTarget);
if (rt_again->m_unscaled_size.x < src->m_unscaled_size.x || rt_again->m_unscaled_size.y < src->m_unscaled_size.y)
{
rt_again->ResizeTexture(std::max(rt_again->m_unscaled_size.x, src->m_unscaled_size.x),
std::max(rt_again->m_unscaled_size.y, src->m_unscaled_size.y));
GSVector2i new_size = GSVector2i(std::max(rt_again->m_unscaled_size.x, src->m_unscaled_size.x),
std::max(rt_again->m_unscaled_size.y, src->m_unscaled_size.y));
rt_again->ResizeTexture(new_size.x, new_size.y);
rt = rt_again->m_texture;
rt_size = new_size;
}
}
@@ -1438,7 +1434,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),
@@ -1471,6 +1466,7 @@ const GSHwHack::Entry<GSRendererHW::GSC_Ptr> GSHwHack::s_get_skip_count_function
// Texture shuffle
CRC_F(GSC_DeathByDegreesTekkenNinaWilliams), // + Upscaling issues
CRC_F(GSC_BigMuthaTruckers),
// Upscaling hacks
CRC_F(GSC_UltramanFightingEvolution),
@@ -1481,7 +1477,6 @@ const GSHwHack::Entry<GSRendererHW::GSC_Ptr> GSHwHack::s_get_skip_count_function
const GSHwHack::Entry<GSRendererHW::OI_Ptr> GSHwHack::s_before_draw_functions[] = {
CRC_F(OI_PointListPalette),
CRC_F(OI_BigMuthaTruckers),
CRC_F(OI_DBZBTGames),
CRC_F(OI_FFX),
CRC_F(OI_RozenMaidenGebetGarden),
+1 -2
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);
@@ -45,9 +44,9 @@ public:
static bool GSC_NFSUndercover(GSRendererHW& r, int& skip);
static bool GSC_PolyphonyDigitalGames(GSRendererHW& r, int& skip);
static bool GSC_MetalGearSolid3(GSRendererHW& r, int& skip);
static bool GSC_BigMuthaTruckers(GSRendererHW& r, int& skip);
static bool OI_PointListPalette(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
static bool OI_BigMuthaTruckers(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
static bool OI_DBZBTGames(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
static bool OI_FFX(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
static bool OI_RozenMaidenGebetGarden(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
+332 -145
View File
@@ -337,7 +337,7 @@ void GSRendererHW::ExpandLineIndices()
}
// Fix the vertex position/tex_coordinate from 16 bits color to 32 bits color
void GSRendererHW::ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba)
void GSRendererHW::ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba, GSTextureCache::Target* rt, GSTextureCache::Source* tex)
{
const u32 count = m_vertex.next;
GSVertex* v = &m_vertex.buff[0];
@@ -351,7 +351,8 @@ void GSRendererHW::ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba)
const float tw = static_cast<float>(1u << m_cached_ctx.TEX0.TW);
int tex_pos = (PRIM->FST) ? first_vert.U : static_cast<int>(tw * first_vert.ST.S);
tex_pos &= 0xFF;
read_ba = (tex_pos > 112 && tex_pos < 144);
// "same group" means it can read blue and write alpha using C32 tricks
read_ba = (tex_pos > 112 && tex_pos < 144) || (m_same_group_texture_shuffle && (m_cached_ctx.FRAME.FBMSK & 0xFFFF0000) != 0xFFFF0000);
// Another way of selecting whether to read RG/BA is to use region repeat.
// Ace Combat 04 reads RG, writes to RGBA by setting a MINU of 1015.
@@ -373,7 +374,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),
@@ -406,60 +407,51 @@ void GSRendererHW::ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba)
return;
}
bool half_bottom = false;
switch (GSConfig.UserHacks_HalfBottomOverride)
bool half_bottom_vert = true;
bool half_right_vert = true;
bool half_bottom_uv = true;
bool half_right_uv = true;
if (m_same_group_texture_shuffle)
{
case 0:
// Force Disabled.
// Force Disabled will help games such as Xenosaga.
// Xenosaga handles the half bottom as an vertex offset instead of a buffer offset which does the effect twice.
// Half bottom won't trigger a cache miss that skip the draw because it is still the normal buffer but with a vertices offset.
half_bottom = false;
break;
case 1:
// Force Enabled.
// Force Enabled will help games such as Superman Shadows of Apokolips, The Lord of the Rings: The Two Towers,
// Demon Stone, Midnight Club 3.
half_bottom = true;
break;
case -1:
default:
// Default, Automatic.
// Here's the idea
// TS effect is 16 bits but we emulate it on a 32 bits format
// Normally this means we need to divide size by 2.
//
// Some games do two TS effects on each half of the buffer.
// This makes a mess for us in the TC because we end up with two targets
// when we only want one, thus half screen bug.
//
// 32bits emulation means we can do the effect once but double the size.
// Test cases: Crash Twinsantiy and DBZ BT3
// Test Case: NFS: HP2 splits the effect h:256 and h:192 so 64
// Other games: Midnight Club 3 headlights, black bar in Xenosaga 3 dialogue,
// Firefighter FD18 fire occlusion, PSI Ops half screen green overlay, Lord of the Rings - Two Towers,
// Demon Stone , Sonic Unleashed, Lord of the Rings Two Towers,
// Superman Shadow of Apokolips, Matrix Path of Neo, Big Mutha Truckers
int maxvert = 0;
int minvert = 4096;
for (u32 i = 0; i < count; i++)
if (m_cached_ctx.FRAME.FBW != rt->m_TEX0.TBW && m_cached_ctx.FRAME.FBW == rt->m_TEX0.TBW * 2)
half_right_vert = false;
else
half_bottom_vert = false;
}
else
{
// Different source (maybe?)
// If a game does the texture and frame doubling differently, they can burn in hell.
if (m_cached_ctx.TEX0.TBP0 != m_cached_ctx.FRAME.Block())
{
// No super source of truth here, since the width can get batted around, the valid is probably our best bet.
int tex_width = tex->m_target ? tex->m_from_target->m_valid.z : (tex->m_TEX0.TBW * 64);
int tex_tbw = tex->m_target ? tex->m_from_target_TEX0.TBW : tex->m_TEX0.TBW;
if (static_cast<int>(m_cached_ctx.TEX0.TBW * 64) >= std::min(tex_width * 2, 1024) && tex_tbw != m_cached_ctx.TEX0.TBW || (m_cached_ctx.TEX0.TBW * 64) < floor(m_vt.m_max.t.x))
{
int YCord = 0;
if (!PRIM->FST)
YCord = static_cast<int>((1 << m_cached_ctx.TEX0.TH) * (v[i].ST.T / v[i].RGBAQ.Q));
else
YCord = (v[i].V >> 4);
if (maxvert < YCord)
maxvert = YCord;
if (minvert > YCord)
minvert = YCord;
half_right_uv = false;
half_right_vert = false;
}
half_bottom = minvert == 0 && m_r.height() <= maxvert;
break;
else
{
half_bottom_uv = false;
half_bottom_vert = false;
}
}
else
{
if ((floor(m_vt.m_max.p.y) <= rt->m_valid.w) && ((floor(m_vt.m_max.p.x) > (m_cached_ctx.FRAME.FBW * 64)) || (rt->m_TEX0.TBW != m_cached_ctx.FRAME.FBW)))
{
half_right_vert = false;
half_right_uv = false;
}
else
{
half_bottom_vert = false;
half_bottom_uv = false;
}
}
}
if (PRIM->FST)
@@ -469,16 +461,16 @@ void GSRendererHW::ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba)
for (u32 i = 0; i < count; i += 2)
{
if (write_ba)
v[i].XYZ.X -= 128u;
v[i].XYZ.X -= 128u;
else
v[i+1].XYZ.X += 128u;
v[i + 1].XYZ.X += 128u;
if (read_ba)
v[i].U -= 128u;
v[i].U -= 128u;
else
v[i+1].U += 128u;
v[i + 1].U += 128u;
if (!half_bottom)
if (!half_bottom_vert)
{
// Height is too big (2x).
const int tex_offset = v[i].V & 0xF;
@@ -488,9 +480,13 @@ void GSRendererHW::ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba)
tmp = GSVector4i(tmp - offset).srl32(1) + offset;
v[i].XYZ.Y = static_cast<u16>(tmp.x);
v[i].V = static_cast<u16>(tmp.y);
v[i + 1].XYZ.Y = static_cast<u16>(tmp.z);
v[i + 1].V = static_cast<u16>(tmp.w);
if (!half_bottom_uv)
{
v[i].V = static_cast<u16>(tmp.y);
v[i + 1].V = static_cast<u16>(tmp.w);
}
}
}
}
@@ -502,16 +498,16 @@ void GSRendererHW::ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba)
for (u32 i = 0; i < count; i += 2)
{
if (write_ba)
v[i].XYZ.X -= 128u;
v[i].XYZ.X -= 128u;
else
v[i+1].XYZ.X += 128u;
v[i + 1].XYZ.X += 128u;
if (read_ba)
v[i].ST.S -= offset_8pix;
v[i].ST.S -= offset_8pix;
else
v[i+1].ST.S += offset_8pix;
v[i + 1].ST.S += offset_8pix;
if (!half_bottom)
if (!half_bottom_vert)
{
// Height is too big (2x).
const GSVector4i offset(o.OFY, o.OFY);
@@ -521,9 +517,13 @@ void GSRendererHW::ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba)
//fprintf(stderr, "Before %d, After %d\n", v[i + 1].XYZ.Y, tmp.y);
v[i].XYZ.Y = static_cast<u16>(tmp.x);
v[i].ST.T /= 2.0f;
v[i + 1].XYZ.Y = static_cast<u16>(tmp.y);
v[i + 1].ST.T /= 2.0f;
if (!half_bottom_uv)
{
v[i].ST.T /= 2.0f;
v[i + 1].ST.T /= 2.0f;
}
}
}
}
@@ -534,21 +534,41 @@ void GSRendererHW::ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba)
else
m_vt.m_max.p.x += 8.0f;
if (!half_bottom)
if (!m_same_group_texture_shuffle)
{
const float delta_Y = m_vt.m_max.p.y - m_vt.m_min.p.y;
m_vt.m_max.p.y -= delta_Y / 2.0f;
if (read_ba)
m_vt.m_min.t.x -= 8.0f;
else
m_vt.m_max.t.x += 8.0f;
}
if (read_ba)
m_vt.m_min.t.x -= 8.0f;
else
m_vt.m_max.t.x += 8.0f;
if (!half_bottom)
if (!half_right_vert)
{
const float delta_T = m_vt.m_max.t.y - m_vt.m_min.t.y;
m_vt.m_max.t.y -= delta_T / 2.0f;
m_vt.m_min.p.x /= 2.0f;
m_vt.m_max.p.x /= 2.0f;
m_context->scissor.in.x = m_vt.m_min.p.x;
m_context->scissor.in.z = m_vt.m_max.p.x + 8.0f;
}
if (!half_bottom_vert)
{
m_vt.m_min.p.y /= 2.0f;
m_vt.m_max.p.y /= 2.0f;
m_context->scissor.in.y = m_vt.m_min.p.y;
m_context->scissor.in.w = m_vt.m_max.p.y + 8.0f;
}
// Only do this is the source is being interpreted as 16bit
if (!half_bottom_uv)
{
m_vt.m_min.t.y /= 2.0f;
m_vt.m_max.t.y /= 2.0f;
}
if (!half_right_uv)
{
m_vt.m_min.t.y /= 2.0f;
m_vt.m_max.t.y /= 2.0f;
}
}
@@ -786,12 +806,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];
@@ -805,13 +821,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));
@@ -823,6 +850,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 ||
@@ -836,23 +865,26 @@ 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.
// "Potential" ones are for Jak3 which does a split shuffle on a 128x128 texture with a width of 256, writing to the lower half then offsetting 2 pages.
const u32 expected_next_FBP = (m_cached_ctx.FRAME.FBP + m_split_texture_shuffle_pages) + num_pages;
const u32 potential_expected_next_FBP = m_cached_ctx.FRAME.FBP + ((m_context->FRAME.FBW * 64) / aligned_rc.width());
const u32 expected_next_TBP0 = (m_cached_ctx.TEX0.TBP0 + (m_split_texture_shuffle_pages + num_pages) * BLOCKS_PER_PAGE);
const u32 potential_expected_next_TBP0 = m_cached_ctx.TEX0.TBP0 + (BLOCKS_PER_PAGE * ((m_context->TEX0.TBW * 64) / aligned_rc.width()));
GL_CACHE("IsSplitTextureShuffle: Draw covers %ux%u pages, next FRAME %x TEX %x",
static_cast<u32>(aligned_rc.width()) / frame_psm.pgs.x, pages_high, expected_next_FBP * BLOCKS_PER_PAGE,
expected_next_TBP0);
if (next_ctx.TEX0.TBP0 != expected_next_TBP0)
if (next_ctx.TEX0.TBP0 != expected_next_TBP0 && next_ctx.TEX0.TBP0 != potential_expected_next_TBP0)
{
GL_CACHE("IsSplitTextureShuffle: Mismatch on TBP0, expecting %x, got %x", expected_next_TBP0, next_ctx.TEX0.TBP0);
return false;
}
// Some games don't offset the FBP.
if (next_ctx.FRAME.FBP != expected_next_FBP && next_ctx.FRAME.FBP != m_cached_ctx.FRAME.FBP)
if (next_ctx.FRAME.FBP != expected_next_FBP && next_ctx.FRAME.FBP != m_cached_ctx.FRAME.FBP && next_ctx.FRAME.FBP != potential_expected_next_FBP)
{
GL_CACHE("IsSplitTextureShuffle: Mismatch on FBP, expecting %x, got %x", expected_next_FBP * BLOCKS_PER_PAGE,
next_ctx.FRAME.FBP * BLOCKS_PER_PAGE);
@@ -866,10 +898,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;
}
@@ -881,7 +922,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;
@@ -1132,7 +1172,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) ||
@@ -1221,6 +1261,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;
@@ -1232,6 +1275,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;
}
@@ -1761,7 +1805,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) ||
@@ -1883,6 +1927,7 @@ void GSRendererHW::Draw()
m_texture_shuffle = false;
m_copy_16bit_to_target_shuffle = false;
m_same_group_texture_shuffle = false;
const bool is_split_texture_shuffle = (m_split_texture_shuffle_pages > 0);
if (is_split_texture_shuffle)
@@ -1902,7 +1947,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 = [&]() {
@@ -2112,9 +2191,30 @@ void GSRendererHW::Draw()
GL_CACHE("Estimated texture region: %u,%u -> %u,%u", MIP_CLAMP.MINU, MIP_CLAMP.MINV, MIP_CLAMP.MAXU + 1,
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);
GIFRegTEX0 FRAME_TEX0;
bool rt_32bit = false;
if (!no_rt && m_cached_ctx.FRAME.Block() != m_cached_ctx.TEX0.TBP0 && GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].bpp == 16)
{
// FBW is going to be wrong for channel shuffling into a new target, so take it from the source.
FRAME_TEX0.U64 = 0;
FRAME_TEX0.TBP0 = m_cached_ctx.FRAME.Block();
FRAME_TEX0.TBW = m_cached_ctx.FRAME.FBW;
FRAME_TEX0.PSM = m_cached_ctx.FRAME.PSM;
GSTextureCache::Target* tgt = g_texture_cache->LookupTarget(FRAME_TEX0, GSVector2i(m_vt.m_max.p.x, m_vt.m_max.p.y), GetTextureScaleFactor(), GSTextureCache::RenderTarget, true,
fm);
if (tgt)
rt_32bit = tgt->m_32_bits_fmt;
tgt = nullptr;
}
const bool possible_shuffle = ((rt_32bit && GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].bpp == 16) || 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, m_vt.IsLinear(), m_cached_ctx.FRAME.Block()) :
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(), m_cached_ctx.FRAME.Block());
if (unlikely(!src))
{
GL_INS("ERROR: Source lookup failed, skipping.");
@@ -2122,8 +2222,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.
@@ -2132,7 +2256,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();
@@ -2176,7 +2302,7 @@ void GSRendererHW::Draw()
}
rt = g_texture_cache->CreateTarget(FRAME_TEX0, t_size, GetValidSize(src), target_scale, GSTextureCache::RenderTarget, true,
fm, false, force_preload, preserve_rt_color, m_r);
fm, false, force_preload, preserve_rt_color, m_r, src);
if (unlikely(!rt))
{
GL_INS("ERROR: Failed to create FRAME target, skipping.");
@@ -2200,7 +2326,7 @@ void GSRendererHW::Draw()
if (!ds)
{
ds = g_texture_cache->CreateTarget(ZBUF_TEX0, t_size, GetValidSize(src), target_scale, GSTextureCache::DepthStencil,
m_cached_ctx.DepthWrite(), 0, false, force_preload, preserve_depth, m_r);
m_cached_ctx.DepthWrite(), 0, false, force_preload, preserve_depth, m_r, src);
if (unlikely(!ds))
{
GL_INS("ERROR: Failed to create ZBUF target, skipping.");
@@ -2213,20 +2339,33 @@ void GSRendererHW::Draw()
if (process_texture)
{
GIFRegCLAMP MIP_CLAMP = m_cached_ctx.CLAMP;
const GSVertex* v = &m_vertex.buff[0];
if (rt)
{
// Hypothesis: texture shuffle is used as a postprocessing effect so texture will be an old target.
// Initially code also tested the RT but it gives too much false-positive
const int first_x = ((v[0].XYZ.X - m_context->XYOFFSET.OFX) + 8) >> 4;
const int first_u = PRIM->FST ? ((v[0].U + 8) >> 4) : static_cast<int>(((1 << m_cached_ctx.TEX0.TW) * (v[0].ST.S / v[1].RGBAQ.Q)) + 0.5f);
const bool shuffle_coords = (first_x ^ first_u) & 8;
const u32 draw_end = GSLocalMemory::GetEndBlockAddress(m_cached_ctx.FRAME.Block(), m_cached_ctx.FRAME.FBW, m_cached_ctx.FRAME.PSM, m_r) + 1;
const bool draw_uses_target = src->m_from_target && ((src->m_from_target_TEX0.TBP0 <= m_cached_ctx.FRAME.Block() &&
src->m_from_target->UnwrappedEndBlock() > m_cached_ctx.FRAME.Block()) ||
(m_cached_ctx.FRAME.Block() < src->m_from_target_TEX0.TBP0 && draw_end > src->m_from_target_TEX0.TBP0));
// copy of a 16bit source in to this target, make sure it's opaque and not bilinear to reduce false positives.
m_copy_16bit_to_target_shuffle = m_cached_ctx.TEX0.TBP0 != m_cached_ctx.FRAME.Block() && rt->m_32_bits_fmt == true && IsOpaque()
&& !(context->TEX1.MMIN & 1) && !src->m_32_bits_fmt && m_cached_ctx.FRAME.FBMSK;
}
// Hypothesis: texture shuffle is used as a postprocessing effect so texture will be an old target.
// Initially code also tested the RT but it gives too much false-positive
//
// Both input and output are 16 bits and texture was initially 32 bits!
m_texture_shuffle = (GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].bpp == 16) && (tex_psm.bpp == 16)
&& draw_sprite_tex && (src->m_32_bits_fmt || m_copy_16bit_to_target_shuffle);
// It's not actually possible to do a C16->C16 texture shuffle of B to A as they are the same group
// However you can do it by using C32 and offsetting the target verticies to point to B A, then mask as appropriate.
m_same_group_texture_shuffle = draw_uses_target && (m_cached_ctx.TEX0.PSM & 0xE) == PSMCT32 && (m_cached_ctx.FRAME.PSM & 0x7) == PSMCT16 && (m_vt.m_min.p.x == 8.0f);
// Both input and output are 16 bits and texture was initially 32 bits! Same for the target, Sonic Unleash makes a new target which really is 16bit.
m_texture_shuffle = ((m_same_group_texture_shuffle || (tex_psm.bpp == 16)) && (GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].bpp == 16) &&
(shuffle_coords || rt->m_32_bits_fmt))
&& draw_sprite_tex && (src->m_32_bits_fmt || m_copy_16bit_to_target_shuffle);
};
// Okami mustn't call this code
if (m_texture_shuffle && m_vertex.next < 3 && PRIM->FST && ((m_cached_ctx.FRAME.FBMSK & fm_mask) == 0))
@@ -2237,7 +2376,6 @@ void GSRendererHW::Draw()
// Shadow of Memories/Destiny shouldn't call this code.
// Causes shadow flickering.
const GSVertex* v = &m_vertex.buff[0];
m_texture_shuffle = ((v[1].U - v[0].U) < 256) ||
// Tomb Raider Angel of Darkness relies on this behavior to produce a fog effect.
// In this case, the address of the framebuffer and texture are the same.
@@ -2252,7 +2390,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.
@@ -2428,9 +2566,23 @@ void GSRendererHW::Draw()
GSTextureCache::Target* old_rt = nullptr;
GSTextureCache::Target* old_ds = nullptr;
{
GSVector2i new_size = t_size;
// We need to adjust the size if it's a texture shuffle as we could end up making the RT twice the size.
if (rt && m_texture_shuffle && m_split_texture_shuffle_pages == 0)
{
if ((new_size.x > rt->m_valid.z && m_vt.m_max.p.x == new_size.x) || (new_size.y > rt->m_valid.w && m_vt.m_max.p.y == new_size.y))
{
if (new_size.y <= rt->m_valid.w && (rt->m_TEX0.TBW != m_cached_ctx.FRAME.FBW))
new_size.x /= 2;
else
new_size.y /= 2;
}
}
// We still need to make sure the dimensions of the targets match.
const int new_w = std::max(t_size.x, std::max(rt ? rt->m_unscaled_size.x : 0, ds ? ds->m_unscaled_size.x : 0));
const int new_h = std::max(t_size.y, std::max(rt ? rt->m_unscaled_size.y : 0, ds ? ds->m_unscaled_size.y : 0));
const int new_w = std::max(new_size.x, std::max(rt ? rt->m_unscaled_size.x : 0, ds ? ds->m_unscaled_size.x : 0));
const int new_h = std::max(new_size.y, std::max(rt ? rt->m_unscaled_size.y : 0, ds ? ds->m_unscaled_size.y : 0));
if (rt)
{
const u32 old_end_block = rt->m_end_block;
@@ -2441,6 +2593,7 @@ void GSRendererHW::Draw()
pxAssert(rt->GetScale() == target_scale);
if (rt->GetUnscaledWidth() != new_w || rt->GetUnscaledHeight() != new_h)
GL_INS("Resize RT from %dx%d to %dx%d", rt->GetUnscaledWidth(), rt->GetUnscaledHeight(), new_w, new_h);
rt->ResizeTexture(new_w, new_h);
if (!m_texture_shuffle && !m_channel_shuffle)
@@ -2450,8 +2603,8 @@ void GSRendererHW::Draw()
}
// Limit to 2x the vertical height of the resolution (for double buffering)
rt->UpdateValidity(m_r, can_update_size || m_r.w <= (resolution.y * 2));
rt->UpdateDrawn(m_r, can_update_size || m_r.w <= (resolution.y * 2));
rt->UpdateValidity(m_r, can_update_size || (m_r.w <= (resolution.y * 2) && !m_texture_shuffle));
rt->UpdateDrawn(m_r, can_update_size || (m_r.w <= (resolution.y * 2) && !m_texture_shuffle));
// Probably changing to double buffering, so invalidate any old target that was next to it.
// This resolves an issue where the PCRTC will find the old target in FMV's causing flashing.
// Grandia Xtreme, Onimusha Warlord.
@@ -2505,7 +2658,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;
@@ -2654,7 +2807,7 @@ void GSRendererHW::Draw()
{
//rt->m_valid = rt->m_valid.runion(r);
// Limit to 2x the vertical height of the resolution (for double buffering)
rt->UpdateValidity(m_r, can_update_size || m_r.w <= (resolution.y * 2));
rt->UpdateValidity(m_r, can_update_size || (m_r.w <= (resolution.y * 2) && !m_texture_shuffle));
g_texture_cache->InvalidateVideoMem(context->offset.fb, m_r, false);
@@ -2667,7 +2820,7 @@ void GSRendererHW::Draw()
{
//ds->m_valid = ds->m_valid.runion(r);
// Limit to 2x the vertical height of the resolution (for double buffering)
ds->UpdateValidity(m_r, can_update_size || m_r.w <= (resolution.y * 2));
ds->UpdateValidity(m_r, can_update_size || m_r.w <= (resolution.y * 2) && !m_texture_shuffle);
g_texture_cache->InvalidateVideoMem(context->offset.zb, m_r, false);
@@ -2915,7 +3068,7 @@ void GSRendererHW::EmulateZbuffer(const GSTextureCache::Target* ds)
}
}
void GSRendererHW::EmulateTextureShuffleAndFbmask(GSTextureCache::Target* rt)
void GSRendererHW::EmulateTextureShuffleAndFbmask(GSTextureCache::Target* rt, GSTextureCache::Source* tex)
{
// Uncomment to disable texture shuffle emulation.
// m_texture_shuffle = false;
@@ -2958,7 +3111,7 @@ void GSRendererHW::EmulateTextureShuffleAndFbmask(GSTextureCache::Target* rt)
bool write_ba;
bool read_ba;
ConvertSpriteTextureShuffle(write_ba, read_ba);
ConvertSpriteTextureShuffle(write_ba, read_ba, rt, tex);
// If date is enabled you need to test the green channel instead of the
// alpha channel. Only enable this code in DATE mode to reduce the number
@@ -2967,6 +3120,7 @@ void GSRendererHW::EmulateTextureShuffleAndFbmask(GSTextureCache::Target* rt)
m_conf.ps.read_ba = read_ba;
m_conf.ps.real16src = m_copy_16bit_to_target_shuffle;
m_conf.ps.shuffle_same = m_same_group_texture_shuffle;
// Please bang my head against the wall!
// 1/ Reduce the frame mask to a 16 bit format
const u32 m = m_cached_ctx.FRAME.FBMSK & GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].fmsk;
@@ -3312,21 +3466,23 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, bool& DATE_PRIMID, bool& DATE_BARRIER, bool& blending_alpha_pass)
{
// AA1: Don't enable blending on AA1, not yet implemented on hardware mode,
// it requires coverage sample so it's safer to turn it off instead.
const bool AA1 = PRIM->AA1 && (m_vt.m_primclass == GS_LINE_CLASS || m_vt.m_primclass == GS_TRIANGLE_CLASS);
// PABE: Check condition early as an optimization.
const bool PABE = PRIM->ABE && m_draw_env->PABE.PABE && (GetAlphaMinMax().max < 128);
// FBMASK: Color is not written, no need to do blending.
const u32 temp_fbmask = m_conf.ps.dfmt == 2 ? 0x00F8F8F8 : 0x00FFFFFF;
const bool FBMASK = (m_cached_ctx.FRAME.FBMSK & temp_fbmask) == temp_fbmask;
// No blending or coverage anti-aliasing so early exit
if (FBMASK || PABE || !(PRIM->ABE || AA1))
{
m_conf.blend = {};
m_conf.ps.no_color1 = true;
return;
// AA1: Don't enable blending on AA1, not yet implemented on hardware mode,
// it requires coverage sample so it's safer to turn it off instead.
const bool AA1 = PRIM->AA1 && (m_vt.m_primclass == GS_LINE_CLASS || m_vt.m_primclass == GS_TRIANGLE_CLASS);
// PABE: Check condition early as an optimization.
const bool PABE = PRIM->ABE && m_draw_env->PABE.PABE && (GetAlphaMinMax().max < 128);
// FBMASK: Color is not written, no need to do blending.
const u32 temp_fbmask = m_conf.ps.dfmt == 2 ? 0x00F8F8F8 : 0x00FFFFFF;
const bool FBMASK = (m_cached_ctx.FRAME.FBMSK & temp_fbmask) == temp_fbmask;
// No blending or coverage anti-aliasing so early exit
if (FBMASK || PABE || !(PRIM->ABE || AA1))
{
m_conf.blend = {};
m_conf.ps.no_color1 = true;
return;
}
}
// Compute the blending equation to detect special case
@@ -3356,11 +3512,20 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, bool& DAT
m_conf.ps.fixed_one_a = 1;
m_conf.ps.blend_c = 0;
}
// 24 bits doesn't have an alpha channel so use 128 (1.0f) fix factor as equivalent.
else if (m_conf.ps.dfmt == 1 && m_conf.ps.blend_c == 1)
else if (m_conf.ps.blend_c == 1)
{
AFIX = 128;
m_conf.ps.blend_c = 2;
// When both rt alpha min and max are equal replace Ad with Af, easier to manage.
if (rt_alpha_min == rt_alpha_max)
{
AFIX = rt_alpha_min;
m_conf.ps.blend_c = 2;
}
// 24 bits doesn't have an alpha channel so use 128 (1.0f) fix factor as equivalent.
else if (m_conf.ps.dfmt == 1)
{
AFIX = 128;
m_conf.ps.blend_c = 2;
}
}
// Get alpha value
@@ -3368,12 +3533,10 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, bool& DAT
const bool alpha_c0_one = (m_conf.ps.blend_c == 0 && (GetAlphaMinMax().min == 128) && (GetAlphaMinMax().max == 128));
const bool alpha_c0_high_min_one = (m_conf.ps.blend_c == 0 && GetAlphaMinMax().min > 128);
const bool alpha_c0_high_max_one = (m_conf.ps.blend_c == 0 && GetAlphaMinMax().max > 128);
const bool alpha_c1_zero = (m_conf.ps.blend_c == 1 && rt_alpha_min == 0 && rt_alpha_max == 0);
const bool alpha_c1_one = (m_conf.ps.blend_c == 1 && rt_alpha_min == 128 && rt_alpha_max == 128);
const bool alpha_c2_zero = (m_conf.ps.blend_c == 2 && AFIX == 0u);
const bool alpha_c2_one = (m_conf.ps.blend_c == 2 && AFIX == 128u);
const bool alpha_c2_high_one = (m_conf.ps.blend_c == 2 && AFIX > 128u);
const bool alpha_one = alpha_c0_one || alpha_c1_one || alpha_c2_one;
const bool alpha_one = alpha_c0_one || alpha_c2_one;
// Optimize blending equations, must be done before index calculation
if ((m_conf.ps.blend_a == m_conf.ps.blend_b) || ((m_conf.ps.blend_b == m_conf.ps.blend_d) && alpha_one))
@@ -3392,7 +3555,7 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, bool& DAT
m_conf.ps.blend_b = 0;
m_conf.ps.blend_c = 0;
}
else if (alpha_c0_zero || alpha_c1_zero || alpha_c2_zero)
else if (alpha_c0_zero || alpha_c2_zero)
{
// C == 0.0f
// (A - B) * C, result will be 0.0f so set A B to Cs
@@ -3863,7 +4026,7 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, bool& DAT
{
m_conf.ps.blend_hw = 1;
}
else if (blend_flag & (BLEND_HW_CLR2))
else if (blend_flag & BLEND_HW_CLR2)
{
if (m_conf.ps.blend_c == 2)
m_conf.cb_ps.TA_MaxDepth_Af.a = static_cast<float>(AFIX) / 128.0f;
@@ -4020,7 +4183,7 @@ __ri void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Target* rt,
// Force a 32 bits access (normally shuffle is done on 16 bits)
// m_ps_sel.tex_fmt = 0; // removed as an optimization
m_conf.ps.aem = TEXA.AEM;
ASSERT(tex->m_target);
//ASSERT(tex->m_target);
// Require a float conversion if the texure is a depth otherwise uses Integral scaling
if (psm.depth)
@@ -4588,7 +4751,7 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
m_prim_overlap = PrimitiveOverlap();
EmulateTextureShuffleAndFbmask(rt);
EmulateTextureShuffleAndFbmask(rt, tex);
const GSDevice::FeatureSupport features = g_gs_device->Features();
@@ -4748,6 +4911,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.
@@ -4756,6 +4925,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;
@@ -5425,7 +5600,7 @@ bool GSRendererHW::DetectDoubleHalfClear(bool& no_rt, bool& no_ds)
const bool clear_depth = (m_cached_ctx.FRAME.FBP > m_cached_ctx.ZBUF.ZBP);
const u32 base = clear_depth ? m_cached_ctx.ZBUF.ZBP : m_cached_ctx.FRAME.FBP;
const u32 half = clear_depth ? m_cached_ctx.FRAME.FBP : m_cached_ctx.ZBUF.ZBP;
const bool enough_bits = clear_depth ? (frame_psm.trbpp >= zbuf_psm.trbpp) : (zbuf_psm.trbpp >= frame_psm.trbpp);
// Size of the current draw
const u32 w_pages = (m_r.z + (frame_psm.pgs.x - 1)) / frame_psm.pgs.x;
const u32 h_pages = (m_r.w + (frame_psm.pgs.y - 1)) / frame_psm.pgs.y;
@@ -5439,7 +5614,7 @@ bool GSRendererHW::DetectDoubleHalfClear(bool& no_rt, bool& no_ds)
// GTA: LCS does this setup, along with a few other games. Thankfully if it's a zero clear, we'll clear both
// separately, and the end result is the same because it gets invalidated. That's better than falsely detecting
// double half clears, and ending up with 1024 high render targets which really shouldn't be.
if ((frame_psm.fmt != zbuf_psm.fmt && m_cached_ctx.FRAME.FBMSK != ((zbuf_psm.fmt == 1) ? 0xFF000000u : 0)) ||
if ((!enough_bits && frame_psm.fmt != zbuf_psm.fmt && m_cached_ctx.FRAME.FBMSK != ((zbuf_psm.fmt == 1) ? 0xFF000000u : 0)) ||
!GSUtil::HasCompatibleBits(m_cached_ctx.FRAME.PSM & ~0x30, m_cached_ctx.ZBUF.PSM & ~0x30)) // Bit depth is not the same (i.e. 32bit + 16bit).
{
GL_INS("Inconsistent FRAME [%s, %08x] and ZBUF [%s] formats, not using double-half clear.",
@@ -5546,6 +5721,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
{
@@ -5564,6 +5745,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
{
+5 -3
View File
@@ -86,7 +86,7 @@ private:
void ResetStates();
void SetupIA(float target_scale, float sx, float sy);
void EmulateTextureShuffleAndFbmask(GSTextureCache::Target* rt);
void EmulateTextureShuffleAndFbmask(GSTextureCache::Target* rt, GSTextureCache::Source* tex);
bool EmulateChannelShuffle(GSTextureCache::Target* src, bool test_only);
void EmulateBlending(int rt_alpha_min, int rt_alpha_max, bool& DATE_PRIMID, bool& DATE_BARRIER, bool& blending_alpha_pass);
@@ -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;
@@ -192,7 +194,7 @@ public:
void Lines2Sprites();
bool VerifyIndices();
void ExpandLineIndices();
void ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba);
void ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba, GSTextureCache::Target* rt, GSTextureCache::Source* tex);
GSVector4 RealignTargetTextureCoordinate(const GSTextureCache::Source* tex);
GSVector4i ComputeBoundingBox(const GSVector2i& rtsize, float rtscale);
void MergeSprite(GSTextureCache::Source* tex);
+228 -59
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, const u32 frame_fbp, 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;
}
}
}
@@ -688,7 +700,7 @@ GSTextureCache::Source* GSTextureCache::LookupDepthSource(const GIFRegTEX0& TEX0
{
for (Target* t : m_dst[RenderTarget])
{
if (t->m_age <= 1 && t->m_TEX0.TBP0 == bp && t->HasValidAlpha())
if (t->m_age <= 1 && t->m_TEX0.TBP0 == bp && t->m_TEX0.TBW == TEX0.TBW && t->HasValidAlpha())
{
GL_CACHE("TC depth: Using RT %x instead of depth because of missing alpha", t->m_TEX0.TBP0);
@@ -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, frame_fbp);
}
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, const u32 frame_fbp)
{
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
{
@@ -1006,51 +1031,80 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
// Make sure the texture actually is INSIDE the RT, it's possibly not valid if it isn't.
// Also check BP >= TBP, create source isn't equpped to expand it backwards and all data comes from the target. (GH3)
else if (GSConfig.UserHacks_TextureInsideRt >= GSTextureInRtMode::InsideTargets && psm >= PSMCT32 &&
psm <= PSMCT16S && GSUtil::HasCompatibleBits(t->m_TEX0.PSM, psm) && (t->Overlaps(bp, bw, psm, r) || t->Wraps()) &&
t->m_age <= 1 && (!found_t || dst->m_TEX0.TBW < bw))
psm <= PSMCT16S && (GSUtil::HasCompatibleBits(t->m_TEX0.PSM, psm) ||
(possible_shuffle && t->m_TEX0.PSM <= PSMCT24 && ((((t->UnwrappedEndBlock() + 1) - t->m_TEX0.TBP0) >> 1) + t->m_TEX0.TBP0) == bp))
&& (t->Overlaps(bp, bw, psm, r) || t->Wraps()) &&
t->m_age <= 1 && (!found_t || dst->m_TEX0.TBW < bw))
{
// PSM equality needed because CreateSource does not handle PSM conversion.
// Only inclusive hit to limit false hits.
GSVector4i rect = r;
int src_bw = bw;
int src_psm = psm;
// If the input is C16 and it's actually a shuffle of 32bits we need to correct the size.
if ((t->m_TEX0.PSM & 0xF) == PSMCT32 && (psm & 0x7) == PSMCT16 && possible_shuffle)
{
src_psm = t->m_TEX0.PSM;
// If it's taking double width for the shuffle, half that.
if (src_bw == (t->m_TEX0.TBW * 2))
{
src_bw = t->m_TEX0.TBW;
rect.x /= 2;
rect.z /= 2;
}
else
{
rect.y /= 2;
rect.w /= 2;
}
}
if (bp > t->m_TEX0.TBP0)
{
GSVector4i new_rect = rect;
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 bool swizzle_match = GSLocalMemory::m_psm[src_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;
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
{
// If it's not page aligned, grab the whole pages it covers, to be safe.
if (GSLocalMemory::m_psm[psm].bpp != GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp)
{
const GSVector2i& dst_page_size = GSLocalMemory::m_psm[psm].pgs;
rect = GSVector4i(rect.x / page_size.x, rect.y / page_size.y, (rect.z + (page_size.x - 1)) / page_size.x, (rect.w + (page_size.y - 1)) / page_size.y);
rect = GSVector4i(rect.x * dst_page_size.x, rect.y * dst_page_size.y, rect.z * dst_page_size.x, rect.w * dst_page_size.y);
const GSVector2i& dst_page_size = GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs;
new_rect = GSVector4i(new_rect.x / page_size.x, new_rect.y / page_size.y, (new_rect.z + (page_size.x - 1)) / page_size.x, (new_rect.w + (page_size.y - 1)) / page_size.y);
new_rect = GSVector4i(new_rect.x * dst_page_size.x, new_rect.y * dst_page_size.y, new_rect.z * dst_page_size.x, new_rect.w * dst_page_size.y);
}
else
{
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);
new_rect.x &= ~(page_size.x - 1);
new_rect.y &= ~(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);
}
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 = TranslateAlignedRectByPage(t, bp & ~((1 << 5) - 1), psm, bw, new_rect);
rect.x -= new_rect.x & ~(page_size.y - 1);
rect.y -= new_rect.x & ~(page_size.y - 1);
}
rect = rect.rintersect(t->m_valid);
@@ -1076,16 +1130,19 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
dst = t;
tex_merge_rt = false;
found_t = true;
continue;
if (dst->m_TEX0.TBP0 == frame_fbp && possible_shuffle)
break;
else
continue;
}
else
{
SurfaceOffset so = ComputeSurfaceOffset(bp, bw, psm, r, t);
SurfaceOffset so = ComputeSurfaceOffset(bp, bw, psm, new_rect, t);
if (!so.is_valid && t->Wraps())
{
// 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)
{
@@ -1096,7 +1153,10 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
tex_merge_rt = false;
found_t = true;
// Keep looking, just in case there is an exact match (Situation: Target frame drawn inside target frame, current makes a separate texture)
continue;
if (dst->m_TEX0.TBP0 == frame_fbp && possible_shuffle)
break;
else
continue;
}
}
}
@@ -1125,7 +1185,10 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
dst = t;
tex_merge_rt = false;
found_t = true;
continue;
if (dst->m_TEX0.TBP0 == frame_fbp && possible_shuffle)
break;
else
continue;
}
// Strictly speaking this path is no longer needed, but I'm leaving it here for now because Guitar
@@ -1139,7 +1202,10 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
// Prefer a target inside over a target outside.
found_t = false;
continue;
if (dst->m_TEX0.TBP0 == frame_fbp && possible_shuffle)
break;
else
continue;
}
}
}
@@ -1172,11 +1238,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, frame_fbp);
}
else
{
return LookupDepthSource(TEX0, TEXA, CLAMP, r, possible_shuffle, true);
return LookupDepthSource(TEX0, TEXA, CLAMP, r, possible_shuffle, linear, frame_fbp, true);
}
}
}
@@ -1275,6 +1341,15 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
if (bp == t->m_TEX0.TBP0)
{
// if It's an old target and it's being completely overwritten, kill it.
if (!preserve_rgb && !preserve_alpha && TEX0.TBW != t->m_TEX0.TBW && TEX0.TBW > 1 && t->m_age >= 1)
{
GL_INS("TC: Deleting RT BP 0x%x BW %d PSM %s due to width change to %d", t->m_TEX0.TBP0, t->m_TEX0.TBW, psm_str(t->m_TEX0.PSM), TEX0.TBW);
InvalidateSourcesFromTarget(t);
i = list.erase(i);
delete t;
continue;
}
list.MoveFront(i.Index());
dst = t;
@@ -1476,7 +1551,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
dst_match = t;
}
}
// We only want to use a matched target if it's actually being used.
if (dst_match)
{
calcRescale(dst_match);
@@ -1501,6 +1576,9 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
dst->m_valid_alpha_high = dst_match->m_valid_alpha_high && psm_s.trbpp != 24;
dst->m_valid_rgb = dst_match->m_valid_rgb;
if(GSLocalMemory::m_psm[dst->m_TEX0.PSM].bpp == 16 && GSLocalMemory::m_psm[dst_match->m_TEX0.PSM].bpp > 16)
dst->m_TEX0.TBW = dst_match->m_TEX0.TBW; // Be careful of shuffles of the depth as C16, but using a buffer width of 16 (Mercenaries).
ShaderConvert shader;
// m_32_bits_fmt gets set on a shuffle or if the format isn't 16bit.
// In this case it needs to make sure it isn't part of a shuffle, where it needs to be interpreted as 32bits.
@@ -1580,7 +1658,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
}
GSTextureCache::Target* GSTextureCache::CreateTarget(GIFRegTEX0 TEX0, const GSVector2i& size, const GSVector2i& valid_size, float scale, int type,
bool used, u32 fbmask, bool is_frame, bool preload, bool preserve_target, const GSVector4i draw_rect)
bool used, u32 fbmask, bool is_frame, bool preload, bool preserve_target, const GSVector4i draw_rect, GSTextureCache::Source* src)
{
if (type == DepthStencil)
{
@@ -1595,7 +1673,7 @@ GSTextureCache::Target* GSTextureCache::CreateTarget(GIFRegTEX0 TEX0, const GSVe
Target* dst = Target::Create(TEX0, size.x, size.y, scale, type, true);
PreloadTarget(TEX0, size, valid_size, is_frame, preload, preserve_target, draw_rect, dst);;
PreloadTarget(TEX0, size, valid_size, is_frame, preload, preserve_target, draw_rect, dst, src);
dst->m_is_frame = is_frame;
@@ -1618,7 +1696,7 @@ GSTextureCache::Target* GSTextureCache::CreateTarget(GIFRegTEX0 TEX0, const GSVe
}
void GSTextureCache::PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, const GSVector2i& valid_size, bool is_frame,
bool preload, bool preserve_target, const GSVector4i draw_rect, Target* dst)
bool preload, bool preserve_target, const GSVector4i draw_rect, Target* dst, GSTextureCache::Source* src)
{
// In theory new textures contain invalidated data. Still in theory a new target
// must contains the content of the GS memory.
@@ -1750,17 +1828,62 @@ void GSTextureCache::PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, cons
auto j = i;
Target* t = *j;
// could be overwriting a double buffer, so if it's the second half of it, just reduce the size down to half.
if (dst != t && t->m_TEX0.TBW == dst->m_TEX0.TBW &&
t->m_TEX0.PSM == dst->m_TEX0.PSM &&
((((t->m_end_block + 1) - t->m_TEX0.TBP0) >> 1) + t->m_TEX0.TBP0) == dst->m_TEX0.TBP0)
{
//DevCon.Warning("Found one %x->%x BW %d PSM %x (new target %x->%x BW %d PSM %x)", t->m_TEX0.TBP0, t->m_end_block, t->m_TEX0.TBW, t->m_TEX0.PSM, dst->m_TEX0.TBP0, dst->m_end_block, dst->m_TEX0.TBW, dst->m_TEX0.PSM);
GSVector4i new_valid = t->m_valid;
new_valid.w /= 2;
t->ResizeValidity(new_valid);
return;
}
if (dst != t && t->m_TEX0.TBW == dst->m_TEX0.TBW && t->m_TEX0.PSM == dst->m_TEX0.PSM && t->m_TEX0.TBW > 4)
if(t->Overlaps(dst->m_TEX0.TBP0, dst->m_TEX0.TBW, dst->m_TEX0.PSM, dst->m_valid))
{
// could be overwriting a double buffer, so if it's the second half of it, just reduce the size down to half.
if (((((t->UnwrappedEndBlock() + 1) - t->m_TEX0.TBP0) >> 1) + t->m_TEX0.TBP0) == dst->m_TEX0.TBP0)
{
GSVector4i new_valid = t->m_valid;
new_valid.w /= 2;
GL_INS("RT resize buffer for FBP 0x%x, %dx%d => %d,%d", t->m_TEX0.TBP0, t->m_valid.width(), t->m_valid.height(), new_valid.width(), new_valid.height());
t->ResizeValidity(new_valid);
return;
}
// The new texture is behind it but engulfs the whole thing, shrink the new target so it grows in the HW Draw resize.
else if (((((dst->UnwrappedEndBlock() + 1) - dst->m_TEX0.TBP0) >> 1) + dst->m_TEX0.TBP0) == t->m_TEX0.TBP0)
{
if (dst->m_TEX0.TBW == 2)
{
i++;
continue;
}
int overlapping_pages = ((dst->UnwrappedEndBlock() + 1) - t->m_TEX0.TBP0) >> 5;
int y_reduction = (overlapping_pages / dst->m_TEX0.TBW) * GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y;
if (y_reduction == 0 || (overlapping_pages % dst->m_TEX0.TBW))
{
i++;
continue;
}
const int copy_width = (t->m_texture->GetWidth()) > (dst->m_texture->GetWidth()) ? (dst->m_texture->GetWidth()) : t->m_texture->GetWidth();
const int copy_height = y_reduction * t->m_scale;
const int old_height = (dst->m_valid.w - y_reduction) * dst->m_scale;
GL_INS("RT double buffer copy from FBP 0x%x, %dx%d => %d,%d", t->m_TEX0.TBP0, copy_width, copy_height, 0, old_height);
// Clear the dirty first
dst->Update();
// Invalidate has been moved to after DrawPrims(), because we might kill the current sources' backing.
g_gs_device->CopyRect(t->m_texture, dst->m_texture, GSVector4i(0, 0, copy_width, copy_height), 0, old_height);
if (src && src->m_target && src->m_from_target == t)
{
// This should never happen as we're making a new target so the src should never be something it overlaps, but just incase..
GSVector4i new_valid = t->m_valid;
new_valid.y = std::max(new_valid.y - y_reduction, 0);
new_valid.w = std::max(new_valid.w - y_reduction, 0);
t->m_TEX0.TBP0 += (y_reduction / GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y) << 5;
t->ResizeValidity(new_valid);
}
else
{
InvalidateSourcesFromTarget(t);
i = list.erase(j);
delete t;
}
return;
}
}
i++;
}
}
@@ -3646,8 +3769,27 @@ 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));
}
}
src->m_32_bits_fmt = dst->m_32_bits_fmt;
if (psm.pal > 0)
{
@@ -3688,8 +3830,27 @@ 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));
}
}
src->m_32_bits_fmt = dst->m_32_bits_fmt;
dst->Update();
@@ -5190,8 +5351,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();
}
+4 -4
View File
@@ -424,7 +424,7 @@ protected:
Source* CreateSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, Target* t, bool half_right, int x_offset, int y_offset, const GSVector2i* lod, const GSVector4i* src_range, GSTexture* gpu_clut, SourceRegion region);
void PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, const GSVector2i& valid_size, bool is_frame,
bool preload, bool preserve_target, const GSVector4i draw_rect, Target* dst);
bool preload, bool preserve_target, const GSVector4i draw_rect, Target* dst, GSTextureCache::Source* src = nullptr);
// Returns scaled texture size.
static GSVector2i ScaleRenderTargetSize(const GSVector2i& sz, float scale);
@@ -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, const u32 frame_fbp = 0xFFFFFFFF);
Source* LookupDepthSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, const bool possible_shuffle, const bool linear, const u32 frame_fbp = 0xFFFFFFFF, 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,
@@ -482,7 +482,7 @@ public:
const GSVector4i draw_rc = GSVector4i::zero());
Target* CreateTarget(GIFRegTEX0 TEX0, const GSVector2i& size, const GSVector2i& valid_size,float scale, int type, bool used = true, u32 fbmask = 0,
bool is_frame = false, bool preload = GSConfig.PreloadFrameWithGSData, bool preserve_target = true,
const GSVector4i draw_rc = GSVector4i::zero());
const GSVector4i draw_rc = GSVector4i::zero(), GSTextureCache::Source* src = nullptr);
Target* LookupDisplayTarget(GIFRegTEX0 TEX0, const GSVector2i& size, float scale);
/// Looks up a target in the cache, and only returns it if the BP/BW match exactly.
+4 -5
View File
@@ -673,7 +673,7 @@ MRCOwned<id<MTLFunction>> GSDeviceMTL::LoadShader(NSString* name)
{
NSString* msg = [NSString stringWithFormat:@"Failed to load shader %@: %@", name, [err localizedDescription]];
Console.Error("%s", [msg UTF8String]);
pxFailRel("Failed to load shader, check the log for more details.");
pxFailRel([msg UTF8String]);
}
return fn;
}
@@ -689,7 +689,7 @@ MRCOwned<id<MTLRenderPipelineState>> GSDeviceMTL::MakePipeline(MTLRenderPipeline
{
NSString* msg = [NSString stringWithFormat:@"Failed to create pipeline %@: %@", name, [err localizedDescription]];
Console.Error("%s", [msg UTF8String]);
pxFailRel("Failed to create pipeline, check the log for more details.");
pxFailRel([msg UTF8String]);
}
return res;
}
@@ -709,7 +709,7 @@ MRCOwned<id<MTLComputePipelineState>> GSDeviceMTL::MakeComputePipeline(id<MTLFun
{
NSString* msg = [NSString stringWithFormat:@"Failed to create pipeline %@: %@", name, [err localizedDescription]];
Console.Error("%s", [msg UTF8String]);
pxFailRel("Failed to create pipeline, check the log for more details.");
pxFailRel([msg UTF8String]);
}
return res;
}
@@ -1803,6 +1803,7 @@ void GSDeviceMTL::MRESetHWPipelineState(GSHWDrawConfig::VSSelector vssel, GSHWDr
setFnConstantB(m_fn_constants, pssel.adjt, GSMTLConstantIndex_PS_ADJT);
setFnConstantB(m_fn_constants, pssel.ltf, GSMTLConstantIndex_PS_LTF);
setFnConstantB(m_fn_constants, pssel.shuffle, GSMTLConstantIndex_PS_SHUFFLE);
setFnConstantB(m_fn_constants, pssel.shuffle_same, GSMTLConstantIndex_PS_SHUFFLE_SAME);
setFnConstantB(m_fn_constants, pssel.read_ba, GSMTLConstantIndex_PS_READ_BA);
setFnConstantB(m_fn_constants, pssel.real16src, GSMTLConstantIndex_PS_READ16_SRC);
setFnConstantB(m_fn_constants, pssel.write_rg, GSMTLConstantIndex_PS_WRITE_RG);
@@ -2141,8 +2142,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)
@@ -182,6 +182,7 @@ enum GSMTLFnConstants
GSMTLConstantIndex_PS_ADJT,
GSMTLConstantIndex_PS_LTF,
GSMTLConstantIndex_PS_SHUFFLE,
GSMTLConstantIndex_PS_SHUFFLE_SAME,
GSMTLConstantIndex_PS_READ_BA,
GSMTLConstantIndex_PS_READ16_SRC,
GSMTLConstantIndex_PS_WRITE_RG,
+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());
+19 -6
View File
@@ -41,6 +41,7 @@ constant bool PS_ADJS [[function_constant(GSMTLConstantIndex_PS_AD
constant bool PS_ADJT [[function_constant(GSMTLConstantIndex_PS_ADJT)]];
constant bool PS_LTF [[function_constant(GSMTLConstantIndex_PS_LTF)]];
constant bool PS_SHUFFLE [[function_constant(GSMTLConstantIndex_PS_SHUFFLE)]];
constant bool PS_SHUFFLE_SAME [[function_constant(GSMTLConstantIndex_PS_SHUFFLE_SAME)]];
constant bool PS_READ_BA [[function_constant(GSMTLConstantIndex_PS_READ_BA)]];
constant bool PS_READ16_SRC [[function_constant(GSMTLConstantIndex_PS_READ16_SRC)]];
constant bool PS_WRITE_RG [[function_constant(GSMTLConstantIndex_PS_WRITE_RG)]];
@@ -1021,7 +1022,16 @@ struct PSMain
uint4 denorm_c = uint4(C);
uint2 denorm_TA = uint2(cb.ta * 255.5f);
if (PS_READ16_SRC)
// Special case for 32bit input and 16bit output, shuffle used by The Godfather
if (PS_SHUFFLE_SAME)
{
if (PS_READ_BA)
C = (denorm_c.b & 0x7Fu) | (denorm_c.a & 0x80);
else
C.ga = C.rg;
}
// Copy of a 16bit source in to this target
else if (PS_READ16_SRC)
{
C.rb = (denorm_c.r >> 3) | (((denorm_c.g >> 3) & 0x7u) << 5);
if (denorm_c.a & 0x80)
@@ -1029,13 +1039,16 @@ struct PSMain
else
C.ga = (denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.x & 0x80);
}
// Write RB part. Mask will take care of the correct destination
else if (PS_READ_BA)
{
C.rb = C.bb;
C.ga = (denorm_c.a & 0x7F) | (denorm_c.a & 0x80 ? denorm_TA.y & 0x80 : denorm_TA.x & 0x80);
}
else
{
C.rb = PS_READ_BA ? C.bb : C.rr;
if (PS_READ_BA)
C.ga = (denorm_c.a & 0x7F) | (denorm_c.a & 0x80 ? denorm_TA.y & 0x80 : denorm_TA.x & 0x80);
else
C.ga = (denorm_c.g & 0x7F) | (denorm_c.g & 0x80 ? denorm_TA.y & 0x80 : denorm_TA.x & 0x80);
C.rb = C.rr;
C.ga = (denorm_c.g & 0x7F) | (denorm_c.g & 0x80 ? denorm_TA.y & 0x80 : denorm_TA.x & 0x80);
}
}
@@ -1378,6 +1378,7 @@ std::string GSDeviceOGL::GetPSSource(const PSSelector& sel)
+ fmt::format("#define PS_BLEND_D {}\n", sel.blend_d)
+ fmt::format("#define PS_IIP {}\n", sel.iip)
+ fmt::format("#define PS_SHUFFLE {}\n", sel.shuffle)
+ fmt::format("#define PS_SHUFFLE_SAME {}\n", sel.shuffle_same)
+ fmt::format("#define PS_READ_BA {}\n", sel.read_ba)
+ fmt::format("#define PS_READ16_SRC {}\n", sel.real16src)
+ fmt::format("#define PS_WRITE_RG {}\n", sel.write_rg)
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: ");
-7
View File
@@ -601,9 +601,6 @@ bool GameDatabaseSchema::GameEntry::configMatchesHWFix(const Pcsx2Config::GSOpti
case GSHWFixId::SkipDrawEnd:
return (config.SkipDrawEnd == value);
case GSHWFixId::HalfBottomOverride:
return (config.UserHacks_HalfBottomOverride == value);
case GSHWFixId::HalfPixelOffset:
return (config.UpscaleMultiplier <= 1.0f || config.UserHacks_HalfPixelOffset == value);
@@ -782,10 +779,6 @@ void GameDatabaseSchema::GameEntry::applyGSHardwareFixes(Pcsx2Config::GSOptions&
config.SkipDrawEnd = value;
break;
case GSHWFixId::HalfBottomOverride:
config.UserHacks_HalfBottomOverride = value;
break;
case GSHWFixId::HalfPixelOffset:
config.UserHacks_HalfPixelOffset = value;
break;
+107 -14
View File
@@ -19,6 +19,7 @@
#include "Elfheader.h"
#include "GameList.h"
#include "Host.h"
#include "INISettingsInterface.h"
#include "VMManager.h"
#include "common/Assertions.h"
@@ -75,10 +76,10 @@ namespace GameList
static bool GetGameListEntryFromCache(const std::string& path, GameList::Entry* entry);
static void ScanDirectory(const char* path, bool recursive, bool only_cache, const std::vector<std::string>& excluded_paths,
const PlayedTimeMap& played_time_map, ProgressCallback* progress);
const PlayedTimeMap& played_time_map, const INISettingsInterface& custom_attributes_ini, ProgressCallback* progress);
static bool AddFileFromCache(const std::string& path, std::time_t timestamp, const PlayedTimeMap& played_time_map);
static bool ScanFile(
std::string path, std::time_t timestamp, std::unique_lock<std::recursive_mutex>& lock, const PlayedTimeMap& played_time_map);
static bool ScanFile(std::string path, std::time_t timestamp, std::unique_lock<std::recursive_mutex>& lock,
const PlayedTimeMap& played_time_map, const INISettingsInterface& custom_attributes_ini);
static void LoadCache();
static bool LoadEntriesFromCache(std::FILE* stream);
@@ -94,6 +95,8 @@ namespace GameList
static PlayedTimeMap LoadPlayedTimeMap(const std::string& path);
static PlayedTimeEntry UpdatePlayedTimeFile(
const std::string& path, const std::string& serial, std::time_t last_time, std::time_t add_time);
static std::string GetCustomPropertiesFile();
} // namespace GameList
static std::vector<GameList::Entry> s_entries;
@@ -582,11 +585,11 @@ 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,
const PlayedTimeMap& played_time_map, ProgressCallback* progress)
const PlayedTimeMap& played_time_map, const INISettingsInterface& custom_attributes_ini, ProgressCallback* progress)
{
Console.WriteLn("Scanning %s%s", path, recursive ? " (recursively)" : "");
@@ -596,7 +599,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;
@@ -619,7 +622,7 @@ void GameList::ScanDirectory(const char* path, bool recursive, bool only_cache,
}
progress->SetFormattedStatusText("Scanning '%s'...", FileSystem::GetDisplayNameFromPath(ffd.FileName).c_str());
ScanFile(std::move(ffd.FileName), ffd.ModificationTime, lock, played_time_map);
ScanFile(std::move(ffd.FileName), ffd.ModificationTime, lock, played_time_map, custom_attributes_ini);
progress->SetProgressValue(files_scanned);
}
@@ -648,8 +651,8 @@ bool GameList::AddFileFromCache(const std::string& path, std::time_t timestamp,
return true;
}
bool GameList::ScanFile(
std::string path, std::time_t timestamp, std::unique_lock<std::recursive_mutex>& lock, const PlayedTimeMap& played_time_map)
bool GameList::ScanFile(std::string path, std::time_t timestamp, std::unique_lock<std::recursive_mutex>& lock,
const PlayedTimeMap& played_time_map, const INISettingsInterface& custom_attributes_ini)
{
// don't block UI while scanning
lock.unlock();
@@ -675,13 +678,28 @@ bool GameList::ScanFile(
return true;
}
auto iter = played_time_map.find(entry.serial);
const auto iter = played_time_map.find(entry.serial);
if (iter != played_time_map.end())
{
entry.last_played_time = iter->second.last_played_time;
entry.total_played_time = iter->second.total_played_time;
}
auto custom_title = custom_attributes_ini.GetOptionalStringValue(entry.path.c_str(), "Title");
if (custom_title)
{
entry.title = std::move(custom_title.value());
}
const auto custom_region = custom_attributes_ini.GetOptionalIntValue(entry.path.c_str(), "Region");
if (custom_region)
{
const int custom_region_value = custom_region.value();
if (custom_region_value >= 0 && custom_region_value < static_cast<int>(Region::Count))
{
entry.region = static_cast<Region>(custom_region_value);
}
}
lock.lock();
// remove if present
@@ -764,6 +782,8 @@ void GameList::Refresh(bool invalidate_cache, bool only_cache, ProgressCallback*
const std::vector<std::string> dirs(Host::GetBaseStringListSetting("GameList", "Paths"));
const std::vector<std::string> recursive_dirs(Host::GetBaseStringListSetting("GameList", "RecursivePaths"));
const PlayedTimeMap played_time(LoadPlayedTimeMap(GetPlayedTimeFile()));
INISettingsInterface custom_attributes_ini(GetCustomPropertiesFile());
custom_attributes_ini.Load();
if (!dirs.empty() || !recursive_dirs.empty())
{
@@ -777,7 +797,7 @@ void GameList::Refresh(bool invalidate_cache, bool only_cache, ProgressCallback*
if (progress->IsCancelled())
break;
ScanDirectory(dir.c_str(), false, only_cache, excluded_paths, played_time, progress);
ScanDirectory(dir.c_str(), false, only_cache, excluded_paths, played_time, custom_attributes_ini, progress);
progress->SetProgressValue(++directory_counter);
}
for (const std::string& dir : recursive_dirs)
@@ -785,7 +805,7 @@ void GameList::Refresh(bool invalidate_cache, bool only_cache, ProgressCallback*
if (progress->IsCancelled())
break;
ScanDirectory(dir.c_str(), true, only_cache, excluded_paths, played_time, progress);
ScanDirectory(dir.c_str(), true, only_cache, excluded_paths, played_time, custom_attributes_ini, progress);
progress->SetProgressValue(++directory_counter);
}
}
@@ -804,16 +824,18 @@ bool GameList::RescanPath(const std::string& path)
std::unique_lock lock(s_mutex);
const PlayedTimeMap played_time(LoadPlayedTimeMap(GetPlayedTimeFile()));
INISettingsInterface custom_attributes_ini(GetCustomPropertiesFile());
custom_attributes_ini.Load();
{
// 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;
}
// re-scan!
if (!ScanFile(path, sd.ModificationTime, lock, played_time))
if (!ScanFile(path, sd.ModificationTime, lock, played_time, custom_attributes_ini))
return true;
// update cache.. this is far from ideal, but since everything's variable length, all we can do.
@@ -1319,3 +1341,74 @@ bool GameList::DownloadCovers(const std::vector<std::string>& url_templates, boo
return true;
}
std::string GameList::GetCustomPropertiesFile()
{
return Path::Combine(EmuFolders::Settings, "custom_properties.ini");
}
void GameList::CheckCustomAttributesForPath(const std::string& path, bool& has_custom_title, bool& has_custom_region)
{
INISettingsInterface names(GetCustomPropertiesFile());
if (names.Load())
{
has_custom_title = names.ContainsValue(path.c_str(), "Title");
has_custom_region = names.ContainsValue(path.c_str(), "Region");
}
}
void GameList::SaveCustomTitleForPath(const std::string& path, const std::string& custom_title)
{
INISettingsInterface names(GetCustomPropertiesFile());
names.Load();
if (!custom_title.empty())
{
names.SetStringValue(path.c_str(), "Title", custom_title.c_str());
}
else
{
names.DeleteValue(path.c_str(), "Title");
}
if (names.Save())
{
// Let the cache update by rescanning
RescanPath(path);
}
}
void GameList::SaveCustomRegionForPath(const std::string& path, int custom_region)
{
INISettingsInterface names(GetCustomPropertiesFile());
names.Load();
if (custom_region >= 0)
{
names.SetIntValue(path.c_str(), "Region", custom_region);
}
else
{
names.DeleteValue(path.c_str(), "Region");
}
if (names.Save())
{
// Let the cache update by rescanning
RescanPath(path);
}
}
std::string GameList::GetCustomTitleForPath(const std::string& path)
{
std::string ret;
std::unique_lock lock(s_mutex);
const GameList::Entry* entry = GetEntryForPath(path.c_str());
if (entry)
{
ret = entry->title;
}
return ret;
}
+6
View File
@@ -154,4 +154,10 @@ namespace GameList
/// the use_serial parameter. save_callback optionall takes the entry and the path the new cover is saved to.
bool DownloadCovers(const std::vector<std::string>& url_templates, bool use_serial = false, ProgressCallback* progress = nullptr,
std::function<void(const Entry*, std::string)> save_callback = {});
// Custom properties support
void CheckCustomAttributesForPath(const std::string& path, bool& has_custom_title, bool& has_custom_region);
void SaveCustomTitleForPath(const std::string& path, const std::string& custom_title);
void SaveCustomRegionForPath(const std::string& path, int custom_region);
std::string GetCustomTitleForPath(const std::string& path);
} // namespace GameList
+1 -1
View File
@@ -329,7 +329,7 @@ static u32 WRITERING_DMA(u32* pMem, u32 qwc)
// so we can get away with transferring "most" of it when it's a big packet.
// Use Wallace and Gromit Project Zoo or The Suffering for testing
if (qwc > 64)
qwc = qwc - 64;
qwc = qwc * 0.5f;
else
qwc = std::min(qwc, 8u);
}
+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
View File
@@ -3232,8 +3232,6 @@ void FullscreenUI::DrawGraphicsSettingsPage()
static constexpr const char* s_auto_flush_options[] = {
"Disabled (Default)", "Enabled (Sprites Only)", "Enabled (All Primitives)"};
DrawIntListSetting(bsi, "Half-Bottom Override", "Control the half-screen fix detection on texture shuffling.", "EmuCore/GS",
"UserHacks_Half_Bottom_Override", -1, s_generic_options, std::size(s_generic_options), -1);
DrawIntListSetting(bsi, "CPU Sprite Render Size", "Uses software renderer to draw texture decompression-like sprites.",
"EmuCore/GS", "UserHacks_CPUSpriteRenderBW", 0, s_cpu_sprite_render_bw_options, std::size(s_cpu_sprite_render_bw_options));
DrawIntListSetting(bsi, "CPU Sprite Render Level", "Determines filter level for CPU sprite render.", "EmuCore/GS",
+5 -4
View File
@@ -138,9 +138,10 @@ bool ImGuiManager::Initialize()
io.BackendUsingLegacyNavInputArray = 0;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard | ImGuiConfigFlags_NavEnableGamepad;
s_window_width = static_cast<float>(g_gs_device->GetWindowWidth());
s_window_height = static_cast<float>(g_gs_device->GetWindowHeight());
io.DisplayFramebufferScale = ImVec2(1, 1); // We already scale things ourselves, this would double-apply scaling
io.DisplaySize.x = static_cast<float>(g_gs_device->GetWindowWidth());
io.DisplaySize.y = static_cast<float>(g_gs_device->GetWindowHeight());
io.DisplaySize = ImVec2(s_window_width, s_window_height);
SetKeyMap();
SetStyle();
@@ -656,7 +657,7 @@ void ImGuiManager::DrawOSDMessages()
const float margin = std::ceil(10.0f * scale);
const float padding = std::ceil(8.0f * scale);
const float rounding = std::ceil(5.0f * scale);
const float max_width = ImGui::GetIO().DisplaySize.x - (margin + padding) * 2.0f;
const float max_width = s_window_width - (margin + padding) * 2.0f;
float position_x = margin;
float position_y = margin;
@@ -679,7 +680,7 @@ void ImGuiManager::DrawOSDMessages()
const float opacity = std::min(time_remaining, 1.0f);
const u32 alpha = static_cast<u32>(opacity * 255.0f);
if (position_y >= ImGui::GetIO().DisplaySize.y)
if (position_y >= s_window_height)
break;
const ImVec2 pos(position_x, position_y);
+8 -10
View File
@@ -118,9 +118,9 @@ void ImGuiManager::DrawPerformanceOverlay(float& position_y)
{ \
text_size = font->CalcTextSizeA(font->FontSize, std::numeric_limits<float>::max(), -1.0f, (text), nullptr, nullptr); \
dl->AddText(font, font->FontSize, \
ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x + shadow_offset, position_y + shadow_offset), \
ImVec2(GetWindowWidth() - margin - text_size.x + shadow_offset, position_y + shadow_offset), \
IM_COL32(0, 0, 0, 100), (text)); \
dl->AddText(font, font->FontSize, ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x, position_y), color, (text)); \
dl->AddText(font, font->FontSize, ImVec2(GetWindowWidth() - margin - text_size.x, position_y), color, (text)); \
position_y += text_size.y + spacing; \
} while (0)
@@ -261,7 +261,7 @@ void ImGuiManager::DrawPerformanceOverlay(float& position_y)
{
const ImVec2 history_size(200.0f * scale, 50.0f * scale);
ImGui::SetNextWindowSize(ImVec2(history_size.x, history_size.y));
ImGui::SetNextWindowPos(ImVec2(ImGui::GetIO().DisplaySize.x - margin - history_size.x, position_y));
ImGui::SetNextWindowPos(ImVec2(GetWindowWidth() - margin - history_size.x, position_y));
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.0f, 0.0f, 0.0f, 0.25f));
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f));
ImGui::PushStyleColor(ImGuiCol_PlotLines, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
@@ -390,8 +390,6 @@ void ImGuiManager::DrawSettingsOverlay()
APPEND("AF={} ", EmuConfig.GS.MaxAnisotropy);
if (GSConfig.Dithering != 2)
APPEND("DI={} ", GSConfig.Dithering);
if (GSConfig.UserHacks_HalfBottomOverride >= 0)
APPEND("HBO={} ", GSConfig.UserHacks_HalfBottomOverride);
if (GSConfig.UserHacks_HalfPixelOffset > 0)
APPEND("HPO={} ", GSConfig.UserHacks_HalfPixelOffset);
if (GSConfig.UserHacks_RoundSprite > 0)
@@ -451,15 +449,15 @@ void ImGuiManager::DrawSettingsOverlay()
const float shadow_offset = 1.0f * scale;
const float margin = 10.0f * scale;
ImFont* font = ImGuiManager::GetFixedFont();
const float position_y = ImGui::GetIO().DisplaySize.y - margin - font->FontSize;
const float position_y = GetWindowHeight() - margin - font->FontSize;
ImDrawList* dl = ImGui::GetBackgroundDrawList();
ImVec2 text_size =
font->CalcTextSizeA(font->FontSize, std::numeric_limits<float>::max(), -1.0f, text.c_str(), text.c_str() + text.length(), nullptr);
dl->AddText(font, font->FontSize,
ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x + shadow_offset, position_y + shadow_offset), IM_COL32(0, 0, 0, 100),
ImVec2(GetWindowWidth() - margin - text_size.x + shadow_offset, position_y + shadow_offset), IM_COL32(0, 0, 0, 100),
text.c_str(), text.c_str() + text.length());
dl->AddText(font, font->FontSize, ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x, position_y), IM_COL32(255, 255, 255, 255),
dl->AddText(font, font->FontSize, ImVec2(GetWindowWidth() - margin - text_size.x, position_y), IM_COL32(255, 255, 255, 255),
text.c_str(), text.c_str() + text.length());
}
@@ -631,9 +629,9 @@ void ImGuiManager::DrawInputRecordingOverlay(float& position_y)
{ \
text_size = font->CalcTextSizeA(font->FontSize, std::numeric_limits<float>::max(), -1.0f, (text), nullptr, nullptr); \
dl->AddText(font, font->FontSize, \
ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x + shadow_offset, position_y + shadow_offset), \
ImVec2(GetWindowWidth() - margin - text_size.x + shadow_offset, position_y + shadow_offset), \
IM_COL32(0, 0, 0, 100), (text)); \
dl->AddText(font, font->FontSize, ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x, position_y), color, (text)); \
dl->AddText(font, font->FontSize, ImVec2(GetWindowWidth() - margin - text_size.x, position_y), color, (text)); \
position_y += text_size.y + spacing; \
} while (0)
// TODO - icon list that would be nice to add
+1 -1
View File
@@ -638,7 +638,7 @@ void InputManager::AddPadBindings(SettingsInterface& si, u32 pad_index)
if (sioPadIsMultitapSlot(pad_index))
{
const auto& [mt_port, mt_slot] = sioConvertPadToPortAndSlot(pad_index);
if (EmuConfig.Pad.IsMultitapPortEnabled(mt_port))
if (!EmuConfig.Pad.IsMultitapPortEnabled(mt_port))
return;
}
+8 -3
View File
@@ -616,7 +616,6 @@ bool Pcsx2Config::GSOptions::OptionsAreEqual(const GSOptions& right) const
OpEqu(SkipDrawStart) &&
OpEqu(UserHacks_AutoFlush) &&
OpEqu(UserHacks_HalfBottomOverride) &&
OpEqu(UserHacks_HalfPixelOffset) &&
OpEqu(UserHacks_RoundSprite) &&
OpEqu(UserHacks_TCOffsetX) &&
@@ -814,7 +813,6 @@ void Pcsx2Config::GSOptions::LoadSave(SettingsWrapper& wrap)
GSSettingIntEx(SkipDrawEnd, "UserHacks_SkipDraw_End");
SkipDrawEnd = std::max(SkipDrawStart, SkipDrawEnd);
GSSettingIntEx(UserHacks_HalfBottomOverride, "UserHacks_Half_Bottom_Override");
GSSettingIntEx(UserHacks_HalfPixelOffset, "UserHacks_HalfPixelOffset");
GSSettingIntEx(UserHacks_RoundSprite, "UserHacks_round_sprite_offset");
GSSettingIntEx(UserHacks_TCOffsetX, "UserHacks_TCOffsetX");
@@ -882,7 +880,6 @@ void Pcsx2Config::GSOptions::MaskUserHacks()
UserHacks_NativePaletteDraw = false;
UserHacks_DisableSafeFeatures = false;
UserHacks_DisableRenderFixes = false;
UserHacks_HalfBottomOverride = -1;
UserHacks_HalfPixelOffset = 0;
UserHacks_RoundSprite = 0;
UserHacks_AutoFlush = GSHWAutoFlushLevel::Disabled;
@@ -1429,6 +1426,7 @@ Pcsx2Config::AchievementsOptions::AchievementsOptions()
Notifications = true;
SoundEffects = true;
PrimedIndicators = true;
NotificationsDuration = 5;
}
void Pcsx2Config::AchievementsOptions::LoadSave(SettingsWrapper& wrap)
@@ -1444,6 +1442,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
+5
View File
@@ -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)
+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 = 33;
+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;
+10 -5
View File
@@ -536,14 +536,19 @@ namespace usb_lightgun
ImGuiManager::ClearSoftwareCursor(prev_pointer_index);
// Pointer changed, so need to update software cursor.
if (!cursor_path.empty())
ImGuiManager::SetSoftwareCursor(new_pointer_index, cursor_path, cursor_scale, cursor_color);
else if (!s->cursor_path.empty())
ImGuiManager::ClearSoftwareCursor(new_pointer_index);
const bool had_software_cursor = !s->cursor_path.empty();
s->cursor_path = std::move(cursor_path);
s->cursor_scale = cursor_scale;
s->cursor_color = cursor_color;
if (!s->cursor_path.empty())
{
ImGuiManager::SetSoftwareCursor(new_pointer_index, s->cursor_path, s->cursor_scale, s->cursor_color);
s->UpdateSoftwarePointerPosition();
}
else if (had_software_cursor)
{
ImGuiManager::ClearSoftwareCursor(new_pointer_index);
}
}
}
+1 -1
View File
@@ -479,7 +479,7 @@ namespace usb_pad
static constexpr const SettingInfo info[] = {
{SettingInfo::Type::Float, "TurntableMultiplier", TRANSLATE_NOOP("USB", "Turntable Multiplier"),
TRANSLATE_NOOP("USB", "Apply a multiplier to the turntable"),
"0.00", "0.00", "100.0", "1.0", "%.0fx", nullptr, nullptr, 1.0f}};
"1.00", "0.00", "100.0", "1.0", "%.0fx", nullptr, nullptr, 1.0f}};
return info;
}

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